package org.simpleflatmapper.reflect.asm; import org.simpleflatmapper.reflect.BiInstantiator; import org.simpleflatmapper.reflect.BuilderInstantiatorDefinition; import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition; import org.simpleflatmapper.reflect.Getter; import org.simpleflatmapper.reflect.Instantiator; import org.simpleflatmapper.reflect.InstantiatorDefinition; import org.simpleflatmapper.reflect.Parameter; import org.simpleflatmapper.reflect.Setter; import org.simpleflatmapper.util.BiFunction; import org.simpleflatmapper.util.TypeHelper; import org.simpleflatmapper.util.UnaryFactory; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; public class AsmFactory { private final FactoryClassLoader factoryClassLoader; private final ConcurrentMap<Object, Setter<?, ?>> setterCache = new ConcurrentHashMap<Object, Setter<?, ?>>(); private final ConcurrentMap<Object, Getter<?, ?>> getterCache = new ConcurrentHashMap<Object, Getter<?, ?>>(); private final ConcurrentMap<InstantiatorKey, Class<? extends Instantiator<?, ?>>> instantiatorCache = new ConcurrentHashMap<InstantiatorKey, Class<? extends Instantiator<?, ?>>>(); private final ConcurrentMap<BiInstantiatorKey, Class<? extends BiInstantiator<?, ?, ?>>> biInstantiatorCache = new ConcurrentHashMap<BiInstantiatorKey, Class<? extends BiInstantiator<?, ?, ?>>>(); private final ConcurrentMap<Class<?>, Object> subFactories = new ConcurrentHashMap<Class<?>, Object>(); public AsmFactory(ClassLoader cl) { factoryClassLoader = new FactoryClassLoader(cl); } @SuppressWarnings("unchecked") public <T, P> Setter<T,P> createSetter(final Method m) throws Exception { Setter<T,P> setter = (Setter<T, P>) setterCache.get(m); if (setter == null) { final String className = generateClassNameForSetter(m); final byte[] bytes = generateSetterByteCodes(m, className); final Class<?> type = createClass(className, bytes, m.getDeclaringClass().getClassLoader()); setter = (Setter<T, P>) type.newInstance(); setterCache.putIfAbsent(m, setter); } return setter; } @SuppressWarnings("unchecked") public <T, P> Setter<T,P> createSetter(Field field) throws Exception { Setter<T,P> setter = (Setter<T, P>) setterCache.get(field); if (setter == null) { final String className = generateClassNameForSetter(field); final byte[] bytes = generateSetterByteCodes(field, className); final Class<?> type = createClass(className, bytes, field.getDeclaringClass().getClassLoader()); setter = (Setter<T, P>) type.newInstance(); setterCache.putIfAbsent(field, setter); } return setter; } public Class<?> createClass(String className, byte[] bytes, ClassLoader declaringClassLoader) { return factoryClassLoader.registerClass(className, bytes, declaringClassLoader); } @SuppressWarnings("unchecked") public <T, P> Getter<T,P> createGetter(final Method m) throws Exception { Getter<T,P> getter = (Getter<T, P>) getterCache.get(m); if (getter == null) { final String className = generateClassNameForGetter(m); final byte[] bytes = generateGetterByteCodes(m, className); final Class<?> type = createClass(className, bytes, m.getDeclaringClass().getClassLoader()); getter = (Getter<T, P>) type.newInstance(); getterCache.putIfAbsent(m, getter); } return getter; } @SuppressWarnings("unchecked") public <T, P> Getter<T,P> createGetter(final Field m) throws Exception { Getter<T,P> getter = (Getter<T, P>) getterCache.get(m); if (getter == null) { final String className = generateClassNameForGetter(m); final byte[] bytes = generateGetterByteCodes(m, className); final Class<?> type = createClass(className, bytes, m.getDeclaringClass().getClassLoader()); getter = (Getter<T, P>) type.newInstance(); getterCache.putIfAbsent(m, getter); } return getter; } private byte[] generateGetterByteCodes(final Method m, final String className) throws Exception { final Class<?> propertyType = m.getReturnType(); if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) { return GetterBuilder.createPrimitiveGetter(className, m); } else { return GetterBuilder.createObjectGetter(className, m); } } private byte[] generateGetterByteCodes(final Field m, final String className) throws Exception { final Class<?> propertyType = m.getType(); if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) { return GetterBuilder.createPrimitiveGetter(className, m); } else { return GetterBuilder.createObjectGetter(className, m); } } private byte[] generateSetterByteCodes(final Method m, final String className) throws Exception { final Class<?> propertyType = m.getParameterTypes()[0]; if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) { return SetterBuilder.createPrimitiveSetter(className, m); } else { return SetterBuilder.createObjectSetter(className, m); } } private byte[] generateSetterByteCodes(final Field m, final String className) throws Exception { final Class<?> propertyType = m.getType(); if (AsmUtils.primitivesClassAndWrapper.contains(propertyType)) { return SetterBuilder.createPrimitiveSetter(className, m); } else { return SetterBuilder.createObjectSetter(className, m); } } @SuppressWarnings("unchecked") public <S, T> Instantiator<S, T> createEmptyArgsInstantiator(final Class<S> source, final Class<? extends T> target) throws Exception { InstantiatorKey instantiatorKey = new InstantiatorKey(target, source); Class<? extends Instantiator<?, ?>> instantiatorType = instantiatorCache.get(instantiatorKey); if (instantiatorType == null) { final String className = generateClassNameForInstantiator(instantiatorKey); final byte[] bytes = ConstructorBuilder.createEmptyConstructor(className, source, target); instantiatorType = (Class<? extends Instantiator<?, ?>>) createClass(className, bytes, target.getClassLoader()); instantiatorCache.putIfAbsent(instantiatorKey, instantiatorType); } return (Instantiator<S, T>) instantiatorType.newInstance(); } @SuppressWarnings("unchecked") public <S, T> Instantiator<S, T> createInstantiator(final Class<S> source, final InstantiatorDefinition instantiatorDefinition, final Map<Parameter, Getter<? super S, ?>> injections) throws Exception { InstantiatorKey<S> instantiatorKey = new InstantiatorKey<S>(instantiatorDefinition, injections, source); Class<? extends Instantiator<?, ?>> instantiator = instantiatorCache.get(instantiatorKey); Instantiator<Void, ?> builderInstantiator = null; if (instantiator == null) { final String className = generateClassNameForInstantiator(instantiatorKey); final byte[] bytes; if (instantiatorDefinition instanceof ExecutableInstantiatorDefinition) { bytes = InstantiatorBuilder.createInstantiator(className, source, (ExecutableInstantiatorDefinition)instantiatorDefinition, injections); } else { builderInstantiator = createInstantiator(Void.class, ((BuilderInstantiatorDefinition)instantiatorDefinition).getBuilderInstantiator(), new HashMap<Parameter, Getter<? super Void, ?>>()); bytes = InstantiatorBuilder.createInstantiator( className, source, builderInstantiator, (BuilderInstantiatorDefinition)instantiatorDefinition, injections); } instantiator = (Class<? extends Instantiator<?, ?>>) createClass(className, bytes, instantiatorKey.getDeclaringClass().getClassLoader()); instantiatorCache.put(instantiatorKey, instantiator); } Map<String, Getter<? super S, ?>> getterPerName = new HashMap<String, Getter<? super S, ?>>(); for(Entry<Parameter, Getter<? super S, ?>> e : injections.entrySet()) { getterPerName.put(e.getKey().getName(), e.getValue()); } if (instantiatorDefinition instanceof ExecutableInstantiatorDefinition) { return (Instantiator<S, T>) instantiator.getConstructor(Map.class).newInstance(getterPerName); } else { return (Instantiator<S, T>) instantiator.getConstructor(Map.class, Instantiator.class).newInstance(getterPerName, builderInstantiator); } } @SuppressWarnings("unchecked") public <S1, S2, T> BiInstantiator<S1, S2, T> createBiInstantiator(final Class<?> s1, final Class<?> s2, final InstantiatorDefinition instantiatorDefinition, final Map<Parameter, BiFunction<? super S1, ? super S2, ?>> injections) throws Exception { BiInstantiatorKey instantiatorKey = new BiInstantiatorKey(instantiatorDefinition, injections, s1, s2); Class<? extends BiInstantiator<?, ?, ?>> instantiator = biInstantiatorCache.get(instantiatorKey); Instantiator builderInstantiator = null; if (instantiator == null) { final String className = generateClassNameForBiInstantiator(instantiatorKey); final byte[] bytes; if (instantiatorDefinition instanceof ExecutableInstantiatorDefinition) { bytes = BiInstantiatorBuilder.createInstantiator(className, s1, s2, (ExecutableInstantiatorDefinition)instantiatorDefinition, injections); } else { builderInstantiator = createInstantiator(Void.class, ((BuilderInstantiatorDefinition)instantiatorDefinition).getBuilderInstantiator(), new HashMap<Parameter, Getter<? super Void, ?>>()); bytes = BiInstantiatorBuilder.createInstantiator( className, s1, s2, builderInstantiator, (BuilderInstantiatorDefinition)instantiatorDefinition, injections); } instantiator = (Class<? extends BiInstantiator<?, ?, ?>>) createClass(className, bytes, instantiatorKey.getDeclaringClass().getClassLoader()); biInstantiatorCache.put(instantiatorKey, instantiator); } Map<String, BiFunction<? super S1, ? super S2, ?>> factoryPerName = new HashMap<String, BiFunction<? super S1, ? super S2, ?>>(); for(Entry<Parameter, BiFunction<? super S1, ? super S2, ?>> e : injections.entrySet()) { factoryPerName.put(e.getKey().getName(), e.getValue()); } if (instantiatorDefinition instanceof ExecutableInstantiatorDefinition) { return (BiInstantiator<S1, S2, T>) instantiator.getConstructor(Map.class).newInstance(factoryPerName); } else { return (BiInstantiator<S1, S2, T>) instantiator.getConstructor(Map.class, Instantiator.class).newInstance(factoryPerName, builderInstantiator); } } private final AtomicLong classNumber = new AtomicLong(); private String generateClassNameForInstantiator(final InstantiatorKey key) { StringBuilder sb = new StringBuilder(); sb.append( "org.simpleflatmapper.reflect.generated.") .append(getPackageName(key.getDeclaringClass())) .append(".AsmInstantiator").append(key.getDeclaringClass().getSimpleName()); sb.append("From"); sb.append(replaceArray(key.getSource().getSimpleName())); String[] injectedParams = key.getInjectedParams(); if (injectedParams != null && injectedParams.length > 0) { sb.append("Into"); int e = Math.min(16, injectedParams.length); for(int i = 0; i < e; i++) { if (i!=0) { sb.append("And"); } sb.append(injectedParams[i]); } int l = injectedParams.length - e; if (l >0) { sb.append("And").append(Integer.toString(l)).append("More"); } } sb.append("_I").append(Long.toHexString(classNumber.getAndIncrement())); return sb.toString(); } private String generateClassNameForBiInstantiator(final BiInstantiatorKey key) { StringBuilder sb = new StringBuilder(); sb.append( "org.simpleflatmapper.reflect.generated.") .append(getPackageName(key.getDeclaringClass())) .append(".AsmBiInstantiator").append(key.getDeclaringClass().getSimpleName()); sb.append("From"); sb.append(replaceArray(key.getS1().getSimpleName())); sb.append("And"); sb.append(replaceArray(key.getS2().getSimpleName())); String[] injectedParams = key.getInjectedParams(); if (injectedParams != null && injectedParams.length > 0) { sb.append("Into"); int e = Math.min(16, injectedParams.length); for(int i = 0; i < e; i++) { if (i!=0) { sb.append("And"); } sb.append(injectedParams[i]); } int l = injectedParams.length - e; if (l >0) { sb.append("And").append(Integer.toString(l)).append("More"); } } sb.append("_I").append(Long.toHexString(classNumber.getAndIncrement())); return sb.toString(); } public String replaceArray(String simpleName) { return simpleName.replace('[', 's').replace(']', '_'); } private String generateClassNameForSetter(final Method m) { return "org.simpleflatmapper.reflect.generated." + (m.getDeclaringClass().getCanonicalName()) + "AsmMethodSetter" +"_" + m.getName()+ "_" + replaceArray(m.getParameterTypes()[0].getSimpleName()) ; } private String generateClassNameForSetter(final Field field) { return "org.simpleflatmapper.reflect.generated." + (field.getDeclaringClass().getCanonicalName()) + "AsmFieldSetter" + "_" + field.getName() + "_" + replaceArray(field.getType().getSimpleName()) ; } private String generateClassNameForGetter(final Method m) { return "org.simpleflatmapper.reflect.generated." + (m.getDeclaringClass().getCanonicalName()) + "AsmMethodGetter" + "_" + m.getName() ; } private String generateClassNameForGetter(final Field m) { return "org.simpleflatmapper.reflect.generated." + (m.getDeclaringClass().getCanonicalName()) + "AsmFieldGetter" + "_" + m.getName() ; } public String getPackageName(Type target) { Package targetPackage = TypeHelper.toClass(target).getPackage(); return targetPackage != null ? targetPackage.getName() : "none"; } public long getNextClassNumber() { return classNumber.getAndIncrement(); } public <T> T registerOrCreate(Class<T> clazz, UnaryFactory<AsmFactory, T> factory) { T t = clazz.cast(subFactories.get(clazz)); if (t == null) { t = factory.newInstance(this); T tPresent = clazz.cast(subFactories.putIfAbsent(clazz, t)); if (tPresent != null) { return tPresent; } } return t; } }