diff --git a/core/pom.xml b/core/pom.xml
index 261add1a4..16c4a19db 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -4,7 +4,7 @@
com.threerings
narya-parent
- 1.21
+ 1.22
narya
diff --git a/core/src/main/java/com/threerings/presents/data/InvocationMarshaller.java b/core/src/main/java/com/threerings/presents/data/InvocationMarshaller.java
index f6147343f..720088b1d 100644
--- a/core/src/main/java/com/threerings/presents/data/InvocationMarshaller.java
+++ b/core/src/main/java/com/threerings/presents/data/InvocationMarshaller.java
@@ -7,7 +7,9 @@ package com.threerings.presents.data;
import java.io.IOException;
+import com.threerings.io.GenStreamUtil;
import com.threerings.io.ObjectInputStream;
+import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamable;
import com.threerings.presents.client.Client;
@@ -244,17 +246,37 @@ public class InvocationMarshaller
}
/**
- * A custom reader method for {@link Streamable}.
+ * A custom reader method for {@link Streamable}. Reads {@code _invOid}/{@code _invCode}
+ * explicitly (by name, in declaration order) rather than via {@code defaultReadObject()} so the
+ * field order is deterministic across ART and HotSpot (Epic A1 Phase 5, approach b) — byte
+ * identical to the old reflective read. Marshaller SUBCLASSES intentionally have NO generated
+ * read/writeObject (they are field-free) so they inherit this method, which also performs the
+ * side-effect the generated flatten-mode streamers would otherwise skip: binding {@link #_invdir}
+ * from the stream's client. That binding is the whole reason this is a hand-written custom
+ * reader; a subclass overriding it would leave {@code _invdir} null and NPE on the first request.
*/
public void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
- in.defaultReadObject();
+ GenStreamUtil.readField(InvocationMarshaller.class, "_invOid", this, in);
+ GenStreamUtil.readField(InvocationMarshaller.class, "_invCode", this, in);
if (in instanceof ClientObjectInputStream) {
_invdir = ((ClientObjectInputStream)in).client.getInvocationDirector();
}
}
+ /**
+ * A custom writer method for {@link Streamable}; mirrors {@link #readObject} (explicit,
+ * deterministic field order) so field-free marshaller subclasses inherit it. Byte identical to
+ * the previous reflective default write.
+ */
+ public void writeObject (ObjectOutputStream out)
+ throws IOException
+ {
+ GenStreamUtil.writeField(InvocationMarshaller.class, "_invOid", this, out);
+ GenStreamUtil.writeField(InvocationMarshaller.class, "_invCode", this, out);
+ }
+
@Override
public String toString ()
{
diff --git a/pom.xml b/pom.xml
index a027437b0..27edaf7cf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
com.threerings
narya-parent
pom
- 1.21
+ 1.22
Narya Parent
Facilities for making networked multiplayer games.
diff --git a/tools/pom.xml b/tools/pom.xml
index b4f1e55ad..3eab7bfdd 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -4,7 +4,7 @@
com.threerings
narya-parent
- 1.21
+ 1.22
narya-tools