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