/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.painless.phase; import org.opensearch.painless.AnalyzerCaster; import org.opensearch.painless.Operation; import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BinaryImplNode; import org.opensearch.painless.ir.BooleanNode; import org.opensearch.painless.ir.CastNode; import org.opensearch.painless.ir.ComparisonNode; import org.opensearch.painless.ir.ConditionalNode; import org.opensearch.painless.ir.ConstantNode; import org.opensearch.painless.ir.DeclarationNode; import org.opensearch.painless.ir.DoWhileLoopNode; import org.opensearch.painless.ir.DupNode; import org.opensearch.painless.ir.ElvisNode; import org.opensearch.painless.ir.ExpressionNode; import org.opensearch.painless.ir.FlipArrayIndexNode; import org.opensearch.painless.ir.FlipCollectionIndexNode; import org.opensearch.painless.ir.FlipDefIndexNode; import org.opensearch.painless.ir.ForEachSubArrayNode; import org.opensearch.painless.ir.ForEachSubIterableNode; import org.opensearch.painless.ir.ForLoopNode; import org.opensearch.painless.ir.IfElseNode; import org.opensearch.painless.ir.IfNode; import org.opensearch.painless.ir.InstanceofNode; import org.opensearch.painless.ir.InvokeCallDefNode; import org.opensearch.painless.ir.InvokeCallMemberNode; import org.opensearch.painless.ir.InvokeCallNode; import org.opensearch.painless.ir.ListInitializationNode; import org.opensearch.painless.ir.MapInitializationNode; import org.opensearch.painless.ir.NewArrayNode; import org.opensearch.painless.ir.NewObjectNode; import org.opensearch.painless.ir.NullNode; import org.opensearch.painless.ir.NullSafeSubNode; import org.opensearch.painless.ir.ReturnNode; import org.opensearch.painless.ir.StatementExpressionNode; import org.opensearch.painless.ir.StoreBraceDefNode; import org.opensearch.painless.ir.StoreBraceNode; import org.opensearch.painless.ir.StoreDotDefNode; import org.opensearch.painless.ir.StoreDotNode; import org.opensearch.painless.ir.StoreDotShortcutNode; import org.opensearch.painless.ir.StoreFieldMemberNode; import org.opensearch.painless.ir.StoreListShortcutNode; import org.opensearch.painless.ir.StoreMapShortcutNode; import org.opensearch.painless.ir.StoreVariableNode; import org.opensearch.painless.ir.StringConcatenationNode; import org.opensearch.painless.ir.ThrowNode; import org.opensearch.painless.ir.UnaryMathNode; import org.opensearch.painless.ir.WhileLoopNode; import org.opensearch.painless.lookup.PainlessLookupUtility; import java.util.function.Consumer; /** * This optimization pass will perform the specified operation on two leafs nodes if they are both * constants. The additional overrides for visiting ir nodes in this class are required whenever * there is a child node that is an expression. The structure of the tree does not have a way * for a child node to introspect into its parent node, so to replace itself the parent node * must pass the child node's particular set method as method reference. */ public class DefaultConstantFoldingOptimizationPhase extends IRTreeBaseVisitor<Consumer<ExpressionNode>> { @Override public void visitIf(IfNode irIfNode, Consumer<ExpressionNode> scope) { irIfNode.getConditionNode().visit(this, irIfNode::setConditionNode); irIfNode.getBlockNode().visit(this, null); } @Override public void visitIfElse(IfElseNode irIfElseNode, Consumer<ExpressionNode> scope) { irIfElseNode.getConditionNode().visit(this, irIfElseNode::setConditionNode); irIfElseNode.getBlockNode().visit(this, null); irIfElseNode.getElseBlockNode().visit(this, null); } @Override public void visitWhileLoop(WhileLoopNode irWhileLoopNode, Consumer<ExpressionNode> scope) { if (irWhileLoopNode.getConditionNode() != null) { irWhileLoopNode.getConditionNode().visit(this, irWhileLoopNode::setConditionNode); } if (irWhileLoopNode.getBlockNode() != null) { irWhileLoopNode.getBlockNode().visit(this, null); } } @Override public void visitDoWhileLoop(DoWhileLoopNode irDoWhileLoopNode, Consumer<ExpressionNode> scope) { irDoWhileLoopNode.getBlockNode().visit(this, null); if (irDoWhileLoopNode.getConditionNode() != null) { irDoWhileLoopNode.getConditionNode().visit(this, irDoWhileLoopNode::setConditionNode); } } @Override public void visitForLoop(ForLoopNode irForLoopNode, Consumer<ExpressionNode> scope) { if (irForLoopNode.getInitializerNode() != null) { irForLoopNode.getInitializerNode().visit(this, irForLoopNode::setInitialzerNode); } if (irForLoopNode.getConditionNode() != null) { irForLoopNode.getConditionNode().visit(this, irForLoopNode::setConditionNode); } if (irForLoopNode.getAfterthoughtNode() != null) { irForLoopNode.getAfterthoughtNode().visit(this, irForLoopNode::setAfterthoughtNode); } if (irForLoopNode.getBlockNode() != null) { irForLoopNode.getBlockNode().visit(this, null); } } @Override public void visitForEachSubArrayLoop(ForEachSubArrayNode irForEachSubArrayNode, Consumer<ExpressionNode> scope) { irForEachSubArrayNode.getConditionNode().visit(this, irForEachSubArrayNode::setConditionNode); irForEachSubArrayNode.getBlockNode().visit(this, null); } @Override public void visitForEachSubIterableLoop(ForEachSubIterableNode irForEachSubIterableNode, Consumer<ExpressionNode> scope) { irForEachSubIterableNode.getConditionNode().visit(this, irForEachSubIterableNode::setConditionNode); irForEachSubIterableNode.getBlockNode().visit(this, null); } @Override public void visitDeclaration(DeclarationNode irDeclarationNode, Consumer<ExpressionNode> scope) { if (irDeclarationNode.getExpressionNode() != null) { irDeclarationNode.getExpressionNode().visit(this, irDeclarationNode::setExpressionNode); } } @Override public void visitReturn(ReturnNode irReturnNode, Consumer<ExpressionNode> scope) { if (irReturnNode.getExpressionNode() != null) { irReturnNode.getExpressionNode().visit(this, irReturnNode::setExpressionNode); } } @Override public void visitStatementExpression(StatementExpressionNode irStatementExpressionNode, Consumer<ExpressionNode> scope) { irStatementExpressionNode.getExpressionNode().visit(this, irStatementExpressionNode::setExpressionNode); } @Override public void visitThrow(ThrowNode irThrowNode, Consumer<ExpressionNode> scope) { irThrowNode.getExpressionNode().visit(this, irThrowNode::setExpressionNode); } @Override public void visitBinaryImpl(BinaryImplNode irBinaryImplNode, Consumer<ExpressionNode> scope) { irBinaryImplNode.getLeftNode().visit(this, irBinaryImplNode::setLeftNode); irBinaryImplNode.getRightNode().visit(this, irBinaryImplNode::setRightNode); } @Override public void visitUnaryMath(UnaryMathNode irUnaryMathNode, Consumer<ExpressionNode> scope) { irUnaryMathNode.getChildNode().visit(this, irUnaryMathNode::setChildNode); if (irUnaryMathNode.getChildNode() instanceof ConstantNode) { ConstantNode irConstantNode = (ConstantNode) irUnaryMathNode.getChildNode(); Operation operation = irUnaryMathNode.getOperation(); Class<?> type = irUnaryMathNode.getExpressionType(); if (operation == Operation.SUB) { if (type == int.class) { irConstantNode.setConstant(-(int) irConstantNode.getConstant()); } else if (type == long.class) { irConstantNode.setConstant(-(long) irConstantNode.getConstant()); } else if (type == float.class) { irConstantNode.setConstant(-(float) irConstantNode.getConstant()); } else if (type == double.class) { irConstantNode.setConstant(-(double) irConstantNode.getConstant()); } else { throw irUnaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "unary operation [" + operation.symbol + "] on " + "constant [" + irConstantNode.getConstant() + "]" ) ); } scope.accept(irConstantNode); } else if (operation == Operation.BWNOT) { if (type == int.class) { irConstantNode.setConstant(~(int) irConstantNode.getConstant()); } else if (type == long.class) { irConstantNode.setConstant(~(long) irConstantNode.getConstant()); } else { throw irUnaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "unary operation [" + operation.symbol + "] on " + "constant [" + irConstantNode.getConstant() + "]" ) ); } scope.accept(irConstantNode); } else if (operation == Operation.NOT) { if (type == boolean.class) { irConstantNode.setConstant(!(boolean) irConstantNode.getConstant()); } else { throw irUnaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "unary operation [" + operation.symbol + "] on " + "constant [" + irConstantNode.getConstant() + "]" ) ); } scope.accept(irConstantNode); } else if (operation == Operation.ADD) { scope.accept(irConstantNode); } } } @Override public void visitBinaryMath(BinaryMathNode irBinaryMathNode, Consumer<ExpressionNode> scope) { irBinaryMathNode.getLeftNode().visit(this, irBinaryMathNode::setLeftNode); irBinaryMathNode.getRightNode().visit(this, irBinaryMathNode::setRightNode); if (irBinaryMathNode.getLeftNode() instanceof ConstantNode && irBinaryMathNode.getRightNode() instanceof ConstantNode) { ConstantNode irLeftConstantNode = (ConstantNode) irBinaryMathNode.getLeftNode(); ConstantNode irRightConstantNode = (ConstantNode) irBinaryMathNode.getRightNode(); Operation operation = irBinaryMathNode.getOperation(); Class<?> type = irBinaryMathNode.getExpressionType(); if (operation == Operation.MUL) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() * (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() * (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant((float) irLeftConstantNode.getConstant() * (float) irRightConstantNode.getConstant()); } else if (type == double.class) { irLeftConstantNode.setConstant((double) irLeftConstantNode.getConstant() * (double) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.DIV) { try { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() / (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() / (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant( (float) irLeftConstantNode.getConstant() / (float) irRightConstantNode.getConstant() ); } else if (type == double.class) { irLeftConstantNode.setConstant( (double) irLeftConstantNode.getConstant() / (double) irRightConstantNode.getConstant() ); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } } catch (ArithmeticException ae) { throw irBinaryMathNode.getLocation().createError(ae); } scope.accept(irLeftConstantNode); } else if (operation == Operation.REM) { try { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() % (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() % (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant( (float) irLeftConstantNode.getConstant() % (float) irRightConstantNode.getConstant() ); } else if (type == double.class) { irLeftConstantNode.setConstant( (double) irLeftConstantNode.getConstant() % (double) irRightConstantNode.getConstant() ); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } } catch (ArithmeticException ae) { throw irBinaryMathNode.getLocation().createError(ae); } scope.accept(irLeftConstantNode); } else if (operation == Operation.ADD) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() + (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() + (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant((float) irLeftConstantNode.getConstant() + (float) irRightConstantNode.getConstant()); } else if (type == double.class) { irLeftConstantNode.setConstant((double) irLeftConstantNode.getConstant() + (double) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.SUB) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() - (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() - (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant((float) irLeftConstantNode.getConstant() - (float) irRightConstantNode.getConstant()); } else if (type == double.class) { irLeftConstantNode.setConstant((double) irLeftConstantNode.getConstant() - (double) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.LSH) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() << (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() << (int) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.RSH) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() >> (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() >> (int) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.USH) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() >>> (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() >>> (int) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.BWAND) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() & (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() & (long) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.XOR) { if (type == boolean.class) { irLeftConstantNode.setConstant( (boolean) irLeftConstantNode.getConstant() ^ (boolean) irRightConstantNode.getConstant() ); } else if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() ^ (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() ^ (long) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.BWOR) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() | (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() | (long) irRightConstantNode.getConstant()); } else { throw irBinaryMathNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } } } @Override public void visitStringConcatenation(StringConcatenationNode irStringConcatenationNode, Consumer<ExpressionNode> scope) { irStringConcatenationNode.getArgumentNodes().get(0).visit(this, (e) -> irStringConcatenationNode.getArgumentNodes().set(0, e)); int i = 0; while (i < irStringConcatenationNode.getArgumentNodes().size() - 1) { ExpressionNode irLeftNode = irStringConcatenationNode.getArgumentNodes().get(i); ExpressionNode irRightNode = irStringConcatenationNode.getArgumentNodes().get(i + 1); int j = i; irRightNode.visit(this, (e) -> irStringConcatenationNode.getArgumentNodes().set(j + 1, e)); if (irLeftNode instanceof ConstantNode && irRightNode instanceof ConstantNode) { ConstantNode irConstantNode = (ConstantNode) irLeftNode; irConstantNode.setConstant("" + irConstantNode.getConstant() + ((ConstantNode) irRightNode).getConstant()); irConstantNode.setExpressionType(String.class); irStringConcatenationNode.getArgumentNodes().remove(i + 1); } else if (irLeftNode instanceof NullNode && irRightNode instanceof ConstantNode) { ConstantNode irConstantNode = (ConstantNode) irRightNode; irConstantNode.setConstant("" + null + ((ConstantNode) irRightNode).getConstant()); irConstantNode.setExpressionType(String.class); irStringConcatenationNode.getArgumentNodes().remove(i); } else if (irLeftNode instanceof ConstantNode && irRightNode instanceof NullNode) { ConstantNode irConstantNode = (ConstantNode) irLeftNode; irConstantNode.setConstant("" + ((ConstantNode) irLeftNode).getConstant() + null); irConstantNode.setExpressionType(String.class); irStringConcatenationNode.getArgumentNodes().remove(i + 1); } else if (irLeftNode instanceof NullNode && irRightNode instanceof NullNode) { ConstantNode irConstantNode = new ConstantNode(irLeftNode.getLocation()); irConstantNode.setConstant("" + null + null); irConstantNode.setExpressionType(String.class); irStringConcatenationNode.getArgumentNodes().set(i, irConstantNode); irStringConcatenationNode.getArgumentNodes().remove(i + 1); } else { i++; } } if (irStringConcatenationNode.getArgumentNodes().size() == 1) { ExpressionNode irArgumentNode = irStringConcatenationNode.getArgumentNodes().get(0); if (irArgumentNode instanceof ConstantNode) { scope.accept(irArgumentNode); } } } @Override public void visitBoolean(BooleanNode irBooleanNode, Consumer<ExpressionNode> scope) { irBooleanNode.getLeftNode().visit(this, irBooleanNode::setLeftNode); irBooleanNode.getRightNode().visit(this, irBooleanNode::setRightNode); if (irBooleanNode.getLeftNode() instanceof ConstantNode && irBooleanNode.getRightNode() instanceof ConstantNode) { ConstantNode irLeftConstantNode = (ConstantNode) irBooleanNode.getLeftNode(); ConstantNode irRightConstantNode = (ConstantNode) irBooleanNode.getRightNode(); Operation operation = irBooleanNode.getOperation(); Class<?> type = irBooleanNode.getExpressionType(); if (operation == Operation.AND) { if (type == boolean.class) { irLeftConstantNode.setConstant( (boolean) irLeftConstantNode.getConstant() && (boolean) irRightConstantNode.getConstant() ); } else { throw irBooleanNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "binary operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } else if (operation == Operation.OR) { if (type == boolean.class) { irLeftConstantNode.setConstant( (boolean) irLeftConstantNode.getConstant() || (boolean) irRightConstantNode.getConstant() ); } else { throw irBooleanNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "boolean operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } scope.accept(irLeftConstantNode); } } } @Override public void visitComparison(ComparisonNode irComparisonNode, Consumer<ExpressionNode> scope) { irComparisonNode.getLeftNode().visit(this, irComparisonNode::setLeftNode); irComparisonNode.getRightNode().visit(this, irComparisonNode::setRightNode); if ((irComparisonNode.getLeftNode() instanceof ConstantNode || irComparisonNode.getLeftNode() instanceof NullNode) && (irComparisonNode.getRightNode() instanceof ConstantNode || irComparisonNode.getRightNode() instanceof NullNode)) { ConstantNode irLeftConstantNode = irComparisonNode.getLeftNode() instanceof NullNode ? null : (ConstantNode) irComparisonNode.getLeftNode(); ConstantNode irRightConstantNode = irComparisonNode.getRightNode() instanceof NullNode ? null : (ConstantNode) irComparisonNode.getRightNode(); Operation operation = irComparisonNode.getOperation(); Class<?> type = irComparisonNode.getComparisonType(); if (operation == Operation.EQ || operation == Operation.EQR) { if (type == boolean.class) { irLeftConstantNode.setConstant( (boolean) irLeftConstantNode.getConstant() == (boolean) irRightConstantNode.getConstant() ); } else if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() == (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() == (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant((float) irLeftConstantNode.getConstant() == (float) irRightConstantNode.getConstant()); } else if (type == double.class) { irLeftConstantNode.setConstant((double) irLeftConstantNode.getConstant() == (double) irRightConstantNode.getConstant()); } else if (irLeftConstantNode == null && irRightConstantNode == null) { irLeftConstantNode = new ConstantNode(irComparisonNode.getLeftNode().getLocation()); irLeftConstantNode.setConstant(true); } else if (irLeftConstantNode == null || irRightConstantNode == null) { irLeftConstantNode = new ConstantNode(irComparisonNode.getLeftNode().getLocation()); irLeftConstantNode.setConstant(false); } else { if (operation == Operation.EQ) { irLeftConstantNode.setConstant(irLeftConstantNode.getConstant().equals(irRightConstantNode.getConstant())); } else { irLeftConstantNode.setConstant(irLeftConstantNode.getConstant() == irRightConstantNode.getConstant()); } } irLeftConstantNode.setExpressionType(boolean.class); scope.accept(irLeftConstantNode); } else if (operation == Operation.NE || operation == Operation.NER) { if (type == boolean.class) { irLeftConstantNode.setConstant( (boolean) irLeftConstantNode.getConstant() != (boolean) irRightConstantNode.getConstant() ); } else if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() != (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() != (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant((float) irLeftConstantNode.getConstant() != (float) irRightConstantNode.getConstant()); } else if (type == double.class) { irLeftConstantNode.setConstant((double) irLeftConstantNode.getConstant() != (double) irRightConstantNode.getConstant()); } else if (irLeftConstantNode == null && irRightConstantNode == null) { irLeftConstantNode = new ConstantNode(irComparisonNode.getLeftNode().getLocation()); irLeftConstantNode.setConstant(false); } else if (irLeftConstantNode == null || irRightConstantNode == null) { irLeftConstantNode = new ConstantNode(irComparisonNode.getLeftNode().getLocation()); irLeftConstantNode.setConstant(true); } else { if (operation == Operation.NE) { irLeftConstantNode.setConstant(irLeftConstantNode.getConstant().equals(irRightConstantNode.getConstant()) == false); } else { irLeftConstantNode.setConstant(irLeftConstantNode.getConstant() != irRightConstantNode.getConstant()); } } irLeftConstantNode.setExpressionType(boolean.class); scope.accept(irLeftConstantNode); } else if (irLeftConstantNode != null && irRightConstantNode != null) { if (operation == Operation.GT) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() > (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() > (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant( (float) irLeftConstantNode.getConstant() > (float) irRightConstantNode.getConstant() ); } else if (type == double.class) { irLeftConstantNode.setConstant( (double) irLeftConstantNode.getConstant() > (double) irRightConstantNode.getConstant() ); } else { throw irComparisonNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "comparison operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } irLeftConstantNode.setExpressionType(boolean.class); scope.accept(irLeftConstantNode); } else if (operation == Operation.GTE) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() >= (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() >= (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant( (float) irLeftConstantNode.getConstant() >= (float) irRightConstantNode.getConstant() ); } else if (type == double.class) { irLeftConstantNode.setConstant( (double) irLeftConstantNode.getConstant() >= (double) irRightConstantNode.getConstant() ); } else { throw irComparisonNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "comparison operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } irLeftConstantNode.setExpressionType(boolean.class); scope.accept(irLeftConstantNode); } else if (operation == Operation.LT) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() < (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() < (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant( (float) irLeftConstantNode.getConstant() < (float) irRightConstantNode.getConstant() ); } else if (type == double.class) { irLeftConstantNode.setConstant( (double) irLeftConstantNode.getConstant() < (double) irRightConstantNode.getConstant() ); } else { throw irComparisonNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "comparison operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } irLeftConstantNode.setExpressionType(boolean.class); scope.accept(irLeftConstantNode); } else if (operation == Operation.LTE) { if (type == int.class) { irLeftConstantNode.setConstant((int) irLeftConstantNode.getConstant() <= (int) irRightConstantNode.getConstant()); } else if (type == long.class) { irLeftConstantNode.setConstant((long) irLeftConstantNode.getConstant() <= (long) irRightConstantNode.getConstant()); } else if (type == float.class) { irLeftConstantNode.setConstant( (float) irLeftConstantNode.getConstant() <= (float) irRightConstantNode.getConstant() ); } else if (type == double.class) { irLeftConstantNode.setConstant( (double) irLeftConstantNode.getConstant() <= (double) irRightConstantNode.getConstant() ); } else { throw irComparisonNode.getLocation() .createError( new IllegalStateException( "constant folding error: " + "unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] for " + "comparison operation [" + operation.symbol + "] on " + "constants [" + irLeftConstantNode.getConstant() + "] and [" + irRightConstantNode.getConstant() + "]" ) ); } irLeftConstantNode.setExpressionType(boolean.class); scope.accept(irLeftConstantNode); } } } } @Override public void visitCast(CastNode irCastNode, Consumer<ExpressionNode> scope) { irCastNode.getChildNode().visit(this, irCastNode::setChildNode); if (irCastNode.getChildNode() instanceof ConstantNode && PainlessLookupUtility.isConstantType(irCastNode.getExpressionType())) { ConstantNode irConstantNode = (ConstantNode) irCastNode.getChildNode(); irConstantNode.setConstant( AnalyzerCaster.constCast(irCastNode.getLocation(), irConstantNode.getConstant(), irCastNode.getCast()) ); irConstantNode.setExpressionType(irCastNode.getExpressionType()); scope.accept(irConstantNode); } } @Override public void visitInstanceof(InstanceofNode irInstanceofNode, Consumer<ExpressionNode> scope) { irInstanceofNode.getChildNode().visit(this, irInstanceofNode::setChildNode); } @Override public void visitConditional(ConditionalNode irConditionalNode, Consumer<ExpressionNode> scope) { irConditionalNode.getConditionNode().visit(this, irConditionalNode::setConditionNode); irConditionalNode.getLeftNode().visit(this, irConditionalNode::setLeftNode); irConditionalNode.getRightNode().visit(this, irConditionalNode::setRightNode); } @Override public void visitElvis(ElvisNode irElvisNode, Consumer<ExpressionNode> scope) { irElvisNode.getLeftNode().visit(this, irElvisNode::setLeftNode); irElvisNode.getRightNode().visit(this, irElvisNode::setRightNode); } @Override public void visitListInitialization(ListInitializationNode irListInitializationNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irListInitializationNode.getArgumentNodes().size(); i++) { int j = i; irListInitializationNode.getArgumentNodes().get(i).visit(this, (e) -> irListInitializationNode.getArgumentNodes().set(j, e)); } } @Override public void visitMapInitialization(MapInitializationNode irMapInitializationNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irMapInitializationNode.getKeyNodes().size(); i++) { int j = i; irMapInitializationNode.getKeyNode(i).visit(this, (e) -> irMapInitializationNode.getKeyNodes().set(j, e)); } for (int i = 0; i < irMapInitializationNode.getValueNodes().size(); i++) { int j = i; irMapInitializationNode.getValueNode(i).visit(this, (e) -> irMapInitializationNode.getValueNodes().set(j, e)); } } @Override public void visitNewArray(NewArrayNode irNewArrayNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irNewArrayNode.getArgumentNodes().size(); i++) { int j = i; irNewArrayNode.getArgumentNodes().get(i).visit(this, (e) -> irNewArrayNode.getArgumentNodes().set(j, e)); } } @Override public void visitNewObject(NewObjectNode irNewObjectNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irNewObjectNode.getArgumentNodes().size(); i++) { int j = i; irNewObjectNode.getArgumentNodes().get(i).visit(this, (e) -> irNewObjectNode.getArgumentNodes().set(j, e)); } } @Override public void visitNullSafeSub(NullSafeSubNode irNullSafeSubNode, Consumer<ExpressionNode> scope) { irNullSafeSubNode.getChildNode().visit(this, irNullSafeSubNode::setChildNode); } @Override public void visitStoreVariable(StoreVariableNode irStoreVariableNode, Consumer<ExpressionNode> scope) { irStoreVariableNode.getChildNode().visit(this, irStoreVariableNode::setChildNode); } @Override public void visitStoreDotDef(StoreDotDefNode irStoreDotDefNode, Consumer<ExpressionNode> scope) { irStoreDotDefNode.getChildNode().visit(this, irStoreDotDefNode::setChildNode); } @Override public void visitStoreDot(StoreDotNode irStoreDotNode, Consumer<ExpressionNode> scope) { irStoreDotNode.getChildNode().visit(this, irStoreDotNode::setChildNode); } @Override public void visitStoreDotShortcut(StoreDotShortcutNode irDotSubShortcutNode, Consumer<ExpressionNode> scope) { irDotSubShortcutNode.getChildNode().visit(this, irDotSubShortcutNode::setChildNode); } @Override public void visitStoreListShortcut(StoreListShortcutNode irStoreListShortcutNode, Consumer<ExpressionNode> scope) { irStoreListShortcutNode.getChildNode().visit(this, irStoreListShortcutNode::setChildNode); } @Override public void visitStoreMapShortcut(StoreMapShortcutNode irStoreMapShortcutNode, Consumer<ExpressionNode> scope) { irStoreMapShortcutNode.getChildNode().visit(this, irStoreMapShortcutNode::setChildNode); } @Override public void visitStoreFieldMember(StoreFieldMemberNode irStoreFieldMemberNode, Consumer<ExpressionNode> scope) { irStoreFieldMemberNode.getChildNode().visit(this, irStoreFieldMemberNode::setChildNode); } @Override public void visitStoreBraceDef(StoreBraceDefNode irStoreBraceDefNode, Consumer<ExpressionNode> scope) { irStoreBraceDefNode.getChildNode().visit(this, irStoreBraceDefNode::setChildNode); } @Override public void visitStoreBrace(StoreBraceNode irStoreBraceNode, Consumer<ExpressionNode> scope) { irStoreBraceNode.getChildNode().visit(this, irStoreBraceNode::setChildNode); } @Override public void visitInvokeCallDef(InvokeCallDefNode irInvokeCallDefNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irInvokeCallDefNode.getArgumentNodes().size(); i++) { int j = i; irInvokeCallDefNode.getArgumentNodes().get(i).visit(this, (e) -> irInvokeCallDefNode.getArgumentNodes().set(j, e)); } } @Override public void visitInvokeCall(InvokeCallNode irInvokeCallNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irInvokeCallNode.getArgumentNodes().size(); i++) { int j = i; irInvokeCallNode.getArgumentNodes().get(i).visit(this, (e) -> irInvokeCallNode.getArgumentNodes().set(j, e)); } } @Override public void visitInvokeCallMember(InvokeCallMemberNode irInvokeCallMemberNode, Consumer<ExpressionNode> scope) { for (int i = 0; i < irInvokeCallMemberNode.getArgumentNodes().size(); i++) { int j = i; irInvokeCallMemberNode.getArgumentNodes().get(i).visit(this, (e) -> irInvokeCallMemberNode.getArgumentNodes().set(j, e)); } } @Override public void visitFlipArrayIndex(FlipArrayIndexNode irFlipArrayIndexNode, Consumer<ExpressionNode> scope) { irFlipArrayIndexNode.getChildNode().visit(this, irFlipArrayIndexNode::setChildNode); } @Override public void visitFlipCollectionIndex(FlipCollectionIndexNode irFlipCollectionIndexNode, Consumer<ExpressionNode> scope) { irFlipCollectionIndexNode.getChildNode().visit(this, irFlipCollectionIndexNode::setChildNode); } @Override public void visitFlipDefIndex(FlipDefIndexNode irFlipDefIndexNode, Consumer<ExpressionNode> scope) { irFlipDefIndexNode.getChildNode().visit(this, irFlipDefIndexNode::setChildNode); } @Override public void visitDup(DupNode irDupNode, Consumer<ExpressionNode> scope) { irDupNode.getChildNode().visit(this, irDupNode::setChildNode); } }