Skip to content

Commit 56bcf12

Browse files
committed
createFunction
1 parent 1474bef commit 56bcf12

File tree

4 files changed

+161
-6
lines changed

4 files changed

+161
-6
lines changed

core-jdk11/src/main/java/com/alibaba/fastjson2/introspect/PropertyAccessorFactoryMethodHandle.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.lang.reflect.Type;
1414
import java.math.BigDecimal;
1515
import java.math.BigInteger;
16+
import java.util.function.Function;
1617
import java.util.function.Supplier;
1718

1819
/**
@@ -42,6 +43,7 @@ protected MethodHandles.Lookup lookup(Class<?> declaringClass) {
4243
* @param constructor the constructor to use for object instantiation
4344
* @return a Supplier that creates new instances using the provided constructor
4445
*/
46+
@Override
4547
public Supplier createSupplier(Constructor constructor) {
4648
try {
4749
MethodHandles.Lookup lookup = lookup(constructor.getDeclaringClass());
@@ -59,6 +61,32 @@ public Supplier createSupplier(Constructor constructor) {
5961
}
6062
}
6163

64+
/**
65+
* Creates a Function that can instantiate objects using the given constructor
66+
* via MethodHandle for better performance than traditional reflection.
67+
* If the MethodHandle approach fails, it falls back to the parent class implementation.
68+
*
69+
* @param constructor the constructor to use for object instantiation
70+
* @return a Supplier that creates new instances using the provided constructor
71+
*/
72+
@Override
73+
public Function createFunction(Constructor constructor) {
74+
try {
75+
MethodHandles.Lookup lookup = lookup(constructor.getDeclaringClass());
76+
MethodHandle methodHandle = lookup.unreflectConstructor(constructor);
77+
return (arg) -> {
78+
try {
79+
return methodHandle.invoke(arg);
80+
} catch (Throwable e) {
81+
throw new RuntimeException(e);
82+
}
83+
};
84+
} catch (Throwable ignored) {
85+
// ignore
86+
return super.createFunction(constructor);
87+
}
88+
}
89+
6290
/**
6391
* Creates a property accessor for the given field using MethodHandle-based implementation.
6492
* Different accessor implementations are created based on the field type to provide

core-jdk11/src/test/java/com/alibaba/fastjson2/introspect/PropertyAccessorFactoryVarHandleTest.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.lang.reflect.Method;
1010
import java.math.BigDecimal;
1111
import java.math.BigInteger;
12-
import java.util.function.Supplier;
1312

1413
import static org.junit.jupiter.api.Assertions.*;
1514

@@ -40,6 +39,13 @@ public static class TestClass {
4039
private Float floatObjField = 10.5f;
4140
private Double doubleObjField = 20.7;
4241
private Boolean booleanObjField = true;
42+
43+
public TestClass() {
44+
}
45+
46+
public TestClass(Byte byteObjField) {
47+
this.byteObjField = byteObjField;
48+
}
4349
}
4450

4551
public static java.util.Map<Class<?>, Field> fieldMap = new java.util.HashMap<>();
@@ -98,8 +104,10 @@ static PropertyAccessorFactory[] propertyAccessorFactories() {
98104
@ParameterizedTest
99105
@MethodSource("propertyAccessorFactories")
100106
public void allocate(PropertyAccessorFactory factory) throws Exception {
101-
Supplier supplier = factory.createSupplier(TestClass.class.getDeclaredConstructor());
102-
assertNotNull(supplier.get());
107+
assertNotNull(factory.createSupplier(TestClass.class.getDeclaredConstructor())
108+
.get());
109+
110+
assertEquals((byte) 50, ((TestClass) factory.createFunction(TestClass.class.getDeclaredConstructor(Byte.class)).apply((byte) 50)).byteObjField);
103111
}
104112

105113
@ParameterizedTest

core/src/main/java/com/alibaba/fastjson2/introspect/PropertyAccessorFactory.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,21 @@ public Supplier createSupplier(Constructor constructor) {
4444
return new ConstructorSupplier(constructor);
4545
}
4646

47+
/**
48+
* Creates a Function that can instantiate objects using the given constructor.
49+
* @param constructor the constructor to use for object instantiation
50+
* @return a Function that creates new instances using the provided constructor
51+
*/
52+
public Function createFunction(Constructor constructor) {
53+
return new ConstructorFunction(constructor);
54+
}
55+
4756
/**
4857
* A Supplier implementation that uses reflection to create new instances
4958
* of a class via its constructor. This class handles constructor accessibility
5059
* and instantiation errors appropriately.
5160
*/
52-
class ConstructorSupplier implements Supplier {
61+
static final class ConstructorSupplier implements Supplier {
5362
private final Constructor constructor;
5463

5564
/**
@@ -111,6 +120,62 @@ public Object get() {
111120
}
112121
}
113122

123+
static final class ConstructorFunction implements Function {
124+
private final Constructor constructor;
125+
126+
/**
127+
* Creates a ConstructorSupplier for the given constructor.
128+
* Automatically makes the constructor accessible.
129+
*
130+
* @param constructor the constructor to use for instantiation
131+
*/
132+
public ConstructorFunction(Constructor constructor) {
133+
this.constructor = constructor;
134+
setAccessible();
135+
}
136+
137+
/**
138+
* Makes the constructor accessible, handling any security exceptions
139+
* that might occur during the process.
140+
*/
141+
protected void setAccessible() {
142+
try {
143+
constructor.setAccessible(true);
144+
} catch (Exception e) {
145+
throw new JSONException(e.getMessage(), e);
146+
}
147+
}
148+
149+
/**
150+
* Creates a specific JSON exception for constructor accessibility errors.
151+
*
152+
* @param e the original exception that occurred
153+
* @return a JSONException with detailed error information
154+
*/
155+
protected JSONException errorOnSetAccessible(Exception e) {
156+
return new JSONException(constructor.toString().concat(" setAccessible error"), e);
157+
}
158+
159+
/**
160+
* Creates a specific JSON exception for constructor instantiation errors.
161+
*
162+
* @param e the original exception that occurred
163+
* @return a JSONException with detailed error information
164+
*/
165+
protected JSONException errorOnNewInstance(Exception e) {
166+
return new JSONException(constructor.toString().concat(" newInstance error"), e);
167+
}
168+
169+
@Override
170+
public Object apply(Object arg) {
171+
try {
172+
return constructor.newInstance(arg);
173+
} catch (Exception e) {
174+
throw errorOnNewInstance(e);
175+
}
176+
}
177+
}
178+
114179
/**
115180
* Creates a property accessor for the specified field.
116181
* This method analyzes the field type and returns an appropriate

core/src/main/java/com/alibaba/fastjson2/introspect/PropertyAccessorFactoryLambda.java

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
import java.lang.reflect.Type;
1111
import java.util.function.*;
1212

13-
import static com.alibaba.fastjson2.util.TypeUtils.METHOD_TYPE_OBJECT;
14-
import static com.alibaba.fastjson2.util.TypeUtils.METHOD_TYPE_SUPPLIER;
13+
import static com.alibaba.fastjson2.util.TypeUtils.*;
14+
import static com.alibaba.fastjson2.util.TypeUtils.METHOD_TYPE_OBJECT_OBJECT;
1515

1616
@SuppressWarnings("ALL")
1717
public abstract class PropertyAccessorFactoryLambda extends PropertyAccessorFactory {
@@ -59,6 +59,60 @@ public Supplier createSupplier(Constructor constructor) {
5959
return super.createSupplier(constructor);
6060
}
6161

62+
public Function createFunction(Constructor constructor) {
63+
try {
64+
Class<?> declaringClass = constructor.getDeclaringClass();
65+
MethodHandles.Lookup lookup = JDKUtils.trustedLookup(declaringClass);
66+
Class<?>[] parameterTypes = constructor.getParameterTypes();
67+
Class<?> param0 = parameterTypes[0];
68+
69+
MethodHandle methodHandle = lookup.findConstructor(
70+
declaringClass,
71+
MethodType.methodType(void.class, param0)
72+
);
73+
74+
CallSite callSite = LambdaMetafactory.metafactory(
75+
lookup,
76+
"apply",
77+
METHOD_TYPE_FUNCTION,
78+
METHOD_TYPE_OBJECT_OBJECT,
79+
methodHandle,
80+
MethodType.methodType(declaringClass, box(param0))
81+
);
82+
return (Function) callSite.getTarget().invokeExact();
83+
} catch (Throwable ignored) {
84+
return super.createFunction(constructor);
85+
}
86+
}
87+
88+
static Class<?> box(Class cls) {
89+
if (cls == int.class) {
90+
return Integer.class;
91+
}
92+
if (cls == long.class) {
93+
return Long.class;
94+
}
95+
if (cls == boolean.class) {
96+
return Boolean.class;
97+
}
98+
if (cls == short.class) {
99+
return Short.class;
100+
}
101+
if (cls == byte.class) {
102+
return Byte.class;
103+
}
104+
if (cls == char.class) {
105+
return Character.class;
106+
}
107+
if (cls == float.class) {
108+
return Float.class;
109+
}
110+
if (cls == double.class) {
111+
return Double.class;
112+
}
113+
return cls;
114+
}
115+
62116
/**
63117
* Creates a property accessor using getter and/or setter methods.
64118
* This method delegates to the parent class's implementation.

0 commit comments

Comments
 (0)