Skip to content

Commit

Permalink
More refactoring, major FieldSerializer clean up, registration requir…
Browse files Browse the repository at this point in the history
…ed by default.

closes #398
closes #522
  • Loading branch information
NathanSweet committed Jul 16, 2017
1 parent 225bc3b commit fc7f0cc
Show file tree
Hide file tree
Showing 91 changed files with 1,335 additions and 1,984 deletions.
2 changes: 1 addition & 1 deletion .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<classpathentry excluding="**/.svn/*" kind="src" path="src"/>
<classpathentry excluding="**/.svn/*" kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="lib" path="lib/reflectasm-1.11.5-all.jar"/>
<classpathentry exported="true" kind="lib" path="lib/objenesis-2.1.jar"/>
<classpathentry exported="true" kind="lib" path="lib/minlog-1.3.0.jar"/>
<classpathentry kind="lib" path="lib/reflectasm-1.10.1-shaded.jar"/>
<classpathentry kind="lib" path="build/commons-lang-2.6.jar"/>
<classpathentry kind="lib" path="build/junit-4.12.jar"/>
<classpathentry kind="lib" path="build/hamcrest-core-1.3.jar"/>
Expand Down
3 changes: 3 additions & 0 deletions .settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=ignore
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
Expand Down
Binary file added build/jarjar-command-1.0.0.jar
Binary file not shown.
Binary file removed lib/reflectasm-1.10.1-shaded.jar
Binary file not shown.
Binary file added lib/reflectasm-1.11.5-all.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/ClassResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import com.esotericsoftware.kryo.io.Output;

