001/*
002 * Copyright 2002-2017 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 org.springframework.asm.MethodVisitor;
020import org.springframework.asm.Type;
021import org.springframework.expression.EvaluationException;
022import org.springframework.expression.TypedValue;
023import org.springframework.expression.spel.CodeFlow;
024import org.springframework.expression.spel.ExpressionState;
025import org.springframework.expression.spel.SpelEvaluationException;
026import org.springframework.expression.spel.SpelMessage;
027import org.springframework.expression.spel.support.BooleanTypedValue;
028
029/**
030 * The operator 'instanceof' checks if an object is of the class specified in the right
031 * hand operand, in the same way that {@code instanceof} does in Java.
032 *
033 * @author Andy Clement
034 * @since 3.0
035 */
036public class OperatorInstanceof extends Operator {
037
038        private Class<?> type;
039        
040
041        public OperatorInstanceof(int pos, SpelNodeImpl... operands) {
042                super("instanceof", pos, operands);
043        }
044
045
046        /**
047         * Compare the left operand to see it is an instance of the type specified as the
048         * right operand. The right operand must be a class.
049         * @param state the expression state
050         * @return {@code true} if the left operand is an instanceof of the right operand,
051         * otherwise {@code false}
052         * @throws EvaluationException if there is a problem evaluating the expression
053         */
054        @Override
055        public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
056                SpelNodeImpl rightOperand = getRightOperand();
057                TypedValue left = getLeftOperand().getValueInternal(state);
058                TypedValue right = rightOperand.getValueInternal(state);
059                Object leftValue = left.getValue();
060                Object rightValue = right.getValue();
061                BooleanTypedValue result;
062                if (rightValue == null || !(rightValue instanceof Class)) {
063                        throw new SpelEvaluationException(getRightOperand().getStartPosition(),
064                                        SpelMessage.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND,
065                                        (rightValue == null ? "null" : rightValue.getClass().getName()));
066                }
067                Class<?> rightClass = (Class<?>) rightValue;
068                if (leftValue == null) {
069                        result = BooleanTypedValue.FALSE;  // null is not an instanceof anything
070                }
071                else {
072                        result = BooleanTypedValue.forValue(rightClass.isAssignableFrom(leftValue.getClass()));
073                }
074                this.type = rightClass;
075                if (rightOperand instanceof TypeReference) {
076                        // Can only generate bytecode where the right operand is a direct type reference, 
077                        // not if it is indirect (for example when right operand is a variable reference)
078                        this.exitTypeDescriptor = "Z";
079                }
080                return result;
081        }
082
083        @Override
084        public boolean isCompilable() {
085                return (this.exitTypeDescriptor != null && getLeftOperand().isCompilable());
086        }
087        
088        @Override
089        public void generateCode(MethodVisitor mv, CodeFlow cf) {
090                getLeftOperand().generateCode(mv, cf);
091                CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor());
092                if (this.type.isPrimitive()) {
093                        // always false - but left operand code always driven
094                        // in case it had side effects
095                        mv.visitInsn(POP);
096                        mv.visitInsn(ICONST_0); // value of false
097                } 
098                else {
099                        mv.visitTypeInsn(INSTANCEOF, Type.getInternalName(this.type));
100                }
101                cf.pushDescriptor(this.exitTypeDescriptor);
102        }
103
104}