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