2222
2323import org .checkerframework .checker .nullness .qual .Nullable ;
2424import org .spongepowered .configurate .serialize .SerializationException ;
25+ import org .spongepowered .configurate .util .CheckedBiFunction ;
2526import org .spongepowered .configurate .util .CheckedFunction ;
2627import org .spongepowered .configurate .util .Types ;
2728
29+ import java .lang .invoke .MethodHandle ;
30+ import java .lang .invoke .MethodHandles ;
31+ import java .lang .invoke .MethodType ;
2832import java .lang .reflect .AnnotatedType ;
2933import java .lang .reflect .Constructor ;
3034import java .lang .reflect .Field ;
31- import java .lang .reflect .InvocationTargetException ;
3235import java .lang .reflect .Modifier ;
3336import java .util .HashMap ;
3437import java .util .Map ;
3538import java .util .function .Supplier ;
3639
37- class ObjectFieldDiscoverer implements FieldDiscoverer <Map <Field , Object >> {
40+ class ObjectFieldDiscoverer implements FieldDiscoverer <Map <ObjectFieldDiscoverer . FieldHandles , Object >> {
3841
39- static final ObjectFieldDiscoverer EMPTY_CONSTRUCTOR_INSTANCE = new ObjectFieldDiscoverer (type -> {
42+ private static final MethodHandles .Lookup OWN_LOOKUP = MethodHandles .lookup ();
43+
44+ static final ObjectFieldDiscoverer EMPTY_CONSTRUCTOR_INSTANCE = new ObjectFieldDiscoverer ((type , lookup ) -> {
4045 try {
41- final Constructor <?> constructor ;
42- constructor = erase (type .getType ()).getDeclaredConstructor ();
43- constructor .setAccessible (true );
46+ final MethodHandle constructor ;
47+ final Class <?> erased = erase (type .getType ());
48+ if (lookup == null ) { // legacy
49+ final Constructor <?> construct = erased .getDeclaredConstructor ();
50+ construct .setAccessible (true );
51+ constructor = OWN_LOOKUP .unreflectConstructor (construct );
52+ } else {
53+ constructor = LookupShim .privateLookupIn (erased , lookup )
54+ .findConstructor (erased , MethodType .methodType (void .class ));
55+ }
56+
4457 return () -> {
4558 try {
46- return constructor .newInstance ();
47- } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e ) {
48- throw new RuntimeException (e );
59+ return constructor .invoke ();
60+ } catch (final RuntimeException ex ) {
61+ throw ex ;
62+ } catch (final Throwable thr ) {
63+ throw new RuntimeException (thr );
4964 }
5065 };
51- } catch (final NoSuchMethodException e ) {
66+ } catch (final NoSuchMethodException | IllegalAccessException e ) {
5267 return null ;
5368 }
5469 }, "Objects must have a zero-argument constructor to be able to create new instances" , false );
5570
56- private final CheckedFunction <AnnotatedType , @ Nullable Supplier <Object >, SerializationException > instanceFactory ;
71+ private final CheckedBiFunction <
72+ AnnotatedType ,
73+ MethodHandles .@ Nullable Lookup ,
74+ @ Nullable Supplier <Object >,
75+ SerializationException
76+ > instanceFactory ;
5777 private final String instanceUnavailableErrorMessage ;
5878 private final boolean requiresInstanceCreation ;
5979
6080 ObjectFieldDiscoverer (
6181 final CheckedFunction <AnnotatedType , @ Nullable Supplier <Object >, SerializationException > instanceFactory ,
6282 final @ Nullable String instanceUnavailableErrorMessage ,
6383 final boolean requiresInstanceCreation
84+ ) {
85+ this ((type , lookup ) -> instanceFactory .apply (type ), instanceUnavailableErrorMessage , requiresInstanceCreation );
86+ }
87+
88+ ObjectFieldDiscoverer (
89+ final CheckedBiFunction <AnnotatedType , MethodHandles .@ Nullable Lookup , @ Nullable Supplier <Object >, SerializationException > instanceFactory ,
90+ final @ Nullable String instanceUnavailableErrorMessage ,
91+ final boolean requiresInstanceCreation
6492 ) {
6593 this .instanceFactory = instanceFactory ;
6694 if (instanceUnavailableErrorMessage == null ) {
@@ -72,60 +100,65 @@ class ObjectFieldDiscoverer implements FieldDiscoverer<Map<Field, Object>> {
72100 }
73101
74102 @ Override
75- public <V > @ Nullable InstanceFactory <Map <Field , Object >> discover (final AnnotatedType target ,
76- final FieldCollector <Map <Field , Object >, V > collector ) throws SerializationException {
103+ public <V > @ Nullable InstanceFactory <Map <FieldHandles , Object >> discover (
104+ final AnnotatedType target ,
105+ final FieldCollector <Map <FieldHandles , Object >, V > collector ,
106+ final MethodHandles .@ Nullable Lookup lookup
107+ ) throws SerializationException {
77108 final Class <?> clazz = erase (target .getType ());
78109 if (clazz .isInterface ()) {
79110 throw new SerializationException (target .getType (), "ObjectMapper can only work with concrete types" );
80111 }
81112
82- final @ Nullable Supplier <Object > maker = this .instanceFactory .apply (target );
113+ final @ Nullable Supplier <Object > maker = this .instanceFactory .apply (target , lookup );
83114 if (maker == null && this .requiresInstanceCreation ) {
84115 return null ;
85116 }
86117
87118 AnnotatedType collectType = target ;
88119 Class <?> collectClass = clazz ;
89120 while (true ) {
90- collectFields (collectType , collector );
121+ collectFields (collectType , collector , lookup );
91122 collectClass = collectClass .getSuperclass ();
92123 if (collectClass .equals (Object .class )) {
93124 break ;
94125 }
95126 collectType = getExactSuperType (collectType , collectClass );
96127 }
97128
98- return new MutableInstanceFactory <Map <Field , Object >>() {
129+ return new MutableInstanceFactory <Map <FieldHandles , Object >>() {
99130
100131 @ Override
101- public Map <Field , Object > begin () {
132+ public Map <FieldHandles , Object > begin () {
102133 return new HashMap <>();
103134 }
104135
105136 @ Override
106- public void complete (final Object instance , final Map <Field , Object > intermediate ) throws SerializationException {
107- for (final Map .Entry <Field , Object > entry : intermediate .entrySet ()) {
137+ public void complete (final Object instance , final Map <FieldHandles , Object > intermediate ) throws SerializationException {
138+ for (final Map .Entry <FieldHandles , Object > entry : intermediate .entrySet ()) {
108139 try {
109140 // Handle implicit field initialization by detecting any existing information in the object
110141 if (entry .getValue () instanceof ImplicitProvider ) {
111142 final @ Nullable Object implicit = ((ImplicitProvider ) entry .getValue ()).provider .get ();
112143 if (implicit != null ) {
113- if (entry .getKey ().get (instance ) == null ) {
114- entry .getKey ().set (instance , implicit );
144+ if (entry .getKey ().getter . invoke (instance ) == null ) {
145+ entry .getKey ().setter . invoke (instance , implicit );
115146 }
116147 }
117148 } else {
118- entry .getKey ().set (instance , entry .getValue ());
149+ entry .getKey ().setter . invoke (instance , entry .getValue ());
119150 }
120151 } catch (final IllegalAccessException e ) {
121152 throw new SerializationException (target .getType (), e );
153+ } catch (final Throwable thr ) {
154+ throw new SerializationException (target .getType (), "An unexpected error occurred while trying to set a field" , thr );
122155 }
123156 }
124157 }
125158
126159 @ Override
127- public Object complete (final Map <Field , Object > intermediate ) throws SerializationException {
128- final Object instance = maker == null ? null : maker .get ();
160+ public Object complete (final Map <FieldHandles , Object > intermediate ) throws SerializationException {
161+ final @ Nullable Object instance = maker == null ? null : maker .get ();
129162 if (instance == null ) {
130163 throw new SerializationException (target .getType (), ObjectFieldDiscoverer .this .instanceUnavailableErrorMessage );
131164 }
@@ -141,22 +174,70 @@ public boolean canCreateInstances() {
141174 };
142175 }
143176
144- private void collectFields (final AnnotatedType clazz , final FieldCollector <Map <Field , Object >, ?> fieldMaker ) {
177+ private <V > void collectFields (
178+ final AnnotatedType clazz ,
179+ final FieldCollector <Map <FieldHandles , Object >, V > fieldMaker ,
180+ final MethodHandles .@ Nullable Lookup lookup
181+ ) throws SerializationException {
145182 for (final Field field : erase (clazz .getType ()).getDeclaredFields ()) {
146183 if ((field .getModifiers () & (Modifier .STATIC | Modifier .TRANSIENT )) != 0 ) {
147184 continue ;
148185 }
149186
150- field .setAccessible (true );
151187 final AnnotatedType fieldType = getFieldType (field , clazz );
152- fieldMaker .accept (field .getName (), fieldType , Types .combinedAnnotations (fieldType , field ),
153- (intermediate , val , implicitProvider ) -> {
154- if (val != null ) {
155- intermediate .put (field , val );
156- } else {
157- intermediate .put (field , new ImplicitProvider (implicitProvider ));
158- }
159- }, field ::get );
188+ final FieldData .Deserializer <Map <FieldHandles , Object >> deserializer ;
189+ final CheckedFunction <V , @ Nullable Object , Exception > serializer ;
190+ final FieldHandles handles ;
191+ try {
192+ if (lookup != null ) {
193+ handles = new FieldHandles (field , lookup );
194+ } else {
195+ handles = new FieldHandles (field );
196+ }
197+ } catch (final IllegalAccessException ex ) {
198+ throw new SerializationException (fieldType , ex );
199+ }
200+ deserializer = (intermediate , val , implicitProvider ) -> {
201+ if (val != null ) {
202+ intermediate .put (handles , val );
203+ } else {
204+ intermediate .put (handles , new ImplicitProvider (implicitProvider ));
205+ }
206+ };
207+ serializer = inst -> {
208+ try {
209+ return handles .getter .invoke (inst );
210+ } catch (final Exception ex ) {
211+ throw ex ;
212+ } catch (final Throwable thr ) {
213+ throw new Exception (thr );
214+ }
215+ };
216+ fieldMaker .accept (
217+ field .getName (),
218+ fieldType ,
219+ Types .combinedAnnotations (fieldType , field ),
220+ deserializer ,
221+ serializer
222+ );
223+ }
224+ }
225+
226+ static class FieldHandles {
227+ final MethodHandle getter ;
228+ final MethodHandle setter ;
229+
230+ FieldHandles (final Field field ) throws IllegalAccessException {
231+ field .setAccessible (true );
232+ final MethodHandles .Lookup lookup = MethodHandles .publicLookup ();
233+
234+ this .getter = lookup .unreflectGetter (field );
235+ this .setter = lookup .unreflectSetter (field );
236+ }
237+
238+ FieldHandles (final Field field , final MethodHandles .Lookup lookup ) throws IllegalAccessException {
239+ this .getter = lookup .unreflectGetter (field );
240+ this .setter = lookup .unreflectSetter (field );
160241 }
161242 }
162243
0 commit comments