Eliminate hotspots in ProxyAutoBean code by memoizing data.
Patch by: bobv
Review by: rchandia
Review at http://gwt-code-reviews.appspot.com/1161801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9312 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java b/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java
index ac2653b..315e185 100644
--- a/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java
+++ b/user/src/com/google/gwt/autobean/server/MethodPropertyContext.java
@@ -23,6 +23,7 @@
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
+import java.util.WeakHashMap;
/**
* A base type to handle analyzing the return value of a getter method. The
@@ -30,47 +31,61 @@
*/
abstract class MethodPropertyContext implements CollectionPropertyContext,
MapPropertyContext {
- private final Class<?> keyType;
- private final Class<?> valueType;
- private final Class<?> elementType;
- private final Class<?> type;
+ private static class Data {
+ Class<?> keyType;
+ Class<?> valueType;
+ Class<?> elementType;
+ Class<?> type;
+ }
+
+ /**
+ * Save prior instances in order to decrease the amount of data computed.
+ */
+ private static final Map<Method, Data> cache = new WeakHashMap<Method, Data>();
+
+ private final Data data;
public MethodPropertyContext(Method getter) {
- this.type = getter.getReturnType();
+ synchronized (cache) {
+ Data previous = cache.get(getter);
+ if (previous != null) {
+ this.data = previous;
+ return;
+ }
- // Compute collection element type
- if (Collection.class.isAssignableFrom(getType())) {
- elementType = TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(
- Collection.class, getter.getGenericReturnType(),
- getter.getReturnType()));
- keyType = valueType = null;
- } else if (Map.class.isAssignableFrom(getType())) {
- Type[] types = TypeUtils.getParameterization(Map.class,
- getter.getGenericReturnType());
- keyType = TypeUtils.ensureBaseType(types[0]);
- valueType = TypeUtils.ensureBaseType(types[1]);
- elementType = null;
- } else {
- elementType = keyType = valueType = null;
+ this.data = new Data();
+ data.type = getter.getReturnType();
+ // Compute collection element type
+ if (Collection.class.isAssignableFrom(getType())) {
+ data.elementType = TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(
+ Collection.class, getter.getGenericReturnType(),
+ getter.getReturnType()));
+ } else if (Map.class.isAssignableFrom(getType())) {
+ Type[] types = TypeUtils.getParameterization(Map.class,
+ getter.getGenericReturnType());
+ data.keyType = TypeUtils.ensureBaseType(types[0]);
+ data.valueType = TypeUtils.ensureBaseType(types[1]);
+ }
+ cache.put(getter, data);
}
}
public abstract boolean canSet();
public Class<?> getElementType() {
- return elementType;
+ return data.elementType;
}
public Class<?> getKeyType() {
- return keyType;
+ return data.keyType;
}
public Class<?> getType() {
- return type;
+ return data.type;
}
public Class<?> getValueType() {
- return valueType;
+ return data.valueType;
}
public abstract void set(Object value);
diff --git a/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java b/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
index 7c4772d..0992adc 100644
--- a/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
+++ b/user/src/com/google/gwt/autobean/server/ProxyAutoBean.java
@@ -28,9 +28,10 @@
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.WeakHashMap;
/**
* An implementation of an AutoBean that uses reflection.
@@ -38,9 +39,60 @@
* @param <T> the type of interface being wrapped
*/
class ProxyAutoBean<T> extends AbstractAutoBean<T> {
+ private static class Data {
+ final List<Method> getters = new ArrayList<Method>();
+ final List<String> getterNames = new ArrayList<String>();
+ final List<PropertyType> propertyType = new ArrayList<PropertyType>();
+ }
+
+ private enum PropertyType {
+ VALUE, REFERENCE, COLLECTION, MAP;
+ }
+
+ private static final Map<Class<?>, Data> cache = new WeakHashMap<Class<?>, Data>();
+
+ private static Data calculateData(Class<?> beanType) {
+ Data toReturn;
+ synchronized (cache) {
+ toReturn = cache.get(beanType);
+ if (toReturn == null) {
+ toReturn = new Data();
+ for (Method method : beanType.getMethods()) {
+ if (BeanMethod.GET.matches(method)) {
+ toReturn.getters.add(method);
+
+ String name;
+ PropertyName annotation = method.getAnnotation(PropertyName.class);
+ if (annotation != null) {
+ name = annotation.value();
+ } else {
+ name = method.getName();
+ name = Character.toLowerCase(name.charAt(3))
+ + (name.length() >= 5 ? name.substring(4) : "");
+ }
+ toReturn.getterNames.add(name);
+
+ Class<?> returnType = method.getReturnType();
+ if (TypeUtils.isValueType(returnType)) {
+ toReturn.propertyType.add(PropertyType.VALUE);
+ } else if (Collection.class.isAssignableFrom(returnType)) {
+ toReturn.propertyType.add(PropertyType.COLLECTION);
+ } else if (Map.class.isAssignableFrom(returnType)) {
+ toReturn.propertyType.add(PropertyType.MAP);
+ } else {
+ toReturn.propertyType.add(PropertyType.REFERENCE);
+ }
+ }
+ }
+ cache.put(beanType, toReturn);
+ }
+ }
+ return toReturn;
+ }
+
private final Class<T> beanType;
private final Configuration configuration;
- private final List<Method> getters;
+ private final Data data;
private final T shim;
// These constructors mirror the generated constructors.
@@ -50,7 +102,7 @@
super(factory);
this.beanType = (Class<T>) beanType;
this.configuration = configuration;
- this.getters = calculateGetters();
+ this.data = calculateData(beanType);
this.shim = createShim();
}
@@ -63,7 +115,7 @@
}
this.beanType = (Class<T>) beanType;
this.configuration = configuration;
- this.getters = calculateGetters();
+ this.data = calculateData(beanType);
this.shim = createShim();
}
@@ -71,7 +123,7 @@
super(toClone, deep);
this.beanType = toClone.beanType;
this.configuration = toClone.configuration;
- this.getters = toClone.getters;
+ this.data = toClone.data;
this.shim = createShim();
}
@@ -156,16 +208,15 @@
// TODO: Port to model-based when class-based TypeOracle is available.
@Override
protected void traverseProperties(AutoBeanVisitor visitor, OneShotContext ctx) {
- for (final Method getter : getters) {
- String name;
- PropertyName annotation = getter.getAnnotation(PropertyName.class);
- if (annotation != null) {
- name = annotation.value();
- } else {
- name = getter.getName();
- name = Character.toLowerCase(name.charAt(3))
- + (name.length() >= 5 ? name.substring(4) : "");
- }
+ assert data.getters.size() == data.getterNames.size()
+ && data.getters.size() == data.propertyType.size();
+ Iterator<Method> getterIt = data.getters.iterator();
+ Iterator<String> nameIt = data.getterNames.iterator();
+ Iterator<PropertyType> typeIt = data.propertyType.iterator();
+ while (getterIt.hasNext()) {
+ Method getter = getterIt.next();
+ String name = nameIt.next();
+ PropertyType propertyType = typeIt.next();
// Use the shim to handle automatic wrapping
Object value;
@@ -184,42 +235,51 @@
MethodPropertyContext x = isUsingSimplePeer() ? new BeanPropertyContext(
this, getter) : new GetterPropertyContext(this, getter);
- if (TypeUtils.isValueType(x.getType())) {
- if (visitor.visitValueProperty(name, value, x)) {
- }
- visitor.endVisitValueProperty(name, value, x);
- } else if (Collection.class.isAssignableFrom(x.getType())) {
- // Workaround for generics bug in mac javac 1.6.0_22
- @SuppressWarnings("rawtypes")
- AutoBean temp = AutoBeanUtils.getAutoBean((Collection) value);
- @SuppressWarnings("unchecked")
- AutoBean<Collection<?>> bean = (AutoBean<Collection<?>>) temp;
- if (visitor.visitCollectionProperty(name, bean, x)) {
- if (value != null) {
- ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+ switch (propertyType) {
+ case VALUE: {
+ if (visitor.visitValueProperty(name, value, x)) {
}
+ visitor.endVisitValueProperty(name, value, x);
+ break;
}
- visitor.endVisitCollectionProperty(name, bean, x);
- } else if (Map.class.isAssignableFrom(x.getType())) {
- // Workaround for generics bug in mac javac 1.6.0_22
- @SuppressWarnings("rawtypes")
- AutoBean temp = AutoBeanUtils.getAutoBean((Map) value);
- @SuppressWarnings("unchecked")
- AutoBean<Map<?, ?>> bean = (AutoBean<Map<?, ?>>) temp;
- if (visitor.visitMapProperty(name, bean, x)) {
- if (value != null) {
- ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+ case COLLECTION: {
+ // Workaround for generics bug in mac javac 1.6.0_22
+ @SuppressWarnings("rawtypes")
+ AutoBean temp = AutoBeanUtils.getAutoBean((Collection) value);
+ @SuppressWarnings("unchecked")
+ AutoBean<Collection<?>> bean = (AutoBean<Collection<?>>) temp;
+ if (visitor.visitCollectionProperty(name, bean, x)) {
+ if (value != null) {
+ ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+ }
}
+ visitor.endVisitCollectionProperty(name, bean, x);
+ break;
}
- visitor.endVisitMapProperty(name, bean, x);
- } else {
- ProxyAutoBean<?> bean = (ProxyAutoBean<?>) AutoBeanUtils.getAutoBean(value);
- if (visitor.visitReferenceProperty(name, bean, x)) {
- if (value != null) {
- bean.traverse(visitor, ctx);
+ case MAP: {
+ // Workaround for generics bug in mac javac 1.6.0_22
+ @SuppressWarnings("rawtypes")
+ AutoBean temp = AutoBeanUtils.getAutoBean((Map) value);
+ @SuppressWarnings("unchecked")
+ AutoBean<Map<?, ?>> bean = (AutoBean<Map<?, ?>>) temp;
+ if (visitor.visitMapProperty(name, bean, x)) {
+ if (value != null) {
+ ((ProxyAutoBean<?>) bean).traverse(visitor, ctx);
+ }
}
+ visitor.endVisitMapProperty(name, bean, x);
+ break;
}
- visitor.endVisitReferenceProperty(name, bean, x);
+ case REFERENCE: {
+ ProxyAutoBean<?> bean = (ProxyAutoBean<?>) AutoBeanUtils.getAutoBean(value);
+ if (visitor.visitReferenceProperty(name, bean, x)) {
+ if (value != null) {
+ bean.traverse(visitor, ctx);
+ }
+ }
+ visitor.endVisitReferenceProperty(name, bean, x);
+ break;
+ }
}
}
}
@@ -232,16 +292,6 @@
return values;
}
- private List<Method> calculateGetters() {
- List<Method> toReturn = new ArrayList<Method>();
- for (Method method : beanType.getMethods()) {
- if (BeanMethod.GET.matches(method)) {
- toReturn.add(method);
- }
- }
- return Collections.unmodifiableList(toReturn);
- }
-
private T createShim() {
T toReturn = AutoBeanFactoryMagic.makeProxy(beanType, new ShimHandler<T>(
this, getWrapped()));
diff --git a/user/src/com/google/gwt/autobean/shared/ValueCodex.java b/user/src/com/google/gwt/autobean/shared/ValueCodex.java
index 19ef456..d4a452f 100644
--- a/user/src/com/google/gwt/autobean/shared/ValueCodex.java
+++ b/user/src/com/google/gwt/autobean/shared/ValueCodex.java
@@ -237,8 +237,7 @@
private static <T> Type findType(Class<T> clazz) {
Type type = typesByClass.get(clazz);
if (type == null) {
- // Necessary due to lack of Class.isAssignable() in client-side
- if (clazz.getEnumConstants() != null) {
+ if (clazz.isEnum()) {
return Type.ENUM;
}
}