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