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}