99import org .objectweb .asm .signature .SignatureWriter ;
1010
1111import java .lang .invoke .MethodHandles ;
12- import java .lang .reflect .Constructor ;
13- import java .lang .reflect .Field ;
14- import java .lang .reflect .InvocationTargetException ;
15- import java .lang .reflect .Modifier ;
12+ import java .lang .reflect .*;
1613import java .util .Arrays ;
1714import java .util .Map ;
1815import java .util .concurrent .ConcurrentHashMap ;
1916
2017public final class ClassAccess {
21-
18+
2219 private static final Map <FieldAccessKey <?>, FieldAccess <?, ?>> fieldCache = new ConcurrentHashMap <>();
2320 private static final Map <MethodAccessKey <?>, MethodAccess <?, ?>> methodCache = new ConcurrentHashMap <>();
2421
25-
2622 private ClassAccess () {}
2723
2824 public static <T > T getField (Object source , String name ) throws NoSuchFieldException {
@@ -105,35 +101,42 @@ public static <S> S invokeConstructor(Class<S> source, Class<?>[] parameters, Ob
105101 }
106102
107103 public static <S , T > FieldAccess <S , T > field (Class <S > source , String name ) throws NoSuchFieldException {
108- return fieldAccess (FieldAccessKey . of (source , name ));
104+ return fieldAccess (new FieldAccessKey <> (source , name ));
109105 }
110106
111- private static <S , T > FieldAccess <S , T > fieldAccess (FieldAccessKey <S > key ) {
112- //noinspection unchecked
113- return ( FieldAccess < S , T >) fieldCache . computeIfAbsent ( key , k -> {
114- Class <?> aClass = defineHiddenIn (key .source (), generateFieldAccess (key ));
107+ private static <S , T > FieldAccess <S , T > fieldAccess (FieldAccessKey <S > key ) throws NoSuchFieldException {
108+ FieldAccess <?, ?> fieldAccess = fieldCache . get ( key );
109+ if ( fieldAccess == null ) {
110+ Class <?> generated = defineHiddenIn (key .source (), generateFieldAccess (key ));
115111 try {
116- return (FieldAccess <?, ?>) aClass .getDeclaredConstructor ().newInstance ();
117- } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e ) {
112+ fieldAccess = (FieldAccess <?, ?>) generated .getDeclaredConstructor ().newInstance ();
113+ fieldCache .put (key , fieldAccess );
114+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
118115 throw new RuntimeException (e ); // Should not happen
119116 }
120- });
117+ }
118+ //noinspection unchecked
119+ return (FieldAccess <S , T >) fieldAccess ;
120+
121121 }
122122
123123 public static <S , T > MethodAccess <S , T > method (Class <S > source , String name , Class <?>... parameters ) throws NoSuchMethodException {
124- return methodAccess (MethodAccessKey . of (source , name , parameters ));
124+ return methodAccess (new MethodAccessKey <> (source , name , parameters ));
125125 }
126126
127- private static <S , T > MethodAccess <S , T > methodAccess (MethodAccessKey <S > key ) {
128- //noinspection unchecked
129- return ( MethodAccess < S , T >) methodCache . computeIfAbsent ( key , k -> {
130- Class <?> aClass = defineHiddenIn (key .source (), generateMethodAccess (key ));
127+ private static <S , T > MethodAccess <S , T > methodAccess (MethodAccessKey <S > key ) throws NoSuchMethodException {
128+ MethodAccess <?, ?> methodAccess = methodCache . get ( key );
129+ if ( methodAccess == null ) {
130+ Class <?> generated = defineHiddenIn (key .source (), generateMethodAccess (key ));
131131 try {
132- return (MethodAccess <?, ?>) aClass .getDeclaredConstructor ().newInstance ();
133- } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e ) {
132+ methodAccess = (MethodAccess <?, ?>) generated .getDeclaredConstructor ().newInstance ();
133+ methodCache .put (key , methodAccess );
134+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
134135 throw new RuntimeException (e ); // Should not happen
135136 }
136- });
137+ }
138+ //noinspection unchecked
139+ return (MethodAccess <S , T >) methodAccess ;
137140 }
138141
139142 private static Class <?> defineHiddenIn (Class <?> host , byte [] bytes ) {
@@ -146,13 +149,16 @@ private static Class<?> defineHiddenIn(Class<?> host, byte[] bytes) {
146149 }
147150 }
148151
149- private static byte [] generateFieldAccess (FieldAccessKey <?> key ) {
152+ private static byte [] generateFieldAccess (FieldAccessKey <?> key ) throws NoSuchFieldException {
150153 Type objectT = Type .getType (Object .class );
151154 Type fieldAccessT = Type .getType (FieldAccess .class );
152155 Type sourceT = Type .getType (key .source ());
153- Type fieldT = Type .getType (key .type ());
154- Class <?> boxedField = TypeUtils .box (key .type ());
156+
157+ Field field = key .field ();
158+ Type fieldT = Type .getType (field .getType ());
159+ Class <?> boxedField = TypeUtils .box (field .getType ());
155160 Type boxedFieldT = boxedField != null ? Type .getType (boxedField ) : fieldT ;
161+ boolean isStatic = Modifier .isStatic (field .getModifiers ());
156162
157163 ClassWriter cw = new ClassWriter (ClassWriter .COMPUTE_MAXS | ClassWriter .COMPUTE_FRAMES );
158164
@@ -172,12 +178,12 @@ private static byte[] generateFieldAccess(FieldAccessKey<?> key) {
172178 GeneratorAdapter ga = new GeneratorAdapter (mv , Opcodes .ACC_PUBLIC , "get" , getterDesc );
173179
174180 ga .visitCode ();
175- if (!key .isStatic ()) {
181+ if (isStatic ) {
182+ ga .getStatic (sourceT , key .name (), fieldT );
183+ } else {
176184 ga .loadArg (0 );
177185 ga .checkCast (sourceT );
178186 ga .getField (sourceT , key .name (), fieldT );
179- } else {
180- ga .getStatic (sourceT , key .name (), fieldT );
181187 }
182188 ga .box (fieldT );
183189 ga .returnValue ();
@@ -188,19 +194,19 @@ private static byte[] generateFieldAccess(FieldAccessKey<?> key) {
188194 ga = new GeneratorAdapter (mv , Opcodes .ACC_PUBLIC , "set" , setterDesc );
189195
190196 ga .visitCode ();
191- if (key . constant ( )) {
192- ga .throwException (Type .getType (IllegalAccessException .class ), "Cannot modify final field " + key . readable () );
197+ if (Modifier . isFinal ( field . getModifiers () )) {
198+ ga .throwException (Type .getType (IllegalAccessException .class ), "Cannot modify final field " + field );
193199 } else {
194- if (!key .isStatic ()) {
195- ga .loadArg (0 );
196- ga .checkCast (sourceT );
200+ if (isStatic ) {
197201 ga .loadArg (1 );
198202 ga .unbox (fieldT );
199- ga .putField (sourceT , key .name (), fieldT );
203+ ga .putStatic (sourceT , key .name (), fieldT );
200204 } else {
205+ ga .loadArg (0 );
206+ ga .checkCast (sourceT );
201207 ga .loadArg (1 );
202208 ga .unbox (fieldT );
203- ga .putStatic (sourceT , key .name (), fieldT );
209+ ga .putField (sourceT , key .name (), fieldT );
204210 }
205211 ga .returnValue ();
206212 }
@@ -210,12 +216,16 @@ private static byte[] generateFieldAccess(FieldAccessKey<?> key) {
210216 return cw .toByteArray ();
211217 }
212218
213- private static byte [] generateMethodAccess (MethodAccessKey <?> key ) {
219+ private static byte [] generateMethodAccess (MethodAccessKey <?> key ) throws NoSuchMethodException {
214220 Type objectT = Type .getType (Object .class );
215221 Type methodAccessT = Type .getType (MethodAccess .class );
216222 Type sourceT = Type .getType (key .source ());
217- Type returnTypeT = Type .getType (key .returnType ());
218- Class <?> boxedReturnType = TypeUtils .box (key .returnType ());
223+
224+ Executable executable = key .executable ();
225+
226+ Class <?> returnType = key .constructor () ? key .source () : ((java .lang .reflect .Method ) executable ).getReturnType ();
227+ Type returnTypeT = key .constructor () ? sourceT : Type .getType (returnType );
228+ Class <?> boxedReturnType = TypeUtils .box (returnType );
219229 Type boxedReturnTypeT = boxedReturnType != null ? Type .getType (boxedReturnType ) : returnTypeT ;
220230 Type [] parametersT = Arrays .stream (key .parameters ()).map (Type ::getType ).toArray (Type []::new );
221231
@@ -242,7 +252,7 @@ private static byte[] generateMethodAccess(MethodAccessKey<?> key) {
242252 ga .dup ();
243253 loadArgs (ga , key .parameters ());
244254 ga .invokeConstructor (sourceT , new Method (key .name (), Type .VOID_TYPE , parametersT ));
245- } else if (key .isStatic ()) {
255+ } else if (Modifier .isStatic (executable . getModifiers () )) {
246256 loadArgs (ga , key .parameters ());
247257 ga .invokeStatic (sourceT , new Method (key .name (), returnTypeT , parametersT ));
248258
@@ -328,46 +338,35 @@ public interface MethodAccess<S, T> {
328338 T invoke (S source , Object ... args );
329339 }
330340
331- private record FieldAccessKey <S >(Class <? extends S > source , String name , Class <?> type , boolean constant , boolean isStatic ) {
341+ private record FieldAccessKey <S >(Class <? extends S > source , String name ) {
332342
333- public String readable () {
334- return (isStatic ? "static " : "" ) + type .getName () + " " + source .getName () + "." + name ;
335- }
336-
337- public static <S > FieldAccessKey <S > of (Class <? extends S > source , String name ) throws NoSuchFieldException {
338- Field field = source .getDeclaredField (name );
339- int modifiers = field .getModifiers ();
340- return new FieldAccessKey <>(source , name , field .getType (), (modifiers & Modifier .FINAL ) != 0 , (modifiers & Modifier .STATIC ) != 0 );
343+ public Field field () throws NoSuchFieldException {
344+ return source .getDeclaredField (name );
341345 }
342346
343347 }
344348
345- private record MethodAccessKey <S >(Class <? extends S > source , String name , Class <?> returnType , Class <?>[] parameters , boolean isStatic ) {
346-
347- public static <S > MethodAccessKey <S > of (Class <? extends S > source , String name , Class <?>[] parameters ) throws NoSuchMethodException {
348- if ("<init>" .equals (name )) {
349- Constructor <?> constructor = source .getDeclaredConstructor (parameters );
350- return new MethodAccessKey <>(source , name , source , constructor .getParameterTypes (), false );
351- }
352- java .lang .reflect .Method method = source .getDeclaredMethod (name , parameters );
353- return new MethodAccessKey <>(source , name , method .getReturnType (), method .getParameterTypes (), (method .getModifiers () & Modifier .STATIC ) != 0 );
354- }
349+ private record MethodAccessKey <S >(Class <? extends S > source , String name , Class <?>[] parameters ) {
355350
356351 public boolean constructor () {
357352 return name .equals ("<init>" );
358353 }
359354
355+ public Executable executable () throws NoSuchMethodException {
356+ return constructor () ? source .getDeclaredConstructor (parameters ) : source .getDeclaredMethod (name , parameters );
357+ }
358+
360359 @ Override
361360 public boolean equals (Object o ) {
362- if (!(o instanceof MethodAccessKey <?>(Class <?> source1 , String name1 , Class <?> type , Class <?> [] parameters1 , boolean aStatic )))
361+ if (!(o instanceof MethodAccessKey <?>(Class <?> source1 , String name1 , Class <?>[] parameters1 )))
363362 return false ;
364363
365- return isStatic == aStatic && name .equals (name1 ) && returnType . equals ( type ) && Arrays .equals (parameters , parameters1 ) && source .equals (source1 );
364+ return name .equals (name1 ) && Arrays .equals (parameters , parameters1 ) && source .equals (source1 );
366365 }
367366
368367 @ Override
369368 public int hashCode () {
370- return Arrays .deepHashCode (new Object []{source , name , returnType , parameters , isStatic });
369+ return Arrays .deepHashCode (new Object []{source , name , parameters });
371370 }
372371
373372 }
0 commit comments