Reviewers: bobv, Description: LinkedHashMap_CustomFieldSerializer currently uses reflection to access a private field, which fails in AppEngine. This patch provides a (possibly slower) alternative approach that does not require reflection, which is used as a fallback when reflection fails.
Please review this at http://gwt-code-reviews.appspot.com/54816 Affected files: user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java Index: user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java =================================================================== --- user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java (revision 5867) +++ user/src/com/google/gwt/user/client/rpc/core/java/util/LinkedHashMap_CustomFieldSerializer.java (working copy) @@ -49,21 +49,68 @@ } @SuppressWarnings("unchecked") // raw LinkedHashMap - private static boolean getAccessOrder(LinkedHashMap instance) - throws SerializationException { + private static boolean getAccessOrder(LinkedHashMap instance) { Field accessOrderField; try { accessOrderField = LinkedHashMap.class.getDeclaredField("accessOrder"); accessOrderField.setAccessible(true); return ((Boolean) accessOrderField.get(instance)).booleanValue(); } catch (SecurityException e) { - throw new SerializationException("Can't get accessOrder field", e); + // fall through } catch (NoSuchFieldException e) { - throw new SerializationException("Can't get accessOrder field", e); + // fall through } catch (IllegalArgumentException e) { - throw new SerializationException("Can't get accessOrder field", e); + // fall through } catch (IllegalAccessException e) { - throw new SerializationException("Can't get accessOrder field", e); + // fall through } + + // Use a (possibly slower) technique that does not require reflection. + return getAccessOrderNoReflection(instance); } + + /** + * Infers the value of the private accessOrder field of instance by examining + * its behavior on a set of test inputs, without using reflection. Note that + * this requires iterating over all of the keys in the instance, which could + * be slow. + * + * @param instance the instance to check + * @return the value of instance.accessOrder + */ + @SuppressWarnings("unchecked") // raw LinkedHashMap + private static boolean getAccessOrderNoReflection(LinkedHashMap instance) { + /* + * We insert key1, then key2, after which we access key1. We then iterate + * over the key set and observe the order in which keys are returned. The + * iterator will return keys in the order of least recent insertion or + * access, depending on the value of the accessOrder field within the + * LinkedHashMap instance. If the iterator is ordered by least recent + * insertion (accessOrder = false), we will encounter key1 first since key2 + * has been inserted more recently. If it is ordered by least recent access + * (accessOrder = true), we will encounter key2 first, since key1 has been + * accessed more recently. + */ + Object key1 = new Object(); + Object key2 = new Object(); + instance.put(key1, key1); // INSERT key1 + instance.put(key2, key2); // INSERT key2 + instance.get(key1); // ACCESS key1 + + boolean accessOrder = false; + for (Object key : instance.keySet()) { + if (key == key1) { + break; + } + if (key == key2) { + accessOrder = true; + break; + } + } + // Clean up + instance.remove(key1); + instance.remove(key2); + + return accessOrder; + } } --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
