# HG changeset patch # User malenkov # Date 1343663193 -14400 # Node ID 2ed3ec2efd5d37886f7ea06789a6658acd9f7afe # Parent c22497df213e4df8878648d5efbd22d6f7387a94 7187618: PropertyDescriptor Performance Slow Reviewed-by: rupashka diff -r c22497df213e -r 2ed3ec2efd5d src/share/classes/com/sun/beans/TypeResolver.java --- a/src/share/classes/com/sun/beans/TypeResolver.java Mon Jul 30 16:38:53 2012 +0400 +++ b/src/share/classes/com/sun/beans/TypeResolver.java Mon Jul 30 19:46:33 2012 +0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -45,6 +45,9 @@ * @author Sergey Malenkov */ public final class TypeResolver { + + private static final WeakCache> CACHE = new WeakCache<>(); + /** * Replaces the given {@code type} in an inherited method * with the actual type it has in the given {@code inClass}. @@ -149,12 +152,55 @@ * @param formal the type where occurrences of the variables * in {@code actual} will be replaced by the corresponding bound values * @return a resolved type - * - * @see #TypeResolver(Type) - * @see #resolve(Type) */ public static Type resolve(Type actual, Type formal) { - return getTypeResolver(actual).resolve(formal); + if (formal instanceof Class) { + return formal; + } + if (formal instanceof GenericArrayType) { + Type comp = ((GenericArrayType) formal).getGenericComponentType(); + comp = resolve(actual, comp); + return (comp instanceof Class) + ? Array.newInstance((Class) comp, 0).getClass() + : GenericArrayTypeImpl.make(comp); + } + if (formal instanceof ParameterizedType) { + ParameterizedType fpt = (ParameterizedType) formal; + Type[] actuals = resolve(actual, fpt.getActualTypeArguments()); + return ParameterizedTypeImpl.make( + (Class) fpt.getRawType(), actuals, fpt.getOwnerType()); + } + if (formal instanceof WildcardType) { + WildcardType fwt = (WildcardType) formal; + Type[] upper = resolve(actual, fwt.getUpperBounds()); + Type[] lower = resolve(actual, fwt.getLowerBounds()); + return new WildcardTypeImpl(upper, lower); + } + if (formal instanceof TypeVariable) { + Map map; + synchronized (CACHE) { + map = CACHE.get(actual); + if (map == null) { + map = new HashMap<>(); + prepare(map, actual); + CACHE.put(actual, map); + } + } + Type result = map.get(formal); + if (result == null || result.equals(formal)) { + return formal; + } + result = fixGenericArray(result); + // A variable can be bound to another variable that is itself bound + // to something. For example, given: + // class Super {...} + // class Mid extends Super {...} + // class Sub extends Mid + // the variable T is bound to X, which is in turn bound to String. + // So if we have to resolve T, we need the tail recursion here. + return resolve(actual, result); + } + throw new IllegalArgumentException("Bad Type kind: " + formal.getClass()); } /** @@ -164,12 +210,14 @@ * @param actual the type that supplies bindings for type variables * @param formals the array of types to resolve * @return an array of resolved types - * - * @see #TypeResolver(Type) - * @see #resolve(Type[]) */ public static Type[] resolve(Type actual, Type[] formals) { - return getTypeResolver(actual).resolve(formals); + int length = formals.length; + Type[] actuals = new Type[length]; + for (int i = 0; i < length; i++) { + actuals[i] = resolve(actual, formals[i]); + } + return actuals; } /** @@ -228,32 +276,6 @@ return classes; } - public static TypeResolver getTypeResolver(Type type) { - synchronized (CACHE) { - TypeResolver resolver = CACHE.get(type); - if (resolver == null) { - resolver = new TypeResolver(type); - CACHE.put(type, resolver); - } - return resolver; - } - } - - private static final WeakCache CACHE = new WeakCache<>(); - - private final Map, Type> map = new HashMap<>(); - - /** - * Constructs the type resolver for the given actual type. - * - * @param actual the type that supplies bindings for type variables - * - * @see #prepare(Type) - */ - private TypeResolver(Type actual) { - prepare(actual); - } - /** * Fills the map from type parameters * to types as seen by the given {@code type}. @@ -265,9 +287,10 @@ * to a {@link ParameterizedType ParameterizedType} with no parameters, * or it represents the erasure of a {@link ParameterizedType ParameterizedType}. * + * @param map the mappings of all type variables * @param type the next type in the hierarchy */ - private void prepare(Type type) { + private static void prepare(Map map, Type type) { Class raw = (Class)((type instanceof Class) ? type : ((ParameterizedType)type).getRawType()); @@ -280,91 +303,25 @@ assert formals.length == actuals.length; for (int i = 0; i < formals.length; i++) { - this.map.put(formals[i], actuals[i]); + map.put(formals[i], actuals[i]); } Type gSuperclass = raw.getGenericSuperclass(); if (gSuperclass != null) { - prepare(gSuperclass); + prepare(map, gSuperclass); } for (Type gInterface : raw.getGenericInterfaces()) { - prepare(gInterface); + prepare(map, gInterface); } // If type is the raw version of a parameterized class, we type-erase // all of its type variables, including inherited ones. if (type instanceof Class && formals.length > 0) { - for (Map.Entry, Type> entry : this.map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { entry.setValue(erase(entry.getValue())); } } } /** - * Replaces the given {@code formal} type - * with the type it stand for in this type resolver. - * - * @param formal the array of types to resolve - * @return a resolved type - */ - private Type resolve(Type formal) { - if (formal instanceof Class) { - return formal; - } - if (formal instanceof GenericArrayType) { - Type comp = ((GenericArrayType)formal).getGenericComponentType(); - comp = resolve(comp); - return (comp instanceof Class) - ? Array.newInstance((Class)comp, 0).getClass() - : GenericArrayTypeImpl.make(comp); - } - if (formal instanceof ParameterizedType) { - ParameterizedType fpt = (ParameterizedType)formal; - Type[] actuals = resolve(fpt.getActualTypeArguments()); - return ParameterizedTypeImpl.make( - (Class)fpt.getRawType(), actuals, fpt.getOwnerType()); - } - if (formal instanceof WildcardType) { - WildcardType fwt = (WildcardType)formal; - Type[] upper = resolve(fwt.getUpperBounds()); - Type[] lower = resolve(fwt.getLowerBounds()); - return new WildcardTypeImpl(upper, lower); - } - if (!(formal instanceof TypeVariable)) { - throw new IllegalArgumentException("Bad Type kind: " + formal.getClass()); - } - Type actual = this.map.get((TypeVariable) formal); - if (actual == null || actual.equals(formal)) { - return formal; - } - actual = fixGenericArray(actual); - return resolve(actual); - // A variable can be bound to another variable that is itself bound - // to something. For example, given: - // class Super {...} - // class Mid extends Super {...} - // class Sub extends Mid - // the variable T is bound to X, which is in turn bound to String. - // So if we have to resolve T, we need the tail recursion here. - } - - /** - * Replaces all formal types in the given array - * with the types they stand for in this type resolver. - * - * @param formals the array of types to resolve - * @return an array of resolved types - * - * @see #resolve(Type) - */ - private Type[] resolve(Type[] formals) { - int length = formals.length; - Type[] actuals = new Type[length]; - for (int i = 0; i < length; i++) { - actuals[i] = resolve(formals[i]); - } - return actuals; - } - - /** * Replaces a {@link GenericArrayType GenericArrayType} * with plain array class where it is possible. * Bug 5041784 diff -r c22497df213e -r 2ed3ec2efd5d src/share/classes/com/sun/beans/finder/MethodFinder.java --- a/src/share/classes/com/sun/beans/finder/MethodFinder.java Mon Jul 30 16:38:53 2012 +0400 +++ b/src/share/classes/com/sun/beans/finder/MethodFinder.java Mon Jul 30 19:46:33 2012 +0400 @@ -164,8 +164,10 @@ return findAccessibleMethod(m); } Type[] gpts = m.getGenericParameterTypes(); - if (Arrays.equals(params, TypeResolver.erase(TypeResolver.resolve(pt, gpts)))) { - return findAccessibleMethod(m); + if (params.length == gpts.length) { + if (Arrays.equals(params, TypeResolver.erase(TypeResolver.resolve(pt, gpts)))) { + return findAccessibleMethod(m); + } } } } diff -r c22497df213e -r 2ed3ec2efd5d src/share/classes/java/beans/IndexedPropertyDescriptor.java --- a/src/share/classes/java/beans/IndexedPropertyDescriptor.java Mon Jul 30 16:38:53 2012 +0400 +++ b/src/share/classes/java/beans/IndexedPropertyDescriptor.java Mon Jul 30 19:46:33 2012 +0400 @@ -181,20 +181,21 @@ // the Indexed readMethod was explicitly set to null. return null; } + String nextMethodName = Introspector.GET_PREFIX + getBaseName(); if (indexedReadMethodName == null) { Class type = getIndexedPropertyType0(); if (type == boolean.class || type == null) { indexedReadMethodName = Introspector.IS_PREFIX + getBaseName(); } else { - indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); + indexedReadMethodName = nextMethodName; } } Class[] args = { int.class }; indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); - if (indexedReadMethod == null) { + if ((indexedReadMethod == null) && !indexedReadMethodName.equals(nextMethodName)) { // no "is" method, so look for a "get" method. - indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); + indexedReadMethodName = nextMethodName; indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, 1, args); } setIndexedReadMethod0(indexedReadMethod); diff -r c22497df213e -r 2ed3ec2efd5d src/share/classes/java/beans/Introspector.java --- a/src/share/classes/java/beans/Introspector.java Mon Jul 30 16:38:53 2012 +0400 +++ b/src/share/classes/java/beans/Introspector.java Mon Jul 30 19:46:33 2012 +0400 @@ -25,6 +25,7 @@ package java.beans; +import com.sun.beans.TypeResolver; import com.sun.beans.WeakCache; import com.sun.beans.finder.ClassFinder; @@ -34,6 +35,7 @@ import java.lang.ref.SoftReference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; import java.util.Map; import java.util.ArrayList; @@ -951,44 +953,61 @@ continue; } - Class argTypes[] = FeatureDescriptor.getParameterTypes(beanClass, method); - Class resultType = FeatureDescriptor.getReturnType(beanClass, method); - - if (name.startsWith(ADD_PREFIX) && argTypes.length == 1 && - resultType == Void.TYPE && - Introspector.isSubclass(argTypes[0], eventListenerType)) { - String listenerName = name.substring(3); - if (listenerName.length() > 0 && - argTypes[0].getName().endsWith(listenerName)) { - if (adds == null) { - adds = new HashMap(); + if (name.startsWith(ADD_PREFIX)) { + Class returnType = method.getReturnType(); + if (returnType == void.class) { + Type[] parameterTypes = method.getGenericParameterTypes(); + if (parameterTypes.length == 1) { + Class type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0])); + if (Introspector.isSubclass(type, eventListenerType)) { + String listenerName = name.substring(3); + if (listenerName.length() > 0 && + type.getName().endsWith(listenerName)) { + if (adds == null) { + adds = new HashMap(); + } + adds.put(listenerName, method); + } + } } - adds.put(listenerName, method); } } - else if (name.startsWith(REMOVE_PREFIX) && argTypes.length == 1 && - resultType == Void.TYPE && - Introspector.isSubclass(argTypes[0], eventListenerType)) { - String listenerName = name.substring(6); - if (listenerName.length() > 0 && - argTypes[0].getName().endsWith(listenerName)) { - if (removes == null) { - removes = new HashMap(); + else if (name.startsWith(REMOVE_PREFIX)) { + Class returnType = method.getReturnType(); + if (returnType == void.class) { + Type[] parameterTypes = method.getGenericParameterTypes(); + if (parameterTypes.length == 1) { + Class type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0])); + if (Introspector.isSubclass(type, eventListenerType)) { + String listenerName = name.substring(6); + if (listenerName.length() > 0 && + type.getName().endsWith(listenerName)) { + if (removes == null) { + removes = new HashMap(); + } + removes.put(listenerName, method); + } + } } - removes.put(listenerName, method); } } - else if (name.startsWith(GET_PREFIX) && argTypes.length == 0 && - resultType.isArray() && - Introspector.isSubclass(resultType.getComponentType(), - eventListenerType)) { - String listenerName = name.substring(3, name.length() - 1); - if (listenerName.length() > 0 && - resultType.getComponentType().getName().endsWith(listenerName)) { - if (gets == null) { - gets = new HashMap(); + else if (name.startsWith(GET_PREFIX)) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 0) { + Class returnType = FeatureDescriptor.getReturnType(beanClass, method); + if (returnType.isArray()) { + Class type = returnType.getComponentType(); + if (Introspector.isSubclass(type, eventListenerType)) { + String listenerName = name.substring(3, name.length() - 1); + if (listenerName.length() > 0 && + type.getName().endsWith(listenerName)) { + if (gets == null) { + gets = new HashMap(); + } + gets.put(listenerName, method); + } + } } - gets.put(listenerName, method); } } } @@ -1240,11 +1259,11 @@ private boolean isEventHandler(Method m) { // We assume that a method is an event handler if it has a single // argument, whose type inherit from java.util.Event. - Class argTypes[] = FeatureDescriptor.getParameterTypes(beanClass, m); + Type argTypes[] = m.getGenericParameterTypes(); if (argTypes.length != 1) { return false; } - return isSubclass(argTypes[0], EventObject.class); + return isSubclass(TypeResolver.erase(TypeResolver.resolveInClass(beanClass, argTypes[0])), EventObject.class); } /* @@ -1296,24 +1315,25 @@ } // make sure method signature matches. - Class params[] = FeatureDescriptor.getParameterTypes(start, method); - if (method.getName().equals(methodName) && - params.length == argCount) { - if (args != null) { - boolean different = false; - if (argCount > 0) { - for (int j = 0; j < argCount; j++) { - if (params[j] != args[j]) { - different = true; + if (method.getName().equals(methodName)) { + Type[] params = method.getGenericParameterTypes(); + if (params.length == argCount) { + if (args != null) { + boolean different = false; + if (argCount > 0) { + for (int j = 0; j < argCount; j++) { + if (TypeResolver.erase(TypeResolver.resolveInClass(start, params[j])) != args[j]) { + different = true; + continue; + } + } + if (different) { continue; } } - if (different) { - continue; - } } + return method; } - return method; } } } diff -r c22497df213e -r 2ed3ec2efd5d src/share/classes/java/beans/PropertyDescriptor.java --- a/src/share/classes/java/beans/PropertyDescriptor.java Mon Jul 30 16:38:53 2012 +0400 +++ b/src/share/classes/java/beans/PropertyDescriptor.java Mon Jul 30 19:46:33 2012 +0400 @@ -210,12 +210,13 @@ // The read method was explicitly set to null. return null; } + String nextMethodName = Introspector.GET_PREFIX + getBaseName(); if (readMethodName == null) { Class type = getPropertyType0(); if (type == boolean.class || type == null) { readMethodName = Introspector.IS_PREFIX + getBaseName(); } else { - readMethodName = Introspector.GET_PREFIX + getBaseName(); + readMethodName = nextMethodName; } } @@ -225,8 +226,8 @@ // methods. If an "is" method exists, this is the official // reader method so look for this one first. readMethod = Introspector.findMethod(cls, readMethodName, 0); - if (readMethod == null) { - readMethodName = Introspector.GET_PREFIX + getBaseName(); + if ((readMethod == null) && !readMethodName.equals(nextMethodName)) { + readMethodName = nextMethodName; readMethod = Introspector.findMethod(cls, readMethodName, 0); } try { diff -r c22497df213e -r 2ed3ec2efd5d test/java/beans/Performance/Test7122740.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/beans/Performance/Test7122740.java Mon Jul 30 19:46:33 2012 +0400 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012, 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 7122740 + * @summary Tests just a benchmark of PropertyDescriptor(String, Class) performance + * @author Sergey Malenkov + * @run main/manual Test7122740 + */ + +import java.beans.PropertyDescriptor; + +public class Test7122740 { + public static void main(String[] args) throws Exception { + long time = System.nanoTime(); + for (int i = 0; i < 1000; i++) { + new PropertyDescriptor("name", PropertyDescriptor.class); + new PropertyDescriptor("value", Concrete.class); + } + time -= System.nanoTime(); + System.out.println("Time (ms): " + (-time / 1000000)); + } + + public static class Abstract { + private T value; + public T getValue() { + return this.value; + } + public void setValue(T value) { + this.value = value; + } + } + + private static class Concrete extends Abstract { + } +} diff -r c22497df213e -r 2ed3ec2efd5d test/java/beans/Performance/Test7184799.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/beans/Performance/Test7184799.java Mon Jul 30 19:46:33 2012 +0400 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012, 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 7184799 + * @summary Tests just a benchmark of Introspector.getBeanInfo(Class) performance + * @author Sergey Malenkov + * @run main/manual Test7184799 + */ + +import java.beans.Introspector; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class Test7184799 { + private static final Class[] TYPES = { + Class.class, + String.class, + Character.class, + Boolean.class, + Byte.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + Collection.class, + Set.class, + HashSet.class, + TreeSet.class, + LinkedHashSet.class, + Map.class, + HashMap.class, + TreeMap.class, + LinkedHashMap.class, + WeakHashMap.class, + ConcurrentHashMap.class, + Dictionary.class, + Exception.class, + }; + + public static void main(String[] args) throws Exception { + long time = System.nanoTime(); + for (Class type : TYPES) { + Introspector.getBeanInfo(type); + } + time -= System.nanoTime(); + System.out.println("Time (ms): " + (-time / 1000000)); + } +}