001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.expression.spel.ast; 018 019import java.lang.reflect.Modifier; 020 021import org.springframework.asm.MethodVisitor; 022import org.springframework.expression.EvaluationContext; 023import org.springframework.expression.TypedValue; 024import org.springframework.expression.spel.CodeFlow; 025import org.springframework.expression.spel.ExpressionState; 026import org.springframework.expression.spel.SpelEvaluationException; 027 028/** 029 * Represents a variable reference, eg. #someVar. Note this is different to a *local* 030 * variable like $someVar 031 * 032 * @author Andy Clement 033 * @since 3.0 034 */ 035public class VariableReference extends SpelNodeImpl { 036 037 // Well known variables: 038 private static final String THIS = "this"; // currently active context object 039 040 private static final String ROOT = "root"; // root context object 041 042 043 private final String name; 044 045 046 public VariableReference(String variableName, int pos) { 047 super(pos); 048 this.name = variableName; 049 } 050 051 052 @Override 053 public ValueRef getValueRef(ExpressionState state) throws SpelEvaluationException { 054 if (this.name.equals(THIS)) { 055 return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(),this); 056 } 057 if (this.name.equals(ROOT)) { 058 return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(),this); 059 } 060 TypedValue result = state.lookupVariable(this.name); 061 // a null value will mean either the value was null or the variable was not found 062 return new VariableRef(this.name,result,state.getEvaluationContext()); 063 } 064 065 @Override 066 public TypedValue getValueInternal(ExpressionState state) throws SpelEvaluationException { 067 if (this.name.equals(THIS)) { 068 return state.getActiveContextObject(); 069 } 070 if (this.name.equals(ROOT)) { 071 TypedValue result = state.getRootContextObject(); 072 this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(result.getValue()); 073 return result; 074 } 075 TypedValue result = state.lookupVariable(this.name); 076 Object value = result.getValue(); 077 if (value == null || !Modifier.isPublic(value.getClass().getModifiers())) { 078 // If the type is not public then when generateCode produces a checkcast to it 079 // then an IllegalAccessError will occur. 080 // If resorting to Object isn't sufficient, the hierarchy could be traversed for 081 // the first public type. 082 this.exitTypeDescriptor = "Ljava/lang/Object"; 083 } 084 else { 085 this.exitTypeDescriptor = CodeFlow.toDescriptorFromObject(value); 086 } 087 // a null value will mean either the value was null or the variable was not found 088 return result; 089 } 090 091 @Override 092 public void setValue(ExpressionState state, Object value) throws SpelEvaluationException { 093 state.setVariable(this.name, value); 094 } 095 096 @Override 097 public String toStringAST() { 098 return "#" + this.name; 099 } 100 101 @Override 102 public boolean isWritable(ExpressionState expressionState) throws SpelEvaluationException { 103 return !(this.name.equals(THIS) || this.name.equals(ROOT)); 104 } 105 106 107 class VariableRef implements ValueRef { 108 109 private final String name; 110 111 private final TypedValue value; 112 113 private final EvaluationContext evaluationContext; 114 115 116 public VariableRef(String name, TypedValue value, 117 EvaluationContext evaluationContext) { 118 this.name = name; 119 this.value = value; 120 this.evaluationContext = evaluationContext; 121 } 122 123 124 @Override 125 public TypedValue getValue() { 126 return this.value; 127 } 128 129 @Override 130 public void setValue(Object newValue) { 131 this.evaluationContext.setVariable(this.name, newValue); 132 } 133 134 @Override 135 public boolean isWritable() { 136 return true; 137 } 138 } 139 140 @Override 141 public boolean isCompilable() { 142 return this.exitTypeDescriptor!=null; 143 } 144 145 @Override 146 public void generateCode(MethodVisitor mv, CodeFlow cf) { 147 if (this.name.equals(ROOT)) { 148 mv.visitVarInsn(ALOAD,1); 149 } 150 else { 151 mv.visitVarInsn(ALOAD, 2); 152 mv.visitLdcInsn(name); 153 mv.visitMethodInsn(INVOKEINTERFACE, "org/springframework/expression/EvaluationContext", "lookupVariable", "(Ljava/lang/String;)Ljava/lang/Object;",true); 154 } 155 CodeFlow.insertCheckCast(mv,this.exitTypeDescriptor); 156 cf.pushDescriptor(this.exitTypeDescriptor); 157 } 158 159 160}