diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AExpression.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AExpression.java index 13c79d6b8ded6..189cb94dcd70b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AExpression.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AExpression.java @@ -91,6 +91,7 @@ public static class Output { AExpression prefix; // TODO: remove placeholders once analysis and write are combined into build + // TODO: https://github.com/elastic/elasticsearch/issues/53561 // This are used to support the transition from a mutable to immutable state. // Currently, the IR tree is built during the user tree "write" phase, so // these are stored on the node to set during the "semantic" phase and then diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AStatement.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AStatement.java index 3ad2b0b8a0800..d8d12d8a14698 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AStatement.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/AStatement.java @@ -30,66 +30,77 @@ */ public abstract class AStatement extends ANode { - /** - * Set to true when the final statement in an {@link SClass} is reached. - * Used to determine whether or not an auto-return is necessary. - */ - boolean lastSource = false; - - /** - * Set to true when a loop begins. Used by {@link SBlock} to help determine - * when the final statement of a loop is reached. - */ - boolean beginLoop = false; - - /** - * Set to true when inside a loop. Used by {@link SBreak} and {@link SContinue} - * to determine if a break/continue statement is legal. - */ - boolean inLoop = false; - - /** - * Set to true when on the last statement of a loop. Used by {@link SContinue} - * to prevent extraneous continue statements. - */ - boolean lastLoop = false; - - /** - * Set to true if a statement would cause the method to exit. Used to - * determine whether or not an auto-return is necessary. - */ - boolean methodEscape = false; - - /** - * Set to true if a statement would cause a loop to exit. Used to - * prevent unreachable statements. - */ - boolean loopEscape = false; - - /** - * Set to true if all current paths escape from the current {@link SBlock}. - * Used during the analysis phase to prevent unreachable statements and - * the writing phase to prevent extraneous bytecode gotos from being written. - */ - boolean allEscape = false; - - /** - * Set to true if any continue statement occurs in a loop. Used to prevent - * unnecessary infinite loops. - */ - boolean anyContinue = false; + public static class Input { + + /** + * Set to true when the final statement in an {@link SClass} is reached. + * Used to determine whether or not an auto-return is necessary. + */ + boolean lastSource = false; + + /** + * Set to true when a loop begins. Used by {@link SBlock} to help determine + * when the final statement of a loop is reached. + */ + boolean beginLoop = false; + + /** + * Set to true when inside a loop. Used by {@link SBreak} and {@link SContinue} + * to determine if a break/continue statement is legal. + */ + boolean inLoop = false; + + /** + * Set to true when on the last statement of a loop. Used by {@link SContinue} + * to prevent extraneous continue statements. + */ + boolean lastLoop = false; + } - /** - * Set to true if any break statement occurs in a loop. Used to prevent - * extraneous loops. - */ - boolean anyBreak = false; + public static class Output { + + /** + * Set to true if a statement would cause the method to exit. Used to + * determine whether or not an auto-return is necessary. + */ + boolean methodEscape = false; + + /** + * Set to true if a statement would cause a loop to exit. Used to + * prevent unreachable statements. + */ + boolean loopEscape = false; + + /** + * Set to true if all current paths escape from the current {@link SBlock}. + * Used during the analysis phase to prevent unreachable statements and + * the writing phase to prevent extraneous bytecode gotos from being written. + */ + boolean allEscape = false; + + /** + * Set to true if any continue statement occurs in a loop. Used to prevent + * unnecessary infinite loops. + */ + boolean anyContinue = false; + + /** + * Set to true if any break statement occurs in a loop. Used to prevent + * extraneous loops. + */ + boolean anyBreak = false; + + /** + * Set to the approximate number of statements in a loop block to prevent + * infinite loops during runtime. + */ + int statementCount = 0; + } - /** - * Set to the approximate number of statements in a loop block to prevent - * infinite loops during runtime. - */ - int statementCount = 0; + // TODO: remove placeholders once analysis and write are combined into build + // TODO: https://github.com/elastic/elasticsearch/issues/53561 + Input input; + Output output; /** * Standard constructor with location used for error tracking. @@ -101,7 +112,9 @@ public abstract class AStatement extends ANode { /** * Checks for errors and collects data for the writing phase. */ - abstract void analyze(ScriptRoot scriptRoot, Scope scope); + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + throw new UnsupportedOperationException(); + } /** * Writes ASM based on the data collected during the analysis phase. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index 4619ff6522724..a4cb19d048c62 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -165,17 +165,18 @@ Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { if (block.statements.isEmpty()) { throw createError(new IllegalArgumentException("cannot generate empty lambda")); } - block.lastSource = true; - block.analyze(scriptRoot, lambdaScope); - captures = new ArrayList<>(lambdaScope.getCaptures()); + AStatement.Input blockInput = new AStatement.Input(); + blockInput.lastSource = true; + AStatement.Output blockOutput = block.analyze(scriptRoot, lambdaScope, blockInput); - if (block.methodEscape == false) { + if (blockOutput.methodEscape == false) { throw createError(new IllegalArgumentException("not all paths return a value for lambda")); } maxLoopCounter = scriptRoot.getCompilerSettings().getMaxLoopCounter(); // prepend capture list to lambda's arguments + captures = new ArrayList<>(lambdaScope.getCaptures()); this.typeParameters = new ArrayList<>(captures.size() + typeParameters.size()); parameterNames = new ArrayList<>(captures.size() + paramNameStrs.size()); for (Variable var : captures) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBlock.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBlock.java index 20ee3ec572d57..8e6db345e0b30 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBlock.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBlock.java @@ -44,7 +44,10 @@ public SBlock(Location location, List statements) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + if (statements == null || statements.isEmpty()) { throw createError(new IllegalArgumentException("A block must contain at least one statement.")); } @@ -54,22 +57,25 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { for (AStatement statement : statements) { // Note that we do not need to check after the last statement because // there is no statement that can be unreachable after the last. - if (allEscape) { + if (output.allEscape) { throw createError(new IllegalArgumentException("Unreachable statement.")); } - statement.inLoop = inLoop; - statement.lastSource = lastSource && statement == last; - statement.lastLoop = (beginLoop || lastLoop) && statement == last; - statement.analyze(scriptRoot, scope); - - methodEscape = statement.methodEscape; - loopEscape = statement.loopEscape; - allEscape = statement.allEscape; - anyContinue |= statement.anyContinue; - anyBreak |= statement.anyBreak; - statementCount += statement.statementCount; + Input statementInput = new Input(); + statementInput.inLoop = input.inLoop; + statementInput.lastSource = input.lastSource && statement == last; + statementInput.lastLoop = (input.beginLoop || input.lastLoop) && statement == last; + Output statementOutput = statement.analyze(scriptRoot, scope, statementInput); + + output.methodEscape = statementOutput.methodEscape; + output.loopEscape = statementOutput.loopEscape; + output.allEscape = statementOutput.allEscape; + output.anyContinue |= statementOutput.anyContinue; + output.anyBreak |= statementOutput.anyBreak; + output.statementCount += statementOutput.statementCount; } + + return output; } @Override @@ -81,8 +87,8 @@ BlockNode write(ClassNode classNode) { } blockNode.setLocation(location); - blockNode.setAllEscape(allEscape); - blockNode.setStatementCount(statementCount); + blockNode.setAllEscape(output.allEscape); + blockNode.setStatementCount(output.statementCount); return blockNode; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBreak.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBreak.java index 8c2d2d3c03403..4b036153319ea 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBreak.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SBreak.java @@ -35,15 +35,20 @@ public SBreak(Location location) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { - if (!inLoop) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + + if (input.inLoop == false) { throw createError(new IllegalArgumentException("Break statement outside of a loop.")); } - loopEscape = true; - allEscape = true; - anyBreak = true; - statementCount = 1; + output.loopEscape = true; + output.allEscape = true; + output.anyBreak = true; + output.statementCount = 1; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java index ed960776a19f2..f00c0b18a70fc 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java @@ -46,8 +46,11 @@ public SCatch(Location location, DType baseException, SDeclaration declaration, } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { - declaration.analyze(scriptRoot, scope); + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + + declaration.analyze(scriptRoot, scope, new Input()); Class baseType = baseException.resolveType(scriptRoot.getPainlessLookup()).getType(); Class type = scope.getVariable(location, declaration.name).getType(); @@ -59,18 +62,21 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { } if (block != null) { - block.lastSource = lastSource; - block.inLoop = inLoop; - block.lastLoop = lastLoop; - block.analyze(scriptRoot, scope); - - methodEscape = block.methodEscape; - loopEscape = block.loopEscape; - allEscape = block.allEscape; - anyContinue = block.anyContinue; - anyBreak = block.anyBreak; - statementCount = block.statementCount; + Input blockInput = new Input(); + blockInput.lastSource = input.lastSource; + blockInput.inLoop = input.inLoop; + blockInput.lastLoop = input.lastLoop; + Output blockOutput = block.analyze(scriptRoot, scope, blockInput); + + output.methodEscape = blockOutput.methodEscape; + output.loopEscape = blockOutput.loopEscape; + output.allEscape = blockOutput.allEscape; + output.anyContinue = blockOutput.anyContinue; + output.anyBreak = blockOutput.anyBreak; + output.statementCount = blockOutput.statementCount; } + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SContinue.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SContinue.java index fe19365095777..1f5c752f8ccef 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SContinue.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SContinue.java @@ -35,18 +35,23 @@ public SContinue(Location location) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { - if (!inLoop) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + + if (input.inLoop == false) { throw createError(new IllegalArgumentException("Continue statement outside of a loop.")); } - if (lastLoop) { + if (input.lastLoop) { throw createError(new IllegalArgumentException("Extraneous continue statement.")); } - allEscape = true; - anyContinue = true; - statementCount = 1; + output.allEscape = true; + output.anyContinue = true; + output.statementCount = 1; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclBlock.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclBlock.java index fa85f57346908..47bbe0d123ecb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclBlock.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclBlock.java @@ -44,12 +44,17 @@ public SDeclBlock(Location location, List declarations) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + for (SDeclaration declaration : declarations) { - declaration.analyze(scriptRoot, scope); + declaration.analyze(scriptRoot, scope, new Input()); } - statementCount = declarations.size(); + output.statementCount = declarations.size(); + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java index 538de2751611c..2a28b4d029558 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java @@ -48,18 +48,23 @@ public SDeclaration(Location location, DType type, String name, boolean requires } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + DResolvedType resolvedType = type.resolveType(scriptRoot.getPainlessLookup()); type = resolvedType; if (expression != null) { - Input expressionInput = new Input(); + AExpression.Input expressionInput = new AExpression.Input(); expressionInput.expected = resolvedType.getType(); expression.analyze(scriptRoot, scope, expressionInput); expression.cast(); } scope.defineVariable(location, resolvedType.getType(), name, false); + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDo.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDo.java index 89c5aa4be4ce1..8d31f325a049f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDo.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDo.java @@ -45,18 +45,22 @@ public SDo(Location location, SBlock block, AExpression condition) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + scope = scope.newLocalScope(); if (block == null) { throw createError(new IllegalArgumentException("Extraneous do while loop.")); } - block.beginLoop = true; - block.inLoop = true; - block.analyze(scriptRoot, scope); + Input blockInput = new Input(); + blockInput.beginLoop = true; + blockInput.inLoop = true; + Output blockOutput = block.analyze(scriptRoot, scope, blockInput); - if (block.loopEscape && !block.anyContinue) { + if (blockOutput.loopEscape && blockOutput.anyContinue == false) { throw createError(new IllegalArgumentException("Extraneous do while loop.")); } @@ -72,13 +76,15 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { throw createError(new IllegalArgumentException("Extraneous do while loop.")); } - if (!block.anyBreak) { - methodEscape = true; - allEscape = true; + if (blockOutput.anyBreak == false) { + output.methodEscape = true; + output.allEscape = true; } } - statementCount = 1; + output.statementCount = 1; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java index 0772331d11b64..b5c439233b64d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java @@ -53,7 +53,10 @@ public SEach(Location location, String type, String name, AExpression expression } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + AExpression.Output expressionOutput = expression.analyze(scriptRoot, scope, new AExpression.Input()); expression.input.expected = expressionOutput.actual; expression.cast(); @@ -76,22 +79,25 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { "[" + PainlessLookupUtility.typeToCanonicalTypeName(expressionOutput.actual) + "].")); } - sub.analyze(scriptRoot, scope); + sub.analyze(scriptRoot, scope, input); if (block == null) { throw createError(new IllegalArgumentException("Extraneous for each loop.")); } - block.beginLoop = true; - block.inLoop = true; - block.analyze(scriptRoot, scope); - block.statementCount = Math.max(1, block.statementCount); + Input blockInput = new Input(); + blockInput.beginLoop = true; + blockInput.inLoop = true; + Output blockOutput = block.analyze(scriptRoot, scope, blockInput); + blockOutput.statementCount = Math.max(1, blockOutput.statementCount); - if (block.loopEscape && !block.anyContinue) { + if (blockOutput.loopEscape && blockOutput.anyContinue == false) { throw createError(new IllegalArgumentException("Extraneous for loop.")); } - statementCount = 1; + output.statementCount = 1; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SExpression.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SExpression.java index 41a6b1757935d..f04cefa622acd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SExpression.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SExpression.java @@ -44,36 +44,40 @@ public SExpression(Location location, AExpression expression) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + Class rtnType = scope.getReturnType(); boolean isVoid = rtnType == void.class; AExpression.Input expressionInput = new AExpression.Input(); - expressionInput.read = lastSource && !isVoid; + expressionInput.read = input.lastSource && !isVoid; AExpression.Output expressionOutput = expression.analyze(scriptRoot, scope, expressionInput); - - if ((lastSource == false || isVoid) && expressionOutput.statement == false) { + if ((input.lastSource == false || isVoid) && expressionOutput.statement == false) { throw createError(new IllegalArgumentException("Not a statement.")); } - boolean rtn = lastSource && isVoid == false && expressionOutput.actual != void.class; + boolean rtn = input.lastSource && isVoid == false && expressionOutput.actual != void.class; expression.input.expected = rtn ? rtnType : expressionOutput.actual; expression.input.internal = rtn; expression.cast(); - methodEscape = rtn; - loopEscape = rtn; - allEscape = rtn; - statementCount = 1; + output = new Output(); + output.methodEscape = rtn; + output.loopEscape = rtn; + output.allEscape = rtn; + output.statementCount = 1; + + return output; } @Override StatementNode write(ClassNode classNode) { ExpressionNode expressionNode = expression.cast(expression.write(classNode)); - if (methodEscape) { + if (output.methodEscape) { ReturnNode returnNode = new ReturnNode(); returnNode.setExpressionNode(expressionNode); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFor.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFor.java index 803e9f493ac69..241c50b8fe2b9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFor.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFor.java @@ -52,12 +52,14 @@ public SFor(Location location, ANode initializer, AExpression condition, AExpres } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + scope = scope.newLocalScope(); if (initializer != null) { if (initializer instanceof SDeclBlock) { - ((SDeclBlock)initializer).analyze(scriptRoot, scope); + ((SDeclBlock)initializer).analyze(scriptRoot, scope, new Input()); } else if (initializer instanceof AExpression) { AExpression initializer = (AExpression)this.initializer; @@ -110,25 +112,30 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { afterthought.cast(); } + output = new Output(); + if (block != null) { - block.beginLoop = true; - block.inLoop = true; + Input blockInput = new Input(); + blockInput.beginLoop = true; + blockInput.inLoop = true; - block.analyze(scriptRoot, scope); + Output blockOutput = block.analyze(scriptRoot, scope, blockInput); - if (block.loopEscape && !block.anyContinue) { + if (blockOutput.loopEscape && blockOutput.anyContinue == false) { throw createError(new IllegalArgumentException("Extraneous for loop.")); } - if (continuous && !block.anyBreak) { - methodEscape = true; - allEscape = true; + if (continuous && blockOutput.anyBreak == false) { + output.methodEscape = true; + output.allEscape = true; } - block.statementCount = Math.max(1, block.statementCount); + blockOutput.statementCount = Math.max(1, blockOutput.statementCount); } - statementCount = 1; + output.statementCount = 1; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index 2ae9ef8d5d78f..96bf040099a17 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -30,6 +30,8 @@ import org.elasticsearch.painless.ir.ReturnNode; import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.lookup.PainlessLookupUtility; +import org.elasticsearch.painless.node.AStatement.Input; +import org.elasticsearch.painless.node.AStatement.Output; import org.elasticsearch.painless.symbol.ScriptRoot; import java.lang.invoke.MethodType; @@ -134,9 +136,10 @@ void analyze(ScriptRoot scriptRoot) { throw createError(new IllegalArgumentException("Cannot generate an empty function [" + name + "].")); } - block.lastSource = true; - block.analyze(scriptRoot, functionScope.newLocalScope()); - methodEscape = block.methodEscape; + Input blockInput = new Input(); + blockInput.lastSource = true; + Output blockOutput = block.analyze(scriptRoot, functionScope.newLocalScope(), blockInput); + methodEscape = blockOutput.methodEscape; if (methodEscape == false && isAutoReturnEnabled == false && returnType != void.class) { throw createError(new IllegalArgumentException("not all paths provide a return value " + diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIf.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIf.java index 518c5dbac9e09..a7d738670ec6f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIf.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIf.java @@ -43,7 +43,10 @@ public SIf(Location location, AExpression condition, SBlock ifblock) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + AExpression.Input conditionInput = new AExpression.Input(); conditionInput.expected = boolean.class; condition.analyze(scriptRoot, scope, conditionInput); @@ -57,15 +60,18 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { throw createError(new IllegalArgumentException("Extraneous if statement.")); } - ifblock.lastSource = lastSource; - ifblock.inLoop = inLoop; - ifblock.lastLoop = lastLoop; + Input ifblockInput = new Input(); + ifblockInput.lastSource = input.lastSource; + ifblockInput.inLoop = input.inLoop; + ifblockInput.lastLoop = input.lastLoop; + + Output ifblockOutput = ifblock.analyze(scriptRoot, scope.newLocalScope(), ifblockInput); - ifblock.analyze(scriptRoot, scope.newLocalScope()); + output.anyContinue = ifblockOutput.anyContinue; + output.anyBreak = ifblockOutput.anyBreak; + output.statementCount = ifblockOutput.statementCount; - anyContinue = ifblock.anyContinue; - anyBreak = ifblock.anyBreak; - statementCount = ifblock.statementCount; + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIfElse.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIfElse.java index d45a18f8d0eac..f433db860ccfb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIfElse.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SIfElse.java @@ -48,7 +48,10 @@ public SIfElse(Location location, AExpression condition, SBlock ifblock, SBlock } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + AExpression.Input conditionInput = new AExpression.Input(); conditionInput.expected = boolean.class; condition.analyze(scriptRoot, scope, conditionInput); @@ -62,32 +65,36 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { throw createError(new IllegalArgumentException("Extraneous if statement.")); } - ifblock.lastSource = lastSource; - ifblock.inLoop = inLoop; - ifblock.lastLoop = lastLoop; + Input ifblockInput = new Input(); + ifblockInput.lastSource = input.lastSource; + ifblockInput.inLoop = input.inLoop; + ifblockInput.lastLoop = input.lastLoop; - ifblock.analyze(scriptRoot, scope.newLocalScope()); + Output ifblockOutput = ifblock.analyze(scriptRoot, scope.newLocalScope(), ifblockInput); - anyContinue = ifblock.anyContinue; - anyBreak = ifblock.anyBreak; - statementCount = ifblock.statementCount; + output.anyContinue = ifblockOutput.anyContinue; + output.anyBreak = ifblockOutput.anyBreak; + output.statementCount = ifblockOutput.statementCount; if (elseblock == null) { throw createError(new IllegalArgumentException("Extraneous else statement.")); } - elseblock.lastSource = lastSource; - elseblock.inLoop = inLoop; - elseblock.lastLoop = lastLoop; + Input elseblockInput = new Input(); + elseblockInput.lastSource = input.lastSource; + elseblockInput.inLoop = input.inLoop; + elseblockInput.lastLoop = input.lastLoop; + + Output elseblockOutput = elseblock.analyze(scriptRoot, scope.newLocalScope(), elseblockInput); - elseblock.analyze(scriptRoot, scope.newLocalScope()); + output.methodEscape = ifblockOutput.methodEscape && elseblockOutput.methodEscape; + output.loopEscape = ifblockOutput.loopEscape && elseblockOutput.loopEscape; + output.allEscape = ifblockOutput.allEscape && elseblockOutput.allEscape; + output.anyContinue |= elseblockOutput.anyContinue; + output.anyBreak |= elseblockOutput.anyBreak; + output.statementCount = Math.max(ifblockOutput.statementCount, elseblockOutput.statementCount); - methodEscape = ifblock.methodEscape && elseblock.methodEscape; - loopEscape = ifblock.loopEscape && elseblock.loopEscape; - allEscape = ifblock.allEscape && elseblock.allEscape; - anyContinue |= elseblock.anyContinue; - anyBreak |= elseblock.anyBreak; - statementCount = Math.max(ifblock.statementCount, elseblock.statementCount); + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java index aa4a198d8efdf..23a5d4183edbc 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java @@ -40,7 +40,10 @@ public SReturn(Location location, AExpression expression) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + if (expression == null) { if (scope.getReturnType() != void.class) { throw location.createError(new ClassCastException("Cannot cast from " + @@ -55,11 +58,13 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { expression.cast(); } - methodEscape = true; - loopEscape = true; - allEscape = true; + output.methodEscape = true; + output.loopEscape = true; + output.allEscape = true; + + output.statementCount = 1; - statementCount = 1; + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java index 32aca4587c311..5e1846457d515 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java @@ -52,13 +52,18 @@ final class SSubEachArray extends AStatement { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + // We must store the array and index as variables for securing slots on the stack, and // also add the location offset to make the names unique in case of nested for each loops. array = scope.defineInternalVariable(location, expression.output.actual, "array" + location.getOffset(), true); index = scope.defineInternalVariable(location, int.class, "index" + location.getOffset(), true); indexed = expression.output.actual.getComponentType(); cast = AnalyzerCaster.getLegalCast(location, indexed, variable.getType(), true, true); + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java index cb18b898dc357..4198452ff8e79 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java @@ -57,7 +57,10 @@ final class SSubEachIterable extends AStatement { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + // We must store the iterator as a variable for securing a slot on the stack, and // also add the location offset to make the name unique in case of nested for each loops. iterator = scope.defineInternalVariable(location, Iterator.class, "itr" + location.getOffset(), true); @@ -74,6 +77,8 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { } cast = AnalyzerCaster.getLegalCast(location, def.class, variable.getType(), true, true); + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SThrow.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SThrow.java index d897ba8653d1f..86b40fafa378b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SThrow.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SThrow.java @@ -41,16 +41,21 @@ public SThrow(Location location, AExpression expression) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + AExpression.Input expressionInput = new AExpression.Input(); expressionInput.expected = Exception.class; expression.analyze(scriptRoot, scope, expressionInput); expression.cast(); - methodEscape = true; - loopEscape = true; - allEscape = true; - statementCount = 1; + output.methodEscape = true; + output.loopEscape = true; + output.allEscape = true; + output.statementCount = 1; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/STry.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/STry.java index 6c73b625a64ac..7798fd13b13a2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/STry.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/STry.java @@ -46,42 +46,49 @@ public STry(Location location, SBlock block, List catches) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + if (block == null) { throw createError(new IllegalArgumentException("Extraneous try statement.")); } - block.lastSource = lastSource; - block.inLoop = inLoop; - block.lastLoop = lastLoop; + Input blockInput = new Input(); + blockInput.lastSource = input.lastSource; + blockInput.inLoop = input.inLoop; + blockInput.lastLoop = input.lastLoop; - block.analyze(scriptRoot, scope.newLocalScope()); + Output blockOutput = block.analyze(scriptRoot, scope.newLocalScope(), blockInput); - methodEscape = block.methodEscape; - loopEscape = block.loopEscape; - allEscape = block.allEscape; - anyContinue = block.anyContinue; - anyBreak = block.anyBreak; + output.methodEscape = blockOutput.methodEscape; + output.loopEscape = blockOutput.loopEscape; + output.allEscape = blockOutput.allEscape; + output.anyContinue = blockOutput.anyContinue; + output.anyBreak = blockOutput.anyBreak; int statementCount = 0; for (SCatch catc : catches) { - catc.lastSource = lastSource; - catc.inLoop = inLoop; - catc.lastLoop = lastLoop; + Input catchInput = new Input(); + catchInput.lastSource = input.lastSource; + catchInput.inLoop = input.inLoop; + catchInput.lastLoop = input.lastLoop; - catc.analyze(scriptRoot, scope.newLocalScope()); + Output catchOutput = catc.analyze(scriptRoot, scope.newLocalScope(), catchInput); - methodEscape &= catc.methodEscape; - loopEscape &= catc.loopEscape; - allEscape &= catc.allEscape; - anyContinue |= catc.anyContinue; - anyBreak |= catc.anyBreak; + output.methodEscape &= catchOutput.methodEscape; + output.loopEscape &= catchOutput.loopEscape; + output.allEscape &= catchOutput.allEscape; + output.anyContinue |= catchOutput.anyContinue; + output.anyBreak |= catchOutput.anyBreak; - statementCount = Math.max(statementCount, catc.statementCount); + statementCount = Math.max(statementCount, catchOutput.statementCount); } - this.statementCount = block.statementCount + statementCount; + output.statementCount = blockOutput.statementCount + statementCount; + + return output; } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SWhile.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SWhile.java index 0ba07f1d5a5a2..eead51786e2d3 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SWhile.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SWhile.java @@ -45,7 +45,10 @@ public SWhile(Location location, AExpression condition, SBlock block) { } @Override - void analyze(ScriptRoot scriptRoot, Scope scope) { + Output analyze(ScriptRoot scriptRoot, Scope scope, Input input) { + this.input = input; + output = new Output(); + scope = scope.newLocalScope(); AExpression.Input conditionInput = new AExpression.Input(); @@ -66,24 +69,27 @@ void analyze(ScriptRoot scriptRoot, Scope scope) { } if (block != null) { - block.beginLoop = true; - block.inLoop = true; + Input blockInput = new Input(); + blockInput.beginLoop = true; + blockInput.inLoop = true; - block.analyze(scriptRoot, scope); + Output blockOutput = block.analyze(scriptRoot, scope, blockInput); - if (block.loopEscape && !block.anyContinue) { + if (blockOutput.loopEscape && blockOutput.anyContinue == false) { throw createError(new IllegalArgumentException("Extraneous while loop.")); } - if (continuous && !block.anyBreak) { - methodEscape = true; - allEscape = true; + if (continuous && blockOutput.anyBreak == false) { + output.methodEscape = true; + output.allEscape = true; } - block.statementCount = Math.max(1, block.statementCount); + blockOutput.statementCount = Math.max(1, blockOutput.statementCount); } - statementCount = 1; + output.statementCount = 1; + + return output; } @Override