@@ -136,8 +136,8 @@ public boolean canRead(EvaluationContext context, @Nullable Object target, Strin
136
136
// The readerCache will only contain gettable properties (let's not worry about setters for now).
137
137
Property property = new Property (type , method , null );
138
138
TypeDescriptor typeDescriptor = new TypeDescriptor (property );
139
- method = ClassUtils .getInterfaceMethodIfPossible (method , type );
140
- this .readerCache .put (cacheKey , new InvokerPair (method , typeDescriptor ));
139
+ Method methodToInvoke = ClassUtils .getInterfaceMethodIfPossible (method , type );
140
+ this .readerCache .put (cacheKey , new InvokerPair (methodToInvoke , typeDescriptor , method ));
141
141
this .typeDescriptorCache .put (cacheKey , typeDescriptor );
142
142
return true ;
143
143
}
@@ -171,22 +171,23 @@ public TypedValue read(EvaluationContext context, @Nullable Object target, Strin
171
171
172
172
if (invoker == null || invoker .member instanceof Method ) {
173
173
Method method = (Method ) (invoker != null ? invoker .member : null );
174
+ Method methodToInvoke = method ;
174
175
if (method == null ) {
175
176
method = findGetterForProperty (name , type , target );
176
177
if (method != null ) {
177
178
// Treat it like a property...
178
179
// The readerCache will only contain gettable properties (let's not worry about setters for now).
179
180
Property property = new Property (type , method , null );
180
181
TypeDescriptor typeDescriptor = new TypeDescriptor (property );
181
- method = ClassUtils .getInterfaceMethodIfPossible (method , type );
182
- invoker = new InvokerPair (method , typeDescriptor );
182
+ methodToInvoke = ClassUtils .getInterfaceMethodIfPossible (method , type );
183
+ invoker = new InvokerPair (methodToInvoke , typeDescriptor , method );
183
184
this .readerCache .put (cacheKey , invoker );
184
185
}
185
186
}
186
- if (method != null ) {
187
+ if (methodToInvoke != null ) {
187
188
try {
188
- ReflectionUtils .makeAccessible (method );
189
- Object value = method .invoke (target );
189
+ ReflectionUtils .makeAccessible (methodToInvoke );
190
+ Object value = methodToInvoke .invoke (target );
190
191
return new TypedValue (value , invoker .typeDescriptor .narrow (value ));
191
192
}
192
193
catch (Exception ex ) {
@@ -532,9 +533,9 @@ public PropertyAccessor createOptimalAccessor(EvaluationContext context, @Nullab
532
533
method = findGetterForProperty (name , type , target );
533
534
if (method != null ) {
534
535
TypeDescriptor typeDescriptor = new TypeDescriptor (new MethodParameter (method , -1 ));
535
- method = ClassUtils .getInterfaceMethodIfPossible (method , type );
536
- invokerPair = new InvokerPair (method , typeDescriptor );
537
- ReflectionUtils .makeAccessible (method );
536
+ Method methodToInvoke = ClassUtils .getInterfaceMethodIfPossible (method , type );
537
+ invokerPair = new InvokerPair (methodToInvoke , typeDescriptor , method );
538
+ ReflectionUtils .makeAccessible (methodToInvoke );
538
539
this .readerCache .put (cacheKey , invokerPair );
539
540
}
540
541
}
@@ -572,8 +573,14 @@ private static boolean isKotlinProperty(Method method, String methodSuffix) {
572
573
/**
573
574
* Captures the member (method/field) to call reflectively to access a property value
574
575
* and the type descriptor for the value returned by the reflective call.
576
+ * <p>The {@code originalMethod} is only used if the member is a method.
575
577
*/
576
- private record InvokerPair (Member member , TypeDescriptor typeDescriptor ) {}
578
+ private record InvokerPair (Member member , TypeDescriptor typeDescriptor , @ Nullable Method originalMethod ) {
579
+
580
+ InvokerPair (Member member , TypeDescriptor typeDescriptor ) {
581
+ this (member , typeDescriptor , null );
582
+ }
583
+ }
577
584
578
585
private record PropertyCacheKey (Class <?> clazz , String property , boolean targetIsClass )
579
586
implements Comparable <PropertyCacheKey > {
@@ -606,9 +613,13 @@ public static class OptimalPropertyAccessor implements CompilablePropertyAccesso
606
613
607
614
private final TypeDescriptor typeDescriptor ;
608
615
616
+ @ Nullable
617
+ private final Method originalMethod ;
618
+
609
619
OptimalPropertyAccessor (InvokerPair invokerPair ) {
610
620
this .member = invokerPair .member ;
611
621
this .typeDescriptor = invokerPair .typeDescriptor ;
622
+ this .originalMethod = invokerPair .originalMethod ;
612
623
}
613
624
614
625
@ Override
@@ -677,8 +688,14 @@ public void write(EvaluationContext context, @Nullable Object target, String nam
677
688
678
689
@ Override
679
690
public boolean isCompilable () {
680
- return (Modifier .isPublic (this .member .getModifiers ()) &&
681
- Modifier .isPublic (this .member .getDeclaringClass ().getModifiers ()));
691
+ if (Modifier .isPublic (this .member .getModifiers ()) &&
692
+ Modifier .isPublic (this .member .getDeclaringClass ().getModifiers ())) {
693
+ return true ;
694
+ }
695
+ if (this .originalMethod != null ) {
696
+ return (ReflectionHelper .findPublicDeclaringClass (this .originalMethod ) != null );
697
+ }
698
+ return false ;
682
699
}
683
700
684
701
@ Override
@@ -693,9 +710,17 @@ public Class<?> getPropertyType() {
693
710
694
711
@ Override
695
712
public void generateCode (String propertyName , MethodVisitor mv , CodeFlow cf ) {
713
+ Class <?> publicDeclaringClass = this .member .getDeclaringClass ();
714
+ if (!Modifier .isPublic (publicDeclaringClass .getModifiers ()) && this .originalMethod != null ) {
715
+ publicDeclaringClass = ReflectionHelper .findPublicDeclaringClass (this .originalMethod );
716
+ }
717
+ Assert .state (publicDeclaringClass != null && Modifier .isPublic (publicDeclaringClass .getModifiers ()),
718
+ () -> "Failed to find public declaring class for: " +
719
+ (this .originalMethod != null ? this .originalMethod : this .member ));
720
+
721
+ String classDesc = publicDeclaringClass .getName ().replace ('.' , '/' );
696
722
boolean isStatic = Modifier .isStatic (this .member .getModifiers ());
697
723
String descriptor = cf .lastDescriptor ();
698
- String classDesc = this .member .getDeclaringClass ().getName ().replace ('.' , '/' );
699
724
700
725
if (!isStatic ) {
701
726
if (descriptor == null ) {
@@ -714,7 +739,7 @@ public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
714
739
}
715
740
716
741
if (this .member instanceof Method method ) {
717
- boolean isInterface = method . getDeclaringClass () .isInterface ();
742
+ boolean isInterface = publicDeclaringClass .isInterface ();
718
743
int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL );
719
744
mv .visitMethodInsn (opcode , classDesc , method .getName (),
720
745
CodeFlow .createSignatureDescriptor (method ), isInterface );
0 commit comments