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
This commit is contained in:
Michael Bayne
2011-03-29 23:55:11 +00:00
parent 10bea22f14
commit 3291089b8c
2 changed files with 48 additions and 34 deletions
+42 -19
View File
@@ -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<Field> IS_STREAMCLOSURE = new Predicate<Field>() {
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<Class<?>,Object> ZEROS = ImmutableMap.<Class<?>,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();
}
@@ -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;
}