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