Skip to content

Commit

Permalink
SerializeWith(getter, setter) and SerialOptions annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
apangin committed Jun 14, 2018
1 parent 399f24b commit 992fd09
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 28 deletions.
16 changes: 16 additions & 0 deletions src/one/nio/serial/FieldDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class FieldDescriptor {
private TypeDescriptor typeDescriptor;
private Field ownField;
private Field parentField;
private boolean useGetter;
private boolean useSetter;

FieldDescriptor(String nameDescriptor, TypeDescriptor typeDescriptor) {
this.nameDescriptor = nameDescriptor;
Expand Down Expand Up @@ -60,13 +62,27 @@ public Field parentField() {
return parentField;
}

public boolean useGetter() {
return useGetter;
}

public boolean useSetter() {
return useSetter;
}

public boolean is(String nameDescriptor, String typeDescriptor) {
return nameDescriptor.equals(this.nameDescriptor) && typeDescriptor.equals(this.typeDescriptor.toString());
}

public void assignField(Field ownField, Field parentField) {
this.ownField = ownField;
this.parentField = parentField;

SerializeWith serializeWith = ownField.getAnnotation(SerializeWith.class);
if (serializeWith != null) {
this.useGetter = !serializeWith.getter().isEmpty();
this.useSetter = !serializeWith.setter().isEmpty();
}
}

public static FieldDescriptor read(ObjectInput in) throws IOException {
Expand Down
15 changes: 11 additions & 4 deletions src/one/nio/serial/GeneratedSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,19 @@ private void getSerializableFields(Class cls, Field parentField, ArrayList<Field
if (cls != null) {
getSerializableFields(cls.getSuperclass(), parentField, list);
for (Field f : cls.getDeclaredFields()) {
if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT | 0x1000)) == 0) {
int modifiers = f.getModifiers();
if ((modifiers & Modifier.STATIC) != 0) {
continue;
}

if ((modifiers & Modifier.TRANSIENT) == 0) {
if (f.isSynthetic() && !Repository.hasOptions(cls, Repository.SYNTHETIC_FIELDS)) {
logFieldMismatch("Skipping synthetic field", f.getType(), cls, f.getName());
continue;
}
list.add(f);
list.add(parentField);
} else if ((f.getModifiers() & Modifier.STATIC) == 0
&& Repository.hasOptions(f.getType(), Repository.INLINE)
&& parentField == null) {
} else if (Repository.hasOptions(f.getType(), Repository.INLINE) && parentField == null) {
logFieldMismatch("Inlining field", f.getType(), cls, f.getName());
getSerializableFields(f.getType(), f, list);
}
Expand Down
16 changes: 11 additions & 5 deletions src/one/nio/serial/InvalidSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,34 @@ class InvalidSerializer extends Serializer {
super(cls);
}

InvalidSerializer(String descriptor) {
super(Object.class);
this.descriptor = descriptor;
this.origin = Origin.EXTERNAL;
}

@Override
public void calcSize(Object obj, CalcSizeStream css) throws NotSerializableException {
throw new NotSerializableException(cls.getName());
throw new NotSerializableException(descriptor);
}

@Override
public void write(Object obj, DataStream out) throws NotSerializableException {
throw new NotSerializableException(cls.getName());
throw new NotSerializableException(descriptor);
}

@Override
public Object read(DataStream in) throws NotSerializableException {
throw new NotSerializableException(cls.getName());
throw new NotSerializableException(descriptor);
}

@Override
public void skip(DataStream in) throws NotSerializableException {
throw new NotSerializableException(cls.getName());
throw new NotSerializableException(descriptor);
}

@Override
public void toJson(Object obj, StringBuilder builder) throws NotSerializableException {
throw new NotSerializableException(cls.getName());
throw new NotSerializableException(descriptor);
}
}
29 changes: 28 additions & 1 deletion src/one/nio/serial/MethodSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
package one.nio.serial;

import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;

public class MethodSerializer extends InvalidSerializer {
public class MethodSerializer<T> extends Serializer<T> {
static final AtomicInteger renamedMethods = new AtomicInteger();

private String holderName;
Expand Down Expand Up @@ -107,6 +108,32 @@ public Method method() {
return method;
}

@Override
public void calcSize(T obj, CalcSizeStream css) throws IOException {
// Typically MethodSerializer uniquely identifies Method - no need to write extra data
}

@Override
public void write(T obj, DataStream out) throws IOException {
// Typically MethodSerializer uniquely identifies Method - no need to write extra data
}

@Override
public T read(DataStream in) throws IOException, ClassNotFoundException {
// Possibly overridden in subclasses
throw new NotSerializableException(cls.getName());
}

@Override
public void skip(DataStream in) throws IOException, ClassNotFoundException {
// Typically MethodSerializer uniquely identifies Method - no need to read extra data
}

@Override
public void toJson(T obj, StringBuilder builder) throws IOException {
Json.appendString(builder, method.getName());
}

private Method findMatchingMethod() throws ClassNotFoundException {
String name = methodName;
String oldName = null;
Expand Down
30 changes: 17 additions & 13 deletions src/one/nio/serial/Repository.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class Repository {
public static final int SKIP_CUSTOM_SERIALIZATION = SKIP_READ_OBJECT | SKIP_WRITE_OBJECT;
public static final int INLINE = 4;
public static final int FIELD_SERIALIZATION = 8;
public static final int SYNTHETIC_FIELDS = 16;

public static final int ARRAY_STUBS = 1;
public static final int COLLECTION_STUBS = 2;
Expand Down Expand Up @@ -155,8 +156,7 @@ private static void addOptionalBootstrap(String className) {
addBootstrap(generateFor(Class.forName(className)));
} catch (ClassNotFoundException e) {
// Optional class is missing. Skip the slot to maintain the order of other bootstrap serializers.
log.warn("Missing optional bootstrap class: " + className);
nextBootstrapUid--;
addBootstrap(new InvalidSerializer(className));
}
}

Expand Down Expand Up @@ -306,6 +306,21 @@ private static Serializer generateFor(Class<?> cls) {
return serializer;
}

if (cls.isAnonymousClass() && (cls.getModifiers() & ENUM) == 0) {
log.warn("Trying to serialize anonymous class: " + cls.getName());
anonymousClasses.incrementAndGet();
}

SerialOptions options = cls.getAnnotation(SerialOptions.class);
if (options != null) {
setOptions(cls, options.value());
}

Renamed renamed = cls.getAnnotation(Renamed.class);
if (renamed != null) {
renamedClasses.put(renamed.from(), cls);
}

if (cls.isArray()) {
get(cls.getComponentType());
serializer = new ObjectArraySerializer(cls);
Expand Down Expand Up @@ -334,17 +349,6 @@ private static Serializer generateFor(Class<?> cls) {

serializer.generateUid();
provideSerializer(serializer);

if (cls.isAnonymousClass()) {
log.warn("Trying to serialize anonymous class: " + cls.getName());
anonymousClasses.incrementAndGet();
}

Renamed renamed = cls.getAnnotation(Renamed.class);
if (renamed != null) {
renamedClasses.put(renamed.from(), cls);
}

return serializer;
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/one/nio/serial/SerialOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2017 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package one.nio.serial;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SerialOptions {
int value();
}
29 changes: 29 additions & 0 deletions src/one/nio/serial/SerializeWith.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2018 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package one.nio.serial;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializeWith {
String getter() default "";
String setter() default "";
}
45 changes: 40 additions & 5 deletions src/one/nio/serial/gen/DelegateGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import one.nio.serial.FieldDescriptor;
import one.nio.serial.JsonName;
import one.nio.serial.Repository;
import one.nio.serial.SerializeWith;
import one.nio.util.JavaInternals;

import org.objectweb.asm.ClassVisitor;
Expand Down Expand Up @@ -100,7 +101,7 @@ private static void generateCalcSize(ClassVisitor cv, Class cls, FieldDescriptor
mv.visitVarInsn(ALOAD, 2);
mv.visitVarInsn(ALOAD, 1);
if (fd.parentField() != null) emitGetField(mv, fd.parentField());
emitGetField(mv, ownField);
emitGetField(mv, ownField, fd.useGetter());
emitTypeCast(mv, ownField.getType(), sourceClass);
mv.visitMethodInsn(INVOKEVIRTUAL, "one/nio/serial/CalcSizeStream", "writeObject", "(Ljava/lang/Object;)V");
}
Expand Down Expand Up @@ -144,7 +145,7 @@ private static void generateWrite(ClassVisitor cv, Class cls, FieldDescriptor[]
} else {
mv.visitVarInsn(ALOAD, 1);
if (fd.parentField() != null) emitGetField(mv, fd.parentField());
emitGetField(mv, ownField);
emitGetField(mv, ownField, fd.useGetter());
emitTypeCast(mv, ownField.getType(), sourceClass);
}

Expand Down Expand Up @@ -187,7 +188,7 @@ private static void generateRead(ClassVisitor cv, Class cls, FieldDescriptor[] f
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "one/nio/serial/DataStream", srcType.readMethod(), srcType.readSignature());
mv.visitInsn(srcType.convertTo(FieldType.Void));
} else if (Modifier.isFinal(ownField.getModifiers())) {
} else if (Modifier.isFinal(ownField.getModifiers()) && !fd.useSetter()) {
FieldType dstType = FieldType.valueOf(ownField.getType());
mv.visitFieldInsn(GETSTATIC, "one/nio/util/JavaInternals", "unsafe", "Lsun/misc/Unsafe;");
mv.visitVarInsn(ALOAD, 2);
Expand All @@ -205,7 +206,7 @@ private static void generateRead(ClassVisitor cv, Class cls, FieldDescriptor[] f
mv.visitMethodInsn(INVOKEVIRTUAL, "one/nio/serial/DataStream", srcType.readMethod(), srcType.readSignature());
if (srcType == FieldType.Object) emitTypeCast(mv, Object.class, sourceClass);
emitTypeCast(mv, sourceClass, ownField.getType());
emitPutField(mv, ownField);
emitPutField(mv, ownField, fd.useSetter());
}
}

Expand Down Expand Up @@ -292,7 +293,7 @@ private static void generateToJson(ClassVisitor cv, FieldDescriptor[] fds) {

mv.visitVarInsn(ALOAD, 1);
if (fd.parentField() != null) emitGetField(mv, fd.parentField());
emitGetField(mv, ownField);
emitGetField(mv, ownField, fd.useGetter());
emitTypeCast(mv, ownField.getType(), sourceClass);

switch (srcType) {
Expand Down Expand Up @@ -501,6 +502,40 @@ private static void emitTypeCast(MethodVisitor mv, Class<?> src, Class<?> dst) {
mv.visitInsn(FieldType.Void.convertTo(FieldType.valueOf(dst)));
}

private static void emitGetField(MethodVisitor mv, Field f, boolean useGetter) {
if (useGetter) {
String getter = f.getAnnotation(SerializeWith.class).getter();
try {
Method m = f.getDeclaringClass().getDeclaredMethod(getter);
if (Modifier.isStatic(m.getModifiers()) || m.getReturnType() != f.getType()) {
throw new IllegalArgumentException("Incompatible getter method: " + m);
}
emitInvoke(mv, m);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Getter method not found", e);
}
} else {
emitGetField(mv, f);
}
}

private static void emitPutField(MethodVisitor mv, Field f, boolean useSetter) {
if (useSetter) {
String setter = f.getAnnotation(SerializeWith.class).setter();
try {
Method m = f.getDeclaringClass().getDeclaredMethod(setter, f.getType());
if (Modifier.isStatic(m.getModifiers()) || m.getReturnType() != void.class) {
throw new IllegalArgumentException("Incompatible setter method: " + m);
}
emitInvoke(mv, m);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Setter method not found", e);
}
} else {
emitPutField(mv, f);
}
}

private static Label emitNullGuard(MethodVisitor mv, Class<?> dst) {
Label isNull = new Label();
Label nonNull = new Label();
Expand Down
Loading

0 comments on commit 992fd09

Please sign in to comment.