WIP checkpoint: Phase 5(b) narya streamer codegen for ART field-order
Pre-crash safety checkpoint (box rebooted mid-implementation; NOT verified complete). GenStreamableTask flatten-mode + GenStreamUtil runtime helper + ObjectInputStream changes; explicit read/writeObject instrumentation across presents net + dobj event classes so ART matches HotSpot's ClassUtil.getFields order without the global sort flag (no data migration). narya 1.21. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhfHCXYp2ctWi76Z91y9Ut
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.threerings</groupId>
|
<groupId>com.threerings</groupId>
|
||||||
<artifactId>narya-parent</artifactId>
|
<artifactId>narya-parent</artifactId>
|
||||||
<version>1.20</version>
|
<version>1.21</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>narya</artifactId>
|
<artifactId>narya</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
//
|
||||||
|
// Narya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2025 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// https://github.com/threerings/narya/blob/master/LICENSE
|
||||||
|
|
||||||
|
package com.threerings.io;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime support for the explicit {@code readObject}/{@code writeObject} methods emitted by {@link
|
||||||
|
* com.threerings.presents.tools.GenStreamableTask} in "flatten" mode.
|
||||||
|
*
|
||||||
|
* <p>The default reflective {@link Streamer} streams a class's fields in {@link
|
||||||
|
* com.samskivert.util.ClassUtil#getFields} order, which is <em>superclass-first</em> but relies on
|
||||||
|
* {@link Class#getDeclaredFields} <em>within</em> each class. That per-class order is not defined by
|
||||||
|
* the JVM spec and differs between HotSpot (server/desktop) and ART (Android) — so a default
|
||||||
|
* streamed object written by one and read by the other misaligns. The generated methods stream each
|
||||||
|
* field explicitly, in an order captured from HotSpot at build time, by calling {@link #readField}/
|
||||||
|
* {@link #writeField} here. Because those methods delegate to the very same {@link FieldMarshaller}
|
||||||
|
* the reflective streamer uses, the produced bytes are identical to the legacy reflective form on
|
||||||
|
* HotSpot (so existing persisted boards and DB blobs read unchanged, and desktop↔server is
|
||||||
|
* unaffected) while being deterministic on ART.
|
||||||
|
*/
|
||||||
|
public final class GenStreamUtil
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Reads the named field of {@code target} (declared by {@code declaringClass}) from the stream,
|
||||||
|
* using the same {@link FieldMarshaller} the reflective streamer would use.
|
||||||
|
*/
|
||||||
|
public static void readField (
|
||||||
|
Class<?> declaringClass, String name, Object target, ObjectInputStream in)
|
||||||
|
throws IOException, ClassNotFoundException
|
||||||
|
{
|
||||||
|
Entry entry = entry(declaringClass, name);
|
||||||
|
try {
|
||||||
|
entry.marshaller.readField(entry.field, target, in);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw ioe;
|
||||||
|
} catch (ClassNotFoundException cnfe) {
|
||||||
|
throw cnfe;
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw re;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException("Failure reading field [class=" + declaringClass.getName() +
|
||||||
|
", field=" + name + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the named field of {@code source} (declared by {@code declaringClass}) to the stream,
|
||||||
|
* using the same {@link FieldMarshaller} the reflective streamer would use.
|
||||||
|
*/
|
||||||
|
public static void writeField (
|
||||||
|
Class<?> declaringClass, String name, Object source, ObjectOutputStream out)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
Entry entry = entry(declaringClass, name);
|
||||||
|
try {
|
||||||
|
entry.marshaller.writeField(entry.field, source, out);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw ioe;
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
throw re;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException("Failure writing field [class=" + declaringClass.getName() +
|
||||||
|
", field=" + name + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Entry entry (Class<?> declaringClass, String name)
|
||||||
|
{
|
||||||
|
String key = declaringClass.getName() + "#" + name;
|
||||||
|
Entry entry = _cache.get(key);
|
||||||
|
if (entry == null) {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = declaringClass.getDeclaredField(name);
|
||||||
|
} catch (NoSuchFieldException nsfe) {
|
||||||
|
throw new RuntimeException("Missing streamed field [class=" +
|
||||||
|
declaringClass.getName() + ", field=" + name + "]", nsfe);
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
entry = new Entry(field, FieldMarshaller.getFieldMarshaller(field));
|
||||||
|
_cache.put(key, entry);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static final class Entry
|
||||||
|
{
|
||||||
|
public final Field field;
|
||||||
|
public final FieldMarshaller marshaller;
|
||||||
|
public Entry (Field field, FieldMarshaller marshaller) {
|
||||||
|
this.field = field;
|
||||||
|
this.marshaller = marshaller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Caches the resolved (accessible) field + its marshaller, keyed by declaring class + name. */
|
||||||
|
protected static final ConcurrentHashMap<String, Entry> _cache = new ConcurrentHashMap<String, Entry>();
|
||||||
|
|
||||||
|
private GenStreamUtil () {}
|
||||||
|
}
|
||||||
@@ -412,5 +412,5 @@ public class ObjectInputStream extends DataInputStream
|
|||||||
protected static volatile List<String> _allowedPrefixes;
|
protected static volatile List<String> _allowedPrefixes;
|
||||||
|
|
||||||
/** Used to activate verbose debug logging. */
|
/** Used to activate verbose debug logging. */
|
||||||
protected static final boolean STREAM_DEBUG = false;
|
protected static final boolean STREAM_DEBUG = Boolean.getBoolean("com.threerings.io.streamDebug");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,4 +165,24 @@ public class AttributeChangedEvent extends NamedEvent
|
|||||||
|
|
||||||
protected Object _value;
|
protected Object _value;
|
||||||
protected transient Object _oldValue = UNSET_OLD_VALUE;
|
protected transient Object _oldValue = UNSET_OLD_VALUE;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.AttributeChangedEvent.class, "_value", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.AttributeChangedEvent.class, "_value", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,4 +173,22 @@ public class CompoundEvent extends DEvent
|
|||||||
|
|
||||||
/** A list of the events associated with this compound event. */
|
/** A list of the events associated with this compound event. */
|
||||||
protected StreamableArrayList<DEvent> _events;
|
protected StreamableArrayList<DEvent> _events;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.CompoundEvent.class, "_events", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.CompoundEvent.class, "_events", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,4 +178,26 @@ public class ElementUpdatedEvent extends NamedEvent
|
|||||||
protected Object _value;
|
protected Object _value;
|
||||||
protected int _index;
|
protected int _index;
|
||||||
protected transient Object _oldValue = UNSET_OLD_VALUE;
|
protected transient Object _oldValue = UNSET_OLD_VALUE;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.ElementUpdatedEvent.class, "_value", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.ElementUpdatedEvent.class, "_index", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.ElementUpdatedEvent.class, "_value", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.ElementUpdatedEvent.class, "_index", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,4 +107,24 @@ public class EntryAddedEvent<T extends DSet.Entry> extends EntryEvent<T>
|
|||||||
/** Used when this event is generated on the authoritative server where object changes are made
|
/** Used when this event is generated on the authoritative server where object changes are made
|
||||||
* immediately. This lets us know not to apply ourselves when we're actually dispatched. */
|
* immediately. This lets us know not to apply ourselves when we're actually dispatched. */
|
||||||
protected transient boolean _alreadyApplied;
|
protected transient boolean _alreadyApplied;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.EntryAddedEvent.class, "_entry", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.EntryAddedEvent.class, "_entry", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,4 +109,24 @@ public class EntryRemovedEvent<T extends DSet.Entry> extends EntryEvent<T>
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected transient T _oldEntry = (T)UNSET_OLD_ENTRY;
|
protected transient T _oldEntry = (T)UNSET_OLD_ENTRY;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.EntryRemovedEvent.class, "_key", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.EntryRemovedEvent.class, "_key", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,4 +112,24 @@ public class EntryUpdatedEvent<T extends DSet.Entry> extends EntryEvent<T>
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected transient T _oldEntry = (T)UNSET_OLD_ENTRY;
|
protected transient T _oldEntry = (T)UNSET_OLD_ENTRY;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.EntryUpdatedEvent.class, "_entry", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.EntryUpdatedEvent.class, "_entry", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,4 +89,26 @@ public class InvocationNotificationEvent extends DEvent
|
|||||||
|
|
||||||
/** The arguments to the receiver method being invoked. */
|
/** The arguments to the receiver method being invoked. */
|
||||||
protected Object[] _args;
|
protected Object[] _args;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationNotificationEvent.class, "_receiverId", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationNotificationEvent.class, "_methodId", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationNotificationEvent.class, "_args", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationNotificationEvent.class, "_receiverId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationNotificationEvent.class, "_methodId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationNotificationEvent.class, "_args", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,4 +89,26 @@ public class InvocationRequestEvent extends DEvent
|
|||||||
|
|
||||||
/** The arguments to the method being invoked. */
|
/** The arguments to the method being invoked. */
|
||||||
protected Object[] _args;
|
protected Object[] _args;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationRequestEvent.class, "_invCode", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationRequestEvent.class, "_methodId", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationRequestEvent.class, "_args", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationRequestEvent.class, "_invCode", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationRequestEvent.class, "_methodId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationRequestEvent.class, "_args", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,4 +88,26 @@ public class InvocationResponseEvent extends DEvent
|
|||||||
|
|
||||||
/** The arguments to the method being invoked. */
|
/** The arguments to the method being invoked. */
|
||||||
protected Object[] _args;
|
protected Object[] _args;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationResponseEvent.class, "_requestId", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationResponseEvent.class, "_methodId", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.InvocationResponseEvent.class, "_args", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationResponseEvent.class, "_requestId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationResponseEvent.class, "_methodId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.InvocationResponseEvent.class, "_args", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,4 +74,24 @@ public class MessageEvent extends NamedEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Object[] _args;
|
protected Object[] _args;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.MessageEvent.class, "_args", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.MessageEvent.class, "_args", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,4 +78,24 @@ public class ObjectAddedEvent extends NamedEvent
|
|||||||
|
|
||||||
protected int _oid;
|
protected int _oid;
|
||||||
protected transient boolean _alreadyApplied;
|
protected transient boolean _alreadyApplied;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.ObjectAddedEvent.class, "_oid", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.ObjectAddedEvent.class, "_oid", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,4 +47,20 @@ public class ObjectDestroyedEvent extends DEvent
|
|||||||
buf.append("DESTROY:");
|
buf.append("DESTROY:");
|
||||||
super.toString(buf);
|
super.toString(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,4 +78,24 @@ public class ObjectRemovedEvent extends NamedEvent
|
|||||||
|
|
||||||
protected int _oid;
|
protected int _oid;
|
||||||
protected transient boolean _alreadyApplied;
|
protected transient boolean _alreadyApplied;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.ObjectRemovedEvent.class, "_oid", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.ObjectRemovedEvent.class, "_oid", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,4 +55,22 @@ public class ReleaseLockEvent extends NamedEvent
|
|||||||
buf.append("UNLOCK:");
|
buf.append("UNLOCK:");
|
||||||
super.toString(buf);
|
super.toString(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,4 +31,24 @@ public class ServerMessageEvent extends MessageEvent
|
|||||||
// this is what makes us server-only
|
// this is what makes us server-only
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.DEvent.class, "_toid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.dobj.MessageEvent.class, "_args", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.DEvent.class, "_toid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.NamedEvent.class, "_name", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.dobj.MessageEvent.class, "_args", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,11 @@ public class AESAuthRequest extends AuthRequest
|
|||||||
public void writeObject (ObjectOutputStream out)
|
public void writeObject (ObjectOutputStream out)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
out.defaultWriteObject();
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.UpstreamMessage.class, "messageId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_creds", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_version", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_zone", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_bootGroups", this, out);
|
||||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||||
ObjectOutputStream oOut = new ObjectOutputStream(byteOut);
|
ObjectOutputStream oOut = new ObjectOutputStream(byteOut);
|
||||||
oOut.writeObject(_clearCreds);
|
oOut.writeObject(_clearCreds);
|
||||||
@@ -136,7 +140,11 @@ public class AESAuthRequest extends AuthRequest
|
|||||||
public void readObject (ObjectInputStream in)
|
public void readObject (ObjectInputStream in)
|
||||||
throws IOException, ClassNotFoundException
|
throws IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
in.defaultReadObject();
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.UpstreamMessage.class, "messageId", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_creds", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_version", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_zone", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_bootGroups", this, in);
|
||||||
_contents = new byte[in.readInt()];
|
_contents = new byte[in.readInt()];
|
||||||
in.read(_contents);
|
in.read(_contents);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,11 @@ public class AuthRequest extends UpstreamMessage
|
|||||||
throws IOException, ClassNotFoundException
|
throws IOException, ClassNotFoundException
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
in.defaultReadObject();
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.UpstreamMessage.class, "messageId", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_creds", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_version", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_zone", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.AuthRequest.class, "_bootGroups", this, in);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
// if we fail here because the client is old, leave ourselves with a partially
|
// if we fail here because the client is old, leave ourselves with a partially
|
||||||
// initialized set of credentials, which the server will generally cope with by telling
|
// initialized set of credentials, which the server will generally cope with by telling
|
||||||
@@ -108,4 +112,17 @@ public class AuthRequest extends UpstreamMessage
|
|||||||
|
|
||||||
/** The set of bootstrap service groups this client is interested in. */
|
/** The set of bootstrap service groups this client is interested in. */
|
||||||
protected String[] _bootGroups;
|
protected String[] _bootGroups;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.UpstreamMessage.class, "messageId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_creds", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_version", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_zone", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.AuthRequest.class, "_bootGroups", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,4 +45,24 @@ public class FailureResponse extends DownstreamMessage
|
|||||||
|
|
||||||
protected int _oid;
|
protected int _oid;
|
||||||
protected String _message;
|
protected String _message;
|
||||||
|
|
||||||
|
// AUTO-GENERATED: METHODS START
|
||||||
|
// from interface Streamable
|
||||||
|
public void readObject (com.threerings.io.ObjectInputStream ins)
|
||||||
|
throws java.io.IOException, java.lang.ClassNotFoundException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.DownstreamMessage.class, "messageId", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.FailureResponse.class, "_oid", this, ins);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.FailureResponse.class, "_message", this, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from interface Streamable
|
||||||
|
public void writeObject (com.threerings.io.ObjectOutputStream out)
|
||||||
|
throws java.io.IOException
|
||||||
|
{
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.DownstreamMessage.class, "messageId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.FailureResponse.class, "_oid", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.FailureResponse.class, "_message", this, out);
|
||||||
|
}
|
||||||
|
// AUTO-GENERATED: METHODS END
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class PingRequest extends UpstreamMessage
|
|||||||
// network
|
// network
|
||||||
_packStamp = System.currentTimeMillis();
|
_packStamp = System.currentTimeMillis();
|
||||||
|
|
||||||
out.defaultWriteObject();
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.UpstreamMessage.class, "messageId", this, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,7 +76,7 @@ public class PingRequest extends UpstreamMessage
|
|||||||
// the network
|
// the network
|
||||||
_unpackStamp = System.currentTimeMillis();
|
_unpackStamp = System.currentTimeMillis();
|
||||||
|
|
||||||
in.defaultReadObject();
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.UpstreamMessage.class, "messageId", this, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -83,7 +83,9 @@ public class PongResponse extends DownstreamMessage
|
|||||||
_processDelay = (int)(_packStamp - _pingStamp);
|
_processDelay = (int)(_packStamp - _pingStamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
out.defaultWriteObject();
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.DownstreamMessage.class, "messageId", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.PongResponse.class, "_packStamp", this, out);
|
||||||
|
com.threerings.io.GenStreamUtil.writeField(com.threerings.presents.net.PongResponse.class, "_processDelay", this, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +98,9 @@ public class PongResponse extends DownstreamMessage
|
|||||||
// the network
|
// the network
|
||||||
_unpackStamp = System.currentTimeMillis();
|
_unpackStamp = System.currentTimeMillis();
|
||||||
|
|
||||||
in.defaultReadObject();
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.DownstreamMessage.class, "messageId", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.PongResponse.class, "_packStamp", this, in);
|
||||||
|
com.threerings.io.GenStreamUtil.readField(com.threerings.presents.net.PongResponse.class, "_processDelay", this, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<groupId>com.threerings</groupId>
|
<groupId>com.threerings</groupId>
|
||||||
<artifactId>narya-parent</artifactId>
|
<artifactId>narya-parent</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>1.20</version>
|
<version>1.21</version>
|
||||||
|
|
||||||
<name>Narya Parent</name>
|
<name>Narya Parent</name>
|
||||||
<description>Facilities for making networked multiplayer games.</description>
|
<description>Facilities for making networked multiplayer games.</description>
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.threerings</groupId>
|
<groupId>com.threerings</groupId>
|
||||||
<artifactId>narya-parent</artifactId>
|
<artifactId>narya-parent</artifactId>
|
||||||
<version>1.20</version>
|
<version>1.21</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>narya-tools</artifactId>
|
<artifactId>narya-tools</artifactId>
|
||||||
|
|||||||
@@ -6,16 +6,24 @@
|
|||||||
package com.threerings.presents.tools;
|
package com.threerings.presents.tools;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
import org.apache.tools.ant.DirectoryScanner;
|
||||||
import org.apache.tools.ant.types.FileSet;
|
import org.apache.tools.ant.types.FileSet;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import com.samskivert.util.ClassUtil;
|
||||||
|
|
||||||
|
import com.threerings.io.NotStreamable;
|
||||||
import com.threerings.io.Streamable;
|
import com.threerings.io.Streamable;
|
||||||
|
|
||||||
import com.threerings.presents.dobj.DObject;
|
import com.threerings.presents.dobj.DObject;
|
||||||
@@ -36,6 +44,21 @@ public class GenStreamableTask extends GenTask
|
|||||||
_filesets.add(set);
|
_filesets.add(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set, generate <em>self-contained</em> declaration-ordered {@code read/writeObject} methods
|
||||||
|
* that stream the entire flattened field set (in {@link ClassUtil#getFields} order, captured on
|
||||||
|
* HotSpot at build time) via {@link com.threerings.io.GenStreamUtil}, rather than the legacy
|
||||||
|
* per-class {@code super.read/writeObject(...)} + local-fields form. In this mode {@link DObject}
|
||||||
|
* subclasses are processed too, and a pass-through {@code readObject}/{@code writeObject} (one
|
||||||
|
* that calls {@code defaultReadObject}/{@code defaultWriteObject}) has that call replaced in
|
||||||
|
* place with the explicit field streaming (preserving any surrounding logic). This is what makes
|
||||||
|
* Streamables deterministic on ART (Android) while staying byte-identical on HotSpot. See {@link
|
||||||
|
* com.threerings.io.GenStreamUtil}.
|
||||||
|
*/
|
||||||
|
public void setFlatten (boolean flatten) {
|
||||||
|
_flatten = flatten;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute ()
|
public void execute ()
|
||||||
{
|
{
|
||||||
@@ -86,6 +109,11 @@ public class GenStreamableTask extends GenTask
|
|||||||
protected void processClass (File source, Class<?> sclass)
|
protected void processClass (File source, Class<?> sclass)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
|
if (_flatten) {
|
||||||
|
processFlatten(source, sclass);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
StreamableClassRequirements reqs = new StreamableClassRequirements(sclass);
|
StreamableClassRequirements reqs = new StreamableClassRequirements(sclass);
|
||||||
// we must implement Streamable, not be a DObject and have some fields that need to be
|
// we must implement Streamable, not be a DObject and have some fields that need to be
|
||||||
// streamed
|
// streamed
|
||||||
@@ -144,6 +172,164 @@ public class GenStreamableTask extends GenTask
|
|||||||
writeFile(source.getAbsolutePath(), sfile.generate(null, methods.toString()));
|
writeFile(source.getAbsolutePath(), sfile.generate(null, methods.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Flatten" mode: emit self-contained declaration-ordered {@code read/writeObject} that stream
|
||||||
|
* the whole {@link ClassUtil#getFields} field set via {@link com.threerings.io.GenStreamUtil}.
|
||||||
|
* Handles {@link DObject}s and replaces pass-through {@code default*Object()} calls in place.
|
||||||
|
*/
|
||||||
|
protected void processFlatten (File source, Class<?> sclass)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
// must be a concrete Streamable; never instrument abstract bases (their concrete subclasses
|
||||||
|
// stream the full flattened field set themselves, so an abstract base carrying a read/write
|
||||||
|
// method would "poison" any subclass that didn't get its own — silently dropping fields)
|
||||||
|
if (!Streamable.class.isAssignableFrom(sclass) || sclass.isInterface() ||
|
||||||
|
sclass.isEnum() || Modifier.isAbstract(sclass.getModifiers())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// gather the streamed fields in the exact order the reflective streamer uses (superclass
|
||||||
|
// first, then this class's declared fields), skipping @NotStreamable and closure refs
|
||||||
|
List<Field> fields = Lists.newArrayList();
|
||||||
|
for (Field field : ClassUtil.getFields(sclass)) {
|
||||||
|
if (field.getAnnotation(NotStreamable.class) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (field.isSynthetic() && field.getName().startsWith("this$")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fields.add(field);
|
||||||
|
}
|
||||||
|
if (fields.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String src = new String(Files.readAllBytes(source.toPath()));
|
||||||
|
|
||||||
|
boolean hasRead = src.contains("public void readObject");
|
||||||
|
boolean hasWrite = src.contains("public void writeObject");
|
||||||
|
boolean hasReadDefault = DEFAULT_READ.matcher(src).find();
|
||||||
|
boolean hasWriteDefault = DEFAULT_WRITE.matcher(src).find();
|
||||||
|
|
||||||
|
// if either method is hand-written custom streaming (present, but NOT a defaultXObject
|
||||||
|
// pass-through), the class fully manages its own streaming and is already deterministic;
|
||||||
|
// leave it entirely alone rather than risk a generated/hand-written mismatch
|
||||||
|
if ((hasRead && !hasReadDefault) || (hasWrite && !hasWriteDefault)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build whole methods for any streaming direction that has no method at all
|
||||||
|
StringBuilder methods = new StringBuilder();
|
||||||
|
if (!hasRead) {
|
||||||
|
methods.append(FLAT_READ_OPEN);
|
||||||
|
for (Field field : fields) {
|
||||||
|
methods.append(" ").append(flatReadStatement(field, "ins")).append("\n");
|
||||||
|
}
|
||||||
|
methods.append(READ_CLOSE);
|
||||||
|
}
|
||||||
|
if (!hasWrite) {
|
||||||
|
if (methods.length() > 0) {
|
||||||
|
methods.append("\n");
|
||||||
|
}
|
||||||
|
methods.append(FLAT_WRITE_OPEN);
|
||||||
|
for (Field field : fields) {
|
||||||
|
methods.append(" ").append(flatWriteStatement(field, "out")).append("\n");
|
||||||
|
}
|
||||||
|
methods.append(WRITE_CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean changed = false;
|
||||||
|
// replace a pass-through default read/write call in place, preserving surrounding logic
|
||||||
|
if (hasReadDefault) {
|
||||||
|
src = substituteDefault(src, DEFAULT_READ, fields, true);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (hasWriteDefault) {
|
||||||
|
src = substituteDefault(src, DEFAULT_WRITE, fields, false);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
// insert whole methods just before the class-closing brace, so we NEVER disturb the
|
||||||
|
// "// AUTO-GENERATED: METHODS" section (which holds the gendobj/genservice accessors) —
|
||||||
|
// SourceFile.generate() would replace that section wholesale and drop the accessors
|
||||||
|
if (methods.length() > 0) {
|
||||||
|
src = insertBeforeClassClose(src, methods.toString());
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
System.err.println("Converting (flatten) " + sclass.getName() + "...");
|
||||||
|
writeFile(source.getAbsolutePath(), src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts {@code methods} (already 4-space indented, newline-terminated) just before the final
|
||||||
|
* top-level class-closing brace of {@code src}.
|
||||||
|
*/
|
||||||
|
protected String insertBeforeClassClose (String src, String methods)
|
||||||
|
{
|
||||||
|
String[] lines = src.split("\n", -1);
|
||||||
|
int lastBrace = -1;
|
||||||
|
for (int ii = 0; ii < lines.length; ii++) {
|
||||||
|
if (lines[ii].trim().equals("}")) {
|
||||||
|
lastBrace = ii;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastBrace < 0) {
|
||||||
|
throw new RuntimeException("No class-closing brace found");
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int ii = 0; ii < lines.length; ii++) {
|
||||||
|
if (ii == lastBrace) {
|
||||||
|
sb.append("\n").append(methods);
|
||||||
|
}
|
||||||
|
sb.append(lines[ii]);
|
||||||
|
if (ii < lines.length - 1) {
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the single {@code recv.defaultReadObject()} / {@code recv.defaultWriteObject()}
|
||||||
|
* statement matched by {@code pat} with an explicit, same-indentation block that streams every
|
||||||
|
* field via {@link com.threerings.io.GenStreamUtil}, reusing the receiver variable name.
|
||||||
|
*/
|
||||||
|
protected String substituteDefault (String src, Pattern pat, List<Field> fields, boolean read)
|
||||||
|
{
|
||||||
|
Matcher m = pat.matcher(src);
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
while (m.find()) {
|
||||||
|
String indent = m.group(1), recv = m.group(2);
|
||||||
|
StringBuilder block = new StringBuilder();
|
||||||
|
for (int ii = 0; ii < fields.size(); ii++) {
|
||||||
|
if (ii > 0) {
|
||||||
|
block.append("\n");
|
||||||
|
}
|
||||||
|
Field field = fields.get(ii);
|
||||||
|
block.append(indent).append(
|
||||||
|
read ? flatReadStatement(field, recv) : flatWriteStatement(field, recv));
|
||||||
|
}
|
||||||
|
m.appendReplacement(sb, Matcher.quoteReplacement(block.toString()));
|
||||||
|
}
|
||||||
|
m.appendTail(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String flatReadStatement (Field field, String recv)
|
||||||
|
{
|
||||||
|
return "com.threerings.io.GenStreamUtil.readField(" +
|
||||||
|
field.getDeclaringClass().getCanonicalName() + ".class, \"" + field.getName() +
|
||||||
|
"\", this, " + recv + ");";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String flatWriteStatement (Field field, String recv)
|
||||||
|
{
|
||||||
|
return "com.threerings.io.GenStreamUtil.writeField(" +
|
||||||
|
field.getDeclaringClass().getCanonicalName() + ".class, \"" + field.getName() +
|
||||||
|
"\", this, " + recv + ");";
|
||||||
|
}
|
||||||
|
|
||||||
protected String toReadObject (Field field)
|
protected String toReadObject (Field field)
|
||||||
{
|
{
|
||||||
Class<?> type = field.getType();
|
Class<?> type = field.getType();
|
||||||
@@ -196,6 +382,9 @@ public class GenStreamableTask extends GenTask
|
|||||||
/** A list of filesets that contain tile images. */
|
/** A list of filesets that contain tile images. */
|
||||||
protected ArrayList<FileSet> _filesets = Lists.newArrayList();
|
protected ArrayList<FileSet> _filesets = Lists.newArrayList();
|
||||||
|
|
||||||
|
/** Whether to generate self-contained flattened methods (Android/ART support). */
|
||||||
|
protected boolean _flatten;
|
||||||
|
|
||||||
protected static final String READ_OPEN =
|
protected static final String READ_OPEN =
|
||||||
" // from interface Streamable\n" +
|
" // from interface Streamable\n" +
|
||||||
" public void readObject (ObjectInputStream ins)\n" +
|
" public void readObject (ObjectInputStream ins)\n" +
|
||||||
@@ -209,4 +398,23 @@ public class GenStreamableTask extends GenTask
|
|||||||
" throws IOException\n" +
|
" throws IOException\n" +
|
||||||
" {\n";
|
" {\n";
|
||||||
protected static final String WRITE_CLOSE = " }\n";
|
protected static final String WRITE_CLOSE = " }\n";
|
||||||
|
|
||||||
|
// fully-qualified signatures for flatten mode so no imports are required in the target file
|
||||||
|
protected static final String FLAT_READ_OPEN =
|
||||||
|
" // from interface Streamable\n" +
|
||||||
|
" public void readObject (com.threerings.io.ObjectInputStream ins)\n" +
|
||||||
|
" throws java.io.IOException, java.lang.ClassNotFoundException\n" +
|
||||||
|
" {\n";
|
||||||
|
protected static final String FLAT_WRITE_OPEN =
|
||||||
|
" // from interface Streamable\n" +
|
||||||
|
" public void writeObject (com.threerings.io.ObjectOutputStream out)\n" +
|
||||||
|
" throws java.io.IOException\n" +
|
||||||
|
" {\n";
|
||||||
|
|
||||||
|
/** Matches a pass-through {@code recv.defaultReadObject();} statement (captures indent, recv). */
|
||||||
|
protected static final Pattern DEFAULT_READ =
|
||||||
|
Pattern.compile("(?m)^([ \\t]*)(\\w+)\\.defaultReadObject\\s*\\(\\s*\\)\\s*;[ \\t]*$");
|
||||||
|
/** Matches a pass-through {@code recv.defaultWriteObject();} statement. */
|
||||||
|
protected static final Pattern DEFAULT_WRITE =
|
||||||
|
Pattern.compile("(?m)^([ \\t]*)(\\w+)\\.defaultWriteObject\\s*\\(\\s*\\)\\s*;[ \\t]*$");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
gendobj=com.threerings.presents.tools.GenDObjectTask
|
gendobj=com.threerings.presents.tools.GenDObjectTask
|
||||||
|
genstreamable=com.threerings.presents.tools.GenStreamableTask
|
||||||
genservice=com.threerings.presents.tools.GenServiceTask
|
genservice=com.threerings.presents.tools.GenServiceTask
|
||||||
genreceiver=com.threerings.presents.tools.GenReceiverTask
|
genreceiver=com.threerings.presents.tools.GenReceiverTask
|
||||||
instream=com.threerings.presents.tools.InstrumentStreamableTask
|
instream=com.threerings.presents.tools.InstrumentStreamableTask
|
||||||
|
|||||||
Reference in New Issue
Block a user