/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.serializer; import io.datakernel.asm.Annotations; import io.datakernel.bytebuf.ByteBuf; import io.datakernel.codegen.ClassBuilder; import io.datakernel.codegen.DefiningClassLoader; import io.datakernel.codegen.Expression; import io.datakernel.codegen.Variable; import io.datakernel.codegen.utils.Preconditions; import io.datakernel.serializer.annotations.*; import io.datakernel.serializer.asm.*; import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.file.Path; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import static io.datakernel.codegen.Expressions.*; import static io.datakernel.codegen.utils.Preconditions.check; import static io.datakernel.codegen.utils.Preconditions.checkNotNull; import static java.lang.Character.toUpperCase; import static java.lang.reflect.Modifier.*; import static java.util.Arrays.asList; /** * Scans fields of classes for serialization. */ public final class SerializerBuilder { private final AtomicInteger counter = new AtomicInteger(); private final DefiningClassLoader definingClassLoader; private String profile; private int version = Integer.MAX_VALUE; private Path saveBytecodePath; private CompatibilityLevel compatibilityLevel = CompatibilityLevel.LEVEL_3; private final Map<Class<?>, SerializerGenBuilder> typeMap = new LinkedHashMap<>(); private final Map<Class<? extends Annotation>, Class<? extends Annotation>> annotationsExMap = new LinkedHashMap<>(); private final Map<Class<? extends Annotation>, AnnotationHandler<?, ?>> annotationsMap = new LinkedHashMap<>(); private final Map<String, Collection<Class<?>>> extraSubclassesMap = new HashMap<>(); private final Map<Key, SerializerGen> cachedSerializers = new HashMap<>(); private final List<Runnable> initTasks = new ArrayList<>(); public interface Helper { SerializerGen createSubclassesSerializer(Class<?> type, SerializeSubclasses serializeSubclasses); } private final Helper helper = new Helper() { @Override public SerializerGen createSubclassesSerializer(Class<?> type, SerializeSubclasses serializeSubclasses) { return SerializerBuilder.this.createSubclassesSerializer(type, serializeSubclasses); } }; private SerializerBuilder(DefiningClassLoader definingClassLoader) { this.definingClassLoader = definingClassLoader; } public static SerializerBuilder create(ClassLoader classLoader) { return create(DefiningClassLoader.create(classLoader)); } public static SerializerBuilder create(String profile, ClassLoader classLoader) { return create(DefiningClassLoader.create(classLoader)).withProfile(profile); } public static SerializerBuilder create(String profile, DefiningClassLoader definingClassLoader) { return create(definingClassLoader).withProfile(profile); } public static SerializerBuilder create(DefiningClassLoader definingClassLoader) { final SerializerBuilder builder = new SerializerBuilder(definingClassLoader); builder.setSerializer(Object.class, new SerializerGenBuilder() { @Override public SerializerGen serializer(final Class<?> type, final SerializerForType[] generics, SerializerGen fallback) { check(type.getTypeParameters().length == generics.length); check(fallback == null); final SerializerGenClass serializer; SerializeInterface annotation = Annotations.findAnnotation(SerializeInterface.class, type.getAnnotations()); if (annotation != null && annotation.impl() != void.class) { serializer = new SerializerGenClass(type, generics, annotation.impl()); } else { serializer = new SerializerGenClass(type, generics); } builder.initTasks.add(new Runnable() { @Override public void run() { builder.scanAnnotations(type, generics, serializer); } }); return serializer; } }); builder.setSerializer(List.class, new SerializerGenBuilder() { @Override public SerializerGen serializer(Class<?> type, final SerializerForType[] generics, SerializerGen fallback) { check(generics.length == 1); return new SerializerGenList(generics[0].serializer); } }); builder.setSerializer(Collection.class, new SerializerGenBuilder() { @Override public SerializerGen serializer(Class<?> type, final SerializerForType[] generics, SerializerGen fallback) { check(generics.length == 1); return new SerializerGenList(generics[0].serializer); } }); builder.setSerializer(Set.class, new SerializerGenBuilder() { @Override public SerializerGen serializer(Class<?> type, SerializerForType[] generics, SerializerGen fallback) { check(generics.length == 1); return new SerializerGenSet(generics[0].serializer); } }); builder.setSerializer(Map.class, new SerializerGenBuilder() { @Override public SerializerGen serializer(Class<?> type, final SerializerForType[] generics, SerializerGen fallback) { check(generics.length == 2); return new SerializerGenMap(generics[0].serializer, generics[1].serializer); } }); builder.setSerializer(Enum.class, new SerializerGenBuilder() { @Override public SerializerGen serializer(final Class<?> type, final SerializerForType[] generics, SerializerGen fallback) { List<FoundSerializer> foundSerializers = builder.scanSerializers(type, generics); if (!foundSerializers.isEmpty()) { final SerializerGenClass serializer = new SerializerGenClass(type); builder.initTasks.add(new Runnable() { @Override public void run() { builder.scanAnnotations(type, generics, serializer); } }); return serializer; } else { return new SerializerGenEnum(type); } } }); builder.setSerializer(Boolean.TYPE, new SerializerGenBoolean()); builder.setSerializer(Character.TYPE, new SerializerGenChar()); builder.setSerializer(Byte.TYPE, new SerializerGenByte()); builder.setSerializer(Short.TYPE, new SerializerGenShort()); builder.setSerializer(Integer.TYPE, new SerializerGenInt(false)); builder.setSerializer(Long.TYPE, new SerializerGenLong(false)); builder.setSerializer(Float.TYPE, new SerializerGenFloat()); builder.setSerializer(Double.TYPE, new SerializerGenDouble()); builder.setSerializer(Boolean.class, new SerializerGenBoolean()); builder.setSerializer(Character.class, new SerializerGenChar()); builder.setSerializer(Byte.class, new SerializerGenByte()); builder.setSerializer(Short.class, new SerializerGenShort()); builder.setSerializer(Integer.class, new SerializerGenInt(false)); builder.setSerializer(Long.class, new SerializerGenLong(false)); builder.setSerializer(Float.class, new SerializerGenFloat()); builder.setSerializer(Double.class, new SerializerGenDouble()); builder.setSerializer(String.class, new SerializerGenString()); builder.setSerializer(Inet4Address.class, SerializerGenInet4Address.instance()); builder.setSerializer(Inet6Address.class, SerializerGenInet6Address.instance()); LinkedHashMap<Class<?>, SerializerGen> addressMap = new LinkedHashMap<>(); addressMap.put(Inet4Address.class, SerializerGenInet4Address.instance()); addressMap.put(Inet6Address.class, SerializerGenInet6Address.instance()); builder.setSerializer(InetAddress.class, new SerializerGenSubclass(InetAddress.class, addressMap, 0)); builder.setSerializer(ByteBuffer.class, new SerializerGenByteBuffer()); builder.setAnnotationHandler(SerializerClass.class, SerializerClassEx.class, new SerializerClassHandler()); builder.setAnnotationHandler(SerializeFixedSize.class, SerializeFixedSizeEx.class, new SerializeFixedSizeHandler()); builder.setAnnotationHandler(SerializeVarLength.class, SerializeVarLengthEx.class, new SerializeVarLengthHandler()); builder.setAnnotationHandler(SerializeSubclasses.class, SerializeSubclassesEx.class, new SerializeSubclassesHandler()); builder.setAnnotationHandler(SerializeNullable.class, SerializeNullableEx.class, new SerializeNullableHandler()); builder.setAnnotationHandler(SerializeMaxLength.class, SerializeMaxLengthEx.class, new SerializeMaxLengthHandler()); builder.setAnnotationHandler(SerializeStringFormat.class, SerializeStringFormatEx.class, new SerializeStringFormatHandler()); return builder; } private <A extends Annotation, P extends Annotation> SerializerBuilder setAnnotationHandler(Class<A> annotation, Class<P> annotationPlural, AnnotationHandler<A, P> annotationHandler) { annotationsMap.put(annotation, annotationHandler); if (annotationPlural != null) annotationsExMap.put(annotation, annotationPlural); return this; } public SerializerBuilder withCompatibilityLevel(CompatibilityLevel compatibilityLevel) { this.compatibilityLevel = compatibilityLevel; return this; } public SerializerBuilder withSaveBytecodePath(Path path) { this.saveBytecodePath = path; return this; } public SerializerBuilder withVersion(int version) { this.version = version; return this; } public SerializerBuilder withDefaultStringFormat(StringFormat format) { setSerializer(String.class, new SerializerGenString(format)); return this; } public SerializerBuilder withProfile(String profile) { this.profile = profile; return this; } private void setSerializer(Class<?> type, final SerializerGen serializer) { setSerializer(type, new SerializerGenBuilderConst(serializer)); } private void setSerializer(Class<?> type, SerializerGenBuilder serializer) { typeMap.put(type, serializer); } public SerializerBuilder withSerializer(Class<?> type, SerializerGenBuilder serializer) { typeMap.put(type, serializer); return this; } public SerializerBuilder withSerializer(Class<?> type, SerializerGen serializer) { return withSerializer(type, new SerializerGenBuilderConst(serializer)); } public SerializerBuilder withSubclasses(String subclassesId, List<Class<?>> subclasses) { extraSubclassesMap.put(subclassesId, subclasses); return this; } public void setSubclasses(String subclassesId, List<Class<?>> subclasses) { extraSubclassesMap.put(subclassesId, subclasses); } public SerializerBuilder withSubclasses(String extraSubclassesId, Class<?>... subclasses) { return withSubclasses(extraSubclassesId, Arrays.asList(subclasses)); } public <T> void setSubclasses(Class<T> type, List<Class<? extends T>> subclasses) { LinkedHashSet<Class<?>> subclassesSet = new LinkedHashSet<>(); subclassesSet.addAll(subclasses); check(subclassesSet.size() == subclasses.size()); SerializerGen subclassesSerializer = createSubclassesSerializer(type, subclassesSet, 0); setSerializer(type, subclassesSerializer); } public <T> void setSubclasses(Class<T> type, Class<? extends T>... subclasses) { setSubclasses(type, Arrays.asList(subclasses)); } public <T> SerializerBuilder withSubclasses(Class<T> type, List<Class<? extends T>> subclasses) { setSubclasses(type, subclasses); return this; } public <T> SerializerBuilder withSubclasses(Class<T> type, Class<? extends T>... subclasses) { setSubclasses(type, subclasses); return this; } public SerializerBuilder withHppcSupport() { registerHppcMaps(); registerHppcSets(); return this; } private void registerHppcMaps() { List<Class<?>> types = asList( byte.class, short.class, int.class, long.class, float.class, double.class, char.class, Object.class ); for (int i = 0; i < types.size(); i++) { Class<?> keyType = types.get(i); String keyTypeName = keyType.getSimpleName(); for (Class<?> valueType : types) { String valueTypeName = valueType.getSimpleName(); String hppcMapTypeName = "com.carrotsearch.hppc." + capitalize(keyTypeName) + capitalize(valueTypeName) + "Map"; Class<?> hppcMapType; try { hppcMapType = Class.forName(hppcMapTypeName, true, definingClassLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException("Cannot load " + e.getClass().getName(), e); } typeMap.put(hppcMapType, SerializerGenHppcMap.serializerGenBuilder(hppcMapType, keyType, valueType)); } } } private void registerHppcSets() { List<Class<?>> types = asList( byte.class, short.class, int.class, long.class, float.class, double.class, char.class, Object.class ); for (Class<?> valueType : types) { String valueTypeName = valueType.getSimpleName(); String hppcSetTypeName = "com.carrotsearch.hppc." + capitalize(valueTypeName) + "Set"; Class<?> hppcSetType; try { hppcSetType = Class.forName(hppcSetTypeName, true, definingClassLoader); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); } typeMap.put(hppcSetType, SerializerGenHppcSet.serializerGenBuilder(hppcSetType, valueType)); } } private static String capitalize(String str) { return String.valueOf(toUpperCase(str.charAt(0))) + str.substring(1); } /** * Creates a {@code SerializerGen} for the given type token. * * @return {@code SerializerGen} for the given type token */ public <T> BufferSerializer<T> build(Class<T> type) { SerializerGenBuilder.SerializerForType[] serializerForTypes = new SerializerGenBuilder.SerializerForType[0]; return build(type, serializerForTypes); } public <T> BufferSerializer<T> build(SerializerGen serializerGen) { return buildBufferSerializer(serializerGen, version); } public <T> BufferSerializer<T> build(Class<?> type, SerializerGenBuilder.SerializerForType[] generics) { return buildBufferSerializer(createSerializerGen(type, generics, Collections.<SerializerGenBuilder>emptyList()), version); } private SerializerGen createSerializerGen(Class<?> type, SerializerGenBuilder.SerializerForType[] generics, List<SerializerGenBuilder> mods) { Key key = new Key(type, generics, mods); SerializerGen serializer = cachedSerializers.get(key); if (serializer == null) { serializer = createNewSerializer(type, generics, mods); cachedSerializers.put(key, serializer); } while (!initTasks.isEmpty()) { initTasks.remove(0).run(); } return serializer; } private SerializerGen createNewSerializer(Class<?> type, SerializerGenBuilder.SerializerForType[] generics, List<SerializerGenBuilder> mods) { if (!mods.isEmpty()) { SerializerGen serializer = createSerializerGen(type, generics, mods.subList(0, mods.size() - 1)); SerializerGenBuilder last = mods.get(mods.size() - 1); return last.serializer(type, generics, serializer); } if (type.isArray()) { check(generics.length == 1); SerializerGen itemSerializer = generics[0].serializer; return new SerializerGenArray(itemSerializer, type); } SerializeSubclasses serializeSubclasses = Annotations.findAnnotation(SerializeSubclasses.class, type.getAnnotations()); if (serializeSubclasses != null) { return createSubclassesSerializer(type, serializeSubclasses); } Class<?> key = findKey(type, typeMap.keySet()); final SerializerGenBuilder builder = typeMap.get(key); if (builder == null) throw new IllegalArgumentException(); SerializerGen serializer = builder.serializer(type, generics, null); checkNotNull(serializer); return serializer; } private SerializerGen createSubclassesSerializer(Class<?> type, SerializeSubclasses serializeSubclasses) { LinkedHashSet<Class<?>> subclassesSet = new LinkedHashSet<>(); subclassesSet.addAll(Arrays.asList(serializeSubclasses.value())); check(subclassesSet.size() == serializeSubclasses.value().length); if (!serializeSubclasses.extraSubclassesId().isEmpty()) { Collection<Class<?>> registeredSubclasses = extraSubclassesMap.get(serializeSubclasses.extraSubclassesId()); if (registeredSubclasses != null) subclassesSet.addAll(registeredSubclasses); } return createSubclassesSerializer(type, subclassesSet, serializeSubclasses.startIndex()); } private SerializerGen createSubclassesSerializer(Class<?> type, LinkedHashSet<Class<?>> subclassesSet, int startIndex) { checkNotNull(subclassesSet); check(!subclassesSet.isEmpty()); LinkedHashMap<Class<?>, SerializerGen> subclasses = new LinkedHashMap<>(); for (Class<?> subclass : subclassesSet) { check(subclass.getTypeParameters().length == 0); check(type.isAssignableFrom(subclass), "Unrelated subclass '%s' for '%s'", subclass, type); SerializerGen serializer = createSerializerGen( subclass, new SerializerGenBuilder.SerializerForType[]{}, Collections.<SerializerGenBuilder>emptyList() ); subclasses.put(subclass, serializer); } return new SerializerGenSubclass(type, subclasses, startIndex); } private static Class<?> findKey(Class<?> classType, Set<Class<?>> classes) { Class<?> foundKey = null; for (Class<?> key : classes) { if (key.isAssignableFrom(classType)) { if (foundKey == null || foundKey.isAssignableFrom(key)) { foundKey = key; } } } return foundKey; } @SuppressWarnings({"unchecked", "rawtypes"}) private TypedModsMap extractMods(Annotation[] annotations) { TypedModsMap.Builder rootBuilder = TypedModsMap.builder(); if (annotations.length == 0) return rootBuilder.build(); for (Class<? extends Annotation> annotationType : annotationsMap.keySet()) { Class<? extends Annotation> annotationExType = annotationsExMap.get(annotationType); AnnotationHandler annotationHandler = annotationsMap.get(annotationType); for (Annotation annotation : annotations) { if (annotation.annotationType() == annotationType) { SerializerGenBuilder serializerGenBuilder = annotationHandler.createBuilder(helper, annotation, compatibilityLevel); TypedModsMap.Builder child = rootBuilder.ensureChild(annotationHandler.extractPath(annotation)); child.add(serializerGenBuilder); } } for (Annotation annotationEx : annotations) { if (annotationEx.annotationType() == annotationExType) { for (Annotation annotation : annotationHandler.extractList(annotationEx)) { SerializerGenBuilder serializerGenBuilder = annotationHandler.createBuilder(helper, annotation, compatibilityLevel); TypedModsMap.Builder child = rootBuilder.ensureChild(annotationHandler.extractPath(annotation)); child.add(serializerGenBuilder); } } } } return rootBuilder.build(); } @SuppressWarnings("rawtypes") public SerializerGenBuilder.SerializerForType resolveSerializer(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, Type genericType, TypedModsMap typedModsMap) { if (genericType instanceof TypeVariable) { String typeVariableName = ((TypeVariable) genericType).getName(); int i; for (i = 0; i < classType.getTypeParameters().length; i++) { TypeVariable<?> typeVariable = classType.getTypeParameters()[i]; if (typeVariableName.equals(typeVariable.getName())) { break; } } check(i < classType.getTypeParameters().length); SerializerGen serializer = typedModsMap.rewrite(classGenerics[i].rawType, new SerializerGenBuilder.SerializerForType[]{}, classGenerics[i].serializer); return new SerializerGenBuilder.SerializerForType(classGenerics[i].rawType, serializer); } else if (genericType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericType; SerializerGenBuilder.SerializerForType[] typeArguments = new SerializerGenBuilder.SerializerForType[parameterizedType.getActualTypeArguments().length]; for (int i = 0; i < parameterizedType.getActualTypeArguments().length; i++) { Type typeArgument = parameterizedType.getActualTypeArguments()[i]; if (typeArgument instanceof WildcardType) { throw new IllegalArgumentException(); } typeArguments[i] = resolveSerializer(classType, classGenerics, typeArgument, typedModsMap.get(i)); } Class<?> rawType = (Class<?>) parameterizedType.getRawType(); SerializerGen serializer = createSerializerGen(rawType, typeArguments, typedModsMap.getMods()); return new SerializerGenBuilder.SerializerForType(rawType, serializer); } else if (genericType instanceof GenericArrayType) { throw new UnsupportedOperationException(); } else if (genericType instanceof Class<?>) { Class<?> rawType = (Class<?>) genericType; SerializerGenBuilder.SerializerForType[] generics = {}; if (rawType.isArray()) { Class<?> componentType = rawType.getComponentType(); SerializerGenBuilder.SerializerForType forType = resolveSerializer(classType, classGenerics, componentType, typedModsMap.get(0)); generics = new SerializerGenBuilder.SerializerForType[]{forType}; } SerializerGen serializer = createSerializerGen(rawType, generics, typedModsMap.getMods()); return new SerializerGenBuilder.SerializerForType(rawType, serializer); } else { throw new IllegalArgumentException(); } } private static final class FoundSerializer implements Comparable<FoundSerializer> { final Object methodOrField; final int order; final int added; final int removed; final TypedModsMap mods; SerializerGen serializerGen; private FoundSerializer(Object methodOrField, int order, int added, int removed, TypedModsMap mods) { this.methodOrField = methodOrField; this.order = order; this.added = added; this.removed = removed; this.mods = mods; } public String getName() { if (methodOrField instanceof Field) return ((Field) methodOrField).getName(); if (methodOrField instanceof Method) return ((Method) methodOrField).getName(); throw new AssertionError(); } private int fieldRank() { if (methodOrField instanceof Field) return 1; if (methodOrField instanceof Method) return 2; throw new AssertionError(); } @SuppressWarnings("NullableProblems") @Override public int compareTo(FoundSerializer o) { int result = Integer.compare(this.order, o.order); if (result != 0) return result; result = Integer.compare(fieldRank(), o.fieldRank()); if (result != 0) return result; result = getName().compareTo(o.getName()); if (result != 0) return result; return 0; } @Override public String toString() { return methodOrField.getClass().getSimpleName() + " " + getName(); } } private FoundSerializer findAnnotations(Object methodOrField, Annotation[] annotations) { TypedModsMap mods = extractMods(annotations); int added = Serialize.DEFAULT_VERSION; int removed = Serialize.DEFAULT_VERSION; Serialize serialize = Annotations.findAnnotation(Serialize.class, annotations); if (serialize != null) { added = serialize.added(); removed = serialize.removed(); } SerializeProfiles profiles = Annotations.findAnnotation(SerializeProfiles.class, annotations); if (profiles != null) { if (!Arrays.asList(profiles.value()).contains((profile == null ? "" : profile))) return null; int addedProfile = getProfileVersion(profiles.value(), profiles.added()); if (addedProfile != SerializeProfiles.DEFAULT_VERSION) { added = addedProfile; } int removedProfile = getProfileVersion(profiles.value(), profiles.removed()); if (removedProfile != SerializeProfiles.DEFAULT_VERSION) { removed = removedProfile; } } if (serialize != null) { return new FoundSerializer(methodOrField, serialize.order(), added, removed, mods); } if (profiles != null || !mods.isEmpty()) throw new IllegalArgumentException("Serialize modifiers without @Serialize annotation on " + methodOrField); return null; } private int getProfileVersion(String[] profiles, int[] versions) { if (profiles == null || profiles.length == 0) return SerializeProfiles.DEFAULT_VERSION; for (int i = 0; i < profiles.length; i++) { if (Objects.equals(profile, profiles[i])) { if (i < versions.length) return versions[i]; return SerializeProfiles.DEFAULT_VERSION; } } return SerializeProfiles.DEFAULT_VERSION; } private FoundSerializer tryAddField(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, Field field) { FoundSerializer result = findAnnotations(field, field.getAnnotations()); if (result == null) return null; check(isPublic(field.getModifiers()), "Field %s must be public", field); check(!isStatic(field.getModifiers()), "Field %s must not be static", field); check(!isTransient(field.getModifiers()), "Field %s must not be transient", field); result.serializerGen = resolveSerializer(classType, classGenerics, field.getGenericType(), result.mods).serializer; return result; } private FoundSerializer tryAddGetter(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, Method getter) { FoundSerializer result = findAnnotations(getter, getter.getAnnotations()); if (result == null) return null; check(isPublic(getter.getModifiers()), "Getter %s must be public", getter); check(!isStatic(getter.getModifiers()), "Getter %s must not be static", getter); check(getter.getReturnType() != Void.TYPE && getter.getParameterTypes().length == 0, "%s must be getter", getter); result.serializerGen = resolveSerializer(classType, classGenerics, getter.getGenericReturnType(), result.mods).serializer; return result; } private void scanAnnotations(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, SerializerGenClass serializerGenClass) { if (classType.isInterface()) { SerializeInterface annotation = Annotations.findAnnotation(SerializeInterface.class, classType.getAnnotations()); scanInterface(classType, classGenerics, serializerGenClass, (annotation != null) && annotation.inherit()); if (annotation != null) { Class<?> impl = annotation.impl(); if (impl == void.class) return; scanSetters(impl, serializerGenClass); scanFactories(impl, serializerGenClass); scanConstructors(impl, serializerGenClass); serializerGenClass.addMatchingSetters(); } return; } check(!classType.isAnonymousClass()); check(!classType.isLocalClass()); scanClass(classType, classGenerics, serializerGenClass); scanFactories(classType, serializerGenClass); scanConstructors(classType, serializerGenClass); serializerGenClass.addMatchingSetters(); } private void scanInterface(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, SerializerGenClass serializerGenClass, boolean inheritSerializers) { List<FoundSerializer> foundSerializers = new ArrayList<>(); scanGetters(classType, classGenerics, foundSerializers); addMethodsAndGettersToClass(serializerGenClass, foundSerializers); if (!inheritSerializers) return; SerializeInterface annotation = Annotations.findAnnotation(SerializeInterface.class, classType.getAnnotations()); if (annotation != null && !annotation.inherit()) { return; } for (Class<?> inter : classType.getInterfaces()) { scanInterface(inter, classGenerics, serializerGenClass, true); } } private void addMethodsAndGettersToClass(SerializerGenClass serializerGenClass, List<FoundSerializer> foundSerializers) { Set<Integer> orders = new HashSet<>(); for (FoundSerializer foundSerializer : foundSerializers) { check(foundSerializer.order >= 0, "Invalid order %s for %s in %s", foundSerializer.order, foundSerializer, serializerGenClass.getRawType().getName()); check(orders.add(foundSerializer.order), "Duplicate order %s for %s in %s", foundSerializer.order, foundSerializer, serializerGenClass.getRawType().getName()); } Collections.sort(foundSerializers); for (FoundSerializer foundSerializer : foundSerializers) { if (foundSerializer.methodOrField instanceof Method) serializerGenClass.addGetter((Method) foundSerializer.methodOrField, foundSerializer.serializerGen, foundSerializer.added, foundSerializer.removed); else if (foundSerializer.methodOrField instanceof Field) serializerGenClass.addField((Field) foundSerializer.methodOrField, foundSerializer.serializerGen, foundSerializer.added, foundSerializer.removed); else throw new AssertionError(); } } private void scanClass(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, SerializerGenClass serializerGenClass) { if (classType == Object.class) return; Type genericSuperclass = classType.getGenericSuperclass(); if (genericSuperclass instanceof ParameterizedType) { ParameterizedType parameterizedSuperclass = (ParameterizedType) genericSuperclass; SerializerGenBuilder.SerializerForType[] superclassGenerics = new SerializerGenBuilder.SerializerForType[parameterizedSuperclass.getActualTypeArguments().length]; for (int i = 0; i < parameterizedSuperclass.getActualTypeArguments().length; i++) { superclassGenerics[i] = resolveSerializer(classType, classGenerics, parameterizedSuperclass.getActualTypeArguments()[i], TypedModsMap.empty()); } scanClass(classType.getSuperclass(), superclassGenerics, serializerGenClass); } else if (genericSuperclass instanceof Class) { scanClass(classType.getSuperclass(), new SerializerGenBuilder.SerializerForType[]{}, serializerGenClass); } else throw new IllegalArgumentException(); List<FoundSerializer> foundSerializers = scanSerializers(classType, classGenerics); addMethodsAndGettersToClass(serializerGenClass, foundSerializers); scanSetters(classType, serializerGenClass); } private List<FoundSerializer> scanSerializers(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics) { List<FoundSerializer> foundSerializers = new ArrayList<>(); scanFields(classType, classGenerics, foundSerializers); scanGetters(classType, classGenerics, foundSerializers); return foundSerializers; } private void scanFields(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, List<FoundSerializer> foundSerializers) { for (Field field : classType.getDeclaredFields()) { FoundSerializer foundSerializer = tryAddField(classType, classGenerics, field); if (foundSerializer != null) foundSerializers.add(foundSerializer); } } private void scanGetters(Class<?> classType, SerializerGenBuilder.SerializerForType[] classGenerics, List<FoundSerializer> foundSerializers) { Method[] methods = classType.getDeclaredMethods(); for (Method method : methods) { FoundSerializer foundSerializer = tryAddGetter(classType, classGenerics, method); if (foundSerializer != null) foundSerializers.add(foundSerializer); } } private void scanSetters(Class<?> classType, SerializerGenClass serializerGenClass) { for (Method method : classType.getDeclaredMethods()) { if (isStatic(method.getModifiers())) continue; if (method.getParameterTypes().length != 0) { List<String> fields = new ArrayList<>(method.getParameterTypes().length); for (int i = 0; i < method.getParameterTypes().length; i++) { Annotation[] parameterAnnotations = method.getParameterAnnotations()[i]; Deserialize annotation = Annotations.findAnnotation(Deserialize.class, parameterAnnotations); if (annotation != null) { String field = annotation.value(); fields.add(field); } } if (fields.size() == method.getParameterTypes().length) { serializerGenClass.addSetter(method, fields); } else { check(fields.isEmpty()); } } } } private void scanFactories(Class<?> classType, SerializerGenClass serializerGenClass) { DeserializeFactory annotationFactory = Annotations.findAnnotation(DeserializeFactory.class, classType.getAnnotations()); Class<?> factoryClassType = (annotationFactory == null) ? classType : annotationFactory.value(); for (Method factory : factoryClassType.getDeclaredMethods()) { if (classType != factory.getReturnType()) continue; if (factory.getParameterTypes().length != 0) { List<String> fields = new ArrayList<>(factory.getParameterTypes().length); for (int i = 0; i < factory.getParameterTypes().length; i++) { Annotation[] parameterAnnotations = factory.getParameterAnnotations()[i]; Deserialize annotation = Annotations.findAnnotation(Deserialize.class, parameterAnnotations); if (annotation != null) { String field = annotation.value(); fields.add(field); } } if (fields.size() == factory.getParameterTypes().length) { serializerGenClass.setFactory(factory, fields); } else { check(fields.isEmpty(), "@Deserialize is not fully specified for %s", fields); } } } } private void scanConstructors(Class<?> classType, SerializerGenClass serializerGenClass) { boolean found = false; for (Constructor<?> constructor : classType.getDeclaredConstructors()) { List<String> fields = new ArrayList<>(constructor.getParameterTypes().length); for (int i = 0; i < constructor.getParameterTypes().length; i++) { Annotation[] parameterAnnotations = constructor.getParameterAnnotations()[i]; Deserialize annotation = Annotations.findAnnotation(Deserialize.class, parameterAnnotations); if (annotation != null) { String field = annotation.value(); fields.add(field); } } if (constructor.getParameterTypes().length != 0 && fields.size() == constructor.getParameterTypes().length) { check(!found, "Duplicate @Deserialize constructor %s", constructor); found = true; serializerGenClass.setConstructor(constructor, fields); } else { check(fields.isEmpty(), "@Deserialize is not fully specified for %s", fields); } } } private <T> BufferSerializer<T> buildBufferSerializer(SerializerGen serializerGen, int serializeVersion) { return (BufferSerializer<T>) createSerializer(serializerGen, serializeVersion); } /** * Constructs buffer serializer for type, described by the given {@code SerializerGen}. * * @param serializerGen {@code SerializerGen} that describes the type that is to serialize * @return buffer serializer for the given {@code SerializerGen} */ private <T> BufferSerializer<T> buildBufferSerializer(SerializerGen serializerGen) { return buildBufferSerializer(serializerGen, Integer.MAX_VALUE); } public class StaticMethods { private final DefiningClassLoader definingClassLoader = SerializerBuilder.this.definingClassLoader; public DefiningClassLoader getDefiningClassLoader() { return definingClassLoader; } private final class Key { public final SerializerGen serializerGen; public final int version; public Key(SerializerGen serializerGen, int version) { this.serializerGen = serializerGen; this.version = version; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; if (version != key.version) return false; return !(serializerGen != null ? !serializerGen.equals(key.serializerGen) : key.serializerGen != null); } @Override public int hashCode() { int result = serializerGen != null ? serializerGen.hashCode() : 0; result = 31 * result + version; return result; } } private final class Value { public String method; public Expression expression; public Value(String method, Expression expression) { this.method = method; this.expression = expression; } } private Map<Key, Value> mapSerialize = new HashMap<>(); private Map<Key, Value> mapDeserialize = new HashMap<>(); public boolean startSerializeStaticMethod(SerializerGen serializerGen, int version) { boolean b = mapSerialize.containsKey(new Key(serializerGen, version)); if (!b) { String methodName = "serialize_" + serializerGen.getRawType().getSimpleName().replace('[', 's').replace(']', '_') + "_V" + version + "_" + (counter.incrementAndGet()); mapSerialize.put(new Key(serializerGen, version), new Value(methodName, null)); } return b; } public boolean startDeserializeStaticMethod(SerializerGen serializerGen, int version) { boolean b = mapDeserialize.containsKey(new Key(serializerGen, version)); if (!b) { String methodName = "deserialize_" + serializerGen.getRawType().getSimpleName().replace('[', 's').replace(']', '_') + "_V" + version + "_" + (counter.incrementAndGet()); mapDeserialize.put(new Key(serializerGen, version), new Value(methodName, null)); } return b; } public void registerStaticSerializeMethod(SerializerGen serializerGen, int version, Expression expression) { Key key = new Key(serializerGen, version); Value value = mapSerialize.get(key); value.expression = expression; } public void registerStaticDeserializeMethod(SerializerGen serializerGen, int version, Expression expression) { Key key = new Key(serializerGen, version); Value value = mapDeserialize.get(key); value.expression = expression; } public Expression callStaticSerializeMethod(SerializerGen serializerGen, int version, Expression... args) { Value value = mapSerialize.get(new Key(serializerGen, version)); return callStaticSelf(value.method, args); } public Expression callStaticDeserializeMethod(SerializerGen serializerGen, int version, Expression... args) { Value value = mapDeserialize.get(new Key(serializerGen, version)); return callStaticSelf(value.method, args); } } synchronized private Object createSerializer(SerializerGen serializerGen, int serializeVersion) { ClassBuilder<BufferSerializer> asmFactory = ClassBuilder.create(definingClassLoader, BufferSerializer.class); if (saveBytecodePath != null) { asmFactory = asmFactory.withBytecodeSaveDir(saveBytecodePath); } Preconditions.check(serializeVersion >= 0, "serializerVersion is negative"); Class<?> dataType = serializerGen.getRawType(); List<Integer> versions = new ArrayList<>(); List<Integer> allVersions = new ArrayList<>(); for (int v : SerializerGen.VersionsCollector.versions(serializerGen)) { if (v <= serializeVersion) versions.add(v); allVersions.add(v); } Collections.sort(versions); Collections.sort(allVersions); Integer currentVersion = getLatestVersion(versions); if (!allVersions.isEmpty() && currentVersion == null) currentVersion = serializeVersion; Expression version = voidExp(); if (currentVersion != null) { version = call(arg(0), "writeVarInt", value(currentVersion)); } StaticMethods staticMethods = new StaticMethods(); if (currentVersion == null) currentVersion = 0; serializerGen.prepareSerializeStaticMethods(currentVersion, staticMethods, compatibilityLevel); for (StaticMethods.Key key : staticMethods.mapSerialize.keySet()) { StaticMethods.Value value = staticMethods.mapSerialize.get(key); asmFactory = asmFactory.withStaticMethod(value.method, int.class, asList(byte[].class, int.class, key.serializerGen.getRawType()), value.expression); } Variable position = let(call(arg(0), "writePosition")); asmFactory = asmFactory.withMethod("serialize", sequence(version, call(arg(0), "writePosition", serializerGen.serialize( call(arg(0), "array"), position, cast(arg(1), dataType), currentVersion, staticMethods, compatibilityLevel)), call(arg(0), "writePosition") ) ); asmFactory = defineDeserialize(serializerGen, asmFactory, allVersions, staticMethods); for (StaticMethods.Key key : staticMethods.mapDeserialize.keySet()) { StaticMethods.Value value = staticMethods.mapDeserialize.get(key); asmFactory = asmFactory.withStaticMethod(value.method, key.serializerGen.getRawType(), asList(ByteBuf.class), value.expression); } return asmFactory.buildClassAndCreateNewInstance(); } private ClassBuilder<BufferSerializer> defineDeserialize(final SerializerGen serializerGen, ClassBuilder<BufferSerializer> asmFactory, final List<Integer> allVersions, StaticMethods staticMethods) { asmFactory = defineDeserializeLatest(serializerGen, asmFactory, getLatestVersion(allVersions), staticMethods); asmFactory = defineDeserializeEarlierVersion(serializerGen, asmFactory, allVersions, staticMethods); for (int i = allVersions.size() - 2; i >= 0; i--) { int version = allVersions.get(i); asmFactory = defineDeserializeVersion(serializerGen, asmFactory, version, staticMethods); } return asmFactory; } private ClassBuilder<BufferSerializer> defineDeserializeVersion(SerializerGen serializerGen, ClassBuilder<BufferSerializer> asmFactory, int version, StaticMethods staticMethods) { return asmFactory.withMethod("deserializeVersion" + String.valueOf(version), serializerGen.getRawType(), asList(ByteBuf.class), sequence(serializerGen.deserialize(serializerGen.getRawType(), version, staticMethods, compatibilityLevel))); } private ClassBuilder<BufferSerializer> defineDeserializeEarlierVersion(SerializerGen serializerGen, ClassBuilder<BufferSerializer> asmFactory, List<Integer> allVersions, StaticMethods staticMethods) { List<Expression> listKey = new ArrayList<>(); List<Expression> listValue = new ArrayList<>(); for (int i = allVersions.size() - 2; i >= 0; i--) { int version = allVersions.get(i); listKey.add(value(version)); serializerGen.prepareDeserializeStaticMethods(version, staticMethods, compatibilityLevel); listValue.add(call(self(), "deserializeVersion" + String.valueOf(version), arg(0))); } return asmFactory.withMethod("deserializeEarlierVersions", serializerGen.getRawType(), asList(ByteBuf.class, int.class), switchForKey(arg(1), listKey, listValue)); } private ClassBuilder<BufferSerializer> defineDeserializeLatest(final SerializerGen serializerGen, final ClassBuilder<BufferSerializer> asmFactory, final Integer latestVersion, StaticMethods staticMethods) { if (latestVersion == null) { serializerGen.prepareDeserializeStaticMethods(0, staticMethods, compatibilityLevel); return asmFactory .withMethod("deserialize", serializerGen.deserialize( serializerGen.getRawType(), 0, staticMethods, compatibilityLevel)); } else { serializerGen.prepareDeserializeStaticMethods(latestVersion, staticMethods, compatibilityLevel); Expression version = let(call(arg(0), "readVarInt")); return asmFactory.withMethod("deserialize", sequence(version, ifThenElse(cmpEq(version, value(latestVersion)), serializerGen.deserialize(serializerGen.getRawType(), latestVersion, staticMethods, compatibilityLevel), call(self(), "deserializeEarlierVersions", arg(0), version)))); } } private Integer getLatestVersion(List<Integer> versions) { return versions.isEmpty() ? null : versions.get(versions.size() - 1); } private static final class Key { final Class<?> type; final SerializerGenBuilder.SerializerForType[] generics; final List<SerializerGenBuilder> mods; private Key(Class<?> type, SerializerGenBuilder.SerializerForType[] generics, List<SerializerGenBuilder> mods) { this.type = checkNotNull(type); this.generics = checkNotNull(generics); this.mods = checkNotNull(mods); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; if (!Arrays.equals(generics, key.generics)) return false; if (!type.equals(key.type)) return false; if (!mods.equals(key.mods)) return false; return true; } @Override public int hashCode() { int result = type.hashCode(); result = 31 * result + Arrays.hashCode(generics); result = 31 * result + mods.hashCode(); return result; } } }