/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; import org.elasticsearch.painless.Operation; import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import java.util.Objects; import java.util.Set; import org.elasticsearch.painless.MethodWriter; import static org.elasticsearch.painless.WriterConstants.OBJECTS_TYPE; import static org.elasticsearch.painless.WriterConstants.EQUALS; /** * Represents a comparison expression. */ public final class EComp extends AExpression { private final Operation operation; private AExpression left; private AExpression right; private Type promotedType; public EComp(Location location, Operation operation, AExpression left, AExpression right) { super(location); this.operation = Objects.requireNonNull(operation); this.left = Objects.requireNonNull(left); this.right = Objects.requireNonNull(right); } @Override void extractVariables(Set<String> variables) { left.extractVariables(variables); right.extractVariables(variables); } @Override void analyze(Locals locals) { if (operation == Operation.EQ) { analyzeEq(locals); } else if (operation == Operation.EQR) { analyzeEqR(locals); } else if (operation == Operation.NE) { analyzeNE(locals); } else if (operation == Operation.NER) { analyzeNER(locals); } else if (operation == Operation.GTE) { analyzeGTE(locals); } else if (operation == Operation.GT) { analyzeGT(locals); } else if (operation == Operation.LTE) { analyzeLTE(locals); } else if (operation == Operation.LT) { analyzeLT(locals); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } private void analyzeEq(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteEquality(left.actual, right.actual); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply equals [==] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } if (promotedType.sort == Sort.DEF) { left.expected = left.actual; right.expected = right.actual; } else { left.expected = promotedType; right.expected = promotedType; } left = left.cast(variables); right = right.cast(variables); if (left.isNull && right.isNull) { throw createError(new IllegalArgumentException("Extraneous comparison of null constants.")); } if ((left.constant != null || left.isNull) && (right.constant != null || right.isNull)) { Sort sort = promotedType.sort; if (sort == Sort.BOOL) { constant = (boolean)left.constant == (boolean)right.constant; } else if (sort == Sort.INT) { constant = (int)left.constant == (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant == (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant == (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant == (double)right.constant; } else if (!left.isNull) { constant = left.constant.equals(right.constant); } else if (!right.isNull) { constant = right.constant.equals(null); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } actual = Definition.BOOLEAN_TYPE; } private void analyzeEqR(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteEquality(left.actual, right.actual); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply reference equals [===] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } left.expected = promotedType; right.expected = promotedType; left = left.cast(variables); right = right.cast(variables); if (left.isNull && right.isNull) { throw createError(new IllegalArgumentException("Extraneous comparison of null constants.")); } if ((left.constant != null || left.isNull) && (right.constant != null || right.isNull)) { Sort sort = promotedType.sort; if (sort == Sort.BOOL) { constant = (boolean)left.constant == (boolean)right.constant; } else if (sort == Sort.INT) { constant = (int)left.constant == (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant == (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant == (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant == (double)right.constant; } else { constant = left.constant == right.constant; } } actual = Definition.BOOLEAN_TYPE; } private void analyzeNE(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteEquality(left.actual, right.actual); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply not equals [!=] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } if (promotedType.sort == Sort.DEF) { left.expected = left.actual; right.expected = right.actual; } else { left.expected = promotedType; right.expected = promotedType; } left = left.cast(variables); right = right.cast(variables); if (left.isNull && right.isNull) { throw createError(new IllegalArgumentException("Extraneous comparison of null constants.")); } if ((left.constant != null || left.isNull) && (right.constant != null || right.isNull)) { Sort sort = promotedType.sort; if (sort == Sort.BOOL) { constant = (boolean)left.constant != (boolean)right.constant; } else if (sort == Sort.INT) { constant = (int)left.constant != (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant != (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant != (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant != (double)right.constant; } else if (!left.isNull) { constant = !left.constant.equals(right.constant); } else if (!right.isNull) { constant = !right.constant.equals(null); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } actual = Definition.BOOLEAN_TYPE; } private void analyzeNER(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteEquality(left.actual, right.actual); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply reference not equals [!==] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } left.expected = promotedType; right.expected = promotedType; left = left.cast(variables); right = right.cast(variables); if (left.isNull && right.isNull) { throw createError(new IllegalArgumentException("Extraneous comparison of null constants.")); } if ((left.constant != null || left.isNull) && (right.constant != null || right.isNull)) { Sort sort = promotedType.sort; if (sort == Sort.BOOL) { constant = (boolean)left.constant != (boolean)right.constant; } else if (sort == Sort.INT) { constant = (int)left.constant != (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant != (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant != (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant != (double)right.constant; } else { constant = left.constant != right.constant; } } actual = Definition.BOOLEAN_TYPE; } private void analyzeGTE(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteNumeric(left.actual, right.actual, true); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply greater than or equals [>=] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } if (promotedType.sort == Sort.DEF) { left.expected = left.actual; right.expected = right.actual; } else { left.expected = promotedType; right.expected = promotedType; } left = left.cast(variables); right = right.cast(variables); if (left.constant != null && right.constant != null) { Sort sort = promotedType.sort; if (sort == Sort.INT) { constant = (int)left.constant >= (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant >= (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant >= (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant >= (double)right.constant; } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } actual = Definition.BOOLEAN_TYPE; } private void analyzeGT(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteNumeric(left.actual, right.actual, true); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply greater than [>] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } if (promotedType.sort == Sort.DEF) { left.expected = left.actual; right.expected = right.actual; } else { left.expected = promotedType; right.expected = promotedType; } left = left.cast(variables); right = right.cast(variables); if (left.constant != null && right.constant != null) { Sort sort = promotedType.sort; if (sort == Sort.INT) { constant = (int)left.constant > (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant > (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant > (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant > (double)right.constant; } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } actual = Definition.BOOLEAN_TYPE; } private void analyzeLTE(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteNumeric(left.actual, right.actual, true); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply less than or equals [<=] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } if (promotedType.sort == Sort.DEF) { left.expected = left.actual; right.expected = right.actual; } else { left.expected = promotedType; right.expected = promotedType; } left = left.cast(variables); right = right.cast(variables); if (left.constant != null && right.constant != null) { Sort sort = promotedType.sort; if (sort == Sort.INT) { constant = (int)left.constant <= (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant <= (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant <= (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant <= (double)right.constant; } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } actual = Definition.BOOLEAN_TYPE; } private void analyzeLT(Locals variables) { left.analyze(variables); right.analyze(variables); promotedType = AnalyzerCaster.promoteNumeric(left.actual, right.actual, true); if (promotedType == null) { throw createError(new ClassCastException("Cannot apply less than [>=] to types " + "[" + left.actual.name + "] and [" + right.actual.name + "].")); } if (promotedType.sort == Sort.DEF) { left.expected = left.actual; right.expected = right.actual; } else { left.expected = promotedType; right.expected = promotedType; } left = left.cast(variables); right = right.cast(variables); if (left.constant != null && right.constant != null) { Sort sort = promotedType.sort; if (sort == Sort.INT) { constant = (int)left.constant < (int)right.constant; } else if (sort == Sort.LONG) { constant = (long)left.constant < (long)right.constant; } else if (sort == Sort.FLOAT) { constant = (float)left.constant < (float)right.constant; } else if (sort == Sort.DOUBLE) { constant = (double)left.constant < (double)right.constant; } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } actual = Definition.BOOLEAN_TYPE; } @Override void write(MethodWriter writer, Globals globals) { writer.writeDebugInfo(location); left.write(writer, globals); if (!right.isNull) { right.write(writer, globals); } Label jump = new Label(); Label end = new Label(); boolean eq = (operation == Operation.EQ || operation == Operation.EQR); boolean ne = (operation == Operation.NE || operation == Operation.NER); boolean lt = operation == Operation.LT; boolean lte = operation == Operation.LTE; boolean gt = operation == Operation.GT; boolean gte = operation == Operation.GTE; boolean writejump = true; switch (promotedType.sort) { case VOID: case BYTE: case SHORT: case CHAR: throw createError(new IllegalStateException("Illegal tree structure.")); case BOOL: if (eq) writer.ifCmp(promotedType.type, MethodWriter.EQ, jump); else if (ne) writer.ifCmp(promotedType.type, MethodWriter.NE, jump); else { throw createError(new IllegalStateException("Illegal tree structure.")); } break; case INT: case LONG: case FLOAT: case DOUBLE: if (eq) writer.ifCmp(promotedType.type, MethodWriter.EQ, jump); else if (ne) writer.ifCmp(promotedType.type, MethodWriter.NE, jump); else if (lt) writer.ifCmp(promotedType.type, MethodWriter.LT, jump); else if (lte) writer.ifCmp(promotedType.type, MethodWriter.LE, jump); else if (gt) writer.ifCmp(promotedType.type, MethodWriter.GT, jump); else if (gte) writer.ifCmp(promotedType.type, MethodWriter.GE, jump); else { throw createError(new IllegalStateException("Illegal tree structure.")); } break; case DEF: org.objectweb.asm.Type booleanType = org.objectweb.asm.Type.getType(boolean.class); org.objectweb.asm.Type descriptor = org.objectweb.asm.Type.getMethodType(booleanType, left.actual.type, right.actual.type); if (eq) { if (right.isNull) { writer.ifNull(jump); } else if (!left.isNull && operation == Operation.EQ) { writer.invokeDefCall("eq", descriptor, DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL); writejump = false; } else { writer.ifCmp(promotedType.type, MethodWriter.EQ, jump); } } else if (ne) { if (right.isNull) { writer.ifNonNull(jump); } else if (!left.isNull && operation == Operation.NE) { writer.invokeDefCall("eq", descriptor, DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL); writer.ifZCmp(MethodWriter.EQ, jump); } else { writer.ifCmp(promotedType.type, MethodWriter.NE, jump); } } else if (lt) { writer.invokeDefCall("lt", descriptor, DefBootstrap.BINARY_OPERATOR, 0); writejump = false; } else if (lte) { writer.invokeDefCall("lte", descriptor, DefBootstrap.BINARY_OPERATOR, 0); writejump = false; } else if (gt) { writer.invokeDefCall("gt", descriptor, DefBootstrap.BINARY_OPERATOR, 0); writejump = false; } else if (gte) { writer.invokeDefCall("gte", descriptor, DefBootstrap.BINARY_OPERATOR, 0); writejump = false; } else { throw createError(new IllegalStateException("Illegal tree structure.")); } break; default: if (eq) { if (right.isNull) { writer.ifNull(jump); } else if (operation == Operation.EQ) { writer.invokeStatic(OBJECTS_TYPE, EQUALS); writejump = false; } else { writer.ifCmp(promotedType.type, MethodWriter.EQ, jump); } } else if (ne) { if (right.isNull) { writer.ifNonNull(jump); } else if (operation == Operation.NE) { writer.invokeStatic(OBJECTS_TYPE, EQUALS); writer.ifZCmp(MethodWriter.EQ, jump); } else { writer.ifCmp(promotedType.type, MethodWriter.NE, jump); } } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } if (writejump) { writer.push(false); writer.goTo(end); writer.mark(jump); writer.push(true); writer.mark(end); } } @Override public String toString() { return singleLineToString(left, operation.symbol, right); } }