From 3291089b8c798a36a7ae9ee6be728e81c2b23be2 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 29 Mar 2011 23:55:11 +0000 Subject: [PATCH] Jumped the gun there a little. We need to be sure not to filter out the synthetic val$local fields that get inserted, only the this$outer field. And we need to pass "zero" values for val$ fields to the synthesized constructor. This means that closures cannot have more than one constructor, but since they're anonymous inner classes, they necessarily have only one constructor, so we should be fine. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6552 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- src/main/java/com/threerings/io/Streamer.java | 61 +++++++++++++------ .../com/threerings/io/StreamableTest.java | 21 ++----- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/threerings/io/Streamer.java b/src/main/java/com/threerings/io/Streamer.java index 9666dd3dc..1f1dc479a 100644 --- a/src/main/java/com/threerings/io/Streamer.java +++ b/src/main/java/com/threerings/io/Streamer.java @@ -40,10 +40,12 @@ import java.io.IOException; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.samskivert.util.ArrayUtil; import com.samskivert.util.ByteEnum; import com.samskivert.util.ByteEnumUtil; import com.samskivert.util.ClassUtil; @@ -456,22 +458,36 @@ public abstract class Streamer } // obtain the constructor we'll use to create instances - _ctorArgs = isClosure ? SINGLE_NULL_ARG : NO_ARGS; - for (Constructor ctor : _target.getDeclaredConstructors()) { - if (ctor.getParameterTypes().length == _ctorArgs.length) { - if (_ctor != null) { - throw new RuntimeException( - "Streamable has multiple applicable ctors [class=" + _target.getName() + - ", argCount=" + _ctorArgs.length + "]"); + if (!isClosure) { + // local our zero argument constructor + _ctorArgs = ArrayUtil.EMPTY_OBJECT; + for (Constructor ctor : _target.getDeclaredConstructors()) { + if (ctor.getParameterTypes().length == 0) { + _ctor = ctor; + break; } - _ctor = ctor; - // keep going, to be sure we catch conflicting ctors + } + + } else { + // a closure should have only one constructor, and we're going to pass bogus + // arguments to it (because unstreaming will take care of further initialization) + Constructor[] ctors = _target.getDeclaredConstructors(); + if (ctors.length > 1) { + throw new RuntimeException("Streamable closure classes must have only " + + "one constructor [class=" + _target.getName() + "]"); + } + _ctor = ctors[0]; + _ctor.setAccessible(true); + Class[] ptypes = _ctor.getParameterTypes(); + _ctorArgs = new Object[ptypes.length]; + for (int ii = 0; ii < ptypes.length; ii++) { + // this will be the appropriately typed zero, or null + _ctorArgs[ii] = ZEROS.get(ptypes[ii]); } } - - // if this is a streamable closure, we need to make the constructor accessible - if (isClosure) { - _ctor.setAccessible(true); + if (_ctor == null) { + throw new RuntimeException("Unable to find applicable ctor " + + "[class=" + _target.getName() + "]"); } } @@ -1009,13 +1025,20 @@ public abstract class Streamer /** Filters "NotStreamable" members and enclosing class refs from a field list. */ protected static final Predicate IS_STREAMCLOSURE = new Predicate() { public boolean apply (Field obj) { - return IS_STREAMABLE.apply(obj) && !obj.isSynthetic(); + return IS_STREAMABLE.apply(obj) && + !(obj.isSynthetic() && obj.getName().startsWith("this$")); } }; - /** Used by {@link ClassStreamer} to create instances. */ - protected static final Object[] NO_ARGS = new Object[] {}; - - /** Used by {@link ClassStreamer} to create instances. */ - protected static final Object[] SINGLE_NULL_ARG = new Object[] { null }; + /** Zero values for the primitive types. */ + protected static final Map,Object> ZEROS = ImmutableMap.,Object>builder(). + put(Boolean.TYPE, false). + put(Byte.TYPE, Byte.valueOf((byte)0)). + put(Short.TYPE, Short.valueOf((short)0)). + put(Character.TYPE, Character.valueOf((char)0)). + put(Integer.TYPE, Integer.valueOf(0)). + put(Long.TYPE, Long.valueOf(0)). + put(Float.TYPE, Float.valueOf(0)). + put(Double.TYPE, Double.valueOf(0)). + build(); } diff --git a/src/test/java/com/threerings/io/StreamableTest.java b/src/test/java/com/threerings/io/StreamableTest.java index 77de8cbeb..00c40d795 100644 --- a/src/test/java/com/threerings/io/StreamableTest.java +++ b/src/test/java/com/threerings/io/StreamableTest.java @@ -398,17 +398,13 @@ public class StreamableTest throws IOException, ClassNotFoundException { abstract class Action implements Streamable { - public int count; - public String arg; public abstract String act (); } Action act = new Action() { public String act () { - return count + ":" + arg; + return "bang!"; } }; - act.count = 3; - act.arg = "hello"; Action react = (Action)unflatten(flatten(act)); assertEquals(act.act(), react.act()); } @@ -418,17 +414,15 @@ public class StreamableTest throws IOException, ClassNotFoundException { abstract class Action implements Streamable.Closure { - public int count; - public String arg; public abstract String act (); } + final int count = unsafeOuterCall(); + final String arg = "hello"; Action act = new Action() { public String act () { return count + ":" + arg; } }; - act.count = 3; - act.arg = "hello"; Action react = (Action)unflatten(flatten(act)); assertEquals(act.act(), react.act()); } @@ -441,22 +435,19 @@ public class StreamableTest throws IOException, ClassNotFoundException { abstract class Action implements Streamable.Closure { - public int count; - public String arg; public abstract String act (); } + final String arg = "hello"; Action act = new Action() { public String act () { - return count + ":" + arg + ":" + naughtyOuterCall(); + return unsafeOuterCall() + ":" + arg; } }; - act.count = 3; - act.arg = "hello"; Action react = (Action)unflatten(flatten(act)); assertEquals(act.act(), react.act()); } - protected int naughtyOuterCall () + protected int unsafeOuterCall () { return 42; }