You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2020/12/25 14:45:53 UTC
[ignite-3] 04/04: Fix generated serializer linkage errors.
This is an automated email from the ASF dual-hosted git repository.
amashenkov pushed a commit to branch gg-13618-asm
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit 0499142e36f92fb146daff8c51b568b6716f3adb
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Fri Dec 25 15:00:28 2020 +0300
Fix generated serializer linkage errors.
---
.../marshaller/asm/AsmSerializerGenerator.java | 316 +++++++++++----------
.../asm/IdentityMarshallerCodeGenerator.java | 17 +-
.../marshaller/asm/MarshallerCodeGenerator.java | 14 +-
.../asm/ObjectMarshallerCodeGenerator.java | 47 ++-
.../IdentityObjectMarshallerExprGenerator.java | 2 +-
.../codegen/MarshallerCodeGenerator.java | 10 +-
.../codegen/ObjectMarshallerCodeGenerator.java | 18 +-
.../marshaller/codegen/SerializerGenerator.java | 75 +++--
.../schema/marshaller/JavaSerializerTest.java | 93 +++++-
9 files changed, 365 insertions(+), 227 deletions(-)
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java
index 4c151a9..d49c7a7 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java
@@ -21,16 +21,17 @@ import com.facebook.presto.bytecode.Access;
import com.facebook.presto.bytecode.BytecodeBlock;
import com.facebook.presto.bytecode.ClassDefinition;
import com.facebook.presto.bytecode.ClassGenerator;
+import com.facebook.presto.bytecode.FieldDefinition;
import com.facebook.presto.bytecode.MethodDefinition;
import com.facebook.presto.bytecode.Parameter;
import com.facebook.presto.bytecode.ParameterizedType;
import com.facebook.presto.bytecode.Scope;
import com.facebook.presto.bytecode.Variable;
import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.control.TryCatch;
import com.facebook.presto.bytecode.expression.BytecodeExpression;
import com.facebook.presto.bytecode.expression.BytecodeExpressions;
import java.io.StringWriter;
-import java.nio.file.Paths;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.concurrent.TimeUnit;
@@ -49,7 +50,6 @@ import org.apache.ignite.internal.schema.marshaller.SerializationException;
import org.apache.ignite.internal.schema.marshaller.Serializer;
import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
import org.apache.ignite.internal.util.ObjectFactory;
-import org.jetbrains.annotations.NotNull;
/**
* {@link Serializer} code generator.
@@ -68,6 +68,7 @@ public class AsmSerializerGenerator implements SerializerFactory {
Class<?> keyClass,
Class<?> valClass
) {
+ final boolean isDebugEnabled = true; // log.isDebugEnabled();
final String className = SERIALIZER_CLASS_NAME_PREFIX + schema.version();
final StringWriter writer = new StringWriter();
@@ -80,34 +81,58 @@ public class AsmSerializerGenerator implements SerializerFactory {
long compilationTime = System.nanoTime();
generation = compilationTime - generation;
- final Class<? extends Serializer> aClass = ClassGenerator.classGenerator(getClassLoader())
- .fakeLineNumbers(true)
- .runAsmVerifier(true)
- .dumpRawBytecode(true)
- .dumpClassFilesTo(Paths.get("./target"))
-// .outputTo(writer)
- .defineClass(classDef, Serializer.class);
+ final ClassGenerator generator = ClassGenerator.classGenerator(getClassLoader());
+
+ if (isDebugEnabled) {
+ generator.outputTo(writer)
+ .fakeLineNumbers(true)
+ .runAsmVerifier(true)
+ .dumpRawBytecode(true);
+ }
+
+ final Class<? extends Serializer> aClass = generator.defineClass(classDef, Serializer.class);
compilationTime = System.nanoTime() - compilationTime;
- //TODO: pass code to logger on trace level.
- System.out.println("Serializer created: generated=" + TimeUnit.NANOSECONDS.toMicros(generation) + "us\n" +
- ", compiled=" + TimeUnit.NANOSECONDS.toMicros(compilationTime) + "us\n" +
- writer.toString());
+ if (isDebugEnabled) //TODO: pass code to logger on debug level.
+ System.out.println("ASM serializer created: generated=" + TimeUnit.NANOSECONDS.toMicros(generation) + "us" +
+ ", compiled=" + TimeUnit.NANOSECONDS.toMicros(compilationTime) + "us." + writer.toString());
// Instantiate serializer.
- return aClass.getDeclaredConstructor(SchemaDescriptor.class, Class.class, Class.class)
- .newInstance(schema, keyClass, valClass);
+ return aClass
+ .getDeclaredConstructor(
+ SchemaDescriptor.class,
+ ObjectFactory.class,
+ ObjectFactory.class)
+ .newInstance(
+ schema,
+ MarshallerUtil.factoryForClass(keyClass),
+ MarshallerUtil.factoryForClass(valClass));
}
- catch (Exception e) {
+ catch (Exception | LinkageError e) {
throw new IllegalStateException("Failed to create serializer for key-value pair: schemaVer=" + schema.version() +
", keyClass=" + keyClass.getSimpleName() + ", valueClass=" + valClass.getSimpleName() + " code=\n" + writer.toString(), e);
}
}
- @NotNull private ClassDefinition generateSerializerClass(String className,
- SchemaDescriptor schema, Class<?> keyClass, Class<?> valClass) throws ReflectiveOperationException {
+ /**
+ * Generates serializer class definition.
+ *
+ * @param className Serializer class name.
+ * @param schema Schema descriptor.
+ * @param keyClass Key class.
+ * @param valClass Value class.
+ * @return Generated java class definition.
+ */
+ private ClassDefinition generateSerializerClass(
+ String className,
+ SchemaDescriptor schema,
+ Class<?> keyClass,
+ Class<?> valClass
+ ) {
+ MarshallerCodeGenerator keyMarsh = createMarshaller(keyClass, schema.keyColumns(), 0);
+ MarshallerCodeGenerator valMarsh = createMarshaller(valClass, schema.valueColumns(), schema.keyColumns().length());
final ClassDefinition classDef = new ClassDefinition(
EnumSet.of(Access.PUBLIC),
@@ -117,12 +142,15 @@ public class AsmSerializerGenerator implements SerializerFactory {
classDef.declareAnnotation(Generated.class).setValue("value", getClass().getCanonicalName());
- MarshallerCodeGenerator keyMarsh = createMarshaller(keyClass, schema.keyColumns(), 0);
- MarshallerCodeGenerator valMarsh = createMarshaller(valClass, schema.valueColumns(), schema.keyColumns().length());
+ final FieldDefinition keyClassField = classDef.declareField(EnumSet.of(Access.PRIVATE, Access.STATIC, Access.FINAL),
+ "KEY_CLASS", Class.class);
+ final FieldDefinition valueClassField = classDef.declareField(EnumSet.of(Access.PRIVATE, Access.STATIC, Access.FINAL),
+ "VALUE_CLASS", Class.class);
- generateStaticHandlers(classDef, keyMarsh, valMarsh);
+ keyMarsh.initStaticHandlers(classDef, keyClassField);
+ valMarsh.initStaticHandlers(classDef, valueClassField);
- generateConstructor(classDef);
+ generateFieldsAndConstructor(classDef);
generateAssemblerFactoryMethod(classDef, schema, keyMarsh, valMarsh);
generateSerializeMethod(classDef, keyMarsh, valMarsh);
@@ -132,46 +160,66 @@ public class AsmSerializerGenerator implements SerializerFactory {
return classDef;
}
- private void generateStaticHandlers(
- ClassDefinition classDef,
- MarshallerCodeGenerator keyMarsh,
- MarshallerCodeGenerator valMarsh
+ /**
+ * Creates marshaller code generator for given class.
+ *
+ * @param tClass Target class.
+ * @param columns Columns that tClass mapped to.
+ * @param firstColIdx First column absolute index in schema.
+ * @return Marshaller code generator.
+ */
+ private static MarshallerCodeGenerator createMarshaller(
+ Class<?> tClass,
+ Columns columns,
+ int firstColIdx
) {
- keyMarsh.initStaticHandlers(classDef);
- valMarsh.initStaticHandlers(classDef);
- }
-
- @NotNull
- private static MarshallerCodeGenerator createMarshaller(Class<?> tClass, Columns columns, int firstColumnIdx) {
final BinaryMode mode = MarshallerUtil.mode(tClass);
if (mode == null)
- return new ObjectMarshallerCodeGenerator(columns, tClass, firstColumnIdx);
+ return new ObjectMarshallerCodeGenerator(columns, tClass, firstColIdx);
else
- return new IdentityMarshallerCodeGenerator(TupleColumnAccessCodeGenerator.createAccessor(mode, firstColumnIdx));
+ return new IdentityMarshallerCodeGenerator(tClass, TupleColumnAccessCodeGenerator.createAccessor(mode, firstColIdx));
}
- private void generateConstructor(ClassDefinition classDef) {
+ /**
+ * Generates fields and constructor.
+ *
+ * @param classDef Serializer class definition.
+ */
+ private void generateFieldsAndConstructor(ClassDefinition classDef) {
+ classDef.declareField(EnumSet.of(Access.PRIVATE, Access.FINAL), "keyFactory", ParameterizedType.type(ObjectFactory.class));
+ classDef.declareField(EnumSet.of(Access.PRIVATE, Access.FINAL), "valFactory", ParameterizedType.type(ObjectFactory.class));
+
final MethodDefinition constrDef = classDef.declareConstructor(
EnumSet.of(Access.PUBLIC),
Parameter.arg("schema", SchemaDescriptor.class),
- Parameter.arg("keyClass", Class.class),
- Parameter.arg("valClass", Class.class)
+ Parameter.arg("keyFactory", ParameterizedType.type(ObjectFactory.class)),
+ Parameter.arg("valFactory", ParameterizedType.type(ObjectFactory.class))
);
constrDef.getBody()
.append(constrDef.getThis())
.append(constrDef.getScope().getVariable("schema"))
.invokeConstructor(classDef.getSuperClass(), ParameterizedType.type(SchemaDescriptor.class))
+ .append(constrDef.getThis().setField("keyFactory", constrDef.getScope().getVariable("keyFactory")))
+ .append(constrDef.getThis().setField("valFactory", constrDef.getScope().getVariable("valFactory")))
.ret();
}
+ /**
+ * Generates helper method.
+ *
+ * @param classDef Serializer class definition.
+ * @param schema Schema descriptor.
+ * @param keyMarsh Key marshaller code generator.
+ * @param valMarsh Value marshaller code generator.
+ */
private void generateAssemblerFactoryMethod(
ClassDefinition classDef,
SchemaDescriptor schema,
MarshallerCodeGenerator keyMarsh,
MarshallerCodeGenerator valMarsh
- ) throws ReflectiveOperationException {
+ ) {
final MethodDefinition methodDef = classDef.declareMethod(
EnumSet.of(Access.PROTECTED),
"createAssembler",
@@ -250,21 +298,18 @@ public class AsmSerializerGenerator implements SerializerFactory {
body.retObject();
}
- @NotNull private BytecodeExpression getColumnValueSize(Variable obj, Variable cols, int i) {
- return BytecodeExpressions.invokeStatic(MarshallerUtil.class, "getValueSize",
- int.class,
- Arrays.asList(Object.class, NativeType.class),
- obj,
- cols.invoke("column", Column.class, BytecodeExpressions.constantInt(i))
- .invoke("type", NativeType.class)
- );
- }
-
+ /**
+ * Generates serialize method.
+ *
+ * @param classDef Serializer class definition.
+ * @param keyMarsh Key marshaller code generator.
+ * @param valMarsh Value marshaller code generator.
+ */
private void generateSerializeMethod(
ClassDefinition classDef,
MarshallerCodeGenerator keyMarsh,
MarshallerCodeGenerator valMarsh
- ) throws ReflectiveOperationException {
+ ) {
final MethodDefinition methodDef = classDef.declareMethod(
EnumSet.of(Access.PROTECTED),
"serialize0",
@@ -272,26 +317,25 @@ public class AsmSerializerGenerator implements SerializerFactory {
Parameter.arg("asm", TupleAssembler.class),
Parameter.arg("key", Object.class),
Parameter.arg("val", Object.class)
- )
- .addException(SerializationException.class);
+ ).addException(SerializationException.class);
+
methodDef.declareAnnotation(Override.class);
final Variable asm = methodDef.getScope().getVariable("asm");
- methodDef.getBody()
- .append(new IfStatement().condition(BytecodeExpressions.isNull(asm)).ifTrue(
- new BytecodeBlock()
- .append(BytecodeExpressions.newInstance(IllegalStateException.class, BytecodeExpressions.constantString("ASM can't be null.")))
- .throwObject()
- ));
-
- methodDef.getBody()
- .append(
- keyMarsh.marshallObject(
- classDef.getType(),
- asm,
- methodDef.getScope().getVariable("key"))
- )
+ methodDef.getBody().append(new IfStatement().condition(BytecodeExpressions.isNull(asm)).ifTrue(
+ new BytecodeBlock()
+ .append(BytecodeExpressions.newInstance(IllegalStateException.class, BytecodeExpressions.constantString("ASM can't be null.")))
+ .throwObject()
+ ));
+
+ final BytecodeBlock block = new BytecodeBlock();
+ block.append(
+ keyMarsh.marshallObject(
+ classDef.getType(),
+ asm,
+ methodDef.getScope().getVariable("key"))
+ )
.append(
valMarsh.marshallObject(
classDef.getType(),
@@ -300,120 +344,100 @@ public class AsmSerializerGenerator implements SerializerFactory {
)
.append(asm.invoke("build", byte[].class))
.retObject();
+
+ final Variable ex = methodDef.getScope().createTempVariable(Throwable.class);
+ methodDef.getBody().append(new TryCatch(
+ block,
+ new BytecodeBlock()
+ .putVariable(ex)
+ .append(BytecodeExpressions.newInstance(SerializationException.class, ex))
+ .throwObject(),
+ ParameterizedType.type(Throwable.class)
+ ));
+
}
- private void generateDeserializeKeyMethod(ClassDefinition classDef,
- MarshallerCodeGenerator keyMarsh) throws ReflectiveOperationException {
+ /**
+ * Generates deserialize method.
+ *
+ * @param classDef Serializer class definition.
+ * @param keyMarsh Key marshaller code generator.
+ */
+ private void generateDeserializeKeyMethod(ClassDefinition classDef, MarshallerCodeGenerator keyMarsh) {
final MethodDefinition methodDef = classDef.declareMethod(
EnumSet.of(Access.PROTECTED),
"deserializeKey0",
ParameterizedType.type(Object.class),
Parameter.arg("tuple", Tuple.class)
- )
- .addException(SerializationException.class);
+ ).addException(SerializationException.class);
+
methodDef.declareAnnotation(Override.class);
final Variable obj = methodDef.getScope().declareVariable(Object.class, "obj");
+ if (!keyMarsh.isSimpleType())
+ methodDef.getBody().append(obj.set(methodDef.getThis().getField("keyFactory", ObjectFactory.class)
+ .invoke("create", Object.class)));
+
methodDef.getBody()
.append(keyMarsh.unmarshallObject(classDef.getType(), methodDef.getScope().getVariable("tuple"), obj))
.append(obj)
.retObject();
}
- private void generateDeserializeValueMethod(ClassDefinition classDef,
- MarshallerCodeGenerator valMarsh) throws ReflectiveOperationException {
+ /**
+ * Generates serialize method.
+ *
+ * @param classDef Serializer class definition.
+ * @param valMarsh Value marshaller code generator.
+ */
+ private void generateDeserializeValueMethod(ClassDefinition classDef, MarshallerCodeGenerator valMarsh) {
final MethodDefinition methodDef = classDef.declareMethod(
EnumSet.of(Access.PROTECTED),
"deserializeValue0",
ParameterizedType.type(Object.class),
Parameter.arg("tuple", Tuple.class)
- )
- .addException(SerializationException.class);
+ ).addException(SerializationException.class);
+
methodDef.declareAnnotation(Override.class);
final Variable obj = methodDef.getScope().declareVariable(Object.class, "obj");
+ final BytecodeBlock block = new BytecodeBlock();
- methodDef.getBody()
- .append(valMarsh.unmarshallObject(classDef.getType(), methodDef.getScope().getVariable("tuple"), obj))
+ if (!valMarsh.isSimpleType())
+ block.append(obj.set(methodDef.getThis().getField("valFactory", ObjectFactory.class)
+ .invoke("create", Object.class)));
+
+ block.append(valMarsh.unmarshallObject(classDef.getType(), methodDef.getScope().getVariable("tuple"), obj))
.append(obj)
.retObject();
+
+ methodDef.getBody().append(block);
}
-// /**
-// * @param valMarsh Value marshaller code generator.
-// * @return Deserialize value method spec.
-// */
-// private MethodSpec generateDeserializeValueMethod(MarshallerCodeGenerator valMarsh) {
-// return MethodSpec
-// .methodBuilder("deserializeValue0")
-// .addAnnotation(Override.class)
-// .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
-// .addParameter(Tuple.class, "tuple", Modifier.FINAL)
-// .addException(SerializationException.class)
-// .returns(TypeName.OBJECT)
-//
-// .beginControlFlow("try")
-// .addCode(valMarsh.unmarshallObjectCode("tuple"))
-// .nextControlFlow("catch($T th)", Throwable.class)
-// .addStatement("throw new $T(th)", SerializationException.class)
-// .endControlFlow()
-// .build();
-// }
-//
-// /**
-// * @param keyMarsh Key marshaller code generator.
-// * @return Deserialize key method spec.
-// */
-// private MethodSpec generateDeserializeKeyMethod(MarshallerCodeGenerator keyMarsh) {
-// return MethodSpec
-// .methodBuilder("deserializeKey0")
-// .addAnnotation(Override.class)
-// .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
-// .addParameter(Tuple.class, "tuple", Modifier.FINAL)
-// .addException(SerializationException.class)
-// .returns(TypeName.OBJECT)
-//
-// .beginControlFlow("try")
-// .addCode(keyMarsh.unmarshallObjectCode("tuple"))
-// .nextControlFlow("catch($T th)", Throwable.class)
-// .addStatement("throw new $T(th)", SerializationException.class)
-// .endControlFlow()
-// .build();
-// }
-//
-// /**
-// * Creates marshaller code generator for given class.
-// *
-// * @param tClass Target class.
-// * @param factoryRefVar Object factory variable.
-// * @param columns Columns that tClass mapped to.
-// * @param firstColIdx First column absolute index in schema.
-// * @return Marshaller code generator.
-// */
-// private MarshallerCodeGenerator createObjectMarshaller(
-// Class<?> tClass,
-// @Nullable String factoryRefVar,
-// Columns columns,
-// int firstColIdx
-// ) {
-// BinaryMode mode = MarshallerUtil.mode(tClass);
-//
-// if (mode != null) // Simple type.
-// return new IdentityObjectMarshallerExprGenerator(TupleColumnAccessCodeGenerator.createAccessor(mode, firstColIdx));
-// else
-// return new ObjectMarshallerCodeGenerator(tClass, factoryRefVar, columns, firstColIdx);
-// }
-
- private static BytecodeExpression cretaObjectFactoryExpr(MethodDefinition constrDef, String aClass) {
- return BytecodeExpressions.invokeStatic(
- MarshallerUtil.class,
- "factoryForClass",
- ObjectFactory.class,
- constrDef.getScope().getVariable(aClass)
+ /**
+ * Generates column size expression.
+ *
+ * @param obj Target object.
+ * @param cols columns.
+ * @param colIdx Column index.
+ * @return Expression.
+ */
+ private BytecodeExpression getColumnValueSize(Variable obj, Variable cols, int colIdx) {
+ return BytecodeExpressions.invokeStatic(MarshallerUtil.class, "getValueSize",
+ int.class,
+ Arrays.asList(Object.class, NativeType.class),
+ obj,
+ cols.invoke("column", Column.class, BytecodeExpressions.constantInt(colIdx))
+ .invoke("type", NativeType.class)
);
}
+ /**
+ * Resolves current classoader.
+ *
+ * @return Classloader.
+ */
private static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader() == null ?
ClassLoader.getSystemClassLoader() :
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java
index ddf0d24..033f06c 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java
@@ -30,16 +30,31 @@ class IdentityMarshallerCodeGenerator implements MarshallerCodeGenerator {
/** Object field access expression generator. */
private final TupleColumnAccessCodeGenerator columnAccessor;
+ /** Target class. */
+ private final Class<?> tClass;
+
/**
* Constructor.
*
+ * @param tClass Target class.
* @param columnAccessor Tuple column code generator.
*/
- public IdentityMarshallerCodeGenerator(TupleColumnAccessCodeGenerator columnAccessor) {
+ public IdentityMarshallerCodeGenerator(Class<?> tClass, TupleColumnAccessCodeGenerator columnAccessor) {
+ this.tClass = tClass;
this.columnAccessor = columnAccessor;
}
/** {@inheritDoc} */
+ @Override public boolean isSimpleType() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Class<?> targetClass() {
+ return tClass;
+ }
+
+ /** {@inheritDoc} */
@Override public BytecodeNode getValue(ParameterizedType type, Variable key, int i) {
return key;
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java
index 6442bc4..2f14409 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.schema.marshaller.asm;
import com.facebook.presto.bytecode.BytecodeNode;
import com.facebook.presto.bytecode.ClassDefinition;
+import com.facebook.presto.bytecode.FieldDefinition;
import com.facebook.presto.bytecode.ParameterizedType;
import com.facebook.presto.bytecode.Variable;
@@ -27,6 +28,16 @@ import com.facebook.presto.bytecode.Variable;
*/
public interface MarshallerCodeGenerator {
/**
+ * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
+ */
+ boolean isSimpleType();
+
+ /**
+ * @return Target class.
+ */
+ Class<?> targetClass();
+
+ /**
* @param serializerClass Serializer type.
* @param obj Target object variable.
* @param colIdx Column index.
@@ -52,8 +63,9 @@ public interface MarshallerCodeGenerator {
/**
* @param classDef Class definition.
+ * @param tClassField Target class field definition.
*/
- default void initStaticHandlers(ClassDefinition classDef) {
+ default void initStaticHandlers(ClassDefinition classDef, FieldDefinition tClassField) {
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java
index cda6bb6..13b3981 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java
@@ -31,7 +31,7 @@ import com.facebook.presto.bytecode.expression.BytecodeExpressions;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.EnumSet;
import org.apache.ignite.internal.schema.Columns;
import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
@@ -50,7 +50,11 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
/** Object field access expression generators. */
private final TupleColumnAccessCodeGenerator[] columnAccessors;
- public ObjectMarshallerCodeGenerator(Columns columns, Class<?> tClass, int firstColIdx) {
+ public ObjectMarshallerCodeGenerator(
+ Columns columns,
+ Class<?> tClass,
+ int firstColIdx
+ ) {
this.columns = columns;
this.tClass = tClass;
columnAccessors = new TupleColumnAccessCodeGenerator[columns.length()];
@@ -68,29 +72,38 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
}
/** {@inheritDoc} */
+ @Override public boolean isSimpleType() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Class<?> targetClass() {
+ return tClass;
+ }
+
+ /** {@inheritDoc} */
@Override public BytecodeNode getValue(ParameterizedType serializerClass, Variable obj,
int i) {
final TupleColumnAccessCodeGenerator columnAccessor = columnAccessors[i];
return BytecodeExpressions.getStatic(serializerClass, "FIELD_HANDER_" + columnAccessor.columnIdx(), ParameterizedType.type(VarHandle.class))
- .invoke("get", columnAccessor.mappedType(), obj.cast(tClass));
+ .invoke("get", columnAccessor.mappedType(), obj);
}
/** {@inheritDoc} */
@Override public BytecodeBlock marshallObject(ParameterizedType serializerClass, Variable asm, Variable obj) {
final BytecodeBlock block = new BytecodeBlock();
- final BytecodeExpression obj0 = obj.cast(tClass);
for (int i = 0; i < columns.length(); i++) {
final TupleColumnAccessCodeGenerator columnAccessor = columnAccessors[i];
final BytecodeExpression fld = BytecodeExpressions.getStatic(serializerClass, "FIELD_HANDER_" + columnAccessor.columnIdx(), ParameterizedType.type(VarHandle.class))
- .invoke("get", columnAccessor.mappedType(), obj0);
+ .invoke("get", columnAccessor.mappedType(), obj);
final BytecodeExpression marshallNonNulExpr = asm.invoke(
columnAccessor.writeMethodName(),
void.class,
- Arrays.asList(columnAccessor.writeArgType()),
+ Collections.singletonList(columnAccessor.writeArgType()),
fld.cast(columnAccessor.writeArgType()));
if (columns.column(i).nullable())
@@ -109,9 +122,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
/** {@inheritDoc} */
@Override public BytecodeBlock unmarshallObject(ParameterizedType serializerClass, Variable tuple, Variable obj) {
final BytecodeBlock block = new BytecodeBlock();
- final BytecodeExpression obj0 = obj.cast(tClass);
-
- block.append(obj.set(BytecodeExpressions.newInstance(tClass)));
for (int i = 0; i < columns.length(); i++) {
final TupleColumnAccessCodeGenerator columnAccessor = columnAccessors[i];
@@ -123,7 +133,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
);
block.append(BytecodeExpressions.getStatic(serializerClass, "FIELD_HANDER_" + columnAccessor.columnIdx(), ParameterizedType.type(VarHandle.class))
- .invoke("set", void.class, obj0, val)
+ .invoke("set", void.class, obj, val)
);
}
@@ -131,19 +141,26 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
}
/** {@inheritDoc} */
- @Override public void initStaticHandlers(ClassDefinition classDef) {
+ @Override public void initStaticHandlers(ClassDefinition classDef, FieldDefinition tClassField) {
final MethodDefinition init = classDef.getClassInitializer();
-
- final BytecodeBlock body = init.getBody();
final Variable lookup = init.getScope().createTempVariable(MethodHandles.Lookup.class);
+ final BytecodeBlock body = init.getBody().append(
+ BytecodeExpressions.setStatic(
+ tClassField,
+ BytecodeExpressions.invokeStatic(Class.class, "forName", Class.class, BytecodeExpressions.constantString(tClass.getName()))
+ ));
+
+ if (isSimpleType())
+ return;
+
body.append(
lookup.set(
BytecodeExpressions.invokeStatic(
MethodHandles.class,
"privateLookupIn",
MethodHandles.Lookup.class,
- BytecodeExpressions.constantClass(tClass),
+ BytecodeExpressions.getStatic(tClassField),
BytecodeExpressions.invokeStatic(MethodHandles.class, "lookup", MethodHandles.Lookup.class))
));
@@ -155,7 +172,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
BytecodeExpressions.setStatic(fld, lookup.invoke(
"findVarHandle",
VarHandle.class,
- BytecodeExpressions.constantClass(tClass),
+ BytecodeExpressions.getStatic(tClassField),
BytecodeExpressions.constantString(columns.column(i).name()),
BytecodeExpressions.constantClass(columnAccessors[i].mappedType())
))
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java
index 77151d9..975599b 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java
@@ -60,7 +60,7 @@ class IdentityObjectMarshallerExprGenerator implements MarshallerCodeGenerator {
}
/** {@inheritDoc} */
- @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+ @Override public void initStaticHandlers(TypeSpec.Builder builder, String tClassExpr, CodeBlock.Builder staticBuilder) {
throw new UnsupportedOperationException("Static handlers are not applicable to simple types.");
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java
index 062d773..b2547d0 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java
@@ -51,14 +51,8 @@ interface MarshallerCodeGenerator {
/**
* @param classBuilder Class builder.
+ * @param tClassExpr Target class expression.
* @param staticInitBuilder Static initializer builder.
*/
- void initStaticHandlers(TypeSpec.Builder classBuilder, CodeBlock.Builder staticInitBuilder);
-
- /**
- * @return Marshaller target class.
- */
- default Class<?> getClazz() {
- return null;
- }
+ void initStaticHandlers(TypeSpec.Builder classBuilder, String tClassExpr, CodeBlock.Builder staticInitBuilder);
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java
index 49a123d..0330075 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java
@@ -34,9 +34,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
/** Target object factory var. */
private final String objectFactoryVar;
- /** Target class. */
- private final Class<?> tClass;
-
/** Mapped columns. */
private final Columns columns;
@@ -53,7 +50,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
*/
public ObjectMarshallerCodeGenerator(Class<?> tClass, String objectFactoryVar, Columns columns, int firstColIdx) {
this.objectFactoryVar = objectFactoryVar;
- this.tClass = tClass;
this.columns = columns;
columnAccessessors = new TupleColumnAccessCodeGenerator[this.columns.length()];
@@ -68,12 +64,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
throw new IllegalStateException(ex);
}
}
-
- /** {@inheritDoc} */
- @Override public Class<?> getClazz() {
- return tClass;
- }
-
/** {@inheritDoc} */
@Override public boolean isSimpleType() {
return false;
@@ -82,7 +72,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
/** {@inheritDoc} */
@Override public CodeBlock unmarshallObjectCode(String tupleExpr) {
final CodeBlock.Builder builder = CodeBlock.builder()
- .addStatement("$T obj = $L.create()", tClass, objectFactoryVar);
+ .addStatement("Object obj = $L.create()", objectFactoryVar);
for (int i = 0; i < columnAccessessors.length; i++)
builder.addStatement("FIELD_HANDLE_$L.set(obj, $L)", columnAccessessors[i].columnIdx(), columnAccessessors[i].read(tupleExpr));
@@ -107,7 +97,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
}
/** {@inheritDoc} */
- @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+ @Override public void initStaticHandlers(TypeSpec.Builder builder, String tClassExpr, CodeBlock.Builder staticBuilder) {
for (int i = 0; i < columnAccessessors.length; i++) {
builder.addField(FieldSpec.builder(
VarHandle.class,
@@ -117,8 +107,8 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
Modifier.STATIC)
.build());
- staticBuilder.addStatement("FIELD_HANDLE_$L = lookup.unreflectVarHandle($T.class.getDeclaredField($S))",
- columnAccessessors[i].columnIdx(), tClass, columns.column(i).name());
+ staticBuilder.addStatement("FIELD_HANDLE_$L = lookup.unreflectVarHandle($L.getDeclaredField($S))",
+ columnAccessessors[i].columnIdx(), tClassExpr, columns.column(i).name());
}
}
}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java
index 65352f1..d37bd0c 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.schema.marshaller.codegen;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
@@ -27,7 +28,7 @@ import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
-import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import javax.annotation.processing.Generated;
import javax.lang.model.element.Modifier;
import jdk.jfr.Experimental;
@@ -62,31 +63,35 @@ public class SerializerGenerator implements SerializerFactory {
Class<?> keyClass,
Class<?> valClass
) {
+ final boolean isDebugEnabled = false; // log.isDebugEnabled();
final String className = SERIALIZER_CLASS_NAME_PREFIX + schema.version();
+
try {
// Generate Serializer code.
- long generation = System.nanoTime();
+ long generated = System.nanoTime();
JavaFile javaFile = generateSerializerClassCode(className, schema, keyClass, valClass);
- generation = System.nanoTime() - generation;
+ generated = System.nanoTime() - generated;
- //TODO: pass code to logger on trace level.
-// System.out.println("Serializer code generated in " + TimeUnit.NANOSECONDS.toMicros(generation) + "us");
-// System.out.println(javaFile.toString());
+ if (isDebugEnabled)
+ System.out.println(javaFile.toString());
// Compile.
long compilation = System.nanoTime();
ClassLoader loader = CompilerUtils.compileCode(javaFile);
compilation = System.nanoTime() - compilation;
- // System.out.println("Serializer code compiled in " + TimeUnit.NANOSECONDS.toMicros(compilation) + "us");
+ //TODO: pass code to logger on trace level.
+ if (isDebugEnabled)
+ System.out.println("Serializer created: generated=" + TimeUnit.NANOSECONDS.toMicros(generated) + "us" +
+ ", compiled=" + TimeUnit.NANOSECONDS.toMicros(compilation) + "us." /*+ javaFile.toString()*/);
// Instantiate serializer.
return (Serializer)loader.loadClass(javaFile.packageName + '.' + className)
- .getDeclaredConstructor(SchemaDescriptor.class, Class.class, Class.class)
- .newInstance(schema, keyClass, valClass);
+ .getDeclaredConstructor(SchemaDescriptor.class)
+ .newInstance(schema);
}
- catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e){
+ catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Failed to create serializer for key-value pair: schemaVer=" + schema.version() +
", keyClass=" + keyClass.getSimpleName() + ", valueClass=" + valClass.getSimpleName(), e);
}
@@ -111,23 +116,21 @@ public class SerializerGenerator implements SerializerFactory {
final TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className)
.superclass(AbstractSerializer.class)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
- .addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", getClass().getCanonicalName()).build());
+ .addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", getClass().getName()).build());
- initFieldHandlers(keyMarsh, valMarsh, classBuilder);
+ initStaticFields(keyMarsh, valMarsh, keyClass, valClass, classBuilder);
classBuilder
- .addField(ParameterizedTypeName.get(ObjectFactory.class, keyClass), "keyFactory", Modifier.PRIVATE, Modifier.FINAL)
- .addField(ParameterizedTypeName.get(ObjectFactory.class, valClass), "valFactory", Modifier.PRIVATE, Modifier.FINAL)
+ .addField(ParameterizedTypeName.get(ObjectFactory.class), "keyFactory", Modifier.PRIVATE, Modifier.FINAL)
+ .addField(ParameterizedTypeName.get(ObjectFactory.class), "valFactory", Modifier.PRIVATE, Modifier.FINAL)
.addMethod(
// Constructor.
MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(SchemaDescriptor.class, "schema")
- .addParameter(Class.class, "keyClass")
- .addParameter(Class.class, "valClass")
.addStatement("super(schema)")
- .addStatement("this.keyFactory = $T.factoryForClass(keyClass)", MarshallerUtil.class)
- .addStatement("this.valFactory = $T.factoryForClass(valClass)", MarshallerUtil.class)
+ .addStatement("this.keyFactory = $T.factoryForClass(KEY_CLASS)", MarshallerUtil.class)
+ .addStatement("this.valFactory = $T.factoryForClass(VALUE_CLASS)", MarshallerUtil.class)
.build()
)
.addMethod(generateTupleAsseblerFactoryMethod(schema, keyMarsh, valMarsh))
@@ -143,7 +146,6 @@ public class SerializerGenerator implements SerializerFactory {
.build();
}
catch (Exception ex) {
- //TODO: fallback to java serializer?
throw new IllegalStateException(ex);
}
}
@@ -153,38 +155,55 @@ public class SerializerGenerator implements SerializerFactory {
* @param valMarsh Value marshaller code generator.
* @param classBuilder Serializer class builder.
*/
- private void initFieldHandlers(
+ private void initStaticFields(
MarshallerCodeGenerator keyMarsh,
MarshallerCodeGenerator valMarsh,
+ Class<?> keyClass,
+ Class<?> valueClass,
TypeSpec.Builder classBuilder
) {
- if (keyMarsh.isSimpleType() && valMarsh.isSimpleType())
- return; // No field hanlders needed for simple types.
+ classBuilder.addField(FieldSpec.builder(
+ Class.class,
+ "KEY_CLASS",
+ Modifier.PRIVATE,
+ Modifier.FINAL,
+ Modifier.STATIC)
+ .build());
+
+ classBuilder.addField(FieldSpec.builder(
+ Class.class,
+ "VALUE_CLASS",
+ Modifier.PRIVATE,
+ Modifier.FINAL,
+ Modifier.STATIC)
+ .build());
final CodeBlock.Builder staticInitBuilder = CodeBlock.builder()
.addStatement("$T.Lookup lookup", MethodHandles.class)
.beginControlFlow("try");
+ // Avoid direct class name usage in code to avoid potential linkage errors.
+ staticInitBuilder.addStatement("KEY_CLASS = $T.forName($S)", Class.class, keyClass.getName());
+ staticInitBuilder.addStatement("VALUE_CLASS = $T.forName($S)", Class.class, valueClass.getName());
+
if (!keyMarsh.isSimpleType()) {
staticInitBuilder.addStatement(
- "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+ "lookup = $T.privateLookupIn(KEY_CLASS, $T.lookup())",
MethodHandles.class,
- Objects.requireNonNull(keyMarsh.getClazz()),
MethodHandles.class
);
- keyMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
+ keyMarsh.initStaticHandlers(classBuilder, "KEY_CLASS", staticInitBuilder);
}
if (!valMarsh.isSimpleType()) {
staticInitBuilder.addStatement(
- "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+ "lookup = $T.privateLookupIn(VALUE_CLASS, $T.lookup())",
MethodHandles.class,
- Objects.requireNonNull(valMarsh.getClazz()),
MethodHandles.class
);
- valMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
+ valMarsh.initStaticHandlers(classBuilder, "VALUE_CLASS", staticInitBuilder);
}
staticInitBuilder
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
index 4db6027..8547c8e 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
@@ -230,8 +230,8 @@ public class JavaSerializerTest {
SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
- final Object key = PrivateTestObject.randomObject(rnd);
- final Object val = PrivateTestObject.randomObject(rnd);
+ final Object key = PrivateContructorTestObject.randomObject(rnd);
+ final Object val = PrivateContructorTestObject.randomObject(rnd);
Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
@@ -262,10 +262,33 @@ public class JavaSerializerTest {
final Object key = WrongTestObject.randomObject(rnd);
final Object val = WrongTestObject.randomObject(rnd);
- assertThrows(IllegalStateException.class,
- () -> factory.create(schema, key.getClass(), val.getClass()),
- "Class has no default constructor: class=org.apache.ignite.internal.schema.marshaller.JavaSerializerTest$WrongTestObject"
- );
+ assertThrows(IllegalStateException.class, () -> factory.create(schema, key.getClass(), val.getClass()));
+ }
+
+ /**
+ *
+ */
+ @ParameterizedTest
+ @MethodSource("serializerFactoryProvider")
+ public void testPrivateClass(SerializerFactory factory) throws SerializationException {
+ Column[] cols = new Column[] {
+ new Column("pLongCol", LONG, false),
+ };
+
+ SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
+
+ final Object key = PrivateTestObject.randomObject(rnd);
+ final Object val = PrivateTestObject.randomObject(rnd);
+
+ final ObjectFactory<?> objFactory = new ObjectFactory<>(PrivateTestObject.class);
+ final Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+ byte[] bytes = serializer.serialize(key, objFactory.create());
+
+ Object key1 = serializer.deserializeKey(bytes);
+ Object val1 = serializer.deserializeValue(bytes);
+
+ assertTrue(key.getClass().isInstance(key1));
+ assertTrue(val.getClass().isInstance(val1));
}
/**
@@ -498,12 +521,12 @@ public class JavaSerializerTest {
/**
* Test object with private constructor.
*/
- public static class PrivateTestObject {
+ public static class PrivateContructorTestObject {
/**
* @return Random TestObject.
*/
- static PrivateTestObject randomObject(Random rnd) {
- final PrivateTestObject obj = new PrivateTestObject();
+ static PrivateContructorTestObject randomObject(Random rnd) {
+ final PrivateContructorTestObject obj = new PrivateContructorTestObject();
obj.pLongCol = rnd.nextLong();
@@ -516,7 +539,7 @@ public class JavaSerializerTest {
/**
* Private constructor.
*/
- private PrivateTestObject() {
+ private PrivateContructorTestObject() {
}
/** {@inheritDoc} */
@@ -527,7 +550,7 @@ public class JavaSerializerTest {
if (o == null || getClass() != o.getClass())
return false;
- PrivateTestObject object = (PrivateTestObject)o;
+ PrivateContructorTestObject object = (PrivateContructorTestObject)o;
return pLongCol == object.pLongCol;
}
@@ -541,7 +564,7 @@ public class JavaSerializerTest {
/**
* Test object without default constructor.
*/
- private static class WrongTestObject {
+ public static class WrongTestObject {
/**
* @return Random TestObject.
*/
@@ -555,7 +578,51 @@ public class JavaSerializerTest {
/**
* Private constructor.
*/
- private WrongTestObject(long val) {
+ public WrongTestObject(long val) {
+ pLongCol = val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ WrongTestObject object = (WrongTestObject)o;
+
+ return pLongCol == object.pLongCol;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return Objects.hash(pLongCol);
+ }
+ }
+
+ /**
+ * Test object without default constructor.
+ */
+ private static class PrivateTestObject {
+ /**
+ * @return Random TestObject.
+ */
+ static PrivateTestObject randomObject(Random rnd) {
+ return new PrivateTestObject(rnd.nextInt());
+ }
+
+ /** Value. */
+ private long pLongCol;
+
+ /** Contructor. */
+ public PrivateTestObject() {
+ }
+
+ /**
+ * Private constructor.
+ */
+ public PrivateTestObject(long val) {
pLongCol = val;
}