com.fasterxml.jackson.module.blackbird.deser.BBDeserializerModifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jackson-module-blackbird Show documentation
Show all versions of jackson-module-blackbird Show documentation
Jackson (https://github.com/FasterXML/jackson) extension module
that uses LambdaMetafactory based code generation to replace reflection calls.
The newest version!
package com.fasterxml.jackson.module.blackbird.deser;
import java.lang.invoke.LambdaConversionException;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.ObjIntConsumer;
import java.util.function.ObjLongConsumer;
import java.util.function.UnaryOperator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.deser.impl.MethodProperty;
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.util.ClassUtil;
import com.fasterxml.jackson.module.blackbird.util.ReflectionHack;
public class BBDeserializerModifier extends BeanDeserializerModifier
{
private static final long serialVersionUID = 1L;
private static final MethodHandle TRAMPOLINE, BOOLEAN_TRAMPOLINE, LONG_TRAMPOLINE, INT_TRAMPOLINE;
static {
try {
TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "trampoline",
MethodType.methodType(void.class, BiFunction.class, Object.class, Object.class));
BOOLEAN_TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "booleanTrampoline",
MethodType.methodType(void.class, ObjBooleanBiFunction.class, Object.class, boolean.class));
LONG_TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "longTrampoline",
MethodType.methodType(void.class, ObjLongBiFunction.class, Object.class, long.class));
INT_TRAMPOLINE = MethodHandles.lookup().findStatic(BBDeserializerModifier.class, "intTrampoline",
MethodType.methodType(void.class, ObjIntBiFunction.class, Object.class, int.class));
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
private final Function, Lookup> _lookups;
private final UnaryOperator _accessGrant;
public BBDeserializerModifier(Function, MethodHandles.Lookup> lookups, UnaryOperator accessGrant)
{
_lookups = lookups;
_accessGrant = accessGrant;
}
/*
/**********************************************************************
/* BeanDeserializerModifier methods
/**********************************************************************
*/
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config,
BeanDescription beanDesc, BeanDeserializerBuilder builder)
{
final Class> beanClass = beanDesc.getBeanClass();
MethodHandles.Lookup lookup = _lookups.apply(beanClass);
if (lookup == null) {
return builder;
}
try {
lookup = ReflectionHack.privateLookupIn(beanClass, lookup);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
/* Hmmh. Can we access stuff from private classes?
* Possibly, if we can use parent class loader.
* (should probably skip all non-public?)
*/
if (Modifier.isPrivate(beanClass.getModifiers())) { // TODO??
return builder;
}
lookup = _accessGrant.apply(lookup);
List> newProps = findOptimizableProperties(
lookup, config, builder.getProperties());
// and if we found any, create mutator proxy, replace property objects
if (!newProps.isEmpty()) {
for (OptimizedSettableBeanProperty> prop : newProps) {
builder.addOrReplaceProperty(prop, true);
}
}
// Second thing: see if we could (re)generate Creator(s):
ValueInstantiator inst = builder.getValueInstantiator();
/* Hmmh. Probably better to require exact default implementation
* and not sub-class; chances are sub-class uses its own
* construction anyway.
*/
if (inst.getClass() == StdValueInstantiator.class) {
// also, only override if using default creator (no-arg ctor, no-arg static factory)
if (inst.canCreateUsingDefault() || inst.canCreateFromObjectWith()) {
inst = new CreatorOptimizer(beanClass, lookup, (StdValueInstantiator) inst).createOptimized();
if (inst != null) {
builder.setValueInstantiator(inst);
}
}
}
// also: may want to replace actual BeanDeserializer as well? For this, need to replace builder
// (but only if builder is the original standard one; don't want to break other impls)
if (builder.getClass() == BeanDeserializerBuilder.class) {
return new SuperSonicDeserializerBuilder(builder);
}
return builder;
}
/*
/**********************************************************************
/* Internal methods
/**********************************************************************
*/
protected List> findOptimizableProperties(
Lookup lookup, DeserializationConfig config,
Iterator propIterator)
{
ArrayList> newProps = new ArrayList>();
// Ok, then, find any properties for which we could generate accessors
while (propIterator.hasNext()) {
try {
nextProperty(propIterator.next(), lookup, newProps);
} catch (Throwable e) {
if (e instanceof Error) {
throw (Error) e;
}
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
}
return newProps;
}
@SuppressWarnings("unchecked")
private void nextProperty(SettableBeanProperty prop,
Lookup lookup,
ArrayList> newProps) throws Throwable
{
AnnotatedMember member = prop.getMember();
Member jdkMember = member.getMember();
// if we ever support virtual properties, this would be null, so check, skip
if (jdkMember == null) {
return;
}
// First: we can't access private fields or methods....
if (Modifier.isPrivate(jdkMember.getModifiers())) {
return;
}
// (although, interestingly enough, can seem to access private classes...)
// 30-Jul-2012, tatu: [module-afterburner#6]: Needs to skip custom deserializers, if any.
if (prop.hasValueDeserializer()) {
if (!isDefaultDeserializer(prop.getValueDeserializer())) {
return;
}
}
MethodHandle setter;
Class> type;
if (jdkMember instanceof Method && prop instanceof MethodProperty) {
final Method method = (Method) jdkMember;
setter = lookup.unreflect(method);
type = ((AnnotatedMethod) member).getRawParameterType(0);
} else {
return;
//setter = lookup.unreflectGetter((Field) jdkMember);
}
if (type.isPrimitive()) {
if (type == Integer.TYPE) {
newProps.add(new SettableIntProperty(prop,
createSetter(lookup, ObjIntConsumer.class, ObjIntBiFunction.class,
INT_TRAMPOLINE, int.class, setter)));
} else if (type == Long.TYPE) {
newProps.add(new SettableLongProperty(prop,
createSetter(lookup, ObjLongConsumer.class, ObjLongBiFunction.class,
LONG_TRAMPOLINE, long.class, setter)));
} else if (type == Boolean.TYPE) {
newProps.add(new SettableBooleanProperty(prop,
createSetter(lookup, ObjBooleanConsumer.class, ObjBooleanBiFunction.class,
BOOLEAN_TRAMPOLINE, boolean.class, setter)));
}
} else {
if (type == String.class) {
newProps.add(new SettableStringProperty(prop,
createSetter(lookup, BiConsumer.class, BiFunction.class, TRAMPOLINE, Object.class, setter)));
} else {
newProps.add(new SettableObjectProperty(prop,
createSetter(lookup, BiConsumer.class, BiFunction.class, TRAMPOLINE, Object.class, setter)));
}
}
}
private T createSetter(Lookup lookup, Class iface, Class> thunkType, MethodHandle trampoline, Class> valueType, MethodHandle setter)
throws Throwable, LambdaConversionException {
if (setter.type().returnType() == void.class) {
return iface.cast(LambdaMetafactory.metafactory(
lookup,
"accept",
MethodType.methodType(iface),
MethodType.methodType(void.class, Object.class, valueType),
setter,
setter.type())
.getTarget().invoke());
}
Object builtThunk = LambdaMetafactory.metafactory(
lookup,
"apply",
MethodType.methodType(thunkType),
MethodType.methodType(Object.class, Object.class, valueType),
setter,
setter.type())
.getTarget().invoke();
return iface.cast(LambdaMetafactory.metafactory(
MethodHandles.lookup(),
"accept",
MethodType.methodType(iface, thunkType),
MethodType.methodType(void.class, Object.class, valueType),
trampoline,
MethodType.methodType(void.class, Object.class, valueType))
.getTarget().invoke(builtThunk));
}
/**
* Helper method used to check whether given deserializer is the default
* deserializer implementation: this is necessary to avoid overriding other
* kinds of deserializers.
*/
protected boolean isDefaultDeserializer(JsonDeserializer> deser) {
return ClassUtil.isJacksonStdImpl(deser)
// 07-May-2018, tatu: Probably can't happen but just in case
|| (deser instanceof SuperSonicBeanDeserializer);
}
// These trampolines adapt BiFunction-like setters (i.e. returns a value)
// to BiConsumer style through a generated thunk lambda.
@FunctionalInterface
public interface ObjIntBiFunction {
Object apply(Object bean, int value);
}
@FunctionalInterface
public interface ObjLongBiFunction {
Object apply(Object bean, long value);
}
@FunctionalInterface
public interface ObjBooleanBiFunction {
Object apply(Object bean, boolean value);
}
static void intTrampoline(ObjIntBiFunction thunk, Object bean, int value) {
thunk.apply(bean, value);
}
static void longTrampoline(ObjLongBiFunction thunk, Object bean, long value) {
thunk.apply(bean, value);
}
static void booleanTrampoline(ObjBooleanBiFunction thunk, Object bean, boolean value) {
thunk.apply(bean, value);
}
static void trampoline(BiFunction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy