View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.expression.spel.ast;
18  
19  import org.springframework.asm.Label;
20  import org.springframework.asm.MethodVisitor;
21  import org.springframework.expression.EvaluationException;
22  import org.springframework.expression.spel.CodeFlow;
23  import org.springframework.expression.spel.ExpressionState;
24  import org.springframework.expression.spel.support.BooleanTypedValue;
25  
26  /**
27   * Implements the equality operator.
28   *
29   * @author Andy Clement
30   * @since 3.0
31   */
32  public class OpEQ extends Operator {
33  
34  	public OpEQ(int pos, SpelNodeImpl... operands) {
35  		super("==", pos, operands);
36  		this.exitTypeDescriptor = "Z";
37  	}
38  
39  
40  	@Override
41  	public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
42  		Object left = getLeftOperand().getValueInternal(state).getValue();
43  		Object right = getRightOperand().getValueInternal(state).getValue();
44  		this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left);
45  		this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right);
46  		return BooleanTypedValue.forValue(equalityCheck(state, left, right));
47  	}
48  	
49  	// This check is different to the one in the other numeric operators (OpLt/etc)
50  	// because it allows for simple object comparison
51  	@Override
52  	public boolean isCompilable() {
53  		SpelNodeImpl left = getLeftOperand();
54  		SpelNodeImpl right= getRightOperand();
55  		if (!left.isCompilable() || !right.isCompilable()) {
56  			return false;
57  		}
58  
59  		String leftDesc = left.exitTypeDescriptor;
60  		String rightDesc = right.exitTypeDescriptor;
61  		DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
62  				this.leftActualDescriptor, this.rightActualDescriptor);
63  		return (!dc.areNumbers || dc.areCompatible);
64  	}
65  	
66  	
67  	@Override
68  	public void generateCode(MethodVisitor mv, CodeFlow cf) {
69  		String leftDesc = getLeftOperand().exitTypeDescriptor;
70  		String rightDesc = getRightOperand().exitTypeDescriptor;
71  		Label elseTarget = new Label();
72  		Label endOfIf = new Label();
73  		boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
74  		boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
75  
76  		DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc,
77  				this.leftActualDescriptor, this.rightActualDescriptor);
78  		
79  		if (dc.areNumbers && dc.areCompatible) {
80  			char targetType = dc.compatibleType;
81  			
82  			getLeftOperand().generateCode(mv, cf);
83  			if (!leftPrim) {
84  				CodeFlow.insertUnboxInsns(mv, targetType, leftDesc);
85  			}
86  		
87  			cf.enterCompilationScope();
88  			getRightOperand().generateCode(mv, cf);
89  			cf.exitCompilationScope();
90  			if (!rightPrim) {
91  				CodeFlow.insertUnboxInsns(mv, targetType, rightDesc);
92  			}
93  			// assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc)
94  			if (targetType=='D') {
95  				mv.visitInsn(DCMPL);
96  				mv.visitJumpInsn(IFNE, elseTarget);
97  			}
98  			else if (targetType=='F') {
99  				mv.visitInsn(FCMPL);		
100 				mv.visitJumpInsn(IFNE, elseTarget);
101 			}
102 			else if (targetType=='J') {
103 				mv.visitInsn(LCMP);		
104 				mv.visitJumpInsn(IFNE, elseTarget);
105 			}
106 			else if (targetType=='I' || targetType=='Z') {
107 				mv.visitJumpInsn(IF_ICMPNE, elseTarget);		
108 			}
109 			else {
110 				throw new IllegalStateException("Unexpected descriptor "+leftDesc);
111 			}
112 		}
113 		else {
114 			getLeftOperand().generateCode(mv, cf);
115 			if (leftPrim) {
116 				CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
117 			}
118 			getRightOperand().generateCode(mv, cf);
119 			if (rightPrim) {
120 				CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
121 			}
122 			Label leftNotNull = new Label();
123 			mv.visitInsn(DUP_X1); // Dup right on the top of the stack
124 			mv.visitJumpInsn(IFNONNULL,leftNotNull);
125 			// Right is null!
126 			mv.visitInsn(SWAP);
127 			mv.visitInsn(POP); // remove it
128 			Label rightNotNull = new Label();
129 			mv.visitJumpInsn(IFNONNULL, rightNotNull);
130 			// Left is null too
131 			mv.visitInsn(ICONST_1);
132 			mv.visitJumpInsn(GOTO, endOfIf);
133 			mv.visitLabel(rightNotNull);
134 			mv.visitInsn(ICONST_0);
135 			mv.visitJumpInsn(GOTO,endOfIf);
136 			mv.visitLabel(leftNotNull);
137 			mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false);
138 			mv.visitLabel(endOfIf);
139 			cf.pushDescriptor("Z");
140 			return;
141 		}
142 		mv.visitInsn(ICONST_1);
143 		mv.visitJumpInsn(GOTO,endOfIf);
144 		mv.visitLabel(elseTarget);
145 		mv.visitInsn(ICONST_0);
146 		mv.visitLabel(endOfIf);
147 		cf.pushDescriptor("Z");
148 	}
149 
150 }