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:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user