001/*
002 * Copyright 2002-2018 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.Constructor;
020import java.lang.reflect.Member;
021import java.lang.reflect.Method;
022
023import org.springframework.asm.MethodVisitor;
024import org.springframework.asm.Opcodes;
025import org.springframework.expression.EvaluationException;
026import org.springframework.expression.TypedValue;
027import org.springframework.expression.common.ExpressionUtils;
028import org.springframework.expression.spel.CodeFlow;
029import org.springframework.expression.spel.ExpressionState;
030import org.springframework.expression.spel.SpelEvaluationException;
031import org.springframework.expression.spel.SpelMessage;
032import org.springframework.expression.spel.SpelNode;
033import org.springframework.expression.spel.support.StandardEvaluationContext;
034import org.springframework.util.Assert;
035import org.springframework.util.ObjectUtils;
036
037/**
038 * The common supertype of all AST nodes in a parsed Spring Expression Language
039 * format expression.
040 *
041 * @author Andy Clement
042 * @since 3.0
043 */
044public abstract class SpelNodeImpl implements SpelNode, Opcodes {
045
046        private static final SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
047
048
049        protected int pos; // start = top 16bits, end = bottom 16bits
050
051        protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
052
053        private SpelNodeImpl parent;
054
055        /**
056         * Indicates the type descriptor for the result of this expression node.
057         * This is set as soon as it is known. For a literal node it is known immediately.
058         * For a property access or method invocation it is known after one evaluation of
059         * that node.
060         * <p>The descriptor is like the bytecode form but is slightly easier to work with.
061         * It does not include the trailing semicolon (for non array reference types).
062         * Some examples: Ljava/lang/String, I, [I
063     */
064        protected volatile String exitTypeDescriptor;
065
066
067        public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
068                this.pos = pos;
069                if (!ObjectUtils.isEmpty(operands)) {
070                        this.children = operands;
071                        for (SpelNodeImpl operand : operands) {
072                                operand.parent = this;
073                        }
074                }
075        }
076
077
078        @Deprecated
079        protected SpelNodeImpl getPreviousChild() {
080                SpelNodeImpl result = null;
081                if (this.parent != null) {
082                        for (SpelNodeImpl child : this.parent.children) {
083                                if (this == child) {
084                                        break;
085                                }
086                                result = child;
087                        }
088                }
089                return result;
090        }
091
092        /**
093     * @return true if the next child is one of the specified classes
094     */
095        protected boolean nextChildIs(Class<?>... clazzes) {
096                if (this.parent != null) {
097                        SpelNodeImpl[] peers = this.parent.children;
098                        for (int i = 0, max = peers.length; i < max; i++) {
099                                if (this == peers[i]) {
100                                        if (i + 1 >= max) {
101                                                return false;
102                                        }
103                                        Class<?> clazz = peers[i + 1].getClass();
104                                        for (Class<?> desiredClazz : clazzes) {
105                                                if (clazz.equals(desiredClazz)) {
106                                                        return true;
107                                                }
108                                        }
109                                        return false;
110                                }
111                        }
112                }
113                return false;
114        }
115
116        @Override
117        public final Object getValue(ExpressionState expressionState) throws EvaluationException {
118                if (expressionState != null) {
119                        return getValueInternal(expressionState).getValue();
120                }
121                else {
122                        // configuration not set - does that matter?
123                        return getValue(new ExpressionState(new StandardEvaluationContext()));
124                }
125        }
126
127        @Override
128        public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
129                if (expressionState != null) {
130                        return getValueInternal(expressionState);
131                }
132                else {
133                        // configuration not set - does that matter?
134                        return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
135                }
136        }
137
138        // by default Ast nodes are not writable
139        @Override
140        public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
141                return false;
142        }
143
144        @Override
145        public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
146                throw new SpelEvaluationException(getStartPosition(),
147                                SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
148        }
149
150        @Override
151        public SpelNode getChild(int index) {
152                return this.children[index];
153        }
154
155        @Override
156        public int getChildCount() {
157                return this.children.length;
158        }
159
160        @Override
161        public Class<?> getObjectClass(Object obj) {
162                if (obj == null) {
163                        return null;
164                }
165                return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
166        }
167
168        protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
169                return ExpressionUtils.convertTypedValue(state.getEvaluationContext(), getValueInternal(state), desiredReturnType);
170        }
171
172        @Override
173        public int getStartPosition() {
174                return (this.pos >> 16);
175        }
176
177        @Override
178        public int getEndPosition() {
179                return (this.pos & 0xffff);
180        }
181
182        protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
183                throw new SpelEvaluationException(this.pos, SpelMessage.NOT_ASSIGNABLE, toStringAST());
184        }
185
186        /**
187         * Check whether a node can be compiled to bytecode. The reasoning in each node may
188         * be different but will typically involve checking whether the exit type descriptor
189         * of the node is known and any relevant child nodes are compilable.
190         * @return {@code true} if this node can be compiled to bytecode
191         */
192        public boolean isCompilable() {
193                return false;
194        }
195
196        /**
197         * Generate the bytecode for this node into the supplied visitor. Context info about
198         * the current expression being compiled is available in the codeflow object. For
199         * example it will include information about the type of the object currently
200         * on the stack.
201         * @param mv the ASM MethodVisitor into which code should be generated
202         * @param cf a context object with info about what is on the stack
203         */
204        public void generateCode(MethodVisitor mv, CodeFlow cf) {
205                throw new IllegalStateException(getClass().getName() +" has no generateCode(..) method");
206        }
207
208        public String getExitDescriptor() {
209                return this.exitTypeDescriptor;
210        }
211
212        public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
213
214        
215        /**
216         * Generate code that handles building the argument values for the specified method.
217         * This method will take account of whether the invoked method is a varargs method
218         * and if it is then the argument values will be appropriately packaged into an array.
219         * @param mv the method visitor where code should be generated
220         * @param cf the current codeflow
221         * @param member the method or constructor for which arguments are being setup
222         * @param arguments the expression nodes for the expression supplied argument values
223         */
224        protected static void generateCodeForArguments(MethodVisitor mv, CodeFlow cf, Member member, SpelNodeImpl[] arguments) {
225                String[] paramDescriptors = null;
226                boolean isVarargs = false;
227                if (member instanceof Constructor) {
228                        Constructor<?> ctor = (Constructor<?>) member;
229                        paramDescriptors = CodeFlow.toDescriptors(ctor.getParameterTypes());
230                        isVarargs = ctor.isVarArgs();
231                }
232                else { // Method
233                        Method method = (Method)member;
234                        paramDescriptors = CodeFlow.toDescriptors(method.getParameterTypes());
235                        isVarargs = method.isVarArgs();
236                }
237                if (isVarargs) {
238                        // The final parameter may or may not need packaging into an array, or nothing may
239                        // have been passed to satisfy the varargs and so something needs to be built.
240                        int p = 0; // Current supplied argument being processed
241                        int childCount = arguments.length;
242                                                
243                        // Fulfill all the parameter requirements except the last one
244                        for (p = 0; p < paramDescriptors.length - 1; p++) {
245                                generateCodeForArgument(mv, cf, arguments[p], paramDescriptors[p]);
246                        }
247                        
248                        SpelNodeImpl lastChild = (childCount == 0 ? null : arguments[childCount - 1]);
249                        String arrayType = paramDescriptors[paramDescriptors.length - 1];
250                        // Determine if the final passed argument is already suitably packaged in array
251                        // form to be passed to the method
252                        if (lastChild != null && arrayType.equals(lastChild.getExitDescriptor())) {
253                                generateCodeForArgument(mv, cf, lastChild, paramDescriptors[p]);
254                        }
255                        else {
256                                arrayType = arrayType.substring(1); // trim the leading '[', may leave other '['
257                                // build array big enough to hold remaining arguments
258                                CodeFlow.insertNewArrayCode(mv, childCount - p, arrayType);
259                                // Package up the remaining arguments into the array
260                                int arrayindex = 0;
261                                while (p < childCount) {
262                                        SpelNodeImpl child = arguments[p];
263                                        mv.visitInsn(DUP);
264                                        CodeFlow.insertOptimalLoad(mv, arrayindex++);
265                                        generateCodeForArgument(mv, cf, child, arrayType);
266                                        CodeFlow.insertArrayStore(mv, arrayType);
267                                        p++;
268                                }
269                        }
270                }
271                else {
272                        for (int i = 0; i < paramDescriptors.length;i++) {
273                                generateCodeForArgument(mv, cf, arguments[i], paramDescriptors[i]);
274                        }
275                }
276        }
277
278        /**
279         * Ask an argument to generate its bytecode and then follow it up
280         * with any boxing/unboxing/checkcasting to ensure it matches the expected parameter descriptor.
281         */
282        protected static void generateCodeForArgument(MethodVisitor mv, CodeFlow cf, SpelNodeImpl argument, String paramDesc) {
283                cf.enterCompilationScope();
284                argument.generateCode(mv, cf);
285                String lastDesc = cf.lastDescriptor();
286                boolean primitiveOnStack = CodeFlow.isPrimitive(lastDesc);
287                // Check if need to box it for the method reference?
288                if (primitiveOnStack && paramDesc.charAt(0) == 'L') {
289                        CodeFlow.insertBoxIfNecessary(mv, lastDesc.charAt(0));
290                }
291                else if (paramDesc.length() == 1 && !primitiveOnStack) {
292                        CodeFlow.insertUnboxInsns(mv, paramDesc.charAt(0), lastDesc);
293                }
294                else if (!paramDesc.equals(lastDesc)) {
295                        // This would be unnecessary in the case of subtyping (e.g. method takes Number but Integer passed in)
296                        CodeFlow.insertCheckCast(mv, paramDesc);
297                }
298                cf.exitCompilationScope();
299        }
300}