From f98be6fa062795a5421102d90f3dd4cff58bb3a1 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 10 Jun 2016 13:59:59 -0700 Subject: [PATCH] Add functions to Painless. Closes #18810 --- .../src/main/antlr/PainlessParser.g4 | 11 +- .../org/elasticsearch/painless/Analyzer.java | 37 - .../org/elasticsearch/painless/Compiler.java | 26 +- .../elasticsearch/painless/Definition.java | 30 +- .../painless/{Variables.java => Locals.java} | 175 ++- .../elasticsearch/painless/MethodWriter.java | 8 +- .../org/elasticsearch/painless/Writer.java | 144 --- .../painless/WriterConstants.java | 8 +- .../painless/antlr/PainlessParser.java | 1072 ++++++++++------- .../antlr/PainlessParserBaseVisitor.java | 21 + .../painless/antlr/PainlessParserVisitor.java | 19 + .../elasticsearch/painless/antlr/Walker.java | 111 +- .../painless/node/AExpression.java | 12 +- .../elasticsearch/painless/node/ALink.java | 4 +- .../painless/node/AStatement.java | 4 +- .../elasticsearch/painless/node/EBinary.java | 48 +- .../elasticsearch/painless/node/EBool.java | 12 +- .../elasticsearch/painless/node/EBoolean.java | 4 +- .../elasticsearch/painless/node/ECast.java | 4 +- .../elasticsearch/painless/node/EChain.java | 16 +- .../elasticsearch/painless/node/EComp.java | 36 +- .../painless/node/EConditional.java | 16 +- .../painless/node/EConstant.java | 4 +- .../elasticsearch/painless/node/EDecimal.java | 4 +- .../painless/node/EExplicit.java | 12 +- .../painless/node/EFunctionRef.java | 34 +- .../elasticsearch/painless/node/ENull.java | 4 +- .../elasticsearch/painless/node/ENumeric.java | 4 +- .../elasticsearch/painless/node/EUnary.java | 20 +- .../painless/node/LArrayLength.java | 4 +- .../elasticsearch/painless/node/LBrace.java | 14 +- .../node/{LCall.java => LCallInvoke.java} | 26 +- .../painless/node/LCallLocal.java | 99 ++ .../elasticsearch/painless/node/LCast.java | 4 +- .../painless/node/LDefArray.java | 8 +- .../elasticsearch/painless/node/LDefCall.java | 10 +- .../painless/node/LDefField.java | 4 +- .../elasticsearch/painless/node/LField.java | 16 +- .../painless/node/LListShortcut.java | 8 +- .../painless/node/LMapShortcut.java | 8 +- .../painless/node/LNewArray.java | 8 +- .../elasticsearch/painless/node/LNewObj.java | 8 +- .../painless/node/LShortcut.java | 4 +- .../elasticsearch/painless/node/LStatic.java | 4 +- .../elasticsearch/painless/node/LString.java | 4 +- .../painless/node/LVariable.java | 8 +- .../elasticsearch/painless/node/SBlock.java | 6 +- .../elasticsearch/painless/node/SBreak.java | 4 +- .../elasticsearch/painless/node/SCatch.java | 10 +- .../painless/node/SContinue.java | 4 +- .../painless/node/SDeclBlock.java | 6 +- .../painless/node/SDeclaration.java | 12 +- .../org/elasticsearch/painless/node/SDo.java | 22 +- .../elasticsearch/painless/node/SEach.java | 44 +- .../painless/node/SExpression.java | 18 +- .../org/elasticsearch/painless/node/SFor.java | 29 +- .../painless/node/SFunction.java | 163 +++ .../org/elasticsearch/painless/node/SIf.java | 14 +- .../elasticsearch/painless/node/SIfElse.java | 20 +- .../elasticsearch/painless/node/SReturn.java | 11 +- .../elasticsearch/painless/node/SSource.java | 151 ++- .../elasticsearch/painless/node/SThrow.java | 8 +- .../org/elasticsearch/painless/node/STry.java | 16 +- .../elasticsearch/painless/node/SWhile.java | 22 +- .../painless/node/package-info.java | 4 +- .../elasticsearch/painless/FunctionTests.java | 69 ++ .../painless/ReservedWordTests.java | 8 +- 67 files changed, 1723 insertions(+), 1055 deletions(-) delete mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java rename modules/lang-painless/src/main/java/org/elasticsearch/painless/{Variables.java => Locals.java} (52%) delete mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java rename modules/lang-painless/src/main/java/org/elasticsearch/painless/node/{LCall.java => LCallInvoke.java} (79%) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallLocal.java create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java create mode 100644 modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionTests.java diff --git a/modules/lang-painless/src/main/antlr/PainlessParser.g4 b/modules/lang-painless/src/main/antlr/PainlessParser.g4 index 141ff25e318..31b6a9445ea 100644 --- a/modules/lang-painless/src/main/antlr/PainlessParser.g4 +++ b/modules/lang-painless/src/main/antlr/PainlessParser.g4 @@ -22,7 +22,15 @@ parser grammar PainlessParser; options { tokenVocab=PainlessLexer; } source - : statement* EOF + : function* statement* EOF + ; + +function + : decltype ID parameters block + ; + +parameters + : LP ( decltype ID ( COMMA decltype ID )* )? RP ; // Note we use a predicate on the if/else case here to prevent the @@ -143,6 +151,7 @@ primary[boolean c] returns [boolean s = true] | { $c }? LP unary[true] RP # chainprec | STRING # string | ID # variable + | ID arguments # calllocal | NEW TYPE arguments # newobject ; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java deleted file mode 100644 index a06bcdf9840..00000000000 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Analyzer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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; - -import org.elasticsearch.painless.Variables.Reserved; -import org.elasticsearch.painless.node.SSource; - -/** - * Runs the analysis phase of compilation using the Painless AST. - */ -final class Analyzer { - static Variables analyze(Reserved shortcut, SSource root) { - Variables variables = new Variables(shortcut); - root.analyze(variables); - - return variables; - } - - private Analyzer() {} -} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index d0a17f64a48..8fa9f5d583e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -20,7 +20,6 @@ package org.elasticsearch.painless; import org.elasticsearch.bootstrap.BootstrapInfo; -import org.elasticsearch.painless.Variables.Reserved; import org.elasticsearch.painless.antlr.Walker; import org.elasticsearch.painless.node.SSource; @@ -36,8 +35,7 @@ import static org.elasticsearch.painless.WriterConstants.CLASS_NAME; /** * The Compiler is the entry point for generating a Painless script. The compiler will receive a Painless * tree based on the type of input passed in (currently only ANTLR). Two passes will then be run over the tree, - * one for analysis using the {@link Analyzer} and another to generate the actual byte code using ASM in - * the {@link Writer}. + * one for analysis and another to generate the actual byte code using ASM using the root of the tree {@link SSource}. */ final class Compiler { @@ -100,18 +98,17 @@ final class Compiler { " plugin if a script longer than this length is a requirement."); } - Reserved reserved = new Reserved(); - SSource root = Walker.buildPainlessTree(name, source, reserved, settings); - Variables variables = Analyzer.analyze(reserved, root); - BitSet expressions = new BitSet(source.length()); - byte[] bytes = Writer.write(settings, name, source, variables, root, expressions); + SSource root = Walker.buildPainlessTree(name, source, settings); + + root.analyze(); + root.write(); try { - Class clazz = loader.define(CLASS_NAME, bytes); + Class clazz = loader.define(CLASS_NAME, root.getBytes()); java.lang.reflect.Constructor constructor = clazz.getConstructor(String.class, String.class, BitSet.class); - return constructor.newInstance(name, source, expressions); + return constructor.newInstance(name, source, root.getExpressions()); } catch (Exception exception) { // Catch everything to let the user know this is something caused internally. throw new IllegalStateException("An internal error occurred attempting to define the script [" + name + "].", exception); } @@ -130,11 +127,12 @@ final class Compiler { " plugin if a script longer than this length is a requirement."); } - Reserved reserved = new Reserved(); - SSource root = Walker.buildPainlessTree(name, source, reserved, settings); - Variables variables = Analyzer.analyze(reserved, root); + SSource root = Walker.buildPainlessTree(name, source, settings); - return Writer.write(settings, name, source, variables, root, new BitSet(source.length())); + root.analyze(); + root.write(); + + return root.getBytes(); } /** diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java index dbfc9993c31..a8d4a803361 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java @@ -45,9 +45,9 @@ import java.util.Spliterator; * methods and fields during at both compile-time and runtime. */ public final class Definition { - + private static final List DEFINITION_FILES = Collections.unmodifiableList( - Arrays.asList("org.elasticsearch.txt", + Arrays.asList("org.elasticsearch.txt", "java.lang.txt", "java.math.txt", "java.text.txt", @@ -188,8 +188,8 @@ public final class Definition { public final int modifiers; public final MethodHandle handle; - private Method(String name, Struct owner, Type rtn, List arguments, - org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) { + public Method(String name, Struct owner, Type rtn, List arguments, + org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) { this.name = name; this.owner = owner; this.rtn = rtn; @@ -286,7 +286,7 @@ public final class Definition { public final Map staticMembers; public final Map members; - + private final SetOnce functionalMethod; private Struct(final String name, final Class clazz, final org.objectweb.asm.Type type) { @@ -300,8 +300,8 @@ public final class Definition { staticMembers = new HashMap<>(); members = new HashMap<>(); - - functionalMethod = new SetOnce(); + + functionalMethod = new SetOnce<>(); } private Struct(final Struct struct) { @@ -315,7 +315,7 @@ public final class Definition { staticMembers = Collections.unmodifiableMap(struct.staticMembers); members = Collections.unmodifiableMap(struct.members); - + functionalMethod = struct.functionalMethod; } @@ -342,8 +342,8 @@ public final class Definition { public int hashCode() { return name.hashCode(); } - - /** + + /** * If this class is a functional interface according to JLS, returns its method. * Otherwise returns null. */ @@ -637,7 +637,7 @@ public final class Definition { final org.objectweb.asm.commons.Method asm = org.objectweb.asm.commons.Method.getMethod(reflect); final Type returnType = getTypeInternal("void"); final MethodHandle handle; - + try { handle = MethodHandles.publicLookup().in(owner.clazz).unreflectConstructor(reflect); } catch (final IllegalAccessException exception) { @@ -645,7 +645,7 @@ public final class Definition { " not found for class [" + owner.clazz.getName() + "]" + " with arguments " + Arrays.toString(classes) + "."); } - + final Method constructor = new Method(name, owner, returnType, Arrays.asList(args), asm, reflect.getModifiers(), handle); owner.constructors.put(methodKey, constructor); @@ -755,7 +755,7 @@ public final class Definition { " method [" + name + "]" + " within the struct [" + owner.name + "]."); } - + final org.objectweb.asm.commons.Method asm = org.objectweb.asm.commons.Method.getMethod(reflect); MethodHandle handle; @@ -856,7 +856,7 @@ public final class Definition { throw new ClassCastException("Child struct [" + child.name + "]" + " is not a super type of owner struct [" + owner.name + "] in copy."); } - + for (Map.Entry kvPair : child.methods.entrySet()) { MethodKey methodKey = kvPair.getKey(); Method method = kvPair.getValue(); @@ -954,7 +954,7 @@ public final class Definition { runtimeMap.put(struct.clazz, new RuntimeClass(methods, getters, setters)); } - + /** computes the functional interface method for a class, or returns null */ private Method computeFunctionalInterfaceMethod(Struct clazz) { if (!clazz.clazz.isInterface()) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Variables.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java similarity index 52% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/Variables.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java index 4905011520a..d102114bdeb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Variables.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java @@ -19,16 +19,21 @@ package org.elasticsearch.painless; +import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.Definition.MethodKey; import org.elasticsearch.painless.Definition.Type; import java.util.ArrayDeque; +import java.util.Collections; import java.util.Deque; import java.util.Iterator; +import java.util.List; +import java.util.Map; /** - * Tracks variables across compilation phases. + * Tracks user defined methods and variables across compilation phases. */ -public final class Variables { +public final class Locals { /** * Tracks reserved variables. Must be given to any source of input @@ -36,7 +41,15 @@ public final class Variables { * are known ahead of time to assign appropriate slots without * being wasteful. */ - public static final class Reserved { + public interface Reserved { + void markReserved(String name); + boolean isReserved(String name); + + void setMaxLoopCounter(int max); + int getMaxLoopCounter(); + } + + public static final class ExecuteReserved implements Reserved { public static final String THIS = "#this"; public static final String PARAMS = "params"; public static final String SCORER = "#scorer"; @@ -46,10 +59,11 @@ public final class Variables { public static final String CTX = "ctx"; public static final String LOOP = "#loop"; - boolean score = false; - boolean ctx = false; - boolean loop = false; + private boolean score = false; + private boolean ctx = false; + private int maxLoopCounter = 0; + @Override public void markReserved(String name) { if (SCORE.equals(name)) { score = true; @@ -58,13 +72,53 @@ public final class Variables { } } + @Override public boolean isReserved(String name) { return name.equals(THIS) || name.equals(PARAMS) || name.equals(SCORER) || name.equals(DOC) || name.equals(VALUE) || name.equals(SCORE) || name.equals(CTX) || name.equals(LOOP); - } + } - public void usesLoop() { - loop = true; + public boolean usesScore() { + return score; + } + + public boolean usesCtx() { + return ctx; + } + + @Override + public void setMaxLoopCounter(int max) { + maxLoopCounter = max; + } + + @Override + public int getMaxLoopCounter() { + return maxLoopCounter; + } + } + + public static final class FunctionReserved implements Reserved { + public static final String THIS = "#this"; + public static final String LOOP = "#loop"; + + private int maxLoopCounter = 0; + + public void markReserved(String name) { + // Do nothing. + } + + public boolean isReserved(String name) { + return name.equals(THIS) || name.equals(LOOP); + } + + @Override + public void setMaxLoopCounter(int max) { + maxLoopCounter = max; + } + + @Override + public int getMaxLoopCounter() { + return maxLoopCounter; } } @@ -86,52 +140,97 @@ public final class Variables { } } - final Reserved reserved; + public static final class Parameter { + public final Location location; + public final String name; + public final Type type; + + public Parameter(Location location, String name, Type type) { + this.location = location; + this.name = name; + this.type = type; + } + } + + private final Reserved reserved; + private final Map methods; + private final Type rtnType; // TODO: this datastructure runs in linear time for nearly all operations. use linkedhashset instead? private final Deque scopes = new ArrayDeque<>(); private final Deque variables = new ArrayDeque<>(); - public Variables(Reserved reserved) { + public Locals(ExecuteReserved reserved, Map methods) { this.reserved = reserved; + this.methods = Collections.unmodifiableMap(methods); + this.rtnType = Definition.OBJECT_TYPE; incrementScope(); // Method variables. // This reference. Internal use only. - addVariable(null, Definition.getType("Object"), Reserved.THIS, true, true); + addVariable(null, Definition.getType("Object"), ExecuteReserved.THIS, true, true); // Input map of variables passed to the script. - addVariable(null, Definition.getType("Map"), Reserved.PARAMS, true, true); + addVariable(null, Definition.getType("Map"), ExecuteReserved.PARAMS, true, true); // Scorer parameter passed to the script. Internal use only. - addVariable(null, Definition.DEF_TYPE, Reserved.SCORER, true, true); + addVariable(null, Definition.DEF_TYPE, ExecuteReserved.SCORER, true, true); // Doc parameter passed to the script. TODO: Currently working as a Map, we can do better? - addVariable(null, Definition.getType("Map"), Reserved.DOC, true, true); + addVariable(null, Definition.getType("Map"), ExecuteReserved.DOC, true, true); // Aggregation _value parameter passed to the script. - addVariable(null, Definition.DEF_TYPE, Reserved.VALUE, true, true); + addVariable(null, Definition.DEF_TYPE, ExecuteReserved.VALUE, true, true); // Shortcut variables. // Document's score as a read-only double. - if (reserved.score) { - addVariable(null, Definition.DOUBLE_TYPE, Reserved.SCORE, true, true); + if (reserved.usesScore()) { + addVariable(null, Definition.DOUBLE_TYPE, ExecuteReserved.SCORE, true, true); } // The ctx map set by executable scripts as a read-only map. - if (reserved.ctx) { - addVariable(null, Definition.getType("Map"), Reserved.CTX, true, true); + if (reserved.usesCtx()) { + addVariable(null, Definition.getType("Map"), ExecuteReserved.CTX, true, true); } // Loop counter to catch infinite loops. Internal use only. - if (reserved.loop) { - addVariable(null, Definition.INT_TYPE, Reserved.LOOP, true, true); + if (reserved.getMaxLoopCounter() > 0) { + addVariable(null, Definition.INT_TYPE, ExecuteReserved.LOOP, true, true); } } + public Locals(FunctionReserved reserved, Locals locals, Type rtnType, List parameters) { + this.reserved = reserved; + this.methods = locals.methods; + this.rtnType = rtnType; + + incrementScope(); + + for (Parameter parameter : parameters) { + addVariable(parameter.location, parameter.type, parameter.name, false, false); + } + + // Loop counter to catch infinite loops. Internal use only. + if (reserved.getMaxLoopCounter() > 0) { + addVariable(null, Definition.INT_TYPE, ExecuteReserved.LOOP, true, true); + } + } + + public int getMaxLoopCounter() { + return reserved.getMaxLoopCounter(); + } + + public Method getMethod(MethodKey key) { + return methods.get(key); + } + + public Type getReturnType() { + return rtnType; + } + public void incrementScope() { scopes.push(0); } @@ -142,9 +241,9 @@ public final class Variables { while (remove > 0) { Variable variable = variables.pop(); - // TODO: is this working? the code reads backwards... + // This checks whether or not a variable is used when exiting a local scope. if (variable.read) { - throw variable.location.createError(new IllegalArgumentException("Variable [" + variable.name + "] never used.")); + throw variable.location.createError(new IllegalArgumentException("Variable [" + variable.name + "] is never used.")); } --remove; @@ -162,26 +261,30 @@ public final class Variables { } } - throw location.createError(new IllegalArgumentException("Variable [" + name + "] not defined.")); + throw location.createError(new IllegalArgumentException("Variable [" + name + "] is not defined.")); } - private boolean variableExists(String name) { - return variables.contains(name); + public boolean isVariable(String name) { + Iterator itr = variables.iterator(); + + while (itr.hasNext()) { + Variable variable = itr.next(); + + if (variable.name.equals(name)) { + return true; + } + } + + return false; } public Variable addVariable(Location location, Type type, String name, boolean readonly, boolean reserved) { if (!reserved && this.reserved.isReserved(name)) { - throw location.createError(new IllegalArgumentException("Variable name [" + name + "] is reserved.")); + throw location.createError(new IllegalArgumentException("Variable [" + name + "] is reserved.")); } - if (variableExists(name)) { - throw new IllegalArgumentException("Variable name [" + name + "] already defined."); - } - - try { - Definition.getType(name); - } catch (IllegalArgumentException exception) { - // Do nothing. + if (isVariable(name)) { + throw location.createError(new IllegalArgumentException("Variable [" + name + "] is already defined.")); } Variable previous = variables.peekFirst(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java index 7b7caec320e..923c2e3f1e8 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/MethodWriter.java @@ -91,10 +91,10 @@ public final class MethodWriter extends GeneratorAdapter { private final ClassWriter parent; private final BitSet statements; - private final Deque> stringConcatArgs = (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? - null : new ArrayDeque<>(); + private final Deque> stringConcatArgs = + (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE == null) ? null : new ArrayDeque<>(); - MethodWriter(int access, Method method, ClassWriter cw, BitSet statements) { + public MethodWriter(int access, Method method, ClassWriter cw, BitSet statements) { super(Opcodes.ASM5, cw.visitMethod(access, method.getName(), method.getDescriptor(), null, null), access, method.getName(), method.getDescriptor()); @@ -105,7 +105,7 @@ public final class MethodWriter extends GeneratorAdapter { /** * @return A new {@link MethodWriter} with the specified access and signature. */ - MethodWriter newMethodWriter(int access, Method method) { + public MethodWriter newMethodWriter(int access, Method method) { return new MethodWriter(access, method, parent, statements); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java deleted file mode 100644 index 4d4a778d558..00000000000 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Writer.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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; - -import org.elasticsearch.painless.Variables.Reserved; -import org.elasticsearch.painless.Variables.Variable; -import org.elasticsearch.painless.node.SSource; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.commons.GeneratorAdapter; - -import static org.elasticsearch.painless.WriterConstants.BASE_CLASS_TYPE; -import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; -import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR; -import static org.elasticsearch.painless.WriterConstants.EXECUTE; -import static org.elasticsearch.painless.WriterConstants.MAP_GET; -import static org.elasticsearch.painless.WriterConstants.MAP_TYPE; - -import java.util.BitSet; - -/** - * Runs the writing phase of compilation using the Painless AST. - */ -final class Writer { - - static byte[] write(CompilerSettings settings, String name, String source, Variables variables, SSource root, BitSet expressions) { - return new Writer(settings, name, source, variables, root, expressions).getBytes(); - } - - private final CompilerSettings settings; - private final String scriptName; - private final String source; - private final Variables variables; - private final SSource root; - - private final ClassWriter writer; - private final MethodWriter adapter; - - private Writer(CompilerSettings settings, String name, String source, Variables variables, SSource root, BitSet expressions) { - this.settings = settings; - this.scriptName = name; - this.source = source; - this.variables = variables; - this.root = root; - - writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - - writeBegin(); - writeConstructor(); - - adapter = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, writer, expressions); - - writeExecute(); - writeEnd(); - } - - private void writeBegin() { - final int version = Opcodes.V1_8; - final int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL; - final String base = BASE_CLASS_TYPE.getInternalName(); - final String name = CLASS_TYPE.getInternalName(); - - // apply marker interface NeedsScore if we use the score! - final String interfaces[] = variables.reserved.score ? - new String[] { WriterConstants.NEEDS_SCORE_TYPE.getInternalName() } : null; - - writer.visit(version, access, name, null, base, interfaces); - writer.visitSource(Location.computeSourceName(scriptName,source), null); - } - - private void writeConstructor() { - final GeneratorAdapter constructor = new GeneratorAdapter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, null, null, writer); - constructor.loadThis(); - constructor.loadArgs(); - constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR); - constructor.returnValue(); - constructor.endMethod(); - } - - private void writeExecute() { - if (variables.reserved.score) { - // if the _score value is used, we do this once: - // final double _score = scorer.score(); - final Variable scorer = variables.getVariable(null, Reserved.SCORER); - final Variable score = variables.getVariable(null, Reserved.SCORE); - - adapter.visitVarInsn(Opcodes.ALOAD, scorer.slot); - adapter.invokeVirtual(WriterConstants.SCORER_TYPE, WriterConstants.SCORER_SCORE); - adapter.visitInsn(Opcodes.F2D); - adapter.visitVarInsn(Opcodes.DSTORE, score.slot); - } - - if (variables.reserved.ctx) { - // if the _ctx value is used, we do this once: - // final Map ctx = input.get("ctx"); - - final Variable input = variables.getVariable(null, Reserved.PARAMS); - final Variable ctx = variables.getVariable(null, Reserved.CTX); - - adapter.visitVarInsn(Opcodes.ALOAD, input.slot); - adapter.push(Reserved.CTX); - adapter.invokeInterface(MAP_TYPE, MAP_GET); - adapter.visitVarInsn(Opcodes.ASTORE, ctx.slot); - } - - if (variables.reserved.loop) { - // if there is infinite loop protection, we do this once: - // int #loop = settings.getMaxLoopCounter() - - final Variable loop = variables.getVariable(null, Reserved.LOOP); - - adapter.push(settings.getMaxLoopCounter()); - adapter.visitVarInsn(Opcodes.ISTORE, loop.slot); - } - - root.write(adapter); - adapter.endMethod(); - } - - private void writeEnd() { - writer.visitEnd(); - } - - private byte[] getBytes() { - return writer.toByteArray(); - } -} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index 80082c4487b..03879a0a3a0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -107,15 +107,15 @@ public final class WriterConstants { public final static Method DEF_LTE_CALL = getAsmMethod(boolean.class, "lte", Object.class, Object.class); public final static Method DEF_GT_CALL = getAsmMethod(boolean.class, "gt" , Object.class, Object.class); public final static Method DEF_GTE_CALL = getAsmMethod(boolean.class, "gte", Object.class, Object.class); - + /** invokedynamic bootstrap for lambda expression/method references */ public final static MethodType LAMBDA_BOOTSTRAP_TYPE = - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, + MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class); public final static Handle LAMBDA_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(LambdaMetafactory.class), "altMetafactory", LAMBDA_BOOTSTRAP_TYPE.toMethodDescriptorString()); - + /** dynamic invokedynamic bootstrap for indy string concats (Java 9+) */ public final static Handle INDY_STRING_CONCAT_BOOTSTRAP_HANDLE; static { @@ -152,7 +152,7 @@ public final class WriterConstants { public final static Method CHECKEQUALS = getAsmMethod(boolean.class, "checkEquals", Object.class, Object.class); - public static Method getAsmMethod(final Class rtype, final String name, final Class... ptypes) { + private static Method getAsmMethod(final Class rtype, final String name, final Class... ptypes) { return new Method(name, MethodType.methodType(rtype, ptypes).toMethodDescriptorString()); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java index 540773fbf70..acaf00a5765 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java @@ -28,17 +28,17 @@ class PainlessParser extends Parser { STRING=68, TRUE=69, FALSE=70, NULL=71, TYPE=72, ID=73, DOTINTEGER=74, DOTID=75; public static final int - RULE_source = 0, RULE_statement = 1, RULE_trailer = 2, RULE_block = 3, - RULE_empty = 4, RULE_initializer = 5, RULE_afterthought = 6, RULE_declaration = 7, - RULE_decltype = 8, RULE_funcref = 9, RULE_declvar = 10, RULE_trap = 11, - RULE_delimiter = 12, RULE_expression = 13, RULE_unary = 14, RULE_chain = 15, - RULE_primary = 16, RULE_secondary = 17, RULE_dot = 18, RULE_brace = 19, - RULE_arguments = 20, RULE_argument = 21; + RULE_source = 0, RULE_function = 1, RULE_parameters = 2, RULE_statement = 3, + RULE_trailer = 4, RULE_block = 5, RULE_empty = 6, RULE_initializer = 7, + RULE_afterthought = 8, RULE_declaration = 9, RULE_decltype = 10, RULE_funcref = 11, + RULE_declvar = 12, RULE_trap = 13, RULE_delimiter = 14, RULE_expression = 15, + RULE_unary = 16, RULE_chain = 17, RULE_primary = 18, RULE_secondary = 19, + RULE_dot = 20, RULE_brace = 21, RULE_arguments = 22, RULE_argument = 23; public static final String[] ruleNames = { - "source", "statement", "trailer", "block", "empty", "initializer", "afterthought", - "declaration", "decltype", "funcref", "declvar", "trap", "delimiter", - "expression", "unary", "chain", "primary", "secondary", "dot", "brace", - "arguments", "argument" + "source", "function", "parameters", "statement", "trailer", "block", "empty", + "initializer", "afterthought", "declaration", "decltype", "funcref", "declvar", + "trap", "delimiter", "expression", "unary", "chain", "primary", "secondary", + "dot", "brace", "arguments", "argument" }; private static final String[] _LITERAL_NAMES = { @@ -113,6 +113,12 @@ class PainlessParser extends Parser { } public static class SourceContext extends ParserRuleContext { public TerminalNode EOF() { return getToken(PainlessParser.EOF, 0); } + public List function() { + return getRuleContexts(FunctionContext.class); + } + public FunctionContext function(int i) { + return getRuleContext(FunctionContext.class,i); + } public List statement() { return getRuleContexts(StatementContext.class); } @@ -137,23 +143,39 @@ class PainlessParser extends Parser { int _alt; enterOuterAlt(_localctx, 1); { - setState(47); + setState(51); _errHandler.sync(this); _alt = getInterpreter().adaptivePredict(_input,0,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(44); + setState(48); + function(); + } + } + } + setState(53); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,0,_ctx); + } + setState(57); + _errHandler.sync(this); + _alt = getInterpreter().adaptivePredict(_input,1,_ctx); + while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + setState(54); statement(); } } } - setState(49); + setState(59); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,0,_ctx); + _alt = getInterpreter().adaptivePredict(_input,1,_ctx); } - setState(50); + setState(60); match(EOF); } } @@ -168,6 +190,136 @@ class PainlessParser extends Parser { return _localctx; } + public static class FunctionContext extends ParserRuleContext { + public DecltypeContext decltype() { + return getRuleContext(DecltypeContext.class,0); + } + public TerminalNode ID() { return getToken(PainlessParser.ID, 0); } + public ParametersContext parameters() { + return getRuleContext(ParametersContext.class,0); + } + public BlockContext block() { + return getRuleContext(BlockContext.class,0); + } + public FunctionContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_function; } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof PainlessParserVisitor ) return ((PainlessParserVisitor)visitor).visitFunction(this); + else return visitor.visitChildren(this); + } + } + + public final FunctionContext function() throws RecognitionException { + FunctionContext _localctx = new FunctionContext(_ctx, getState()); + enterRule(_localctx, 2, RULE_function); + try { + enterOuterAlt(_localctx, 1); + { + setState(62); + decltype(); + setState(63); + match(ID); + setState(64); + parameters(); + setState(65); + block(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + + public static class ParametersContext extends ParserRuleContext { + public TerminalNode LP() { return getToken(PainlessParser.LP, 0); } + public TerminalNode RP() { return getToken(PainlessParser.RP, 0); } + public List decltype() { + return getRuleContexts(DecltypeContext.class); + } + public DecltypeContext decltype(int i) { + return getRuleContext(DecltypeContext.class,i); + } + public List ID() { return getTokens(PainlessParser.ID); } + public TerminalNode ID(int i) { + return getToken(PainlessParser.ID, i); + } + public List COMMA() { return getTokens(PainlessParser.COMMA); } + public TerminalNode COMMA(int i) { + return getToken(PainlessParser.COMMA, i); + } + public ParametersContext(ParserRuleContext parent, int invokingState) { + super(parent, invokingState); + } + @Override public int getRuleIndex() { return RULE_parameters; } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof PainlessParserVisitor ) return ((PainlessParserVisitor)visitor).visitParameters(this); + else return visitor.visitChildren(this); + } + } + + public final ParametersContext parameters() throws RecognitionException { + ParametersContext _localctx = new ParametersContext(_ctx, getState()); + enterRule(_localctx, 4, RULE_parameters); + int _la; + try { + enterOuterAlt(_localctx, 1); + { + setState(67); + match(LP); + setState(79); + _la = _input.LA(1); + if (_la==TYPE) { + { + setState(68); + decltype(); + setState(69); + match(ID); + setState(76); + _errHandler.sync(this); + _la = _input.LA(1); + while (_la==COMMA) { + { + { + setState(70); + match(COMMA); + setState(71); + decltype(); + setState(72); + match(ID); + } + } + setState(78); + _errHandler.sync(this); + _la = _input.LA(1); + } + } + } + + setState(81); + match(RP); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + _errHandler.reportError(this, re); + _errHandler.recover(this, re); + } + finally { + exitRule(); + } + return _localctx; + } + public static class StatementContext extends ParserRuleContext { public StatementContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -396,38 +548,38 @@ class PainlessParser extends Parser { public final StatementContext statement() throws RecognitionException { StatementContext _localctx = new StatementContext(_ctx, getState()); - enterRule(_localctx, 2, RULE_statement); + enterRule(_localctx, 6, RULE_statement); try { int _alt; - setState(130); - switch ( getInterpreter().adaptivePredict(_input,8,_ctx) ) { + setState(161); + switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) { case 1: _localctx = new IfContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(52); + setState(83); match(IF); - setState(53); + setState(84); match(LP); - setState(54); + setState(85); expression(0); - setState(55); + setState(86); match(RP); - setState(56); + setState(87); trailer(); - setState(60); - switch ( getInterpreter().adaptivePredict(_input,1,_ctx) ) { + setState(91); + switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) { case 1: { - setState(57); + setState(88); match(ELSE); - setState(58); + setState(89); trailer(); } break; case 2: { - setState(59); + setState(90); if (!( _input.LA(1) != ELSE )) throw new FailedPredicateException(this, " _input.LA(1) != ELSE "); } break; @@ -438,25 +590,25 @@ class PainlessParser extends Parser { _localctx = new WhileContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(62); + setState(93); match(WHILE); - setState(63); + setState(94); match(LP); - setState(64); + setState(95); expression(0); - setState(65); + setState(96); match(RP); - setState(68); - switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) { + setState(99); + switch ( getInterpreter().adaptivePredict(_input,5,_ctx) ) { case 1: { - setState(66); + setState(97); trailer(); } break; case 2: { - setState(67); + setState(98); empty(); } break; @@ -467,19 +619,19 @@ class PainlessParser extends Parser { _localctx = new DoContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(70); + setState(101); match(DO); - setState(71); + setState(102); block(); - setState(72); + setState(103); match(WHILE); - setState(73); + setState(104); match(LP); - setState(74); + setState(105); expression(0); - setState(75); + setState(106); match(RP); - setState(76); + setState(107); delimiter(); } break; @@ -487,54 +639,54 @@ class PainlessParser extends Parser { _localctx = new ForContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(78); + setState(109); match(FOR); - setState(79); + setState(110); match(LP); - setState(81); - switch ( getInterpreter().adaptivePredict(_input,3,_ctx) ) { + setState(112); + switch ( getInterpreter().adaptivePredict(_input,6,_ctx) ) { case 1: { - setState(80); + setState(111); initializer(); } break; } - setState(83); + setState(114); match(SEMICOLON); - setState(85); - switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) { + setState(116); + switch ( getInterpreter().adaptivePredict(_input,7,_ctx) ) { case 1: { - setState(84); + setState(115); expression(0); } break; } - setState(87); + setState(118); match(SEMICOLON); - setState(89); - switch ( getInterpreter().adaptivePredict(_input,5,_ctx) ) { + setState(120); + switch ( getInterpreter().adaptivePredict(_input,8,_ctx) ) { case 1: { - setState(88); + setState(119); afterthought(); } break; } - setState(91); + setState(122); match(RP); - setState(94); - switch ( getInterpreter().adaptivePredict(_input,6,_ctx) ) { + setState(125); + switch ( getInterpreter().adaptivePredict(_input,9,_ctx) ) { case 1: { - setState(92); + setState(123); trailer(); } break; case 2: { - setState(93); + setState(124); empty(); } break; @@ -545,21 +697,21 @@ class PainlessParser extends Parser { _localctx = new EachContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(96); + setState(127); match(FOR); - setState(97); + setState(128); match(LP); - setState(98); + setState(129); decltype(); - setState(99); + setState(130); match(ID); - setState(100); + setState(131); match(COLON); - setState(101); + setState(132); expression(0); - setState(102); + setState(133); match(RP); - setState(103); + setState(134); trailer(); } break; @@ -567,9 +719,9 @@ class PainlessParser extends Parser { _localctx = new DeclContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(105); + setState(136); declaration(); - setState(106); + setState(137); delimiter(); } break; @@ -577,9 +729,9 @@ class PainlessParser extends Parser { _localctx = new ContinueContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(108); + setState(139); match(CONTINUE); - setState(109); + setState(140); delimiter(); } break; @@ -587,9 +739,9 @@ class PainlessParser extends Parser { _localctx = new BreakContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(110); + setState(141); match(BREAK); - setState(111); + setState(142); delimiter(); } break; @@ -597,11 +749,11 @@ class PainlessParser extends Parser { _localctx = new ReturnContext(_localctx); enterOuterAlt(_localctx, 9); { - setState(112); + setState(143); match(RETURN); - setState(113); + setState(144); expression(0); - setState(114); + setState(145); delimiter(); } break; @@ -609,11 +761,11 @@ class PainlessParser extends Parser { _localctx = new TryContext(_localctx); enterOuterAlt(_localctx, 10); { - setState(116); + setState(147); match(TRY); - setState(117); + setState(148); block(); - setState(119); + setState(150); _errHandler.sync(this); _alt = 1; do { @@ -621,7 +773,7 @@ class PainlessParser extends Parser { case 1: { { - setState(118); + setState(149); trap(); } } @@ -629,9 +781,9 @@ class PainlessParser extends Parser { default: throw new NoViableAltException(this); } - setState(121); + setState(152); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,7,_ctx); + _alt = getInterpreter().adaptivePredict(_input,10,_ctx); } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); } break; @@ -639,11 +791,11 @@ class PainlessParser extends Parser { _localctx = new ThrowContext(_localctx); enterOuterAlt(_localctx, 11); { - setState(123); + setState(154); match(THROW); - setState(124); + setState(155); expression(0); - setState(125); + setState(156); delimiter(); } break; @@ -651,9 +803,9 @@ class PainlessParser extends Parser { _localctx = new ExprContext(_localctx); enterOuterAlt(_localctx, 12); { - setState(127); + setState(158); expression(0); - setState(128); + setState(159); delimiter(); } break; @@ -690,21 +842,21 @@ class PainlessParser extends Parser { public final TrailerContext trailer() throws RecognitionException { TrailerContext _localctx = new TrailerContext(_ctx, getState()); - enterRule(_localctx, 4, RULE_trailer); + enterRule(_localctx, 8, RULE_trailer); try { - setState(134); - switch ( getInterpreter().adaptivePredict(_input,9,_ctx) ) { + setState(165); + switch ( getInterpreter().adaptivePredict(_input,12,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(132); + setState(163); block(); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(133); + setState(164); statement(); } break; @@ -743,30 +895,30 @@ class PainlessParser extends Parser { public final BlockContext block() throws RecognitionException { BlockContext _localctx = new BlockContext(_ctx, getState()); - enterRule(_localctx, 6, RULE_block); + enterRule(_localctx, 10, RULE_block); try { int _alt; enterOuterAlt(_localctx, 1); { - setState(136); + setState(167); match(LBRACK); - setState(140); + setState(171); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,10,_ctx); + _alt = getInterpreter().adaptivePredict(_input,13,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(137); + setState(168); statement(); } } } - setState(142); + setState(173); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,10,_ctx); + _alt = getInterpreter().adaptivePredict(_input,13,_ctx); } - setState(143); + setState(174); match(RBRACK); } } @@ -796,11 +948,11 @@ class PainlessParser extends Parser { public final EmptyContext empty() throws RecognitionException { EmptyContext _localctx = new EmptyContext(_ctx, getState()); - enterRule(_localctx, 8, RULE_empty); + enterRule(_localctx, 12, RULE_empty); try { enterOuterAlt(_localctx, 1); { - setState(145); + setState(176); match(SEMICOLON); } } @@ -835,21 +987,21 @@ class PainlessParser extends Parser { public final InitializerContext initializer() throws RecognitionException { InitializerContext _localctx = new InitializerContext(_ctx, getState()); - enterRule(_localctx, 10, RULE_initializer); + enterRule(_localctx, 14, RULE_initializer); try { - setState(149); - switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) { + setState(180); + switch ( getInterpreter().adaptivePredict(_input,14,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(147); + setState(178); declaration(); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(148); + setState(179); expression(0); } break; @@ -883,11 +1035,11 @@ class PainlessParser extends Parser { public final AfterthoughtContext afterthought() throws RecognitionException { AfterthoughtContext _localctx = new AfterthoughtContext(_ctx, getState()); - enterRule(_localctx, 12, RULE_afterthought); + enterRule(_localctx, 16, RULE_afterthought); try { enterOuterAlt(_localctx, 1); { - setState(151); + setState(182); expression(0); } } @@ -929,28 +1081,28 @@ class PainlessParser extends Parser { public final DeclarationContext declaration() throws RecognitionException { DeclarationContext _localctx = new DeclarationContext(_ctx, getState()); - enterRule(_localctx, 14, RULE_declaration); + enterRule(_localctx, 18, RULE_declaration); int _la; try { enterOuterAlt(_localctx, 1); { - setState(153); + setState(184); decltype(); - setState(154); + setState(185); declvar(); - setState(159); + setState(190); _errHandler.sync(this); _la = _input.LA(1); while (_la==COMMA) { { { - setState(155); + setState(186); match(COMMA); - setState(156); + setState(187); declvar(); } } - setState(161); + setState(192); _errHandler.sync(this); _la = _input.LA(1); } @@ -990,26 +1142,26 @@ class PainlessParser extends Parser { public final DecltypeContext decltype() throws RecognitionException { DecltypeContext _localctx = new DecltypeContext(_ctx, getState()); - enterRule(_localctx, 16, RULE_decltype); + enterRule(_localctx, 20, RULE_decltype); int _la; try { enterOuterAlt(_localctx, 1); { - setState(162); + setState(193); match(TYPE); - setState(167); + setState(198); _errHandler.sync(this); _la = _input.LA(1); while (_la==LBRACE) { { { - setState(163); + setState(194); match(LBRACE); - setState(164); + setState(195); match(RBRACE); } } - setState(169); + setState(200); _errHandler.sync(this); _la = _input.LA(1); } @@ -1044,16 +1196,16 @@ class PainlessParser extends Parser { public final FuncrefContext funcref() throws RecognitionException { FuncrefContext _localctx = new FuncrefContext(_ctx, getState()); - enterRule(_localctx, 18, RULE_funcref); + enterRule(_localctx, 22, RULE_funcref); int _la; try { enterOuterAlt(_localctx, 1); { - setState(170); + setState(201); match(TYPE); - setState(171); + setState(202); match(REF); - setState(172); + setState(203); _la = _input.LA(1); if ( !(_la==NEW || _la==ID) ) { _errHandler.recoverInline(this); @@ -1092,20 +1244,20 @@ class PainlessParser extends Parser { public final DeclvarContext declvar() throws RecognitionException { DeclvarContext _localctx = new DeclvarContext(_ctx, getState()); - enterRule(_localctx, 20, RULE_declvar); + enterRule(_localctx, 24, RULE_declvar); int _la; try { enterOuterAlt(_localctx, 1); { - setState(174); + setState(205); match(ID); - setState(177); + setState(208); _la = _input.LA(1); if (_la==ASSIGN) { { - setState(175); + setState(206); match(ASSIGN); - setState(176); + setState(207); expression(0); } } @@ -1145,21 +1297,21 @@ class PainlessParser extends Parser { public final TrapContext trap() throws RecognitionException { TrapContext _localctx = new TrapContext(_ctx, getState()); - enterRule(_localctx, 22, RULE_trap); + enterRule(_localctx, 26, RULE_trap); try { enterOuterAlt(_localctx, 1); { - setState(179); + setState(210); match(CATCH); - setState(180); + setState(211); match(LP); - setState(181); + setState(212); match(TYPE); - setState(182); + setState(213); match(ID); - setState(183); + setState(214); match(RP); - setState(184); + setState(215); block(); } } @@ -1190,12 +1342,12 @@ class PainlessParser extends Parser { public final DelimiterContext delimiter() throws RecognitionException { DelimiterContext _localctx = new DelimiterContext(_ctx, getState()); - enterRule(_localctx, 24, RULE_delimiter); + enterRule(_localctx, 28, RULE_delimiter); int _la; try { enterOuterAlt(_localctx, 1); { - setState(186); + setState(217); _la = _input.LA(1); if ( !(_la==EOF || _la==SEMICOLON) ) { _errHandler.recoverInline(this); @@ -1357,31 +1509,31 @@ class PainlessParser extends Parser { int _parentState = getState(); ExpressionContext _localctx = new ExpressionContext(_ctx, _parentState); ExpressionContext _prevctx = _localctx; - int _startState = 26; - enterRecursionRule(_localctx, 26, RULE_expression, _p); + int _startState = 30; + enterRecursionRule(_localctx, 30, RULE_expression, _p); int _la; try { int _alt; enterOuterAlt(_localctx, 1); { - setState(197); - switch ( getInterpreter().adaptivePredict(_input,15,_ctx) ) { + setState(228); + switch ( getInterpreter().adaptivePredict(_input,18,_ctx) ) { case 1: { _localctx = new AssignmentContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(189); + setState(220); chain(true); - setState(190); + setState(221); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << ASSIGN) | (1L << AADD) | (1L << ASUB) | (1L << AMUL) | (1L << ADIV) | (1L << AREM) | (1L << AAND) | (1L << AXOR) | (1L << AOR) | (1L << ALSH) | (1L << ARSH) | (1L << AUSH))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(191); + setState(222); expression(1); ((AssignmentContext)_localctx).s = false; } @@ -1391,37 +1543,37 @@ class PainlessParser extends Parser { _localctx = new SingleContext(_localctx); _ctx = _localctx; _prevctx = _localctx; - setState(194); + setState(225); ((SingleContext)_localctx).u = unary(false); ((SingleContext)_localctx).s = ((SingleContext)_localctx).u.s; } break; } _ctx.stop = _input.LT(-1); - setState(258); + setState(289); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,17,_ctx); + _alt = getInterpreter().adaptivePredict(_input,20,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { if ( _parseListeners!=null ) triggerExitRuleEvent(); _prevctx = _localctx; { - setState(256); - switch ( getInterpreter().adaptivePredict(_input,16,_ctx) ) { + setState(287); + switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) { case 1: { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(199); + setState(230); if (!(precpred(_ctx, 12))) throw new FailedPredicateException(this, "precpred(_ctx, 12)"); - setState(200); + setState(231); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << MUL) | (1L << DIV) | (1L << REM))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(201); + setState(232); expression(13); ((BinaryContext)_localctx).s = false; } @@ -1430,16 +1582,16 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(204); + setState(235); if (!(precpred(_ctx, 11))) throw new FailedPredicateException(this, "precpred(_ctx, 11)"); - setState(205); + setState(236); _la = _input.LA(1); if ( !(_la==ADD || _la==SUB) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(206); + setState(237); expression(12); ((BinaryContext)_localctx).s = false; } @@ -1448,16 +1600,16 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(209); + setState(240); if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)"); - setState(210); + setState(241); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LSH) | (1L << RSH) | (1L << USH))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(211); + setState(242); expression(11); ((BinaryContext)_localctx).s = false; } @@ -1466,16 +1618,16 @@ class PainlessParser extends Parser { { _localctx = new CompContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(214); + setState(245); if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); - setState(215); + setState(246); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LT) | (1L << LTE) | (1L << GT) | (1L << GTE))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(216); + setState(247); expression(10); ((CompContext)_localctx).s = false; } @@ -1484,16 +1636,16 @@ class PainlessParser extends Parser { { _localctx = new CompContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(219); + setState(250); if (!(precpred(_ctx, 8))) throw new FailedPredicateException(this, "precpred(_ctx, 8)"); - setState(220); + setState(251); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << EQ) | (1L << EQR) | (1L << NE) | (1L << NER))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(221); + setState(252); expression(9); ((CompContext)_localctx).s = false; } @@ -1502,11 +1654,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(224); + setState(255); if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); - setState(225); + setState(256); match(BWAND); - setState(226); + setState(257); expression(8); ((BinaryContext)_localctx).s = false; } @@ -1515,11 +1667,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(229); + setState(260); if (!(precpred(_ctx, 6))) throw new FailedPredicateException(this, "precpred(_ctx, 6)"); - setState(230); + setState(261); match(XOR); - setState(231); + setState(262); expression(7); ((BinaryContext)_localctx).s = false; } @@ -1528,11 +1680,11 @@ class PainlessParser extends Parser { { _localctx = new BinaryContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(234); + setState(265); if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)"); - setState(235); + setState(266); match(BWOR); - setState(236); + setState(267); expression(6); ((BinaryContext)_localctx).s = false; } @@ -1541,11 +1693,11 @@ class PainlessParser extends Parser { { _localctx = new BoolContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(239); + setState(270); if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)"); - setState(240); + setState(271); match(BOOLAND); - setState(241); + setState(272); expression(5); ((BoolContext)_localctx).s = false; } @@ -1554,11 +1706,11 @@ class PainlessParser extends Parser { { _localctx = new BoolContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(244); + setState(275); if (!(precpred(_ctx, 3))) throw new FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(245); + setState(276); match(BOOLOR); - setState(246); + setState(277); expression(4); ((BoolContext)_localctx).s = false; } @@ -1567,15 +1719,15 @@ class PainlessParser extends Parser { { _localctx = new ConditionalContext(new ExpressionContext(_parentctx, _parentState)); pushNewRecursionContext(_localctx, _startState, RULE_expression); - setState(249); + setState(280); if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)"); - setState(250); + setState(281); match(COND); - setState(251); + setState(282); ((ConditionalContext)_localctx).e0 = expression(0); - setState(252); + setState(283); match(COLON); - setState(253); + setState(284); ((ConditionalContext)_localctx).e1 = expression(2); ((ConditionalContext)_localctx).s = ((ConditionalContext)_localctx).e0.s && ((ConditionalContext)_localctx).e1.s; } @@ -1583,9 +1735,9 @@ class PainlessParser extends Parser { } } } - setState(260); + setState(291); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,17,_ctx); + _alt = getInterpreter().adaptivePredict(_input,20,_ctx); } } } @@ -1727,25 +1879,25 @@ class PainlessParser extends Parser { public final UnaryContext unary(boolean c) throws RecognitionException { UnaryContext _localctx = new UnaryContext(_ctx, getState(), c); - enterRule(_localctx, 28, RULE_unary); + enterRule(_localctx, 32, RULE_unary); int _la; try { - setState(290); - switch ( getInterpreter().adaptivePredict(_input,18,_ctx) ) { + setState(321); + switch ( getInterpreter().adaptivePredict(_input,21,_ctx) ) { case 1: _localctx = new PreContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(261); + setState(292); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(262); + setState(293); _la = _input.LA(1); if ( !(_la==INCR || _la==DECR) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(263); + setState(294); chain(true); } break; @@ -1753,11 +1905,11 @@ class PainlessParser extends Parser { _localctx = new PostContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(264); + setState(295); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(265); + setState(296); chain(true); - setState(266); + setState(297); _la = _input.LA(1); if ( !(_la==INCR || _la==DECR) ) { _errHandler.recoverInline(this); @@ -1770,9 +1922,9 @@ class PainlessParser extends Parser { _localctx = new ReadContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(268); + setState(299); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(269); + setState(300); chain(false); } break; @@ -1780,9 +1932,9 @@ class PainlessParser extends Parser { _localctx = new NumericContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(270); + setState(301); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(271); + setState(302); _la = _input.LA(1); if ( !(((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & ((1L << (OCTAL - 64)) | (1L << (HEX - 64)) | (1L << (INTEGER - 64)) | (1L << (DECIMAL - 64)))) != 0)) ) { _errHandler.recoverInline(this); @@ -1796,9 +1948,9 @@ class PainlessParser extends Parser { _localctx = new TrueContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(273); + setState(304); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(274); + setState(305); match(TRUE); ((TrueContext)_localctx).s = false; } @@ -1807,9 +1959,9 @@ class PainlessParser extends Parser { _localctx = new FalseContext(_localctx); enterOuterAlt(_localctx, 6); { - setState(276); + setState(307); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(277); + setState(308); match(FALSE); ((FalseContext)_localctx).s = false; } @@ -1818,9 +1970,9 @@ class PainlessParser extends Parser { _localctx = new NullContext(_localctx); enterOuterAlt(_localctx, 7); { - setState(279); + setState(310); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(280); + setState(311); match(NULL); ((NullContext)_localctx).s = false; } @@ -1829,16 +1981,16 @@ class PainlessParser extends Parser { _localctx = new OperatorContext(_localctx); enterOuterAlt(_localctx, 8); { - setState(282); + setState(313); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(283); + setState(314); _la = _input.LA(1); if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L << SUB))) != 0)) ) { _errHandler.recoverInline(this); } else { consume(); } - setState(284); + setState(315); unary(false); } break; @@ -1846,13 +1998,13 @@ class PainlessParser extends Parser { _localctx = new CastContext(_localctx); enterOuterAlt(_localctx, 9); { - setState(285); + setState(316); match(LP); - setState(286); + setState(317); decltype(); - setState(287); + setState(318); match(RP); - setState(288); + setState(319); unary(_localctx.c); } break; @@ -1958,32 +2110,32 @@ class PainlessParser extends Parser { public final ChainContext chain(boolean c) throws RecognitionException { ChainContext _localctx = new ChainContext(_ctx, getState(), c); - enterRule(_localctx, 30, RULE_chain); + enterRule(_localctx, 34, RULE_chain); try { int _alt; - setState(326); - switch ( getInterpreter().adaptivePredict(_input,24,_ctx) ) { + setState(357); + switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { case 1: _localctx = new DynamicContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(292); + setState(323); ((DynamicContext)_localctx).p = primary(_localctx.c); - setState(296); + setState(327); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,19,_ctx); + _alt = getInterpreter().adaptivePredict(_input,22,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(293); + setState(324); secondary(((DynamicContext)_localctx).p.s); } } } - setState(298); + setState(329); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,19,_ctx); + _alt = getInterpreter().adaptivePredict(_input,22,_ctx); } } break; @@ -1991,25 +2143,25 @@ class PainlessParser extends Parser { _localctx = new StaticContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(299); + setState(330); decltype(); - setState(300); + setState(331); dot(); - setState(304); + setState(335); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,20,_ctx); + _alt = getInterpreter().adaptivePredict(_input,23,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(301); + setState(332); secondary(true); } } } - setState(306); + setState(337); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,20,_ctx); + _alt = getInterpreter().adaptivePredict(_input,23,_ctx); } } break; @@ -2017,11 +2169,11 @@ class PainlessParser extends Parser { _localctx = new NewarrayContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(307); + setState(338); match(NEW); - setState(308); + setState(339); match(TYPE); - setState(313); + setState(344); _errHandler.sync(this); _alt = 1; do { @@ -2029,11 +2181,11 @@ class PainlessParser extends Parser { case 1: { { - setState(309); + setState(340); match(LBRACE); - setState(310); + setState(341); expression(0); - setState(311); + setState(342); match(RBRACE); } } @@ -2041,31 +2193,31 @@ class PainlessParser extends Parser { default: throw new NoViableAltException(this); } - setState(315); + setState(346); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,21,_ctx); + _alt = getInterpreter().adaptivePredict(_input,24,_ctx); } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ); - setState(324); - switch ( getInterpreter().adaptivePredict(_input,23,_ctx) ) { + setState(355); + switch ( getInterpreter().adaptivePredict(_input,26,_ctx) ) { case 1: { - setState(317); + setState(348); dot(); - setState(321); + setState(352); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + _alt = getInterpreter().adaptivePredict(_input,25,_ctx); while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) { if ( _alt==1 ) { { { - setState(318); + setState(349); secondary(true); } } } - setState(323); + setState(354); _errHandler.sync(this); - _alt = getInterpreter().adaptivePredict(_input,22,_ctx); + _alt = getInterpreter().adaptivePredict(_input,25,_ctx); } } break; @@ -2111,6 +2263,18 @@ class PainlessParser extends Parser { else return visitor.visitChildren(this); } } + public static class CalllocalContext extends PrimaryContext { + public TerminalNode ID() { return getToken(PainlessParser.ID, 0); } + public ArgumentsContext arguments() { + return getRuleContext(ArgumentsContext.class,0); + } + public CalllocalContext(PrimaryContext ctx) { copyFrom(ctx); } + @Override + public T accept(ParseTreeVisitor visitor) { + if ( visitor instanceof PainlessParserVisitor ) return ((PainlessParserVisitor)visitor).visitCalllocal(this); + else return visitor.visitChildren(this); + } + } public static class VariableContext extends PrimaryContext { public TerminalNode ID() { return getToken(PainlessParser.ID, 0); } public VariableContext(PrimaryContext ctx) { copyFrom(ctx); } @@ -2163,21 +2327,21 @@ class PainlessParser extends Parser { public final PrimaryContext primary(boolean c) throws RecognitionException { PrimaryContext _localctx = new PrimaryContext(_ctx, getState(), c); - enterRule(_localctx, 32, RULE_primary); + enterRule(_localctx, 36, RULE_primary); try { - setState(344); - switch ( getInterpreter().adaptivePredict(_input,25,_ctx) ) { + setState(377); + switch ( getInterpreter().adaptivePredict(_input,28,_ctx) ) { case 1: _localctx = new ExprprecContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(328); + setState(359); if (!( !_localctx.c )) throw new FailedPredicateException(this, " !$c "); - setState(329); + setState(360); match(LP); - setState(330); + setState(361); ((ExprprecContext)_localctx).e = expression(0); - setState(331); + setState(362); match(RP); ((ExprprecContext)_localctx).s = ((ExprprecContext)_localctx).e.s; } @@ -2186,13 +2350,13 @@ class PainlessParser extends Parser { _localctx = new ChainprecContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(334); + setState(365); if (!( _localctx.c )) throw new FailedPredicateException(this, " $c "); - setState(335); + setState(366); match(LP); - setState(336); + setState(367); unary(true); - setState(337); + setState(368); match(RP); } break; @@ -2200,7 +2364,7 @@ class PainlessParser extends Parser { _localctx = new StringContext(_localctx); enterOuterAlt(_localctx, 3); { - setState(339); + setState(370); match(STRING); } break; @@ -2208,19 +2372,29 @@ class PainlessParser extends Parser { _localctx = new VariableContext(_localctx); enterOuterAlt(_localctx, 4); { - setState(340); + setState(371); match(ID); } break; case 5: - _localctx = new NewobjectContext(_localctx); + _localctx = new CalllocalContext(_localctx); enterOuterAlt(_localctx, 5); { - setState(341); + setState(372); + match(ID); + setState(373); + arguments(); + } + break; + case 6: + _localctx = new NewobjectContext(_localctx); + enterOuterAlt(_localctx, 6); + { + setState(374); match(NEW); - setState(342); + setState(375); match(TYPE); - setState(343); + setState(376); arguments(); } break; @@ -2260,25 +2434,25 @@ class PainlessParser extends Parser { public final SecondaryContext secondary(boolean s) throws RecognitionException { SecondaryContext _localctx = new SecondaryContext(_ctx, getState(), s); - enterRule(_localctx, 34, RULE_secondary); + enterRule(_localctx, 38, RULE_secondary); try { - setState(350); - switch ( getInterpreter().adaptivePredict(_input,26,_ctx) ) { + setState(383); + switch ( getInterpreter().adaptivePredict(_input,29,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(346); + setState(379); if (!( _localctx.s )) throw new FailedPredicateException(this, " $s "); - setState(347); + setState(380); dot(); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(348); + setState(381); if (!( _localctx.s )) throw new FailedPredicateException(this, " $s "); - setState(349); + setState(382); brace(); } break; @@ -2333,20 +2507,20 @@ class PainlessParser extends Parser { public final DotContext dot() throws RecognitionException { DotContext _localctx = new DotContext(_ctx, getState()); - enterRule(_localctx, 36, RULE_dot); + enterRule(_localctx, 40, RULE_dot); int _la; try { - setState(357); - switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) { + setState(390); + switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) { case 1: _localctx = new CallinvokeContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(352); + setState(385); match(DOT); - setState(353); + setState(386); match(DOTID); - setState(354); + setState(387); arguments(); } break; @@ -2354,9 +2528,9 @@ class PainlessParser extends Parser { _localctx = new FieldaccessContext(_localctx); enterOuterAlt(_localctx, 2); { - setState(355); + setState(388); match(DOT); - setState(356); + setState(389); _la = _input.LA(1); if ( !(_la==DOTINTEGER || _la==DOTID) ) { _errHandler.recoverInline(this); @@ -2405,16 +2579,16 @@ class PainlessParser extends Parser { public final BraceContext brace() throws RecognitionException { BraceContext _localctx = new BraceContext(_ctx, getState()); - enterRule(_localctx, 38, RULE_brace); + enterRule(_localctx, 42, RULE_brace); try { _localctx = new BraceaccessContext(_localctx); enterOuterAlt(_localctx, 1); { - setState(359); + setState(392); match(LBRACE); - setState(360); + setState(393); expression(0); - setState(361); + setState(394); match(RBRACE); } } @@ -2455,40 +2629,40 @@ class PainlessParser extends Parser { public final ArgumentsContext arguments() throws RecognitionException { ArgumentsContext _localctx = new ArgumentsContext(_ctx, getState()); - enterRule(_localctx, 40, RULE_arguments); + enterRule(_localctx, 44, RULE_arguments); int _la; try { enterOuterAlt(_localctx, 1); { { - setState(363); + setState(396); match(LP); - setState(372); - switch ( getInterpreter().adaptivePredict(_input,29,_ctx) ) { + setState(405); + switch ( getInterpreter().adaptivePredict(_input,32,_ctx) ) { case 1: { - setState(364); + setState(397); argument(); - setState(369); + setState(402); _errHandler.sync(this); _la = _input.LA(1); while (_la==COMMA) { { { - setState(365); + setState(398); match(COMMA); - setState(366); + setState(399); argument(); } } - setState(371); + setState(404); _errHandler.sync(this); _la = _input.LA(1); } } break; } - setState(374); + setState(407); match(RP); } } @@ -2524,21 +2698,21 @@ class PainlessParser extends Parser { public final ArgumentContext argument() throws RecognitionException { ArgumentContext _localctx = new ArgumentContext(_ctx, getState()); - enterRule(_localctx, 42, RULE_argument); + enterRule(_localctx, 46, RULE_argument); try { - setState(378); - switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) { + setState(411); + switch ( getInterpreter().adaptivePredict(_input,33,_ctx) ) { case 1: enterOuterAlt(_localctx, 1); { - setState(376); + setState(409); expression(0); } break; case 2: enterOuterAlt(_localctx, 2); { - setState(377); + setState(410); funcref(); } break; @@ -2557,15 +2731,15 @@ class PainlessParser extends Parser { public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) { switch (ruleIndex) { - case 1: + case 3: return statement_sempred((StatementContext)_localctx, predIndex); - case 13: + case 15: return expression_sempred((ExpressionContext)_localctx, predIndex); - case 14: - return unary_sempred((UnaryContext)_localctx, predIndex); case 16: + return unary_sempred((UnaryContext)_localctx, predIndex); + case 18: return primary_sempred((PrimaryContext)_localctx, predIndex); - case 17: + case 19: return secondary_sempred((SecondaryContext)_localctx, predIndex); } return true; @@ -2645,146 +2819,158 @@ class PainlessParser extends Parser { } public static final String _serializedATN = - "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3M\u017f\4\2\t\2\4"+ + "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3M\u01a0\4\2\t\2\4"+ "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t"+ "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+ - "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\3\2\7\2\60\n\2\f\2"+ - "\16\2\63\13\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\5\3?\n\3\3\3\3\3"+ - "\3\3\3\3\3\3\3\3\5\3G\n\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3"+ - "\5\3T\n\3\3\3\3\3\5\3X\n\3\3\3\3\3\5\3\\\n\3\3\3\3\3\3\3\5\3a\n\3\3\3"+ - "\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3\3"+ - "\3\3\3\3\3\3\3\3\3\6\3z\n\3\r\3\16\3{\3\3\3\3\3\3\3\3\3\3\3\3\3\3\5\3"+ - "\u0085\n\3\3\4\3\4\5\4\u0089\n\4\3\5\3\5\7\5\u008d\n\5\f\5\16\5\u0090"+ - "\13\5\3\5\3\5\3\6\3\6\3\7\3\7\5\7\u0098\n\7\3\b\3\b\3\t\3\t\3\t\3\t\7"+ - "\t\u00a0\n\t\f\t\16\t\u00a3\13\t\3\n\3\n\3\n\7\n\u00a8\n\n\f\n\16\n\u00ab"+ - "\13\n\3\13\3\13\3\13\3\13\3\f\3\f\3\f\5\f\u00b4\n\f\3\r\3\r\3\r\3\r\3"+ - "\r\3\r\3\r\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\5\17"+ - "\u00c8\n\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ - "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ - "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ - "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\17"+ - "\3\17\3\17\3\17\7\17\u0103\n\17\f\17\16\17\u0106\13\17\3\20\3\20\3\20"+ - "\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20"+ - "\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\3\20\5\20\u0125"+ - "\n\20\3\21\3\21\7\21\u0129\n\21\f\21\16\21\u012c\13\21\3\21\3\21\3\21"+ - "\7\21\u0131\n\21\f\21\16\21\u0134\13\21\3\21\3\21\3\21\3\21\3\21\3\21"+ - "\6\21\u013c\n\21\r\21\16\21\u013d\3\21\3\21\7\21\u0142\n\21\f\21\16\21"+ - "\u0145\13\21\5\21\u0147\n\21\5\21\u0149\n\21\3\22\3\22\3\22\3\22\3\22"+ - "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\5\22\u015b\n\22"+ - "\3\23\3\23\3\23\3\23\5\23\u0161\n\23\3\24\3\24\3\24\3\24\3\24\5\24\u0168"+ - "\n\24\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\7\26\u0172\n\26\f\26\16"+ - "\26\u0175\13\26\5\26\u0177\n\26\3\26\3\26\3\27\3\27\5\27\u017d\n\27\3"+ - "\27\2\3\34\30\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*,\2\16\4\2"+ - "\26\26KK\3\3\r\r\3\2\66A\3\2\34\36\3\2\37 \3\2!#\3\2$\'\3\2(+\3\2\64\65"+ - "\3\2BE\4\2\32\33\37 \3\2LM\u01a5\2\61\3\2\2\2\4\u0084\3\2\2\2\6\u0088"+ - "\3\2\2\2\b\u008a\3\2\2\2\n\u0093\3\2\2\2\f\u0097\3\2\2\2\16\u0099\3\2"+ - "\2\2\20\u009b\3\2\2\2\22\u00a4\3\2\2\2\24\u00ac\3\2\2\2\26\u00b0\3\2\2"+ - "\2\30\u00b5\3\2\2\2\32\u00bc\3\2\2\2\34\u00c7\3\2\2\2\36\u0124\3\2\2\2"+ - " \u0148\3\2\2\2\"\u015a\3\2\2\2$\u0160\3\2\2\2&\u0167\3\2\2\2(\u0169\3"+ - "\2\2\2*\u016d\3\2\2\2,\u017c\3\2\2\2.\60\5\4\3\2/.\3\2\2\2\60\63\3\2\2"+ - "\2\61/\3\2\2\2\61\62\3\2\2\2\62\64\3\2\2\2\63\61\3\2\2\2\64\65\7\2\2\3"+ - "\65\3\3\2\2\2\66\67\7\16\2\2\678\7\t\2\289\5\34\17\29:\7\n\2\2:>\5\6\4"+ - "\2;<\7\17\2\2;\3\2\2\2>=\3\2\2\2?\u0085\3\2\2\2@"+ - "A\7\20\2\2AB\7\t\2\2BC\5\34\17\2CF\7\n\2\2DG\5\6\4\2EG\5\n\6\2FD\3\2\2"+ - "\2FE\3\2\2\2G\u0085\3\2\2\2HI\7\21\2\2IJ\5\b\5\2JK\7\20\2\2KL\7\t\2\2"+ - "LM\5\34\17\2MN\7\n\2\2NO\5\32\16\2O\u0085\3\2\2\2PQ\7\22\2\2QS\7\t\2\2"+ - "RT\5\f\7\2SR\3\2\2\2ST\3\2\2\2TU\3\2\2\2UW\7\r\2\2VX\5\34\17\2WV\3\2\2"+ - "\2WX\3\2\2\2XY\3\2\2\2Y[\7\r\2\2Z\\\5\16\b\2[Z\3\2\2\2[\\\3\2\2\2\\]\3"+ - "\2\2\2]`\7\n\2\2^a\5\6\4\2_a\5\n\6\2`^\3\2\2\2`_\3\2\2\2a\u0085\3\2\2"+ - "\2bc\7\22\2\2cd\7\t\2\2de\5\22\n\2ef\7K\2\2fg\7\62\2\2gh\5\34\17\2hi\7"+ - "\n\2\2ij\5\6\4\2j\u0085\3\2\2\2kl\5\20\t\2lm\5\32\16\2m\u0085\3\2\2\2"+ - "no\7\23\2\2o\u0085\5\32\16\2pq\7\24\2\2q\u0085\5\32\16\2rs\7\25\2\2st"+ - "\5\34\17\2tu\5\32\16\2u\u0085\3\2\2\2vw\7\27\2\2wy\5\b\5\2xz\5\30\r\2"+ - "yx\3\2\2\2z{\3\2\2\2{y\3\2\2\2{|\3\2\2\2|\u0085\3\2\2\2}~\7\31\2\2~\177"+ - "\5\34\17\2\177\u0080\5\32\16\2\u0080\u0085\3\2\2\2\u0081\u0082\5\34\17"+ - "\2\u0082\u0083\5\32\16\2\u0083\u0085\3\2\2\2\u0084\66\3\2\2\2\u0084@\3"+ - "\2\2\2\u0084H\3\2\2\2\u0084P\3\2\2\2\u0084b\3\2\2\2\u0084k\3\2\2\2\u0084"+ - "n\3\2\2\2\u0084p\3\2\2\2\u0084r\3\2\2\2\u0084v\3\2\2\2\u0084}\3\2\2\2"+ - "\u0084\u0081\3\2\2\2\u0085\5\3\2\2\2\u0086\u0089\5\b\5\2\u0087\u0089\5"+ - "\4\3\2\u0088\u0086\3\2\2\2\u0088\u0087\3\2\2\2\u0089\7\3\2\2\2\u008a\u008e"+ - "\7\5\2\2\u008b\u008d\5\4\3\2\u008c\u008b\3\2\2\2\u008d\u0090\3\2\2\2\u008e"+ - "\u008c\3\2\2\2\u008e\u008f\3\2\2\2\u008f\u0091\3\2\2\2\u0090\u008e\3\2"+ - "\2\2\u0091\u0092\7\6\2\2\u0092\t\3\2\2\2\u0093\u0094\7\r\2\2\u0094\13"+ - "\3\2\2\2\u0095\u0098\5\20\t\2\u0096\u0098\5\34\17\2\u0097\u0095\3\2\2"+ - "\2\u0097\u0096\3\2\2\2\u0098\r\3\2\2\2\u0099\u009a\5\34\17\2\u009a\17"+ - "\3\2\2\2\u009b\u009c\5\22\n\2\u009c\u00a1\5\26\f\2\u009d\u009e\7\f\2\2"+ - "\u009e\u00a0\5\26\f\2\u009f\u009d\3\2\2\2\u00a0\u00a3\3\2\2\2\u00a1\u009f"+ - "\3\2\2\2\u00a1\u00a2\3\2\2\2\u00a2\21\3\2\2\2\u00a3\u00a1\3\2\2\2\u00a4"+ - "\u00a9\7J\2\2\u00a5\u00a6\7\7\2\2\u00a6\u00a8\7\b\2\2\u00a7\u00a5\3\2"+ - "\2\2\u00a8\u00ab\3\2\2\2\u00a9\u00a7\3\2\2\2\u00a9\u00aa\3\2\2\2\u00aa"+ - "\23\3\2\2\2\u00ab\u00a9\3\2\2\2\u00ac\u00ad\7J\2\2\u00ad\u00ae\7\63\2"+ - "\2\u00ae\u00af\t\2\2\2\u00af\25\3\2\2\2\u00b0\u00b3\7K\2\2\u00b1\u00b2"+ - "\7\66\2\2\u00b2\u00b4\5\34\17\2\u00b3\u00b1\3\2\2\2\u00b3\u00b4\3\2\2"+ - "\2\u00b4\27\3\2\2\2\u00b5\u00b6\7\30\2\2\u00b6\u00b7\7\t\2\2\u00b7\u00b8"+ - "\7J\2\2\u00b8\u00b9\7K\2\2\u00b9\u00ba\7\n\2\2\u00ba\u00bb\5\b\5\2\u00bb"+ - "\31\3\2\2\2\u00bc\u00bd\t\3\2\2\u00bd\33\3\2\2\2\u00be\u00bf\b\17\1\2"+ - "\u00bf\u00c0\5 \21\2\u00c0\u00c1\t\4\2\2\u00c1\u00c2\5\34\17\3\u00c2\u00c3"+ - "\b\17\1\2\u00c3\u00c8\3\2\2\2\u00c4\u00c5\5\36\20\2\u00c5\u00c6\b\17\1"+ - "\2\u00c6\u00c8\3\2\2\2\u00c7\u00be\3\2\2\2\u00c7\u00c4\3\2\2\2\u00c8\u0104"+ - "\3\2\2\2\u00c9\u00ca\f\16\2\2\u00ca\u00cb\t\5\2\2\u00cb\u00cc\5\34\17"+ - "\17\u00cc\u00cd\b\17\1\2\u00cd\u0103\3\2\2\2\u00ce\u00cf\f\r\2\2\u00cf"+ - "\u00d0\t\6\2\2\u00d0\u00d1\5\34\17\16\u00d1\u00d2\b\17\1\2\u00d2\u0103"+ - "\3\2\2\2\u00d3\u00d4\f\f\2\2\u00d4\u00d5\t\7\2\2\u00d5\u00d6\5\34\17\r"+ - "\u00d6\u00d7\b\17\1\2\u00d7\u0103\3\2\2\2\u00d8\u00d9\f\13\2\2\u00d9\u00da"+ - "\t\b\2\2\u00da\u00db\5\34\17\f\u00db\u00dc\b\17\1\2\u00dc\u0103\3\2\2"+ - "\2\u00dd\u00de\f\n\2\2\u00de\u00df\t\t\2\2\u00df\u00e0\5\34\17\13\u00e0"+ - "\u00e1\b\17\1\2\u00e1\u0103\3\2\2\2\u00e2\u00e3\f\t\2\2\u00e3\u00e4\7"+ - ",\2\2\u00e4\u00e5\5\34\17\n\u00e5\u00e6\b\17\1\2\u00e6\u0103\3\2\2\2\u00e7"+ - "\u00e8\f\b\2\2\u00e8\u00e9\7-\2\2\u00e9\u00ea\5\34\17\t\u00ea\u00eb\b"+ - "\17\1\2\u00eb\u0103\3\2\2\2\u00ec\u00ed\f\7\2\2\u00ed\u00ee\7.\2\2\u00ee"+ - "\u00ef\5\34\17\b\u00ef\u00f0\b\17\1\2\u00f0\u0103\3\2\2\2\u00f1\u00f2"+ - "\f\6\2\2\u00f2\u00f3\7/\2\2\u00f3\u00f4\5\34\17\7\u00f4\u00f5\b\17\1\2"+ - "\u00f5\u0103\3\2\2\2\u00f6\u00f7\f\5\2\2\u00f7\u00f8\7\60\2\2\u00f8\u00f9"+ - "\5\34\17\6\u00f9\u00fa\b\17\1\2\u00fa\u0103\3\2\2\2\u00fb\u00fc\f\4\2"+ - "\2\u00fc\u00fd\7\61\2\2\u00fd\u00fe\5\34\17\2\u00fe\u00ff\7\62\2\2\u00ff"+ - "\u0100\5\34\17\4\u0100\u0101\b\17\1\2\u0101\u0103\3\2\2\2\u0102\u00c9"+ - "\3\2\2\2\u0102\u00ce\3\2\2\2\u0102\u00d3\3\2\2\2\u0102\u00d8\3\2\2\2\u0102"+ - "\u00dd\3\2\2\2\u0102\u00e2\3\2\2\2\u0102\u00e7\3\2\2\2\u0102\u00ec\3\2"+ - "\2\2\u0102\u00f1\3\2\2\2\u0102\u00f6\3\2\2\2\u0102\u00fb\3\2\2\2\u0103"+ - "\u0106\3\2\2\2\u0104\u0102\3\2\2\2\u0104\u0105\3\2\2\2\u0105\35\3\2\2"+ - "\2\u0106\u0104\3\2\2\2\u0107\u0108\6\20\16\3\u0108\u0109\t\n\2\2\u0109"+ - "\u0125\5 \21\2\u010a\u010b\6\20\17\3\u010b\u010c\5 \21\2\u010c\u010d\t"+ - "\n\2\2\u010d\u0125\3\2\2\2\u010e\u010f\6\20\20\3\u010f\u0125\5 \21\2\u0110"+ - "\u0111\6\20\21\3\u0111\u0112\t\13\2\2\u0112\u0125\b\20\1\2\u0113\u0114"+ - "\6\20\22\3\u0114\u0115\7G\2\2\u0115\u0125\b\20\1\2\u0116\u0117\6\20\23"+ - "\3\u0117\u0118\7H\2\2\u0118\u0125\b\20\1\2\u0119\u011a\6\20\24\3\u011a"+ - "\u011b\7I\2\2\u011b\u0125\b\20\1\2\u011c\u011d\6\20\25\3\u011d\u011e\t"+ - "\f\2\2\u011e\u0125\5\36\20\2\u011f\u0120\7\t\2\2\u0120\u0121\5\22\n\2"+ - "\u0121\u0122\7\n\2\2\u0122\u0123\5\36\20\2\u0123\u0125\3\2\2\2\u0124\u0107"+ - "\3\2\2\2\u0124\u010a\3\2\2\2\u0124\u010e\3\2\2\2\u0124\u0110\3\2\2\2\u0124"+ - "\u0113\3\2\2\2\u0124\u0116\3\2\2\2\u0124\u0119\3\2\2\2\u0124\u011c\3\2"+ - "\2\2\u0124\u011f\3\2\2\2\u0125\37\3\2\2\2\u0126\u012a\5\"\22\2\u0127\u0129"+ - "\5$\23\2\u0128\u0127\3\2\2\2\u0129\u012c\3\2\2\2\u012a\u0128\3\2\2\2\u012a"+ - "\u012b\3\2\2\2\u012b\u0149\3\2\2\2\u012c\u012a\3\2\2\2\u012d\u012e\5\22"+ - "\n\2\u012e\u0132\5&\24\2\u012f\u0131\5$\23\2\u0130\u012f\3\2\2\2\u0131"+ - "\u0134\3\2\2\2\u0132\u0130\3\2\2\2\u0132\u0133\3\2\2\2\u0133\u0149\3\2"+ - "\2\2\u0134\u0132\3\2\2\2\u0135\u0136\7\26\2\2\u0136\u013b\7J\2\2\u0137"+ - "\u0138\7\7\2\2\u0138\u0139\5\34\17\2\u0139\u013a\7\b\2\2\u013a\u013c\3"+ - "\2\2\2\u013b\u0137\3\2\2\2\u013c\u013d\3\2\2\2\u013d\u013b\3\2\2\2\u013d"+ - "\u013e\3\2\2\2\u013e\u0146\3\2\2\2\u013f\u0143\5&\24\2\u0140\u0142\5$"+ - "\23\2\u0141\u0140\3\2\2\2\u0142\u0145\3\2\2\2\u0143\u0141\3\2\2\2\u0143"+ - "\u0144\3\2\2\2\u0144\u0147\3\2\2\2\u0145\u0143\3\2\2\2\u0146\u013f\3\2"+ - "\2\2\u0146\u0147\3\2\2\2\u0147\u0149\3\2\2\2\u0148\u0126\3\2\2\2\u0148"+ - "\u012d\3\2\2\2\u0148\u0135\3\2\2\2\u0149!\3\2\2\2\u014a\u014b\6\22\26"+ - "\3\u014b\u014c\7\t\2\2\u014c\u014d\5\34\17\2\u014d\u014e\7\n\2\2\u014e"+ - "\u014f\b\22\1\2\u014f\u015b\3\2\2\2\u0150\u0151\6\22\27\3\u0151\u0152"+ - "\7\t\2\2\u0152\u0153\5\36\20\2\u0153\u0154\7\n\2\2\u0154\u015b\3\2\2\2"+ - "\u0155\u015b\7F\2\2\u0156\u015b\7K\2\2\u0157\u0158\7\26\2\2\u0158\u0159"+ - "\7J\2\2\u0159\u015b\5*\26\2\u015a\u014a\3\2\2\2\u015a\u0150\3\2\2\2\u015a"+ - "\u0155\3\2\2\2\u015a\u0156\3\2\2\2\u015a\u0157\3\2\2\2\u015b#\3\2\2\2"+ - "\u015c\u015d\6\23\30\3\u015d\u0161\5&\24\2\u015e\u015f\6\23\31\3\u015f"+ - "\u0161\5(\25\2\u0160\u015c\3\2\2\2\u0160\u015e\3\2\2\2\u0161%\3\2\2\2"+ - "\u0162\u0163\7\13\2\2\u0163\u0164\7M\2\2\u0164\u0168\5*\26\2\u0165\u0166"+ - "\7\13\2\2\u0166\u0168\t\r\2\2\u0167\u0162\3\2\2\2\u0167\u0165\3\2\2\2"+ - "\u0168\'\3\2\2\2\u0169\u016a\7\7\2\2\u016a\u016b\5\34\17\2\u016b\u016c"+ - "\7\b\2\2\u016c)\3\2\2\2\u016d\u0176\7\t\2\2\u016e\u0173\5,\27\2\u016f"+ - "\u0170\7\f\2\2\u0170\u0172\5,\27\2\u0171\u016f\3\2\2\2\u0172\u0175\3\2"+ - "\2\2\u0173\u0171\3\2\2\2\u0173\u0174\3\2\2\2\u0174\u0177\3\2\2\2\u0175"+ - "\u0173\3\2\2\2\u0176\u016e\3\2\2\2\u0176\u0177\3\2\2\2\u0177\u0178\3\2"+ - "\2\2\u0178\u0179\7\n\2\2\u0179+\3\2\2\2\u017a\u017d\5\34\17\2\u017b\u017d"+ - "\5\24\13\2\u017c\u017a\3\2\2\2\u017c\u017b\3\2\2\2\u017d-\3\2\2\2!\61"+ - ">FSW[`{\u0084\u0088\u008e\u0097\u00a1\u00a9\u00b3\u00c7\u0102\u0104\u0124"+ - "\u012a\u0132\u013d\u0143\u0146\u0148\u015a\u0160\u0167\u0173\u0176\u017c"; + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+ + "\3\2\7\2\64\n\2\f\2\16\2\67\13\2\3\2\7\2:\n\2\f\2\16\2=\13\2\3\2\3\2\3"+ + "\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\7\4M\n\4\f\4\16\4P\13\4"+ + "\5\4R\n\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\5\5^\n\5\3\5\3\5\3\5"+ + "\3\5\3\5\3\5\5\5f\n\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\5\5"+ + "s\n\5\3\5\3\5\5\5w\n\5\3\5\3\5\5\5{\n\5\3\5\3\5\3\5\5\5\u0080\n\5\3\5"+ + "\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3"+ + "\5\3\5\3\5\3\5\3\5\6\5\u0099\n\5\r\5\16\5\u009a\3\5\3\5\3\5\3\5\3\5\3"+ + "\5\3\5\5\5\u00a4\n\5\3\6\3\6\5\6\u00a8\n\6\3\7\3\7\7\7\u00ac\n\7\f\7\16"+ + "\7\u00af\13\7\3\7\3\7\3\b\3\b\3\t\3\t\5\t\u00b7\n\t\3\n\3\n\3\13\3\13"+ + "\3\13\3\13\7\13\u00bf\n\13\f\13\16\13\u00c2\13\13\3\f\3\f\3\f\7\f\u00c7"+ + "\n\f\f\f\16\f\u00ca\13\f\3\r\3\r\3\r\3\r\3\16\3\16\3\16\5\16\u00d3\n\16"+ + "\3\17\3\17\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21"+ + "\3\21\3\21\3\21\3\21\5\21\u00e7\n\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ + "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ + "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ + "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21"+ + "\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\21\7\21\u0122\n\21\f\21\16\21\u0125"+ + "\13\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ + "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22"+ + "\3\22\3\22\5\22\u0144\n\22\3\23\3\23\7\23\u0148\n\23\f\23\16\23\u014b"+ + "\13\23\3\23\3\23\3\23\7\23\u0150\n\23\f\23\16\23\u0153\13\23\3\23\3\23"+ + "\3\23\3\23\3\23\3\23\6\23\u015b\n\23\r\23\16\23\u015c\3\23\3\23\7\23\u0161"+ + "\n\23\f\23\16\23\u0164\13\23\5\23\u0166\n\23\5\23\u0168\n\23\3\24\3\24"+ + "\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24"+ + "\3\24\3\24\5\24\u017c\n\24\3\25\3\25\3\25\3\25\5\25\u0182\n\25\3\26\3"+ + "\26\3\26\3\26\3\26\5\26\u0189\n\26\3\27\3\27\3\27\3\27\3\30\3\30\3\30"+ + "\3\30\7\30\u0193\n\30\f\30\16\30\u0196\13\30\5\30\u0198\n\30\3\30\3\30"+ + "\3\31\3\31\5\31\u019e\n\31\3\31\2\3 \32\2\4\6\b\n\f\16\20\22\24\26\30"+ + "\32\34\36 \"$&(*,.\60\2\16\4\2\26\26KK\3\3\r\r\3\2\66A\3\2\34\36\3\2\37"+ + " \3\2!#\3\2$\'\3\2(+\3\2\64\65\3\2BE\4\2\32\33\37 \3\2LM\u01c8\2\65\3"+ + "\2\2\2\4@\3\2\2\2\6E\3\2\2\2\b\u00a3\3\2\2\2\n\u00a7\3\2\2\2\f\u00a9\3"+ + "\2\2\2\16\u00b2\3\2\2\2\20\u00b6\3\2\2\2\22\u00b8\3\2\2\2\24\u00ba\3\2"+ + "\2\2\26\u00c3\3\2\2\2\30\u00cb\3\2\2\2\32\u00cf\3\2\2\2\34\u00d4\3\2\2"+ + "\2\36\u00db\3\2\2\2 \u00e6\3\2\2\2\"\u0143\3\2\2\2$\u0167\3\2\2\2&\u017b"+ + "\3\2\2\2(\u0181\3\2\2\2*\u0188\3\2\2\2,\u018a\3\2\2\2.\u018e\3\2\2\2\60"+ + "\u019d\3\2\2\2\62\64\5\4\3\2\63\62\3\2\2\2\64\67\3\2\2\2\65\63\3\2\2\2"+ + "\65\66\3\2\2\2\66;\3\2\2\2\67\65\3\2\2\28:\5\b\5\298\3\2\2\2:=\3\2\2\2"+ + ";9\3\2\2\2;<\3\2\2\2<>\3\2\2\2=;\3\2\2\2>?\7\2\2\3?\3\3\2\2\2@A\5\26\f"+ + "\2AB\7K\2\2BC\5\6\4\2CD\5\f\7\2D\5\3\2\2\2EQ\7\t\2\2FG\5\26\f\2GN\7K\2"+ + "\2HI\7\f\2\2IJ\5\26\f\2JK\7K\2\2KM\3\2\2\2LH\3\2\2\2MP\3\2\2\2NL\3\2\2"+ + "\2NO\3\2\2\2OR\3\2\2\2PN\3\2\2\2QF\3\2\2\2QR\3\2\2\2RS\3\2\2\2ST\7\n\2"+ + "\2T\7\3\2\2\2UV\7\16\2\2VW\7\t\2\2WX\5 \21\2XY\7\n\2\2Y]\5\n\6\2Z[\7\17"+ + "\2\2[^\5\n\6\2\\^\6\5\2\2]Z\3\2\2\2]\\\3\2\2\2^\u00a4\3\2\2\2_`\7\20\2"+ + "\2`a\7\t\2\2ab\5 \21\2be\7\n\2\2cf\5\n\6\2df\5\16\b\2ec\3\2\2\2ed\3\2"+ + "\2\2f\u00a4\3\2\2\2gh\7\21\2\2hi\5\f\7\2ij\7\20\2\2jk\7\t\2\2kl\5 \21"+ + "\2lm\7\n\2\2mn\5\36\20\2n\u00a4\3\2\2\2op\7\22\2\2pr\7\t\2\2qs\5\20\t"+ + "\2rq\3\2\2\2rs\3\2\2\2st\3\2\2\2tv\7\r\2\2uw\5 \21\2vu\3\2\2\2vw\3\2\2"+ + "\2wx\3\2\2\2xz\7\r\2\2y{\5\22\n\2zy\3\2\2\2z{\3\2\2\2{|\3\2\2\2|\177\7"+ + "\n\2\2}\u0080\5\n\6\2~\u0080\5\16\b\2\177}\3\2\2\2\177~\3\2\2\2\u0080"+ + "\u00a4\3\2\2\2\u0081\u0082\7\22\2\2\u0082\u0083\7\t\2\2\u0083\u0084\5"+ + "\26\f\2\u0084\u0085\7K\2\2\u0085\u0086\7\62\2\2\u0086\u0087\5 \21\2\u0087"+ + "\u0088\7\n\2\2\u0088\u0089\5\n\6\2\u0089\u00a4\3\2\2\2\u008a\u008b\5\24"+ + "\13\2\u008b\u008c\5\36\20\2\u008c\u00a4\3\2\2\2\u008d\u008e\7\23\2\2\u008e"+ + "\u00a4\5\36\20\2\u008f\u0090\7\24\2\2\u0090\u00a4\5\36\20\2\u0091\u0092"+ + "\7\25\2\2\u0092\u0093\5 \21\2\u0093\u0094\5\36\20\2\u0094\u00a4\3\2\2"+ + "\2\u0095\u0096\7\27\2\2\u0096\u0098\5\f\7\2\u0097\u0099\5\34\17\2\u0098"+ + "\u0097\3\2\2\2\u0099\u009a\3\2\2\2\u009a\u0098\3\2\2\2\u009a\u009b\3\2"+ + "\2\2\u009b\u00a4\3\2\2\2\u009c\u009d\7\31\2\2\u009d\u009e\5 \21\2\u009e"+ + "\u009f\5\36\20\2\u009f\u00a4\3\2\2\2\u00a0\u00a1\5 \21\2\u00a1\u00a2\5"+ + "\36\20\2\u00a2\u00a4\3\2\2\2\u00a3U\3\2\2\2\u00a3_\3\2\2\2\u00a3g\3\2"+ + "\2\2\u00a3o\3\2\2\2\u00a3\u0081\3\2\2\2\u00a3\u008a\3\2\2\2\u00a3\u008d"+ + "\3\2\2\2\u00a3\u008f\3\2\2\2\u00a3\u0091\3\2\2\2\u00a3\u0095\3\2\2\2\u00a3"+ + "\u009c\3\2\2\2\u00a3\u00a0\3\2\2\2\u00a4\t\3\2\2\2\u00a5\u00a8\5\f\7\2"+ + "\u00a6\u00a8\5\b\5\2\u00a7\u00a5\3\2\2\2\u00a7\u00a6\3\2\2\2\u00a8\13"+ + "\3\2\2\2\u00a9\u00ad\7\5\2\2\u00aa\u00ac\5\b\5\2\u00ab\u00aa\3\2\2\2\u00ac"+ + "\u00af\3\2\2\2\u00ad\u00ab\3\2\2\2\u00ad\u00ae\3\2\2\2\u00ae\u00b0\3\2"+ + "\2\2\u00af\u00ad\3\2\2\2\u00b0\u00b1\7\6\2\2\u00b1\r\3\2\2\2\u00b2\u00b3"+ + "\7\r\2\2\u00b3\17\3\2\2\2\u00b4\u00b7\5\24\13\2\u00b5\u00b7\5 \21\2\u00b6"+ + "\u00b4\3\2\2\2\u00b6\u00b5\3\2\2\2\u00b7\21\3\2\2\2\u00b8\u00b9\5 \21"+ + "\2\u00b9\23\3\2\2\2\u00ba\u00bb\5\26\f\2\u00bb\u00c0\5\32\16\2\u00bc\u00bd"+ + "\7\f\2\2\u00bd\u00bf\5\32\16\2\u00be\u00bc\3\2\2\2\u00bf\u00c2\3\2\2\2"+ + "\u00c0\u00be\3\2\2\2\u00c0\u00c1\3\2\2\2\u00c1\25\3\2\2\2\u00c2\u00c0"+ + "\3\2\2\2\u00c3\u00c8\7J\2\2\u00c4\u00c5\7\7\2\2\u00c5\u00c7\7\b\2\2\u00c6"+ + "\u00c4\3\2\2\2\u00c7\u00ca\3\2\2\2\u00c8\u00c6\3\2\2\2\u00c8\u00c9\3\2"+ + "\2\2\u00c9\27\3\2\2\2\u00ca\u00c8\3\2\2\2\u00cb\u00cc\7J\2\2\u00cc\u00cd"+ + "\7\63\2\2\u00cd\u00ce\t\2\2\2\u00ce\31\3\2\2\2\u00cf\u00d2\7K\2\2\u00d0"+ + "\u00d1\7\66\2\2\u00d1\u00d3\5 \21\2\u00d2\u00d0\3\2\2\2\u00d2\u00d3\3"+ + "\2\2\2\u00d3\33\3\2\2\2\u00d4\u00d5\7\30\2\2\u00d5\u00d6\7\t\2\2\u00d6"+ + "\u00d7\7J\2\2\u00d7\u00d8\7K\2\2\u00d8\u00d9\7\n\2\2\u00d9\u00da\5\f\7"+ + "\2\u00da\35\3\2\2\2\u00db\u00dc\t\3\2\2\u00dc\37\3\2\2\2\u00dd\u00de\b"+ + "\21\1\2\u00de\u00df\5$\23\2\u00df\u00e0\t\4\2\2\u00e0\u00e1\5 \21\3\u00e1"+ + "\u00e2\b\21\1\2\u00e2\u00e7\3\2\2\2\u00e3\u00e4\5\"\22\2\u00e4\u00e5\b"+ + "\21\1\2\u00e5\u00e7\3\2\2\2\u00e6\u00dd\3\2\2\2\u00e6\u00e3\3\2\2\2\u00e7"+ + "\u0123\3\2\2\2\u00e8\u00e9\f\16\2\2\u00e9\u00ea\t\5\2\2\u00ea\u00eb\5"+ + " \21\17\u00eb\u00ec\b\21\1\2\u00ec\u0122\3\2\2\2\u00ed\u00ee\f\r\2\2\u00ee"+ + "\u00ef\t\6\2\2\u00ef\u00f0\5 \21\16\u00f0\u00f1\b\21\1\2\u00f1\u0122\3"+ + "\2\2\2\u00f2\u00f3\f\f\2\2\u00f3\u00f4\t\7\2\2\u00f4\u00f5\5 \21\r\u00f5"+ + "\u00f6\b\21\1\2\u00f6\u0122\3\2\2\2\u00f7\u00f8\f\13\2\2\u00f8\u00f9\t"+ + "\b\2\2\u00f9\u00fa\5 \21\f\u00fa\u00fb\b\21\1\2\u00fb\u0122\3\2\2\2\u00fc"+ + "\u00fd\f\n\2\2\u00fd\u00fe\t\t\2\2\u00fe\u00ff\5 \21\13\u00ff\u0100\b"+ + "\21\1\2\u0100\u0122\3\2\2\2\u0101\u0102\f\t\2\2\u0102\u0103\7,\2\2\u0103"+ + "\u0104\5 \21\n\u0104\u0105\b\21\1\2\u0105\u0122\3\2\2\2\u0106\u0107\f"+ + "\b\2\2\u0107\u0108\7-\2\2\u0108\u0109\5 \21\t\u0109\u010a\b\21\1\2\u010a"+ + "\u0122\3\2\2\2\u010b\u010c\f\7\2\2\u010c\u010d\7.\2\2\u010d\u010e\5 \21"+ + "\b\u010e\u010f\b\21\1\2\u010f\u0122\3\2\2\2\u0110\u0111\f\6\2\2\u0111"+ + "\u0112\7/\2\2\u0112\u0113\5 \21\7\u0113\u0114\b\21\1\2\u0114\u0122\3\2"+ + "\2\2\u0115\u0116\f\5\2\2\u0116\u0117\7\60\2\2\u0117\u0118\5 \21\6\u0118"+ + "\u0119\b\21\1\2\u0119\u0122\3\2\2\2\u011a\u011b\f\4\2\2\u011b\u011c\7"+ + "\61\2\2\u011c\u011d\5 \21\2\u011d\u011e\7\62\2\2\u011e\u011f\5 \21\4\u011f"+ + "\u0120\b\21\1\2\u0120\u0122\3\2\2\2\u0121\u00e8\3\2\2\2\u0121\u00ed\3"+ + "\2\2\2\u0121\u00f2\3\2\2\2\u0121\u00f7\3\2\2\2\u0121\u00fc\3\2\2\2\u0121"+ + "\u0101\3\2\2\2\u0121\u0106\3\2\2\2\u0121\u010b\3\2\2\2\u0121\u0110\3\2"+ + "\2\2\u0121\u0115\3\2\2\2\u0121\u011a\3\2\2\2\u0122\u0125\3\2\2\2\u0123"+ + "\u0121\3\2\2\2\u0123\u0124\3\2\2\2\u0124!\3\2\2\2\u0125\u0123\3\2\2\2"+ + "\u0126\u0127\6\22\16\3\u0127\u0128\t\n\2\2\u0128\u0144\5$\23\2\u0129\u012a"+ + "\6\22\17\3\u012a\u012b\5$\23\2\u012b\u012c\t\n\2\2\u012c\u0144\3\2\2\2"+ + "\u012d\u012e\6\22\20\3\u012e\u0144\5$\23\2\u012f\u0130\6\22\21\3\u0130"+ + "\u0131\t\13\2\2\u0131\u0144\b\22\1\2\u0132\u0133\6\22\22\3\u0133\u0134"+ + "\7G\2\2\u0134\u0144\b\22\1\2\u0135\u0136\6\22\23\3\u0136\u0137\7H\2\2"+ + "\u0137\u0144\b\22\1\2\u0138\u0139\6\22\24\3\u0139\u013a\7I\2\2\u013a\u0144"+ + "\b\22\1\2\u013b\u013c\6\22\25\3\u013c\u013d\t\f\2\2\u013d\u0144\5\"\22"+ + "\2\u013e\u013f\7\t\2\2\u013f\u0140\5\26\f\2\u0140\u0141\7\n\2\2\u0141"+ + "\u0142\5\"\22\2\u0142\u0144\3\2\2\2\u0143\u0126\3\2\2\2\u0143\u0129\3"+ + "\2\2\2\u0143\u012d\3\2\2\2\u0143\u012f\3\2\2\2\u0143\u0132\3\2\2\2\u0143"+ + "\u0135\3\2\2\2\u0143\u0138\3\2\2\2\u0143\u013b\3\2\2\2\u0143\u013e\3\2"+ + "\2\2\u0144#\3\2\2\2\u0145\u0149\5&\24\2\u0146\u0148\5(\25\2\u0147\u0146"+ + "\3\2\2\2\u0148\u014b\3\2\2\2\u0149\u0147\3\2\2\2\u0149\u014a\3\2\2\2\u014a"+ + "\u0168\3\2\2\2\u014b\u0149\3\2\2\2\u014c\u014d\5\26\f\2\u014d\u0151\5"+ + "*\26\2\u014e\u0150\5(\25\2\u014f\u014e\3\2\2\2\u0150\u0153\3\2\2\2\u0151"+ + "\u014f\3\2\2\2\u0151\u0152\3\2\2\2\u0152\u0168\3\2\2\2\u0153\u0151\3\2"+ + "\2\2\u0154\u0155\7\26\2\2\u0155\u015a\7J\2\2\u0156\u0157\7\7\2\2\u0157"+ + "\u0158\5 \21\2\u0158\u0159\7\b\2\2\u0159\u015b\3\2\2\2\u015a\u0156\3\2"+ + "\2\2\u015b\u015c\3\2\2\2\u015c\u015a\3\2\2\2\u015c\u015d\3\2\2\2\u015d"+ + "\u0165\3\2\2\2\u015e\u0162\5*\26\2\u015f\u0161\5(\25\2\u0160\u015f\3\2"+ + "\2\2\u0161\u0164\3\2\2\2\u0162\u0160\3\2\2\2\u0162\u0163\3\2\2\2\u0163"+ + "\u0166\3\2\2\2\u0164\u0162\3\2\2\2\u0165\u015e\3\2\2\2\u0165\u0166\3\2"+ + "\2\2\u0166\u0168\3\2\2\2\u0167\u0145\3\2\2\2\u0167\u014c\3\2\2\2\u0167"+ + "\u0154\3\2\2\2\u0168%\3\2\2\2\u0169\u016a\6\24\26\3\u016a\u016b\7\t\2"+ + "\2\u016b\u016c\5 \21\2\u016c\u016d\7\n\2\2\u016d\u016e\b\24\1\2\u016e"+ + "\u017c\3\2\2\2\u016f\u0170\6\24\27\3\u0170\u0171\7\t\2\2\u0171\u0172\5"+ + "\"\22\2\u0172\u0173\7\n\2\2\u0173\u017c\3\2\2\2\u0174\u017c\7F\2\2\u0175"+ + "\u017c\7K\2\2\u0176\u0177\7K\2\2\u0177\u017c\5.\30\2\u0178\u0179\7\26"+ + "\2\2\u0179\u017a\7J\2\2\u017a\u017c\5.\30\2\u017b\u0169\3\2\2\2\u017b"+ + "\u016f\3\2\2\2\u017b\u0174\3\2\2\2\u017b\u0175\3\2\2\2\u017b\u0176\3\2"+ + "\2\2\u017b\u0178\3\2\2\2\u017c\'\3\2\2\2\u017d\u017e\6\25\30\3\u017e\u0182"+ + "\5*\26\2\u017f\u0180\6\25\31\3\u0180\u0182\5,\27\2\u0181\u017d\3\2\2\2"+ + "\u0181\u017f\3\2\2\2\u0182)\3\2\2\2\u0183\u0184\7\13\2\2\u0184\u0185\7"+ + "M\2\2\u0185\u0189\5.\30\2\u0186\u0187\7\13\2\2\u0187\u0189\t\r\2\2\u0188"+ + "\u0183\3\2\2\2\u0188\u0186\3\2\2\2\u0189+\3\2\2\2\u018a\u018b\7\7\2\2"+ + "\u018b\u018c\5 \21\2\u018c\u018d\7\b\2\2\u018d-\3\2\2\2\u018e\u0197\7"+ + "\t\2\2\u018f\u0194\5\60\31\2\u0190\u0191\7\f\2\2\u0191\u0193\5\60\31\2"+ + "\u0192\u0190\3\2\2\2\u0193\u0196\3\2\2\2\u0194\u0192\3\2\2\2\u0194\u0195"+ + "\3\2\2\2\u0195\u0198\3\2\2\2\u0196\u0194\3\2\2\2\u0197\u018f\3\2\2\2\u0197"+ + "\u0198\3\2\2\2\u0198\u0199\3\2\2\2\u0199\u019a\7\n\2\2\u019a/\3\2\2\2"+ + "\u019b\u019e\5 \21\2\u019c\u019e\5\30\r\2\u019d\u019b\3\2\2\2\u019d\u019c"+ + "\3\2\2\2\u019e\61\3\2\2\2$\65;NQ]ervz\177\u009a\u00a3\u00a7\u00ad\u00b6"+ + "\u00c0\u00c8\u00d2\u00e6\u0121\u0123\u0143\u0149\u0151\u015c\u0162\u0165"+ + "\u0167\u017b\u0181\u0188\u0194\u0197\u019d"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserBaseVisitor.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserBaseVisitor.java index f116f087c5c..08358384fca 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserBaseVisitor.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserBaseVisitor.java @@ -18,6 +18,20 @@ class PainlessParserBaseVisitor extends AbstractParseTreeVisitor implement * {@link #visitChildren} on {@code ctx}.

