Mercurial > hg > icedtea6-hg
changeset 3153:c380668f35d0
Merge
author | Andrew John Hughes <gnu.andrew@redhat.com> |
---|---|
date | Wed, 08 Oct 2014 17:18:58 +0100 |
parents | f2308ef26cf8 (current diff) 251d55dd9268 (diff) |
children | a17dc173fdc5 |
files | ChangeLog Makefile.am |
diffstat | 4 files changed, 1980 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Jul 30 16:17:30 2014 +0100 +++ b/ChangeLog Wed Oct 08 17:18:58 2014 +0100 @@ -1,3 +1,23 @@ +2014-10-02 Andrew John Hughes <gnu.andrew@redhat.com> + + * Makefile.am: + (ICEDTEA_PATCHES): Add new patch. + * NEWS: Updated. + * patches/openjdk/7122142-annotation_race_condition.patch: + Backport fix for annotation race condition. + +2014-07-30 Andrew John Hughes <gnu.andrew@redhat.com> + + PR1886: IcedTea does not checksum supplied tarballs + * Makefile.am: + (download-openjdk): Check all tarballs, + rather than just those downloaded by $(WGET). + +2014-07-14 Omair Majid <omajid@redhat.com> + + * Makefile.am: + (OPENJDK_SHA256SUM): Update checksum for new tarball. + 2014-07-30 Andrew John Hughes <gnu.andrew@redhat.com> * patches/openjdk/8010213-set_socketoptions_windows.patch:
--- a/Makefile.am Wed Jul 30 16:17:30 2014 +0100 +++ b/Makefile.am Wed Oct 08 17:18:58 2014 +0100 @@ -1,7 +1,7 @@ # Dependencies OPENJDK_DATE = 15_jul_2014 -OPENJDK_SHA256SUM = 1a7404f38b3fa7cbb25d4273c0d94885912badd3343c5184c3b0947437501256 +OPENJDK_SHA256SUM = 9a5ad1b599953baac1b6b34189b9487ac5dcdb367aac5cc0aa5aa49700e73871 OPENJDK_VERSION = b33 OPENJDK_URL = https://java.net/downloads/openjdk6/ @@ -616,7 +616,8 @@ patches/openjdk/6611637-npe_in_glyphlayout.patch \ patches/openjdk/6727719-performance_of_textlayout_getbounds.patch \ patches/openjdk/6745225-memory_leak_in_attributed_string.patch \ - patches/openjdk/oj639-handle_fonts_with_no_canon_flag_set.patch + patches/openjdk/oj639-handle_fonts_with_no_canon_flag_set.patch \ + patches/openjdk/7122142-annotation_race_condition.patch if WITH_RHINO ICEDTEA_PATCHES += \ @@ -1120,7 +1121,7 @@ else if USE_ALT_OPENJDK_SRC_ZIP ln -sf $(ALT_OPENJDK_SRC_ZIP) $(OPENJDK_SRC_ZIP) -else +endif if ! echo "$(OPENJDK_SHA256SUM) $(OPENJDK_SRC_ZIP)" \ | $(SHA256SUM) --check ; \ then \ @@ -1139,7 +1140,6 @@ fi endif endif -endif mkdir -p stamps touch $@
--- a/NEWS Wed Jul 30 16:17:30 2014 +0100 +++ b/NEWS Wed Oct 08 17:18:58 2014 +0100 @@ -19,10 +19,13 @@ - S6727719: Performance of TextLayout.getBounds() - S6745225: Memory leak while drawing Attributed String - S6904962: GlyphVector.getVisualBounds should not be affected by leading or trailing white space. + - S7122142: (ann) Race condition between isAnnotationPresent and getAnnotations - S7151089: PS NUMA: NUMA allocator should not attempt to free pages when using SHM large pages - S8013057: Detect mmap() commit failures in Linux and Solaris os::commit_memory() impls and call vm_exit_out_of_memory() - S8026887: Make issues due to failed large pages allocations easier to debug - OJ39: Handle fonts with the non-canonical processing flag set +* Bug fixes + - PR1886: IcedTea does not checksum supplied tarballs New in release 1.13.4 (2014-07-15):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/patches/openjdk/7122142-annotation_race_condition.patch Wed Oct 08 17:18:58 2014 +0100 @@ -0,0 +1,1953 @@ +diff -r d1f592073a0e src/share/classes/java/lang/Class.java +--- openjdk/jdk/src/share/classes/java/lang/Class.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/java/lang/Class.java Thu Oct 02 20:18:56 2014 +0100 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -271,7 +271,7 @@ + } + + /** Called after security checks have been made. */ +- private static native Class forName0(String name, boolean initialize, ++ private static native Class<?> forName0(String name, boolean initialize, + ClassLoader loader) + throws ClassNotFoundException; + +@@ -341,15 +341,15 @@ + ); + } + try { +- Class[] empty = {}; ++ Class<?>[] empty = {}; + final Constructor<T> c = getConstructor0(empty, Member.DECLARED); + // Disable accessibility checks on the constructor + // since we have to do the security check here anyway + // (the stack depth is wrong for the Constructor's + // security check to work) +- java.security.AccessController.doPrivileged +- (new java.security.PrivilegedAction() { +- public Object run() { ++ java.security.AccessController.doPrivileged( ++ new java.security.PrivilegedAction<Void>() { ++ public Void run() { + c.setAccessible(true); + return null; + } +@@ -379,7 +379,7 @@ + } + } + private volatile transient Constructor<T> cachedConstructor; +- private volatile transient Class newInstanceCallerCache; ++ private volatile transient Class<?> newInstanceCallerCache; + + + /** +@@ -637,7 +637,7 @@ + if (getGenericSignature() != null) + return (TypeVariable<Class<T>>[])getGenericInfo().getTypeParameters(); + else +- return (TypeVariable<Class<T>>[])new TypeVariable[0]; ++ return (TypeVariable<Class<T>>[])new TypeVariable<?>[0]; + } + + +@@ -901,7 +901,7 @@ + + MethodRepository typeInfo = MethodRepository.make(enclosingInfo.getDescriptor(), + getFactory()); +- Class returnType = toClass(typeInfo.getReturnType()); ++ Class<?> returnType = toClass(typeInfo.getReturnType()); + Type [] parameterTypes = typeInfo.getParameterTypes(); + Class<?>[] parameterClasses = new Class<?>[parameterTypes.length]; + +@@ -1005,12 +1005,12 @@ + + } + +- private static Class toClass(Type o) { ++ private static Class<?> toClass(Type o) { + if (o instanceof GenericArrayType) + return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()), + 0) + .getClass(); +- return (Class)o; ++ return (Class<?>)o; + } + + /** +@@ -1340,13 +1340,13 @@ + // out anything other than public members and (2) public member access + // has already been ok'd by the SecurityManager. + +- Class[] result = (Class[]) java.security.AccessController.doPrivileged +- (new java.security.PrivilegedAction() { +- public Object run() { +- java.util.List<Class> list = new java.util.ArrayList(); +- Class currentClass = Class.this; ++ return java.security.AccessController.doPrivileged( ++ new java.security.PrivilegedAction<Class<?>[]>() { ++ public Class[] run() { ++ List<Class<?>> list = new ArrayList<Class<?>>(); ++ Class<?> currentClass = Class.this; + while (currentClass != null) { +- Class[] members = currentClass.getDeclaredClasses(); ++ Class<?>[] members = currentClass.getDeclaredClasses(); + for (int i = 0; i < members.length; i++) { + if (Modifier.isPublic(members[i].getModifiers())) { + list.add(members[i]); +@@ -1354,12 +1354,9 @@ + } + currentClass = currentClass.getSuperclass(); + } +- Class[] empty = {}; +- return list.toArray(empty); ++ return list.toArray(new Class[0]); + } + }); +- +- return result; + } + + +@@ -2283,7 +2280,7 @@ + return name; + } + if (!name.startsWith("/")) { +- Class c = this; ++ Class<?> c = this; + while (c.isArray()) { + c = c.getComponentType(); + } +@@ -2300,44 +2297,111 @@ + } + + /** ++ * Atomic operations support. ++ */ ++ private static class Atomic { ++ // initialize Unsafe machinery here, since we need to call Class.class instance method ++ // and have to avoid calling it in the static initializer of the Class class... ++ private static final Unsafe unsafe = Unsafe.getUnsafe(); ++ // offset of Class.reflectionData instance field ++ private static final long reflectionDataOffset; ++ // offset of Class.annotationType instance field ++ private static final long annotationTypeOffset; ++ ++ static { ++ Field[] fields = Class.class.getDeclaredFields0(false); // bypass caches ++ reflectionDataOffset = objectFieldOffset(fields, "reflectionData"); ++ annotationTypeOffset = objectFieldOffset(fields, "annotationType"); ++ } ++ ++ private static long objectFieldOffset(Field[] fields, String fieldName) { ++ Field field = searchFields(fields, fieldName); ++ if (field == null) { ++ throw new Error("No " + fieldName + " field found in java.lang.Class"); ++ } ++ return unsafe.objectFieldOffset(field); ++ } ++ ++ static <T> boolean casReflectionData(Class<?> clazz, ++ SoftReference<ReflectionData<T>> oldData, ++ SoftReference<ReflectionData<T>> newData) { ++ return unsafe.compareAndSwapObject(clazz, reflectionDataOffset, oldData, newData); ++ } ++ ++ static <T> boolean casAnnotationType(Class<?> clazz, ++ AnnotationType oldType, ++ AnnotationType newType) { ++ return unsafe.compareAndSwapObject(clazz, annotationTypeOffset, oldType, newType); ++ } ++ } ++ ++ /** + * Reflection support. + */ + + // Caches for certain reflective results + private static boolean useCaches = true; +- private volatile transient SoftReference declaredFields; +- private volatile transient SoftReference publicFields; +- private volatile transient SoftReference declaredMethods; +- private volatile transient SoftReference publicMethods; +- private volatile transient SoftReference declaredConstructors; +- private volatile transient SoftReference publicConstructors; +- // Intermediate results for getFields and getMethods +- private volatile transient SoftReference declaredPublicFields; +- private volatile transient SoftReference declaredPublicMethods; ++ ++ // reflection data that might get invalidated when JVM TI RedefineClasses() is called ++ static class ReflectionData<T> { ++ volatile Field[] declaredFields; ++ volatile Field[] publicFields; ++ volatile Method[] declaredMethods; ++ volatile Method[] publicMethods; ++ volatile Constructor<T>[] declaredConstructors; ++ volatile Constructor<T>[] publicConstructors; ++ // Intermediate results for getFields and getMethods ++ volatile Field[] declaredPublicFields; ++ volatile Method[] declaredPublicMethods; ++ // Value of classRedefinedCount when we created this ReflectionData instance ++ final int redefinedCount; ++ ++ ReflectionData(int redefinedCount) { ++ this.redefinedCount = redefinedCount; ++ } ++ } ++ ++ private volatile transient SoftReference<ReflectionData<T>> reflectionData; + + // Incremented by the VM on each call to JVM TI RedefineClasses() + // that redefines this class or a superclass. + private volatile transient int classRedefinedCount = 0; + +- // Value of classRedefinedCount when we last cleared the cached values +- // that are sensitive to class redefinition. +- private volatile transient int lastRedefinedCount = 0; ++ // Lazily create and cache ReflectionData ++ private ReflectionData<T> reflectionData() { ++ SoftReference<ReflectionData<T>> reflectionData = this.reflectionData; ++ int classRedefinedCount = this.classRedefinedCount; ++ ReflectionData<T> rd; ++ if (useCaches && ++ reflectionData != null && ++ (rd = reflectionData.get()) != null && ++ rd.redefinedCount == classRedefinedCount) { ++ return rd; ++ } ++ // else no SoftReference or cleared SoftReference or stale ReflectionData ++ // -> create and replace new instance ++ return newReflectionData(reflectionData, classRedefinedCount); ++ } + +- // Clears cached values that might possibly have been obsoleted by +- // a class redefinition. +- private void clearCachesOnClassRedefinition() { +- if (lastRedefinedCount != classRedefinedCount) { +- declaredFields = publicFields = declaredPublicFields = null; +- declaredMethods = publicMethods = declaredPublicMethods = null; +- declaredConstructors = publicConstructors = null; +- annotations = declaredAnnotations = null; ++ private ReflectionData<T> newReflectionData(SoftReference<ReflectionData<T>> oldReflectionData, ++ int classRedefinedCount) { ++ if (!useCaches) return null; + +- // Use of "volatile" (and synchronization by caller in the case +- // of annotations) ensures that no thread sees the update to +- // lastRedefinedCount before seeing the caches cleared. +- // We do not guard against brief windows during which multiple +- // threads might redundantly work to fill an empty cache. +- lastRedefinedCount = classRedefinedCount; ++ while (true) { ++ ReflectionData<T> rd = new ReflectionData<T>(classRedefinedCount); ++ // try to CAS it... ++ if (Atomic.casReflectionData(this, oldReflectionData, ++ new SoftReference<ReflectionData<T>>(rd))) { ++ return rd; ++ } ++ // else retry ++ oldReflectionData = this.reflectionData; ++ classRedefinedCount = this.classRedefinedCount; ++ if (oldReflectionData != null && ++ (rd = oldReflectionData.get()) != null && ++ rd.redefinedCount == classRedefinedCount) { ++ return rd; ++ } + } + } + +@@ -2365,7 +2429,7 @@ + } + + // Annotations handling +- private native byte[] getRawAnnotations(); ++ native byte[] getRawAnnotations(); + + native ConstantPool getConstantPool(); + +@@ -2380,27 +2444,19 @@ + // via ReflectionFactory.copyField. + private Field[] privateGetDeclaredFields(boolean publicOnly) { + checkInitted(); +- Field[] res = null; +- if (useCaches) { +- clearCachesOnClassRedefinition(); +- if (publicOnly) { +- if (declaredPublicFields != null) { +- res = (Field[]) declaredPublicFields.get(); +- } +- } else { +- if (declaredFields != null) { +- res = (Field[]) declaredFields.get(); +- } +- } ++ Field[] res; ++ ReflectionData<T> rd = reflectionData(); ++ if (rd != null) { ++ res = publicOnly ? rd.declaredPublicFields : rd.declaredFields; + if (res != null) return res; + } + // No cached value available; request value from VM + res = Reflection.filterFields(this, getDeclaredFields0(publicOnly)); +- if (useCaches) { ++ if (rd != null) { + if (publicOnly) { +- declaredPublicFields = new SoftReference(res); ++ rd.declaredPublicFields = res; + } else { +- declaredFields = new SoftReference(res); ++ rd.declaredFields = res; + } + } + return res; +@@ -2409,22 +2465,20 @@ + // Returns an array of "root" fields. These Field objects must NOT + // be propagated to the outside world, but must instead be copied + // via ReflectionFactory.copyField. +- private Field[] privateGetPublicFields(Set traversedInterfaces) { ++ private Field[] privateGetPublicFields(Set<Class<?>> traversedInterfaces) { + checkInitted(); +- Field[] res = null; +- if (useCaches) { +- clearCachesOnClassRedefinition(); +- if (publicFields != null) { +- res = (Field[]) publicFields.get(); +- } ++ Field[] res; ++ ReflectionData<T> rd = reflectionData(); ++ if (rd != null) { ++ res = rd.publicFields; + if (res != null) return res; + } + + // No cached value available; compute value recursively. + // Traverse in correct order for getField(). +- List fields = new ArrayList(); ++ List<Field> fields = new ArrayList<Field>(); + if (traversedInterfaces == null) { +- traversedInterfaces = new HashSet(); ++ traversedInterfaces = new HashSet<Class<?>>(); + } + + // Local fields +@@ -2432,9 +2486,7 @@ + addAll(fields, tmp); + + // Direct superinterfaces, recursively +- Class[] interfaces = getInterfaces(); +- for (int i = 0; i < interfaces.length; i++) { +- Class c = interfaces[i]; ++ for (Class<?> c : getInterfaces()) { + if (!traversedInterfaces.contains(c)) { + traversedInterfaces.add(c); + addAll(fields, c.privateGetPublicFields(traversedInterfaces)); +@@ -2443,7 +2495,7 @@ + + // Direct superclass, recursively + if (!isInterface()) { +- Class c = getSuperclass(); ++ Class<?> c = getSuperclass(); + if (c != null) { + addAll(fields, c.privateGetPublicFields(traversedInterfaces)); + } +@@ -2451,13 +2503,13 @@ + + res = new Field[fields.size()]; + fields.toArray(res); +- if (useCaches) { +- publicFields = new SoftReference(res); ++ if (rd != null) { ++ rd.publicFields = res; + } + return res; + } + +- private static void addAll(Collection c, Field[] o) { ++ private static void addAll(Collection<Field> c, Field[] o) { + for (int i = 0; i < o.length; i++) { + c.add(o[i]); + } +@@ -2473,20 +2525,12 @@ + // Returns an array of "root" constructors. These Constructor + // objects must NOT be propagated to the outside world, but must + // instead be copied via ReflectionFactory.copyConstructor. +- private Constructor[] privateGetDeclaredConstructors(boolean publicOnly) { ++ private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) { + checkInitted(); +- Constructor[] res = null; +- if (useCaches) { +- clearCachesOnClassRedefinition(); +- if (publicOnly) { +- if (publicConstructors != null) { +- res = (Constructor[]) publicConstructors.get(); +- } +- } else { +- if (declaredConstructors != null) { +- res = (Constructor[]) declaredConstructors.get(); +- } +- } ++ Constructor<T>[] res; ++ ReflectionData<T> rd = reflectionData(); ++ if (rd != null) { ++ res = publicOnly ? rd.publicConstructors : rd.declaredConstructors; + if (res != null) return res; + } + // No cached value available; request value from VM +@@ -2495,11 +2539,11 @@ + } else { + res = getDeclaredConstructors0(publicOnly); + } +- if (useCaches) { ++ if (rd != null) { + if (publicOnly) { +- publicConstructors = new SoftReference(res); ++ rd.publicConstructors = res; + } else { +- declaredConstructors = new SoftReference(res); ++ rd.declaredConstructors = res; + } + } + return res; +@@ -2516,27 +2560,19 @@ + // via ReflectionFactory.copyMethod. + private Method[] privateGetDeclaredMethods(boolean publicOnly) { + checkInitted(); +- Method[] res = null; +- if (useCaches) { +- clearCachesOnClassRedefinition(); +- if (publicOnly) { +- if (declaredPublicMethods != null) { +- res = (Method[]) declaredPublicMethods.get(); +- } +- } else { +- if (declaredMethods != null) { +- res = (Method[]) declaredMethods.get(); +- } +- } ++ Method[] res; ++ ReflectionData<T> rd = reflectionData(); ++ if (rd != null) { ++ res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods; + if (res != null) return res; + } + // No cached value available; request value from VM + res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly)); +- if (useCaches) { ++ if (rd != null) { + if (publicOnly) { +- declaredPublicMethods = new SoftReference(res); ++ rd.declaredPublicMethods = res; + } else { +- declaredMethods = new SoftReference(res); ++ rd.declaredMethods = res; + } + } + return res; +@@ -2638,12 +2674,10 @@ + // via ReflectionFactory.copyMethod. + private Method[] privateGetPublicMethods() { + checkInitted(); +- Method[] res = null; +- if (useCaches) { +- clearCachesOnClassRedefinition(); +- if (publicMethods != null) { +- res = (Method[]) publicMethods.get(); +- } ++ Method[] res; ++ ReflectionData<T> rd = reflectionData(); ++ if (rd != null) { ++ res = rd.publicMethods; + if (res != null) return res; + } + +@@ -2659,12 +2693,12 @@ + // out concrete implementations inherited from superclasses at + // the end. + MethodArray inheritedMethods = new MethodArray(); +- Class[] interfaces = getInterfaces(); ++ Class<?>[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + inheritedMethods.addAll(interfaces[i].privateGetPublicMethods()); + } + if (!isInterface()) { +- Class c = getSuperclass(); ++ Class<?> c = getSuperclass(); + if (c != null) { + MethodArray supers = new MethodArray(); + supers.addAll(c.privateGetPublicMethods()); +@@ -2691,8 +2725,8 @@ + methods.addAllIfNotPresent(inheritedMethods); + methods.compactAndTrim(); + res = methods.getArray(); +- if (useCaches) { +- publicMethods = new SoftReference(res); ++ if (rd != null) { ++ rd.publicMethods = res; + } + return res; + } +@@ -2702,7 +2736,7 @@ + // Helpers for fetchers of one field, method, or constructor + // + +- private Field searchFields(Field[] fields, String name) { ++ private static Field searchFields(Field[] fields, String name) { + String internedName = name.intern(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName() == internedName) { +@@ -2720,22 +2754,22 @@ + // of Field objects which have to be created for the common + // case where the field being requested is declared in the + // class which is being queried. +- Field res = null; ++ Field res; + // Search declared public fields + if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) { + return res; + } + // Direct superinterfaces, recursively +- Class[] interfaces = getInterfaces(); ++ Class<?>[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { +- Class c = interfaces[i]; ++ Class<?> c = interfaces[i]; + if ((res = c.getField0(name)) != null) { + return res; + } + } + // Direct superclass, recursively + if (!isInterface()) { +- Class c = getSuperclass(); ++ Class<?> c = getSuperclass(); + if (c != null) { + if ((res = c.getField0(name)) != null) { + return res; +@@ -2747,7 +2781,7 @@ + + private static Method searchMethods(Method[] methods, + String name, +- Class[] parameterTypes) ++ Class<?>[] parameterTypes) + { + Method res = null; + String internedName = name.intern(); +@@ -2764,7 +2798,7 @@ + } + + +- private Method getMethod0(String name, Class[] parameterTypes) { ++ private Method getMethod0(String name, Class<?>[] parameterTypes) { + // Note: the intent is that the search algorithm this routine + // uses be equivalent to the ordering imposed by + // privateGetPublicMethods(). It fetches only the declared +@@ -2772,7 +2806,7 @@ + // number of Method objects which have to be created for the + // common case where the method being requested is declared in + // the class which is being queried. +- Method res = null; ++ Method res; + // Search declared public methods + if ((res = searchMethods(privateGetDeclaredMethods(true), + name, +@@ -2781,7 +2815,7 @@ + } + // Search superclass's methods + if (!isInterface()) { +- Class c = getSuperclass(); ++ Class<? super T> c = getSuperclass(); + if (c != null) { + if ((res = c.getMethod0(name, parameterTypes)) != null) { + return res; +@@ -2789,9 +2823,9 @@ + } + } + // Search superinterfaces' methods +- Class[] interfaces = getInterfaces(); ++ Class<?>[] interfaces = getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { +- Class c = interfaces[i]; ++ Class<?> c = interfaces[i]; + if ((res = c.getMethod0(name, parameterTypes)) != null) { + return res; + } +@@ -2800,14 +2834,14 @@ + return null; + } + +- private Constructor<T> getConstructor0(Class[] parameterTypes, ++ private Constructor<T> getConstructor0(Class<?>[] parameterTypes, + int which) throws NoSuchMethodException + { +- Constructor[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); +- for (int i = 0; i < constructors.length; i++) { ++ Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC)); ++ for (Constructor<T> constructor : constructors) { + if (arrayContentsEq(parameterTypes, +- constructors[i].getParameterTypes())) { +- return getReflectionFactory().copyConstructor(constructors[i]); ++ constructor.getParameterTypes())) { ++ return getReflectionFactory().copyConstructor(constructor); + } + } + throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes)); +@@ -2857,21 +2891,21 @@ + return out; + } + +- private static Constructor[] copyConstructors(Constructor[] arg) { +- Constructor[] out = new Constructor[arg.length]; ++ private static <U> Constructor<U>[] copyConstructors(Constructor<U>[] arg) { ++ Constructor<U>[] out = arg.clone(); + ReflectionFactory fact = getReflectionFactory(); +- for (int i = 0; i < arg.length; i++) { +- out[i] = fact.copyConstructor(arg[i]); ++ for (int i = 0; i < out.length; i++) { ++ out[i] = fact.copyConstructor(out[i]); + } + return out; + } + + private native Field[] getDeclaredFields0(boolean publicOnly); + private native Method[] getDeclaredMethods0(boolean publicOnly); +- private native Constructor[] getDeclaredConstructors0(boolean publicOnly); +- private native Class[] getDeclaredClasses0(); ++ private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly); ++ private native Class<?>[] getDeclaredClasses0(); + +- private static String argumentTypesToString(Class[] argTypes) { ++ private static String argumentTypesToString(Class<?>[] argTypes) { + StringBuilder buf = new StringBuilder(); + buf.append("("); + if (argTypes != null) { +@@ -2879,7 +2913,7 @@ + if (i > 0) { + buf.append(", "); + } +- Class c = argTypes[i]; ++ Class<?> c = argTypes[i]; + buf.append((c == null) ? "null" : c.getName()); + } + } +@@ -2952,7 +2986,7 @@ + } + + // Retrieves the desired assertion status of this class from the VM +- private static native boolean desiredAssertionStatus0(Class clazz); ++ private static native boolean desiredAssertionStatus0(Class<?> clazz); + + /** + * Returns true if and only if this class was declared as an enum in the +@@ -2973,7 +3007,7 @@ + // Fetches the factory for reflective objects + private static ReflectionFactory getReflectionFactory() { + if (reflectionFactory == null) { +- reflectionFactory = (ReflectionFactory) ++ reflectionFactory = + java.security.AccessController.doPrivileged + (new sun.reflect.ReflectionFactory.GetReflectionFactoryAction()); + } +@@ -3039,9 +3073,9 @@ + if (!isEnum()) return null; + try { + final Method values = getMethod("values"); +- java.security.AccessController.doPrivileged +- (new java.security.PrivilegedAction() { +- public Object run() { ++ java.security.AccessController.doPrivileged( ++ new java.security.PrivilegedAction<Void>() { ++ public Void run() { + values.setAccessible(true); + return null; + } +@@ -3073,7 +3107,7 @@ + getName() + " is not an enum type"); + Map<String, T> m = new HashMap<String, T>(2 * universe.length); + for (T constant : universe) +- m.put(((Enum)constant).name(), constant); ++ m.put(((Enum<?>)constant).name(), constant); + enumConstantDirectory = m; + } + return enumConstantDirectory; +@@ -3173,11 +3207,22 @@ + } + + // Annotations cache +- private transient Map<Class, Annotation> annotations; +- private transient Map<Class, Annotation> declaredAnnotations; ++ private transient Map<Class<? extends Annotation>, Annotation> annotations; ++ private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations; ++ // Value of classRedefinedCount when we last cleared the cached annotations and declaredAnnotations fields ++ private transient int lastAnnotationsRedefinedCount = 0; ++ ++ // Clears cached values that might possibly have been obsoleted by ++ // a class redefinition. ++ private void clearAnnotationCachesOnClassRedefinition() { ++ if (lastAnnotationsRedefinedCount != classRedefinedCount) { ++ annotations = declaredAnnotations = null; ++ lastAnnotationsRedefinedCount = classRedefinedCount; ++ } ++ } + + private synchronized void initAnnotationsIfNecessary() { +- clearCachesOnClassRedefinition(); ++ clearAnnotationCachesOnClassRedefinition(); + if (annotations != null) + return; + declaredAnnotations = AnnotationParser.parseAnnotations( +@@ -3186,10 +3231,10 @@ + if (superClass == null) { + annotations = declaredAnnotations; + } else { +- annotations = new HashMap<Class, Annotation>(); ++ annotations = new HashMap<Class<? extends Annotation>, Annotation>(); + superClass.initAnnotationsIfNecessary(); +- for (Map.Entry<Class, Annotation> e : superClass.annotations.entrySet()) { +- Class annotationClass = e.getKey(); ++ for (Map.Entry<Class<? extends Annotation>, Annotation> e : superClass.annotations.entrySet()) { ++ Class<? extends Annotation> annotationClass = e.getKey(); + if (AnnotationType.getInstance(annotationClass).isInherited()) + annotations.put(annotationClass, e.getValue()); + } +@@ -3199,10 +3244,11 @@ + + // Annotation types cache their internal (AnnotationType) form + +- private AnnotationType annotationType; ++ @SuppressWarnings("UnusedDeclaration") ++ private volatile transient AnnotationType annotationType; + +- void setAnnotationType(AnnotationType type) { +- annotationType = type; ++ boolean casAnnotationType(AnnotationType oldType, AnnotationType newType) { ++ return Atomic.casAnnotationType(this, oldType, newType); + } + + AnnotationType getAnnotationType() { +diff -r d1f592073a0e src/share/classes/java/lang/System.java +--- openjdk/jdk/src/share/classes/java/lang/System.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/java/lang/System.java Thu Oct 02 20:18:56 2014 +0100 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1994, 2007, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -1146,12 +1146,15 @@ + public sun.reflect.ConstantPool getConstantPool(Class klass) { + return klass.getConstantPool(); + } +- public void setAnnotationType(Class klass, AnnotationType type) { +- klass.setAnnotationType(type); ++ public boolean casAnnotationType(Class<?> klass, AnnotationType oldType, AnnotationType newType) { ++ return klass.casAnnotationType(oldType, newType); + } + public AnnotationType getAnnotationType(Class klass) { + return klass.getAnnotationType(); + } ++ public byte[] getRawClassAnnotations(Class<?> klass) { ++ return klass.getRawAnnotations(); ++ } + public <E extends Enum<E>> + E[] getEnumConstantsShared(Class<E> klass) { + return klass.getEnumConstantsShared(); +diff -r d1f592073a0e src/share/classes/java/lang/reflect/Constructor.java +--- openjdk/jdk/src/share/classes/java/lang/reflect/Constructor.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/java/lang/reflect/Constructor.java Thu Oct 02 20:18:56 2014 +0100 +@@ -65,8 +65,8 @@ + + private Class<T> clazz; + private int slot; +- private Class[] parameterTypes; +- private Class[] exceptionTypes; ++ private Class<?>[] parameterTypes; ++ private Class<?>[] exceptionTypes; + private int modifiers; + // Generics and annotations support + private transient String signature; +@@ -118,8 +118,8 @@ + * package via sun.reflect.LangReflectAccess. + */ + Constructor(Class<T> declaringClass, +- Class[] parameterTypes, +- Class[] checkedExceptions, ++ Class<?>[] parameterTypes, ++ Class<?>[] checkedExceptions, + int modifiers, + int slot, + String signature, +@@ -366,14 +366,14 @@ + } + sb.append(Field.getTypeName(getDeclaringClass())); + sb.append("("); +- Class[] params = parameterTypes; // avoid clone ++ Class<?>[] params = parameterTypes; // avoid clone + for (int j = 0; j < params.length; j++) { + sb.append(Field.getTypeName(params[j])); + if (j < (params.length - 1)) + sb.append(","); + } + sb.append(")"); +- Class[] exceptions = exceptionTypes; // avoid clone ++ Class<?>[] exceptions = exceptionTypes; // avoid clone + if (exceptions.length > 0) { + sb.append(" throws "); + for (int k = 0; k < exceptions.length; k++) { +@@ -454,7 +454,7 @@ + sb.append(" throws "); + for (int k = 0; k < exceptions.length; k++) { + sb.append((exceptions[k] instanceof Class)? +- ((Class)exceptions[k]).getName(): ++ ((Class<?>)exceptions[k]).getName(): + exceptions[k].toString()); + if (k < (exceptions.length - 1)) + sb.append(","); +@@ -630,9 +630,9 @@ + return declaredAnnotations().values().toArray(EMPTY_ANNOTATION_ARRAY); + } + +- private transient Map<Class, Annotation> declaredAnnotations; ++ private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations; + +- private synchronized Map<Class, Annotation> declaredAnnotations() { ++ private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() { + if (declaredAnnotations == null) { + declaredAnnotations = AnnotationParser.parseAnnotations( + annotations, sun.misc.SharedSecrets.getJavaLangAccess(). +diff -r d1f592073a0e src/share/classes/java/lang/reflect/Field.java +--- openjdk/jdk/src/share/classes/java/lang/reflect/Field.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/java/lang/reflect/Field.java Thu Oct 02 20:18:56 2014 +0100 +@@ -59,12 +59,12 @@ + public final + class Field extends AccessibleObject implements Member { + +- private Class clazz; ++ private Class<?> clazz; + private int slot; + // This is guaranteed to be interned by the VM in the 1.4 + // reflection implementation + private String name; +- private Class type; ++ private Class<?> type; + private int modifiers; + // Generics and annotations support + private transient String signature; +@@ -113,9 +113,9 @@ + * instantiation of these objects in Java code from the java.lang + * package via sun.reflect.LangReflectAccess. + */ +- Field(Class declaringClass, ++ Field(Class<?> declaringClass, + String name, +- Class type, ++ Class<?> type, + int modifiers, + int slot, + String signature, +@@ -1090,10 +1090,10 @@ + /* + * Utility routine to paper over array type names + */ +- static String getTypeName(Class type) { ++ static String getTypeName(Class<?> type) { + if (type.isArray()) { + try { +- Class cl = type; ++ Class<?> cl = type; + int dimensions = 0; + while (cl.isArray()) { + dimensions++; +@@ -1130,9 +1130,9 @@ + return declaredAnnotations().values().toArray(EMPTY_ANNOTATION_ARRAY); + } + +- private transient Map<Class, Annotation> declaredAnnotations; ++ private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations; + +- private synchronized Map<Class, Annotation> declaredAnnotations() { ++ private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() { + if (declaredAnnotations == null) { + declaredAnnotations = AnnotationParser.parseAnnotations( + annotations, sun.misc.SharedSecrets.getJavaLangAccess(). +diff -r d1f592073a0e src/share/classes/java/lang/reflect/Method.java +--- openjdk/jdk/src/share/classes/java/lang/reflect/Method.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/java/lang/reflect/Method.java Thu Oct 02 20:18:56 2014 +0100 +@@ -62,14 +62,14 @@ + public final + class Method extends AccessibleObject implements GenericDeclaration, + Member { +- private Class clazz; ++ private Class<?> clazz; + private int slot; + // This is guaranteed to be interned by the VM in the 1.4 + // reflection implementation + private String name; +- private Class returnType; +- private Class[] parameterTypes; +- private Class[] exceptionTypes; ++ private Class<?> returnType; ++ private Class<?>[] parameterTypes; ++ private Class<?>[] exceptionTypes; + private int modifiers; + // Generics and annotations support + private transient String signature; +@@ -121,11 +121,11 @@ + * instantiation of these objects in Java code from the java.lang + * package via sun.reflect.LangReflectAccess. + */ +- Method(Class declaringClass, ++ Method(Class<?> declaringClass, + String name, +- Class[] parameterTypes, +- Class returnType, +- Class[] checkedExceptions, ++ Class<?>[] parameterTypes, ++ Class<?> returnType, ++ Class<?>[] checkedExceptions, + int modifiers, + int slot, + String signature, +@@ -366,8 +366,8 @@ + if (!returnType.equals(other.getReturnType())) + return false; + /* Avoid unnecessary cloning */ +- Class[] params1 = parameterTypes; +- Class[] params2 = other.parameterTypes; ++ Class<?>[] params1 = parameterTypes; ++ Class<?>[] params2 = other.parameterTypes; + if (params1.length == params2.length) { + for (int i = 0; i < params1.length; i++) { + if (params1[i] != params2[i]) +@@ -428,7 +428,7 @@ + sb.append(","); + } + sb.append(")"); +- Class[] exceptions = exceptionTypes; // avoid clone ++ Class<?>[] exceptions = exceptionTypes; // avoid clone + if (exceptions.length > 0) { + sb.append(" throws "); + for (int k = 0; k < exceptions.length; k++) { +@@ -723,9 +723,9 @@ + return declaredAnnotations().values().toArray(EMPTY_ANNOTATION_ARRAY); + } + +- private transient Map<Class, Annotation> declaredAnnotations; ++ private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations; + +- private synchronized Map<Class, Annotation> declaredAnnotations() { ++ private synchronized Map<Class<? extends Annotation>, Annotation> declaredAnnotations() { + if (declaredAnnotations == null) { + declaredAnnotations = AnnotationParser.parseAnnotations( + annotations, sun.misc.SharedSecrets.getJavaLangAccess(). +@@ -752,7 +752,7 @@ + public Object getDefaultValue() { + if (annotationDefault == null) + return null; +- Class memberType = AnnotationType.invocationHandlerReturnType( ++ Class<?> memberType = AnnotationType.invocationHandlerReturnType( + getReturnType()); + Object result = AnnotationParser.parseMemberValue( + memberType, ByteBuffer.wrap(annotationDefault), +diff -r d1f592073a0e src/share/classes/sun/misc/JavaLangAccess.java +--- openjdk/jdk/src/share/classes/sun/misc/JavaLangAccess.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/sun/misc/JavaLangAccess.java Thu Oct 02 20:18:56 2014 +0100 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -35,10 +35,10 @@ + ConstantPool getConstantPool(Class klass); + + /** +- * Set the AnnotationType instance corresponding to this class. ++ * Compare-And-Swap the AnnotationType instance corresponding to this class. + * (This method only applies to annotation types.) + */ +- void setAnnotationType(Class klass, AnnotationType annotationType); ++ boolean casAnnotationType(Class<?> klass, AnnotationType oldType, AnnotationType newType); + + /** + * Get the AnnotationType instance corresponding to this class. +@@ -47,6 +47,12 @@ + AnnotationType getAnnotationType(Class klass); + + /** ++ * Get the array of bytes that is the class-file representation ++ * of this Class' annotations. ++ */ ++ byte[] getRawClassAnnotations(Class<?> klass); ++ ++ /** + * Returns the elements of an enum class or null if the + * Class object does not represent an enum type; + * the result is uncloned, cached, and shared by all callers. +diff -r d1f592073a0e src/share/classes/sun/reflect/ReflectionFactory.java +--- openjdk/jdk/src/share/classes/sun/reflect/ReflectionFactory.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/sun/reflect/ReflectionFactory.java Thu Oct 02 20:18:56 2014 +0100 +@@ -84,8 +84,8 @@ + * <code>AccessController.doPrivileged</code>. + */ + public static final class GetReflectionFactoryAction +- implements PrivilegedAction { +- public Object run() { ++ implements PrivilegedAction<ReflectionFactory> { ++ public ReflectionFactory run() { + return getReflectionFactory(); + } + } +@@ -164,7 +164,7 @@ + public ConstructorAccessor newConstructorAccessor(Constructor c) { + checkInitted(); + +- Class declaringClass = c.getDeclaringClass(); ++ Class<?> declaringClass = c.getDeclaringClass(); + if (Modifier.isAbstract(declaringClass.getModifiers())) { + return new InstantiationExceptionConstructorAccessorImpl(null); + } +@@ -203,10 +203,10 @@ + // + + /** Creates a new java.lang.reflect.Field. Access checks as per +- java.lang.reflect.AccessibleObject are not overridden. */ +- public Field newField(Class declaringClass, ++ java.lang.reflect.AccessibleObject are not overridden. */ ++ public Field newField(Class<?> declaringClass, + String name, +- Class type, ++ Class<?> type, + int modifiers, + int slot, + String signature, +@@ -223,11 +223,11 @@ + + /** Creates a new java.lang.reflect.Method. Access checks as per + java.lang.reflect.AccessibleObject are not overridden. */ +- public Method newMethod(Class declaringClass, ++ public Method newMethod(Class<?> declaringClass, + String name, +- Class[] parameterTypes, +- Class returnType, +- Class[] checkedExceptions, ++ Class<?>[] parameterTypes, ++ Class<?> returnType, ++ Class<?>[] checkedExceptions, + int modifiers, + int slot, + String signature, +@@ -250,9 +250,9 @@ + + /** Creates a new java.lang.reflect.Constructor. Access checks as + per java.lang.reflect.AccessibleObject are not overridden. */ +- public Constructor newConstructor(Class declaringClass, +- Class[] parameterTypes, +- Class[] checkedExceptions, ++ public Constructor newConstructor(Class<?> declaringClass, ++ Class<?>[] parameterTypes, ++ Class<?>[] checkedExceptions, + int modifiers, + int slot, + String signature, +@@ -310,7 +310,7 @@ + /** Makes a copy of the passed constructor. The returned + constructor is a "child" of the passed one; see the comments + in Constructor.java for details. */ +- public Constructor copyConstructor(Constructor arg) { ++ public <T> Constructor<T> copyConstructor(Constructor<T> arg) { + return langReflectAccess().copyConstructor(arg); + } + +@@ -321,7 +321,7 @@ + // + + public Constructor newConstructorForSerialization +- (Class classToInstantiate, Constructor constructorToCall) ++ (Class<?> classToInstantiate, Constructor constructorToCall) + { + // Fast path + if (constructorToCall.getDeclaringClass() == classToInstantiate) { +@@ -366,8 +366,9 @@ + run, before the system properties are set up. */ + private static void checkInitted() { + if (initted) return; +- AccessController.doPrivileged(new PrivilegedAction() { +- public Object run() { ++ AccessController.doPrivileged( ++ new PrivilegedAction<Void>() { ++ public Void run() { + // Tests to ensure the system properties table is fully + // initialized. This is needed because reflection code is + // called very early in the initialization process (before +diff -r d1f592073a0e src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java +--- openjdk/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java Thu Oct 02 20:18:56 2014 +0100 +@@ -41,17 +41,17 @@ + */ + class AnnotationInvocationHandler implements InvocationHandler, Serializable { + private static final long serialVersionUID = 6182022883658399397L; +- private final Class type; ++ private final Class<? extends Annotation> type; + private final Map<String, Object> memberValues; + +- AnnotationInvocationHandler(Class type, Map<String, Object> memberValues) { ++ AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { + this.type = type; + this.memberValues = memberValues; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String member = method.getName(); +- Class[] paramTypes = method.getParameterTypes(); ++ Class<?>[] paramTypes = method.getParameterTypes(); + + // Handle Object and Annotation methods + if (member.equals("equals") && paramTypes.length == 1 && +@@ -85,7 +85,7 @@ + * if Cloneable had a public clone method. + */ + private Object cloneArray(Object array) { +- Class type = array.getClass(); ++ Class<?> type = array.getClass(); + + if (type == byte[].class) { + byte[] byteArray = (byte[])array; +@@ -152,7 +152,7 @@ + * Translates a member value (in "dynamic proxy return form") into a string + */ + private static String memberValueToString(Object value) { +- Class type = value.getClass(); ++ Class<?> type = value.getClass(); + if (!type.isArray()) // primitive, string, class, enum const, + // or annotation + return value.toString(); +@@ -230,7 +230,7 @@ + * two members are identical object references. + */ + private static boolean memberValueEquals(Object v1, Object v2) { +- Class type = v1.getClass(); ++ Class<?> type = v1.getClass(); + + // Check for primitive, string, class, enum const, annotation, + // or ExceptionProxy +@@ -302,7 +302,7 @@ + * Computes hashCode of a member value (in "dynamic proxy return form") + */ + private static int memberValueHashCode(Object value) { +- Class type = value.getClass(); ++ Class<?> type = value.getClass(); + if (!type.isArray()) // primitive, string, class, enum const, + // or annotation + return value.hashCode(); +@@ -341,14 +341,14 @@ + throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); + } + +- Map<String, Class> memberTypes = annotationType.memberTypes(); ++ Map<String, Class<?>> memberTypes = annotationType.memberTypes(); + + + // If there are annotation members without values, that + // situation is handled by the invoke method. + for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) { + String name = memberValue.getKey(); +- Class memberType = memberTypes.get(name); ++ Class<?> memberType = memberTypes.get(name); + if (memberType != null) { // i.e. member still exists + Object value = memberValue.getValue(); + if (!(memberType.isInstance(value) || +diff -r d1f592073a0e src/share/classes/sun/reflect/annotation/AnnotationParser.java +--- openjdk/jdk/src/share/classes/sun/reflect/annotation/AnnotationParser.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/sun/reflect/annotation/AnnotationParser.java Thu Oct 02 20:18:56 2014 +0100 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -59,15 +59,15 @@ + * @throws AnnotationFormatError if an annotation is found to be + * malformed. + */ +- public static Map<Class, Annotation> parseAnnotations( ++ public static Map<Class<? extends Annotation>, Annotation> parseAnnotations( + byte[] rawAnnotations, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + if (rawAnnotations == null) + return Collections.emptyMap(); + + try { +- return parseAnnotations2(rawAnnotations, constPool, container); ++ return parseAnnotations2(rawAnnotations, constPool, container, null); + } catch(BufferUnderflowException e) { + throw new AnnotationFormatError("Unexpected end of annotations."); + } catch(IllegalArgumentException e) { +@@ -76,24 +76,53 @@ + } + } + +- private static Map<Class, Annotation> parseAnnotations2( ++ /** ++ * Like {@link #parseAnnotations(byte[], sun.reflect.ConstantPool, Class)} ++ * with an additional parameter {@code selectAnnotationClasses} which selects the ++ * annotation types to parse (other than selected are quickly skipped).<p> ++ * This method is only used to parse select meta annotations in the construction ++ * phase of {@link AnnotationType} instances to prevent infinite recursion. ++ * ++ * @param selectAnnotationClasses an array of annotation types to select when parsing ++ */ ++ static Map<Class<? extends Annotation>, Annotation> parseSelectAnnotations( + byte[] rawAnnotations, + ConstantPool constPool, +- Class container) { +- Map<Class, Annotation> result = new LinkedHashMap<Class, Annotation>(); ++ Class<?> container, ++ Class<? extends Annotation> ... selectAnnotationClasses) { ++ if (rawAnnotations == null) ++ return Collections.emptyMap(); ++ ++ try { ++ return parseAnnotations2(rawAnnotations, constPool, container, selectAnnotationClasses); ++ } catch(BufferUnderflowException e) { ++ throw new AnnotationFormatError("Unexpected end of annotations."); ++ } catch(IllegalArgumentException e) { ++ // Type mismatch in constant pool ++ throw new AnnotationFormatError(e); ++ } ++ } ++ ++ private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2( ++ byte[] rawAnnotations, ++ ConstantPool constPool, ++ Class<?> container, ++ Class<? extends Annotation>[] selectAnnotationClasses) { ++ Map<Class<? extends Annotation>, Annotation> result = ++ new LinkedHashMap<Class<? extends Annotation>, Annotation>(); + ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); + int numAnnotations = buf.getShort() & 0xFFFF; + for (int i = 0; i < numAnnotations; i++) { +- Annotation a = parseAnnotation(buf, constPool, container, false); ++ Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses); + if (a != null) { +- Class klass = a.annotationType(); +- AnnotationType type = AnnotationType.getInstance(klass); +- if (type.retention() == RetentionPolicy.RUNTIME) +- if (result.put(klass, a) != null) ++ Class<? extends Annotation> klass = a.annotationType(); ++ if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME && ++ result.put(klass, a) != null) { + throw new AnnotationFormatError( + "Duplicate annotation for class: "+klass+": " + a); + } + } ++ } + return result; + } + +@@ -123,7 +152,7 @@ + public static Annotation[][] parseParameterAnnotations( + byte[] rawAnnotations, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + try { + return parseParameterAnnotations2(rawAnnotations, constPool, container); + } catch(BufferUnderflowException e) { +@@ -138,7 +167,7 @@ + private static Annotation[][] parseParameterAnnotations2( + byte[] rawAnnotations, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); + int numParameters = buf.get() & 0xFF; + Annotation[][] result = new Annotation[numParameters][]; +@@ -188,15 +217,24 @@ + */ + private static Annotation parseAnnotation(ByteBuffer buf, + ConstantPool constPool, +- Class container, ++ Class<?> container, + boolean exceptionOnMissingAnnotationClass) { ++ return parseAnnotation2(buf, constPool, container, exceptionOnMissingAnnotationClass, null); ++ } ++ ++ @SuppressWarnings("unchecked") ++ private static Annotation parseAnnotation2(ByteBuffer buf, ++ ConstantPool constPool, ++ Class<?> container, ++ boolean exceptionOnMissingAnnotationClass, ++ Class<? extends Annotation>[] selectAnnotationClasses) { + int typeIndex = buf.getShort() & 0xFFFF; +- Class annotationClass = null; ++ Class<? extends Annotation> annotationClass = null; + String sig = "[unknown]"; + try { + try { + sig = constPool.getUTF8At(typeIndex); +- annotationClass = parseSig(sig, container); ++ annotationClass = (Class<? extends Annotation>)parseSig(sig, container); + } catch (IllegalArgumentException ex) { + // support obsolete early jsr175 format class files + annotationClass = constPool.getClassAt(typeIndex); +@@ -215,6 +253,10 @@ + skipAnnotation(buf, false); + return null; + } ++ if (selectAnnotationClasses != null && !contains(selectAnnotationClasses, annotationClass)) { ++ skipAnnotation(buf, false); ++ return null; ++ } + AnnotationType type = null; + try { + type = AnnotationType.getInstance(annotationClass); +@@ -223,7 +265,7 @@ + return null; + } + +- Map<String, Class> memberTypes = type.memberTypes(); ++ Map<String, Class<?>> memberTypes = type.memberTypes(); + Map<String, Object> memberValues = + new LinkedHashMap<String, Object>(type.memberDefaults()); + +@@ -231,7 +273,7 @@ + for (int i = 0; i < numMembers; i++) { + int memberNameIndex = buf.getShort() & 0xFFFF; + String memberName = constPool.getUTF8At(memberNameIndex); +- Class memberType = memberTypes.get(memberName); ++ Class<?> memberType = memberTypes.get(memberName); + + if (memberType == null) { + // Member is no longer present in annotation type; ignore it +@@ -252,7 +294,7 @@ + * member -> value map. + */ + public static Annotation annotationForMap( +- Class type, Map<String, Object> memberValues) ++ Class<? extends Annotation> type, Map<String, Object> memberValues) + { + return (Annotation) Proxy.newProxyInstance( + type.getClassLoader(), new Class[] { type }, +@@ -286,14 +328,15 @@ + * The member must be of the indicated type. If it is not, this + * method returns an AnnotationTypeMismatchExceptionProxy. + */ +- public static Object parseMemberValue(Class memberType, ByteBuffer buf, ++ public static Object parseMemberValue(Class<?> memberType, ++ ByteBuffer buf, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + Object result = null; + int tag = buf.get(); + switch(tag) { + case 'e': +- return parseEnumValue(memberType, buf, constPool, container); ++ return parseEnumValue((Class<? extends Enum<?>>)memberType, buf, constPool, container); + case 'c': + result = parseClassValue(buf, constPool, container); + break; +@@ -361,7 +404,7 @@ + */ + private static Object parseClassValue(ByteBuffer buf, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + int classIndex = buf.getShort() & 0xFFFF; + try { + try { +@@ -379,7 +422,7 @@ + } + } + +- private static Class<?> parseSig(String sig, Class container) { ++ private static Class<?> parseSig(String sig, Class<?> container) { + if (sig.equals("V")) return void.class; + SignatureParser parser = SignatureParser.make(); + TypeSignature typeSig = parser.parseTypeSig(sig); +@@ -389,7 +432,7 @@ + Type result = reify.getResult(); + return toClass(result); + } +- static Class toClass(Type o) { ++ static Class<?> toClass(Type o) { + if (o instanceof GenericArrayType) + return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()), + 0) +@@ -409,9 +452,9 @@ + * u2 const_name_index; + * } enum_const_value; + */ +- private static Object parseEnumValue(Class enumType, ByteBuffer buf, ++ private static Object parseEnumValue(Class<? extends Enum> enumType, ByteBuffer buf, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + int typeNameIndex = buf.getShort() & 0xFFFF; + String typeName = constPool.getUTF8At(typeNameIndex); + int constNameIndex = buf.getShort() & 0xFFFF; +@@ -449,12 +492,12 @@ + * If the array values do not match arrayType, an + * AnnotationTypeMismatchExceptionProxy will be returned. + */ +- private static Object parseArray(Class arrayType, ++ private static Object parseArray(Class<?> arrayType, + ByteBuffer buf, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + int length = buf.getShort() & 0xFFFF; // Number of array components +- Class componentType = arrayType.getComponentType(); ++ Class<?> componentType = arrayType.getComponentType(); + + if (componentType == byte.class) { + return parseByteArray(length, buf, constPool); +@@ -477,11 +520,11 @@ + } else if (componentType == Class.class) { + return parseClassArray(length, buf, constPool, container); + } else if (componentType.isEnum()) { +- return parseEnumArray(length, componentType, buf, ++ return parseEnumArray(length, (Class<? extends Enum>)componentType, buf, + constPool, container); + } else { + assert componentType.isAnnotation(); +- return parseAnnotationArray(length, componentType, buf, ++ return parseAnnotationArray(length, (Class <? extends Annotation>)componentType, buf, + constPool, container); + } + } +@@ -660,8 +703,8 @@ + private static Object parseClassArray(int length, + ByteBuffer buf, + ConstantPool constPool, +- Class container) { +- Object[] result = new Class[length]; ++ Class<?> container) { ++ Object[] result = new Class<?>[length]; + boolean typeMismatch = false; + int tag = 0; + +@@ -677,10 +720,10 @@ + return typeMismatch ? exceptionProxy(tag) : result; + } + +- private static Object parseEnumArray(int length, Class enumType, ++ private static Object parseEnumArray(int length, Class<? extends Enum> enumType, + ByteBuffer buf, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + Object[] result = (Object[]) Array.newInstance(enumType, length); + boolean typeMismatch = false; + int tag = 0; +@@ -698,10 +741,10 @@ + } + + private static Object parseAnnotationArray(int length, +- Class annotationType, ++ Class<? extends Annotation> annotationType, + ByteBuffer buf, + ConstantPool constPool, +- Class container) { ++ Class<?> container) { + Object[] result = (Object[]) Array.newInstance(annotationType, length); + boolean typeMismatch = false; + int tag = 0; +@@ -788,4 +831,16 @@ + for (int i = 0; i < length; i++) + skipMemberValue(buf); + } ++ ++ /** ++ * Searches for given {@code element} in given {@code array} by identity. ++ * Returns {@code true} if found {@code false} if not. ++ */ ++ private static boolean contains(Object[] array, Object element) { ++ for (Object e : array) ++ if (e == element) ++ return true; ++ return false; ++ } ++ + } +diff -r d1f592073a0e src/share/classes/sun/reflect/annotation/AnnotationType.java +--- openjdk/jdk/src/share/classes/sun/reflect/annotation/AnnotationType.java Fri Sep 12 22:39:32 2014 +0100 ++++ openjdk/jdk/src/share/classes/sun/reflect/annotation/AnnotationType.java Thu Oct 02 20:18:56 2014 +0100 +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it +@@ -25,6 +25,8 @@ + + package sun.reflect.annotation; + ++import sun.misc.JavaLangAccess; ++ + import java.lang.annotation.*; + import java.lang.reflect.*; + import java.util.*; +@@ -45,29 +47,28 @@ + * types. This matches the return value that must be used for a + * dynamic proxy, allowing for a simple isInstance test. + */ +- private final Map<String, Class> memberTypes = new HashMap<String,Class>(); ++ private final Map<String, Class<?>> memberTypes; + + /** + * Member name -> default value mapping. + */ +- private final Map<String, Object> memberDefaults = +- new HashMap<String, Object>(); ++ private final Map<String, Object> memberDefaults; + + /** +- * Member name -> Method object mapping. This (and its assoicated ++ * Member name -> Method object mapping. This (and its associated + * accessor) are used only to generate AnnotationTypeMismatchExceptions. + */ +- private final Map<String, Method> members = new HashMap<String, Method>(); ++ private final Map<String, Method> members; + + /** + * The retention policy for this annotation type. + */ +- private RetentionPolicy retention = RetentionPolicy.RUNTIME;; ++ private final RetentionPolicy retention; + + /** + * Whether this annotation type is inherited. + */ +- private boolean inherited = false; ++ private final boolean inherited; + + /** + * Returns an AnnotationType instance for the specified annotation type. +@@ -75,13 +76,20 @@ + * @throw IllegalArgumentException if the specified class object for + * does not represent a valid annotation type + */ +- public static synchronized AnnotationType getInstance( +- Class annotationClass) ++ public static AnnotationType getInstance( ++ Class<? extends Annotation> annotationClass) + { +- AnnotationType result = sun.misc.SharedSecrets.getJavaLangAccess(). +- getAnnotationType(annotationClass); +- if (result == null) +- result = new AnnotationType((Class<?>) annotationClass); ++ JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess(); ++ AnnotationType result = jla.getAnnotationType(annotationClass); // volatile read ++ if (result == null) { ++ result = new AnnotationType(annotationClass); ++ // try to CAS the AnnotationType: null -> result ++ if (!jla.casAnnotationType(annotationClass, null, result)) { ++ // somebody was quicker -> read its result ++ result = jla.getAnnotationType(annotationClass); ++ assert result != null; ++ } ++ } + + return result; + } +@@ -93,7 +101,7 @@ + * @throw IllegalArgumentException if the specified class object for + * does not represent a valid annotation type + */ +- private AnnotationType(final Class<?> annotationClass) { ++ private AnnotationType(final Class<? extends Annotation> annotationClass) { + if (!annotationClass.isAnnotation()) + throw new IllegalArgumentException("Not an annotation type"); + +@@ -105,32 +113,42 @@ + } + }); + ++ memberTypes = new HashMap<String,Class<?>>(methods.length+1, 1.0f); ++ memberDefaults = new HashMap<String, Object>(0); ++ members = new HashMap<String, Method>(methods.length+1, 1.0f); + + for (Method method : methods) { + if (method.getParameterTypes().length != 0) + throw new IllegalArgumentException(method + " has params"); + String name = method.getName(); +- Class type = method.getReturnType(); ++ Class<?> type = method.getReturnType(); + memberTypes.put(name, invocationHandlerReturnType(type)); + members.put(name, method); + + Object defaultValue = method.getDefaultValue(); + if (defaultValue != null) + memberDefaults.put(name, defaultValue); +- +- members.put(name, method); + } + +- sun.misc.SharedSecrets.getJavaLangAccess(). +- setAnnotationType(annotationClass, this); +- + // Initialize retention, & inherited fields. Special treatment + // of the corresponding annotation types breaks infinite recursion. + if (annotationClass != Retention.class && + annotationClass != Inherited.class) { +- Retention ret = annotationClass.getAnnotation(Retention.class); ++ JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess(); ++ Map<Class<? extends Annotation>, Annotation> metaAnnotations = ++ AnnotationParser.parseSelectAnnotations( ++ jla.getRawClassAnnotations(annotationClass), ++ jla.getConstantPool(annotationClass), ++ annotationClass, ++ Retention.class, Inherited.class ++ ); ++ Retention ret = (Retention) metaAnnotations.get(Retention.class); + retention = (ret == null ? RetentionPolicy.CLASS : ret.value()); +- inherited = annotationClass.isAnnotationPresent(Inherited.class); ++ inherited = metaAnnotations.containsKey(Inherited.class); ++ } ++ else { ++ retention = RetentionPolicy.RUNTIME; ++ inherited = false; + } + } + +@@ -140,7 +158,7 @@ + * the specified type (which is assumed to be a legal member type + * for an annotation). + */ +- public static Class invocationHandlerReturnType(Class type) { ++ public static Class<?> invocationHandlerReturnType(Class<?> type) { + // Translate primitives to wrappers + if (type == byte.class) + return Byte.class; +@@ -167,7 +185,7 @@ + * Returns member types for this annotation type + * (member name -> type mapping). + */ +- public Map<String, Class> memberTypes() { ++ public Map<String, Class<?>> memberTypes() { + return memberTypes; + } + +@@ -205,11 +223,10 @@ + * For debugging. + */ + public String toString() { +- StringBuffer s = new StringBuffer("Annotation Type:" + "\n"); +- s.append(" Member types: " + memberTypes + "\n"); +- s.append(" Member defaults: " + memberDefaults + "\n"); +- s.append(" Retention policy: " + retention + "\n"); +- s.append(" Inherited: " + inherited); +- return s.toString(); ++ return "Annotation Type:\n" + ++ " Member types: " + memberTypes + "\n" + ++ " Member defaults: " + memberDefaults + "\n" + ++ " Retention policy: " + retention + "\n" + ++ " Inherited: " + inherited; + } + } +diff -r d1f592073a0e test/java/lang/annotation/AnnotationType/AnnotationTypeDeadlockTest.java +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ openjdk/jdk/test/java/lang/annotation/AnnotationType/AnnotationTypeDeadlockTest.java Thu Oct 02 20:18:56 2014 +0100 +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7122142 ++ * @summary Test deadlock situation when recursive annotations are parsed ++ */ ++ ++import java.lang.annotation.Retention; ++import java.lang.management.ManagementFactory; ++import java.lang.management.ThreadInfo; ++import java.lang.management.ThreadMXBean; ++import java.util.concurrent.CountDownLatch; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++import static java.lang.annotation.RetentionPolicy.RUNTIME; ++ ++public class AnnotationTypeDeadlockTest { ++ ++ @Retention(RUNTIME) ++ @AnnB ++ public @interface AnnA { ++ } ++ ++ @Retention(RUNTIME) ++ @AnnA ++ public @interface AnnB { ++ } ++ ++ static class Task extends Thread { ++ final CountDownLatch prepareLatch; ++ final AtomicInteger goLatch; ++ final Class<?> clazz; ++ ++ Task(CountDownLatch prepareLatch, AtomicInteger goLatch, Class<?> clazz) { ++ super(clazz.getSimpleName()); ++ setDaemon(true); // in case it deadlocks ++ this.prepareLatch = prepareLatch; ++ this.goLatch = goLatch; ++ this.clazz = clazz; ++ } ++ ++ @Override ++ public void run() { ++ prepareLatch.countDown(); // notify we are prepared ++ while (goLatch.get() > 0); // spin-wait before go ++ clazz.getDeclaredAnnotations(); ++ } ++ } ++ ++ public static void main(String[] args) throws Exception { ++ CountDownLatch prepareLatch = new CountDownLatch(2); ++ AtomicInteger goLatch = new AtomicInteger(1); ++ Task taskA = new Task(prepareLatch, goLatch, AnnA.class); ++ Task taskB = new Task(prepareLatch, goLatch, AnnB.class); ++ taskA.start(); ++ taskB.start(); ++ // wait until both threads start-up ++ prepareLatch.await(); ++ // let them go ++ goLatch.set(0); ++ // obtain ThreadMXBean ++ ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); ++ // wait for threads to finish or dead-lock ++ while (taskA.isAlive() || taskB.isAlive()) { ++ // attempt to join threads ++ taskA.join(500L); ++ taskB.join(500L); ++ // detect dead-lock ++ long[] deadlockedIds = threadBean.findMonitorDeadlockedThreads(); ++ if (deadlockedIds != null && deadlockedIds.length > 0) { ++ StringBuilder sb = new StringBuilder("deadlock detected:\n\n"); ++ for (ThreadInfo ti : threadBean.getThreadInfo(deadlockedIds, Integer.MAX_VALUE)) { ++ sb.append(ti); ++ } ++ throw new IllegalStateException(sb.toString()); ++ } ++ } ++ } ++} +diff -r d1f592073a0e test/java/lang/annotation/AnnotationType/AnnotationTypeRuntimeAssumptionTest.java +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ openjdk/jdk/test/java/lang/annotation/AnnotationType/AnnotationTypeRuntimeAssumptionTest.java Thu Oct 02 20:18:56 2014 +0100 +@@ -0,0 +1,187 @@ ++/* ++ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. ++ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ++ * ++ * This code is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 only, as ++ * published by the Free Software Foundation. ++ * ++ * This code is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * version 2 for more details (a copy is included in the LICENSE file that ++ * accompanied this code). ++ * ++ * You should have received a copy of the GNU General Public License version ++ * 2 along with this work; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ++ * or visit www.oracle.com if you need additional information or have any ++ * questions. ++ */ ++ ++/* ++ * @test ++ * @bug 7122142 ++ * @summary Test consistent parsing of ex-RUNTIME annotations that ++ * were changed and separately compiled to have CLASS retention ++ */ ++ ++import sun.misc.IOUtils; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.lang.annotation.Annotation; ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++ ++import static java.lang.annotation.RetentionPolicy.CLASS; ++import static java.lang.annotation.RetentionPolicy.RUNTIME; ++ ++/** ++ * This test simulates a situation where there are two mutually recursive ++ * {@link RetentionPolicy#RUNTIME RUNTIME} annotations {@link AnnA_v1 AnnA_v1} ++ * and {@link AnnB AnnB} and then the first is changed to have ++ * {@link RetentionPolicy#CLASS CLASS} retention and separately compiled. ++ * When {@link AnnA_v1 AnnA_v1} annotation is looked-up on {@link AnnB AnnB} ++ * it still appears to have {@link RetentionPolicy#RUNTIME RUNTIME} retention. ++ */ ++public class AnnotationTypeRuntimeAssumptionTest { ++ ++ @Retention(RUNTIME) ++ @AnnB ++ public @interface AnnA_v1 { ++ } ++ ++ // An alternative version of AnnA_v1 with CLASS retention instead. ++ // Used to simulate separate compilation (see AltClassLoader below). ++ @Retention(CLASS) ++ @AnnB ++ public @interface AnnA_v2 { ++ } ++ ++ @Retention(RUNTIME) ++ @AnnA_v1 ++ public @interface AnnB { ++ } ++ ++ @AnnA_v1 ++ public static class TestTask implements Runnable { ++ @Override ++ public void run() { ++ AnnA_v1 ann1 = getDeclaredAnnotation(TestTask.class, AnnA_v1.class); ++ if (ann1 != null) { ++ throw new IllegalStateException( ++ "@" + ann1.annotationType().getSimpleName() + ++ " found on: " + TestTask.class.getName() + ++ " should not be visible at runtime"); ++ } ++ AnnA_v1 ann2 = getDeclaredAnnotation(AnnB.class, AnnA_v1.class); ++ if (ann2 != null) { ++ throw new IllegalStateException( ++ "@" + ann2.annotationType().getSimpleName() + ++ " found on: " + AnnB.class.getName() + ++ " should not be visible at runtime"); ++ } ++ } ++ ++ private static <A extends Annotation> A getDeclaredAnnotation(Class<?> clazz, Class<A> annotationClass) { ++ for (Annotation ann : clazz.getDeclaredAnnotations()) { ++ if (ann.annotationType() == annotationClass) { ++ return annotationClass.cast(ann); ++ } ++ } ++ return null; ++ } ++ } ++ ++ public static void main(String[] args) throws Exception { ++ ClassLoader altLoader = new AltClassLoader( ++ AnnotationTypeRuntimeAssumptionTest.class.getClassLoader()); ++ ++ Runnable altTask = (Runnable) Class.forName( ++ TestTask.class.getName(), ++ true, ++ altLoader).newInstance(); ++ ++ altTask.run(); ++ } ++ ++ /** ++ * A ClassLoader implementation that loads alternative implementations of ++ * classes. If class name ends with "_v1" it locates instead a class with ++ * name ending with "_v2" and loads that class instead. ++ */ ++ static class AltClassLoader extends ClassLoader { ++ AltClassLoader(ClassLoader parent) { ++ super(parent); ++ } ++ ++ @Override ++ protected Class<?> loadClass(String name, boolean resolve) ++ throws ClassNotFoundException { ++ if (name.indexOf('.') < 0) { // root package is our class ++ synchronized (getClassLoadingLock(name)) { ++ // First, check if the class has already been loaded ++ Class<?> c = findLoadedClass(name); ++ if (c == null) { ++ c = findClass(name); ++ } ++ if (resolve) { ++ resolveClass(c); ++ } ++ return c; ++ } ++ } ++ else { // not our class ++ return super.loadClass(name, resolve); ++ } ++ } ++ ++ @Override ++ protected Class<?> findClass(String name) ++ throws ClassNotFoundException { ++ // special class name -> replace it with alternative name ++ if (name.endsWith("_v1")) { ++ String altName = name.substring(0, name.length() - 3) + "_v2"; ++ String altPath = altName.replace('.', '/').concat(".class"); ++ try (InputStream is = getResourceAsStream(altPath)) { ++ if (is != null) { ++ byte[] bytes = IOUtils.readFully(is, -1, true); ++ // patch class bytes to contain original name ++ for (int i = 0; i < bytes.length - 2; i++) { ++ if (bytes[i] == '_' && ++ bytes[i + 1] == 'v' && ++ bytes[i + 2] == '2') { ++ bytes[i + 2] = '1'; ++ } ++ } ++ return defineClass(name, bytes, 0, bytes.length); ++ } ++ else { ++ throw new ClassNotFoundException(name); ++ } ++ } ++ catch (IOException e) { ++ throw new ClassNotFoundException(name, e); ++ } ++ } ++ else { // not special class name -> just load the class ++ String path = name.replace('.', '/').concat(".class"); ++ try (InputStream is = getResourceAsStream(path)) { ++ if (is != null) { ++ byte[] bytes = IOUtils.readFully(is, -1, true); ++ return defineClass(name, bytes, 0, bytes.length); ++ } ++ else { ++ throw new ClassNotFoundException(name); ++ } ++ } ++ catch (IOException e) { ++ throw new ClassNotFoundException(name, e); ++ } ++ } ++ } ++ } ++}