Fine-tuned method/field access checks

This commit is contained in:
Juergen Hoeller 2015-12-13 01:10:01 +01:00
parent a28fc760ba
commit 04f765506e
3 changed files with 69 additions and 72 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -248,6 +248,7 @@ public class Indexer extends SpelNodeImpl {
cf.exitCompilationScope(); cf.exitCompilationScope();
mv.visitInsn(insn); mv.visitInsn(insn);
} }
else if (this.indexedType == IndexedType.LIST) { else if (this.indexedType == IndexedType.LIST) {
mv.visitTypeInsn(CHECKCAST, "java/util/List"); mv.visitTypeInsn(CHECKCAST, "java/util/List");
cf.enterCompilationScope(); cf.enterCompilationScope();
@ -255,6 +256,7 @@ public class Indexer extends SpelNodeImpl {
cf.exitCompilationScope(); cf.exitCompilationScope();
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
} }
else if (this.indexedType == IndexedType.MAP) { else if (this.indexedType == IndexedType.MAP) {
mv.visitTypeInsn(CHECKCAST, "java/util/Map"); mv.visitTypeInsn(CHECKCAST, "java/util/Map");
// Special case when the key is an unquoted string literal that will be parsed as // Special case when the key is an unquoted string literal that will be parsed as
@ -271,27 +273,30 @@ public class Indexer extends SpelNodeImpl {
} }
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
} }
else if (this.indexedType == IndexedType.OBJECT) { else if (this.indexedType == IndexedType.OBJECT) {
ReflectivePropertyAccessor.OptimalPropertyAccessor accessor = ReflectivePropertyAccessor.OptimalPropertyAccessor accessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) this.cachedReadAccessor; (ReflectivePropertyAccessor.OptimalPropertyAccessor) this.cachedReadAccessor;
Member member = accessor.member; Member member = accessor.member;
boolean isStatic = Modifier.isStatic(member.getModifiers()); boolean isStatic = Modifier.isStatic(member.getModifiers());
String memberDeclaringClassSlashedDescriptor = member.getDeclaringClass().getName().replace('.', '/'); String classDesc = member.getDeclaringClass().getName().replace('.', '/');
if (!isStatic) { if (!isStatic) {
if (descriptor == null) { if (descriptor == null) {
cf.loadTarget(mv); cf.loadTarget(mv);
} }
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { if (descriptor == null || !classDesc.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); mv.visitTypeInsn(CHECKCAST, classDesc);
} }
} }
if (member instanceof Field) {
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor, if (member instanceof Method) {
member.getName(), CodeFlow.toJvmDescriptor(((Field) member).getType())); mv.visitMethodInsn((isStatic? INVOKESTATIC : INVOKEVIRTUAL), classDesc, member.getName(),
CodeFlow.createSignatureDescriptor((Method) member), false);
} }
else { else {
mv.visitMethodInsn(isStatic? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, member.getName(),
member.getName(), CodeFlow.createSignatureDescriptor((Method) member), false); CodeFlow.toJvmDescriptor(((Field) member).getType()));
} }
} }
@ -555,12 +560,8 @@ public class Indexer extends SpelNodeImpl {
ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor = ReflectivePropertyAccessor.OptimalPropertyAccessor optimalAccessor =
(ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor; (ReflectivePropertyAccessor.OptimalPropertyAccessor) accessor;
Member member = optimalAccessor.member; Member member = optimalAccessor.member;
if (member instanceof Field) { Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(member instanceof Method ?
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(((Field)member).getType()); ((Method) member).getReturnType() : ((Field) member).getType());
}
else {
Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(((Method)member).getReturnType());
}
} }
return accessor.read(this.evaluationContext, this.targetObject, this.name); return accessor.read(this.evaluationContext, this.targetObject, this.name);
} }

View File

