Revision: 9690
Author: to...@google.com
Date: Tue Feb  8 07:28:00 2011
Log: Make FieldSerializers use reflection directly instead of round-tripping through JSNI. Speeds up DevMode.

Review at http://gwt-code-reviews.appspot.com/1310807

Review by: jbrosenb...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9690

Modified:
 /trunk/user/src/com/google/gwt/user/client/rpc/impl/ReflectionHelper.java
 /trunk/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java
 /trunk/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
/trunk/user/super/com/google/gwt/user/translatable/com/google/gwt/user/client/rpc/impl/ReflectionHelper.java

=======================================
--- /trunk/user/src/com/google/gwt/user/client/rpc/impl/ReflectionHelper.java Tue Dec 14 13:56:04 2010 +++ /trunk/user/src/com/google/gwt/user/client/rpc/impl/ReflectionHelper.java Tue Feb 8 07:28:00 2011
@@ -15,19 +15,83 @@
  */
 package com.google.gwt.user.client.rpc.impl;

+import com.google.gwt.dev.util.Pair;
+
+import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.LinkedHashMap;

 /**
  * Provides access to reflection capability, but only when running from
  * bytecode.
  */
 public class ReflectionHelper {
+
+  // Only used from single-threaded JS. Doesn't need to be thread-safe.
+  private static class Cache<K, V extends AccessibleObject>
+      extends LinkedHashMap<K, V> {
+
+    private static final int MAX_SIZE = 1024;
+
+    // These values lifted from defaults.
+    private static final int INITIAL_CAPACITY = 16;
+    private static final float LOAD_FACTOR = 0.75f;
+
+    public Cache() {
+      super(INITIAL_CAPACITY, LOAD_FACTOR, true);
+    }
+
+    @Override
+    protected boolean removeEldestEntry(
+        java.util.Map.Entry<K, V> eldest) {
+      return size() > MAX_SIZE;
+    }
+  }
+
+  private static final Cache<Class<?>, Constructor> constructorCache
+      = new Cache<Class<?>, Constructor>();
+
+  private static final Cache<Pair<Class<?>,String>, Field> fieldCache
+      = new Cache<Pair<Class<?>,String>, Field>();
+
+  private static Field findField(Class<?> klass, String name) {
+    Pair<Class<?>, String> key = Pair.<Class<?>,String>create(klass, name);
+    Field f = fieldCache.get(key);
+    if (f == null) {
+      try {
+        f = klass.getDeclaredField(name);
+      } catch (NoSuchFieldException ex) {
+        throw new RuntimeException(
+            "Unable to find field " + klass.getName() + "." + name, ex);
+      }
+      f.setAccessible(true);
+      fieldCache.put(key, f);
+    }
+    return f;
+  }
+
+  /**
+   * Gets the value of a field.
+   */
+  public static Object getField(Class<?> klass, Object obj, String name) {
+    Field f = findField(klass, name);
+    try {
+      return f.get(obj);
+    } catch (IllegalAccessException ex) {
+      throw new RuntimeException("Unexpected failure", ex);
+    }
+  }

   /**
    * Loads {@code klass} using Class.forName.
    */
-  public static Class<?> loadClass(String klass) throws Exception {
-    return Class.forName(klass);
+  public static Class<?> loadClass(String klass) {
+    try {
+      return Class.forName(klass);
+    } catch (ClassNotFoundException ex) {
+      throw new RuntimeException("Unable to find class " + klass, ex);
+    }
   }

   /**
@@ -35,10 +99,31 @@
* constructor. The constructor may have any access modifier (for example,
    * private).
    */
-  public static <T> T newInstance(Class<T> klass)
-      throws Exception {
-    Constructor<T> c = klass.getDeclaredConstructor();
-    c.setAccessible(true);
-    return c.newInstance();
+  @SuppressWarnings("unchecked")
+  public static <T> T newInstance(Class<T> klass) {
+    Constructor<T> c = (Constructor<T>) constructorCache.get(klass);
+    try {
+      if (c == null) {
+        c = klass.getDeclaredConstructor();
+        c.setAccessible(true);
+        constructorCache.put(klass, c);
+      }
+      return c.newInstance();
+    } catch (Exception ex) {
+      throw new RuntimeException("Unexpected failure", ex);
+    }
+  }
+
+  /**
+   * Sets the value of a field.
+   */
+  public static void setField(Class<?> klass, Object obj, String name,
+      Object value) throws Exception {
+    Field f = findField(klass, name);
+    try {
+      f.set(obj, value);
+    } catch (IllegalAccessException ex) {
+      throw new RuntimeException("Unexpected failure", ex);
+    }
   }
 }
=======================================
--- /trunk/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java Tue Dec 14 13:56:04 2010 +++ /trunk/user/src/com/google/gwt/user/rebind/rpc/FieldSerializerCreator.java Tue Feb 8 07:28:00 2011
@@ -18,6 +18,7 @@
 import com.google.gwt.core.client.UnsafeNativeLong;
 import com.google.gwt.core.client.impl.WeakMapping;
 import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.GeneratorContextExt;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JArrayType;
 import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -33,6 +34,7 @@
 import com.google.gwt.user.client.rpc.SerializationStreamReader;
 import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.client.rpc.core.java.lang.Object_Array_CustomFieldSerializer;
+import com.google.gwt.user.client.rpc.impl.ReflectionHelper;
 import com.google.gwt.user.client.rpc.impl.TypeHandler;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
@@ -51,8 +53,17 @@
  */
 public class FieldSerializerCreator {

+  /* NB: FieldSerializerCreator generates two different sets of code for
+   * DevMode and ProdMode. In ProdMode, the generated code uses the JSNI
+ * violator pattern to access private class members. In DevMode, the generated
+   * code uses ReflectionHelper instead of JSNI to avoid the many JSNI
+   * boundary-crossings which are slow in DevMode.
+   */
+
private static final String WEAK_MAPPING_CLASS_NAME = WeakMapping.class.getName();

+  private final GeneratorContextExt context;
+
   private final JClassType customFieldSerializer;

   private final boolean customFieldSerializerHasInstantiate;
@@ -61,6 +72,12 @@

   private final boolean isJRE;

+  private final boolean isProd;
+
+  private final String methodEnd;
+
+  private final String methodStart;
+
   private final JClassType serializableClass;

   private final JField[] serializableFields;
@@ -76,15 +93,19 @@
   /**
    * Constructs a field serializer for the class.
    */
-  public FieldSerializerCreator(TypeOracle typeOracle,
+  public FieldSerializerCreator(GeneratorContextExt context,
       SerializableTypeOracle typesSentFromBrowser,
       SerializableTypeOracle typesSentToBrowser, JClassType requestedClass,
       JClassType customFieldSerializer) {
+    this.context = context;
+    this.isProd = context.isProdMode();
+    methodStart = isProd ? "/*-{" : "{";
+    methodEnd = isProd ? "}-*/;" : "}";
     this.customFieldSerializer = customFieldSerializer;
     assert (requestedClass != null);
assert (requestedClass.isClass() != null || requestedClass.isArray() != null);

-    this.typeOracle = typeOracle;
+    this.typeOracle = context.getTypeOracle();
     this.typesSentFromBrowser = typesSentFromBrowser;
     this.typesSentToBrowser = typesSentToBrowser;
     serializableClass = requestedClass;
@@ -191,6 +212,7 @@
composerFactory.addImport(SerializationException.class.getCanonicalName()); composerFactory.addImport(SerializationStreamReader.class.getCanonicalName()); composerFactory.addImport(SerializationStreamWriter.class.getCanonicalName());
+    composerFactory.addImport(ReflectionHelper.class.getCanonicalName());
composerFactory.addAnnotationDeclaration("@SuppressWarnings(\"deprecation\")");
     if (needsTypeHandler()) {
composerFactory.addImplementedInterface(TypeHandler.class.getCanonicalName());
@@ -298,8 +320,10 @@
     JClassType isClass = serializableClass.isClass();

     boolean useViolator = false;
+    boolean isAccessible = true;
     if (isEnum == null && isClass != null) {
-      useViolator = !classIsAccessible() || !ctorIsAccessible();
+      isAccessible = classIsAccessible() && ctorIsAccessible();
+      useViolator = !isAccessible && isProd;
     }

     sourceWriter.print("public static" + (useViolator ? " native " : " "));
@@ -319,8 +343,14 @@
           + qualifiedSourceName + ".values();");
sourceWriter.println("assert (ordinal >= 0 && ordinal < values.length);");
       sourceWriter.println("return values[ordinal];");
-    } else if (useViolator) {
- sourceWriter.println("return @" + qualifiedSourceName + "::new()();");
+    } else if (!isAccessible) {
+      if (isProd) {
+ sourceWriter.println("return @" + qualifiedSourceName + "::new()();");
+      } else {
+        sourceWriter.println(
+            "return ReflectionHelper.newInstance(" + qualifiedSourceName
+                + ".class);");
+      }
     } else {
       sourceWriter.println("return new " + qualifiedSourceName + "();");
     }
@@ -634,26 +664,42 @@
   private void writeFieldGet(JField serializableField) {
     JType fieldType = serializableField.getType();
String fieldTypeQualifiedSourceName = fieldType.getQualifiedSourceName(); + String serializableClassQualifedName = serializableClass.getQualifiedSourceName();
     String fieldName = serializableField.getName();

     maybeSuppressLongWarnings(fieldType);
-    sourceWriter.print("private static native ");
+    sourceWriter.print("private static " + (isProd ? "native " : ""));
     sourceWriter.print(fieldTypeQualifiedSourceName);
     sourceWriter.print(" get");
     sourceWriter.print(Shared.capitalize(fieldName));
     sourceWriter.print("(");
-    sourceWriter.print(serializableClass.getQualifiedSourceName());
-    sourceWriter.println(" instance) /*-{");
+    sourceWriter.print(serializableClassQualifedName);
+    sourceWriter.print(" instance) ");
+    sourceWriter.println(methodStart);
+
     sourceWriter.indent();

-    sourceWriter.print("return instance.@");
- sourceWriter.print(SerializationUtils.getRpcTypeName(serializableClass));
-    sourceWriter.print("::");
-    sourceWriter.print(fieldName);
-    sourceWriter.println(";");
+    if (context.isProdMode()) {
+      sourceWriter.print("return instance.@");
+ sourceWriter.print(SerializationUtils.getRpcTypeName(serializableClass));
+      sourceWriter.print("::");
+      sourceWriter.print(fieldName);
+      sourceWriter.println(";");
+    } else {
+      sourceWriter.print("return ");
+      JPrimitiveType primType = fieldType.isPrimitive();
+      if (primType != null) {
+ sourceWriter.print("(" + primType.getQualifiedBoxedSourceName() + ") ");
+      } else {
+        sourceWriter.print("(" + fieldTypeQualifiedSourceName + ") ");
+      }
+      sourceWriter.println(
+          "ReflectionHelper.getField(" + serializableClassQualifedName
+              + ".class, instance, \"" + fieldName + "\");");
+    }

     sourceWriter.outdent();
-    sourceWriter.println("}-*/;");
+    sourceWriter.println(methodEnd);
     sourceWriter.println();
   }

@@ -667,24 +713,32 @@
     String fieldName = serializableField.getName();

     maybeSuppressLongWarnings(fieldType);
-    sourceWriter.print("private static native void ");
+ sourceWriter.print("private static " + (isProd ? "native " : "") + "void");
     sourceWriter.print(" set");
     sourceWriter.print(Shared.capitalize(fieldName));
     sourceWriter.print("(");
     sourceWriter.print(serializableClassQualifedName);
     sourceWriter.print(" instance, ");
     sourceWriter.print(fieldTypeQualifiedSourceName);
-    sourceWriter.println(" value) /*-{");
+    sourceWriter.println(" value) ");
+    sourceWriter.println(methodStart);
+
     sourceWriter.indent();

-    sourceWriter.print("instance.@");
- sourceWriter.print(SerializationUtils.getRpcTypeName(serializableClass));
-    sourceWriter.print("::");
-    sourceWriter.print(fieldName);
-    sourceWriter.println(" = value;");
+    if (context.isProdMode()) {
+      sourceWriter.print("instance.@");
+ sourceWriter.print(SerializationUtils.getRpcTypeName(serializableClass));
+      sourceWriter.print("::");
+      sourceWriter.print(fieldName);
+      sourceWriter.println(" = value;");
+    } else {
+      sourceWriter.println(
+          "ReflectionHelper.setField(" + serializableClassQualifedName
+              + ".class, instance, \"" + fieldName + "\", value);");
+    }

     sourceWriter.outdent();
-    sourceWriter.println("}-*/;");
+    sourceWriter.println(methodEnd);
     sourceWriter.println();
   }

=======================================
--- /trunk/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java Fri Feb 4 09:24:25 2011 +++ /trunk/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java Tue Feb 8 07:28:00 2011
@@ -234,7 +234,7 @@

JClassType customFieldSerializer = SerializableTypeOracleBuilder.findCustomFieldSerializer(
           typeOracle, type);
- FieldSerializerCreator creator = new FieldSerializerCreator(typeOracle,
+      FieldSerializerCreator creator = new FieldSerializerCreator(context,
           serializationOracle, deserializationOracle, (JClassType) type,
           customFieldSerializer);
       creator.realize(logger, ctx);
=======================================
--- /trunk/user/super/com/google/gwt/user/translatable/com/google/gwt/user/client/rpc/impl/ReflectionHelper.java Tue Dec 14 13:56:04 2010 +++ /trunk/user/super/com/google/gwt/user/translatable/com/google/gwt/user/client/rpc/impl/ReflectionHelper.java Tue Feb 8 07:28:00 2011
@@ -23,12 +23,33 @@
  */
 @GwtScriptOnly
 public class ReflectionHelper {
-  public static Class<?> loadClass(String name) throws Exception {
+
+  public static Object getField(Class<?> klass, Object obj, String name) {
throw new RuntimeException("ReflectionHelper can't be used from web mode.");
   }

-  public static <T> T newInstance(Class<T> klass)
-      throws Exception {
+  /**
+   * Loads {@code klass} using Class.forName.
+   */
+  public static Class<?> loadClass(String klass) {
+ throw new RuntimeException("ReflectionHelper can't be used from web mode.");
+  }
+
+  /**
+   * Creates a new instance of {@code klass}. The class must have a no-arg
+ * constructor. The constructor may have any access modifier (for example,
+   * private).
+   */
+  @SuppressWarnings("unchecked")
+  public static <T> T newInstance(Class<T> klass) {
+ throw new RuntimeException("ReflectionHelper can't be used from web mode.");
+  }
+
+  /**
+   * Sets the value of a field.
+   */
+  public static void setField(Class<?> klass, Object obj, String name,
+      Object value) {
throw new RuntimeException("ReflectionHelper can't be used from web mode.");
   }
 }

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to