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.Label; 020import org.springframework.asm.MethodVisitor; 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 * Represents the boolean AND operation. 031 * 032 * @author Andy Clement 033 * @author Mark Fisher 034 * @author Oliver Becker 035 * @since 3.0 036 */ 037public class OpAnd extends Operator { 038 039 public OpAnd(int pos, SpelNodeImpl... operands) { 040 super("and", pos, operands); 041 this.exitTypeDescriptor = "Z"; 042 } 043 044 045 @Override 046 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { 047 if (!getBooleanValue(state, getLeftOperand())) { 048 // no need to evaluate right operand 049 return BooleanTypedValue.FALSE; 050 } 051 return BooleanTypedValue.forValue(getBooleanValue(state, getRightOperand())); 052 } 053 054 private boolean getBooleanValue(ExpressionState state, SpelNodeImpl operand) { 055 try { 056 Boolean value = operand.getValue(state, Boolean.class); 057 assertValueNotNull(value); 058 return value; 059 } 060 catch (SpelEvaluationException ex) { 061 ex.setPosition(operand.getStartPosition()); 062 throw ex; 063 } 064 } 065 066 private void assertValueNotNull(Boolean value) { 067 if (value == null) { 068 throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean"); 069 } 070 } 071 072 @Override 073 public boolean isCompilable() { 074 SpelNodeImpl left = getLeftOperand(); 075 SpelNodeImpl right = getRightOperand(); 076 return (left.isCompilable() && right.isCompilable() && 077 CodeFlow.isBooleanCompatible(left.exitTypeDescriptor) && 078 CodeFlow.isBooleanCompatible(right.exitTypeDescriptor)); 079 } 080 081 @Override 082 public void generateCode(MethodVisitor mv, CodeFlow cf) { 083 // Pseudo: if (!leftOperandValue) { result=false; } else { result=rightOperandValue; } 084 Label elseTarget = new Label(); 085 Label endOfIf = new Label(); 086 cf.enterCompilationScope(); 087 getLeftOperand().generateCode(mv, cf); 088 cf.unboxBooleanIfNecessary(mv); 089 cf.exitCompilationScope(); 090 mv.visitJumpInsn(IFNE, elseTarget); 091 mv.visitLdcInsn(0); // FALSE 092 mv.visitJumpInsn(GOTO,endOfIf); 093 mv.visitLabel(elseTarget); 094 cf.enterCompilationScope(); 095 getRightOperand().generateCode(mv, cf); 096 cf.unboxBooleanIfNecessary(mv); 097 cf.exitCompilationScope(); 098 mv.visitLabel(endOfIf); 099 cf.pushDescriptor(this.exitTypeDescriptor); 100 } 101 102}