Updated rpc generator result caching to support full cacheability
Also added support for changes to relevant properties and custom field serializers
Review at http://gwt-code-reviews.appspot.com/1464802
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10357 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/rebind/rpc/CachedRpcTypeInformation.java b/user/src/com/google/gwt/user/rebind/rpc/CachedRpcTypeInformation.java
new file mode 100644
index 0000000..24b2fa7
--- /dev/null
+++ b/user/src/com/google/gwt/user/rebind/rpc/CachedRpcTypeInformation.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.rebind.rpc;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
+import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
+import com.google.gwt.core.ext.typeinfo.JRawType;
+import com.google.gwt.core.ext.typeinfo.JRealClassType;
+import com.google.gwt.core.ext.typeinfo.JType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A container for type information, for use with generator result caching.
+ */
+public class CachedRpcTypeInformation implements Serializable {
+ private final Map<String, Long> lastModifiedTimes = new HashMap<String, Long>();
+ private final Set<String> instantiableFromBrowser = new HashSet<String>();
+ private final Set<String> instantiableToBrowser = new HashSet<String>();
+ private final Set<String> serializableFromBrowser = new HashSet<String>();
+ private final Set<String> serializableToBrowser = new HashSet<String>();
+ private final Set<String> typesNotUsingCustomSerializer = new HashSet<String>();
+ private final Set<String> customSerializerTypes = new HashSet<String>();
+
+ public CachedRpcTypeInformation(SerializableTypeOracle typesFromBrowser,
+ SerializableTypeOracle typesToBrowser, Set<JType> customSerializersUsed,
+ Set<JType> typesNotUsingCustomSerializers) {
+
+ recordTypes(serializableFromBrowser, instantiableFromBrowser, typesFromBrowser);
+ recordTypes(serializableToBrowser, instantiableToBrowser, typesToBrowser);
+
+ for (JType type : customSerializersUsed) {
+ addCustomSerializerType(type);
+ }
+
+ for (JType type : typesNotUsingCustomSerializers) {
+ addTypeNotUsingCustomSerializer(type);
+ }
+ }
+
+ public void addCustomSerializerType(JType type) {
+ String sourceName = type.getQualifiedSourceName();
+ lastModifiedTimes.put(sourceName, getLastModifiedTime(type));
+ customSerializerTypes.add(sourceName);
+ }
+
+ public void addTypeNotUsingCustomSerializer(JType type) {
+ String sourceName = type.getQualifiedSourceName();
+ typesNotUsingCustomSerializer.add(sourceName);
+ }
+
+ public boolean checkLastModifiedTime(JType type) {
+ Long cachedTime = lastModifiedTimes.get(type.getQualifiedSourceName());
+ if (cachedTime == null) {
+ return false;
+ }
+ return cachedTime == getLastModifiedTime(type);
+ }
+
+ public boolean checkTypeInformation(TreeLogger logger, TypeOracle typeOracle,
+ SerializableTypeOracle typesFromBrowser, SerializableTypeOracle typesToBrowser) {
+
+ JType[] typesFrom = typesFromBrowser.getSerializableTypes();
+ if (typesFrom.length != serializableFromBrowser.size()) {
+ if (logger.isLoggable(TreeLogger.TRACE)) {
+ logger.log(TreeLogger.TRACE,
+ "The number of serializable types sent from the browser has changed");
+ logDifferencesBetweenCurrentAndCachedTypes(logger, typesFrom, serializableFromBrowser);
+ }
+ return false;
+ }
+
+ JType[] typesTo = typesToBrowser.getSerializableTypes();
+ if (typesTo.length != serializableToBrowser.size()) {
+ if (logger.isLoggable(TreeLogger.TRACE)) {
+ logger.log(TreeLogger.TRACE,
+ "The number of serializable types sent to the browser has changed");
+ logDifferencesBetweenCurrentAndCachedTypes(logger, typesTo, serializableToBrowser);
+ }
+ return false;
+ }
+
+ if (!checkTypes(logger, serializableFromBrowser, instantiableFromBrowser, typesFromBrowser)
+ || !checkTypes(logger, serializableToBrowser, instantiableToBrowser, typesToBrowser)) {
+ return false;
+ }
+
+ for (String customSerializerType : customSerializerTypes) {
+ JType currType = typeOracle.findType(customSerializerType);
+ if (currType == null) {
+ logger.log(TreeLogger.TRACE, "Custom serializer no longer available: "
+ + customSerializerType);
+ return false;
+ }
+ if (!checkLastModifiedTime(currType)) {
+ logger.log(TreeLogger.TRACE, "A change was detected in custom serializer: "
+ + customSerializerType);
+ return false;
+ }
+ }
+
+ for (String sourceName : typesNotUsingCustomSerializer) {
+ String fieldSerializerName =
+ SerializableTypeOracleBuilder.getCustomFieldSerializerName(sourceName);
+ if (SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle, fieldSerializerName) != null) {
+ logger.log(TreeLogger.TRACE, "A new custom serializer is available " + sourceName);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public boolean checkTypeNotUsingCustomSerializer(JType type) {
+ return typesNotUsingCustomSerializer.contains(type.getQualifiedSourceName());
+ }
+
+ /*
+ * Finds a last modified time for a type, for testing cacheability.
+ */
+ public long getLastModifiedTime(JType type) {
+ JType typeToCheck;
+ if (type instanceof JArrayType) {
+ typeToCheck = type.getLeafType();
+ } else if (type instanceof JRawType) {
+ typeToCheck = ((JRawType) type).getGenericType();
+ } else {
+ assert type instanceof JRealClassType;
+ typeToCheck = type;
+ }
+
+ if (typeToCheck instanceof JRealClassType) {
+ return ((JRealClassType) typeToCheck).getLastModifiedTime();
+ } else {
+ // we have a type that is an array with a primitive leafType
+ assert typeToCheck instanceof JPrimitiveType;
+ // this type is never out of date
+ return Long.MAX_VALUE;
+ }
+ }
+
+ private boolean checkTypes(TreeLogger logger, Set<String> serializable, Set<String> instantiable,
+ SerializableTypeOracle sto) {
+ for (JType type : sto.getSerializableTypes()) {
+ String sourceName = type.getQualifiedSourceName();
+ if (sto.isSerializable(type) != serializable.contains(sourceName)
+ || sto.maybeInstantiated(type) != instantiable.contains(sourceName)
+ || !checkLastModifiedTime(type)) {
+ logger.log(TreeLogger.TRACE, "A change was detected in type " + sourceName);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void logDifferencesBetweenCurrentAndCachedTypes(TreeLogger logger, JType[] currentTypes,
+ Set<String> cachedTypes) {
+
+ Set<String> remainingCachedTypes = new HashSet<String>(cachedTypes);
+ for (JType currentType : currentTypes) {
+ String sourceName = currentType.getQualifiedSourceName();
+ if (!remainingCachedTypes.remove(sourceName)) {
+ logger.log(TreeLogger.TRACE, "New type " + sourceName + " not in cached list");
+ }
+ }
+
+ for (String remainingCachedType : remainingCachedTypes) {
+ logger.log(TreeLogger.TRACE, "Cached type " + remainingCachedType + " not in new list");
+ }
+ }
+
+ private void recordTypes(Set<String> serializable, Set<String> instantiable,
+ SerializableTypeOracle sto) {
+ for (JType type : sto.getSerializableTypes()) {
+ String sourceName = type.getQualifiedSourceName();
+ lastModifiedTimes.put(sourceName, getLastModifiedTime(type));
+ serializable.add(sourceName);
+ if (sto.maybeInstantiated(type)) {
+ instantiable.add(sourceName);
+ }
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
index 8446a30..71acaee 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -37,6 +37,10 @@
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.generator.NameFactory;
import com.google.gwt.dev.javac.rebind.CachedClientDataMap;
+import com.google.gwt.dev.javac.rebind.CachedPropertyInformation;
+import com.google.gwt.dev.javac.rebind.CachedRebindResult;
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
@@ -71,6 +75,7 @@
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -83,16 +88,42 @@
*/
public class ProxyCreator {
/**
+ * Some keys for storing cached information for use with generator result
+ * caching.
+ */
+ public static final String CACHED_PROPERTY_INFO_KEY = "cached-property-info";
+ public static final String CACHED_TYPE_INFO_KEY = "cached-type-info";
+
+ /**
* The directory within which RPC manifests are placed for individual
* permutations.
*/
public static final String MANIFEST_ARTIFACT_DIR = "rpcPolicyManifest/manifests";
- private static final Map<JPrimitiveType, ResponseReader> JPRIMITIVETYPE_TO_RESPONSEREADER =
- new HashMap<JPrimitiveType, ResponseReader>();
+ /**
+ * Properties which need to be checked to determine cacheability.
+ */
+ private static final Collection<String> configPropsToCheck = Arrays.asList(
+ TypeSerializerCreator.GWT_ELIDE_TYPE_NAMES_FROM_RPC, Shared.RPC_ENHANCED_CLASSES);
+ private static final Collection<String> selectionPropsToCheck = Arrays
+ .asList(Shared.RPC_PROP_SUPPRESS_NON_STATIC_FINAL_FIELD_WARNINGS);
private static final String PROXY_SUFFIX = "_Proxy";
+ private static final Map<JPrimitiveType, ResponseReader> JPRIMITIVETYPE_TO_RESPONSEREADER =
+ new HashMap<JPrimitiveType, ResponseReader>();
+ static {
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.BOOLEAN, ResponseReader.BOOLEAN);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.BYTE, ResponseReader.BYTE);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.CHAR, ResponseReader.CHAR);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.DOUBLE, ResponseReader.DOUBLE);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.FLOAT, ResponseReader.FLOAT);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.INT, ResponseReader.INT);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.LONG, ResponseReader.LONG);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.SHORT, ResponseReader.SHORT);
+ JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.VOID, ResponseReader.VOID);
+ }
+
/**
* Adds a root type for each type that appears in the RemoteService interface
* methods.
@@ -196,7 +227,7 @@
for (JType[] a : types) {
typesList.addAll(Arrays.asList(a));
}
- JType[] serializableTypes = typesList.toArray(new JType[0]);
+ JType[] serializableTypes = typesList.toArray(new JType[typesList.size()]);
Arrays.sort(serializableTypes, SerializableTypeOracleBuilder.JTYPE_COMPARATOR);
return serializableTypes;
}
@@ -205,24 +236,13 @@
private boolean elideTypeNames;
- private Map<String, Long> cachedTypeLastModifiedTimes = null;
-
/**
* The possibly obfuscated type signatures used to represent a type.
*/
private Map<JType, String> typeStrings;
- {
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.BOOLEAN, ResponseReader.BOOLEAN);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.BYTE, ResponseReader.BYTE);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.CHAR, ResponseReader.CHAR);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.DOUBLE, ResponseReader.DOUBLE);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.FLOAT, ResponseReader.FLOAT);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.INT, ResponseReader.INT);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.LONG, ResponseReader.LONG);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.SHORT, ResponseReader.SHORT);
- JPRIMITIVETYPE_TO_RESPONSEREADER.put(JPrimitiveType.VOID, ResponseReader.VOID);
- }
+ private Set<JType> customSerializersUsed;
+ private Set<JType> typesNotUsingCustomSerializers;
public ProxyCreator(JClassType serviceIntf) {
assert (serviceIntf.isInterface() != null);
@@ -234,7 +254,7 @@
*
* @throws UnableToCompleteException
*/
- public String create(TreeLogger logger, GeneratorContextExt context)
+ public RebindResult create(TreeLogger logger, GeneratorContextExt context)
throws UnableToCompleteException {
TypeOracle typeOracle = context.getTypeOracle();
@@ -247,9 +267,8 @@
throw new UnableToCompleteException();
}
- SourceWriter srcWriter = getSourceWriter(logger, context, serviceAsync);
- if (srcWriter == null) {
- return getProxyQualifiedName();
+ if (checkAlreadyGenerated(typeOracle, serviceIntf)) {
+ return new RebindResult(RebindStatus.USE_EXISTING, getProxyQualifiedName());
}
// Make sure that the async and synchronous versions of the RemoteService
@@ -265,15 +284,52 @@
// Determine the set of serializable types
Event event = SpeedTracerLogger.start(CompilerEventType.GENERATOR_RPC_STOB);
+ SerializableTypeOracle typesSentFromBrowser;
+ SerializableTypeOracle typesSentToBrowser;
+ String rpcLog;
+ try {
+ SerializableTypeOracleBuilder typesSentFromBrowserBuilder =
+ new SerializableTypeOracleBuilder(logger, propertyOracle, context);
+ typesSentFromBrowserBuilder.setTypeFilter(blacklistTypeFilter);
+ SerializableTypeOracleBuilder typesSentToBrowserBuilder =
+ new SerializableTypeOracleBuilder(logger, propertyOracle, context);
+ typesSentToBrowserBuilder.setTypeFilter(blacklistTypeFilter);
- SerializableTypeOracleBuilder typesSentFromBrowserBuilder =
- new SerializableTypeOracleBuilder(logger, propertyOracle, context);
- typesSentFromBrowserBuilder.setTypeFilter(blacklistTypeFilter);
- SerializableTypeOracleBuilder typesSentToBrowserBuilder =
- new SerializableTypeOracleBuilder(logger, propertyOracle, context);
- typesSentToBrowserBuilder.setTypeFilter(blacklistTypeFilter);
+ addRoots(logger, typeOracle, typesSentFromBrowserBuilder, typesSentToBrowserBuilder);
- addRoots(logger, typeOracle, typesSentFromBrowserBuilder, typesSentToBrowserBuilder);
+ // Decide what types to send in each direction.
+ // Log the decisions to a string that will be written later in this method
+ {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+
+ typesSentFromBrowserBuilder.setLogOutputWriter(writer);
+ typesSentToBrowserBuilder.setLogOutputWriter(writer);
+
+ writer.write("====================================\n");
+ writer.write("Types potentially sent from browser:\n");
+ writer.write("====================================\n\n");
+ writer.flush();
+ typesSentFromBrowser = typesSentFromBrowserBuilder.build(logger);
+
+ writer.write("===================================\n");
+ writer.write("Types potentially sent from server:\n");
+ writer.write("===================================\n\n");
+ writer.flush();
+ typesSentToBrowser = typesSentToBrowserBuilder.build(logger);
+
+ writer.close();
+ rpcLog = stringWriter.toString();
+ }
+ } finally {
+ event.end();
+ }
+
+ // Check generator result cacheability, to see if we can return now
+ if (checkGeneratorResultCacheability(logger, context, typesSentFromBrowser, typesSentToBrowser)) {
+ logger.log(TreeLogger.TRACE, "Reusing all cached artifacts for " + getProxyQualifiedName());
+ return new RebindResult(RebindStatus.USE_ALL_CACHED, getProxyQualifiedName());
+ }
try {
ConfigurationProperty prop =
@@ -287,34 +343,12 @@
throw new UnableToCompleteException();
}
- // Decide what types to send in each direction.
- // Log the decisions to a string that will be written later in this method
- SerializableTypeOracle typesSentFromBrowser;
- SerializableTypeOracle typesSentToBrowser;
- String rpcLog;
- {
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
-
- typesSentFromBrowserBuilder.setLogOutputWriter(writer);
- typesSentToBrowserBuilder.setLogOutputWriter(writer);
-
- writer.write("====================================\n");
- writer.write("Types potentially sent from browser:\n");
- writer.write("====================================\n\n");
- writer.flush();
- typesSentFromBrowser = typesSentFromBrowserBuilder.build(logger);
-
- writer.write("===================================\n");
- writer.write("Types potentially sent from server:\n");
- writer.write("===================================\n\n");
- writer.flush();
- typesSentToBrowser = typesSentToBrowserBuilder.build(logger);
-
- writer.close();
- rpcLog = stringWriter.toString();
+ SourceWriter srcWriter = getSourceWriter(logger, context, serviceAsync);
+ if (srcWriter == null) {
+ // don't expect this to occur, but could happen if an instance was
+ // recently generated but not yet committed
+ return new RebindResult(RebindStatus.USE_EXISTING, getProxyQualifiedName());
}
- event.end();
generateTypeHandlers(logger, context, typesSentFromBrowser, typesSentToBrowser);
@@ -344,12 +378,26 @@
serializationPolicyStrongName, rpcLog));
}
- return getProxyQualifiedName();
- }
+ if (context.isGeneratorResultCachingEnabled()) {
+ // Remember the type info that we care about for cacheability testing.
+ CachedClientDataMap clientData = new CachedClientDataMap();
+ CachedRpcTypeInformation cti =
+ new CachedRpcTypeInformation(typesSentFromBrowser, typesSentToBrowser,
+ customSerializersUsed, typesNotUsingCustomSerializers);
+ clientData.put(CACHED_TYPE_INFO_KEY, cti);
+ CachedPropertyInformation cpi =
+ new CachedPropertyInformation(logger, context.getPropertyOracle(), selectionPropsToCheck,
+ configPropsToCheck);
+ clientData.put(CACHED_PROPERTY_INFO_KEY, cpi);
- public void updateResultCacheData(CachedClientDataMap clientData) {
- if (cachedTypeLastModifiedTimes != null) {
- clientData.put(TypeSerializerCreator.CACHED_TYPE_INFO_KEY, cachedTypeLastModifiedTimes);
+ /*
+ * Return with RebindStatus.USE_PARTIAL_CACHED, since we are allowing
+ * generator result caching for field serializers, but other generated
+ * types cannot be cached effectively.
+ */
+ return new RebindResult(RebindStatus.USE_PARTIAL_CACHED, getProxyQualifiedName(), clientData);
+ } else {
+ return new RebindResult(RebindStatus.USE_ALL_NEW_WITH_NO_CACHING, getProxyQualifiedName());
}
}
@@ -655,7 +703,8 @@
typeStrings = new HashMap<JType, String>(tsc.getTypeStrings());
typeStrings.put(serviceIntf, TypeNameObfuscator.SERVICE_INTERFACE_ID);
- cachedTypeLastModifiedTimes = tsc.getTypeLastModifiedTimeMap();
+ customSerializersUsed = tsc.getCustomSerializersUsed();
+ typesNotUsingCustomSerializers = tsc.getTypesNotUsingCustomSerializers();
}
protected String getProxySimpleName() {
@@ -777,6 +826,44 @@
}
}
+ private boolean checkAlreadyGenerated(TypeOracle typeOracle, JClassType serviceAsync) {
+ JPackage serviceIntfPkg = serviceAsync.getPackage();
+ String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
+ return typeOracle.findType(packageName, getProxySimpleName()) != null;
+ }
+
+ private boolean checkGeneratorResultCacheability(TreeLogger logger, GeneratorContextExt ctx,
+ SerializableTypeOracle typesSentFromBrowser, SerializableTypeOracle typesSentToBrowser) {
+
+ CachedRebindResult lastResult = ctx.getCachedGeneratorResult();
+ if (lastResult == null || !ctx.isGeneratorResultCachingEnabled()) {
+ return false;
+ }
+
+ CachedPropertyInformation cpi =
+ (CachedPropertyInformation) lastResult.getClientData(CACHED_PROPERTY_INFO_KEY);
+ if (cpi == null) {
+ return false;
+ }
+
+ CachedRpcTypeInformation cti =
+ (CachedRpcTypeInformation) lastResult.getClientData(CACHED_TYPE_INFO_KEY);
+ if (cti == null) {
+ return false;
+ }
+
+ if (!cti.checkTypeInformation(logger, ctx.getTypeOracle(), typesSentFromBrowser,
+ typesSentToBrowser)) {
+ return false;
+ }
+
+ if (!cpi.checkPropertiesWithPropertyOracle(logger, ctx.getPropertyOracle())) {
+ return false;
+ }
+
+ return true;
+ }
+
private void emitPolicyFileArtifact(TreeLogger logger, GeneratorContextExt context,
String partialPath) throws UnableToCompleteException {
try {
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index 5673994..22437ff 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -404,7 +404,20 @@
return null;
}
- String customFieldSerializerName = type.getQualifiedSourceName() + "_CustomFieldSerializer";
+ String customFieldSerializerName = getCustomFieldSerializerName(type.getQualifiedSourceName());
+ return findCustomFieldSerializer(typeOracle, customFieldSerializerName);
+ }
+
+ /**
+ * Finds the custom field serializer for a given qualified source name.
+ *
+ * @param typeOracle
+ * @param customFieldSerializerName
+ * @return the custom field serializer for a type of <code>null</code> if
+ * there is not one
+ */
+ public static JClassType findCustomFieldSerializer(TypeOracle typeOracle,
+ String customFieldSerializerName) {
JClassType customSerializer = typeOracle.findType(customFieldSerializerName);
if (customSerializer == null) {
// If the type is in the java.lang or java.util packages then it will be
@@ -416,6 +429,16 @@
return customSerializer;
}
+ /**
+ * Returns the name for a custom field serializer, given a source name.
+ *
+ * @param sourceName
+ * @return the custom field serializer type name for a given source name.
+ */
+ public static String getCustomFieldSerializerName(String sourceName) {
+ return sourceName + "_CustomFieldSerializer";
+ }
+
static JRealClassType getBaseType(JClassType type) {
if (type.isParameterized() != null) {
return type.isParameterized().getBaseType();
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
index c49244c..60435de 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ServiceInterfaceProxyGenerator.java
@@ -21,9 +21,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.dev.javac.rebind.CachedClientDataMap;
import com.google.gwt.dev.javac.rebind.RebindResult;
-import com.google.gwt.dev.javac.rebind.RebindStatus;
/**
* Generator for producing the asynchronous version of a
@@ -57,23 +55,7 @@
logger.branch(TreeLogger.DEBUG, "Generating client proxy for remote service interface '"
+ remoteService.getQualifiedSourceName() + "'", null);
- String returnTypeName = proxyCreator.create(proxyLogger, ctx);
-
- if (ctx.isGeneratorResultCachingEnabled()) {
- // Remember the type info that we care about for cacheability testing.
- CachedClientDataMap clientData = new CachedClientDataMap();
- proxyCreator.updateResultCacheData(clientData);
-
- /*
- * Return with RebindStatus.USE_PARTIAL_CACHED, since we are allowing
- * generator result caching for field serializers, but other generated
- * types cannot be cached effectively.
- */
- return new RebindResult(RebindStatus.USE_PARTIAL_CACHED, returnTypeName, clientData);
- } else {
- // If we can't be cacheable, don't return a cacheable result
- return new RebindResult(RebindStatus.USE_ALL_NEW_WITH_NO_CACHING, returnTypeName);
- }
+ return proxyCreator.create(proxyLogger, ctx);
}
protected ProxyCreator createProxyCreator(JClassType remoteService) {
diff --git a/user/src/com/google/gwt/user/rebind/rpc/Shared.java b/user/src/com/google/gwt/user/rebind/rpc/Shared.java
index 4c710d1..7db70ae 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/Shared.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/Shared.java
@@ -37,7 +37,7 @@
* Property used to control whether or not the RPC system will emit warnings
* when a type has final fields.
*/
- private static final String RPC_PROP_SUPPRESS_NON_STATIC_FINAL_FIELD_WARNINGS =
+ public static final String RPC_PROP_SUPPRESS_NON_STATIC_FINAL_FIELD_WARNINGS =
"gwt.suppressNonStaticFinalFieldWarnings";
/**
@@ -45,7 +45,7 @@
* (potentially) enhanced with server-only fields, to be handled specially by
* RPC.
*/
- private static final String RPC_ENHANCED_CLASSES = "rpc.enhancedClasses";
+ public static final String RPC_ENHANCED_CLASSES = "rpc.enhancedClasses";
/**
* Capitalizes a name.
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
index 288b7cc..7489b7b 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
@@ -24,13 +24,9 @@
import com.google.gwt.core.ext.GeneratorContextExt;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
-import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
-import com.google.gwt.core.ext.typeinfo.JRawType;
-import com.google.gwt.core.ext.typeinfo.JRealClassType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.rebind.CachedRebindResult;
@@ -63,12 +59,6 @@
public class TypeSerializerCreator {
/**
- * A key for storing cached type information for use with generator result
- * caching.
- */
- public static final String CACHED_TYPE_INFO_KEY = "cached-type-info";
-
- /**
* Configuration property to use type indices instead of type signatures.
*/
public static final String GWT_ELIDE_TYPE_NAMES_FROM_RPC = "gwt.elideTypeNamesFromRPC";
@@ -127,10 +117,12 @@
private final String typeSerializerSimpleName;
- private final Map<String, Long> typeLastModifiedTimeMap;
-
private final Map<JType, String> typeStrings = new IdentityHashMap<JType, String>();
+ private final Set<JType> typesNotUsingCustomFieldSerializers;
+
+ private final Set<JType> customFieldSerializersUsed;
+
public TypeSerializerCreator(TreeLogger logger, SerializableTypeOracle serializationOracle,
SerializableTypeOracle deserializationOracle, GeneratorContextExt context,
String typeSerializerClassName, String typeSerializerSimpleName)
@@ -169,14 +161,20 @@
}
if (context.isGeneratorResultCachingEnabled()) {
- typeLastModifiedTimeMap = new HashMap<String, Long>();
+ typesNotUsingCustomFieldSerializers = new HashSet<JType>();
+ customFieldSerializersUsed = new HashSet<JType>();
} else {
- typeLastModifiedTimeMap = null;
+ typesNotUsingCustomFieldSerializers = null;
+ customFieldSerializersUsed = null;
}
}
- public Map<String, Long> getTypeLastModifiedTimeMap() {
- return typeLastModifiedTimeMap;
+ public Set<JType> getCustomSerializersUsed() {
+ return customFieldSerializersUsed;
+ }
+
+ public Set<JType> getTypesNotUsingCustomSerializers() {
+ return typesNotUsingCustomFieldSerializers;
}
public Map<JType, String> getTypeStrings() {
@@ -246,12 +244,21 @@
*/
assert (type.isClass() != null || type.isArray() != null);
+ // get custom field serializer, if available
+ JClassType customFieldSerializer =
+ SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle, type);
+
if (ctx.isGeneratorResultCachingEnabled()) {
- // get the last modified time for our type, and remember it
- typeLastModifiedTimeMap.put(type.getQualifiedSourceName(), getLastModifiedTime(type));
+ // update cacheable info for next iteration
+ if (customFieldSerializer != null) {
+ customFieldSerializersUsed.add(customFieldSerializer);
+ } else {
+ typesNotUsingCustomFieldSerializers.add(type);
+ }
// check the cache for a valid field serializer for the current type
- if (findCacheableFieldSerializerAndMarkForReuseIfAvailable(logger, ctx, type)) {
+ if (findCacheableFieldSerializerAndMarkForReuseIfAvailable(logger, ctx, type,
+ customFieldSerializer)) {
// we can skip re-generation of the field serializer for the current
// type
return;
@@ -259,8 +266,6 @@
}
// generate a new field serializer
- JClassType customFieldSerializer =
- SerializableTypeOracleBuilder.findCustomFieldSerializer(typeOracle, type);
FieldSerializerCreator creator =
new FieldSerializerCreator(context, serializationOracle, deserializationOracle,
(JClassType) type, customFieldSerializer);
@@ -290,7 +295,7 @@
* return false.
*/
private boolean findCacheableFieldSerializerAndMarkForReuseIfAvailable(TreeLogger logger,
- GeneratorContextExt ctx, JType type) {
+ GeneratorContextExt ctx, JType type, JType customFieldSerializer) {
CachedRebindResult lastResult = ctx.getCachedGeneratorResult();
if (lastResult == null || !ctx.isGeneratorResultCachingEnabled()) {
@@ -302,6 +307,12 @@
if (type instanceof JClassType) {
// check that it is available for reuse
if (!lastResult.isTypeCached(fieldSerializerName)) {
+ if (logger.isLoggable(TreeLogger.TRACE)) {
+ if (ctx.getTypeOracle().findType(fieldSerializerName) == null) {
+ logger.log(TreeLogger.TRACE, "No cached field serializer available for "
+ + type.getQualifiedSourceName());
+ }
+ }
return false;
}
} else {
@@ -309,14 +320,15 @@
}
@SuppressWarnings("unchecked")
- Map<String, Long> cachedLastModifiedTimes =
- (Map<String, Long>) lastResult.getClientData(CACHED_TYPE_INFO_KEY);
- String sourceName = type.getQualifiedSourceName();
+ CachedRpcTypeInformation cachedTypeInfo =
+ (CachedRpcTypeInformation) lastResult.getClientData(ProxyCreator.CACHED_TYPE_INFO_KEY);
- assert cachedLastModifiedTimes != null;
- assert typeLastModifiedTimeMap.get(sourceName) != null;
+ assert cachedTypeInfo != null;
boolean foundMatch = false;
- if (typeLastModifiedTimeMap.get(sourceName).equals(cachedLastModifiedTimes.get(sourceName))) {
+ if (cachedTypeInfo.checkLastModifiedTime(type)
+ && ((customFieldSerializer != null && cachedTypeInfo
+ .checkLastModifiedTime(customFieldSerializer)) || (customFieldSerializer == null && cachedTypeInfo
+ .checkTypeNotUsingCustomSerializer(type)))) {
// use cached version, if available
foundMatch = ctx.reuseTypeFromCacheIfAvailable(fieldSerializerName);
}
@@ -334,29 +346,6 @@
return foundMatch;
}
- private long getLastModifiedTime(JType type) {
- JType typeToCheck;
- if (type instanceof JArrayType) {
- typeToCheck = ((JArrayType) type).getLeafType();
- } else if (type instanceof JRawType) {
- typeToCheck = ((JRawType) type).getGenericType();
- } else {
- assert type instanceof JRealClassType;
- typeToCheck = type;
- }
-
- long lastModifiedTime;
- if (typeToCheck instanceof JRealClassType) {
- lastModifiedTime = ((JRealClassType) typeToCheck).getLastModifiedTime();
- } else {
- // we have a type that is an array with a primitive leafType
- assert typeToCheck instanceof JPrimitiveType;
- lastModifiedTime = Long.MAX_VALUE;
- }
-
- return lastModifiedTime;
- }
-
private String[] getPackageAndClassName(String fullClassName) {
String className = fullClassName;
String packageName = "";