Adds @GwtTransient, an annotation that tells GWT RPC
to treat a field as if it were marked with the Java
transient keyword, even though it's not.
Review by: robertvawter
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4422 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/GwtTransient.java b/user/src/com/google/gwt/user/client/rpc/GwtTransient.java
new file mode 100644
index 0000000..376e055
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/GwtTransient.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 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.client.rpc;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation means the same thing as the <code>transient</code> keyword,
+ * but it is ignored by all serialization systems other than GWT's. Usually the
+ * <code>transient</code> keyword should be used in preference to this
+ * annotation. However, for types used with multiple serialization systems, it
+ * can be useful.
+ */
+@Documented
+@Target(ElementType.FIELD)
+public @interface GwtTransient {
+}
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 687c07a..73c4564 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -31,6 +31,7 @@
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+import com.google.gwt.user.client.rpc.GwtTransient;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.rebind.rpc.TypeParameterExposureComputer.TypeParameterFlowInfo;
import com.google.gwt.user.rebind.rpc.TypePaths.TypePath;
@@ -87,14 +88,14 @@
private class TypeInfoComputed {
/**
- * <code>true</code> if the type is assignable to {@link IsSerializable}
- * or {@link java.io.Serializable Serializable}.
+ * <code>true</code> if the type is assignable to {@link IsSerializable} or
+ * {@link java.io.Serializable Serializable}.
*/
private final boolean autoSerializable;
/**
- * <code>true</code> if the this type directly implements one of the
- * marker interfaces.
+ * <code>true</code> if the this type directly implements one of the marker
+ * interfaces.
*/
private final boolean directlyImplementsMarker;
@@ -384,8 +385,8 @@
/**
* Return <code>true</code> if a class's fields should be considered for
- * serialization. If it returns <code>false</code> then none of the fields
- * of this class should be serialized.
+ * serialization. If it returns <code>false</code> then none of the fields of
+ * this class should be serialized.
*/
static boolean shouldConsiderFieldsForSerialization(TreeLogger logger,
JClassType type, boolean isSpeculative, TypeFilter filter) {
@@ -450,8 +451,8 @@
}
/**
- * Returns <code>true</code> if the field qualifies for serialization
- * without considering its type.
+ * Returns <code>true</code> if the field qualifies for serialization without
+ * considering its type.
*/
static boolean shouldConsiderForSerialization(TreeLogger logger,
boolean suppressNonStaticFinalFieldWarnings, JField field) {
@@ -459,6 +460,10 @@
return false;
}
+ if (field.isAnnotationPresent(GwtTransient.class)) {
+ return false;
+ }
+
if (field.isFinal()) {
logger.branch(suppressNonStaticFinalFieldWarnings ? TreeLogger.DEBUG
: TreeLogger.WARN, "Field '" + field.toString()
@@ -711,7 +716,8 @@
logSerializableTypes(logger, fieldSerializableTypes);
- return new SerializableTypeOracleImpl(fieldSerializableTypes, possiblyInstantiatedTypes);
+ return new SerializableTypeOracleImpl(fieldSerializableTypes,
+ possiblyInstantiatedTypes);
}
/**
@@ -748,8 +754,8 @@
/**
* Same as
- * {@link #checkTypeInstantiable(TreeLogger, JType, boolean, com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.TypePath)},
- * except that returns the set of instantiable subtypes.
+ * {@link #checkTypeInstantiable(TreeLogger, JType, boolean, com.google.gwt.user.rebind.rpc.SerializableTypeOracleBuilder.TypePath)}
+ * , except that returns the set of instantiable subtypes.
*/
boolean checkTypeInstantiable(TreeLogger logger, JType type,
boolean isSpeculative, TypePath path, Set<JClassType> instSubtypes) {
@@ -1064,9 +1070,8 @@
/*
* If my super type did not check out, then I am not instantiable and we
- * should error out... UNLESS I am *directly* serializable myself, in
- * which case it's ok for me to be the root of a new instantiable
- * hierarchy.
+ * should error out... UNLESS I amdirectly serializable myself, in which
+ * case it's ok for me to be the root of a new instantiable hierarchy.
*/
if (!superTypeOk && !isDirectlySerializable(classOrInterface)) {
return false;
diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java b/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
index 6be308f..8936d73 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializationUtils.java
@@ -15,6 +15,7 @@
*/
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.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
@@ -114,13 +115,10 @@
JField[] declFields = classType.getFields();
assert (declFields != null);
for (JField field : declFields) {
- // TODO(mmendez): this is shared with the serializable type oracle
- // builder, join with that
- if (field.isStatic() || field.isTransient() || field.isFinal()) {
- continue;
+ if (SerializableTypeOracleBuilder.shouldConsiderForSerialization(
+ TreeLogger.NULL, true, field)) {
+ fields.add(field);
}
-
- fields.add(field);
}
Collections.sort(fields, FIELD_COMPARATOR);
diff --git a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
index a3d9d13..e7b1258 100644
--- a/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
+++ b/user/test/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilderTest.java
@@ -105,6 +105,14 @@
private static final int EXPOSURE_NONE = TypeParameterExposureComputer.EXPOSURE_NONE;
+ private static void addGwtTransient(Set<CompilationUnit> units) {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.user.client.rpc;\n");
+ code.append("public @interface GwtTransient { }\n");
+ units.add(createMockCompilationUnit(
+ "com.google.gwt.user.client.rpc.GwtTransient", code));
+ }
+
private static void addICRSE(Set<CompilationUnit> units) {
StringBuffer code = new StringBuffer();
code.append("package com.google.gwt.user.client.rpc;\n");
@@ -167,6 +175,7 @@
}
private static void addStandardClasses(Set<CompilationUnit> units) {
+ addGwtTransient(units);
addJavaIoSerializable(units);
addJavaLangObject(units);
addJavaLangString(units);
@@ -2022,6 +2031,59 @@
}
/**
+ * Tests that STOB skips transient fields.
+ */
+ public void testTransient() throws UnableToCompleteException,
+ NotFoundException {
+ Set<CompilationUnit> units = new HashSet<CompilationUnit>();
+ addStandardClasses(units);
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import com.google.gwt.user.client.rpc.GwtTransient;\n");
+ code.append("import java.io.Serializable;\n");
+ code.append("public class A implements Serializable {\n");
+ code.append(" transient ServerOnly1 serverOnly1;\n");
+ code.append(" @GwtTransient ServerOnly2 serverOnly2;\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("A", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("class ServerOnly1 implements Serializable {\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("ServerOnly1", code));
+ }
+
+ {
+ StringBuilder code = new StringBuilder();
+ code.append("import java.io.Serializable;\n");
+ code.append("class ServerOnly2 implements Serializable {\n");
+ code.append("}\n");
+ units.add(createMockCompilationUnit("ServerOnly2", code));
+ }
+
+ TreeLogger logger = createLogger();
+ TypeOracle to = TypeOracleTestingUtils.buildTypeOracle(logger, units);
+
+ JClassType a = to.getType("A");
+ JClassType serverOnly1 = to.getType("ServerOnly1");
+ JClassType serverOnly2 = to.getType("ServerOnly2");
+
+ SerializableTypeOracleBuilder sob = createSerializableTypeOracleBuilder(
+ logger, to);
+ sob.addRootType(logger, a);
+ SerializableTypeOracle so = sob.build(logger);
+
+ assertSerializableTypes(so, a);
+ assertInstantiable(so, a);
+ assertNotFieldSerializable(so, serverOnly1);
+ assertNotFieldSerializable(so, serverOnly2);
+ }
+
+ /**
* Miscellaneous direct tests of {@link TypeConstrainer}.
*
* @throws UnableToCompleteException