001/*
002 * Copyright 2002-2018 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.expression.EvaluationContext;
021import org.springframework.expression.EvaluationException;
022import org.springframework.expression.spel.CodeFlow;
023import org.springframework.expression.spel.ExpressionState;
024import org.springframework.expression.spel.support.BooleanTypedValue;
025
026/**
027 * Implements the equality operator.
028 *
029 * @author Andy Clement
030 * @since 3.0
031 */
032public class OpEQ extends Operator {
033
034        public OpEQ(int pos, SpelNodeImpl... operands) {
035                super("==", pos, operands);
036                this.exitTypeDescriptor = "Z";
037        }
038
039
040        @Override
041        public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
042                Object left = getLeftOperand().getValueInternal(state).getValue();
043                Object right = getRightOperand().getValueInternal(state).getValue();
044                this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
045                this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
046                return BooleanTypedValue.forValue(equalityCheck(state.getEvaluationContext(), left, right));
047        }
048
049        // This check is different to the one in the other numeric operators (OpLt/etc)
050        // because it allows for simple object comparison
051        @Override
052        public boolean isCompilable() {
053                SpelNodeImpl left = getLeftOperand();
054                SpelNodeImpl right = getRightOperand();
055                if (!left.isCompilable() || !right.isCompilable()) {
056                        return false;
057                }
058
059                String leftDesc = left.exitTypeDescriptor;
060                String rightDesc = right.exitTypeDescriptor;
061                DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc,
062                                rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
063                return (!dc.areNumbers || dc.areCompatible);
064        }
065
066        @Override
067        public void generateCode(MethodVisitor mv, CodeFlow cf) {
068                cf.loadEvaluationContext(mv);
069                String leftDesc = getLeftOperand().exitTypeDescriptor;
070                String rightDesc = getRightOperand().exitTypeDescriptor;
071                boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
072                boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
073
074                cf.enterCompilationScope();
075                getLeftOperand().generateCode(mv, cf);
076                cf.exitCompilationScope();
077                if (leftPrim) {
078                        CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
079                }
080                cf.enterCompilationScope();
081                getRightOperand().generateCode(mv, cf);
082                cf.exitCompilationScope();
083                if (rightPrim) {
084                        CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
085                }
086
087                String operatorClassName = Operator.class.getName().replace('.', '/');
088                String evaluationContextClassName = EvaluationContext.class.getName().replace('.', '/');
089                mv.visitMethodInsn(INVOKESTATIC, operatorClassName, "equalityCheck",
090                                "(L" + evaluationContextClassName + ";Ljava/lang/Object;Ljava/lang/Object;)Z", false);
091                cf.pushDescriptor("Z");
092        }
093
094}