@ -287,7 +287,7 @@ public class MethodReference extends SpelNodeImpl {
throw new IllegalStateException("No applicable cached executor found: " + executorToCheck); throw new IllegalStateException("No applicable cached executor found: " + executorToCheck);
} }
ReflectiveMethodExecutor methodExecutor = (ReflectiveMethodExecutor)executorToCheck.get(); ReflectiveMethodExecutor methodExecutor = (ReflectiveMethodExecutor) executorToCheck.get();
Method method = methodExecutor.getMethod(); Method method = methodExecutor.getMethod();
boolean isStaticMethod = Modifier.isStatic(method.getModifiers()); boolean isStaticMethod = Modifier.isStatic(method.getModifiers());
String descriptor = cf.lastDescriptor(); String descriptor = cf.lastDescriptor();
@ -297,7 +297,8 @@ public class MethodReference extends SpelNodeImpl {
// Nothing on the stack but something is needed // Nothing on the stack but something is needed
cf.loadTarget(mv); cf.loadTarget(mv);
} }
} else { }
else {
if (isStaticMethod) { if (isStaticMethod) {
// Something on the stack when nothing is needed // Something on the stack when nothing is needed
mv.visitInsn(POP); mv.visitInsn(POP);
@ -308,22 +309,18 @@ public class MethodReference extends SpelNodeImpl {
CodeFlow.insertBoxIfNecessary(mv, descriptor.charAt(0)); CodeFlow.insertBoxIfNecessary(mv, descriptor.charAt(0));
} }
boolean itf = method.getDeclaringClass().isInterface(); String classDesc = (Modifier.isPublic(method.getDeclaringClass().getModifiers()) ?
String methodDeclaringClassSlashedDescriptor = null; method.getDeclaringClass().getName().replace('.', '/') :
if (Modifier.isPublic(method.getDeclaringClass().getModifiers())) { methodExecutor.getPublicDeclaringClass().getName().replace('.', '/'));
methodDeclaringClassSlashedDescriptor = method.getDeclaringClass().getName().replace('.', '/');
}
else {
methodDeclaringClassSlashedDescriptor = methodExecutor.getPublicDeclaringClass().getName().replace('.', '/');
}
if (!isStaticMethod) { if (!isStaticMethod) {
if (descriptor == null || !descriptor.substring(1).equals(methodDeclaringClassSlashedDescriptor)) { if (descriptor == null || !descriptor.substring(1).equals(classDesc)) {
CodeFlow.insertCheckCast(mv, "L"+ methodDeclaringClassSlashedDescriptor); CodeFlow.insertCheckCast(mv, "L" + classDesc);
} }
} }
generateCodeForArguments(mv, cf, method, children);
mv.visitMethodInsn(isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL, generateCodeForArguments(mv, cf, method, this.children);
methodDeclaringClassSlashedDescriptor, method.getName(), CodeFlow.createSignatureDescriptor(method), itf); mv.visitMethodInsn((isStaticMethod ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, method.getName(),
CodeFlow.createSignatureDescriptor(method), method.getDeclaringClass().isInterface());
cf.pushDescriptor(this.exitTypeDescriptor); cf.pushDescriptor(this.exitTypeDescriptor);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -165,7 +165,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return new TypedValue(value, invoker.typeDescriptor.narrow(value)); return new TypedValue(value, invoker.typeDescriptor.narrow(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access property '" + name + "' through getter", ex); throw new AccessException("Unable to access property '" + name + "' through getter method", ex);
} }
} }
} }
@ -187,12 +187,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return new TypedValue(value, invoker.typeDescriptor.narrow(value)); return new TypedValue(value, invoker.typeDescriptor.narrow(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access field: " + name, ex); throw new AccessException("Unable to access field '" + name + "'", ex);
} }
} }
} }
throw new AccessException("Neither getter nor field found for property '" + name + "'"); throw new AccessException("Neither getter method nor field found for property '" + name + "'");
} }
@Override @Override
@ -240,7 +240,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
newValue, TypeDescriptor.forObject(newValue), typeDescriptor); newValue, TypeDescriptor.forObject(newValue), typeDescriptor);
} }
catch (EvaluationException evaluationException) { catch (EvaluationException evaluationException) {
throw new AccessException("Type conversion failure",evaluationException); throw new AccessException("Type conversion failure", evaluationException);
} }
} }
CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); CacheKey cacheKey = new CacheKey(type, name, target instanceof Class);
@ -262,7 +262,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return; return;
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access property '" + name + "' through setter", ex); throw new AccessException("Unable to access property '" + name + "' through setter method", ex);
} }
} }
} }
@ -283,12 +283,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return; return;
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access field: " + name, ex); throw new AccessException("Unable to access field '" + name + "'", ex);
} }
} }
} }
throw new AccessException("Neither setter nor field found for property '" + name + "'"); throw new AccessException("Neither setter method nor field found for property '" + name + "'");
} }
private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) { private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
@ -469,11 +469,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
InvokerPair invocationTarget = this.readerCache.get(cacheKey); InvokerPair invocationTarget = this.readerCache.get(cacheKey);
if (invocationTarget == null || invocationTarget.member instanceof Method) { if (invocationTarget == null || invocationTarget.member instanceof Method) {
Method method = (Method) (invocationTarget==null?null:invocationTarget.member); Method method = (Method) (invocationTarget != null ? invocationTarget.member : null);
if (method == null) { if (method == null) {
method = findGetterForProperty(name, type, target); method = findGetterForProperty(name, type, target);
if (method != null) { if (method != null) {
invocationTarget = new InvokerPair(method,new TypeDescriptor(new MethodParameter(method,-1))); invocationTarget = new InvokerPair(method, new TypeDescriptor(new MethodParameter(method, -1)));
ReflectionUtils.makeAccessible(method); ReflectionUtils.makeAccessible(method);
this.readerCache.put(cacheKey, invocationTarget); this.readerCache.put(cacheKey, invocationTarget);
} }
@ -497,6 +497,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return new OptimalPropertyAccessor(invocationTarget); return new OptimalPropertyAccessor(invocationTarget);
} }
} }
return this; return this;
} }
@ -577,16 +578,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
OptimalPropertyAccessor(InvokerPair target) { OptimalPropertyAccessor(InvokerPair target) {
this.member = target.member; this.member = target.member;
this.typeDescriptor = target.typeDescriptor; this.typeDescriptor = target.typeDescriptor;
if (this.member instanceof Field) { this.needsToBeMadeAccessible = (!Modifier.isPublic(this.member.getModifiers()) ||
Field field = (Field) this.member; !Modifier.isPublic(this.member.getDeclaringClass().getModifiers()));
this.needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) ||
!Modifier.isPublic(field.getDeclaringClass().getModifiers())) && !field.isAccessible();
}
else {
Method method = (Method) this.member;
this.needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible());
}
} }
@Override @Override
@ -599,10 +592,12 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (target == null) { if (target == null) {
return false; return false;
} }
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass()); Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
if (type.isArray()) { if (type.isArray()) {
return false; return false;
} }
if (this.member instanceof Method) { if (this.member instanceof Method) {
Method method = (Method) this.member; Method method = (Method) this.member;
String getterName = "get" + StringUtils.capitalize(name); String getterName = "get" + StringUtils.capitalize(name);
@ -621,30 +616,31 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
@Override @Override
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (this.member instanceof Method) { if (this.member instanceof Method) {
Method method = (Method) this.member;
try { try {
if (this.needsToBeMadeAccessible) { if (this.needsToBeMadeAccessible && !method.isAccessible()) {
ReflectionUtils.makeAccessible((Method) this.member); method.setAccessible(true);
} }
Object value = ((Method) this.member).invoke(target); Object value = method.invoke(target);
return new TypedValue(value, this.typeDescriptor.narrow(value)); return new TypedValue(value, this.typeDescriptor.narrow(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access property '" + name + "' through getter", ex); throw new AccessException("Unable to access property '" + name + "' through getter method", ex);
} }
} }
if (this.member instanceof Field) { else {
Field field = (Field) this.member;
try { try {
if (this.needsToBeMadeAccessible) { if (this.needsToBeMadeAccessible && !field.isAccessible()) {
ReflectionUtils.makeAccessible((Field) this.member); field.setAccessible(true);
} }
Object value = ((Field) this.member).get(target); Object value = field.get(target);
return new TypedValue(value, this.typeDescriptor.narrow(value)); return new TypedValue(value, this.typeDescriptor.narrow(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access field: " + name, ex); throw new AccessException("Unable to access field '" + name + "'", ex);
} }
} }
throw new AccessException("Neither getter nor field found for property '" + name + "'");
} }
@Override @Override
@ -665,11 +661,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
@Override @Override
public Class<?> getPropertyType() { public Class<?> getPropertyType() {
if (this.member instanceof Field) { if (this.member instanceof Method) {
return ((Field) this.member).getType(); return ((Method) this.member).getReturnType();
} }
else { else {
return ((Method) this.member).getReturnType(); return ((Field) this.member).getType();
} }
} }
@ -677,28 +673,31 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) { public void generateCode(String propertyName, MethodVisitor mv, CodeFlow cf) {
boolean isStatic = Modifier.isStatic(this.member.getModifiers()); boolean isStatic = Modifier.isStatic(this.member.getModifiers());
String descriptor = cf.lastDescriptor(); String descriptor = cf.lastDescriptor();
String memberDeclaringClassSlashedDescriptor = this.member.getDeclaringClass().getName().replace('.', '/'); String classDesc = this.member.getDeclaringClass().getName().replace('.', '/');
if (!isStatic) { if (!isStatic) {
if (descriptor == null) { if (descriptor == null) {
cf.loadTarget(mv); cf.loadTarget(mv);
} }
if (descriptor == null || !memberDeclaringClassSlashedDescriptor.equals(descriptor.substring(1))) { if (descriptor == null || !classDesc.equals(descriptor.substring(1))) {
mv.visitTypeInsn(CHECKCAST, memberDeclaringClassSlashedDescriptor); mv.visitTypeInsn(CHECKCAST, classDesc);
} }
} else { }
else {
if (descriptor != null) { if (descriptor != null) {
// A static field/method call will not consume what is on the stack, // A static field/method call will not consume what is on the stack,
// it needs to be popped off. // it needs to be popped off.
mv.visitInsn(POP); mv.visitInsn(POP);
} }
} }
if (this.member instanceof Field) {
mv.visitFieldInsn(isStatic ? GETSTATIC : GETFIELD, memberDeclaringClassSlashedDescriptor, if (this.member instanceof Method) {
this.member.getName(), CodeFlow.toJvmDescriptor(((Field) this.member).getType())); mv.visitMethodInsn((isStatic ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, this.member.getName(),
CodeFlow.createSignatureDescriptor((Method) this.member), false);
} }
else { else {
mv.visitMethodInsn(isStatic ? INVOKESTATIC : INVOKEVIRTUAL, memberDeclaringClassSlashedDescriptor, mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, this.member.getName(),
this.member.getName(), CodeFlow.createSignatureDescriptor((Method) this.member),false); CodeFlow.toJvmDescriptor(((Field) this.member).getType()));
} }
} }
} }