/** Handles class registration, writing class identifiers to bytes, and reading class identifiers from bytes.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public interface ClassResolver {
/** Sets the Kryo instance that this ClassResolver will be used for. This is called automatically by Kryo. */
public void setKryo (Kryo kryo);
Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/DefaultSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
/** Sets the default serializer to use for the annotated class. The specified Serializer class must have a constructor taking a
* Kryo instance and a class, a Kryo instance, a class, or no arguments.
* @see Kryo#register(Class)
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DefaultSerializer {
Expand Down
131 changes: 23 additions & 108 deletions src/com/esotericsoftware/kryo/Kryo.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import static com.esotericsoftware.kryo.util.Util.*;
import static com.esotericsoftware.minlog.Log.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
Expand Down Expand Up @@ -104,15 +103,15 @@
import com.esotericsoftware.kryo.serializers.OptionalSerializers;
import com.esotericsoftware.kryo.serializers.TimeSerializers;
import com.esotericsoftware.kryo.util.DefaultClassResolver;
import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy;
import com.esotericsoftware.kryo.util.IdentityMap;
import com.esotericsoftware.kryo.util.IntArray;
import com.esotericsoftware.kryo.util.MapReferenceResolver;
import com.esotericsoftware.kryo.util.ObjectMap;
import com.esotericsoftware.kryo.util.Util;
import com.esotericsoftware.reflectasm.ConstructorAccess;

/** Maps classes to serializers so object graphs can be serialized automatically.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public class Kryo {
static public final byte NULL = 0;
static public final byte NOT_NULL = 1;
Expand All @@ -121,14 +120,14 @@ public class Kryo {
static private final int NO_REF = -2;

private SerializerFactory defaultSerializer = new ReflectionSerializerFactory(FieldSerializer.class);
private final ArrayList<DefaultSerializerEntry> defaultSerializers = new ArrayList(33);
private final ArrayList<DefaultSerializerEntry> defaultSerializers = new ArrayList(53);
private final int lowPriorityDefaultSerializerCount;

private final ClassResolver classResolver;
private int nextRegisterID;
private ClassLoader classLoader = getClass().getClassLoader();
private InstantiatorStrategy strategy = new DefaultInstantiatorStrategy();
private boolean registrationRequired;
private boolean registrationRequired = true;
private boolean warnUnregisteredClasses;

private int depth, maxDepth = Integer.MAX_VALUE;
Expand Down Expand Up @@ -224,6 +223,7 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) {
}

// --- Default serializers ---

/** Sets the serializer factory to use when no {@link #addDefaultSerializer(Class, Class) default serializers} match an
* object's type. Default is {@link ReflectionSerializerFactory} with {@link FieldSerializer}.
* @see #newDefaultSerializer(Class) */
Expand Down Expand Up @@ -641,11 +641,11 @@ public void writeClassAndObject (Output output, Object object) {
boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) {
if (object == null) {
if (TRACE || (DEBUG && depth == 1)) log("Write", null);
output.writeVarInt(Kryo.NULL, true);
output.writeInt(Kryo.NULL, true);
return true;
}
if (!referenceResolver.useReferences(object.getClass())) {
if (mayBeNull) output.writeVarInt(Kryo.NOT_NULL, true);
if (mayBeNull) output.writeInt(Kryo.NOT_NULL, true);
return false;
}

Expand All @@ -655,13 +655,13 @@ boolean writeReferenceOrNull (Output output, Object object, boolean mayBeNull) {
// If not the first time encountered, only write reference ID.
if (id != -1) {
if (DEBUG) debug("kryo", "Write object reference " + id + ": " + string(object));
output.writeVarInt(id + 2, true); // + 2 because 0 and 1 are used for NULL and NOT_NULL.
output.writeInt(id + 2, true); // + 2 because 0 and 1 are used for NULL and NOT_NULL.
return true;
}

// Otherwise write NOT_NULL and then the object bytes.
id = referenceResolver.addWrittenObject(object);
output.writeVarInt(NOT_NULL, true);
output.writeInt(NOT_NULL, true);
if (TRACE) trace("kryo", "Write initial object reference " + id + ": " + string(object));
return false;
}
Expand Down Expand Up @@ -810,7 +810,7 @@ int readReferenceOrNull (Input input, Class type, boolean mayBeNull) {
boolean referencesSupported = referenceResolver.useReferences(type);
int id;
if (mayBeNull) {
id = input.readVarInt(true);
id = input.readInt(true);
if (id == Kryo.NULL) {
if (TRACE || (DEBUG && depth == 1)) log("Read", null);
readObject = null;
Expand All @@ -825,7 +825,7 @@ int readReferenceOrNull (Input input, Class type, boolean mayBeNull) {
readReferenceIds.add(NO_REF);
return readReferenceIds.size;
}
id = input.readVarInt(true);
id = input.readInt(true);
}
if (id == NOT_NULL) {
// First time object has been encountered.
Expand Down Expand Up @@ -1015,14 +1015,18 @@ public ClassLoader getClassLoader () {
return classLoader;
}

/** If true, an exception is thrown when an unregistered class is encountered. Default is false.
/** If true, an exception is thrown when an unregistered class is encountered. Default is true.
* <p>
* If false, when an unregistered class is encountered, its fully qualified class name will be serialized and the
* {@link #addDefaultSerializer(Class, Class) default serializer} for the class used to serialize the object. Subsequent
* appearances of the class within the same object graph are serialized as an int id.
* <p>
* Registered classes are serialized as an int id, avoiding the overhead of serializing the class name, but have the drawback
* of needing to know the classes to be serialized up front. */
* of needing to know the classes to be serialized up front.
* <p>
* Requiring class registeration controls which classes Kryo will instantiate. When false, during deserialization Kryo will
* invoke the constructor for whatever class name is found in the data. It can be a security problem to allow arbitrary classes
* to be instantiated (and later finalized). */
public void setRegistrationRequired (boolean registrationRequired) {
this.registrationRequired = registrationRequired;
if (TRACE) trace("kryo", "Registration required: " + registrationRequired);
Expand All @@ -1032,15 +1036,13 @@ public boolean isRegistrationRequired () {
return registrationRequired;
}

/** If true, kryo writes a warn log telling about the classes unregistered. Default is false.
* <p>
* If false, no log are written when unregistered classes are encountered. */
/** If true, kryo writes a warn log entry when an unregistered class is encountered. Default is false. */
public void setWarnUnregisteredClasses (boolean warnUnregisteredClasses) {
this.warnUnregisteredClasses = warnUnregisteredClasses;
if (TRACE) trace("kryo", "Warn unregistered classes: " + warnUnregisteredClasses);
}

public boolean isWarnUnregisteredClasses () {
public boolean getWarnUnregisteredClasses () {
return warnUnregisteredClasses;
}

Expand Down Expand Up @@ -1166,6 +1168,10 @@ protected boolean isClosure (Class type) {
return type.getName().indexOf('/') >= 0;
}

public GenericsResolver getGenericsResolver () {
return genericsResolver;
}

static final class DefaultSerializerEntry {
final Class type;
final SerializerFactory serializerFactory;
Expand All @@ -1175,95 +1181,4 @@ static final class DefaultSerializerEntry {
this.serializerFactory = serializerFactory;
}
}

public GenericsResolver getGenericsResolver () {
return genericsResolver;
}

static public class DefaultInstantiatorStrategy implements org.objenesis.strategy.InstantiatorStrategy {
private InstantiatorStrategy fallbackStrategy;

public DefaultInstantiatorStrategy () {
}

public DefaultInstantiatorStrategy (InstantiatorStrategy fallbackStrategy) {
this.fallbackStrategy = fallbackStrategy;
}

public void setFallbackInstantiatorStrategy (final InstantiatorStrategy fallbackStrategy) {
this.fallbackStrategy = fallbackStrategy;
}

public InstantiatorStrategy getFallbackInstantiatorStrategy () {
return fallbackStrategy;
}

public ObjectInstantiator newInstantiatorOf (final Class type) {
if (!Util.isAndroid) {
// Use ReflectASM if the class is not a non-static member class.
Class enclosingType = type.getEnclosingClass();
boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass()
&& !Modifier.isStatic(type.getModifiers());
if (!isNonStaticMemberClass) {
try {
final ConstructorAccess access = ConstructorAccess.get(type);
return new ObjectInstantiator() {
public Object newInstance () {
try {
return access.newInstance();
} catch (Exception ex) {
throw new KryoException("Error constructing instance of class: " + className(type), ex);
}
}
};
} catch (Exception ignored) {
}
}
}
// Reflection.
try {
Constructor ctor;
try {
ctor = type.getConstructor((Class[])null);
} catch (Exception ex) {
ctor = type.getDeclaredConstructor((Class[])null);
ctor.setAccessible(true);
}
final Constructor constructor = ctor;
return new ObjectInstantiator() {
public Object newInstance () {
try {
return constructor.newInstance();
} catch (Exception ex) {
throw new KryoException("Error constructing instance of class: " + className(type), ex);
}
}
};
} catch (Exception ignored) {
}
if (fallbackStrategy == null) {
if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers()))
throw new KryoException("Class cannot be created (non-static member class): " + className(type));
else {
StringBuilder errorMessageSb = new StringBuilder(
"Class cannot be created (missing no-arg constructor): " + className(type));
if (type.getSimpleName().equals("")) {
errorMessageSb
.append("\n\tThis is an anonymous class, which is not serializable by default in Kryo. Possible solutions: ")
.append("1. Remove uses of anonymous classes, including double brace initialization, from the containing ")
.append(
"class. This is the safest solution, as anonymous classes don't have predictable names for serialization.")
.append("\n\t2. Register a FieldSerializer for the containing class and call ")
.append(
"FieldSerializer#setIgnoreSyntheticFields(false) on it. This is not safe but may be sufficient temporarily. ")
.append("Use at your own risk.");
}
throw new KryoException(errorMessageSb.toString());
}
}
// InstantiatorStrategy.
return fallbackStrategy.newInstantiatorOf(type);
}
}

}
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/KryoCopyable.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/** Allows implementing classes to perform their own copying. Hand written copying can be more efficient in some cases.
* <p>
* This method is used instead of the registered serializer {@link Serializer#copy(Kryo, Object)} method.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public interface KryoCopyable<T> {
/** Returns a copy that has the same values as this object. Before Kryo can be used to copy child objects,
* {@link Kryo#reference(Object)} must be called with the copy to ensure it can be referenced by the child objects.
Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/KryoException.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
package com.esotericsoftware.kryo;

/** General Kryo RuntimeException.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public class KryoException extends RuntimeException {
private StringBuffer trace;

Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/KryoSerializable.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* <p>
* The default serializer for KryoSerializable is {@link KryoSerializableSerializer}, which uses {@link Kryo#newInstance(Class)}
* to construct the class.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public interface KryoSerializable {
public void write (Kryo kryo, Output output);

Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/NotNull.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

/** Indicates a field can never be null when it is being serialized and deserialized. Some serializers use this to save space. Eg,
* {@link FieldSerializer} may save 1 byte per field.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull {
Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/ReferenceResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/** When references are enabled, this tracks objects that have already been read or written, provides an ID for objects that are
* written, and looks up by ID objects that have been read.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public interface ReferenceResolver {
/** Sets the Kryo instance that this ClassResolver will be used for. This is called automatically by Kryo. */
public void setKryo (Kryo kryo);
Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/Registration.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import org.objenesis.instantiator.ObjectInstantiator;

/** Describes the {@link Serializer} and class ID to use for a class.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public class Registration {
private final Class type;
private final int id;
Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/Serializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import com.esotericsoftware.kryo.io.Output;

/** Reads and writes objects to and from bytes.
* @author Nathan Sweet <misc@n4te.com> */
* @author Nathan Sweet */
public abstract class Serializer<T> {
private boolean acceptsNull, immutable;

Expand Down
Loading

0 comments on commit fc7f0cc

Please sign in to comment.