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 ;
29- import java .lang .reflect .Constructor ;
3033import java .lang .reflect .Field ;
31- import java .lang .reflect .InvocationTargetException ;
3234import java .lang .reflect .Modifier ;
3335import java .util .HashMap ;
3436import java .util .Map ;
3537import java .util .function .Supplier ;
3638
37- class ObjectFieldDiscoverer implements FieldDiscoverer <Map <Field , Object >> {
39+ class ObjectFieldDiscoverer implements FieldDiscoverer <Map <ObjectFieldDiscoverer . FieldHandles , Object >> {
3840
39- static final ObjectFieldDiscoverer EMPTY_CONSTRUCTOR_INSTANCE = new ObjectFieldDiscoverer (type -> {
41+ private static final MethodHandles .Lookup OWN_LOOKUP = MethodHandles .lookup ();
42+
43+ static final ObjectFieldDiscoverer EMPTY_CONSTRUCTOR_INSTANCE = new ObjectFieldDiscoverer ((type , lookup ) -> {
4044 try {
41- final Constructor <?> constructor ;
42- constructor = erase (type .getType ()).getDeclaredConstructor ();
43- constructor .setAccessible (true );
45+ final MethodHandle constructor ;
46+ final Class <?> erased = erase (type .getType ());
47+ constructor = LookupShim .privateLookupIn (erased , lookup == null ? OWN_LOOKUP : lookup )
48+ .findConstructor (erased , MethodType .methodType (void .class ));
4449 return () -> {
4550 try {
46- return constructor .newInstance ();
47- } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e ) {
48- throw new RuntimeException (e );
51+ return constructor .invoke ();
52+ } catch (final RuntimeException ex ) {
53+ throw ex ;
54+ } catch (final Throwable thr ) {
55+ throw new RuntimeException (thr );
4956 }
5057 };
51- } catch (final NoSuchMethodException e ) {
58+ } catch (final NoSuchMethodException | IllegalAccessException e ) {
5259 return null ;
5360 }
5461 }, "Objects must have a zero-argument constructor to be able to create new instances" , false );
5562
56- private final CheckedFunction <AnnotatedType , @ Nullable Supplier <Object >, SerializationException > instanceFactory ;
63+ private final CheckedBiFunction <
64+ AnnotatedType ,
65+ MethodHandles .@ Nullable Lookup ,
66+ @ Nullable Supplier <Object >,
67+ SerializationException
68+ > instanceFactory ;
5769 private final String instanceUnavailableErrorMessage ;
5870 private final boolean requiresInstanceCreation ;
5971
6072 ObjectFieldDiscoverer (
6173 final CheckedFunction <AnnotatedType , @ Nullable Supplier <Object >, SerializationException > instanceFactory ,
6274 final @ Nullable String instanceUnavailableErrorMessage ,
6375 final boolean requiresInstanceCreation
76+ ) {
77+ this ((type , lookup ) -> instanceFactory .apply (type ), instanceUnavailableErrorMessage , requiresInstanceCreation );
78+ }
79+
80+ ObjectFieldDiscoverer (
81+ final CheckedBiFunction <AnnotatedType , MethodHandles .@ Nullable Lookup , @ Nullable Supplier <Object >, SerializationException > instanceFactory ,
82+ final @ Nullable String instanceUnavailableErrorMessage ,
83+ final boolean requiresInstanceCreation
6484 ) {
6585 this .instanceFactory = instanceFactory ;
6686 if (instanceUnavailableErrorMessage == null ) {
@@ -72,60 +92,65 @@ class ObjectFieldDiscoverer implements FieldDiscoverer<Map<Field, Object>> {
7292 }
7393
7494 @ Override
75- public <V > @ Nullable InstanceFactory <Map <Field , Object >> discover (final AnnotatedType target ,
76- final FieldCollector <Map <Field , Object >, V > collector ) throws SerializationException {
95+ public <V > @ Nullable InstanceFactory <Map <FieldHandles , Object >> discover (
96+ final AnnotatedType target ,
97+ final FieldCollector <Map <FieldHandles , Object >, V > collector ,
98+ final MethodHandles .@ Nullable Lookup lookup
99+ ) throws SerializationException {
77100 final Class <?> clazz = erase (target .getType ());
78101 if (clazz .isInterface ()) {
79102 throw new SerializationException (target .getType (), "ObjectMapper can only work with concrete types" );
80103 }
81104
82- final @ Nullable Supplier <Object > maker = this .instanceFactory .apply (target );
105+ final @ Nullable Supplier <Object > maker = this .instanceFactory .apply (target , lookup );
83106 if (maker == null && this .requiresInstanceCreation ) {
84107 return null ;
85108 }
86109
87110 AnnotatedType collectType = target ;
88111 Class <?> collectClass = clazz ;
89112 while (true ) {
90- collectFields (collectType , collector );
113+ collectFields (collectType , collector , lookup );
91114 collectClass = collectClass .getSuperclass ();
92115 if (collectClass .equals (Object .class )) {
93116 break ;
94117 }
95118 collectType = getExactSuperType (collectType , collectClass );
96119 }
97120
98- return new MutableInstanceFactory <Map <Field , Object >>() {
121+ return new MutableInstanceFactory <Map <FieldHandles , Object >>() {
99122
100123 @ Override
101- public Map <Field , Object > begin () {
124+ public Map <FieldHandles , Object > begin () {
102125 return new HashMap <>();
103126 }
104127
105128 @ 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 ()) {
129+ public void complete (final Object instance , final Map <FieldHandles , Object > intermediate ) throws SerializationException {
130+ for (final Map .Entry <FieldHandles , Object > entry : intermediate .entrySet ()) {
108131 try {
109132 // Handle implicit field initialization by detecting any existing information in the object
110133 if (entry .getValue () instanceof ImplicitProvider ) {
111134 final @ Nullable Object implicit = ((ImplicitProvider ) entry .getValue ()).provider .get ();
112135 if (implicit != null ) {
113- if (entry .getKey ().get (instance ) == null ) {
114- entry .getKey ().set (instance , implicit );
136+ if (entry .getKey ().getter . invoke (instance ) == null ) {
137+ entry .getKey ().setter . invoke (instance , implicit );
115138 }
116139 }
117140 } else {
118- entry .getKey ().set (instance , entry .getValue ());
141+ entry .getKey ().setter . invoke (instance , entry .getValue ());
119142 }
120143 } catch (final IllegalAccessException e ) {
121144 throw new SerializationException (target .getType (), e );
145+ } catch (final Throwable thr ) {
146+ throw new SerializationException (target .getType (), "An unexpected error occurred while trying to set a field" , thr );
122147 }
123148 }
124149 }
125150
126151 @ Override
127- public Object complete (final Map <Field , Object > intermediate ) throws SerializationException {
128- final Object instance = maker == null ? null : maker .get ();
152+ public Object complete (final Map <FieldHandles , Object > intermediate ) throws SerializationException {
153+ final @ Nullable Object instance = maker == null ? null : maker .get ();
129154 if (instance == null ) {
130155 throw new SerializationException (target .getType (), ObjectFieldDiscoverer .this .instanceUnavailableErrorMessage );
131156 }
@@ -141,22 +166,70 @@ public boolean canCreateInstances() {
141166 };
142167 }
143168
144- private void collectFields (final AnnotatedType clazz , final FieldCollector <Map <Field , Object >, ?> fieldMaker ) {
169+ private <V > void collectFields (
170+ final AnnotatedType clazz ,
171+ final FieldCollector <Map <FieldHandles , Object >, V > fieldMaker ,
172+ final MethodHandles .@ Nullable Lookup lookup
173+ ) throws SerializationException {
145174 for (final Field field : erase (clazz .getType ()).getDeclaredFields ()) {
146175 if ((field .getModifiers () & (Modifier .STATIC | Modifier .TRANSIENT )) != 0 ) {
147176 continue ;
148177 }
149178
150- field .setAccessible (true );
151179 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 );
180+ final FieldData .Deserializer <Map <FieldHandles , Object >> deserializer ;
181+ final CheckedFunction <V , @ Nullable Object , Exception > serializer ;
182+ final FieldHandles handles ;
183+ try {
184+ if (lookup != null ) {
185+ handles = new FieldHandles (field , lookup );
186+ } else {
187+ handles = new FieldHandles (field );
188+ }
189+ } catch (final IllegalAccessException ex ) {
190+ throw new SerializationException (fieldType , ex );
191+ }
192+ deserializer = (intermediate , val , implicitProvider ) -> {
193+ if (val != null ) {
194+ intermediate .put (handles , val );
195+ } else {
196+ intermediate .put (handles , new ImplicitProvider (implicitProvider ));
197+ }
198+ };
199+ serializer = inst -> {
200+ try {
201+ return handles .getter .invoke (inst );
202+ } catch (final Exception ex ) {
203+ throw ex ;
204+ } catch (final Throwable thr ) {
205+ throw new Exception (thr );
206+ }
207+ };
208+ fieldMaker .accept (
209+ field .getName (),
210+ fieldType ,
211+ Types .combinedAnnotations (fieldType , field ),
212+ deserializer ,
213+ serializer
214+ );
215+ }
216+ }
217+
218+ static class FieldHandles {
219+ final MethodHandle getter ;
220+ final MethodHandle setter ;
221+
222+ FieldHandles (final Field field ) throws IllegalAccessException {
223+ field .setAccessible (true );
224+ final MethodHandles .Lookup lookup = MethodHandles .publicLookup ();
225+
226+ this .getter = lookup .unreflectGetter (field );
227+ this .setter = lookup .unreflectSetter (field );
228+ }
229+
230+ FieldHandles (final Field field , final MethodHandles .Lookup lookup ) throws IllegalAccessException {
231+ this .getter = lookup .unreflectGetter (field );
232+ this .setter = lookup .unreflectSetter (field );
160233 }
161234 }
162235
0 commit comments