*/ @Override public T visitSource(PainlessParser.SourceContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitFunction(PainlessParser.FunctionContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitParameters(PainlessParser.ParametersContext ctx) { return visitChildren(ctx); } /** * {@inheritDoc} * @@ -333,6 +347,13 @@ class PainlessParserBaseVisitor extends AbstractParseTreeVisitor implement * {@link #visitChildren} on {@code ctx}.

*/ @Override public T visitVariable(PainlessParser.VariableContext ctx) { return visitChildren(ctx); } + /** + * {@inheritDoc} + * + *

The default implementation returns the result of calling + * {@link #visitChildren} on {@code ctx}.

+ */ + @Override public T visitCalllocal(PainlessParser.CalllocalContext ctx) { return visitChildren(ctx); } /** * {@inheritDoc} * diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserVisitor.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserVisitor.java index f0943743ef8..5c0adb5076a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserVisitor.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParserVisitor.java @@ -16,6 +16,18 @@ interface PainlessParserVisitor extends ParseTreeVisitor { * @return the visitor result */ T visitSource(PainlessParser.SourceContext ctx); + /** + * Visit a parse tree produced by {@link PainlessParser#function}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitFunction(PainlessParser.FunctionContext ctx); + /** + * Visit a parse tree produced by {@link PainlessParser#parameters}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitParameters(PainlessParser.ParametersContext ctx); /** * Visit a parse tree produced by the {@code if} * labeled alternative in {@link PainlessParser#statement}. @@ -320,6 +332,13 @@ interface PainlessParserVisitor extends ParseTreeVisitor { * @return the visitor result */ T visitVariable(PainlessParser.VariableContext ctx); + /** + * Visit a parse tree produced by the {@code calllocal} + * labeled alternative in {@link PainlessParser#primary}. + * @param ctx the parse tree + * @return the visitor result + */ + T visitCalllocal(PainlessParser.CalllocalContext ctx); /** * Visit a parse tree produced by the {@code newobject} * labeled alternative in {@link PainlessParser#primary}. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java index 471cb7d64ea..c1fe02f9388 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java @@ -27,10 +27,13 @@ import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.atn.PredictionMode; +import org.antlr.v4.runtime.tree.TerminalNode; import org.elasticsearch.painless.CompilerSettings; +import org.elasticsearch.painless.Locals.ExecuteReserved; +import org.elasticsearch.painless.Locals.FunctionReserved; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.Variables.Reserved; +import org.elasticsearch.painless.Locals.Reserved; import org.elasticsearch.painless.antlr.PainlessParser.AfterthoughtContext; import org.elasticsearch.painless.antlr.PainlessParser.ArgumentContext; import org.elasticsearch.painless.antlr.PainlessParser.ArgumentsContext; @@ -41,6 +44,7 @@ import org.elasticsearch.painless.antlr.PainlessParser.BoolContext; import org.elasticsearch.painless.antlr.PainlessParser.BraceaccessContext; import org.elasticsearch.painless.antlr.PainlessParser.BreakContext; import org.elasticsearch.painless.antlr.PainlessParser.CallinvokeContext; +import org.elasticsearch.painless.antlr.PainlessParser.CalllocalContext; import org.elasticsearch.painless.antlr.PainlessParser.CastContext; import org.elasticsearch.painless.antlr.PainlessParser.ChainprecContext; import org.elasticsearch.painless.antlr.PainlessParser.CompContext; @@ -62,6 +66,7 @@ import org.elasticsearch.painless.antlr.PainlessParser.FalseContext; import org.elasticsearch.painless.antlr.PainlessParser.FieldaccessContext; import org.elasticsearch.painless.antlr.PainlessParser.ForContext; import org.elasticsearch.painless.antlr.PainlessParser.FuncrefContext; +import org.elasticsearch.painless.antlr.PainlessParser.FunctionContext; import org.elasticsearch.painless.antlr.PainlessParser.IfContext; import org.elasticsearch.painless.antlr.PainlessParser.InitializerContext; import org.elasticsearch.painless.antlr.PainlessParser.NewarrayContext; @@ -69,6 +74,7 @@ import org.elasticsearch.painless.antlr.PainlessParser.NewobjectContext; import org.elasticsearch.painless.antlr.PainlessParser.NullContext; import org.elasticsearch.painless.antlr.PainlessParser.NumericContext; import org.elasticsearch.painless.antlr.PainlessParser.OperatorContext; +import org.elasticsearch.painless.antlr.PainlessParser.ParametersContext; import org.elasticsearch.painless.antlr.PainlessParser.PostContext; import org.elasticsearch.painless.antlr.PainlessParser.PreContext; import org.elasticsearch.painless.antlr.PainlessParser.ReadContext; @@ -104,7 +110,8 @@ import org.elasticsearch.painless.node.ENull; import org.elasticsearch.painless.node.ENumeric; import org.elasticsearch.painless.node.EUnary; import org.elasticsearch.painless.node.LBrace; -import org.elasticsearch.painless.node.LCall; +import org.elasticsearch.painless.node.LCallInvoke; +import org.elasticsearch.painless.node.LCallLocal; import org.elasticsearch.painless.node.LCast; import org.elasticsearch.painless.node.LField; import org.elasticsearch.painless.node.LNewArray; @@ -122,6 +129,7 @@ import org.elasticsearch.painless.node.SDo; import org.elasticsearch.painless.node.SEach; import org.elasticsearch.painless.node.SExpression; import org.elasticsearch.painless.node.SFor; +import org.elasticsearch.painless.node.SFunction; import org.elasticsearch.painless.node.SIf; import org.elasticsearch.painless.node.SIfElse; import org.elasticsearch.painless.node.SReturn; @@ -130,7 +138,9 @@ import org.elasticsearch.painless.node.SThrow; import org.elasticsearch.painless.node.STry; import org.elasticsearch.painless.node.SWhile; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.List; /** @@ -138,19 +148,21 @@ import java.util.List; */ public final class Walker extends PainlessParserBaseVisitor { - public static SSource buildPainlessTree(String name, String sourceText, Reserved reserved, CompilerSettings settings) { - return new Walker(name, sourceText, reserved, settings).source; + public static SSource buildPainlessTree(String sourceName, String sourceText, CompilerSettings settings) { + return new Walker(sourceName, sourceText, settings).source; } - private final Reserved reserved; private final SSource source; private final CompilerSettings settings; private final String sourceName; + private final String sourceText; - private Walker(String name, String sourceText, Reserved reserved, CompilerSettings settings) { - this.reserved = reserved; + private final Deque reserved = new ArrayDeque<>(); + + private Walker(String sourceName, String sourceText, CompilerSettings settings) { this.settings = settings; - this.sourceName = Location.computeSourceName(name, sourceText); + this.sourceName = Location.computeSourceName(sourceName, sourceText); + this.sourceText = sourceText; this.source = (SSource)visit(buildAntlrTree(sourceText)); } @@ -196,13 +208,51 @@ public final class Walker extends PainlessParserBaseVisitor { @Override public Object visitSource(SourceContext ctx) { + reserved.push(new ExecuteReserved()); + + List functions = new ArrayList<>(); + + for (FunctionContext function : ctx.function()) { + functions.add((SFunction)visit(function)); + } + List statements = new ArrayList<>(); for (StatementContext statement : ctx.statement()) { statements.add((AStatement)visit(statement)); } - return new SSource(location(ctx), statements); + return new SSource(sourceName, sourceText, (ExecuteReserved)reserved.pop(), location(ctx), functions, statements); + } + + @Override + public Object visitFunction(FunctionContext ctx) { + reserved.push(new FunctionReserved()); + + String rtnType = ctx.decltype().getText(); + String name = ctx.ID().getText(); + List paramTypes = new ArrayList<>(); + List paramNames = new ArrayList<>(); + List statements = new ArrayList<>(); + + for (DecltypeContext decltype : ctx.parameters().decltype()) { + paramTypes.add(decltype.getText()); + } + + for (TerminalNode id : ctx.parameters().ID()) { + paramNames.add(id.getText()); + } + + for (StatementContext statement : ctx.block().statement()) { + statements.add((AStatement)visit(statement)); + } + + return new SFunction((FunctionReserved)reserved.pop(), location(ctx), rtnType, name, paramTypes, paramNames, statements); + } + + @Override + public Object visitParameters(ParametersContext ctx) { + throw location(ctx).createError(new IllegalStateException("Illegal tree structure.")); } @Override @@ -221,18 +271,16 @@ public final class Walker extends PainlessParserBaseVisitor { @Override public Object visitWhile(WhileContext ctx) { - if (settings.getMaxLoopCounter() > 0) { - reserved.usesLoop(); - } + reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter()); AExpression expression = (AExpression)visitExpression(ctx.expression()); if (ctx.trailer() != null) { SBlock block = (SBlock)visit(ctx.trailer()); - return new SWhile(location(ctx), settings.getMaxLoopCounter(), expression, block); + return new SWhile(location(ctx), expression, block); } else if (ctx.empty() != null) { - return new SWhile(location(ctx), settings.getMaxLoopCounter(), expression, null); + return new SWhile(location(ctx), expression, null); } else { throw location(ctx).createError(new IllegalStateException(" Illegal tree structure.")); } @@ -240,21 +288,17 @@ public final class Walker extends PainlessParserBaseVisitor { @Override public Object visitDo(DoContext ctx) { - if (settings.getMaxLoopCounter() > 0) { - reserved.usesLoop(); - } + reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter()); AExpression expression = (AExpression)visitExpression(ctx.expression()); SBlock block = (SBlock)visit(ctx.block()); - return new SDo(location(ctx), settings.getMaxLoopCounter(), block, expression); + return new SDo(location(ctx), block, expression); } @Override public Object visitFor(ForContext ctx) { - if (settings.getMaxLoopCounter() > 0) { - reserved.usesLoop(); - } + reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter()); ANode initializer = ctx.initializer() == null ? null : (ANode)visit(ctx.initializer()); AExpression expression = ctx.expression() == null ? null : (AExpression)visitExpression(ctx.expression()); @@ -263,9 +307,9 @@ public final class Walker extends PainlessParserBaseVisitor { if (ctx.trailer() != null) { SBlock block = (SBlock)visit(ctx.trailer()); - return new SFor(location(ctx), settings.getMaxLoopCounter(), initializer, expression, afterthought, block); + return new SFor(location(ctx), initializer, expression, afterthought, block); } else if (ctx.empty() != null) { - return new SFor(location(ctx), settings.getMaxLoopCounter(), initializer, expression, afterthought, null); + return new SFor(location(ctx), initializer, expression, afterthought, null); } else { throw location(ctx).createError(new IllegalStateException("Illegal tree structure.")); } @@ -273,16 +317,14 @@ public final class Walker extends PainlessParserBaseVisitor { @Override public Object visitEach(EachContext ctx) { - if (settings.getMaxLoopCounter() > 0) { - reserved.usesLoop(); - } + reserved.peek().setMaxLoopCounter(settings.getMaxLoopCounter()); String type = ctx.decltype().getText(); String name = ctx.ID().getText(); AExpression expression = (AExpression)visitExpression(ctx.expression()); SBlock block = (SBlock)visit(ctx.trailer()); - return new SEach(location(ctx), settings.getMaxLoopCounter(), type, name, expression, block); + return new SEach(location(ctx), type, name, expression, block); } @Override @@ -791,7 +833,18 @@ public final class Walker extends PainlessParserBaseVisitor { List links = new ArrayList<>(); links.add(new LVariable(location(ctx), name)); - reserved.markReserved(name); + reserved.peek().markReserved(name); + + return links; + } + + @Override + public Object visitCalllocal(CalllocalContext ctx) { + String name = ctx.ID().getText(); + @SuppressWarnings("unchecked") + List arguments = (List)visit(ctx.arguments()); + List links = new ArrayList<>(); + links.add(new LCallLocal(location(ctx), name, arguments)); return links; } @@ -825,7 +878,7 @@ public final class Walker extends PainlessParserBaseVisitor { @SuppressWarnings("unchecked") List arguments = (List)visit(ctx.arguments()); - return new LCall(location(ctx), name, arguments); + return new LCallInvoke(location(ctx), name, arguments); } @Override 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 c5494b978d8..fbaaa83a6ad 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 @@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -106,7 +106,7 @@ public abstract class AExpression extends ANode { /** * Checks for errors and collects data for the writing phase. */ - abstract void analyze(Variables variables); + abstract void analyze(Locals locals); /** * Writes ASM based on the data collected during the analysis phase. @@ -118,7 +118,7 @@ public abstract class AExpression extends ANode { * nodes with the constant variable set to a non-null value with {@link EConstant}. * @return The new child node for the parent node calling this method. */ - AExpression cast(Variables variables) { + AExpression cast(Locals locals) { final Cast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal); if (cast == null) { @@ -136,7 +136,7 @@ public abstract class AExpression extends ANode { // will already be the same. EConstant econstant = new EConstant(location, constant); - econstant.analyze(variables); + econstant.analyze(locals); if (!expected.equals(econstant.actual)) { throw createError(new IllegalStateException("Illegal tree structure.")); @@ -170,7 +170,7 @@ public abstract class AExpression extends ANode { constant = AnalyzerCaster.constCast(location, constant, cast); EConstant econstant = new EConstant(location, constant); - econstant.analyze(variables); + econstant.analyze(locals); if (!expected.equals(econstant.actual)) { throw createError(new IllegalStateException("Illegal tree structure.")); @@ -201,7 +201,7 @@ public abstract class AExpression extends ANode { // the EConstant will already be the same. EConstant econstant = new EConstant(location, constant); - econstant.analyze(variables); + econstant.analyze(locals); if (!actual.equals(econstant.actual)) { throw createError(new IllegalStateException("Illegal tree structure.")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ALink.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ALink.java index 19875ebeec5..6d52bfe0de1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ALink.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ALink.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -86,7 +86,7 @@ public abstract class ALink extends ANode { * def or a shortcut is used. Otherwise, returns itself. This will be * updated into the {@link EChain} node's list of links. */ - abstract ALink analyze(Variables variables); + abstract ALink analyze(Locals locals); /** * Write values before a load/store occurs such as an array index. 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 b9e6679f630..e34d174ac43 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; @@ -115,7 +115,7 @@ public abstract class AStatement extends ANode { /** * Checks for errors and collects data for the writing phase. */ - abstract void analyze(Variables variables); + abstract void analyze(Locals locals); /** * Writes ASM based on the data collected during the analysis phase. diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java index 32a6e2382f2..55c2cc18210 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBinary.java @@ -26,7 +26,7 @@ import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; /** * Represents a binary math expression. @@ -48,35 +48,35 @@ public final class EBinary extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (operation == Operation.MUL) { - analyzeMul(variables); + analyzeMul(locals); } else if (operation == Operation.DIV) { - analyzeDiv(variables); + analyzeDiv(locals); } else if (operation == Operation.REM) { - analyzeRem(variables); + analyzeRem(locals); } else if (operation == Operation.ADD) { - analyzeAdd(variables); + analyzeAdd(locals); } else if (operation == Operation.SUB) { - analyzeSub(variables); + analyzeSub(locals); } else if (operation == Operation.LSH) { - analyzeLSH(variables); + analyzeLSH(locals); } else if (operation == Operation.RSH) { - analyzeRSH(variables); + analyzeRSH(locals); } else if (operation == Operation.USH) { - analyzeUSH(variables); + analyzeUSH(locals); } else if (operation == Operation.BWAND) { - analyzeBWAnd(variables); + analyzeBWAnd(locals); } else if (operation == Operation.XOR) { - analyzeXor(variables); + analyzeXor(locals); } else if (operation == Operation.BWOR) { - analyzeBWOr(variables); + analyzeBWOr(locals); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } - private void analyzeMul(Variables variables) { + private void analyzeMul(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -112,7 +112,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeDiv(Variables variables) { + private void analyzeDiv(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -152,7 +152,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeRem(Variables variables) { + private void analyzeRem(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -192,7 +192,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeAdd(Variables variables) { + private void analyzeAdd(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -244,7 +244,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeSub(Variables variables) { + private void analyzeSub(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -280,7 +280,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeLSH(Variables variables) { + private void analyzeLSH(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -313,7 +313,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeRSH(Variables variables) { + private void analyzeRSH(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -346,7 +346,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeUSH(Variables variables) { + private void analyzeUSH(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -379,7 +379,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeBWAnd(Variables variables) { + private void analyzeBWAnd(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -411,7 +411,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeXor(Variables variables) { + private void analyzeXor(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -445,7 +445,7 @@ public final class EBinary extends AExpression { actual = promote; } - private void analyzeBWOr(Variables variables) { + private void analyzeBWOr(Locals variables) { left.analyze(variables); right.analyze(variables); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBool.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBool.java index dd221f040c4..5aa2daeeb34 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBool.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBool.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -44,14 +44,14 @@ public final class EBool extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { left.expected = Definition.BOOLEAN_TYPE; - left.analyze(variables); - left = left.cast(variables); + left.analyze(locals); + left = left.cast(locals); right.expected = Definition.BOOLEAN_TYPE; - right.analyze(variables); - right = right.cast(variables); + right.analyze(locals); + right = right.cast(locals); if (left.constant != null && right.constant != null) { if (operation == Operation.AND) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBoolean.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBoolean.java index 5065989600f..877e79549af 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBoolean.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EBoolean.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -36,7 +36,7 @@ public final class EBoolean extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { actual = Definition.BOOLEAN_TYPE; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java index 27974240125..7892b918ed1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECast.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -45,7 +45,7 @@ final class ECast extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { throw createError(new IllegalStateException("Illegal tree structure.")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java index 9b2968db565..e21a1dd3134 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EChain.java @@ -26,7 +26,7 @@ import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -59,20 +59,20 @@ public final class EChain extends AExpression { } @Override - void analyze(Variables variables) { - analyzeLinks(variables); + void analyze(Locals locals) { + analyzeLinks(locals); analyzeIncrDecr(); if (operation != null) { - analyzeCompound(variables); + analyzeCompound(locals); } else if (expression != null) { - analyzeWrite(variables); + analyzeWrite(locals); } else { analyzeRead(); } } - private void analyzeLinks(Variables variables) { + private void analyzeLinks(Locals variables) { ALink previous = null; int index = 0; @@ -153,7 +153,7 @@ public final class EChain extends AExpression { } } - private void analyzeCompound(Variables variables) { + private void analyzeCompound(Locals variables) { ALink last = links.get(links.size() - 1); expression.analyze(variables); @@ -214,7 +214,7 @@ public final class EChain extends AExpression { this.actual = read ? last.after : Definition.VOID_TYPE; } - private void analyzeWrite(Variables variables) { + private void analyzeWrite(Locals variables) { ALink last = links.get(links.size() - 1); // If the store node is a def node, we remove the cast to def from the expression diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EComp.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EComp.java index 407b59f92cf..ec84600d323 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EComp.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EComp.java @@ -25,7 +25,7 @@ import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -56,29 +56,29 @@ public final class EComp extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (operation == Operation.EQ) { - analyzeEq(variables); + analyzeEq(locals); } else if (operation == Operation.EQR) { - analyzeEqR(variables); + analyzeEqR(locals); } else if (operation == Operation.NE) { - analyzeNE(variables); + analyzeNE(locals); } else if (operation == Operation.NER) { - analyzeNER(variables); + analyzeNER(locals); } else if (operation == Operation.GTE) { - analyzeGTE(variables); + analyzeGTE(locals); } else if (operation == Operation.GT) { - analyzeGT(variables); + analyzeGT(locals); } else if (operation == Operation.LTE) { - analyzeLTE(variables); + analyzeLTE(locals); } else if (operation == Operation.LT) { - analyzeLT(variables); + analyzeLT(locals); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } - private void analyzeEq(Variables variables) { + private void analyzeEq(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -124,7 +124,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeEqR(Variables variables) { + private void analyzeEqR(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -166,7 +166,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeNE(Variables variables) { + private void analyzeNE(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -212,7 +212,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeNER(Variables variables) { + private void analyzeNER(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -254,7 +254,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeGTE(Variables variables) { + private void analyzeGTE(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -290,7 +290,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeGT(Variables variables) { + private void analyzeGT(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -326,7 +326,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeLTE(Variables variables) { + private void analyzeLTE(Locals variables) { left.analyze(variables); right.analyze(variables); @@ -362,7 +362,7 @@ public final class EComp extends AExpression { actual = Definition.BOOLEAN_TYPE; } - private void analyzeLT(Variables variables) { + private void analyzeLT(Locals variables) { left.analyze(variables); right.analyze(variables); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConditional.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConditional.java index 65231c45ed3..024e0c800fc 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConditional.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConditional.java @@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -45,10 +45,10 @@ public final class EConditional extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { condition.expected = Definition.BOOLEAN_TYPE; - condition.analyze(variables); - condition = condition.cast(variables); + condition.analyze(locals); + condition = condition.cast(locals); if (condition.constant != null) { throw createError(new IllegalArgumentException("Extraneous conditional statement.")); @@ -62,8 +62,8 @@ public final class EConditional extends AExpression { right.internal = internal; actual = expected; - left.analyze(variables); - right.analyze(variables); + left.analyze(locals); + right.analyze(locals); if (expected == null) { final Type promote = AnalyzerCaster.promoteConditional(left.actual, right.actual, left.constant, right.constant); @@ -73,8 +73,8 @@ public final class EConditional extends AExpression { actual = promote; } - left = left.cast(variables); - right = right.cast(variables); + left = left.cast(locals); + right = right.cast(locals); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConstant.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConstant.java index 8cb032c5c3f..267d32983b9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConstant.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EConstant.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -38,7 +38,7 @@ final class EConstant extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (constant instanceof String) { actual = Definition.STRING_TYPE; } else if (constant instanceof Double) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDecimal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDecimal.java index e59002c2a3b..0fd7fe46b51 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDecimal.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EDecimal.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -38,7 +38,7 @@ public final class EDecimal extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (value.endsWith("f") || value.endsWith("F")) { try { constant = Float.parseFloat(value.substring(0, value.length() - 1)); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java index 0a55727b5e9..007a5d59e59 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EExplicit.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -40,7 +40,7 @@ public final class EExplicit extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { try { actual = Definition.getType(this.type); } catch (IllegalArgumentException exception) { @@ -49,8 +49,8 @@ public final class EExplicit extends AExpression { child.expected = actual; child.explicit = true; - child.analyze(variables); - child = child.cast(variables); + child.analyze(locals); + child = child.cast(locals); } @Override @@ -58,11 +58,11 @@ public final class EExplicit extends AExpression { throw createError(new IllegalStateException("Illegal tree structure.")); } - AExpression cast(Variables variables) { + AExpression cast(Locals locals) { child.expected = expected; child.explicit = explicit; child.internal = internal; - return child.cast(variables); + return child.cast(locals); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java index 38b7a1b8ddb..ec45f89868a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EFunctionRef.java @@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Type; import static org.elasticsearch.painless.WriterConstants.LAMBDA_BOOTSTRAP_HANDLE; @@ -36,7 +36,7 @@ import java.lang.invoke.LambdaMetafactory; public class EFunctionRef extends AExpression { public final String type; public final String call; - + private FunctionRef ref; public EFunctionRef(Location location, String type, String call) { @@ -47,7 +47,7 @@ public class EFunctionRef extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (expected == null) { ref = null; actual = Definition.getType("String"); @@ -72,22 +72,22 @@ public class EFunctionRef extends AExpression { Type samMethodType = Type.getMethodType(ref.samMethodType.toMethodDescriptorString()); Type interfaceType = Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString()); if (ref.needsBridges()) { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, - LambdaMetafactory.FLAG_BRIDGES, - 1, + writer.invokeDynamic(ref.invokedName, + invokedType, + LAMBDA_BOOTSTRAP_HANDLE, + samMethodType, + ref.implMethodASM, + samMethodType, + LambdaMetafactory.FLAG_BRIDGES, + 1, interfaceType); } else { - writer.invokeDynamic(ref.invokedName, - invokedType, - LAMBDA_BOOTSTRAP_HANDLE, - samMethodType, - ref.implMethodASM, - samMethodType, + writer.invokeDynamic(ref.invokedName, + invokedType, + LAMBDA_BOOTSTRAP_HANDLE, + samMethodType, + ref.implMethodASM, + samMethodType, 0); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENull.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENull.java index 3f934da4968..eefc09c5946 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENull.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENull.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Opcodes; import org.elasticsearch.painless.MethodWriter; @@ -35,7 +35,7 @@ public final class ENull extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { isNull = true; if (expected != null) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENumeric.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENumeric.java index 53f3559c294..9abdc8f6a12 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENumeric.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENumeric.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -41,7 +41,7 @@ public final class ENumeric extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (value.endsWith("d") || value.endsWith("D")) { if (radix != 10) { throw createError(new IllegalStateException("Illegal tree structure.")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EUnary.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EUnary.java index 89d654cdf1f..2d7d8d4fd49 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EUnary.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EUnary.java @@ -25,7 +25,7 @@ import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -49,21 +49,21 @@ public final class EUnary extends AExpression { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (operation == Operation.NOT) { - analyzeNot(variables); + analyzeNot(locals); } else if (operation == Operation.BWNOT) { - analyzeBWNot(variables); + analyzeBWNot(locals); } else if (operation == Operation.ADD) { - analyzerAdd(variables); + analyzerAdd(locals); } else if (operation == Operation.SUB) { - analyzerSub(variables); + analyzerSub(locals); } else { throw createError(new IllegalStateException("Illegal tree structure.")); } } - void analyzeNot(Variables variables) { + void analyzeNot(Locals variables) { child.expected = Definition.BOOLEAN_TYPE; child.analyze(variables); child = child.cast(variables); @@ -75,7 +75,7 @@ public final class EUnary extends AExpression { actual = Definition.BOOLEAN_TYPE; } - void analyzeBWNot(Variables variables) { + void analyzeBWNot(Locals variables) { child.analyze(variables); Type promote = AnalyzerCaster.promoteNumeric(child.actual, false); @@ -102,7 +102,7 @@ public final class EUnary extends AExpression { actual = promote; } - void analyzerAdd(Variables variables) { + void analyzerAdd(Locals variables) { child.analyze(variables); Type promote = AnalyzerCaster.promoteNumeric(child.actual, true); @@ -133,7 +133,7 @@ public final class EUnary extends AExpression { actual = promote; } - void analyzerSub(Variables variables) { + void analyzerSub(Locals variables) { child.analyze(variables); Type promote = AnalyzerCaster.promoteNumeric(child.actual, true); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LArrayLength.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LArrayLength.java index c80cc8b8ada..38c1ae43907 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LArrayLength.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LArrayLength.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -38,7 +38,7 @@ public final class LArrayLength extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if ("length".equals(value)) { if (!load) { throw createError(new IllegalArgumentException("Must read array field [length].")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LBrace.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LBrace.java index b0816540c5e..91346b7a208 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LBrace.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LBrace.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -42,7 +42,7 @@ public final class LBrace extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before == null) { throw createError(new IllegalArgumentException("Illegal array access made without target.")); } @@ -51,18 +51,18 @@ public final class LBrace extends ALink { if (sort == Sort.ARRAY) { index.expected = Definition.INT_TYPE; - index.analyze(variables); - index = index.cast(variables); + index.analyze(locals); + index = index.cast(locals); after = Definition.getType(before.struct, before.dimensions - 1); return this; } else if (sort == Sort.DEF) { - return new LDefArray(location, index).copy(this).analyze(variables); + return new LDefArray(location, index).copy(this).analyze(locals); } else if (Map.class.isAssignableFrom(before.clazz)) { - return new LMapShortcut(location, index).copy(this).analyze(variables); + return new LMapShortcut(location, index).copy(this).analyze(locals); } else if (List.class.isAssignableFrom(before.clazz)) { - return new LListShortcut(location, index).copy(this).analyze(variables); + return new LListShortcut(location, index).copy(this).analyze(locals); } throw createError(new IllegalArgumentException("Illegal array access on type [" + before.name + "].")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCall.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallInvoke.java similarity index 79% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCall.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallInvoke.java index 53e66ee68b1..1f6e899b1dd 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCall.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallInvoke.java @@ -19,12 +19,12 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.Definition.MethodKey; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -32,14 +32,14 @@ import java.util.List; /** * Represents a method call or deferes to a def call. */ -public final class LCall extends ALink { +public final class LCallInvoke extends ALink { final String name; final List arguments; Method method = null; - public LCall(Location location, String name, List arguments) { + public LCallInvoke(Location location, String name, List arguments) { super(location, -1); this.name = name; @@ -47,7 +47,7 @@ public final class LCall extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before == null) { throw createError(new IllegalArgumentException("Illegal call [" + name + "] made without target.")); } else if (before.sort == Sort.ARRAY) { @@ -56,7 +56,7 @@ public final class LCall extends ALink { throw createError(new IllegalArgumentException("Cannot assign a value to a call [" + name + "].")); } - Definition.MethodKey methodKey = new Definition.MethodKey(name, arguments.size()); + MethodKey methodKey = new MethodKey(name, arguments.size()); Struct struct = before.struct; method = statik ? struct.staticMethods.get(methodKey) : struct.methods.get(methodKey); @@ -66,8 +66,8 @@ public final class LCall extends ALink { expression.expected = method.arguments.get(argument); expression.internal = true; - expression.analyze(variables); - arguments.set(argument, expression.cast(variables)); + expression.analyze(locals); + arguments.set(argument, expression.cast(locals)); } statement = true; @@ -78,11 +78,11 @@ public final class LCall extends ALink { ALink link = new LDefCall(location, name, arguments); link.copy(this); - return link.analyze(variables); + return link.analyze(locals); } - throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() + - "] arguments on type [" + struct.name + "].")); + throw createError(new IllegalArgumentException( + "Unknown call [" + name + "] with [" + arguments.size() + "] arguments on type [" + struct.name + "].")); } @Override @@ -105,10 +105,6 @@ public final class LCall extends ALink { } else { writer.invokeVirtual(method.owner.type, method.method); } - - if (!method.rtn.clazz.equals(method.handle.type().returnType())) { - writer.checkCast(method.rtn.type); - } } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallLocal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallLocal.java new file mode 100644 index 00000000000..31085171bf6 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCallLocal.java @@ -0,0 +1,99 @@ +/* + * 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.Method; +import org.elasticsearch.painless.Definition.MethodKey; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Location; +import org.elasticsearch.painless.MethodWriter; + +import java.util.List; + +import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; + +/** + * Represents a user-defined call. + */ +public class LCallLocal extends ALink { + + final String name; + final List arguments; + + Method method = null; + + public LCallLocal(Location location, String name, List arguments) { + super(location, -1); + + this.name = name; + this.arguments = arguments; + } + + @Override + ALink analyze(Locals locals) { + if (before != null) { + throw createError(new IllegalArgumentException("Illegal call [" + name + "] against an existing target.")); + } else if (store) { + throw createError(new IllegalArgumentException("Cannot assign a value to a call [" + name + "].")); + } + + MethodKey methodKey = new MethodKey(name, arguments.size()); + method = locals.getMethod(methodKey); + + if (method != null) { + for (int argument = 0; argument < arguments.size(); ++argument) { + AExpression expression = arguments.get(argument); + + expression.expected = method.arguments.get(argument); + expression.internal = true; + expression.analyze(locals); + arguments.set(argument, expression.cast(locals)); + } + + statement = true; + after = method.rtn; + + return this; + } + + throw createError(new IllegalArgumentException("Unknown call [" + name + "] with [" + arguments.size() + "] arguments.")); + } + + @Override + void write(MethodWriter writer) { + // Do nothing. + } + + @Override + void load(MethodWriter writer) { + writer.writeDebugInfo(location); + + for (AExpression argument : arguments) { + argument.write(writer); + } + + writer.invokeStatic(CLASS_TYPE, method.method); + } + + @Override + void store(MethodWriter writer) { + throw createError(new IllegalStateException("Illegal tree structure.")); + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCast.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCast.java index de4a22a5ad8..d2b4f83a823 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCast.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LCast.java @@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Cast; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -42,7 +42,7 @@ public final class LCast extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before == null) { throw createError(new IllegalStateException("Illegal cast without a target.")); } else if (store) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java index 5e28cd31b98..524f62eea15 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefArray.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Type; import org.elasticsearch.painless.MethodWriter; @@ -42,10 +42,10 @@ final class LDefArray extends ALink implements IDefLink { } @Override - ALink analyze(Variables variables) { - index.analyze(variables); + ALink analyze(Locals locals) { + index.analyze(locals); index.expected = index.actual; - index = index.cast(variables); + index = index.cast(locals); after = Definition.DEF_TYPE; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java index 28c57744860..871bd2273db 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -46,14 +46,14 @@ final class LDefCall extends ALink implements IDefLink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (arguments.size() > 63) { // technically, the limitation is just methods with > 63 params, containing method references. // this is because we are lazy and use a long as a bitset. we can always change to a "string" if need be. // but NEED NOT BE. nothing with this many parameters is in the whitelist and we do not support varargs. throw new UnsupportedOperationException("methods with > 63 arguments are currently not supported"); } - + recipe = 0; for (int argument = 0; argument < arguments.size(); ++argument) { AExpression expression = arguments.get(argument); @@ -62,9 +62,9 @@ final class LDefCall extends ALink implements IDefLink { recipe |= (1L << argument); // mark argument as deferred reference } expression.internal = true; - expression.analyze(variables); + expression.analyze(locals); expression.expected = expression.actual; - arguments.set(argument, expression.cast(variables)); + arguments.set(argument, expression.cast(locals)); } statement = true; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java index ec9d5bbe9e7..861dfe0df53 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefField.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Type; import org.elasticsearch.painless.MethodWriter; @@ -43,7 +43,7 @@ final class LDefField extends ALink implements IDefLink { @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { after = Definition.DEF_TYPE; return this; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LField.java index 8f34692d666..049dd2d8524 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LField.java @@ -24,7 +24,7 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Field; import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -46,7 +46,7 @@ public final class LField extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before == null) { throw createError(new IllegalArgumentException("Illegal field [" + value + "] access made without target.")); } @@ -54,9 +54,9 @@ public final class LField extends ALink { Sort sort = before.sort; if (sort == Sort.ARRAY) { - return new LArrayLength(location, value).copy(this).analyze(variables); + return new LArrayLength(location, value).copy(this).analyze(locals); } else if (sort == Sort.DEF) { - return new LDefField(location, value).copy(this).analyze(variables); + return new LDefField(location, value).copy(this).analyze(locals); } Struct struct = before.struct; @@ -81,17 +81,17 @@ public final class LField extends ALink { Character.toUpperCase(value.charAt(0)) + value.substring(1), 1)); if (shortcut) { - return new LShortcut(location, value).copy(this).analyze(variables); + return new LShortcut(location, value).copy(this).analyze(locals); } else { EConstant index = new EConstant(location, value); - index.analyze(variables); + index.analyze(locals); if (Map.class.isAssignableFrom(before.clazz)) { - return new LMapShortcut(location, index).copy(this).analyze(variables); + return new LMapShortcut(location, index).copy(this).analyze(locals); } if (List.class.isAssignableFrom(before.clazz)) { - return new LListShortcut(location, index).copy(this).analyze(variables); + return new LListShortcut(location, index).copy(this).analyze(locals); } } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LListShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LListShortcut.java index a8252b40770..7b0feb094e1 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LListShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LListShortcut.java @@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -42,7 +42,7 @@ final class LListShortcut extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { getter = before.struct.methods.get(new Definition.MethodKey("get", 1)); setter = before.struct.methods.get(new Definition.MethodKey("set", 2)); @@ -62,8 +62,8 @@ final class LListShortcut extends ALink { if ((load || store) && (!load || getter != null) && (!store || setter != null)) { index.expected = Definition.INT_TYPE; - index.analyze(variables); - index = index.cast(variables); + index.analyze(locals); + index = index.cast(locals); after = setter != null ? setter.arguments.get(1) : getter.rtn; } else { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LMapShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LMapShortcut.java index f31179d135a..5d4c5a9e50a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LMapShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LMapShortcut.java @@ -23,7 +23,7 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -42,7 +42,7 @@ final class LMapShortcut extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { getter = before.struct.methods.get(new Definition.MethodKey("get", 1)); setter = before.struct.methods.get(new Definition.MethodKey("put", 2)); @@ -61,8 +61,8 @@ final class LMapShortcut extends ALink { if ((load || store) && (!load || getter != null) && (!store || setter != null)) { index.expected = setter != null ? setter.arguments.get(0) : getter.arguments.get(0); - index.analyze(variables); - index = index.cast(variables); + index.analyze(locals); + index = index.cast(locals); after = setter != null ? setter.arguments.get(1) : getter.rtn; } else { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewArray.java index 75a4aecbe19..15aa01c1d26 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewArray.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Type; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -43,7 +43,7 @@ public final class LNewArray extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before != null) { throw createError(new IllegalArgumentException("Cannot create a new array with a target already defined.")); } else if (store) { @@ -64,8 +64,8 @@ public final class LNewArray extends ALink { AExpression expression = arguments.get(argument); expression.expected = Definition.INT_TYPE; - expression.analyze(variables); - arguments.set(argument, expression.cast(variables)); + expression.analyze(locals); + arguments.set(argument, expression.cast(locals)); } after = Definition.getType(type.struct, arguments.size()); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewObj.java index 2f80b254350..aeb6f64f9db 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LNewObj.java @@ -24,7 +24,7 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Struct; import org.elasticsearch.painless.Definition.Type; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; import java.util.List; @@ -47,7 +47,7 @@ public final class LNewObj extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before != null) { throw createError(new IllegalArgumentException("Illegal new call with a target already defined.")); } else if (store) { @@ -79,8 +79,8 @@ public final class LNewObj extends ALink { expression.expected = types[argument]; expression.internal = true; - expression.analyze(variables); - arguments.set(argument, expression.cast(variables)); + expression.analyze(locals); + arguments.set(argument, expression.cast(locals)); } statement = true; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LShortcut.java index a91970fa577..73cbc201db4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LShortcut.java @@ -24,7 +24,7 @@ import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -44,7 +44,7 @@ final class LShortcut extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { Struct struct = before.struct; getter = struct.methods.get(new Definition.MethodKey("get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LStatic.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LStatic.java index 98774513188..6f04e9a22c4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LStatic.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LStatic.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; /** * Represents a static type target. @@ -38,7 +38,7 @@ public final class LStatic extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before != null) { throw createError(new IllegalArgumentException("Illegal static type [" + type + "] after target already defined.")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LString.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LString.java index 6afe62d02ba..41eb027e3ab 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LString.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LString.java @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -36,7 +36,7 @@ public final class LString extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before != null) { throw createError(new IllegalArgumentException("Illegal String constant [" + string + "].")); } else if (store) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LVariable.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LVariable.java index 8fe6f17b0b5..64dc852117d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LVariable.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LVariable.java @@ -21,8 +21,8 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.Variables; -import org.elasticsearch.painless.Variables.Variable; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.Variable; import org.objectweb.asm.Opcodes; /** @@ -41,12 +41,12 @@ public final class LVariable extends ALink { } @Override - ALink analyze(Variables variables) { + ALink analyze(Locals locals) { if (before != null) { throw createError(new IllegalArgumentException("Illegal variable [" + name + "] access with target already defined.")); } - Variable variable = variables.getVariable(location, name); + Variable variable = locals.getVariable(location, name); if (store && variable.readonly) { throw createError(new IllegalArgumentException("Variable [" + variable.name + "] is read-only.")); 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 4dbbd80de54..5fec362ec17 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; @@ -40,7 +40,7 @@ public final class SBlock extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (statements == null || statements.isEmpty()) { throw createError(new IllegalArgumentException("A block must contain at least one statement.")); } @@ -58,7 +58,7 @@ public final class SBlock extends AStatement { statement.lastSource = lastSource && statement == last; statement.lastLoop = (beginLoop || lastLoop) && statement == last; - statement.analyze(variables); + statement.analyze(locals); methodEscape = statement.methodEscape; loopEscape = statement.loopEscape; 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 ca72dd0b55b..d4f8bfff4e4 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; @@ -33,7 +33,7 @@ public final class SBreak extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (!inLoop) { throw createError(new IllegalArgumentException("Break statement outside of a loop.")); } 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 8bcaf9d22cf..42de28e48f5 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 @@ -22,8 +22,8 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Type; -import org.elasticsearch.painless.Variables; -import org.elasticsearch.painless.Variables.Variable; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.Variable; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.elasticsearch.painless.MethodWriter; @@ -52,7 +52,7 @@ public final class SCatch extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { final Type type; try { @@ -65,14 +65,14 @@ public final class SCatch extends AStatement { throw createError(new ClassCastException("Not an exception type [" + this.type + "].")); } - variable = variables.addVariable(location, type, name, true, false); + variable = locals.addVariable(location, type, name, true, false); if (block != null) { block.lastSource = lastSource; block.inLoop = inLoop; block.lastLoop = lastLoop; - block.analyze(variables); + block.analyze(locals); methodEscape = block.methodEscape; loopEscape = block.loopEscape; 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 ef80766bdd1..802cf90087b 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; @@ -33,7 +33,7 @@ public final class SContinue extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (!inLoop) { throw createError(new IllegalArgumentException("Continue statement outside of a loop.")); } 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 1ff187afe29..5365e72888a 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; @@ -40,9 +40,9 @@ public final class SDeclBlock extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { for (SDeclaration declaration : declarations) { - declaration.analyze(variables); + declaration.analyze(locals); } statementCount = declarations.size(); 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 5f039184cb6..a62a287df9c 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 @@ -22,8 +22,8 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Type; -import org.elasticsearch.painless.Variables; -import org.elasticsearch.painless.Variables.Variable; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.Variable; import org.objectweb.asm.Opcodes; import org.elasticsearch.painless.MethodWriter; @@ -47,7 +47,7 @@ public final class SDeclaration extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { final Type type; try { @@ -58,11 +58,11 @@ public final class SDeclaration extends AStatement { if (expression != null) { expression.expected = type; - expression.analyze(variables); - expression = expression.cast(variables); + expression.analyze(locals); + expression = expression.cast(locals); } - variable = variables.addVariable(location, type, name, false, false); + variable = locals.addVariable(location, type, name, false, false); } @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 4989fb77a79..b25d903dc41 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 @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -30,21 +30,19 @@ import org.elasticsearch.painless.MethodWriter; */ public final class SDo extends AStatement { - final int maxLoopCounter; final SBlock block; AExpression condition; - public SDo(Location location, int maxLoopCounter, SBlock block, AExpression condition) { + public SDo(Location location, SBlock block, AExpression condition) { super(location); this.condition = condition; this.block = block; - this.maxLoopCounter = maxLoopCounter; } @Override - void analyze(Variables variables) { - variables.incrementScope(); + void analyze(Locals locals) { + locals.incrementScope(); if (block == null) { throw createError(new IllegalArgumentException("Extraneous do while loop.")); @@ -53,15 +51,15 @@ public final class SDo extends AStatement { block.beginLoop = true; block.inLoop = true; - block.analyze(variables); + block.analyze(locals); if (block.loopEscape && !block.anyContinue) { throw createError(new IllegalArgumentException("Extraneous do while loop.")); } condition.expected = Definition.BOOLEAN_TYPE; - condition.analyze(variables); - condition = condition.cast(variables); + condition.analyze(locals); + condition = condition.cast(locals); if (condition.constant != null) { final boolean continuous = (boolean)condition.constant; @@ -78,11 +76,11 @@ public final class SDo extends AStatement { statementCount = 1; - if (maxLoopCounter > 0) { - loopCounterSlot = variables.getVariable(location, "#loop").slot; + if (locals.getMaxLoopCounter() > 0) { + loopCounterSlot = locals.getVariable(location, "#loop").slot; } - variables.decrementScope(); + locals.decrementScope(); } @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 9a6fe798957..b42bd1aff5a 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 @@ -29,8 +29,8 @@ import org.elasticsearch.painless.Definition.Sort; import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.Variables; -import org.elasticsearch.painless.Variables.Variable; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.Variable; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -44,7 +44,6 @@ import static org.elasticsearch.painless.WriterConstants.ITERATOR_TYPE; */ public class SEach extends AStatement { - final int maxLoopCounter; final String type; final String name; AExpression expression; @@ -63,10 +62,9 @@ public class SEach extends AStatement { Variable iterator = null; Method method = null; - public SEach(Location location, int maxLoopCounter, String type, String name, AExpression expression, SBlock block) { + public SEach(Location location, String type, String name, AExpression expression, SBlock block) { super(location); - this.maxLoopCounter = maxLoopCounter; this.type = type; this.name = name; this.expression = expression; @@ -74,10 +72,10 @@ public class SEach extends AStatement { } @Override - void analyze(Variables variables) { - expression.analyze(variables); + void analyze(Locals locals) { + expression.analyze(locals); expression.expected = expression.actual; - expression = expression.cast(variables); + expression = expression.cast(locals); final Type type; @@ -87,25 +85,25 @@ public class SEach extends AStatement { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } - variables.incrementScope(); + locals.incrementScope(); - variable = variables.addVariable(location, type, name, true, false); + variable = locals.addVariable(location, type, name, true, false); if (expression.actual.sort == Sort.ARRAY) { - analyzeArray(variables, type); + analyzeArray(locals, type); } else if (expression.actual.sort == Sort.DEF || Iterable.class.isAssignableFrom(expression.actual.clazz)) { - analyzeIterable(variables, type); + analyzeIterable(locals, type); } else { - throw location.createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "].")); + throw createError(new IllegalArgumentException("Illegal for each type [" + expression.actual.name + "].")); } if (block == null) { - throw location.createError(new IllegalArgumentException("Extraneous for each loop.")); + throw createError(new IllegalArgumentException("Extraneous for each loop.")); } block.beginLoop = true; block.inLoop = true; - block.analyze(variables); + block.analyze(locals); block.statementCount = Math.max(1, block.statementCount); if (block.loopEscape && !block.anyContinue) { @@ -114,14 +112,14 @@ public class SEach extends AStatement { statementCount = 1; - if (maxLoopCounter > 0) { - loopCounterSlot = variables.getVariable(location, "#loop").slot; + if (locals.getMaxLoopCounter() > 0) { + loopCounterSlot = locals.getVariable(location, "#loop").slot; } - variables.decrementScope(); + locals.decrementScope(); } - void analyzeArray(Variables variables, Type type) { + void analyzeArray(Locals variables, Type type) { // 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 = variables.addVariable(location, expression.actual, "#array" + location.getOffset(), true, false); @@ -130,7 +128,7 @@ public class SEach extends AStatement { cast = AnalyzerCaster.getLegalCast(location, indexed, type, true, true); } - void analyzeIterable(Variables variables, Type type) { + void analyzeIterable(Locals variables, Type type) { // 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 = variables.addVariable(location, Definition.getType("Iterator"), "#itr" + location.getOffset(), true, false); @@ -141,7 +139,7 @@ public class SEach extends AStatement { method = expression.actual.struct.methods.get(new MethodKey("iterator", 0)); if (method == null) { - throw location.createError(new IllegalArgumentException( + throw createError(new IllegalArgumentException( "Unable to create iterator for the type [" + expression.actual.name + "].")); } } @@ -158,7 +156,7 @@ public class SEach extends AStatement { } else if (iterator != null) { writeIterable(writer); } else { - throw location.createError(new IllegalStateException("Illegal tree structure.")); + throw createError(new IllegalStateException("Illegal tree structure.")); } } @@ -197,7 +195,7 @@ public class SEach extends AStatement { if (method == null) { Type itr = Definition.getType("Iterator"); String desc = org.objectweb.asm.Type.getMethodDescriptor(itr.type, Definition.DEF_TYPE.type); - writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ITERATOR, 0); + writer.invokeDynamic("iterator", desc, DEF_BOOTSTRAP_HANDLE, (Object)DefBootstrap.ITERATOR, (Object)0); } else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) { writer.invokeInterface(method.owner.type, method.method); } else { 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 1bea07d5599..0a65664fa74 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 @@ -20,9 +20,10 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Definition.Sort; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -39,19 +40,22 @@ public final class SExpression extends AStatement { } @Override - void analyze(Variables variables) { - expression.read = lastSource; - expression.analyze(variables); + void analyze(Locals locals) { + Type rtnType = locals.getReturnType(); + boolean isVoid = rtnType.sort == Sort.VOID; + + expression.read = lastSource && !isVoid; + expression.analyze(locals); if (!lastSource && !expression.statement) { throw createError(new IllegalArgumentException("Not a statement.")); } - final boolean rtn = lastSource && expression.actual.sort != Sort.VOID; + boolean rtn = lastSource && !isVoid && expression.actual.sort != Sort.VOID; - expression.expected = rtn ? Definition.OBJECT_TYPE : expression.actual; + expression.expected = rtn ? rtnType : expression.actual; expression.internal = rtn; - expression = expression.cast(variables); + expression = expression.cast(locals); methodEscape = rtn; loopEscape = rtn; 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 04475a91a1a..cc9dee20138 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 @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -30,17 +30,14 @@ import org.elasticsearch.painless.MethodWriter; */ public final class SFor extends AStatement { - final int maxLoopCounter; ANode initializer; AExpression condition; AExpression afterthought; final SBlock block; - public SFor(Location location, int maxLoopCounter, - ANode initializer, AExpression condition, AExpression afterthought, SBlock block) { + public SFor(Location location, ANode initializer, AExpression condition, AExpression afterthought, SBlock block) { super(location); - this.maxLoopCounter = maxLoopCounter; this.initializer = initializer; this.condition = condition; this.afterthought = afterthought; @@ -48,19 +45,19 @@ public final class SFor extends AStatement { } @Override - void analyze(Variables variables) { - variables.incrementScope(); + void analyze(Locals locals) { + locals.incrementScope(); boolean continuous = false; if (initializer != null) { if (initializer instanceof AStatement) { - ((AStatement)initializer).analyze(variables); + ((AStatement)initializer).analyze(locals); } else if (initializer instanceof AExpression) { AExpression initializer = (AExpression)this.initializer; initializer.read = false; - initializer.analyze(variables); + initializer.analyze(locals); if (!initializer.statement) { throw createError(new IllegalArgumentException("Not a statement.")); @@ -72,8 +69,8 @@ public final class SFor extends AStatement { if (condition != null) { condition.expected = Definition.BOOLEAN_TYPE; - condition.analyze(variables); - condition = condition.cast(variables); + condition.analyze(locals); + condition = condition.cast(locals); if (condition.constant != null) { continuous = (boolean)condition.constant; @@ -92,7 +89,7 @@ public final class SFor extends AStatement { if (afterthought != null) { afterthought.read = false; - afterthought.analyze(variables); + afterthought.analyze(locals); if (!afterthought.statement) { throw createError(new IllegalArgumentException("Not a statement.")); @@ -103,7 +100,7 @@ public final class SFor extends AStatement { block.beginLoop = true; block.inLoop = true; - block.analyze(variables); + block.analyze(locals); if (block.loopEscape && !block.anyContinue) { throw createError(new IllegalArgumentException("Extraneous for loop.")); @@ -119,11 +116,11 @@ public final class SFor extends AStatement { statementCount = 1; - if (maxLoopCounter > 0) { - loopCounterSlot = variables.getVariable(location, "#loop").slot; + if (locals.getMaxLoopCounter() > 0) { + loopCounterSlot = locals.getVariable(location, "#loop").slot; } - variables.decrementScope(); + locals.decrementScope(); } @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 new file mode 100644 index 00000000000..16d53f23bc6 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -0,0 +1,163 @@ +/* + * 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.Definition.Method; +import org.elasticsearch.painless.Definition.Sort; +import org.elasticsearch.painless.Definition.Type; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.Parameter; +import org.elasticsearch.painless.Locals.FunctionReserved; +import org.elasticsearch.painless.Locals.Variable; +import org.elasticsearch.painless.Location; +import org.elasticsearch.painless.MethodWriter; +import org.objectweb.asm.Opcodes; + +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a user-defined function. + */ +public class SFunction extends AStatement { + final FunctionReserved reserved; + final String rtnTypeStr; + final String name; + final List paramTypeStrs; + final List paramNameStrs; + final List statements; + + Type rtnType = null; + List parameters = new ArrayList<>(); + Method method = null; + + Locals locals = null; + + public SFunction(FunctionReserved reserved, Location location, + String rtnType, String name, List paramTypes, List paramNames, List statements) { + super(location); + + this.reserved = reserved; + this.rtnTypeStr = rtnType; + this.name = name; + this.paramTypeStrs = Collections.unmodifiableList(paramTypes); + this.paramNameStrs = Collections.unmodifiableList(paramNames); + this.statements = Collections.unmodifiableList(statements); + } + + void generate() { + try { + rtnType = Definition.getType(rtnTypeStr); + } catch (IllegalArgumentException exception) { + throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "].")); + } + + if (paramTypeStrs.size() != paramNameStrs.size()) { + throw createError(new IllegalStateException("Illegal tree structure.")); + } + + Class[] paramClasses = new Class[this.paramTypeStrs.size()]; + List paramTypes = new ArrayList<>(); + + for (int param = 0; param < this.paramTypeStrs.size(); ++param) { + try { + Type paramType = Definition.getType(this.paramTypeStrs.get(param)); + + paramClasses[param] = paramType.clazz; + paramTypes.add(paramType); + parameters.add(new Parameter(location, paramNameStrs.get(param), paramType)); + } catch (IllegalArgumentException exception) { + throw createError(new IllegalArgumentException( + "Illegal parameter type [" + this.paramTypeStrs.get(param) + "] for function [" + name + "].")); + } + } + + org.objectweb.asm.commons.Method method = + new org.objectweb.asm.commons.Method(name, MethodType.methodType(rtnType.clazz, paramClasses).toMethodDescriptorString()); + this.method = new Method(name, null, rtnType, paramTypes, method, 0, null); + } + + @Override + void analyze(Locals locals) { + if (statements == null || statements.isEmpty()) { + throw createError(new IllegalArgumentException("Cannot generate an empty function [" + name + "].")); + } + + this.locals = new Locals(reserved, locals, rtnType, parameters); + locals = this.locals; + + locals.incrementScope(); + + AStatement last = statements.get(statements.size() - 1); + + 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) { + throw createError(new IllegalArgumentException("Unreachable statement.")); + } + + statement.lastSource = statement == last; + + statement.analyze(locals); + + methodEscape = statement.methodEscape; + allEscape = statement.allEscape; + } + + if (!methodEscape && rtnType.sort != Sort.VOID) { + throw createError(new IllegalArgumentException("Not all paths provide a return value for method [" + name + "].")); + } + + locals.decrementScope(); + } + + @Override + void write(MethodWriter writer) { + MethodWriter function = writer.newMethodWriter(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, method.method); + + if (reserved.getMaxLoopCounter() > 0) { + // if there is infinite loop protection, we do this once: + // int #loop = settings.getMaxLoopCounter() + + Variable loop = locals.getVariable(null, FunctionReserved.LOOP); + + function.push(reserved.getMaxLoopCounter()); + function.visitVarInsn(Opcodes.ISTORE, loop.slot); + } + + for (AStatement statement : statements) { + statement.write(function); + } + + if (!methodEscape) { + if (rtnType.sort == Sort.VOID) { + function.returnValue(); + } else { + throw createError(new IllegalStateException("Illegal tree structure.")); + } + } + + function.endMethod(); + } +} 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 954ddac9c6a..a46075af9a1 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 @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -41,10 +41,10 @@ public final class SIf extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { condition.expected = Definition.BOOLEAN_TYPE; - condition.analyze(variables); - condition = condition.cast(variables); + condition.analyze(locals); + condition = condition.cast(locals); if (condition.constant != null) { throw createError(new IllegalArgumentException("Extraneous if statement.")); @@ -58,9 +58,9 @@ public final class SIf extends AStatement { ifblock.inLoop = inLoop; ifblock.lastLoop = lastLoop; - variables.incrementScope(); - ifblock.analyze(variables); - variables.decrementScope(); + locals.incrementScope(); + ifblock.analyze(locals); + locals.decrementScope(); anyContinue = ifblock.anyContinue; anyBreak = ifblock.anyBreak; 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 1d801267054..22cbfe25614 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 @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -43,10 +43,10 @@ public final class SIfElse extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { condition.expected = Definition.BOOLEAN_TYPE; - condition.analyze(variables); - condition = condition.cast(variables); + condition.analyze(locals); + condition = condition.cast(locals); if (condition.constant != null) { throw createError(new IllegalArgumentException("Extraneous if statement.")); @@ -60,9 +60,9 @@ public final class SIfElse extends AStatement { ifblock.inLoop = inLoop; ifblock.lastLoop = lastLoop; - variables.incrementScope(); - ifblock.analyze(variables); - variables.decrementScope(); + locals.incrementScope(); + ifblock.analyze(locals); + locals.decrementScope(); anyContinue = ifblock.anyContinue; anyBreak = ifblock.anyBreak; @@ -76,9 +76,9 @@ public final class SIfElse extends AStatement { elseblock.inLoop = inLoop; elseblock.lastLoop = lastLoop; - variables.incrementScope(); - elseblock.analyze(variables); - variables.decrementScope(); + locals.incrementScope(); + elseblock.analyze(locals); + locals.decrementScope(); methodEscape = ifblock.methodEscape && elseblock.methodEscape; loopEscape = ifblock.loopEscape && elseblock.loopEscape; 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 bd9707eb6a4..e789f3372e3 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 @@ -19,9 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -38,11 +37,11 @@ public final class SReturn extends AStatement { } @Override - void analyze(Variables variables) { - expression.expected = Definition.OBJECT_TYPE; + void analyze(Locals locals) { + expression.expected = locals.getReturnType(); expression.internal = true; - expression.analyze(variables); - expression = expression.cast(variables); + expression.analyze(locals); + expression = expression.cast(locals); methodEscape = true; loopEscape = true; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java index 22fd2e3e28c..c7764b3d60a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java @@ -19,34 +19,85 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.Definition.MethodKey; +import org.elasticsearch.painless.Executable; +import org.elasticsearch.painless.Locals; +import org.elasticsearch.painless.Locals.ExecuteReserved; +import org.elasticsearch.painless.Locals.Variable; +import org.elasticsearch.painless.WriterConstants; +import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import java.util.BitSet; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static org.elasticsearch.painless.WriterConstants.BASE_CLASS_TYPE; +import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; +import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR; +import static org.elasticsearch.painless.WriterConstants.EXECUTE; +import static org.elasticsearch.painless.WriterConstants.MAP_GET; +import static org.elasticsearch.painless.WriterConstants.MAP_TYPE; /** * The root of all Painless trees. Contains a series of statements. */ public final class SSource extends AStatement { + final String name; + final String source; + final ExecuteReserved reserved; + final List functions; final List statements; - public SSource(Location location, List statements) { + private Locals locals; + private BitSet expressions; + private byte[] bytes; + + public SSource(String name, String source, ExecuteReserved reserved, Location location, + List functions, List statements) { super(location); + this.name = name; + this.source = source; + this.reserved = reserved; + this.functions = Collections.unmodifiableList(functions); this.statements = Collections.unmodifiableList(statements); } + public void analyze() { + Map methods = new HashMap<>(); + + for (SFunction function : functions) { + function.generate(); + + MethodKey key = new MethodKey(function.name, function.parameters.size()); + + if (methods.put(key, function.method) != null) { + throw createError(new IllegalArgumentException("Duplicate functions with name [" + function.name + "].")); + } + } + + locals = new Locals(reserved, methods); + analyze(locals); + } + @Override - public void analyze(Variables variables) { + void analyze(Locals locals) { + for (SFunction function : functions) { + function.analyze(locals); + } + if (statements == null || statements.isEmpty()) { throw createError(new IllegalArgumentException("Cannot generate an empty script.")); } - variables.incrementScope(); + locals.incrementScope(); AStatement last = statements.get(statements.size() - 1); @@ -59,17 +110,95 @@ public final class SSource extends AStatement { statement.lastSource = statement == last; - statement.analyze(variables); + statement.analyze(locals); methodEscape = statement.methodEscape; allEscape = statement.allEscape; } - variables.decrementScope(); + locals.decrementScope(); + } + + public void write() { + // Create the ClassWriter. + + int classFrames = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; + int classVersion = Opcodes.V1_8; + int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL; + String classBase = BASE_CLASS_TYPE.getInternalName(); + String className = CLASS_TYPE.getInternalName(); + String classInterfaces[] = reserved.usesScore() ? new String[] { WriterConstants.NEEDS_SCORE_TYPE.getInternalName() } : null; + + ClassWriter writer = new ClassWriter(classFrames); + writer.visit(classVersion, classAccess, className, null, classBase, classInterfaces); + writer.visitSource(Location.computeSourceName(name, source), null); + + // Create the execute MethodWriter. + + expressions = new BitSet(source.length()); + MethodWriter execute = new MethodWriter(Opcodes.ACC_PUBLIC, EXECUTE, writer, expressions); + + // Write the constructor. + + MethodWriter constructor = execute.newMethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR); + constructor.loadThis(); + constructor.loadArgs(); + constructor.invokeConstructor(org.objectweb.asm.Type.getType(Executable.class), CONSTRUCTOR); + constructor.returnValue(); + constructor.endMethod(); + + // Write the execute method. + + write(execute); + execute.endMethod(); + + // End writing the class and store the generated bytes. + + writer.visitEnd(); + bytes = writer.toByteArray(); } @Override - public void write(MethodWriter writer) { + void write(MethodWriter writer) { + for (SFunction function : functions) { + function.write(writer); + } + + if (reserved.usesScore()) { + // if the _score value is used, we do this once: + // final double _score = scorer.score(); + Variable scorer = locals.getVariable(null, ExecuteReserved.SCORER); + Variable score = locals.getVariable(null, ExecuteReserved.SCORE); + + writer.visitVarInsn(Opcodes.ALOAD, scorer.slot); + writer.invokeVirtual(WriterConstants.SCORER_TYPE, WriterConstants.SCORER_SCORE); + writer.visitInsn(Opcodes.F2D); + writer.visitVarInsn(Opcodes.DSTORE, score.slot); + } + + if (reserved.usesCtx()) { + // if the _ctx value is used, we do this once: + // final Map ctx = input.get("ctx"); + + Variable input = locals.getVariable(null, ExecuteReserved.PARAMS); + Variable ctx = locals.getVariable(null, ExecuteReserved.CTX); + + writer.visitVarInsn(Opcodes.ALOAD, input.slot); + writer.push(ExecuteReserved.CTX); + writer.invokeInterface(MAP_TYPE, MAP_GET); + writer.visitVarInsn(Opcodes.ASTORE, ctx.slot); + } + + if (reserved.getMaxLoopCounter() > 0) { + // if there is infinite loop protection, we do this once: + // int #loop = settings.getMaxLoopCounter() + + Variable loop = locals.getVariable(null, ExecuteReserved.LOOP); + + writer.push(reserved.getMaxLoopCounter()); + writer.visitVarInsn(Opcodes.ISTORE, loop.slot); + } + for (AStatement statement : statements) { statement.write(writer); } @@ -79,4 +208,12 @@ public final class SSource extends AStatement { writer.returnValue(); } } + + public BitSet getExpressions() { + return expressions; + } + + public byte[] getBytes() { + return bytes; + } } 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 af9d7a65990..db04d622839 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 @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.MethodWriter; /** @@ -38,10 +38,10 @@ public final class SThrow extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { expression.expected = Definition.EXCEPTION_TYPE; - expression.analyze(variables); - expression = expression.cast(variables); + expression.analyze(locals); + expression = expression.cast(locals); methodEscape = true; loopEscape = true; 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 c24c8273dba..42fffc759ce 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; @@ -43,7 +43,7 @@ public final class STry extends AStatement { } @Override - void analyze(Variables variables) { + void analyze(Locals locals) { if (block == null) { throw createError(new IllegalArgumentException("Extraneous try statement.")); } @@ -52,9 +52,9 @@ public final class STry extends AStatement { block.inLoop = inLoop; block.lastLoop = lastLoop; - variables.incrementScope(); - block.analyze(variables); - variables.decrementScope(); + locals.incrementScope(); + block.analyze(locals); + locals.decrementScope(); methodEscape = block.methodEscape; loopEscape = block.loopEscape; @@ -69,9 +69,9 @@ public final class STry extends AStatement { catc.inLoop = inLoop; catc.lastLoop = lastLoop; - variables.incrementScope(); - catc.analyze(variables); - variables.decrementScope(); + locals.incrementScope(); + catc.analyze(locals); + locals.decrementScope(); methodEscape &= catc.methodEscape; loopEscape &= catc.loopEscape; 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 59c1bb75ee8..20478a55aa0 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 @@ -21,7 +21,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.Variables; +import org.elasticsearch.painless.Locals; import org.objectweb.asm.Label; import org.elasticsearch.painless.MethodWriter; @@ -30,25 +30,23 @@ import org.elasticsearch.painless.MethodWriter; */ public final class SWhile extends AStatement { - final int maxLoopCounter; AExpression condition; final SBlock block; - public SWhile(Location location, int maxLoopCounter, AExpression condition, SBlock block) { + public SWhile(Location location, AExpression condition, SBlock block) { super(location); - this.maxLoopCounter = maxLoopCounter; this.condition = condition; this.block = block; } @Override - void analyze(Variables variables) { - variables.incrementScope(); + void analyze(Locals locals) { + locals.incrementScope(); condition.expected = Definition.BOOLEAN_TYPE; - condition.analyze(variables); - condition = condition.cast(variables); + condition.analyze(locals); + condition = condition.cast(locals); boolean continuous = false; @@ -68,7 +66,7 @@ public final class SWhile extends AStatement { block.beginLoop = true; block.inLoop = true; - block.analyze(variables); + block.analyze(locals); if (block.loopEscape && !block.anyContinue) { throw createError(new IllegalArgumentException("Extraneous while loop.")); @@ -84,11 +82,11 @@ public final class SWhile extends AStatement { statementCount = 1; - if (maxLoopCounter > 0) { - loopCounterSlot = variables.getVariable(location, "#loop").slot; + if (locals.getMaxLoopCounter() > 0) { + loopCounterSlot = locals.getVariable(location, "#loop").slot; } - variables.decrementScope(); + locals.decrementScope(); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java index 71cde33e979..8ab69366f24 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/package-info.java @@ -49,7 +49,8 @@ * {@link org.elasticsearch.painless.node.IDefLink} - A marker interface for all LDef* (link) nodes. * {@link org.elasticsearch.painless.node.LArrayLength} - Represents an array length field load. * {@link org.elasticsearch.painless.node.LBrace} - Represents an array load/store or defers to possible shortcuts. - * {@link org.elasticsearch.painless.node.LCall} - Represents a method call or defers to a def call. + * {@link org.elasticsearch.painless.node.LCallInvoke} - Represents a method call or defers to a def call. + * {@link org.elasticsearch.painless.node.LCallLocal} - Represents a user-defined call. * {@link org.elasticsearch.painless.node.LCast} - Represents a cast made in a variable/method chain. * {@link org.elasticsearch.painless.node.LDefArray} - Represents an array load/store or shortcut on a def type. (Internal only.) * {@link org.elasticsearch.painless.node.LDefCall} - Represents a method call made on a def type. (Internal only.) @@ -73,6 +74,7 @@ * {@link org.elasticsearch.painless.node.SEach} - Represents a for each loop shortcut for iterables. * {@link org.elasticsearch.painless.node.SExpression} - Represents the top-level node for an expression as a statement. * {@link org.elasticsearch.painless.node.SFor} - Represents a for loop. + * {@link org.elasticsearch.painless.node.SFunction} - Represents a user-defined function. * {@link org.elasticsearch.painless.node.SIf} - Represents an if block. * {@link org.elasticsearch.painless.node.SIfElse} - Represents an if/else block. * {@link org.elasticsearch.painless.node.SReturn} - Represents a return statement. diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionTests.java new file mode 100644 index 00000000000..48c09cd4025 --- /dev/null +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/FunctionTests.java @@ -0,0 +1,69 @@ +/* + * 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; + +public class FunctionTests extends ScriptTestCase { + public void testBasic() { + assertEquals(5, exec("int get() {5;} get()")); + } + + public void testReference() { + assertEquals(5, exec("void get(int[] x) {x[0] = 5;} int[] y = new int[1]; y[0] = 1; get(y); y[0]")); + } + + public void testConcat() { + assertEquals("xyxy", exec("String catcat(String single) {single + single;} catcat('xy')")); + } + + public void testMultiArgs() { + assertEquals(5, exec("int add(int x, int y) {return x + y;} int x = 1, y = 2; add(add(x, x), add(x, y))")); + } + + public void testMultiFuncs() { + assertEquals(1, exec("int add(int x, int y) {return x + y;} int sub(int x, int y) {return x - y;} add(2, sub(3, 4))")); + assertEquals(3, exec("int sub2(int x, int y) {sub(x, y) - y;} int sub(int x, int y) {return x - y;} sub2(5, 1)")); + } + + public void testRecursion() { + assertEquals(55, exec("int fib(int n) {if (n <= 1) return n; else return fib(n-1) + fib(n-2);} fib(10)")); + } + + public void testEmpty() { + Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("void test(int x) {} test()"); + }); + assertTrue(expected.getMessage().contains("Cannot generate an empty function")); + } + + public void testDuplicates() { + Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("void test(int x) {x = 2;} void test(def y) {y = 3;} test()"); + }); + assertTrue(expected.getMessage().contains("Duplicate functions")); + } + + public void testInfiniteLoop() { + Error expected = expectScriptThrows(PainlessError.class, () -> { + exec("void test() {boolean x = true; while (x) {}} test()"); + }); + assertTrue(expected.getMessage().contains( + "The maximum number of statements that can be executed in a loop has been reached.")); + } +} diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ReservedWordTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ReservedWordTests.java index 9136261c164..e1dbe9db0f7 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ReservedWordTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ReservedWordTests.java @@ -30,7 +30,7 @@ public class ReservedWordTests extends ScriptTestCase { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("int _score = 5; return _score;"); }); - assertTrue(expected.getMessage().contains("Variable name [_score] is reserved")); + assertTrue(expected.getMessage().contains("Variable [_score] is reserved")); } /** check that we can't write to _score, its read-only! */ @@ -46,7 +46,7 @@ public class ReservedWordTests extends ScriptTestCase { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("int doc = 5; return doc;"); }); - assertTrue(expected.getMessage().contains("Variable name [doc] is reserved")); + assertTrue(expected.getMessage().contains("Variable [doc] is reserved")); } /** check that we can't write to doc, its read-only! */ @@ -62,7 +62,7 @@ public class ReservedWordTests extends ScriptTestCase { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("int ctx = 5; return ctx;"); }); - assertTrue(expected.getMessage().contains("Variable name [ctx] is reserved")); + assertTrue(expected.getMessage().contains("Variable [ctx] is reserved")); } /** check that we can't write to ctx, its read-only! */ @@ -83,7 +83,7 @@ public class ReservedWordTests extends ScriptTestCase { IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { exec("int _value = 5; return _value;"); }); - assertTrue(expected.getMessage().contains("Variable name [_value] is reserved")); + assertTrue(expected.getMessage().contains("Variable [_value] is reserved")); } /** check that we can't write to _value, its read-only! */