From d9a92011bc0ee1e43b4930462ed1ee7bd30bc370 Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Sun, 8 Jul 2018 12:00:23 -0700 Subject: [PATCH] Painless: Restructure Definition/Whitelist (#31879) Create lookup package rename Definition to PainlessLookup and move to lookup package rename Definition.Method to PainlessMethod rename Definition.MethodKey to PainlessMethod rename Definition.Field to PainlessField rename Definition.Struct to PainlessClass rename Definition.Cast to PainlessCast rename Whitelist.Struct to WhitelistClass rename Whitelist.Constructor to WhitelistConstructor rename Whitelist.Method to WhitelistMethod rename Whitelist.Field to WhitelistField --- .../elasticsearch/painless/spi/Whitelist.java | 169 +------ .../painless/spi/WhitelistClass.java | 76 ++++ .../painless/spi/WhitelistConstructor.java | 48 ++ .../painless/spi/WhitelistField.java | 46 ++ .../painless/spi/WhitelistLoader.java | 66 +-- .../painless/spi/WhitelistMethod.java | 76 ++++ .../painless/AnalyzerCaster.java | 391 ++++++++-------- .../org/elasticsearch/painless/Compiler.java | 37 +- .../java/org/elasticsearch/painless/Def.java | 121 ++--- .../elasticsearch/painless/DefBootstrap.java | 65 +-- .../org/elasticsearch/painless/DefMath.java | 57 ++- .../elasticsearch/painless/FunctionRef.java | 39 +- .../org/elasticsearch/painless/Locals.java | 43 +- .../elasticsearch/painless/MethodWriter.java | 6 +- .../painless/PainlessExplainError.java | 6 +- .../painless/PainlessScriptEngine.java | 5 +- .../painless/ScriptClassInfo.java | 25 +- .../painless/WriterConstants.java | 15 +- .../painless/antlr/EnhancedPainlessLexer.java | 10 +- .../elasticsearch/painless/antlr/Walker.java | 14 +- .../painless/lookup/PainlessCast.java | 67 +++ .../painless/lookup/PainlessClass.java | 103 +++++ .../painless/lookup/PainlessField.java | 43 ++ .../PainlessLookup.java} | 420 +++--------------- .../painless/lookup/PainlessMethod.java | 130 ++++++ .../painless/lookup/PainlessMethodKey.java | 75 ++++ .../painless/node/AExpression.java | 8 +- .../painless/node/EAssignment.java | 8 +- .../elasticsearch/painless/node/EBinary.java | 26 +- .../painless/node/ECallLocal.java | 8 +- .../painless/node/ECapturingFunctionRef.java | 10 +- .../elasticsearch/painless/node/ECast.java | 10 +- .../elasticsearch/painless/node/EComp.java | 20 +- .../painless/node/EExplicit.java | 2 +- .../painless/node/EFunctionRef.java | 16 +- .../painless/node/EInstanceof.java | 8 +- .../elasticsearch/painless/node/ELambda.java | 22 +- .../painless/node/EListInit.java | 15 +- .../elasticsearch/painless/node/EMapInit.java | 15 +- .../painless/node/ENewArray.java | 2 +- .../elasticsearch/painless/node/ENewObj.java | 14 +- .../elasticsearch/painless/node/ENull.java | 4 +- .../elasticsearch/painless/node/EStatic.java | 2 +- .../elasticsearch/painless/node/EUnary.java | 12 +- .../elasticsearch/painless/node/PBrace.java | 10 +- .../painless/node/PCallInvoke.java | 18 +- .../elasticsearch/painless/node/PField.java | 31 +- .../painless/node/PSubCallInvoke.java | 6 +- .../painless/node/PSubDefArray.java | 2 +- .../painless/node/PSubDefCall.java | 2 +- .../painless/node/PSubDefField.java | 2 +- .../painless/node/PSubField.java | 10 +- .../painless/node/PSubListShortcut.java | 18 +- .../painless/node/PSubMapShortcut.java | 18 +- .../painless/node/PSubShortcut.java | 8 +- .../elasticsearch/painless/node/SCatch.java | 2 +- .../painless/node/SDeclaration.java | 2 +- .../elasticsearch/painless/node/SEach.java | 9 +- .../painless/node/SFunction.java | 18 +- .../elasticsearch/painless/node/SSource.java | 16 +- .../painless/node/SSubEachArray.java | 8 +- .../painless/node/SSubEachIterable.java | 21 +- .../painless/AnalyzerCasterTests.java | 4 +- .../painless/BaseClassTests.java | 47 +- .../elasticsearch/painless/DebugTests.java | 15 +- .../org/elasticsearch/painless/Debugger.java | 3 +- .../painless/DefBootstrapTests.java | 23 +- .../painless/PainlessDocGenerator.java | 75 ++-- .../painless/ScriptTestCase.java | 7 +- .../painless/node/NodeToStringTests.java | 46 +- 70 files changed, 1510 insertions(+), 1266 deletions(-) create mode 100644 modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistClass.java create mode 100644 modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistConstructor.java create mode 100644 modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistField.java create mode 100644 modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistMethod.java create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java rename modules/lang-painless/src/main/java/org/elasticsearch/painless/{Definition.java => lookup/PainlessLookup.java} (71%) create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java create mode 100644 modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethodKey.java diff --git a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/Whitelist.java b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/Whitelist.java index e715eb0090c7f..55b64b0420df1 100644 --- a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/Whitelist.java +++ b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/Whitelist.java @@ -24,13 +24,14 @@ import java.util.Objects; /** - * Whitelist contains data structures designed to be used to generate a white-list of Java classes, + * Whitelist contains data structures designed to be used to generate a whitelist of Java classes, * constructors, methods, and fields that can be used within a Painless script at both compile-time * and run-time. * - * A white-list consists of several pieces with {@link Struct}s as the top level. Each {@link Struct} - * will contain zero-to-many {@link Constructor}s, {@link Method}s, and {@link Field}s which are what - * will be available with a Painless script. See each individual white-list object for more detail. + * A whitelist consists of several pieces with {@link WhitelistClass}s as the top level. Each + * {@link WhitelistClass} will contain zero-to-many {@link WhitelistConstructor}s, {@link WhitelistMethod}s, and + * {@link WhitelistField}s which are what will be available with a Painless script. See each individual + * whitelist object for more detail. */ public final class Whitelist { @@ -54,166 +55,14 @@ public final class Whitelist { public static final List BASE_WHITELISTS = Collections.singletonList(WhitelistLoader.loadFromResourceFiles(Whitelist.class, BASE_WHITELIST_FILES)); - /** - * Struct represents the equivalent of a Java class in Painless complete with super classes, - * constructors, methods, and fields. In Painless a class is known as a struct primarily to avoid - * naming conflicts internally. There must be a one-to-one mapping of struct names to Java classes. - * Though, since multiple white-lists may be combined into a single white-list for a specific - * {@link org.elasticsearch.script.ScriptContext}, as long as multiple structs representing the same - * Java class have the same Painless type name and have legal constructor/method overloading they - * can be merged together. - * - * Structs in Painless allow for arity overloading for constructors and methods. Arity overloading - * means that multiple constructors are allowed for a single struct as long as they have a different - * number of parameter types, and multiples methods with the same name are allowed for a single struct - * as long as they have the same return type and a different number of parameter types. - * - * Structs will automatically extend other white-listed structs if the Java class they represent is a - * subclass of other structs including Java interfaces. - */ - public static final class Struct { - - /** Information about where this struct was white-listed from. Can be used for error messages. */ - public final String origin; - - /** The Java class name this struct represents. */ - public final String javaClassName; - - /** - * Allow the Java class name to only be specified as the fully-qualified name. - */ - public final boolean onlyFQNJavaClassName; - - /** The {@link List} of white-listed ({@link Constructor}s) available to this struct. */ - public final List whitelistConstructors; - - /** The {@link List} of white-listed ({@link Method}s) available to this struct. */ - public final List whitelistMethods; - - /** The {@link List} of white-listed ({@link Field}s) available to this struct. */ - public final List whitelistFields; - - /** Standard constructor. All values must be not {@code null}. */ - public Struct(String origin, String javaClassName, boolean onlyFQNJavaClassName, - List whitelistConstructors, List whitelistMethods, List whitelistFields) { - this.origin = Objects.requireNonNull(origin); - this.javaClassName = Objects.requireNonNull(javaClassName); - this.onlyFQNJavaClassName = onlyFQNJavaClassName; - - this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors)); - this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods)); - this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields)); - } - } - - /** - * Constructor represents the equivalent of a Java constructor available as a white-listed struct - * constructor within Painless. Constructors for Painless structs may be accessed exactly as - * constructors for Java classes are using the 'new' keyword. Painless structs may have multiple - * constructors as long as they comply with arity overloading described for {@link Struct}. - */ - public static final class Constructor { - - /** Information about where this constructor was white-listed from. Can be used for error messages. */ - public final String origin; - - /** - * A {@link List} of {@link String}s that are the Painless type names for the parameters of the - * constructor which can be used to look up the Java constructor through reflection. - */ - public final List painlessParameterTypeNames; - - /** Standard constructor. All values must be not {@code null}. */ - public Constructor(String origin, List painlessParameterTypeNames) { - this.origin = Objects.requireNonNull(origin); - this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames)); - } - } - - /** - * Method represents the equivalent of a Java method available as a white-listed struct method - * within Painless. Methods for Painless structs may be accessed exactly as methods for Java classes - * are using the '.' operator on an existing struct variable/field. Painless structs may have multiple - * methods with the same name as long as they comply with arity overloading described for {@link Method}. - * - * Structs may also have additional methods that are not part of the Java class the struct represents - - * these are known as augmented methods. An augmented method can be added to a struct as a part of any - * Java class as long as the method is static and the first parameter of the method is the Java class - * represented by the struct. Note that the augmented method's parent Java class does not need to be - * white-listed. - */ - public static class Method { - - /** Information about where this method was white-listed from. Can be used for error messages. */ - public final String origin; - - /** - * The Java class name for the owner of an augmented method. If the method is not augmented - * this should be {@code null}. - */ - public final String javaAugmentedClassName; - - /** The Java method name used to look up the Java method through reflection. */ - public final String javaMethodName; - - /** - * The Painless type name for the return type of the method which can be used to look up the Java - * method through reflection. - */ - public final String painlessReturnTypeName; - - /** - * A {@link List} of {@link String}s that are the Painless type names for the parameters of the - * method which can be used to look up the Java method through reflection. - */ - public final List painlessParameterTypeNames; - - /** - * Standard constructor. All values must be not {@code null} with the exception of jAugmentedClass; - * jAugmentedClass will be {@code null} unless the method is augmented as described in the class documentation. - */ - public Method(String origin, String javaAugmentedClassName, String javaMethodName, - String painlessReturnTypeName, List painlessParameterTypeNames) { - this.origin = Objects.requireNonNull(origin); - this.javaAugmentedClassName = javaAugmentedClassName; - this.javaMethodName = javaMethodName; - this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName); - this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames)); - } - } - - /** - * Field represents the equivalent of a Java field available as a white-listed struct field - * within Painless. Fields for Painless structs may be accessed exactly as fields for Java classes - * are using the '.' operator on an existing struct variable/field. - */ - public static class Field { - - /** Information about where this method was white-listed from. Can be used for error messages. */ - public final String origin; - - /** The Java field name used to look up the Java field through reflection. */ - public final String javaFieldName; - - /** The Painless type name for the field which can be used to look up the Java field through reflection. */ - public final String painlessFieldTypeName; - - /** Standard constructor. All values must be not {@code null}. */ - public Field(String origin, String javaFieldName, String painlessFieldTypeName) { - this.origin = Objects.requireNonNull(origin); - this.javaFieldName = Objects.requireNonNull(javaFieldName); - this.painlessFieldTypeName = Objects.requireNonNull(painlessFieldTypeName); - } - } - - /** The {@link ClassLoader} used to look up the white-listed Java classes, constructors, methods, and fields. */ + /** The {@link ClassLoader} used to look up the whitelisted Java classes, constructors, methods, and fields. */ public final ClassLoader javaClassLoader; - /** The {@link List} of all the white-listed Painless structs. */ - public final List whitelistStructs; + /** The {@link List} of all the whitelisted Painless classes. */ + public final List whitelistStructs; /** Standard constructor. All values must be not {@code null}. */ - public Whitelist(ClassLoader javaClassLoader, List whitelistStructs) { + public Whitelist(ClassLoader javaClassLoader, List whitelistStructs) { this.javaClassLoader = Objects.requireNonNull(javaClassLoader); this.whitelistStructs = Collections.unmodifiableList(Objects.requireNonNull(whitelistStructs)); } diff --git a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistClass.java b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistClass.java new file mode 100644 index 0000000000000..12aa5f5bdd634 --- /dev/null +++ b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistClass.java @@ -0,0 +1,76 @@ +/* + * 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.spi; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Class represents the equivalent of a Java class in Painless complete with super classes, + * constructors, methods, and fields. There must be a one-to-one mapping of class names to Java + * classes. Though, since multiple whitelists may be combined into a single whitelist for a + * specific context, as long as multiple classes representing the same Java class have the same + * class name and have legal constructor/method overloading they can be merged together. + * + * Classes in Painless allow for arity overloading for constructors and methods. Arity overloading + * means that multiple constructors are allowed for a single class as long as they have a different + * number of parameters, and multiples methods with the same name are allowed for a single class + * as long as they have the same return type and a different number of parameters. + * + * Classes will automatically extend other whitelisted classes if the Java class they represent is a + * subclass of other classes including Java interfaces. + */ +public final class WhitelistClass { + + /** Information about where this class was white-listed from. Can be used for error messages. */ + public final String origin; + + /** The Java class name this class represents. */ + public final String javaClassName; + + /** + * Allow the Java class name to only be specified as the fully-qualified name. + */ + public final boolean onlyFQNJavaClassName; + + /** The {@link List} of whitelisted ({@link WhitelistConstructor}s) available to this class. */ + public final List whitelistConstructors; + + /** The {@link List} of whitelisted ({@link WhitelistMethod}s) available to this class. */ + public final List whitelistMethods; + + /** The {@link List} of whitelisted ({@link WhitelistField}s) available to this class. */ + public final List whitelistFields; + + /** Standard constructor. All values must be not {@code null}. */ + public WhitelistClass(String origin, String javaClassName, boolean onlyFQNJavaClassName, + List whitelistConstructors, + List whitelistMethods, + List whitelistFields) { + this.origin = Objects.requireNonNull(origin); + this.javaClassName = Objects.requireNonNull(javaClassName); + this.onlyFQNJavaClassName = onlyFQNJavaClassName; + + this.whitelistConstructors = Collections.unmodifiableList(Objects.requireNonNull(whitelistConstructors)); + this.whitelistMethods = Collections.unmodifiableList(Objects.requireNonNull(whitelistMethods)); + this.whitelistFields = Collections.unmodifiableList(Objects.requireNonNull(whitelistFields)); + } +} diff --git a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistConstructor.java b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistConstructor.java new file mode 100644 index 0000000000000..0e70552760208 --- /dev/null +++ b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistConstructor.java @@ -0,0 +1,48 @@ +/* + * 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.spi; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Constructor represents the equivalent of a Java constructor available as a whitelisted class + * constructor within Painless. Constructors for Painless classes may be accessed exactly as + * constructors for Java classes are using the 'new' keyword. Painless classes may have multiple + * constructors as long as they comply with arity overloading described for {@link WhitelistClass}. + */ +public final class WhitelistConstructor { + + /** Information about where this constructor was whitelisted from. Can be used for error messages. */ + public final String origin; + + /** + * A {@link List} of {@link String}s that are the Painless type names for the parameters of the + * constructor which can be used to look up the Java constructor through reflection. + */ + public final List painlessParameterTypeNames; + + /** Standard constructor. All values must be not {@code null}. */ + public WhitelistConstructor(String origin, List painlessParameterTypeNames) { + this.origin = Objects.requireNonNull(origin); + this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames)); + } +} diff --git a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistField.java b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistField.java new file mode 100644 index 0000000000000..116aea98fcf89 --- /dev/null +++ b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistField.java @@ -0,0 +1,46 @@ +/* + * 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.spi; + +import java.util.Objects; + +/** + * Field represents the equivalent of a Java field available as a whitelisted class field + * within Painless. Fields for Painless classes may be accessed exactly as fields for Java classes + * are using the '.' operator on an existing class variable/field. + */ +public class WhitelistField { + + /** Information about where this method was whitelisted from. Can be used for error messages. */ + public final String origin; + + /** The Java field name used to look up the Java field through reflection. */ + public final String javaFieldName; + + /** The Painless type name for the field which can be used to look up the Java field through reflection. */ + public final String painlessFieldTypeName; + + /** Standard constructor. All values must be not {@code null}. */ + public WhitelistField(String origin, String javaFieldName, String painlessFieldTypeName) { + this.origin = Objects.requireNonNull(origin); + this.javaFieldName = Objects.requireNonNull(javaFieldName); + this.painlessFieldTypeName = Objects.requireNonNull(painlessFieldTypeName); + } +} diff --git a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistLoader.java b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistLoader.java index 8817bfa274c60..b104d03f1ea82 100644 --- a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistLoader.java +++ b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistLoader.java @@ -39,25 +39,25 @@ public final class WhitelistLoader { * {@link String}s with a single {@link Class} to be be used to load the resources where each {@link String} * is the path of a single text file. The {@link Class}'s {@link ClassLoader} will be used to lookup the Java * reflection objects for each individual {@link Class}, {@link Constructor}, {@link Method}, and {@link Field} - * specified as part of the white-list in the text file. + * specified as part of the whitelist in the text file. * - * A single pass is made through each file to collect all the information about each struct, constructor, method, - * and field. Most validation will be done at a later point after all white-lists have been gathered and their + * A single pass is made through each file to collect all the information about each class, constructor, method, + * and field. Most validation will be done at a later point after all whitelists have been gathered and their * merging takes place. * * A painless type name is one of the following: *
    *
  • def - The Painless dynamic type which is automatically included without a need to be - * white-listed.
  • - *
  • fully-qualified Java type name - Any white-listed Java class will have the equivalent name as + * whitelisted.
  • + *
  • fully-qualified Java type name - Any whitelisted Java class will have the equivalent name as * a Painless type name with the exception that any dollar symbols used as part of inner classes will * be replaced with dot symbols.
  • *
  • short Java type name - The text after the final dot symbol of any specified Java class. A - * short type Java name may be excluded by using the 'only_fqn' token during Painless struct parsing + * short type Java name may be excluded by using the 'only_fqn' token during Painless class parsing * as described later.
  • *
* - * The following can be parsed from each white-list text file: + * The following can be parsed from each whitelist text file: *
    *
  • Blank lines will be ignored by the parser.
  • *
  • Comments may be created starting with a pound '#' symbol and end with a newline. These will @@ -71,19 +71,19 @@ public final class WhitelistLoader { *
      *
    • A constructor may be specified starting with an opening parenthesis, followed by a * comma-delimited list of Painless type names corresponding to the type/class names for - * the equivalent Java parameter types (these must be white-listed as well), a closing + * the equivalent Java parameter types (these must be whitelisted as well), a closing * parenthesis, and a newline.
    • *
    • A method may be specified starting with a Painless type name for the return type, * followed by the Java name of the method (which will also be the Painless name for the * method), an opening parenthesis, a comma-delimited list of Painless type names * corresponding to the type/class names for the equivalent Java parameter types - * (these must be white-listed as well), a closing parenthesis, and a newline.
    • + * (these must be whitelisted as well), a closing parenthesis, and a newline. *
    • An augmented method may be specified starting with a Painless type name for the return * type, followed by the fully qualified Java name of the class the augmented method is - * part of (this class does not need to be white-listed), the Java name of the method + * part of (this class does not need to be whitelisted), the Java name of the method * (which will also be the Painless name for the method), an opening parenthesis, a * comma-delimited list of Painless type names corresponding to the type/class names - * for the equivalent Java parameter types (these must be white-listed as well), a closing + * for the equivalent Java parameter types (these must be whitelisted as well), a closing * parenthesis, and a newline.
    • *
    • A field may be specified starting with a Painless type name for the equivalent Java type * of the field, followed by the Java name of the field (which all be the Painless name @@ -99,7 +99,7 @@ public final class WhitelistLoader { * fully-qualified Java class name. Method argument types, method return types, and field types * must be specified with Painless type names (def, fully-qualified, or short) as described earlier. * - * The following example is used to create a single white-list text file: + * The following example is used to create a single whitelist text file: * * {@code * # primitive types @@ -132,10 +132,10 @@ public final class WhitelistLoader { * } */ public static Whitelist loadFromResourceFiles(Class resource, String... filepaths) { - List whitelistStructs = new ArrayList<>(); + List whitelistStructs = new ArrayList<>(); - // Execute a single pass through the white-list text files. This will gather all the - // constructors, methods, augmented methods, and fields for each white-listed struct. + // Execute a single pass through the whitelist text files. This will gather all the + // constructors, methods, augmented methods, and fields for each whitelisted class. for (String filepath : filepaths) { String line; int number = -1; @@ -146,9 +146,9 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep String whitelistStructOrigin = null; String javaClassName = null; boolean onlyFQNJavaClassName = false; - List whitelistConstructors = null; - List whitelistMethods = null; - List whitelistFields = null; + List whitelistConstructors = null; + List whitelistMethods = null; + List whitelistFields = null; while ((line = reader.readLine()) != null) { number = reader.getLineNumber(); @@ -159,13 +159,13 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep continue; } - // Handle a new struct by resetting all the variables necessary to construct a new Whitelist.Struct for the white-list. + // Handle a new class by resetting all the variables necessary to construct a new WhitelistClass for the whitelist. // Expects the following format: 'class' ID 'only_fqn'? '{' '\n' if (line.startsWith("class ")) { // Ensure the final token of the line is '{'. if (line.endsWith("{") == false) { throw new IllegalArgumentException( - "invalid struct definition: failed to parse class opening bracket [" + line + "]"); + "invalid class definition: failed to parse class opening bracket [" + line + "]"); } // Parse the Java class name. @@ -175,29 +175,29 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep if (tokens.length == 2 && "only_fqn".equals(tokens[1])) { onlyFQNJavaClassName = true; } else if (tokens.length != 1) { - throw new IllegalArgumentException("invalid struct definition: failed to parse class name [" + line + "]"); + throw new IllegalArgumentException("invalid class definition: failed to parse class name [" + line + "]"); } whitelistStructOrigin = "[" + filepath + "]:[" + number + "]"; javaClassName = tokens[0]; - // Reset all the constructors, methods, and fields to support a new struct. + // Reset all the constructors, methods, and fields to support a new class. whitelistConstructors = new ArrayList<>(); whitelistMethods = new ArrayList<>(); whitelistFields = new ArrayList<>(); - // Handle the end of a struct, by creating a new Whitelist.Struct with all the previously gathered - // constructors, methods, augmented methods, and fields, and adding it to the list of white-listed structs. + // Handle the end of a class, by creating a new WhitelistClass with all the previously gathered + // constructors, methods, augmented methods, and fields, and adding it to the list of whitelisted classes. // Expects the following format: '}' '\n' } else if (line.equals("}")) { if (javaClassName == null) { - throw new IllegalArgumentException("invalid struct definition: extraneous closing bracket"); + throw new IllegalArgumentException("invalid class definition: extraneous closing bracket"); } - whitelistStructs.add(new Whitelist.Struct(whitelistStructOrigin, javaClassName, onlyFQNJavaClassName, + whitelistStructs.add(new WhitelistClass(whitelistStructOrigin, javaClassName, onlyFQNJavaClassName, whitelistConstructors, whitelistMethods, whitelistFields)); - // Set all the variables to null to ensure a new struct definition is found before other parsable values. + // Set all the variables to null to ensure a new class definition is found before other parsable values. whitelistStructOrigin = null; javaClassName = null; onlyFQNJavaClassName = false; @@ -210,7 +210,7 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep // Mark the origin of this parsable object. String origin = "[" + filepath + "]:[" + number + "]"; - // Ensure we have a defined struct before adding any constructors, methods, augmented methods, or fields. + // Ensure we have a defined class before adding any constructors, methods, augmented methods, or fields. if (javaClassName == null) { throw new IllegalArgumentException("invalid object definition: expected a class name [" + line + "]"); } @@ -232,7 +232,7 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep tokens = new String[0]; } - whitelistConstructors.add(new Whitelist.Constructor(origin, Arrays.asList(tokens))); + whitelistConstructors.add(new WhitelistConstructor(origin, Arrays.asList(tokens))); // Handle the case for a method or augmented method definition. // Expects the following format: ID ID? ID '(' ( ID ( ',' ID )* )? ')' '\n' @@ -271,7 +271,7 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep tokens = new String[0]; } - whitelistMethods.add(new Whitelist.Method(origin, javaAugmentedClassName, javaMethodName, + whitelistMethods.add(new WhitelistMethod(origin, javaAugmentedClassName, javaMethodName, painlessReturnTypeName, Arrays.asList(tokens))); // Handle the case for a field definition. @@ -285,14 +285,14 @@ public static Whitelist loadFromResourceFiles(Class resource, String... filep throw new IllegalArgumentException("invalid field definition: unexpected format [" + line + "]"); } - whitelistFields.add(new Whitelist.Field(origin, tokens[1], tokens[0])); + whitelistFields.add(new WhitelistField(origin, tokens[1], tokens[0])); } } } - // Ensure all structs end with a '}' token before the end of the file. + // Ensure all classes end with a '}' token before the end of the file. if (javaClassName != null) { - throw new IllegalArgumentException("invalid struct definition: expected closing bracket"); + throw new IllegalArgumentException("invalid class definition: expected closing bracket"); } } catch (Exception exception) { throw new RuntimeException("error in [" + filepath + "] at line [" + number + "]", exception); diff --git a/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistMethod.java b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistMethod.java new file mode 100644 index 0000000000000..df86619055b08 --- /dev/null +++ b/modules/lang-painless/spi/src/main/java/org/elasticsearch/painless/spi/WhitelistMethod.java @@ -0,0 +1,76 @@ +/* + * 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.spi; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Method represents the equivalent of a Java method available as a whitelisted class method + * within Painless. Methods for Painless classes may be accessed exactly as methods for Java classes + * are using the '.' operator on an existing class variable/field. Painless classes may have multiple + * methods with the same name as long as they comply with arity overloading described for {@link WhitelistMethod}. + * + * Classes may also have additional methods that are not part of the Java class the class represents - + * these are known as augmented methods. An augmented method can be added to a class as a part of any + * Java class as long as the method is static and the first parameter of the method is the Java class + * represented by the class. Note that the augmented method's parent Java class does not need to be + * whitelisted. + */ +public class WhitelistMethod { + + /** Information about where this method was whitelisted from. Can be used for error messages. */ + public final String origin; + + /** + * The Java class name for the owner of an augmented method. If the method is not augmented + * this should be {@code null}. + */ + public final String javaAugmentedClassName; + + /** The Java method name used to look up the Java method through reflection. */ + public final String javaMethodName; + + /** + * The Painless type name for the return type of the method which can be used to look up the Java + * method through reflection. + */ + public final String painlessReturnTypeName; + + /** + * A {@link List} of {@link String}s that are the Painless type names for the parameters of the + * method which can be used to look up the Java method through reflection. + */ + public final List painlessParameterTypeNames; + + /** + * Standard constructor. All values must be not {@code null} with the exception of jAugmentedClass; + * jAugmentedClass will be {@code null} unless the method is augmented as described in the class documentation. + */ + public WhitelistMethod(String origin, String javaAugmentedClassName, String javaMethodName, + String painlessReturnTypeName, List painlessParameterTypeNames) { + this.origin = Objects.requireNonNull(origin); + this.javaAugmentedClassName = javaAugmentedClassName; + this.javaMethodName = javaMethodName; + this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName); + this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames)); + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java index abba62de39c19..69ef57faad63e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/AnalyzerCaster.java @@ -19,8 +19,9 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessCast; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import java.util.Objects; @@ -30,7 +31,7 @@ */ public final class AnalyzerCaster { - public static Cast getLegalCast(Location location, Class actual, Class expected, boolean explicit, boolean internal) { + public static PainlessCast getLegalCast(Location location, Class actual, Class expected, boolean explicit, boolean internal) { Objects.requireNonNull(actual); Objects.requireNonNull(expected); @@ -40,421 +41,421 @@ public static Cast getLegalCast(Location location, Class actual, Class exp if (actual == def.class) { if (expected == boolean.class) { - return Cast.unboxTo(def.class, Boolean.class, explicit, boolean.class); + return PainlessCast.unboxTo(def.class, Boolean.class, explicit, boolean.class); } else if (expected == byte.class) { - return Cast.unboxTo(def.class, Byte.class, explicit, byte.class); + return PainlessCast.unboxTo(def.class, Byte.class, explicit, byte.class); } else if (expected == short.class) { - return Cast.unboxTo(def.class, Short.class, explicit, short.class); + return PainlessCast.unboxTo(def.class, Short.class, explicit, short.class); } else if (expected == char.class) { - return Cast.unboxTo(def.class, Character.class, explicit, char.class); + return PainlessCast.unboxTo(def.class, Character.class, explicit, char.class); } else if (expected == int.class) { - return Cast.unboxTo(def.class, Integer.class, explicit, int.class); + return PainlessCast.unboxTo(def.class, Integer.class, explicit, int.class); } else if (expected == long.class) { - return Cast.unboxTo(def.class, Long.class, explicit, long.class); + return PainlessCast.unboxTo(def.class, Long.class, explicit, long.class); } else if (expected == float.class) { - return Cast.unboxTo(def.class, Float.class, explicit, float.class); + return PainlessCast.unboxTo(def.class, Float.class, explicit, float.class); } else if (expected == double.class) { - return Cast.unboxTo(def.class, Double.class, explicit, double.class); + return PainlessCast.unboxTo(def.class, Double.class, explicit, double.class); } } else if (actual == Object.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxTo(Object.class, Byte.class, true, byte.class); + return PainlessCast.unboxTo(Object.class, Byte.class, true, byte.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxTo(Object.class, Short.class, true, short.class); + return PainlessCast.unboxTo(Object.class, Short.class, true, short.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxTo(Object.class, Character.class, true, char.class); + return PainlessCast.unboxTo(Object.class, Character.class, true, char.class); } else if (expected == int.class && explicit && internal) { - return Cast.unboxTo(Object.class, Integer.class, true, int.class); + return PainlessCast.unboxTo(Object.class, Integer.class, true, int.class); } else if (expected == long.class && explicit && internal) { - return Cast.unboxTo(Object.class, Long.class, true, long.class); + return PainlessCast.unboxTo(Object.class, Long.class, true, long.class); } else if (expected == float.class && explicit && internal) { - return Cast.unboxTo(Object.class, Float.class, true, float.class); + return PainlessCast.unboxTo(Object.class, Float.class, true, float.class); } else if (expected == double.class && explicit && internal) { - return Cast.unboxTo(Object.class, Double.class, true, double.class); + return PainlessCast.unboxTo(Object.class, Double.class, true, double.class); } } else if (actual == Number.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxTo(Number.class, Byte.class, true, byte.class); + return PainlessCast.unboxTo(Number.class, Byte.class, true, byte.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxTo(Number.class, Short.class, true, short.class); + return PainlessCast.unboxTo(Number.class, Short.class, true, short.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxTo(Number.class, Character.class, true, char.class); + return PainlessCast.unboxTo(Number.class, Character.class, true, char.class); } else if (expected == int.class && explicit && internal) { - return Cast.unboxTo(Number.class, Integer.class, true, int.class); + return PainlessCast.unboxTo(Number.class, Integer.class, true, int.class); } else if (expected == long.class && explicit && internal) { - return Cast.unboxTo(Number.class, Long.class, true, long.class); + return PainlessCast.unboxTo(Number.class, Long.class, true, long.class); } else if (expected == float.class && explicit && internal) { - return Cast.unboxTo(Number.class, Float.class, true, float.class); + return PainlessCast.unboxTo(Number.class, Float.class, true, float.class); } else if (expected == double.class && explicit && internal) { - return Cast.unboxTo(Number.class, Double.class, true, double.class); + return PainlessCast.unboxTo(Number.class, Double.class, true, double.class); } } else if (actual == String.class) { if (expected == char.class && explicit) { - return Cast.standard(String.class, char.class, true); + return PainlessCast.standard(String.class, char.class, true); } } else if (actual == boolean.class) { if (expected == def.class) { - return Cast.boxFrom(Boolean.class, def.class, explicit, boolean.class); + return PainlessCast.boxFrom(Boolean.class, def.class, explicit, boolean.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Boolean.class, Object.class, explicit, boolean.class); + return PainlessCast.boxFrom(Boolean.class, Object.class, explicit, boolean.class); } else if (expected == Boolean.class && internal) { - return Cast.boxTo(boolean.class, boolean.class, explicit, boolean.class); + return PainlessCast.boxTo(boolean.class, boolean.class, explicit, boolean.class); } } else if (actual == byte.class) { if (expected == def.class) { - return Cast.boxFrom(Byte.class, def.class, explicit, byte.class); + return PainlessCast.boxFrom(Byte.class, def.class, explicit, byte.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Byte.class, Object.class, explicit, byte.class); + return PainlessCast.boxFrom(Byte.class, Object.class, explicit, byte.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Byte.class, Number.class, explicit, byte.class); + return PainlessCast.boxFrom(Byte.class, Number.class, explicit, byte.class); } else if (expected == short.class) { - return Cast.standard(byte.class, short.class, explicit); + return PainlessCast.standard(byte.class, short.class, explicit); } else if (expected == char.class && explicit) { - return Cast.standard(byte.class, char.class, true); + return PainlessCast.standard(byte.class, char.class, true); } else if (expected == int.class) { - return Cast.standard(byte.class, int.class, explicit); + return PainlessCast.standard(byte.class, int.class, explicit); } else if (expected == long.class) { - return Cast.standard(byte.class, long.class, explicit); + return PainlessCast.standard(byte.class, long.class, explicit); } else if (expected == float.class) { - return Cast.standard(byte.class, float.class, explicit); + return PainlessCast.standard(byte.class, float.class, explicit); } else if (expected == double.class) { - return Cast.standard(byte.class, double.class, explicit); + return PainlessCast.standard(byte.class, double.class, explicit); } else if (expected == Byte.class && internal) { - return Cast.boxTo(byte.class, byte.class, explicit, byte.class); + return PainlessCast.boxTo(byte.class, byte.class, explicit, byte.class); } else if (expected == Short.class && internal) { - return Cast.boxTo(byte.class, short.class, explicit, short.class); + return PainlessCast.boxTo(byte.class, short.class, explicit, short.class); } else if (expected == Character.class && explicit && internal) { - return Cast.boxTo(byte.class, char.class, true, char.class); + return PainlessCast.boxTo(byte.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return Cast.boxTo(byte.class, int.class, explicit, int.class); + return PainlessCast.boxTo(byte.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return Cast.boxTo(byte.class, long.class, explicit, long.class); + return PainlessCast.boxTo(byte.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return Cast.boxTo(byte.class, float.class, explicit, float.class); + return PainlessCast.boxTo(byte.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(byte.class, double.class, explicit, double.class); + return PainlessCast.boxTo(byte.class, double.class, explicit, double.class); } } else if (actual == short.class) { if (expected == def.class) { - return Cast.boxFrom(Short.class, def.class, explicit, short.class); + return PainlessCast.boxFrom(Short.class, def.class, explicit, short.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Short.class, Object.class, explicit, short.class); + return PainlessCast.boxFrom(Short.class, Object.class, explicit, short.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Short.class, Number.class, explicit, short.class); + return PainlessCast.boxFrom(Short.class, Number.class, explicit, short.class); } else if (expected == byte.class && explicit) { - return Cast.standard(short.class, byte.class, true); + return PainlessCast.standard(short.class, byte.class, true); } else if (expected == char.class && explicit) { - return Cast.standard(short.class, char.class, true); + return PainlessCast.standard(short.class, char.class, true); } else if (expected == int.class) { - return Cast.standard(short.class, int.class, explicit); + return PainlessCast.standard(short.class, int.class, explicit); } else if (expected == long.class) { - return Cast.standard(short.class, long.class, explicit); + return PainlessCast.standard(short.class, long.class, explicit); } else if (expected == float.class) { - return Cast.standard(short.class, float.class, explicit); + return PainlessCast.standard(short.class, float.class, explicit); } else if (expected == double.class) { - return Cast.standard(short.class, double.class, explicit); + return PainlessCast.standard(short.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return Cast.boxTo(short.class, byte.class, true, byte.class); + return PainlessCast.boxTo(short.class, byte.class, true, byte.class); } else if (expected == Short.class && internal) { - return Cast.boxTo(short.class, short.class, explicit, short.class); + return PainlessCast.boxTo(short.class, short.class, explicit, short.class); } else if (expected == Character.class && explicit && internal) { - return Cast.boxTo(short.class, char.class, true, char.class); + return PainlessCast.boxTo(short.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return Cast.boxTo(short.class, int.class, explicit, int.class); + return PainlessCast.boxTo(short.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return Cast.boxTo(short.class, long.class, explicit, long.class); + return PainlessCast.boxTo(short.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return Cast.boxTo(short.class, float.class, explicit, float.class); + return PainlessCast.boxTo(short.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(short.class, double.class, explicit, double.class); + return PainlessCast.boxTo(short.class, double.class, explicit, double.class); } } else if (actual == char.class) { if (expected == def.class) { - return Cast.boxFrom(Character.class, def.class, explicit, char.class); + return PainlessCast.boxFrom(Character.class, def.class, explicit, char.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Character.class, Object.class, explicit, char.class); + return PainlessCast.boxFrom(Character.class, Object.class, explicit, char.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Character.class, Number.class, explicit, char.class); + return PainlessCast.boxFrom(Character.class, Number.class, explicit, char.class); } else if (expected == String.class) { - return Cast.standard(char.class, String.class, explicit); + return PainlessCast.standard(char.class, String.class, explicit); } else if (expected == byte.class && explicit) { - return Cast.standard(char.class, byte.class, true); + return PainlessCast.standard(char.class, byte.class, true); } else if (expected == short.class && explicit) { - return Cast.standard(char.class, short.class, true); + return PainlessCast.standard(char.class, short.class, true); } else if (expected == int.class) { - return Cast.standard(char.class, int.class, explicit); + return PainlessCast.standard(char.class, int.class, explicit); } else if (expected == long.class) { - return Cast.standard(char.class, long.class, explicit); + return PainlessCast.standard(char.class, long.class, explicit); } else if (expected == float.class) { - return Cast.standard(char.class, float.class, explicit); + return PainlessCast.standard(char.class, float.class, explicit); } else if (expected == double.class) { - return Cast.standard(char.class, double.class, explicit); + return PainlessCast.standard(char.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return Cast.boxTo(char.class, byte.class, true, byte.class); + return PainlessCast.boxTo(char.class, byte.class, true, byte.class); } else if (expected == Short.class && internal) { - return Cast.boxTo(char.class, short.class, explicit, short.class); + return PainlessCast.boxTo(char.class, short.class, explicit, short.class); } else if (expected == Character.class && internal) { - return Cast.boxTo(char.class, char.class, true, char.class); + return PainlessCast.boxTo(char.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return Cast.boxTo(char.class, int.class, explicit, int.class); + return PainlessCast.boxTo(char.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return Cast.boxTo(char.class, long.class, explicit, long.class); + return PainlessCast.boxTo(char.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return Cast.boxTo(char.class, float.class, explicit, float.class); + return PainlessCast.boxTo(char.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(char.class, double.class, explicit, double.class); + return PainlessCast.boxTo(char.class, double.class, explicit, double.class); } } else if (actual == int.class) { if (expected == def.class) { - return Cast.boxFrom(Integer.class, def.class, explicit, int.class); + return PainlessCast.boxFrom(Integer.class, def.class, explicit, int.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Integer.class, Object.class, explicit, int.class); + return PainlessCast.boxFrom(Integer.class, Object.class, explicit, int.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Integer.class, Number.class, explicit, int.class); + return PainlessCast.boxFrom(Integer.class, Number.class, explicit, int.class); } else if (expected == byte.class && explicit) { - return Cast.standard(int.class, byte.class, true); + return PainlessCast.standard(int.class, byte.class, true); } else if (expected == char.class && explicit) { - return Cast.standard(int.class, char.class, true); + return PainlessCast.standard(int.class, char.class, true); } else if (expected == short.class && explicit) { - return Cast.standard(int.class, short.class, true); + return PainlessCast.standard(int.class, short.class, true); } else if (expected == long.class) { - return Cast.standard(int.class, long.class, explicit); + return PainlessCast.standard(int.class, long.class, explicit); } else if (expected == float.class) { - return Cast.standard(int.class, float.class, explicit); + return PainlessCast.standard(int.class, float.class, explicit); } else if (expected == double.class) { - return Cast.standard(int.class, double.class, explicit); + return PainlessCast.standard(int.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return Cast.boxTo(int.class, byte.class, true, byte.class); + return PainlessCast.boxTo(int.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return Cast.boxTo(int.class, short.class, true, short.class); + return PainlessCast.boxTo(int.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return Cast.boxTo(int.class, char.class, true, char.class); + return PainlessCast.boxTo(int.class, char.class, true, char.class); } else if (expected == Integer.class && internal) { - return Cast.boxTo(int.class, int.class, explicit, int.class); + return PainlessCast.boxTo(int.class, int.class, explicit, int.class); } else if (expected == Long.class && internal) { - return Cast.boxTo(int.class, long.class, explicit, long.class); + return PainlessCast.boxTo(int.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return Cast.boxTo(int.class, float.class, explicit, float.class); + return PainlessCast.boxTo(int.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(int.class, double.class, explicit, double.class); + return PainlessCast.boxTo(int.class, double.class, explicit, double.class); } } else if (actual == long.class) { if (expected == def.class) { - return Cast.boxFrom(Long.class, def.class, explicit, long.class); + return PainlessCast.boxFrom(Long.class, def.class, explicit, long.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Long.class, Object.class, explicit, long.class); + return PainlessCast.boxFrom(Long.class, Object.class, explicit, long.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Long.class, Number.class, explicit, long.class); + return PainlessCast.boxFrom(Long.class, Number.class, explicit, long.class); } else if (expected == byte.class && explicit) { - return Cast.standard(long.class, byte.class, true); + return PainlessCast.standard(long.class, byte.class, true); } else if (expected == char.class && explicit) { - return Cast.standard(long.class, char.class, true); + return PainlessCast.standard(long.class, char.class, true); } else if (expected == short.class && explicit) { - return Cast.standard(long.class, short.class, true); + return PainlessCast.standard(long.class, short.class, true); } else if (expected == int.class && explicit) { - return Cast.standard(long.class, int.class, true); + return PainlessCast.standard(long.class, int.class, true); } else if (expected == float.class) { - return Cast.standard(long.class, float.class, explicit); + return PainlessCast.standard(long.class, float.class, explicit); } else if (expected == double.class) { - return Cast.standard(long.class, double.class, explicit); + return PainlessCast.standard(long.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return Cast.boxTo(long.class, byte.class, true, byte.class); + return PainlessCast.boxTo(long.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return Cast.boxTo(long.class, short.class, true, short.class); + return PainlessCast.boxTo(long.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return Cast.boxTo(long.class, char.class, true, char.class); + return PainlessCast.boxTo(long.class, char.class, true, char.class); } else if (expected == Integer.class && explicit && internal) { - return Cast.boxTo(long.class, int.class, true, int.class); + return PainlessCast.boxTo(long.class, int.class, true, int.class); } else if (expected == Long.class && internal) { - return Cast.boxTo(long.class, long.class, explicit, long.class); + return PainlessCast.boxTo(long.class, long.class, explicit, long.class); } else if (expected == Float.class && internal) { - return Cast.boxTo(long.class, float.class, explicit, float.class); + return PainlessCast.boxTo(long.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(long.class, double.class, explicit, double.class); + return PainlessCast.boxTo(long.class, double.class, explicit, double.class); } } else if (actual == float.class) { if (expected == def.class) { - return Cast.boxFrom(Float.class, def.class, explicit, float.class); + return PainlessCast.boxFrom(Float.class, def.class, explicit, float.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Float.class, Object.class, explicit, float.class); + return PainlessCast.boxFrom(Float.class, Object.class, explicit, float.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Float.class, Number.class, explicit, float.class); + return PainlessCast.boxFrom(Float.class, Number.class, explicit, float.class); } else if (expected == byte.class && explicit) { - return Cast.standard(float.class, byte.class, true); + return PainlessCast.standard(float.class, byte.class, true); } else if (expected == char.class && explicit) { - return Cast.standard(float.class, char.class, true); + return PainlessCast.standard(float.class, char.class, true); } else if (expected == short.class && explicit) { - return Cast.standard(float.class, short.class, true); + return PainlessCast.standard(float.class, short.class, true); } else if (expected == int.class && explicit) { - return Cast.standard(float.class, int.class, true); + return PainlessCast.standard(float.class, int.class, true); } else if (expected == long.class && explicit) { - return Cast.standard(float.class, long.class, true); + return PainlessCast.standard(float.class, long.class, true); } else if (expected == double.class) { - return Cast.standard(float.class, double.class, explicit); + return PainlessCast.standard(float.class, double.class, explicit); } else if (expected == Byte.class && explicit && internal) { - return Cast.boxTo(float.class, byte.class, true, byte.class); + return PainlessCast.boxTo(float.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return Cast.boxTo(float.class, short.class, true, short.class); + return PainlessCast.boxTo(float.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return Cast.boxTo(float.class, char.class, true, char.class); + return PainlessCast.boxTo(float.class, char.class, true, char.class); } else if (expected == Integer.class && explicit && internal) { - return Cast.boxTo(float.class, int.class, true, int.class); + return PainlessCast.boxTo(float.class, int.class, true, int.class); } else if (expected == Long.class && explicit && internal) { - return Cast.boxTo(float.class, long.class, true, long.class); + return PainlessCast.boxTo(float.class, long.class, true, long.class); } else if (expected == Float.class && internal) { - return Cast.boxTo(float.class, float.class, explicit, float.class); + return PainlessCast.boxTo(float.class, float.class, explicit, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(float.class, double.class, explicit, double.class); + return PainlessCast.boxTo(float.class, double.class, explicit, double.class); } } else if (actual == double.class) { if (expected == def.class) { - return Cast.boxFrom(Double.class, def.class, explicit, double.class); + return PainlessCast.boxFrom(Double.class, def.class, explicit, double.class); } else if (expected == Object.class && internal) { - return Cast.boxFrom(Double.class, Object.class, explicit, double.class); + return PainlessCast.boxFrom(Double.class, Object.class, explicit, double.class); } else if (expected == Number.class && internal) { - return Cast.boxFrom(Double.class, Number.class, explicit, double.class); + return PainlessCast.boxFrom(Double.class, Number.class, explicit, double.class); } else if (expected == byte.class && explicit) { - return Cast.standard(double.class, byte.class, true); + return PainlessCast.standard(double.class, byte.class, true); } else if (expected == char.class && explicit) { - return Cast.standard(double.class, char.class, true); + return PainlessCast.standard(double.class, char.class, true); } else if (expected == short.class && explicit) { - return Cast.standard(double.class, short.class, true); + return PainlessCast.standard(double.class, short.class, true); } else if (expected == int.class && explicit) { - return Cast.standard(double.class, int.class, true); + return PainlessCast.standard(double.class, int.class, true); } else if (expected == long.class && explicit) { - return Cast.standard(double.class, long.class, true); + return PainlessCast.standard(double.class, long.class, true); } else if (expected == float.class && explicit) { - return Cast.standard(double.class, float.class, true); + return PainlessCast.standard(double.class, float.class, true); } else if (expected == Byte.class && explicit && internal) { - return Cast.boxTo(double.class, byte.class, true, byte.class); + return PainlessCast.boxTo(double.class, byte.class, true, byte.class); } else if (expected == Short.class && explicit && internal) { - return Cast.boxTo(double.class, short.class, true, short.class); + return PainlessCast.boxTo(double.class, short.class, true, short.class); } else if (expected == Character.class && explicit && internal) { - return Cast.boxTo(double.class, char.class, true, char.class); + return PainlessCast.boxTo(double.class, char.class, true, char.class); } else if (expected == Integer.class && explicit && internal) { - return Cast.boxTo(double.class, int.class, true, int.class); + return PainlessCast.boxTo(double.class, int.class, true, int.class); } else if (expected == Long.class && explicit && internal) { - return Cast.boxTo(double.class, long.class, true, long.class); + return PainlessCast.boxTo(double.class, long.class, true, long.class); } else if (expected == Float.class && explicit && internal) { - return Cast.boxTo(double.class, float.class, true, float.class); + return PainlessCast.boxTo(double.class, float.class, true, float.class); } else if (expected == Double.class && internal) { - return Cast.boxTo(double.class, double.class, explicit, double.class); + return PainlessCast.boxTo(double.class, double.class, explicit, double.class); } } else if (actual == Boolean.class) { if (expected == boolean.class && internal) { - return Cast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class); + return PainlessCast.unboxFrom(boolean.class, boolean.class, explicit, boolean.class); } } else if (actual == Byte.class) { if (expected == byte.class && internal) { - return Cast.unboxFrom(byte.class, byte.class, explicit, byte.class); + return PainlessCast.unboxFrom(byte.class, byte.class, explicit, byte.class); } else if (expected == short.class && internal) { - return Cast.unboxFrom(byte.class, short.class, explicit, byte.class); + return PainlessCast.unboxFrom(byte.class, short.class, explicit, byte.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxFrom(byte.class, char.class, true, byte.class); + return PainlessCast.unboxFrom(byte.class, char.class, true, byte.class); } else if (expected == int.class && internal) { - return Cast.unboxFrom(byte.class, int.class, explicit, byte.class); + return PainlessCast.unboxFrom(byte.class, int.class, explicit, byte.class); } else if (expected == long.class && internal) { - return Cast.unboxFrom(byte.class, long.class, explicit, byte.class); + return PainlessCast.unboxFrom(byte.class, long.class, explicit, byte.class); } else if (expected == float.class && internal) { - return Cast.unboxFrom(byte.class, float.class, explicit, byte.class); + return PainlessCast.unboxFrom(byte.class, float.class, explicit, byte.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(byte.class, double.class, explicit, byte.class); + return PainlessCast.unboxFrom(byte.class, double.class, explicit, byte.class); } } else if (actual == Short.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxFrom(short.class, byte.class, true, short.class); + return PainlessCast.unboxFrom(short.class, byte.class, true, short.class); } else if (expected == short.class && internal) { - return Cast.unboxFrom(short.class, short.class, explicit, short.class); + return PainlessCast.unboxFrom(short.class, short.class, explicit, short.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxFrom(short.class, char.class, true, short.class); + return PainlessCast.unboxFrom(short.class, char.class, true, short.class); } else if (expected == int.class && internal) { - return Cast.unboxFrom(short.class, int.class, explicit, short.class); + return PainlessCast.unboxFrom(short.class, int.class, explicit, short.class); } else if (expected == long.class && internal) { - return Cast.unboxFrom(short.class, long.class, explicit, short.class); + return PainlessCast.unboxFrom(short.class, long.class, explicit, short.class); } else if (expected == float.class && internal) { - return Cast.unboxFrom(short.class, float.class, explicit, short.class); + return PainlessCast.unboxFrom(short.class, float.class, explicit, short.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(short.class, double.class, explicit, short.class); + return PainlessCast.unboxFrom(short.class, double.class, explicit, short.class); } } else if (actual == Character.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxFrom(char.class, byte.class, true, char.class); + return PainlessCast.unboxFrom(char.class, byte.class, true, char.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxFrom(char.class, short.class, true, char.class); + return PainlessCast.unboxFrom(char.class, short.class, true, char.class); } else if (expected == char.class && internal) { - return Cast.unboxFrom(char.class, char.class, explicit, char.class); + return PainlessCast.unboxFrom(char.class, char.class, explicit, char.class); } else if (expected == int.class && internal) { - return Cast.unboxFrom(char.class, int.class, explicit, char.class); + return PainlessCast.unboxFrom(char.class, int.class, explicit, char.class); } else if (expected == long.class && internal) { - return Cast.unboxFrom(char.class, long.class, explicit, char.class); + return PainlessCast.unboxFrom(char.class, long.class, explicit, char.class); } else if (expected == float.class && internal) { - return Cast.unboxFrom(char.class, float.class, explicit, char.class); + return PainlessCast.unboxFrom(char.class, float.class, explicit, char.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(char.class, double.class, explicit, char.class); + return PainlessCast.unboxFrom(char.class, double.class, explicit, char.class); } } else if (actual == Integer.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxFrom(int.class, byte.class, true, int.class); + return PainlessCast.unboxFrom(int.class, byte.class, true, int.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxFrom(int.class, short.class, true, int.class); + return PainlessCast.unboxFrom(int.class, short.class, true, int.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxFrom(int.class, char.class, true, int.class); + return PainlessCast.unboxFrom(int.class, char.class, true, int.class); } else if (expected == int.class && internal) { - return Cast.unboxFrom(int.class, int.class, explicit, int.class); + return PainlessCast.unboxFrom(int.class, int.class, explicit, int.class); } else if (expected == long.class && internal) { - return Cast.unboxFrom(int.class, long.class, explicit, int.class); + return PainlessCast.unboxFrom(int.class, long.class, explicit, int.class); } else if (expected == float.class && internal) { - return Cast.unboxFrom(int.class, float.class, explicit, int.class); + return PainlessCast.unboxFrom(int.class, float.class, explicit, int.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(int.class, double.class, explicit, int.class); + return PainlessCast.unboxFrom(int.class, double.class, explicit, int.class); } } else if (actual == Long.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxFrom(long.class, byte.class, true, long.class); + return PainlessCast.unboxFrom(long.class, byte.class, true, long.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxFrom(long.class, short.class, true, long.class); + return PainlessCast.unboxFrom(long.class, short.class, true, long.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxFrom(long.class, char.class, true, long.class); + return PainlessCast.unboxFrom(long.class, char.class, true, long.class); } else if (expected == int.class && explicit && internal) { - return Cast.unboxFrom(long.class, int.class, true, long.class); + return PainlessCast.unboxFrom(long.class, int.class, true, long.class); } else if (expected == long.class && internal) { - return Cast.unboxFrom(long.class, long.class, explicit, long.class); + return PainlessCast.unboxFrom(long.class, long.class, explicit, long.class); } else if (expected == float.class && internal) { - return Cast.unboxFrom(long.class, float.class, explicit, long.class); + return PainlessCast.unboxFrom(long.class, float.class, explicit, long.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(long.class, double.class, explicit, long.class); + return PainlessCast.unboxFrom(long.class, double.class, explicit, long.class); } } else if (actual == Float.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxFrom(float.class, byte.class, true, float.class); + return PainlessCast.unboxFrom(float.class, byte.class, true, float.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxFrom(float.class, short.class, true, float.class); + return PainlessCast.unboxFrom(float.class, short.class, true, float.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxFrom(float.class, char.class, true, float.class); + return PainlessCast.unboxFrom(float.class, char.class, true, float.class); } else if (expected == int.class && explicit && internal) { - return Cast.unboxFrom(float.class, int.class, true, float.class); + return PainlessCast.unboxFrom(float.class, int.class, true, float.class); } else if (expected == long.class && explicit && internal) { - return Cast.unboxFrom(float.class, long.class, true, float.class); + return PainlessCast.unboxFrom(float.class, long.class, true, float.class); } else if (expected == float.class && internal) { - return Cast.unboxFrom(float.class, float.class, explicit, float.class); + return PainlessCast.unboxFrom(float.class, float.class, explicit, float.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(float.class, double.class, explicit, float.class); + return PainlessCast.unboxFrom(float.class, double.class, explicit, float.class); } } else if (actual == Double.class) { if (expected == byte.class && explicit && internal) { - return Cast.unboxFrom(double.class, byte.class, true, double.class); + return PainlessCast.unboxFrom(double.class, byte.class, true, double.class); } else if (expected == short.class && explicit && internal) { - return Cast.unboxFrom(double.class, short.class, true, double.class); + return PainlessCast.unboxFrom(double.class, short.class, true, double.class); } else if (expected == char.class && explicit && internal) { - return Cast.unboxFrom(double.class, char.class, true, double.class); + return PainlessCast.unboxFrom(double.class, char.class, true, double.class); } else if (expected == int.class && explicit && internal) { - return Cast.unboxFrom(double.class, int.class, true, double.class); + return PainlessCast.unboxFrom(double.class, int.class, true, double.class); } else if (expected == long.class && explicit && internal) { - return Cast.unboxFrom(double.class, long.class, true, double.class); + return PainlessCast.unboxFrom(double.class, long.class, true, double.class); } else if (expected == float.class && explicit && internal) { - return Cast.unboxFrom(double.class, float.class, true, double.class); + return PainlessCast.unboxFrom(double.class, float.class, true, double.class); } else if (expected == double.class && internal) { - return Cast.unboxFrom(double.class, double.class, explicit, double.class); + return PainlessCast.unboxFrom(double.class, double.class, explicit, double.class); } } @@ -462,14 +463,14 @@ public static Cast getLegalCast(Location location, Class actual, Class exp (actual != void.class && expected == def.class) || expected.isAssignableFrom(actual) || (actual.isAssignableFrom(expected) && explicit)) { - return Cast.standard(actual, expected, explicit); + return PainlessCast.standard(actual, expected, explicit); } else { throw location.createError(new ClassCastException( - "Cannot cast from [" + Definition.ClassToName(actual) + "] to [" + Definition.ClassToName(expected) + "].")); + "Cannot cast from [" + PainlessLookup.ClassToName(actual) + "] to [" + PainlessLookup.ClassToName(expected) + "].")); } } - public static Object constCast(Location location, Object constant, Cast cast) { + public static Object constCast(Location location, Object constant, PainlessCast cast) { Class fsort = cast.from; Class tsort = cast.to; 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 8102016828c30..03345fcfff35a 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 @@ -21,6 +21,7 @@ import org.elasticsearch.bootstrap.BootstrapInfo; import org.elasticsearch.painless.antlr.Walker; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.node.SSource; import org.elasticsearch.painless.spi.Whitelist; import org.objectweb.asm.util.Printer; @@ -70,26 +71,26 @@ final class Compiler { */ static final class Loader extends SecureClassLoader { private final AtomicInteger lambdaCounter = new AtomicInteger(0); - private final Definition definition; + private final PainlessLookup painlessLookup; /** * @param parent The parent ClassLoader. */ - Loader(ClassLoader parent, Definition definition) { + Loader(ClassLoader parent, PainlessLookup painlessLookup) { super(parent); - this.definition = definition; + this.painlessLookup = painlessLookup; } /** * Will check to see if the {@link Class} has already been loaded when - * the {@link Definition} was initially created. Allows for {@link Whitelist}ed + * the {@link PainlessLookup} was initially created. Allows for {@link Whitelist}ed * classes to be loaded from other modules/plugins without a direct relationship * to the module's/plugin's {@link ClassLoader}. */ @Override public Class findClass(String name) throws ClassNotFoundException { - Class found = definition.getClassFromBinaryName(name); + Class found = painlessLookup.getClassFromBinaryName(name); return found != null ? found : super.findClass(name); } @@ -135,10 +136,10 @@ int newLambdaIdentifier() { /** * Return a new {@link Loader} for a script using the - * {@link Compiler}'s specified {@link Definition}. + * {@link Compiler}'s specified {@link PainlessLookup}. */ public Loader createLoader(ClassLoader parent) { - return new Loader(parent, definition); + return new Loader(parent, painlessLookup); } /** @@ -149,16 +150,16 @@ public Loader createLoader(ClassLoader parent) { /** * The whitelist the script will use. */ - private final Definition definition; + private final PainlessLookup painlessLookup; /** * Standard constructor. * @param base The class/interface the script is guaranteed to derive/implement. - * @param definition The whitelist the script will use. + * @param painlessLookup The whitelist the script will use. */ - Compiler(Class base, Definition definition) { + Compiler(Class base, PainlessLookup painlessLookup) { this.base = base; - this.definition = definition; + this.painlessLookup = painlessLookup; } /** @@ -176,10 +177,10 @@ Constructor compile(Loader loader, MainMethodReserved reserved, String name, " plugin if a script longer than this length is a requirement."); } - ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base); - SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, definition, + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base); + SSource root = Walker.buildPainlessTree(scriptClassInfo, reserved, name, source, settings, painlessLookup, null); - root.analyze(definition); + root.analyze(painlessLookup); root.write(); try { @@ -187,7 +188,7 @@ Constructor compile(Loader loader, MainMethodReserved reserved, String name, clazz.getField("$NAME").set(null, name); clazz.getField("$SOURCE").set(null, source); clazz.getField("$STATEMENTS").set(null, root.getStatements()); - clazz.getField("$DEFINITION").set(null, definition); + clazz.getField("$DEFINITION").set(null, painlessLookup); return clazz.getConstructors()[0]; } catch (Exception exception) { // Catch everything to let the user know this is something caused internally. @@ -208,10 +209,10 @@ byte[] compile(String name, String source, CompilerSettings settings, Printer de " plugin if a script longer than this length is a requirement."); } - ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base); - SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, definition, + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, base); + SSource root = Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), name, source, settings, painlessLookup, debugStream); - root.analyze(definition); + root.analyze(painlessLookup); root.write(); return root.getBytes(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index 8694ff7903859..f006b57f46bad 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -19,13 +19,14 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.util.BitSet; import java.util.Collections; @@ -60,14 +61,15 @@ public final class Def { */ @SuppressWarnings("unused") // getArrayLength() methods are are actually used, javac just does not know :) private static final class ArrayLengthHelper { - private static final Lookup PRIV_LOOKUP = MethodHandles.lookup(); + private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup(); private static final Map,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap( Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class, char[].class, float[].class, double[].class, Object[].class) .collect(Collectors.toMap(Function.identity(), type -> { try { - return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "getArrayLength", MethodType.methodType(int.class, type)); + return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic( + PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "getArrayLength", MethodType.methodType(int.class, type)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } @@ -116,17 +118,17 @@ private ArrayLengthHelper() {} static final MethodHandle JAVA9_ARRAY_LENGTH_MH_FACTORY; static { - final Lookup lookup = MethodHandles.publicLookup(); + final MethodHandles.Lookup methodHandlesLookup = MethodHandles.publicLookup(); try { - MAP_GET = lookup.findVirtual(Map.class , "get", MethodType.methodType(Object.class, Object.class)); - MAP_PUT = lookup.findVirtual(Map.class , "put", MethodType.methodType(Object.class, Object.class, Object.class)); - LIST_GET = lookup.findVirtual(List.class, "get", MethodType.methodType(Object.class, int.class)); - LIST_SET = lookup.findVirtual(List.class, "set", MethodType.methodType(Object.class, int.class, Object.class)); - ITERATOR = lookup.findVirtual(Iterable.class, "iterator", MethodType.methodType(Iterator.class)); - MAP_INDEX_NORMALIZE = lookup.findStatic(Def.class, "mapIndexNormalize", + MAP_GET = methodHandlesLookup.findVirtual(Map.class , "get", MethodType.methodType(Object.class, Object.class)); + MAP_PUT = methodHandlesLookup.findVirtual(Map.class , "put", MethodType.methodType(Object.class, Object.class, Object.class)); + LIST_GET = methodHandlesLookup.findVirtual(List.class, "get", MethodType.methodType(Object.class, int.class)); + LIST_SET = methodHandlesLookup.findVirtual(List.class, "set", MethodType.methodType(Object.class, int.class, Object.class)); + ITERATOR = methodHandlesLookup.findVirtual(Iterable.class, "iterator", MethodType.methodType(Iterator.class)); + MAP_INDEX_NORMALIZE = methodHandlesLookup.findStatic(Def.class, "mapIndexNormalize", MethodType.methodType(Object.class, Map.class, Object.class)); - LIST_INDEX_NORMALIZE = lookup.findStatic(Def.class, "listIndexNormalize", + LIST_INDEX_NORMALIZE = methodHandlesLookup.findStatic(Def.class, "listIndexNormalize", MethodType.methodType(int.class, List.class, int.class)); } catch (final ReflectiveOperationException roe) { throw new AssertionError(roe); @@ -136,7 +138,7 @@ private ArrayLengthHelper() {} // https://bugs.openjdk.java.net/browse/JDK-8156915 MethodHandle arrayLengthMHFactory; try { - arrayLengthMHFactory = lookup.findStatic(MethodHandles.class, "arrayLength", + arrayLengthMHFactory = methodHandlesLookup.findStatic(MethodHandles.class, "arrayLength", MethodType.methodType(MethodHandle.class, Class.class)); } catch (final ReflectiveOperationException roe) { arrayLengthMHFactory = null; @@ -174,31 +176,31 @@ static MethodHandle arrayLengthGetter(Class arrayType) { * until it finds a matching whitelisted method. If one is not found, it throws an exception. * Otherwise it returns the matching method. *

      - * @params definition the whitelist + * @params painlessLookup the whitelist * @param receiverClass Class of the object to invoke the method on. * @param name Name of the method. * @param arity arity of method * @return matching method to invoke. never returns null. * @throws IllegalArgumentException if no matching whitelisted method was found. */ - static Method lookupMethodInternal(Definition definition, Class receiverClass, String name, int arity) { - Definition.MethodKey key = new Definition.MethodKey(name, arity); + static PainlessMethod lookupMethodInternal(PainlessLookup painlessLookup, Class receiverClass, String name, int arity) { + PainlessMethodKey key = new PainlessMethodKey(name, arity); // check whitelist for matching method for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - Struct struct = definition.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz); if (struct != null) { - Method method = struct.methods.get(key); + PainlessMethod method = struct.methods.get(key); if (method != null) { return method; } } for (Class iface : clazz.getInterfaces()) { - struct = definition.getPainlessStructFromJavaClass(iface); + struct = painlessLookup.getPainlessStructFromJavaClass(iface); if (struct != null) { - Method method = struct.methods.get(key); + PainlessMethod method = struct.methods.get(key); if (method != null) { return method; } @@ -220,8 +222,8 @@ static Method lookupMethodInternal(Definition definition, Class receiverClass * until it finds a matching whitelisted method. If one is not found, it throws an exception. * Otherwise it returns a handle to the matching method. *

      - * @param definition the whitelist - * @param lookup caller's lookup + * @param painlessLookup the whitelist + * @param methodHandlesLookup caller's lookup * @param callSiteType callsite's type * @param receiverClass Class of the object to invoke the method on. * @param name Name of the method. @@ -230,13 +232,13 @@ static Method lookupMethodInternal(Definition definition, Class receiverClass * @throws IllegalArgumentException if no matching whitelisted method was found. * @throws Throwable if a method reference cannot be converted to an functional interface */ - static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodType callSiteType, - Class receiverClass, String name, Object args[]) throws Throwable { + static MethodHandle lookupMethod(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, MethodType callSiteType, + Class receiverClass, String name, Object args[]) throws Throwable { String recipeString = (String) args[0]; int numArguments = callSiteType.parameterCount(); // simple case: no lambdas if (recipeString.isEmpty()) { - return lookupMethodInternal(definition, receiverClass, name, numArguments - 1).handle; + return lookupMethodInternal(painlessLookup, receiverClass, name, numArguments - 1).handle; } // convert recipe string to a bitset for convenience (the code below should be refactored...) @@ -259,7 +261,7 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp // lookup the method with the proper arity, then we know everything (e.g. interface types of parameters). // based on these we can finally link any remaining lambdas that were deferred. - Method method = lookupMethodInternal(definition, receiverClass, name, arity); + PainlessMethod method = lookupMethodInternal(painlessLookup, receiverClass, name, arity); MethodHandle handle = method.handle; int replaced = 0; @@ -283,8 +285,8 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp if (signature.charAt(0) == 'S') { // the implementation is strongly typed, now that we know the interface type, // we have everything. - filter = lookupReferenceInternal(definition, - lookup, + filter = lookupReferenceInternal(painlessLookup, + methodHandlesLookup, interfaceType, type, call, @@ -294,13 +296,13 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp // this is dynamically based on the receiver type (and cached separately, underneath // this cache). It won't blow up since we never nest here (just references) MethodType nestedType = MethodType.methodType(interfaceType, captures); - CallSite nested = DefBootstrap.bootstrap(definition, - lookup, + CallSite nested = DefBootstrap.bootstrap(painlessLookup, + methodHandlesLookup, call, nestedType, 0, DefBootstrap.REFERENCE, - Definition.ClassToName(interfaceType)); + PainlessLookup.ClassToName(interfaceType)); filter = nested.dynamicInvoker(); } else { throw new AssertionError(); @@ -322,37 +324,37 @@ static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodTyp * This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known, * so we simply need to lookup the matching implementation method based on receiver type. */ - static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass, - Class receiverClass, String name) throws Throwable { - Class interfaceType = definition.getJavaClassFromPainlessType(interfaceClass); - Method interfaceMethod = definition.getPainlessStructFromJavaClass(interfaceType).functionalMethod; + static MethodHandle lookupReference(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String interfaceClass, + Class receiverClass, String name) throws Throwable { + Class interfaceType = painlessLookup.getJavaClassFromPainlessType(interfaceClass); + PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(interfaceType).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface"); } int arity = interfaceMethod.arguments.size(); - Method implMethod = lookupMethodInternal(definition, receiverClass, name, arity); - return lookupReferenceInternal(definition, lookup, interfaceType, implMethod.owner.name, + PainlessMethod implMethod = lookupMethodInternal(painlessLookup, receiverClass, name, arity); + return lookupReferenceInternal(painlessLookup, methodHandlesLookup, interfaceType, implMethod.owner.name, implMethod.name, receiverClass); } /** Returns a method handle to an implementation of clazz, given method reference signature. */ - private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup, - Class clazz, String type, String call, Class... captures) + private static MethodHandle lookupReferenceInternal(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, + Class clazz, String type, String call, Class... captures) throws Throwable { final FunctionRef ref; if ("this".equals(type)) { // user written method - Method interfaceMethod = definition.getPainlessStructFromJavaClass(clazz).functionalMethod; + PainlessMethod interfaceMethod = painlessLookup.getPainlessStructFromJavaClass(clazz).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + Definition.ClassToName(clazz) + "], not a functional interface"); + "to [" + PainlessLookup.ClassToName(clazz) + "], not a functional interface"); } int arity = interfaceMethod.arguments.size() + captures.length; final MethodHandle handle; try { - MethodHandle accessor = lookup.findStaticGetter(lookup.lookupClass(), - getUserFunctionHandleFieldName(call, arity), - MethodHandle.class); + MethodHandle accessor = methodHandlesLookup.findStaticGetter(methodHandlesLookup.lookupClass(), + getUserFunctionHandleFieldName(call, arity), + MethodHandle.class); handle = (MethodHandle)accessor.invokeExact(); } catch (NoSuchFieldException | IllegalAccessException e) { // is it a synthetic method? If we generated the method ourselves, be more helpful. It can only fail @@ -366,10 +368,10 @@ private static MethodHandle lookupReferenceInternal(Definition definition, Looku ref = new FunctionRef(clazz, interfaceMethod, call, handle.type(), captures.length); } else { // whitelist lookup - ref = new FunctionRef(definition, clazz, type, call, captures.length); + ref = new FunctionRef(painlessLookup, clazz, type, call, captures.length); } final CallSite callSite = LambdaBootstrap.lambdaBootstrap( - lookup, + methodHandlesLookup, ref.interfaceMethodName, ref.factoryMethodType, ref.interfaceMethodType, @@ -407,16 +409,16 @@ public static String getUserFunctionHandleFieldName(String name, int arity) { * until it finds a matching whitelisted getter. If one is not found, it throws an exception. * Otherwise it returns a handle to the matching getter. *

      - * @param definition the whitelist + * @param painlessLookup the whitelist * @param receiverClass Class of the object to retrieve the field from. * @param name Name of the field. * @return pointer to matching field. never returns null. * @throws IllegalArgumentException if no matching whitelisted field was found. */ - static MethodHandle lookupGetter(Definition definition, Class receiverClass, String name) { + static MethodHandle lookupGetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - Struct struct = definition.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz); if (struct != null) { MethodHandle handle = struct.getters.get(name); @@ -426,7 +428,7 @@ static MethodHandle lookupGetter(Definition definition, Class receiverClass, } for (final Class iface : clazz.getInterfaces()) { - struct = definition.getPainlessStructFromJavaClass(iface); + struct = painlessLookup.getPainlessStructFromJavaClass(iface); if (struct != null) { MethodHandle handle = struct.getters.get(name); @@ -478,16 +480,16 @@ static MethodHandle lookupGetter(Definition definition, Class receiverClass, * until it finds a matching whitelisted setter. If one is not found, it throws an exception. * Otherwise it returns a handle to the matching setter. *

      - * @param definition the whitelist + * @param painlessLookup the whitelist * @param receiverClass Class of the object to retrieve the field from. * @param name Name of the field. * @return pointer to matching field. never returns null. * @throws IllegalArgumentException if no matching whitelisted field was found. */ - static MethodHandle lookupSetter(Definition definition, Class receiverClass, String name) { + static MethodHandle lookupSetter(PainlessLookup painlessLookup, Class receiverClass, String name) { // first try whitelist for (Class clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) { - Struct struct = definition.getPainlessStructFromJavaClass(clazz); + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(clazz); if (struct != null) { MethodHandle handle = struct.setters.get(name); @@ -497,7 +499,7 @@ static MethodHandle lookupSetter(Definition definition, Class receiverClass, } for (final Class iface : clazz.getInterfaces()) { - struct = definition.getPainlessStructFromJavaClass(iface); + struct = painlessLookup.getPainlessStructFromJavaClass(iface); if (struct != null) { MethodHandle handle = struct.setters.get(name); @@ -592,14 +594,15 @@ static MethodHandle lookupArrayLoad(Class receiverClass) { */ @SuppressWarnings("unused") // iterator() methods are are actually used, javac just does not know :) private static final class ArrayIteratorHelper { - private static final Lookup PRIV_LOOKUP = MethodHandles.lookup(); + private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup(); private static final Map,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap( Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class, char[].class, float[].class, double[].class, Object[].class) .collect(Collectors.toMap(Function.identity(), type -> { try { - return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "iterator", MethodType.methodType(Iterator.class, type)); + return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic( + PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "iterator", MethodType.methodType(Iterator.class, type)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } @@ -860,14 +863,14 @@ public static int listIndexNormalize(final List value, int index) { */ @SuppressWarnings("unused") // normalizeIndex() methods are are actually used, javac just does not know :) private static final class ArrayIndexNormalizeHelper { - private static final Lookup PRIV_LOOKUP = MethodHandles.lookup(); + private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup(); private static final Map,MethodHandle> ARRAY_TYPE_MH_MAPPING = Collections.unmodifiableMap( Stream.of(boolean[].class, byte[].class, short[].class, int[].class, long[].class, char[].class, float[].class, double[].class, Object[].class) .collect(Collectors.toMap(Function.identity(), type -> { try { - return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), "normalizeIndex", + return PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(), "normalizeIndex", MethodType.methodType(int.class, type, int.class)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java index 9c7c7f631b68d..2fadaf30964a6 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java @@ -20,11 +20,11 @@ package org.elasticsearch.painless; import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.painless.lookup.PainlessLookup; import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.invoke.MutableCallSite; import java.lang.invoke.WrongMethodTypeException; @@ -104,20 +104,21 @@ static final class PIC extends MutableCallSite { /** maximum number of types before we go megamorphic */ static final int MAX_DEPTH = 5; - private final Definition definition; - private final Lookup lookup; + private final PainlessLookup painlessLookup; + private final MethodHandles.Lookup methodHandlesLookup; private final String name; private final int flavor; private final Object[] args; int depth; // pkg-protected for testing - PIC(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) { + PIC(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, + String name, MethodType type, int initialDepth, int flavor, Object[] args) { super(type); if (type.parameterType(0) != Object.class) { throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object."); } - this.definition = definition; - this.lookup = lookup; + this.painlessLookup = painlessLookup; + this.methodHandlesLookup = methodHandlesLookup; this.name = name; this.flavor = flavor; this.args = args; @@ -144,11 +145,11 @@ static boolean checkClass(Class clazz, Object receiver) { private MethodHandle lookup(int flavor, String name, Class receiver) throws Throwable { switch(flavor) { case METHOD_CALL: - return Def.lookupMethod(definition, lookup, type(), receiver, name, args); + return Def.lookupMethod(painlessLookup, methodHandlesLookup, type(), receiver, name, args); case LOAD: - return Def.lookupGetter(definition, receiver, name); + return Def.lookupGetter(painlessLookup, receiver, name); case STORE: - return Def.lookupSetter(definition, receiver, name); + return Def.lookupSetter(painlessLookup, receiver, name); case ARRAY_LOAD: return Def.lookupArrayLoad(receiver); case ARRAY_STORE: @@ -156,7 +157,7 @@ private MethodHandle lookup(int flavor, String name, Class receiver) throws T case ITERATOR: return Def.lookupIterator(receiver); case REFERENCE: - return Def.lookupReference(definition, lookup, (String) args[0], receiver, name); + return Def.lookupReference(painlessLookup, methodHandlesLookup, (String) args[0], receiver, name); case INDEX_NORMALIZE: return Def.lookupIndexNormalize(receiver); default: throw new AssertionError(); @@ -216,17 +217,17 @@ Object fallback(final Object[] callArgs) throws Throwable { private static final MethodHandle FALLBACK; private static final MethodHandle MEGAMORPHIC_LOOKUP; static { - final Lookup lookup = MethodHandles.lookup(); - final Lookup publicLookup = MethodHandles.publicLookup(); + final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup(); + final MethodHandles.Lookup publicMethodHandlesLookup = MethodHandles.publicLookup(); try { - CHECK_CLASS = lookup.findStatic(lookup.lookupClass(), "checkClass", - MethodType.methodType(boolean.class, Class.class, Object.class)); - FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback", + CHECK_CLASS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkClass", + MethodType.methodType(boolean.class, Class.class, Object.class)); + FALLBACK = methodHandlesLookup.findVirtual(methodHandlesLookup.lookupClass(), "fallback", MethodType.methodType(Object.class, Object[].class)); - MethodHandle mh = publicLookup.findVirtual(ClassValue.class, "get", + MethodHandle mh = publicMethodHandlesLookup.findVirtual(ClassValue.class, "get", MethodType.methodType(Object.class, Class.class)); mh = MethodHandles.filterArguments(mh, 1, - publicLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class))); + publicMethodHandlesLookup.findVirtual(Object.class, "getClass", MethodType.methodType(Class.class))); MEGAMORPHIC_LOOKUP = mh.asType(mh.type().changeReturnType(MethodHandle.class)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); @@ -402,16 +403,16 @@ static boolean checkBoth(Class left, Class right, Object leftObject, Objec private static final MethodHandle CHECK_BOTH; private static final MethodHandle FALLBACK; static { - final Lookup lookup = MethodHandles.lookup(); + final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup(); try { - CHECK_LHS = lookup.findStatic(lookup.lookupClass(), "checkLHS", - MethodType.methodType(boolean.class, Class.class, Object.class)); - CHECK_RHS = lookup.findStatic(lookup.lookupClass(), "checkRHS", - MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class)); - CHECK_BOTH = lookup.findStatic(lookup.lookupClass(), "checkBoth", - MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class)); - FALLBACK = lookup.findVirtual(lookup.lookupClass(), "fallback", - MethodType.methodType(Object.class, Object[].class)); + CHECK_LHS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkLHS", + MethodType.methodType(boolean.class, Class.class, Object.class)); + CHECK_RHS = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkRHS", + MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class)); + CHECK_BOTH = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), "checkBoth", + MethodType.methodType(boolean.class, Class.class, Class.class, Object.class, Object.class)); + FALLBACK = methodHandlesLookup.findVirtual(methodHandlesLookup.lookupClass(), "fallback", + MethodType.methodType(Object.class, Object[].class)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } @@ -427,12 +428,12 @@ static boolean checkBoth(Class left, Class right, Object leftObject, Objec *

    • {@code flavor}: type of dynamic call it is (and which part of whitelist to look at). *
    • {@code args}: flavor-specific args. *
    - * And we take the {@link Definition} used to compile the script for whitelist checking. + * And we take the {@link PainlessLookup} used to compile the script for whitelist checking. *

    * see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic */ - public static CallSite bootstrap(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor, - Object... args) { + public static CallSite bootstrap(PainlessLookup painlessLookup, MethodHandles.Lookup methodHandlesLookup, String name, + MethodType type, int initialDepth, int flavor, Object... args) { // validate arguments switch(flavor) { // "function-call" like things get a polymorphic cache @@ -451,7 +452,7 @@ public static CallSite bootstrap(Definition definition, Lookup lookup, String na if (args.length != numLambdas + 1) { throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references"); } - return new PIC(definition, lookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args); case LOAD: case STORE: case ARRAY_LOAD: @@ -461,7 +462,7 @@ public static CallSite bootstrap(Definition definition, Lookup lookup, String na if (args.length > 0) { throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor); } - return new PIC(definition, lookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args); case REFERENCE: if (args.length != 1) { throw new BootstrapMethodError("Invalid number of parameters for reference call"); @@ -469,7 +470,7 @@ public static CallSite bootstrap(Definition definition, Lookup lookup, String na if (args[0] instanceof String == false) { throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]); } - return new PIC(definition, lookup, name, type, initialDepth, flavor, args); + return new PIC(painlessLookup, methodHandlesLookup, name, type, initialDepth, flavor, args); // operators get monomorphic cache, with a generic impl for a fallback case UNARY_OPERATOR: diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java index f903c0571b2bd..01d627e3962a2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefMath.java @@ -21,7 +21,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.util.Collections; import java.util.HashMap; @@ -1070,7 +1069,7 @@ private static Class promote(Class a, Class b) { } } - private static final Lookup PRIV_LOOKUP = MethodHandles.lookup(); + private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup(); private static final Map,Map> TYPE_OP_MAPPING = Collections.unmodifiableMap( Stream.of(boolean.class, int.class, long.class, float.class, double.class, Object.class) @@ -1081,26 +1080,26 @@ private static Class promote(Class a, Class b) { MethodType binary = MethodType.methodType(type, type, type); MethodType comparison = MethodType.methodType(boolean.class, type, type); MethodType shift = MethodType.methodType(type, type, long.class); - Class clazz = PRIV_LOOKUP.lookupClass(); - map.put("not", PRIV_LOOKUP.findStatic(clazz, "not", unary)); - map.put("neg", PRIV_LOOKUP.findStatic(clazz, "neg", unary)); - map.put("plus", PRIV_LOOKUP.findStatic(clazz, "plus", unary)); - map.put("mul", PRIV_LOOKUP.findStatic(clazz, "mul", binary)); - map.put("div", PRIV_LOOKUP.findStatic(clazz, "div", binary)); - map.put("rem", PRIV_LOOKUP.findStatic(clazz, "rem", binary)); - map.put("add", PRIV_LOOKUP.findStatic(clazz, "add", binary)); - map.put("sub", PRIV_LOOKUP.findStatic(clazz, "sub", binary)); - map.put("and", PRIV_LOOKUP.findStatic(clazz, "and", binary)); - map.put("or", PRIV_LOOKUP.findStatic(clazz, "or", binary)); - map.put("xor", PRIV_LOOKUP.findStatic(clazz, "xor", binary)); - map.put("eq", PRIV_LOOKUP.findStatic(clazz, "eq", comparison)); - map.put("lt", PRIV_LOOKUP.findStatic(clazz, "lt", comparison)); - map.put("lte", PRIV_LOOKUP.findStatic(clazz, "lte", comparison)); - map.put("gt", PRIV_LOOKUP.findStatic(clazz, "gt", comparison)); - map.put("gte", PRIV_LOOKUP.findStatic(clazz, "gte", comparison)); - map.put("lsh", PRIV_LOOKUP.findStatic(clazz, "lsh", shift)); - map.put("rsh", PRIV_LOOKUP.findStatic(clazz, "rsh", shift)); - map.put("ush", PRIV_LOOKUP.findStatic(clazz, "ush", shift)); + Class clazz = PRIVATE_METHOD_HANDLES_LOOKUP.lookupClass(); + map.put("not", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "not", unary)); + map.put("neg", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "neg", unary)); + map.put("plus", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "plus", unary)); + map.put("mul", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "mul", binary)); + map.put("div", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "div", binary)); + map.put("rem", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "rem", binary)); + map.put("add", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "add", binary)); + map.put("sub", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "sub", binary)); + map.put("and", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "and", binary)); + map.put("or", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "or", binary)); + map.put("xor", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "xor", binary)); + map.put("eq", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "eq", comparison)); + map.put("lt", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lt", comparison)); + map.put("lte", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lte", comparison)); + map.put("gt", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "gt", comparison)); + map.put("gte", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "gte", comparison)); + map.put("lsh", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "lsh", shift)); + map.put("rsh", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "rsh", shift)); + map.put("ush", PRIVATE_METHOD_HANDLES_LOOKUP.findStatic(clazz, "ush", shift)); return map; } catch (ReflectiveOperationException e) { throw new AssertionError(e); @@ -1188,14 +1187,14 @@ static Number getNumber(Object o) { private static final MethodHandle DYNAMIC_CAST; private static final MethodHandle DYNAMIC_RECEIVER_CAST; static { - final Lookup lookup = MethodHandles.lookup(); + final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup(); try { - DYNAMIC_CAST = lookup.findStatic(lookup.lookupClass(), - "dynamicCast", - MethodType.methodType(Object.class, Class.class, Object.class)); - DYNAMIC_RECEIVER_CAST = lookup.findStatic(lookup.lookupClass(), - "dynamicReceiverCast", - MethodType.methodType(Object.class, Object.class, Object.class)); + DYNAMIC_CAST = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), + "dynamicCast", + MethodType.methodType(Object.class, Class.class, Object.class)); + DYNAMIC_RECEIVER_CAST = methodHandlesLookup.findStatic(methodHandlesLookup.lookupClass(), + "dynamicReceiverCast", + MethodType.methodType(Object.class, Object.class, Object.class)); } catch (ReflectiveOperationException e) { throw new AssertionError(e); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java index 1b438965538ce..6ab7292c7f65b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/FunctionRef.java @@ -19,7 +19,10 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import org.objectweb.asm.Type; import java.lang.invoke.MethodType; @@ -55,9 +58,9 @@ public class FunctionRef { public final MethodType delegateMethodType; /** interface method */ - public final Method interfaceMethod; + public final PainlessMethod interfaceMethod; /** delegate method */ - public final Method delegateMethod; + public final PainlessMethod delegateMethod; /** factory method type descriptor */ public final String factoryDescriptor; @@ -71,15 +74,15 @@ public class FunctionRef { /** * Creates a new FunctionRef, which will resolve {@code type::call} from the whitelist. - * @param definition the whitelist against which this script is being compiled + * @param painlessLookup the whitelist against which this script is being compiled * @param expected functional interface type to implement. * @param type the left hand side of a method reference expression * @param call the right hand side of a method reference expression * @param numCaptures number of captured arguments */ - public FunctionRef(Definition definition, Class expected, String type, String call, int numCaptures) { - this(expected, definition.getPainlessStructFromJavaClass(expected).functionalMethod, - lookup(definition, expected, type, call, numCaptures > 0), numCaptures); + public FunctionRef(PainlessLookup painlessLookup, Class expected, String type, String call, int numCaptures) { + this(expected, painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod, + lookup(painlessLookup, expected, type, call, numCaptures > 0), numCaptures); } /** @@ -89,7 +92,7 @@ public FunctionRef(Definition definition, Class expected, String type, String * @param delegateMethod implementation method * @param numCaptures number of captured arguments */ - public FunctionRef(Class expected, Method interfaceMethod, Method delegateMethod, int numCaptures) { + public FunctionRef(Class expected, PainlessMethod interfaceMethod, PainlessMethod delegateMethod, int numCaptures) { MethodType delegateMethodType = delegateMethod.getMethodType(); interfaceMethodName = interfaceMethod.name; @@ -135,7 +138,7 @@ public FunctionRef(Class expected, Method interfaceMethod, Method delegateMet * It is for runtime use only. */ public FunctionRef(Class expected, - Method interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) { + PainlessMethod interfaceMethod, String delegateMethodName, MethodType delegateMethodType, int numCaptures) { interfaceMethodName = interfaceMethod.name; factoryMethodType = MethodType.methodType(expected, delegateMethodType.dropParameterTypes(numCaptures, delegateMethodType.parameterCount())); @@ -158,25 +161,25 @@ public FunctionRef(Class expected, /** * Looks up {@code type::call} from the whitelist, and returns a matching method. */ - private static Definition.Method lookup(Definition definition, Class expected, - String type, String call, boolean receiverCaptured) { + private static PainlessMethod lookup(PainlessLookup painlessLookup, Class expected, + String type, String call, boolean receiverCaptured) { // check its really a functional interface // for e.g. Comparable - Method method = definition.getPainlessStructFromJavaClass(expected).functionalMethod; + PainlessMethod method = painlessLookup.getPainlessStructFromJavaClass(expected).functionalMethod; if (method == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + Definition.ClassToName(expected) + "], not a functional interface"); + "to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface"); } // lookup requested method - Definition.Struct struct = definition.getPainlessStructFromJavaClass(definition.getJavaClassFromPainlessType(type)); - final Definition.Method impl; + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(painlessLookup.getJavaClassFromPainlessType(type)); + final PainlessMethod impl; // ctor ref if ("new".equals(call)) { - impl = struct.constructors.get(new Definition.MethodKey("", method.arguments.size())); + impl = struct.constructors.get(new PainlessMethodKey("", method.arguments.size())); } else { // look for a static impl first - Definition.Method staticImpl = struct.staticMethods.get(new Definition.MethodKey(call, method.arguments.size())); + PainlessMethod staticImpl = struct.staticMethods.get(new PainlessMethodKey(call, method.arguments.size())); if (staticImpl == null) { // otherwise a virtual impl final int arity; @@ -187,7 +190,7 @@ private static Definition.Method lookup(Definition definition, Class expected // receiver passed arity = method.arguments.size() - 1; } - impl = struct.methods.get(new Definition.MethodKey(call, arity)); + impl = struct.methods.get(new PainlessMethodKey(call, arity)); } else { impl = staticImpl; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java index 2498396c891d1..bab1b7942d657 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java @@ -19,8 +19,9 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import org.elasticsearch.painless.ScriptClassInfo.MethodArgument; import java.util.Arrays; @@ -59,7 +60,7 @@ public static Locals newLocalScope(Locals currentScope) { */ public static Locals newLambdaScope(Locals programScope, Class returnType, List parameters, int captureCount, int maxLoopCounter) { - Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS); + Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS); for (int i = 0; i < parameters.size(); i++) { Parameter parameter = parameters.get(i); // TODO: allow non-captures to be r/w: @@ -78,7 +79,7 @@ public static Locals newLambdaScope(Locals programScope, Class returnType, Li /** Creates a new function scope inside the current scope */ public static Locals newFunctionScope(Locals programScope, Class returnType, List parameters, int maxLoopCounter) { - Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS); + Locals locals = new Locals(programScope, programScope.painlessLookup, returnType, KEYWORDS); for (Parameter parameter : parameters) { locals.addVariable(parameter.location, parameter.clazz, parameter.name, false); } @@ -92,7 +93,7 @@ public static Locals newFunctionScope(Locals programScope, Class returnType, /** Creates a new main method scope */ public static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals programScope, int maxLoopCounter) { Locals locals = new Locals( - programScope, programScope.definition, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS); + programScope, programScope.painlessLookup, scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS); // This reference. Internal use only. locals.defineVariable(null, Object.class, THIS, true); @@ -109,9 +110,9 @@ public static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals } /** Creates a new program scope: the list of methods. It is the parent for all methods */ - public static Locals newProgramScope(Definition definition, Collection methods) { - Locals locals = new Locals(null, definition, null, null); - for (Method method : methods) { + public static Locals newProgramScope(PainlessLookup painlessLookup, Collection methods) { + Locals locals = new Locals(null, painlessLookup, null, null); + for (PainlessMethod method : methods) { locals.addMethod(method); } return locals; @@ -142,8 +143,8 @@ public Variable getVariable(Location location, String name) { } /** Looks up a method. Returns null if the method does not exist. */ - public Method getMethod(MethodKey key) { - Method method = lookupMethod(key); + public PainlessMethod getMethod(PainlessMethodKey key) { + PainlessMethod method = lookupMethod(key); if (method != null) { return method; } @@ -179,14 +180,14 @@ public Locals getProgramScope() { } /** Whitelist against which this script is being compiled. */ - public Definition getDefinition() { - return definition; + public PainlessLookup getPainlessLookup() { + return painlessLookup; } ///// private impl /** Whitelist against which this script is being compiled. */ - private final Definition definition; + private final PainlessLookup painlessLookup; // parent scope private final Locals parent; // return type of this scope @@ -198,21 +199,21 @@ public Definition getDefinition() { // variable name -> variable private Map variables; // method name+arity -> methods - private Map methods; + private Map methods; /** * Create a new Locals */ private Locals(Locals parent) { - this(parent, parent.definition, parent.returnType, parent.keywords); + this(parent, parent.painlessLookup, parent.returnType, parent.keywords); } /** * Create a new Locals with specified return type */ - private Locals(Locals parent, Definition definition, Class returnType, Set keywords) { + private Locals(Locals parent, PainlessLookup painlessLookup, Class returnType, Set keywords) { this.parent = parent; - this.definition = definition; + this.painlessLookup = painlessLookup; this.returnType = returnType; this.keywords = keywords; if (parent == null) { @@ -236,7 +237,7 @@ private Variable lookupVariable(Location location, String name) { } /** Looks up a method at this scope only. Returns null if the method does not exist. */ - private Method lookupMethod(MethodKey key) { + private PainlessMethod lookupMethod(PainlessMethodKey key) { if (methods == null) { return null; } @@ -255,11 +256,11 @@ private Variable defineVariable(Location location, Class type, String name, b return variable; } - private void addMethod(Method method) { + private void addMethod(PainlessMethod method) { if (methods == null) { methods = new HashMap<>(); } - methods.put(new MethodKey(method.name, method.arguments.size()), method); + methods.put(new PainlessMethodKey(method.name, method.arguments.size()), method); // TODO: check result } @@ -291,7 +292,7 @@ public int getSlot() { @Override public String toString() { StringBuilder b = new StringBuilder(); - b.append("Variable[type=").append(Definition.ClassToName(clazz)); + b.append("Variable[type=").append(PainlessLookup.ClassToName(clazz)); b.append(",name=").append(name); b.append(",slot=").append(slot); if (readonly) { 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 5167f7d1434de..e0a780d418843 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 @@ -19,8 +19,8 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessCast; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -130,7 +130,7 @@ public void writeLoopCounter(int slot, int count, Location location) { mark(end); } - public void writeCast(Cast cast) { + public void writeCast(PainlessCast cast) { if (cast != null) { if (cast.from == char.class && cast.to == String.class) { invokeStatic(UTILITY_TYPE, CHAR_TO_STRING); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java index 7ae93eba22632..1236c4977e802 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessExplainError.java @@ -20,6 +20,8 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.api.Debug; +import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.script.ScriptException; import java.util.List; @@ -46,7 +48,7 @@ Object getObjectToExplain() { /** * Headers to be added to the {@link ScriptException} for structured rendering. */ - public Map> getHeaders(Definition definition) { + public Map> getHeaders(PainlessLookup painlessLookup) { Map> headers = new TreeMap<>(); String toString = "null"; String javaClassName = null; @@ -54,7 +56,7 @@ public Map> getHeaders(Definition definition) { if (objectToExplain != null) { toString = objectToExplain.toString(); javaClassName = objectToExplain.getClass().getName(); - Definition.Struct struct = definition.getPainlessStructFromJavaClass(objectToExplain.getClass()); + PainlessClass struct = painlessLookup.getPainlessStructFromJavaClass(objectToExplain.getClass()); if (struct != null) { painlessClassName = struct.name; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java index 339e58c763c78..ae1944c9bd3a9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.painless.Compiler.Loader; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.spi.Whitelist; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptContext; @@ -101,9 +102,9 @@ public PainlessScriptEngine(Settings settings, Map, List, List> entry : contexts.entrySet()) { ScriptContext context = entry.getKey(); if (context.instanceClazz.equals(SearchScript.class) || context.instanceClazz.equals(ExecutableScript.class)) { - contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new Definition(entry.getValue()))); + contextsToCompilers.put(context, new Compiler(GenericElasticsearchScript.class, new PainlessLookup(entry.getValue()))); } else { - contextsToCompilers.put(context, new Compiler(context.instanceClazz, new Definition(entry.getValue()))); + contextsToCompilers.put(context, new Compiler(context.instanceClazz, new PainlessLookup(entry.getValue()))); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java index 0ec806282db2f..df43dba7b3476 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java @@ -19,6 +19,8 @@ package org.elasticsearch.painless; +import org.elasticsearch.painless.lookup.PainlessLookup; + import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -42,7 +44,7 @@ public class ScriptClassInfo { private final List getMethods; private final List> getReturns; - public ScriptClassInfo(Definition definition, Class baseClass) { + public ScriptClassInfo(PainlessLookup painlessLookup, Class baseClass) { this.baseClass = baseClass; // Find the main method and the uses$argName methods @@ -68,8 +70,9 @@ public ScriptClassInfo(Definition definition, Class baseClass) { } if (m.getName().startsWith("get") && m.getName().equals("getClass") == false && Modifier.isStatic(m.getModifiers()) == false) { getReturns.add( - definitionTypeForClass(definition, m.getReturnType(), componentType -> "[" + m.getName() + "] has unknown return type [" - + componentType.getName() + "]. Painless can only support getters with return types that are whitelisted.")); + definitionTypeForClass(painlessLookup, m.getReturnType(), componentType -> "[" + m.getName() + "] has unknown return " + + "type [" + componentType.getName() + "]. Painless can only support getters with return types that are " + + "whitelisted.")); getMethods.add(new org.objectweb.asm.commons.Method(m.getName(), MethodType.methodType(m.getReturnType()).toMethodDescriptorString())); @@ -78,7 +81,7 @@ public ScriptClassInfo(Definition definition, Class baseClass) { } MethodType methodType = MethodType.methodType(executeMethod.getReturnType(), executeMethod.getParameterTypes()); this.executeMethod = new org.objectweb.asm.commons.Method(executeMethod.getName(), methodType.toMethodDescriptorString()); - executeMethodReturnType = definitionTypeForClass(definition, executeMethod.getReturnType(), + executeMethodReturnType = definitionTypeForClass(painlessLookup, executeMethod.getReturnType(), componentType -> "Painless can only implement execute methods returning a whitelisted type but [" + baseClass.getName() + "#execute] returns [" + componentType.getName() + "] which isn't whitelisted."); @@ -91,7 +94,7 @@ public ScriptClassInfo(Definition definition, Class baseClass) { + baseClass.getName() + "#execute] takes [1] argument."); } for (int arg = 0; arg < types.length; arg++) { - arguments.add(methodArgument(definition, types[arg], argumentNamesConstant[arg])); + arguments.add(methodArgument(painlessLookup, types[arg], argumentNamesConstant[arg])); } this.executeArguments = unmodifiableList(arguments); this.needsMethods = unmodifiableList(needsMethods); @@ -171,22 +174,22 @@ public String getName() { } } - private MethodArgument methodArgument(Definition definition, Class clazz, String argName) { - Class defClass = definitionTypeForClass(definition, clazz, componentType -> "[" + argName + "] is of unknown type [" + private MethodArgument methodArgument(PainlessLookup painlessLookup, Class clazz, String argName) { + Class defClass = definitionTypeForClass(painlessLookup, clazz, componentType -> "[" + argName + "] is of unknown type [" + componentType.getName() + ". Painless interfaces can only accept arguments that are of whitelisted types."); return new MethodArgument(defClass, argName); } - private static Class definitionTypeForClass(Definition definition, Class type, - Function, String> unknownErrorMessageSource) { - type = Definition.ObjectClassTodefClass(type); + private static Class definitionTypeForClass(PainlessLookup painlessLookup, Class type, + Function, String> unknownErrorMessageSource) { + type = PainlessLookup.ObjectClassTodefClass(type); Class componentType = type; while (componentType.isArray()) { componentType = componentType.getComponentType(); } - if (definition.getPainlessStructFromJavaClass(componentType) == null) { + if (painlessLookup.getPainlessStructFromJavaClass(componentType) == null) { throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType)); } 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 18d7d94492e67..db3aeff0483f6 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 @@ -20,6 +20,7 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.api.Augmentation; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.script.ScriptException; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; @@ -74,12 +75,12 @@ public final class WriterConstants { public static final Type STACK_OVERFLOW_ERROR_TYPE = Type.getType(StackOverflowError.class); public static final Type EXCEPTION_TYPE = Type.getType(Exception.class); public static final Type PAINLESS_EXPLAIN_ERROR_TYPE = Type.getType(PainlessExplainError.class); - public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", Definition.class); + public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", PainlessLookup.class); public static final Type OBJECT_TYPE = Type.getType(Object.class); public static final Type BITSET_TYPE = Type.getType(BitSet.class); - public static final Type DEFINITION_TYPE = Type.getType(Definition.class); + public static final Type DEFINITION_TYPE = Type.getType(PainlessLookup.class); public static final Type COLLECTIONS_TYPE = Type.getType(Collections.class); public static final Method EMPTY_MAP_METHOD = getAsmMethod(Map.class, "emptyMap"); @@ -103,10 +104,10 @@ public final class WriterConstants { public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class); /** - * A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there - * so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building regexes - * because it can do it statically. This is both faster and prevents the script from doing something super slow like building a regex - * per time it is run. + * A Method instance for {@linkplain Pattern#compile}. This isn't available from PainlessLookup because we intentionally don't add it + * there so that the script can't create regexes without this syntax. Essentially, our static regex syntax has a monopoly on building + * regexes because it can do it statically. This is both faster and prevents the script from doing something super slow like building a + * regex per time it is run. */ public static final Method PATTERN_COMPILE = getAsmMethod(Pattern.class, "compile", String.class, int.class); public static final Method PATTERN_MATCHER = getAsmMethod(Matcher.class, "matcher", CharSequence.class); @@ -118,7 +119,7 @@ public final class WriterConstants { static final Handle DEF_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), "$bootstrapDef", DEF_BOOTSTRAP_METHOD.getDescriptor(), false); public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class); - public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", Definition.class, + public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", PainlessLookup.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class); public static final Type DEF_UTIL_TYPE = Type.getType(Def.class); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java index add3aaabe51e0..e6c5da057980f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java @@ -23,7 +23,7 @@ import org.antlr.v4.runtime.LexerNoViableAltException; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.misc.Interval; -import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.Location; /** @@ -39,14 +39,14 @@ */ final class EnhancedPainlessLexer extends PainlessLexer { private final String sourceName; - private final Definition definition; + private final PainlessLookup painlessLookup; private Token current = null; - EnhancedPainlessLexer(CharStream charStream, String sourceName, Definition definition) { + EnhancedPainlessLexer(CharStream charStream, String sourceName, PainlessLookup painlessLookup) { super(charStream); this.sourceName = sourceName; - this.definition = definition; + this.painlessLookup = painlessLookup; } @Override @@ -75,7 +75,7 @@ public void recover(final LexerNoViableAltException lnvae) { @Override protected boolean isType(String name) { - return definition.isSimplePainlessType(name); + return painlessLookup.isSimplePainlessType(name); } @Override 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 a481c99a99d12..e2742ffb9936d 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 @@ -29,7 +29,7 @@ import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.tree.TerminalNode; import org.elasticsearch.painless.CompilerSettings; -import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Operation; @@ -174,9 +174,9 @@ public final class Walker extends PainlessParserBaseVisitor { public static SSource buildPainlessTree(ScriptClassInfo mainMethod, MainMethodReserved reserved, String sourceName, - String sourceText, CompilerSettings settings, Definition definition, + String sourceText, CompilerSettings settings, PainlessLookup painlessLookup, Printer debugStream) { - return new Walker(mainMethod, reserved, sourceName, sourceText, settings, definition, debugStream).source; + return new Walker(mainMethod, reserved, sourceName, sourceText, settings, painlessLookup, debugStream).source; } private final ScriptClassInfo scriptClassInfo; @@ -185,14 +185,14 @@ public static SSource buildPainlessTree(ScriptClassInfo mainMethod, MainMethodRe private final Printer debugStream; private final String sourceName; private final String sourceText; - private final Definition definition; + private final PainlessLookup painlessLookup; private final Deque reserved = new ArrayDeque<>(); private final Globals globals; private int syntheticCounter = 0; private Walker(ScriptClassInfo scriptClassInfo, MainMethodReserved reserved, String sourceName, String sourceText, - CompilerSettings settings, Definition definition, Printer debugStream) { + CompilerSettings settings, PainlessLookup painlessLookup, Printer debugStream) { this.scriptClassInfo = scriptClassInfo; this.reserved.push(reserved); this.debugStream = debugStream; @@ -200,13 +200,13 @@ private Walker(ScriptClassInfo scriptClassInfo, MainMethodReserved reserved, Str this.sourceName = Location.computeSourceName(sourceName); this.sourceText = sourceText; this.globals = new Globals(new BitSet(sourceText.length())); - this.definition = definition; + this.painlessLookup = painlessLookup; this.source = (SSource)visit(buildAntlrTree(sourceText)); } private SourceContext buildAntlrTree(String source) { ANTLRInputStream stream = new ANTLRInputStream(source); - PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, definition); + PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, painlessLookup); PainlessParser parser = new PainlessParser(new CommonTokenStream(lexer)); ParserErrorStrategy strategy = new ParserErrorStrategy(sourceName); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java new file mode 100644 index 0000000000000..2440fb45d4dfb --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessCast.java @@ -0,0 +1,67 @@ +/* + * 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.lookup; + +public class PainlessCast { + + /** Create a standard cast with no boxing/unboxing. */ + public static PainlessCast standard(Class from, Class to, boolean explicit) { + return new PainlessCast(from, to, explicit, null, null, null, null); + } + + /** Create a cast where the from type will be unboxed, and then the cast will be performed. */ + public static PainlessCast unboxFrom(Class from, Class to, boolean explicit, Class unboxFrom) { + return new PainlessCast(from, to, explicit, unboxFrom, null, null, null); + } + + /** Create a cast where the to type will be unboxed, and then the cast will be performed. */ + public static PainlessCast unboxTo(Class from, Class to, boolean explicit, Class unboxTo) { + return new PainlessCast(from, to, explicit, null, unboxTo, null, null); + } + + /** Create a cast where the from type will be boxed, and then the cast will be performed. */ + public static PainlessCast boxFrom(Class from, Class to, boolean explicit, Class boxFrom) { + return new PainlessCast(from, to, explicit, null, null, boxFrom, null); + } + + /** Create a cast where the to type will be boxed, and then the cast will be performed. */ + public static PainlessCast boxTo(Class from, Class to, boolean explicit, Class boxTo) { + return new PainlessCast(from, to, explicit, null, null, null, boxTo); + } + + public final Class from; + public final Class to; + public final boolean explicit; + public final Class unboxFrom; + public final Class unboxTo; + public final Class boxFrom; + public final Class boxTo; + + private PainlessCast(Class from, Class to, boolean explicit, + Class unboxFrom, Class unboxTo, Class boxFrom, Class boxTo) { + this.from = from; + this.to = to; + this.explicit = explicit; + this.unboxFrom = unboxFrom; + this.unboxTo = unboxTo; + this.boxFrom = boxFrom; + this.boxTo = boxTo; + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java new file mode 100644 index 0000000000000..7d84899b00e58 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessClass.java @@ -0,0 +1,103 @@ +/* + * 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.lookup; + +import java.lang.invoke.MethodHandle; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class PainlessClass { + public final String name; + public final Class clazz; + public final org.objectweb.asm.Type type; + + public final Map constructors; + public final Map staticMethods; + public final Map methods; + + public final Map staticMembers; + public final Map members; + + public final Map getters; + public final Map setters; + + public final PainlessMethod functionalMethod; + + PainlessClass(String name, Class clazz, org.objectweb.asm.Type type) { + this.name = name; + this.clazz = clazz; + this.type = type; + + constructors = new HashMap<>(); + staticMethods = new HashMap<>(); + methods = new HashMap<>(); + + staticMembers = new HashMap<>(); + members = new HashMap<>(); + + getters = new HashMap<>(); + setters = new HashMap<>(); + + functionalMethod = null; + } + + private PainlessClass(PainlessClass struct, PainlessMethod functionalMethod) { + name = struct.name; + clazz = struct.clazz; + type = struct.type; + + constructors = Collections.unmodifiableMap(struct.constructors); + staticMethods = Collections.unmodifiableMap(struct.staticMethods); + methods = Collections.unmodifiableMap(struct.methods); + + staticMembers = Collections.unmodifiableMap(struct.staticMembers); + members = Collections.unmodifiableMap(struct.members); + + getters = Collections.unmodifiableMap(struct.getters); + setters = Collections.unmodifiableMap(struct.setters); + + this.functionalMethod = functionalMethod; + } + + public PainlessClass freeze(PainlessMethod functionalMethod) { + return new PainlessClass(this, functionalMethod); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + PainlessClass struct = (PainlessClass)object; + + return name.equals(struct.name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java new file mode 100644 index 0000000000000..7c85bd269b461 --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessField.java @@ -0,0 +1,43 @@ +/* + * 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.lookup; + +import java.lang.invoke.MethodHandle; + +public final class PainlessField { + public final String name; + public final PainlessClass owner; + public final Class clazz; + public final String javaName; + public final int modifiers; + public final MethodHandle getter; + public final MethodHandle setter; + + PainlessField(String name, String javaName, PainlessClass owner, Class clazz, int modifiers, + MethodHandle getter, MethodHandle setter) { + this.name = name; + this.javaName = javaName; + this.owner = owner; + this.clazz = clazz; + this.modifiers = modifiers; + this.getter = getter; + this.setter = setter; + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java similarity index 71% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java index 25145a44b5853..5833767fbd3d2 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessLookup.java @@ -17,15 +17,17 @@ * under the License. */ -package org.elasticsearch.painless; +package org.elasticsearch.painless.lookup; import org.elasticsearch.painless.spi.Whitelist; -import org.objectweb.asm.Opcodes; +import org.elasticsearch.painless.spi.WhitelistClass; +import org.elasticsearch.painless.spi.WhitelistConstructor; +import org.elasticsearch.painless.spi.WhitelistField; +import org.elasticsearch.painless.spi.WhitelistMethod; import org.objectweb.asm.Type; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -34,7 +36,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Stack; import java.util.regex.Pattern; @@ -42,10 +43,10 @@ * The entire API for Painless. Also used as a whitelist for checking for legal * methods and fields during at both compile-time and runtime. */ -public final class Definition { +public final class PainlessLookup { - private static final Map methodCache = new HashMap<>(); - private static final Map fieldCache = new HashMap<>(); + private static final Map methodCache = new HashMap<>(); + private static final Map fieldCache = new HashMap<>(); private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$"); @@ -56,306 +57,6 @@ private def() { } } - public static class Method { - public final String name; - public final Struct owner; - public final Class augmentation; - public final Class rtn; - public final List> arguments; - public final org.objectweb.asm.commons.Method method; - public final int modifiers; - public final MethodHandle handle; - - public Method(String name, Struct owner, Class augmentation, Class rtn, List> arguments, - org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) { - this.name = name; - this.augmentation = augmentation; - this.owner = owner; - this.rtn = rtn; - this.arguments = Collections.unmodifiableList(arguments); - this.method = method; - this.modifiers = modifiers; - this.handle = handle; - } - - /** - * Returns MethodType for this method. - *

    - * This works even for user-defined Methods (where the MethodHandle is null). - */ - public MethodType getMethodType() { - // we have a methodhandle already (e.g. whitelisted class) - // just return its type - if (handle != null) { - return handle.type(); - } - // otherwise compute it - final Class params[]; - final Class returnValue; - if (augmentation != null) { - // static method disguised as virtual/interface method - params = new Class[1 + arguments.size()]; - params[0] = augmentation; - for (int i = 0; i < arguments.size(); i++) { - params[i + 1] = defClassToObjectClass(arguments.get(i)); - } - returnValue = defClassToObjectClass(rtn); - } else if (Modifier.isStatic(modifiers)) { - // static method: straightforward copy - params = new Class[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - params[i] = defClassToObjectClass(arguments.get(i)); - } - returnValue = defClassToObjectClass(rtn); - } else if ("".equals(name)) { - // constructor: returns the owner class - params = new Class[arguments.size()]; - for (int i = 0; i < arguments.size(); i++) { - params[i] = defClassToObjectClass(arguments.get(i)); - } - returnValue = owner.clazz; - } else { - // virtual/interface method: add receiver class - params = new Class[1 + arguments.size()]; - params[0] = owner.clazz; - for (int i = 0; i < arguments.size(); i++) { - params[i + 1] = defClassToObjectClass(arguments.get(i)); - } - returnValue = defClassToObjectClass(rtn); - } - return MethodType.methodType(returnValue, params); - } - - public void write(MethodWriter writer) { - final org.objectweb.asm.Type type; - final Class clazz; - if (augmentation != null) { - assert java.lang.reflect.Modifier.isStatic(modifiers); - clazz = augmentation; - type = org.objectweb.asm.Type.getType(augmentation); - } else { - clazz = owner.clazz; - type = owner.type; - } - - if (java.lang.reflect.Modifier.isStatic(modifiers)) { - // invokeStatic assumes that the owner class is not an interface, so this is a - // special case for interfaces where the interface method boolean needs to be set to - // true to reference the appropriate class constant when calling a static interface - // method since java 8 did not check, but java 9 and 10 do - if (java.lang.reflect.Modifier.isInterface(clazz.getModifiers())) { - writer.visitMethodInsn(Opcodes.INVOKESTATIC, - type.getInternalName(), name, getMethodType().toMethodDescriptorString(), true); - } else { - writer.invokeStatic(type, method); - } - } else if (java.lang.reflect.Modifier.isInterface(clazz.getModifiers())) { - writer.invokeInterface(type, method); - } else { - writer.invokeVirtual(type, method); - } - } - } - - public static final class Field { - public final String name; - public final Struct owner; - public final Class clazz; - public final String javaName; - public final int modifiers; - private final MethodHandle getter; - private final MethodHandle setter; - - private Field(String name, String javaName, Struct owner, Class clazz, int modifiers, MethodHandle getter, MethodHandle setter) { - this.name = name; - this.javaName = javaName; - this.owner = owner; - this.clazz = clazz; - this.modifiers = modifiers; - this.getter = getter; - this.setter = setter; - } - } - - // TODO: instead of hashing on this, we could have a 'next' pointer in Method itself, but it would make code more complex - // please do *NOT* under any circumstances change this to be the crappy Tuple from elasticsearch! - /** - * Key for looking up a method. - *

    - * Methods are keyed on both name and arity, and can be overloaded once per arity. - * This allows signatures such as {@code String.indexOf(String) vs String.indexOf(String, int)}. - *

    - * It is less flexible than full signature overloading where types can differ too, but - * better than just the name, and overloading types adds complexity to users, too. - */ - public static final class MethodKey { - public final String name; - public final int arity; - - /** - * Create a new lookup key - * @param name name of the method - * @param arity number of parameters - */ - public MethodKey(String name, int arity) { - this.name = Objects.requireNonNull(name); - this.arity = arity; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + arity; - result = prime * result + name.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - MethodKey other = (MethodKey) obj; - if (arity != other.arity) return false; - if (!name.equals(other.name)) return false; - return true; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(name); - sb.append('/'); - sb.append(arity); - return sb.toString(); - } - } - - public static final class Struct { - public final String name; - public final Class clazz; - public final org.objectweb.asm.Type type; - - public final Map constructors; - public final Map staticMethods; - public final Map methods; - - public final Map staticMembers; - public final Map members; - - public final Map getters; - public final Map setters; - - public final Method functionalMethod; - - private Struct(String name, Class clazz, org.objectweb.asm.Type type) { - this.name = name; - this.clazz = clazz; - this.type = type; - - constructors = new HashMap<>(); - staticMethods = new HashMap<>(); - methods = new HashMap<>(); - - staticMembers = new HashMap<>(); - members = new HashMap<>(); - - getters = new HashMap<>(); - setters = new HashMap<>(); - - functionalMethod = null; - } - - private Struct(Struct struct, Method functionalMethod) { - name = struct.name; - clazz = struct.clazz; - type = struct.type; - - constructors = Collections.unmodifiableMap(struct.constructors); - staticMethods = Collections.unmodifiableMap(struct.staticMethods); - methods = Collections.unmodifiableMap(struct.methods); - - staticMembers = Collections.unmodifiableMap(struct.staticMembers); - members = Collections.unmodifiableMap(struct.members); - - getters = Collections.unmodifiableMap(struct.getters); - setters = Collections.unmodifiableMap(struct.setters); - - this.functionalMethod = functionalMethod; - } - - private Struct freeze(Method functionalMethod) { - return new Struct(this, functionalMethod); - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - - if (object == null || getClass() != object.getClass()) { - return false; - } - - Struct struct = (Struct)object; - - return name.equals(struct.name); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - } - - public static class Cast { - - /** Create a standard cast with no boxing/unboxing. */ - public static Cast standard(Class from, Class to, boolean explicit) { - return new Cast(from, to, explicit, null, null, null, null); - } - - /** Create a cast where the from type will be unboxed, and then the cast will be performed. */ - public static Cast unboxFrom(Class from, Class to, boolean explicit, Class unboxFrom) { - return new Cast(from, to, explicit, unboxFrom, null, null, null); - } - - /** Create a cast where the to type will be unboxed, and then the cast will be performed. */ - public static Cast unboxTo(Class from, Class to, boolean explicit, Class unboxTo) { - return new Cast(from, to, explicit, null, unboxTo, null, null); - } - - /** Create a cast where the from type will be boxed, and then the cast will be performed. */ - public static Cast boxFrom(Class from, Class to, boolean explicit, Class boxFrom) { - return new Cast(from, to, explicit, null, null, boxFrom, null); - } - - /** Create a cast where the to type will be boxed, and then the cast will be performed. */ - public static Cast boxTo(Class from, Class to, boolean explicit, Class boxTo) { - return new Cast(from, to, explicit, null, null, null, boxTo); - } - - public final Class from; - public final Class to; - public final boolean explicit; - public final Class unboxFrom; - public final Class unboxTo; - public final Class boxFrom; - public final Class boxTo; - - private Cast(Class from, Class to, boolean explicit, Class unboxFrom, Class unboxTo, Class boxFrom, Class boxTo) { - this.from = from; - this.to = to; - this.explicit = explicit; - this.unboxFrom = unboxFrom; - this.unboxTo = unboxTo; - this.boxFrom = boxFrom; - this.boxTo = boxTo; - } - } - public static Class getBoxedType(Class clazz) { if (clazz == boolean.class) { return Boolean.class; @@ -520,29 +221,29 @@ private static String buildFieldCacheKey(String structName, String fieldName, St return structName + fieldName + typeName; } - public Collection getStructs() { + public Collection getStructs() { return javaClassesToPainlessStructs.values(); } private final Map> painlessTypesToJavaClasses; - private final Map, Struct> javaClassesToPainlessStructs; + private final Map, PainlessClass> javaClassesToPainlessStructs; - public Definition(List whitelists) { + public PainlessLookup(List whitelists) { painlessTypesToJavaClasses = new HashMap<>(); javaClassesToPainlessStructs = new HashMap<>(); String origin = null; painlessTypesToJavaClasses.put("def", def.class); - javaClassesToPainlessStructs.put(def.class, new Struct("def", Object.class, Type.getType(Object.class))); + javaClassesToPainlessStructs.put(def.class, new PainlessClass("def", Object.class, Type.getType(Object.class))); try { // first iteration collects all the Painless type names that // are used for validation during the second iteration for (Whitelist whitelist : whitelists) { - for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) { + for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) { String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.'); - Struct painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName)); + PainlessClass painlessStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(painlessTypeName)); if (painlessStruct != null && painlessStruct.clazz.getName().equals(whitelistStruct.javaClassName) == false) { throw new IllegalArgumentException("struct [" + painlessStruct.name + "] cannot represent multiple classes " + @@ -561,20 +262,20 @@ public Definition(List whitelists) { // be available in Painless along with validating they exist and all their types have // been white-listed during the first iteration for (Whitelist whitelist : whitelists) { - for (Whitelist.Struct whitelistStruct : whitelist.whitelistStructs) { + for (WhitelistClass whitelistStruct : whitelist.whitelistStructs) { String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.'); - for (Whitelist.Constructor whitelistConstructor : whitelistStruct.whitelistConstructors) { + for (WhitelistConstructor whitelistConstructor : whitelistStruct.whitelistConstructors) { origin = whitelistConstructor.origin; addConstructor(painlessTypeName, whitelistConstructor); } - for (Whitelist.Method whitelistMethod : whitelistStruct.whitelistMethods) { + for (WhitelistMethod whitelistMethod : whitelistStruct.whitelistMethods) { origin = whitelistMethod.origin; addMethod(whitelist.javaClassLoader, painlessTypeName, whitelistMethod); } - for (Whitelist.Field whitelistField : whitelistStruct.whitelistFields) { + for (WhitelistField whitelistField : whitelistStruct.whitelistFields) { origin = whitelistField.origin; addField(painlessTypeName, whitelistField); } @@ -587,7 +288,7 @@ public Definition(List whitelists) { // goes through each Painless struct and determines the inheritance list, // and then adds all inherited types to the Painless struct's whitelist for (Class javaClass : javaClassesToPainlessStructs.keySet()) { - Struct painlessStruct = javaClassesToPainlessStructs.get(javaClass); + PainlessClass painlessStruct = javaClassesToPainlessStructs.get(javaClass); List painlessSuperStructs = new ArrayList<>(); Class javaSuperClass = painlessStruct.clazz.getSuperclass(); @@ -598,7 +299,7 @@ public Definition(List whitelists) { // adds super classes to the inheritance list if (javaSuperClass != null && javaSuperClass.isInterface() == false) { while (javaSuperClass != null) { - Struct painlessSuperStruct = javaClassesToPainlessStructs.get(javaSuperClass); + PainlessClass painlessSuperStruct = javaClassesToPainlessStructs.get(javaSuperClass); if (painlessSuperStruct != null) { painlessSuperStructs.add(painlessSuperStruct.name); @@ -614,7 +315,7 @@ public Definition(List whitelists) { Class javaInterfaceLookup = javaInteraceLookups.pop(); for (Class javaSuperInterface : javaInterfaceLookup.getInterfaces()) { - Struct painlessInterfaceStruct = javaClassesToPainlessStructs.get(javaSuperInterface); + PainlessClass painlessInterfaceStruct = javaClassesToPainlessStructs.get(javaSuperInterface); if (painlessInterfaceStruct != null) { String painlessInterfaceStructName = painlessInterfaceStruct.name; @@ -635,7 +336,7 @@ public Definition(List whitelists) { // copies methods and fields from Object into interface types if (painlessStruct.clazz.isInterface() || (def.class.getSimpleName()).equals(painlessStruct.name)) { - Struct painlessObjectStruct = javaClassesToPainlessStructs.get(Object.class); + PainlessClass painlessObjectStruct = javaClassesToPainlessStructs.get(Object.class); if (painlessObjectStruct != null) { copyStruct(painlessStruct.name, Collections.singletonList(painlessObjectStruct.name)); @@ -644,17 +345,17 @@ public Definition(List whitelists) { } // precompute runtime classes - for (Struct painlessStruct : javaClassesToPainlessStructs.values()) { + for (PainlessClass painlessStruct : javaClassesToPainlessStructs.values()) { addRuntimeClass(painlessStruct); } // copy all structs to make them unmodifiable for outside users: - for (Map.Entry,Struct> entry : javaClassesToPainlessStructs.entrySet()) { + for (Map.Entry,PainlessClass> entry : javaClassesToPainlessStructs.entrySet()) { entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue()))); } } - private void addStruct(ClassLoader whitelistClassLoader, Whitelist.Struct whitelistStruct) { + private void addStruct(ClassLoader whitelistClassLoader, WhitelistClass whitelistStruct) { String painlessTypeName = whitelistStruct.javaClassName.replace('$', '.'); String importedPainlessTypeName = painlessTypeName; @@ -688,10 +389,10 @@ private void addStruct(ClassLoader whitelistClassLoader, Whitelist.Struct whitel } } - Struct existingStruct = javaClassesToPainlessStructs.get(javaClass); + PainlessClass existingStruct = javaClassesToPainlessStructs.get(javaClass); if (existingStruct == null) { - Struct struct = new Struct(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass)); + PainlessClass struct = new PainlessClass(painlessTypeName, javaClass, org.objectweb.asm.Type.getType(javaClass)); painlessTypesToJavaClasses.put(painlessTypeName, javaClass); javaClassesToPainlessStructs.put(javaClass, struct); } else if (existingStruct.clazz.equals(javaClass) == false) { @@ -725,8 +426,8 @@ private void addStruct(ClassLoader whitelistClassLoader, Whitelist.Struct whitel } } - private void addConstructor(String ownerStructName, Whitelist.Constructor whitelistConstructor) { - Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName)); + private void addConstructor(String ownerStructName, WhitelistConstructor whitelistConstructor) { + PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName)); if (ownerStruct == null) { throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for constructor with " + @@ -760,8 +461,8 @@ private void addConstructor(String ownerStructName, Whitelist.Constructor whitel " with constructor parameters " + whitelistConstructor.painlessParameterTypeNames, exception); } - MethodKey painlessMethodKey = new MethodKey("", whitelistConstructor.painlessParameterTypeNames.size()); - Method painlessConstructor = ownerStruct.constructors.get(painlessMethodKey); + PainlessMethodKey painlessMethodKey = new PainlessMethodKey("", whitelistConstructor.painlessParameterTypeNames.size()); + PainlessMethod painlessConstructor = ownerStruct.constructors.get(painlessMethodKey); if (painlessConstructor == null) { org.objectweb.asm.commons.Method asmConstructor = org.objectweb.asm.commons.Method.getMethod(javaConstructor); @@ -775,7 +476,7 @@ private void addConstructor(String ownerStructName, Whitelist.Constructor whitel } painlessConstructor = methodCache.computeIfAbsent(buildMethodCacheKey(ownerStruct.name, "", painlessParametersTypes), - key -> new Method("", ownerStruct, null, void.class, painlessParametersTypes, + key -> new PainlessMethod("", ownerStruct, null, void.class, painlessParametersTypes, asmConstructor, javaConstructor.getModifiers(), javaHandle)); ownerStruct.constructors.put(painlessMethodKey, painlessConstructor); } else if (painlessConstructor.arguments.equals(painlessParametersTypes) == false){ @@ -785,8 +486,8 @@ private void addConstructor(String ownerStructName, Whitelist.Constructor whitel } } - private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, Whitelist.Method whitelistMethod) { - Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName)); + private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, WhitelistMethod whitelistMethod) { + PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName)); if (ownerStruct == null) { throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " + @@ -864,10 +565,11 @@ private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, "and parameters " + whitelistMethod.painlessParameterTypeNames); } - MethodKey painlessMethodKey = new MethodKey(whitelistMethod.javaMethodName, whitelistMethod.painlessParameterTypeNames.size()); + PainlessMethodKey painlessMethodKey = + new PainlessMethodKey(whitelistMethod.javaMethodName, whitelistMethod.painlessParameterTypeNames.size()); if (javaAugmentedClass == null && Modifier.isStatic(javaMethod.getModifiers())) { - Method painlessMethod = ownerStruct.staticMethods.get(painlessMethodKey); + PainlessMethod painlessMethod = ownerStruct.staticMethods.get(painlessMethodKey); if (painlessMethod == null) { org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod); @@ -882,8 +584,8 @@ private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, painlessMethod = methodCache.computeIfAbsent( buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes), - key -> new Method(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnClass, painlessParametersTypes, - asmMethod, javaMethod.getModifiers(), javaMethodHandle)); + key -> new PainlessMethod(whitelistMethod.javaMethodName, ownerStruct, null, painlessReturnClass, + painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle)); ownerStruct.staticMethods.put(painlessMethodKey, painlessMethod); } else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn == painlessReturnClass && painlessMethod.arguments.equals(painlessParametersTypes)) == false) { @@ -893,7 +595,7 @@ private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, "and parameters " + painlessParametersTypes + " and " + painlessMethod.arguments); } } else { - Method painlessMethod = ownerStruct.methods.get(painlessMethodKey); + PainlessMethod painlessMethod = ownerStruct.methods.get(painlessMethodKey); if (painlessMethod == null) { org.objectweb.asm.commons.Method asmMethod = org.objectweb.asm.commons.Method.getMethod(javaMethod); @@ -908,7 +610,7 @@ private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, painlessMethod = methodCache.computeIfAbsent( buildMethodCacheKey(ownerStruct.name, whitelistMethod.javaMethodName, painlessParametersTypes), - key -> new Method(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnClass, + key -> new PainlessMethod(whitelistMethod.javaMethodName, ownerStruct, javaAugmentedClass, painlessReturnClass, painlessParametersTypes, asmMethod, javaMethod.getModifiers(), javaMethodHandle)); ownerStruct.methods.put(painlessMethodKey, painlessMethod); } else if ((painlessMethod.name.equals(whitelistMethod.javaMethodName) && painlessMethod.rtn.equals(painlessReturnClass) && @@ -921,8 +623,8 @@ private void addMethod(ClassLoader whitelistClassLoader, String ownerStructName, } } - private void addField(String ownerStructName, Whitelist.Field whitelistField) { - Struct ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName)); + private void addField(String ownerStructName, WhitelistField whitelistField) { + PainlessClass ownerStruct = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(ownerStructName)); if (ownerStruct == null) { throw new IllegalArgumentException("owner struct [" + ownerStructName + "] not defined for method with " + @@ -958,12 +660,12 @@ private void addField(String ownerStructName, Whitelist.Field whitelistField) { "with owner struct [" + ownerStruct.name + "] is not final"); } - Field painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName); + PainlessField painlessField = ownerStruct.staticMembers.get(whitelistField.javaFieldName); if (painlessField == null) { painlessField = fieldCache.computeIfAbsent( buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldClass.getName()), - key -> new Field(whitelistField.javaFieldName, javaField.getName(), + key -> new PainlessField(whitelistField.javaFieldName, javaField.getName(), ownerStruct, painlessFieldClass, javaField.getModifiers(), null, null)); ownerStruct.staticMembers.put(whitelistField.javaFieldName, painlessField); } else if (painlessField.clazz != painlessFieldClass) { @@ -987,12 +689,12 @@ private void addField(String ownerStructName, Whitelist.Field whitelistField) { " not found for class [" + ownerStruct.clazz.getName() + "]."); } - Field painlessField = ownerStruct.members.get(whitelistField.javaFieldName); + PainlessField painlessField = ownerStruct.members.get(whitelistField.javaFieldName); if (painlessField == null) { painlessField = fieldCache.computeIfAbsent( buildFieldCacheKey(ownerStruct.name, whitelistField.javaFieldName, painlessFieldClass.getName()), - key -> new Field(whitelistField.javaFieldName, javaField.getName(), + key -> new PainlessField(whitelistField.javaFieldName, javaField.getName(), ownerStruct, painlessFieldClass, javaField.getModifiers(), javaMethodHandleGetter, javaMethodHandleSetter)); ownerStruct.members.put(whitelistField.javaFieldName, painlessField); } else if (painlessField.clazz != painlessFieldClass) { @@ -1003,14 +705,14 @@ private void addField(String ownerStructName, Whitelist.Field whitelistField) { } private void copyStruct(String struct, List children) { - final Struct owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct)); + final PainlessClass owner = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(struct)); if (owner == null) { throw new IllegalArgumentException("Owner struct [" + struct + "] not defined for copy."); } for (int count = 0; count < children.size(); ++count) { - final Struct child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count))); + final PainlessClass child = javaClassesToPainlessStructs.get(painlessTypesToJavaClasses.get(children.get(count))); if (child == null) { throw new IllegalArgumentException("Child struct [" + children.get(count) + "]" + @@ -1022,9 +724,9 @@ private void copyStruct(String struct, List children) { " 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(); + for (Map.Entry kvPair : child.methods.entrySet()) { + PainlessMethodKey methodKey = kvPair.getKey(); + PainlessMethod method = kvPair.getValue(); if (owner.methods.get(methodKey) == null) { // TODO: some of these are no longer valid or outright don't work // TODO: since classes may not come from the Painless classloader @@ -1076,10 +778,10 @@ private void copyStruct(String struct, List children) { } } - for (Field field : child.members.values()) { + for (PainlessField field : child.members.values()) { if (owner.members.get(field.name) == null) { owner.members.put(field.name, - new Field(field.name, field.javaName, owner, field.clazz, field.modifiers, field.getter, field.setter)); + new PainlessField(field.name, field.javaName, owner, field.clazz, field.modifiers, field.getter, field.setter)); } } } @@ -1088,11 +790,11 @@ private void copyStruct(String struct, List children) { /** * Precomputes a more efficient structure for dynamic method/field access. */ - private void addRuntimeClass(final Struct struct) { + private void addRuntimeClass(final PainlessClass struct) { // add all getters/setters - for (Map.Entry method : struct.methods.entrySet()) { + for (Map.Entry method : struct.methods.entrySet()) { String name = method.getKey().name; - Method m = method.getValue(); + PainlessMethod m = method.getValue(); if (m.arguments.size() == 0 && name.startsWith("get") && @@ -1124,14 +826,14 @@ private void addRuntimeClass(final Struct struct) { } // add all members - for (Map.Entry member : struct.members.entrySet()) { + for (Map.Entry member : struct.members.entrySet()) { struct.getters.put(member.getKey(), member.getValue().getter); struct.setters.put(member.getKey(), member.getValue().setter); } } /** computes the functional interface method for a class, or returns null */ - private Method computeFunctionalInterfaceMethod(Struct clazz) { + private PainlessMethod computeFunctionalInterfaceMethod(PainlessClass clazz) { if (!clazz.clazz.isInterface()) { return null; } @@ -1166,7 +868,7 @@ private Method computeFunctionalInterfaceMethod(Struct clazz) { } // inspect the one method found from the reflection API, it should match the whitelist! java.lang.reflect.Method oneMethod = methods.get(0); - Method painless = clazz.methods.get(new Definition.MethodKey(oneMethod.getName(), oneMethod.getParameterCount())); + PainlessMethod painless = clazz.methods.get(new PainlessMethodKey(oneMethod.getName(), oneMethod.getParameterCount())); if (painless == null || painless.method.equals(org.objectweb.asm.commons.Method.getMethod(oneMethod)) == false) { throw new IllegalArgumentException("Class: " + clazz.name + " is functional but the functional " + "method is not whitelisted!"); @@ -1178,7 +880,7 @@ public boolean isSimplePainlessType(String painlessType) { return painlessTypesToJavaClasses.containsKey(painlessType); } - public Struct getPainlessStructFromJavaClass(Class clazz) { + public PainlessClass getPainlessStructFromJavaClass(Class clazz) { return javaClassesToPainlessStructs.get(clazz); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java new file mode 100644 index 0000000000000..e87e1d4bf38fc --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethod.java @@ -0,0 +1,130 @@ +/* + * 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.lookup; + +import org.elasticsearch.painless.MethodWriter; +import org.objectweb.asm.Opcodes; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.List; + +public class PainlessMethod { + public final String name; + public final PainlessClass owner; + public final Class augmentation; + public final Class rtn; + public final List> arguments; + public final org.objectweb.asm.commons.Method method; + public final int modifiers; + public final MethodHandle handle; + + public PainlessMethod(String name, PainlessClass owner, Class augmentation, Class rtn, List> arguments, + org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) { + this.name = name; + this.augmentation = augmentation; + this.owner = owner; + this.rtn = rtn; + this.arguments = Collections.unmodifiableList(arguments); + this.method = method; + this.modifiers = modifiers; + this.handle = handle; + } + + /** + * Returns MethodType for this method. + *

    + * This works even for user-defined Methods (where the MethodHandle is null). + */ + public MethodType getMethodType() { + // we have a methodhandle already (e.g. whitelisted class) + // just return its type + if (handle != null) { + return handle.type(); + } + // otherwise compute it + final Class params[]; + final Class returnValue; + if (augmentation != null) { + // static method disguised as virtual/interface method + params = new Class[1 + arguments.size()]; + params[0] = augmentation; + for (int i = 0; i < arguments.size(); i++) { + params[i + 1] = PainlessLookup.defClassToObjectClass(arguments.get(i)); + } + returnValue = PainlessLookup.defClassToObjectClass(rtn); + } else if (Modifier.isStatic(modifiers)) { + // static method: straightforward copy + params = new Class[arguments.size()]; + for (int i = 0; i < arguments.size(); i++) { + params[i] = PainlessLookup.defClassToObjectClass(arguments.get(i)); + } + returnValue = PainlessLookup.defClassToObjectClass(rtn); + } else if ("".equals(name)) { + // constructor: returns the owner class + params = new Class[arguments.size()]; + for (int i = 0; i < arguments.size(); i++) { + params[i] = PainlessLookup.defClassToObjectClass(arguments.get(i)); + } + returnValue = owner.clazz; + } else { + // virtual/interface method: add receiver class + params = new Class[1 + arguments.size()]; + params[0] = owner.clazz; + for (int i = 0; i < arguments.size(); i++) { + params[i + 1] = PainlessLookup.defClassToObjectClass(arguments.get(i)); + } + returnValue = PainlessLookup.defClassToObjectClass(rtn); + } + return MethodType.methodType(returnValue, params); + } + + public void write(MethodWriter writer) { + final org.objectweb.asm.Type type; + final Class clazz; + if (augmentation != null) { + assert Modifier.isStatic(modifiers); + clazz = augmentation; + type = org.objectweb.asm.Type.getType(augmentation); + } else { + clazz = owner.clazz; + type = owner.type; + } + + if (Modifier.isStatic(modifiers)) { + // invokeStatic assumes that the owner class is not an interface, so this is a + // special case for interfaces where the interface method boolean needs to be set to + // true to reference the appropriate class constant when calling a static interface + // method since java 8 did not check, but java 9 and 10 do + if (Modifier.isInterface(clazz.getModifiers())) { + writer.visitMethodInsn(Opcodes.INVOKESTATIC, + type.getInternalName(), name, getMethodType().toMethodDescriptorString(), true); + } else { + writer.invokeStatic(type, method); + } + } else if (Modifier.isInterface(clazz.getModifiers())) { + writer.invokeInterface(type, method); + } else { + writer.invokeVirtual(type, method); + } + } +} diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethodKey.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethodKey.java new file mode 100644 index 0000000000000..49413ab0c5fef --- /dev/null +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/lookup/PainlessMethodKey.java @@ -0,0 +1,75 @@ +/* + * 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.lookup; + +import java.util.Objects; + +/** + * Key for looking up a method. + *

    + * Methods are keyed on both name and arity, and can be overloaded once per arity. + * This allows signatures such as {@code String.indexOf(String) vs String.indexOf(String, int)}. + *

    + * It is less flexible than full signature overloading where types can differ too, but + * better than just the name, and overloading types adds complexity to users, too. + */ +public final class PainlessMethodKey { + public final String name; + public final int arity; + + /** + * Create a new lookup key + * @param name name of the method + * @param arity number of parameters + */ + public PainlessMethodKey(String name, int arity) { + this.name = Objects.requireNonNull(name); + this.arity = arity; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + arity; + result = prime * result + name.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + PainlessMethodKey other = (PainlessMethodKey) obj; + if (arity != other.arity) return false; + if (!name.equals(other.name)) return false; + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(name); + sb.append('/'); + sb.append(arity); + return sb.toString(); + } +} 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 550e9db9495b7..0dfcf4d409335 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 @@ -20,8 +20,8 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Cast; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessCast; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -118,7 +118,7 @@ public abstract class AExpression extends ANode { * @return The new child node for the parent node calling this method. */ AExpression cast(Locals locals) { - Cast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal); + PainlessCast cast = AnalyzerCaster.getLegalCast(location, actual, expected, explicit, internal); if (cast == null) { if (constant == null || this instanceof EConstant) { @@ -157,7 +157,7 @@ AExpression cast(Locals locals) { return ecast; } else { - if (Definition.isConstantType(expected)) { + if (PainlessLookup.isConstantType(expected)) { // For the case where a cast is required, a constant is set, // and the constant can be immediately cast to the expected type. // An EConstant replaces this node with the constant cast appropriately diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java index dce04661dbca8..dda246b5f6cda 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EAssignment.java @@ -22,8 +22,8 @@ import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessCast; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -49,8 +49,8 @@ public final class EAssignment extends AExpression { private boolean cat = false; private Class promote = null; private Class shiftDistance; // for shifts, the RHS is promoted independently - private Cast there = null; - private Cast back = null; + private PainlessCast there = null; + private PainlessCast back = null; public EAssignment(Location location, AExpression lhs, AExpression rhs, boolean pre, boolean post, Operation operation) { super(location); 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 00168549b783e..46fbeefd6f557 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 @@ -21,8 +21,8 @@ import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -106,7 +106,7 @@ private void analyzeMul(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply multiply [*] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -148,7 +148,7 @@ private void analyzeDiv(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply divide [/] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -195,7 +195,7 @@ private void analyzeRem(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply remainder [%] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -242,7 +242,7 @@ private void analyzeAdd(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply add [+] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -300,7 +300,7 @@ private void analyzeSub(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply subtract [-] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -358,7 +358,7 @@ private void analyzeLSH(Locals variables) { if (lhspromote == null || rhspromote == null) { throw createError(new ClassCastException("Cannot apply left shift [<<] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote = lhspromote; @@ -405,7 +405,7 @@ private void analyzeRSH(Locals variables) { if (lhspromote == null || rhspromote == null) { throw createError(new ClassCastException("Cannot apply right shift [>>] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote = lhspromote; @@ -455,7 +455,7 @@ private void analyzeUSH(Locals variables) { if (lhspromote == null || rhspromote == null) { throw createError(new ClassCastException("Cannot apply unsigned shift [>>>] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (lhspromote == def.class || rhspromote == def.class) { @@ -498,7 +498,7 @@ private void analyzeBWAnd(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply and [&] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -537,7 +537,7 @@ private void analyzeXor(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply xor [^] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; @@ -577,7 +577,7 @@ private void analyzeBWOr(Locals variables) { if (promote == null) { throw createError(new ClassCastException("Cannot apply or [|] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } actual = promote; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java index 4c977fa66e89a..ede1a2a6b9c36 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java @@ -19,8 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -40,7 +40,7 @@ public final class ECallLocal extends AExpression { private final String name; private final List arguments; - private Method method = null; + private PainlessMethod method = null; public ECallLocal(Location location, String name, List arguments) { super(location); @@ -58,7 +58,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - MethodKey methodKey = new MethodKey(name, arguments.size()); + PainlessMethodKey methodKey = new PainlessMethodKey(name, arguments.size()); method = locals.getMethod(methodKey); if (method == null) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java index e6f2f7ebf91f9..a3e1b4bde6a86 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECapturingFunctionRef.java @@ -21,8 +21,8 @@ import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; @@ -69,7 +69,7 @@ void analyze(Locals locals) { defPointer = "D" + variable + "." + call + ",1"; } else { // typed implementation - defPointer = "S" + Definition.ClassToName(captured.clazz) + "." + call + ",1"; + defPointer = "S" + PainlessLookup.ClassToName(captured.clazz) + "." + call + ",1"; } actual = String.class; } else { @@ -77,7 +77,7 @@ void analyze(Locals locals) { // static case if (captured.clazz != def.class) { try { - ref = new FunctionRef(locals.getDefinition(), expected, Definition.ClassToName(captured.clazz), call, 1); + ref = new FunctionRef(locals.getPainlessLookup(), expected, PainlessLookup.ClassToName(captured.clazz), call, 1); // check casts between the interface method and the delegate method are legal for (int i = 0; i < ref.interfaceMethod.arguments.size(); ++i) { @@ -109,7 +109,7 @@ void write(MethodWriter writer, Globals globals) { // typed interface, dynamic implementation writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot()); Type methodType = Type.getMethodType(MethodWriter.getType(expected), MethodWriter.getType(captured.clazz)); - writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, Definition.ClassToName(expected)); + writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, PainlessLookup.ClassToName(expected)); } else { // typed interface, typed implementation writer.visitVarInsn(MethodWriter.getType(captured.clazz).getOpcode(Opcodes.ILOAD), captured.getSlot()); 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 2f492fe6edb36..55a9dbf71c891 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 @@ -19,8 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Cast; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessCast; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -35,9 +35,9 @@ final class ECast extends AExpression { private AExpression child; - private final Cast cast; + private final PainlessCast cast; - ECast(Location location, AExpression child, Cast cast) { + ECast(Location location, AExpression child, PainlessCast cast) { super(location); this.child = Objects.requireNonNull(child); @@ -63,6 +63,6 @@ void write(MethodWriter writer, Globals globals) { @Override public String toString() { - return singleLineToString(Definition.ClassToName(cast.to), child); + return singleLineToString(PainlessLookup.ClassToName(cast.to), child); } } 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 876229797ecb0..c0fccab8e8a8e 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 @@ -21,8 +21,8 @@ import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -93,7 +93,7 @@ private void analyzeEq(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply equals [==] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (promotedType == def.class) { @@ -142,7 +142,7 @@ private void analyzeEqR(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply reference equals [===] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } left.expected = promotedType; @@ -182,7 +182,7 @@ private void analyzeNE(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply not equals [!=] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (promotedType == def.class) { @@ -231,7 +231,7 @@ private void analyzeNER(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply reference not equals [!==] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } left.expected = promotedType; @@ -271,7 +271,7 @@ private void analyzeGTE(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply greater than or equals [>=] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (promotedType == def.class) { @@ -310,7 +310,7 @@ private void analyzeGT(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply greater than [>] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (promotedType == def.class) { @@ -349,7 +349,7 @@ private void analyzeLTE(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply less than or equals [<=] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (promotedType == def.class) { @@ -388,7 +388,7 @@ private void analyzeLT(Locals variables) { if (promotedType == null) { throw createError(new ClassCastException("Cannot apply less than [>=] to types " + - "[" + Definition.ClassToName(left.actual) + "] and [" + Definition.ClassToName(right.actual) + "].")); + "[" + PainlessLookup.ClassToName(left.actual) + "] and [" + PainlessLookup.ClassToName(right.actual) + "].")); } if (promotedType == def.class) { 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 5a897e04a8d98..d19068f8fa6a2 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 @@ -50,7 +50,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { try { - actual = locals.getDefinition().getJavaClassFromPainlessType(type); + actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + type + "].")); } 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 21bef9aa2ed5d..82c24e27c5d16 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 @@ -20,9 +20,9 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; @@ -66,15 +66,15 @@ void analyze(Locals locals) { try { if ("this".equals(type)) { // user's own function - Method interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod; + PainlessMethod interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod; if (interfaceMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + Definition.ClassToName(expected) + "], not a functional interface"); + "to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface"); } - Method delegateMethod = locals.getMethod(new MethodKey(call, interfaceMethod.arguments.size())); + PainlessMethod delegateMethod = locals.getMethod(new PainlessMethodKey(call, interfaceMethod.arguments.size())); if (delegateMethod == null) { throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + - "to [" + Definition.ClassToName(expected) + "], function not found"); + "to [" + PainlessLookup.ClassToName(expected) + "], function not found"); } ref = new FunctionRef(expected, interfaceMethod, delegateMethod, 0); @@ -90,7 +90,7 @@ void analyze(Locals locals) { } } else { // whitelist lookup - ref = new FunctionRef(locals.getDefinition(), expected, type, call, 0); + ref = new FunctionRef(locals.getPainlessLookup(), expected, type, call, 0); } } catch (IllegalArgumentException e) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java index 5296d79e214ed..a3835cbc21372 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EInstanceof.java @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -58,13 +58,13 @@ void analyze(Locals locals) { // ensure the specified type is part of the definition try { - clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } // map to wrapped type for primitive types - resolvedType = clazz.isPrimitive() ? Definition.getBoxedType(clazz) : Definition.defClassToObjectClass(clazz); + resolvedType = clazz.isPrimitive() ? PainlessLookup.getBoxedType(clazz) : PainlessLookup.defClassToObjectClass(clazz); // analyze and cast the expression expression.analyze(locals); @@ -75,7 +75,7 @@ void analyze(Locals locals) { primitiveExpression = expression.actual.isPrimitive(); // map to wrapped type for primitive types expressionType = expression.actual.isPrimitive() ? - Definition.getBoxedType(expression.actual) : Definition.defClassToObjectClass(clazz); + PainlessLookup.getBoxedType(expression.actual) : PainlessLookup.defClassToObjectClass(clazz); actual = boolean.class; } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java index e40d21ab110ab..a7b7a41fe051a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ELambda.java @@ -20,9 +20,9 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.FunctionRef; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; @@ -103,7 +103,7 @@ void extractVariables(Set variables) { void analyze(Locals locals) { Class returnType; List actualParamTypeStrs; - Method interfaceMethod; + PainlessMethod interfaceMethod; // inspect the target first, set interface method if we know it. if (expected == null) { interfaceMethod = null; @@ -120,15 +120,15 @@ void analyze(Locals locals) { } } else { // we know the method statically, infer return type and any unknown/def types - interfaceMethod = locals.getDefinition().getPainlessStructFromJavaClass(expected).functionalMethod; + interfaceMethod = locals.getPainlessLookup().getPainlessStructFromJavaClass(expected).functionalMethod; if (interfaceMethod == null) { - throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) + + throw createError(new IllegalArgumentException("Cannot pass lambda to [" + PainlessLookup.ClassToName(expected) + "], not a functional interface")); } // check arity before we manipulate parameters if (interfaceMethod.arguments.size() != paramTypeStrs.size()) throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name + - "] in [" + Definition.ClassToName(expected) + "]"); + "] in [" + PainlessLookup.ClassToName(expected) + "]"); // for method invocation, its allowed to ignore the return value if (interfaceMethod.rtn == void.class) { returnType = def.class; @@ -140,7 +140,7 @@ void analyze(Locals locals) { for (int i = 0; i < paramTypeStrs.size(); i++) { String paramType = paramTypeStrs.get(i); if (paramType == null) { - actualParamTypeStrs.add(Definition.ClassToName(interfaceMethod.arguments.get(i))); + actualParamTypeStrs.add(PainlessLookup.ClassToName(interfaceMethod.arguments.get(i))); } else { actualParamTypeStrs.add(paramType); } @@ -162,16 +162,16 @@ void analyze(Locals locals) { List paramTypes = new ArrayList<>(captures.size() + actualParamTypeStrs.size()); List paramNames = new ArrayList<>(captures.size() + paramNameStrs.size()); for (Variable var : captures) { - paramTypes.add(Definition.ClassToName(var.clazz)); + paramTypes.add(PainlessLookup.ClassToName(var.clazz)); paramNames.add(var.name); } paramTypes.addAll(actualParamTypeStrs); paramNames.addAll(paramNameStrs); // desugar lambda body into a synthetic method - desugared = new SFunction(reserved, location, Definition.ClassToName(returnType), name, + desugared = new SFunction(reserved, location, PainlessLookup.ClassToName(returnType), name, paramTypes, paramNames, statements, true); - desugared.generateSignature(locals.getDefinition()); + desugared.generateSignature(locals.getPainlessLookup()); desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType, desugared.parameters, captures.size(), reserved.getMaxLoopCounter())); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java index 05b10796cb4f9..518f1953525a6 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EListInit.java @@ -19,9 +19,9 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -37,8 +37,8 @@ public final class EListInit extends AExpression { private final List values; - private Method constructor = null; - private Method method = null; + private PainlessMethod constructor = null; + private PainlessMethod method = null; public EListInit(Location location, List values) { super(location); @@ -61,13 +61,14 @@ void analyze(Locals locals) { actual = ArrayList.class; - constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("", 0)); + constructor = + locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("", 0)); if (constructor == null) { throw createError(new IllegalStateException("Illegal tree structure.")); } - method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("add", 1)); + method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods.get(new PainlessMethodKey("add", 1)); if (method == null) { throw createError(new IllegalStateException("Illegal tree structure.")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java index f5763042b8191..45158aedcf787 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EMapInit.java @@ -19,9 +19,9 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -38,8 +38,8 @@ public final class EMapInit extends AExpression { private final List keys; private final List values; - private Method constructor = null; - private Method method = null; + private PainlessMethod constructor = null; + private PainlessMethod method = null; public EMapInit(Location location, List keys, List values) { super(location); @@ -67,13 +67,14 @@ void analyze(Locals locals) { actual = HashMap.class; - constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("", 0)); + constructor = + locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("", 0)); if (constructor == null) { throw createError(new IllegalStateException("Illegal tree structure.")); } - method = locals.getDefinition().getPainlessStructFromJavaClass(actual).methods.get(new MethodKey("put", 2)); + method = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).methods.get(new PainlessMethodKey("put", 2)); if (method == null) { throw createError(new IllegalStateException("Illegal tree structure.")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java index 1a0a718ae7fc8..f9bd4cebc3fed 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewArray.java @@ -61,7 +61,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index e3a926ef2244b..197e2fcb7fd42 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -19,13 +19,13 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import java.util.List; import java.util.Objects; @@ -39,7 +39,7 @@ public final class ENewObj extends AExpression { private final String type; private final List arguments; - private Method constructor; + private PainlessMethod constructor; public ENewObj(Location location, String type, List arguments) { super(location); @@ -58,13 +58,13 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { try { - actual = locals.getDefinition().getJavaClassFromPainlessType(this.type); + actual = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } - Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(actual); - constructor = struct.constructors.get(new Definition.MethodKey("", arguments.size())); + PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual); + constructor = struct.constructors.get(new PainlessMethodKey("", arguments.size())); if (constructor != null) { Class[] types = new Class[constructor.arguments.size()]; 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 ae682d5f7be3c..983819b6b2bf9 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 @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -53,7 +53,7 @@ void analyze(Locals locals) { if (expected != null) { if (expected.isPrimitive()) { throw createError(new IllegalArgumentException( - "Cannot cast null to a primitive type [" + Definition.ClassToName(expected) + "].")); + "Cannot cast null to a primitive type [" + PainlessLookup.ClassToName(expected) + "].")); } actual = expected; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java index 5ebf30f5781cf..a556b3ad315c6 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EStatic.java @@ -48,7 +48,7 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { try { - actual = locals.getDefinition().getJavaClassFromPainlessType(type); + actual = locals.getPainlessLookup().getJavaClassFromPainlessType(type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + type + "].")); } 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 a746ade26a15c..8e293556eac01 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 @@ -21,8 +21,8 @@ import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -93,7 +93,7 @@ void analyzeBWNot(Locals variables) { promote = AnalyzerCaster.promoteNumeric(child.actual, false); if (promote == null) { - throw createError(new ClassCastException("Cannot apply not [~] to type [" + Definition.ClassToName(child.actual) + "].")); + throw createError(new ClassCastException("Cannot apply not [~] to type [" + PainlessLookup.ClassToName(child.actual) + "].")); } child.expected = promote; @@ -122,7 +122,8 @@ void analyzerAdd(Locals variables) { promote = AnalyzerCaster.promoteNumeric(child.actual, true); if (promote == null) { - throw createError(new ClassCastException("Cannot apply positive [+] to type [" + Definition.ClassToName(child.actual) + "].")); + throw createError( + new ClassCastException("Cannot apply positive [+] to type [" + PainlessLookup.ClassToName(child.actual) + "].")); } child.expected = promote; @@ -155,7 +156,8 @@ void analyzerSub(Locals variables) { promote = AnalyzerCaster.promoteNumeric(child.actual, true); if (promote == null) { - throw createError(new ClassCastException("Cannot apply negative [-] to type [" + Definition.ClassToName(child.actual) + "].")); + throw createError( + new ClassCastException("Cannot apply negative [-] to type [" + PainlessLookup.ClassToName(child.actual) + "].")); } child.expected = promote; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PBrace.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PBrace.java index 0e2ab70897fe5..ec7d0f6d7bb7a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PBrace.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PBrace.java @@ -19,8 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -63,12 +63,12 @@ void analyze(Locals locals) { } else if (prefix.actual == def.class) { sub = new PSubDefArray(location, index); } else if (Map.class.isAssignableFrom(prefix.actual)) { - sub = new PSubMapShortcut(location, locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual), index); + sub = new PSubMapShortcut(location, locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual), index); } else if (List.class.isAssignableFrom(prefix.actual)) { - sub = new PSubListShortcut(location, locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual), index); + sub = new PSubListShortcut(location, locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual), index); } else { throw createError( - new IllegalArgumentException("Illegal array access on type [" + Definition.ClassToName(prefix.actual) + "].")); + new IllegalArgumentException("Illegal array access on type [" + PainlessLookup.ClassToName(prefix.actual) + "].")); } sub.write = write; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java index 6fff5a8e93f3e..12ff483248367 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PCallInvoke.java @@ -19,11 +19,11 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; -import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; +import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -71,14 +71,14 @@ void analyze(Locals locals) { throw createError(new IllegalArgumentException("Illegal call [" + name + "] on array type.")); } - Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual); + PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual); if (prefix.actual.isPrimitive()) { - struct = locals.getDefinition().getPainlessStructFromJavaClass(Definition.getBoxedType(prefix.actual)); + struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(PainlessLookup.getBoxedType(prefix.actual)); } - MethodKey methodKey = new MethodKey(name, arguments.size()); - Method method = prefix instanceof EStatic ? struct.staticMethods.get(methodKey) : struct.methods.get(methodKey); + PainlessMethodKey methodKey = new PainlessMethodKey(name, arguments.size()); + PainlessMethod method = prefix instanceof EStatic ? struct.staticMethods.get(methodKey) : struct.methods.get(methodKey); if (method != null) { sub = new PSubCallInvoke(location, method, prefix.actual, arguments); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java index de2c05dfa9b28..8d27162fc367b 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PField.java @@ -19,15 +19,16 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Field; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessField; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessClass; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import java.util.List; import java.util.Map; @@ -63,29 +64,29 @@ void analyze(Locals locals) { prefix = prefix.cast(locals); if (prefix.actual.isArray()) { - sub = new PSubArrayLength(location, Definition.ClassToName(prefix.actual), value); + sub = new PSubArrayLength(location, PainlessLookup.ClassToName(prefix.actual), value); } else if (prefix.actual == def.class) { sub = new PSubDefField(location, value); } else { - Struct struct = locals.getDefinition().getPainlessStructFromJavaClass(prefix.actual); - Field field = prefix instanceof EStatic ? struct.staticMembers.get(value) : struct.members.get(value); + PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(prefix.actual); + PainlessField field = prefix instanceof EStatic ? struct.staticMembers.get(value) : struct.members.get(value); if (field != null) { sub = new PSubField(location, field); } else { - Method getter = struct.methods.get( - new Definition.MethodKey("get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); + PainlessMethod getter = struct.methods.get( + new PainlessMethodKey("get" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); if (getter == null) { getter = struct.methods.get( - new Definition.MethodKey("is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); + new PainlessMethodKey("is" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 0)); } - Method setter = struct.methods.get( - new Definition.MethodKey("set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 1)); + PainlessMethod setter = struct.methods.get( + new PainlessMethodKey("set" + Character.toUpperCase(value.charAt(0)) + value.substring(1), 1)); if (getter != null || setter != null) { - sub = new PSubShortcut(location, value, Definition.ClassToName(prefix.actual), getter, setter); + sub = new PSubShortcut(location, value, PainlessLookup.ClassToName(prefix.actual), getter, setter); } else { EConstant index = new EConstant(location, value); index.analyze(locals); @@ -103,7 +104,7 @@ void analyze(Locals locals) { if (sub == null) { throw createError(new IllegalArgumentException( - "Unknown field [" + value + "] for type [" + Definition.ClassToName(prefix.actual) + "].")); + "Unknown field [" + value + "] for type [" + PainlessLookup.ClassToName(prefix.actual) + "].")); } if (nullSafe) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java index e13fe0d85c143..66ad0ecff1b89 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -34,11 +34,11 @@ */ final class PSubCallInvoke extends AExpression { - private final Method method; + private final PainlessMethod method; private final Class box; private final List arguments; - PSubCallInvoke(Location location, Method method, Class box, List arguments) { + PSubCallInvoke(Location location, PainlessMethod method, Class box, List arguments) { super(location); this.method = Objects.requireNonNull(method); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefArray.java index 27087928d4fec..8e30d43432953 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefArray.java @@ -20,7 +20,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java index 6428e47d1bacc..0882f19177006 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefCall.java @@ -20,7 +20,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefField.java index fbdfc47a65e66..41fcf563d241c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubDefField.java @@ -20,7 +20,7 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java index b386feacb846d..d6c367cfeabec 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubField.java @@ -19,8 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Field; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessField; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -35,9 +35,9 @@ */ final class PSubField extends AStoreable { - private final Field field; + private final PainlessField field; - PSubField(Location location, Field field) { + PSubField(Location location, PainlessField field) { super(location); this.field = Objects.requireNonNull(field); @@ -52,7 +52,7 @@ void extractVariables(Set variables) { void analyze(Locals locals) { if (write && Modifier.isFinal(field.modifiers)) { throw createError(new IllegalArgumentException( - "Cannot write to read-only field [" + field.name + "] for type [" + Definition.ClassToName(field.clazz) + "].")); + "Cannot write to read-only field [" + field.name + "] for type [" + PainlessLookup.ClassToName(field.clazz) + "].")); } actual = field.clazz; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java index 194a43e16dd16..5d881b30db22d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubListShortcut.java @@ -19,14 +19,14 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; import org.elasticsearch.painless.WriterConstants; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import java.util.Objects; import java.util.Set; @@ -36,13 +36,13 @@ */ final class PSubListShortcut extends AStoreable { - private final Struct struct; + private final PainlessClass struct; private AExpression index; - private Method getter; - private Method setter; + private PainlessMethod getter; + private PainlessMethod setter; - PSubListShortcut(Location location, Struct struct, AExpression index) { + PSubListShortcut(Location location, PainlessClass struct, AExpression index) { super(location); this.struct = Objects.requireNonNull(struct); @@ -56,8 +56,8 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - getter = struct.methods.get(new Definition.MethodKey("get", 1)); - setter = struct.methods.get(new Definition.MethodKey("set", 2)); + getter = struct.methods.get(new PainlessMethodKey("get", 1)); + setter = struct.methods.get(new PainlessMethodKey("set", 2)); if (getter != null && (getter.rtn == void.class || getter.arguments.size() != 1 || getter.arguments.get(0) != int.class)) { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java index 033db404640e4..4875d55cbeb58 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubMapShortcut.java @@ -19,13 +19,13 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import java.util.Objects; import java.util.Set; @@ -35,13 +35,13 @@ */ final class PSubMapShortcut extends AStoreable { - private final Struct struct; + private final PainlessClass struct; private AExpression index; - private Method getter; - private Method setter; + private PainlessMethod getter; + private PainlessMethod setter; - PSubMapShortcut(Location location, Struct struct, AExpression index) { + PSubMapShortcut(Location location, PainlessClass struct, AExpression index) { super(location); this.struct = Objects.requireNonNull(struct); @@ -55,8 +55,8 @@ void extractVariables(Set variables) { @Override void analyze(Locals locals) { - getter = struct.methods.get(new Definition.MethodKey("get", 1)); - setter = struct.methods.get(new Definition.MethodKey("put", 2)); + getter = struct.methods.get(new PainlessMethodKey("get", 1)); + setter = struct.methods.get(new PainlessMethodKey("put", 2)); if (getter != null && (getter.rtn == void.class || getter.arguments.size() != 1)) { throw createError(new IllegalArgumentException("Illegal map get shortcut for type [" + struct.name + "].")); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java index ff88f0018556c..4b2910dbc010e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java @@ -19,7 +19,7 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; @@ -34,10 +34,10 @@ final class PSubShortcut extends AStoreable { private final String value; private final String type; - private final Method getter; - private final Method setter; + private final PainlessMethod getter; + private final PainlessMethod setter; - PSubShortcut(Location location, String value, String type, Method getter, Method setter) { + PSubShortcut(Location location, String value, String type, PainlessMethod getter, PainlessMethod setter) { super(location); this.value = value; 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 98e45ca29f416..8a703c80cba2f 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 @@ -67,7 +67,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } 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 9f3f86abf438b..fb92c20e89e01 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 @@ -62,7 +62,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } 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 a3c8319825a26..e7d18ece0590d 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 @@ -19,8 +19,8 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -71,7 +71,7 @@ void analyze(Locals locals) { Class clazz; try { - clazz = locals.getDefinition().getJavaClassFromPainlessType(this.type); + clazz = locals.getPainlessLookup().getJavaClassFromPainlessType(this.type); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Not a type [" + this.type + "].")); } @@ -84,7 +84,8 @@ void analyze(Locals locals) { } else if (expression.actual == def.class || Iterable.class.isAssignableFrom(expression.actual)) { sub = new SSubEachIterable(location, variable, expression, block); } else { - throw createError(new IllegalArgumentException("Illegal for each type [" + Definition.ClassToName(expression.actual) + "].")); + throw createError( + new IllegalArgumentException("Illegal for each type [" + PainlessLookup.ClassToName(expression.actual) + "].")); } sub.analyze(locals); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java index 1b1e6bd2ef84b..628bb1d32d59e 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SFunction.java @@ -22,8 +22,8 @@ import org.elasticsearch.painless.CompilerSettings; import org.elasticsearch.painless.Constant; import org.elasticsearch.painless.Def; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Parameter; @@ -93,7 +93,7 @@ public int getMaxLoopCounter() { Class rtnType = null; List parameters = new ArrayList<>(); - Method method = null; + PainlessMethod method = null; private Variable loop = null; @@ -117,9 +117,9 @@ void extractVariables(Set variables) { throw new IllegalStateException("Illegal tree structure"); } - void generateSignature(Definition definition) { + void generateSignature(PainlessLookup painlessLookup) { try { - rtnType = definition.getJavaClassFromPainlessType(rtnTypeStr); + rtnType = painlessLookup.getJavaClassFromPainlessType(rtnTypeStr); } catch (IllegalArgumentException exception) { throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "].")); } @@ -133,9 +133,9 @@ void generateSignature(Definition definition) { for (int param = 0; param < this.paramTypeStrs.size(); ++param) { try { - Class paramType = definition.getJavaClassFromPainlessType(this.paramTypeStrs.get(param)); + Class paramType = painlessLookup.getJavaClassFromPainlessType(this.paramTypeStrs.get(param)); - paramClasses[param] = Definition.defClassToObjectClass(paramType); + paramClasses[param] = PainlessLookup.defClassToObjectClass(paramType); paramTypes.add(paramType); parameters.add(new Parameter(location, paramNameStrs.get(param), paramType)); } catch (IllegalArgumentException exception) { @@ -145,8 +145,8 @@ void generateSignature(Definition definition) { } org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method( - name, MethodType.methodType(Definition.defClassToObjectClass(rtnType), paramClasses).toMethodDescriptorString()); - this.method = new Method(name, null, null, rtnType, paramTypes, method, Modifier.STATIC | Modifier.PRIVATE, null); + name, MethodType.methodType(PainlessLookup.defClassToObjectClass(rtnType), paramClasses).toMethodDescriptorString()); + this.method = new PainlessMethod(name, null, null, rtnType, paramTypes, method, Modifier.STATIC | Modifier.PRIVATE, null); } @Override 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 efb6db278140d..4781457a57dfa 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 @@ -21,9 +21,9 @@ import org.elasticsearch.painless.CompilerSettings; import org.elasticsearch.painless.Constant; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -167,20 +167,20 @@ void extractVariables(Set variables) { throw new IllegalStateException("Illegal tree structure."); } - public void analyze(Definition definition) { - Map methods = new HashMap<>(); + public void analyze(PainlessLookup painlessLookup) { + Map methods = new HashMap<>(); for (SFunction function : functions) { - function.generateSignature(definition); + function.generateSignature(painlessLookup); - MethodKey key = new MethodKey(function.name, function.parameters.size()); + PainlessMethodKey key = new PainlessMethodKey(function.name, function.parameters.size()); if (methods.put(key, function.method) != null) { throw createError(new IllegalArgumentException("Duplicate functions with name [" + function.name + "].")); } } - analyze(Locals.newProgramScope(definition, methods.values())); + analyze(Locals.newProgramScope(painlessLookup, methods.values())); } @Override diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java index e0c9476ba640a..5db161b8002a4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachArray.java @@ -20,8 +20,8 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.AnalyzerCaster; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Cast; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessCast; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -41,7 +41,7 @@ final class SSubEachArray extends AStatement { private AExpression expression; private final SBlock block; - private Cast cast = null; + private PainlessCast cast = null; private Variable array = null; private Variable index = null; private Class indexed = null; @@ -109,6 +109,6 @@ void write(MethodWriter writer, Globals globals) { @Override public String toString() { - return singleLineToString(Definition.ClassToName(variable.clazz), variable.name, expression, block); + return singleLineToString(PainlessLookup.ClassToName(variable.clazz), variable.name, expression, block); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java index 11e0f15d7e4f8..faee2ed74a6d0 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSubEachIterable.java @@ -21,11 +21,11 @@ import org.elasticsearch.painless.AnalyzerCaster; import org.elasticsearch.painless.DefBootstrap; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; -import org.elasticsearch.painless.Definition.def; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessCast; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; +import org.elasticsearch.painless.lookup.PainlessLookup.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; @@ -51,9 +51,9 @@ final class SSubEachIterable extends AStatement { private final SBlock block; private final Variable variable; - private Cast cast = null; + private PainlessCast cast = null; private Variable iterator = null; - private Method method = null; + private PainlessMethod method = null; SSubEachIterable(Location location, Variable variable, AExpression expression, SBlock block) { super(location); @@ -77,11 +77,12 @@ void analyze(Locals locals) { if (expression.actual == def.class) { method = null; } else { - method = locals.getDefinition().getPainlessStructFromJavaClass(expression.actual).methods.get(new MethodKey("iterator", 0)); + method = locals.getPainlessLookup(). + getPainlessStructFromJavaClass(expression.actual).methods.get(new PainlessMethodKey("iterator", 0)); if (method == null) { throw createError(new IllegalArgumentException( - "Unable to create iterator for the type [" + Definition.ClassToName(expression.actual) + "].")); + "Unable to create iterator for the type [" + PainlessLookup.ClassToName(expression.actual) + "].")); } } @@ -132,6 +133,6 @@ void write(MethodWriter writer, Globals globals) { @Override public String toString() { - return singleLineToString(Definition.ClassToName(variable.clazz), variable.name, expression, block); + return singleLineToString(PainlessLookup.ClassToName(variable.clazz), variable.name, expression, block); } } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java index b369d8beca764..a702490fff9d4 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/AnalyzerCasterTests.java @@ -19,7 +19,7 @@ package org.elasticsearch.painless; -import org.elasticsearch.painless.Definition.Cast; +import org.elasticsearch.painless.lookup.PainlessCast; import org.elasticsearch.test.ESTestCase; @@ -35,7 +35,7 @@ private static void assertCast(Class actual, Class expected, boolean mustB return; } - Cast cast = AnalyzerCaster.getLegalCast(location, actual, expected, true, false); + PainlessCast cast = AnalyzerCaster.getLegalCast(location, actual, expected, true, false); assertEquals(actual, cast.from); assertEquals(expected, cast.to); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java index 59cafa96ddcb9..78e5814e963f7 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Map; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.spi.Whitelist; import static java.util.Collections.emptyMap; @@ -36,7 +37,7 @@ */ public class BaseClassTests extends ScriptTestCase { - private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS); + private final PainlessLookup painlessLookup = new PainlessLookup(Whitelist.BASE_WHITELISTS); public abstract static class Gets { @@ -67,7 +68,7 @@ public Map getTestMap() { } public void testGets() { - Compiler compiler = new Compiler(Gets.class, definition); + Compiler compiler = new Compiler(Gets.class, painlessLookup); Map map = new HashMap<>(); map.put("s", 1); @@ -85,7 +86,7 @@ public abstract static class NoArgs { public abstract Object execute(); } public void testNoArgs() { - Compiler compiler = new Compiler(NoArgs.class, definition); + Compiler compiler = new Compiler(NoArgs.class, painlessLookup); assertEquals(1, ((NoArgs)scriptEngine.compile(compiler, null, "1", emptyMap())).execute()); assertEquals("foo", ((NoArgs)scriptEngine.compile(compiler, null, "'foo'", emptyMap())).execute()); @@ -109,13 +110,13 @@ public abstract static class OneArg { public abstract Object execute(Object arg); } public void testOneArg() { - Compiler compiler = new Compiler(OneArg.class, definition); + Compiler compiler = new Compiler(OneArg.class, painlessLookup); Object rando = randomInt(); assertEquals(rando, ((OneArg)scriptEngine.compile(compiler, null, "arg", emptyMap())).execute(rando)); rando = randomAlphaOfLength(5); assertEquals(rando, ((OneArg)scriptEngine.compile(compiler, null, "arg", emptyMap())).execute(rando)); - Compiler noargs = new Compiler(NoArgs.class, definition); + Compiler noargs = new Compiler(NoArgs.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, () -> scriptEngine.compile(noargs, null, "doc", emptyMap())); assertEquals("Variable [doc] is not defined.", e.getMessage()); @@ -130,7 +131,7 @@ public abstract static class ArrayArg { public abstract Object execute(String[] arg); } public void testArrayArg() { - Compiler compiler = new Compiler(ArrayArg.class, definition); + Compiler compiler = new Compiler(ArrayArg.class, painlessLookup); String rando = randomAlphaOfLength(5); assertEquals(rando, ((ArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new String[] {rando, "foo"})); } @@ -140,7 +141,7 @@ public abstract static class PrimitiveArrayArg { public abstract Object execute(int[] arg); } public void testPrimitiveArrayArg() { - Compiler compiler = new Compiler(PrimitiveArrayArg.class, definition); + Compiler compiler = new Compiler(PrimitiveArrayArg.class, painlessLookup); int rando = randomInt(); assertEquals(rando, ((PrimitiveArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new int[] {rando, 10})); } @@ -150,7 +151,7 @@ public abstract static class DefArrayArg { public abstract Object execute(Object[] arg); } public void testDefArrayArg() { - Compiler compiler = new Compiler(DefArrayArg.class, definition); + Compiler compiler = new Compiler(DefArrayArg.class, painlessLookup); Object rando = randomInt(); assertEquals(rando, ((DefArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new Object[] {rando, 10})); rando = randomAlphaOfLength(5); @@ -168,7 +169,7 @@ public abstract static class ManyArgs { public abstract boolean needsD(); } public void testManyArgs() { - Compiler compiler = new Compiler(ManyArgs.class, definition); + Compiler compiler = new Compiler(ManyArgs.class, painlessLookup); int rando = randomInt(); assertEquals(rando, ((ManyArgs)scriptEngine.compile(compiler, null, "a", emptyMap())).execute(rando, 0, 0, 0)); assertEquals(10, ((ManyArgs)scriptEngine.compile(compiler, null, "a + b + c + d", emptyMap())).execute(1, 2, 3, 4)); @@ -196,7 +197,7 @@ public abstract static class VarargTest { public abstract Object execute(String... arg); } public void testVararg() { - Compiler compiler = new Compiler(VarargTest.class, definition); + Compiler compiler = new Compiler(VarargTest.class, painlessLookup); assertEquals("foo bar baz", ((VarargTest)scriptEngine.compile(compiler, null, "String.join(' ', Arrays.asList(arg))", emptyMap())) .execute("foo", "bar", "baz")); } @@ -212,7 +213,7 @@ public Object executeWithASingleOne(int a, int b, int c) { } } public void testDefaultMethods() { - Compiler compiler = new Compiler(DefaultMethods.class, definition); + Compiler compiler = new Compiler(DefaultMethods.class, painlessLookup); int rando = randomInt(); assertEquals(rando, ((DefaultMethods)scriptEngine.compile(compiler, null, "a", emptyMap())).execute(rando, 0, 0, 0)); assertEquals(rando, ((DefaultMethods)scriptEngine.compile(compiler, null, "a", emptyMap())).executeWithASingleOne(rando, 0, 0)); @@ -226,7 +227,7 @@ public abstract static class ReturnsVoid { public abstract void execute(Map map); } public void testReturnsVoid() { - Compiler compiler = new Compiler(ReturnsVoid.class, definition); + Compiler compiler = new Compiler(ReturnsVoid.class, painlessLookup); Map map = new HashMap<>(); ((ReturnsVoid)scriptEngine.compile(compiler, null, "map.a = 'foo'", emptyMap())).execute(map); assertEquals(singletonMap("a", "foo"), map); @@ -245,7 +246,7 @@ public abstract static class ReturnsPrimitiveBoolean { public abstract boolean execute(); } public void testReturnsPrimitiveBoolean() { - Compiler compiler = new Compiler(ReturnsPrimitiveBoolean.class, definition); + Compiler compiler = new Compiler(ReturnsPrimitiveBoolean.class, painlessLookup); assertEquals(true, ((ReturnsPrimitiveBoolean)scriptEngine.compile(compiler, null, "true", emptyMap())).execute()); assertEquals(false, ((ReturnsPrimitiveBoolean)scriptEngine.compile(compiler, null, "false", emptyMap())).execute()); @@ -287,7 +288,7 @@ public abstract static class ReturnsPrimitiveInt { public abstract int execute(); } public void testReturnsPrimitiveInt() { - Compiler compiler = new Compiler(ReturnsPrimitiveInt.class, definition); + Compiler compiler = new Compiler(ReturnsPrimitiveInt.class, painlessLookup); assertEquals(1, ((ReturnsPrimitiveInt)scriptEngine.compile(compiler, null, "1", emptyMap())).execute()); assertEquals(1, ((ReturnsPrimitiveInt)scriptEngine.compile(compiler, null, "(int) 1L", emptyMap())).execute()); @@ -329,7 +330,7 @@ public abstract static class ReturnsPrimitiveFloat { public abstract float execute(); } public void testReturnsPrimitiveFloat() { - Compiler compiler = new Compiler(ReturnsPrimitiveFloat.class, definition); + Compiler compiler = new Compiler(ReturnsPrimitiveFloat.class, painlessLookup); assertEquals(1.1f, ((ReturnsPrimitiveFloat)scriptEngine.compile(compiler, null, "1.1f", emptyMap())).execute(), 0); assertEquals(1.1f, ((ReturnsPrimitiveFloat)scriptEngine.compile(compiler, null, "(float) 1.1d", emptyMap())).execute(), 0); @@ -360,7 +361,7 @@ public abstract static class ReturnsPrimitiveDouble { public abstract double execute(); } public void testReturnsPrimitiveDouble() { - Compiler compiler = new Compiler(ReturnsPrimitiveDouble.class, definition); + Compiler compiler = new Compiler(ReturnsPrimitiveDouble.class, painlessLookup); assertEquals(1.0, ((ReturnsPrimitiveDouble)scriptEngine.compile(compiler, null, "1", emptyMap())).execute(), 0); assertEquals(1.0, ((ReturnsPrimitiveDouble)scriptEngine.compile(compiler, null, "1L", emptyMap())).execute(), 0); @@ -394,7 +395,7 @@ public abstract static class NoArgumentsConstant { public abstract Object execute(String foo); } public void testNoArgumentsConstant() { - Compiler compiler = new Compiler(NoArgumentsConstant.class, definition); + Compiler compiler = new Compiler(NoArgumentsConstant.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); assertThat(e.getMessage(), startsWith( @@ -407,7 +408,7 @@ public abstract static class WrongArgumentsConstant { public abstract Object execute(String foo); } public void testWrongArgumentsConstant() { - Compiler compiler = new Compiler(WrongArgumentsConstant.class, definition); + Compiler compiler = new Compiler(WrongArgumentsConstant.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); assertThat(e.getMessage(), startsWith( @@ -420,7 +421,7 @@ public abstract static class WrongLengthOfArgumentConstant { public abstract Object execute(String foo); } public void testWrongLengthOfArgumentConstant() { - Compiler compiler = new Compiler(WrongLengthOfArgumentConstant.class, definition); + Compiler compiler = new Compiler(WrongLengthOfArgumentConstant.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); assertThat(e.getMessage(), startsWith("[" + WrongLengthOfArgumentConstant.class.getName() + "#ARGUMENTS] has length [2] but [" @@ -432,7 +433,7 @@ public abstract static class UnknownArgType { public abstract Object execute(UnknownArgType foo); } public void testUnknownArgType() { - Compiler compiler = new Compiler(UnknownArgType.class, definition); + Compiler compiler = new Compiler(UnknownArgType.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); assertEquals("[foo] is of unknown type [" + UnknownArgType.class.getName() + ". Painless interfaces can only accept arguments " @@ -444,7 +445,7 @@ public abstract static class UnknownReturnType { public abstract UnknownReturnType execute(String foo); } public void testUnknownReturnType() { - Compiler compiler = new Compiler(UnknownReturnType.class, definition); + Compiler compiler = new Compiler(UnknownReturnType.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); assertEquals("Painless can only implement execute methods returning a whitelisted type but [" + UnknownReturnType.class.getName() @@ -456,7 +457,7 @@ public abstract static class UnknownArgTypeInArray { public abstract Object execute(UnknownArgTypeInArray[] foo); } public void testUnknownArgTypeInArray() { - Compiler compiler = new Compiler(UnknownArgTypeInArray.class, definition); + Compiler compiler = new Compiler(UnknownArgTypeInArray.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); assertEquals("[foo] is of unknown type [" + UnknownArgTypeInArray.class.getName() + ". Painless interfaces can only accept " @@ -468,7 +469,7 @@ public abstract static class TwoExecuteMethods { public abstract Object execute(boolean foo); } public void testTwoExecuteMethods() { - Compiler compiler = new Compiler(TwoExecuteMethods.class, definition); + Compiler compiler = new Compiler(TwoExecuteMethods.class, painlessLookup); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "null", emptyMap())); assertEquals("Painless can only implement interfaces that have a single method named [execute] but [" diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java index 279438e74a7c3..987eef31eeeaf 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DebugTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.spi.Whitelist; import org.elasticsearch.script.ScriptException; @@ -35,7 +36,7 @@ import static org.hamcrest.Matchers.not; public class DebugTests extends ScriptTestCase { - private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS); + private final PainlessLookup painlessLookup = new PainlessLookup(Whitelist.BASE_WHITELISTS); public void testExplain() { // Debug.explain can explain an object @@ -43,16 +44,16 @@ public void testExplain() { PainlessExplainError e = expectScriptThrows(PainlessExplainError.class, () -> exec( "Debug.explain(params.a)", singletonMap("a", dummy), true)); assertSame(dummy, e.getObjectToExplain()); - assertThat(e.getHeaders(definition), hasEntry("es.to_string", singletonList(dummy.toString()))); - assertThat(e.getHeaders(definition), hasEntry("es.java_class", singletonList("java.lang.Object"))); - assertThat(e.getHeaders(definition), hasEntry("es.painless_class", singletonList("java.lang.Object"))); + assertThat(e.getHeaders(painlessLookup), hasEntry("es.to_string", singletonList(dummy.toString()))); + assertThat(e.getHeaders(painlessLookup), hasEntry("es.java_class", singletonList("java.lang.Object"))); + assertThat(e.getHeaders(painlessLookup), hasEntry("es.painless_class", singletonList("java.lang.Object"))); // Null should be ok e = expectScriptThrows(PainlessExplainError.class, () -> exec("Debug.explain(null)")); assertNull(e.getObjectToExplain()); - assertThat(e.getHeaders(definition), hasEntry("es.to_string", singletonList("null"))); - assertThat(e.getHeaders(definition), not(hasKey("es.java_class"))); - assertThat(e.getHeaders(definition), not(hasKey("es.painless_class"))); + assertThat(e.getHeaders(painlessLookup), hasEntry("es.to_string", singletonList("null"))); + assertThat(e.getHeaders(painlessLookup), not(hasKey("es.java_class"))); + assertThat(e.getHeaders(painlessLookup), not(hasKey("es.painless_class"))); // You can't catch the explain exception e = expectScriptThrows(PainlessExplainError.class, () -> exec( diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/Debugger.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/Debugger.java index d17b9e55ab0c1..0d5e2748b7b32 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/Debugger.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/Debugger.java @@ -19,6 +19,7 @@ package org.elasticsearch.painless; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.spi.Whitelist; import org.objectweb.asm.util.Textifier; @@ -39,7 +40,7 @@ static String toString(Class iface, String source, CompilerSettings settings) PrintWriter outputWriter = new PrintWriter(output); Textifier textifier = new Textifier(); try { - new Compiler(iface, new Definition(Whitelist.BASE_WHITELISTS)) + new Compiler(iface, new PainlessLookup(Whitelist.BASE_WHITELISTS)) .compile("", source, settings, textifier); } catch (RuntimeException e) { textifier.print(outputWriter); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java index 52528c358fc82..ab4844dd58bd9 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java @@ -27,15 +27,16 @@ import java.util.Collections; import java.util.HashMap; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.spi.Whitelist; import org.elasticsearch.test.ESTestCase; public class DefBootstrapTests extends ESTestCase { - private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS); + private final PainlessLookup painlessLookup = new PainlessLookup(Whitelist.BASE_WHITELISTS); /** calls toString() on integers, twice */ public void testOneType() throws Throwable { - CallSite site = DefBootstrap.bootstrap(definition, + CallSite site = DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), @@ -55,7 +56,7 @@ public void testOneType() throws Throwable { } public void testTwoTypes() throws Throwable { - CallSite site = DefBootstrap.bootstrap(definition, + CallSite site = DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), @@ -80,7 +81,7 @@ public void testTwoTypes() throws Throwable { public void testTooManyTypes() throws Throwable { // if this changes, test must be rewritten assertEquals(5, DefBootstrap.PIC.MAX_DEPTH); - CallSite site = DefBootstrap.bootstrap(definition, + CallSite site = DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "toString", MethodType.methodType(String.class, Object.class), @@ -106,7 +107,7 @@ public void testTooManyTypes() throws Throwable { /** test that we revert to the megamorphic classvalue cache and that it works as expected */ public void testMegamorphic() throws Throwable { - DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(definition, + DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "size", MethodType.methodType(int.class, Object.class), @@ -138,7 +139,7 @@ public void testMegamorphic() throws Throwable { // test operators with null guards public void testNullGuardAdd() throws Throwable { - DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition, + DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, Object.class, Object.class), @@ -150,7 +151,7 @@ public void testNullGuardAdd() throws Throwable { } public void testNullGuardAddWhenCached() throws Throwable { - DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition, + DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, Object.class, Object.class), @@ -163,7 +164,7 @@ public void testNullGuardAddWhenCached() throws Throwable { } public void testNullGuardEq() throws Throwable { - DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition, + DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "eq", MethodType.methodType(boolean.class, Object.class, Object.class), @@ -176,7 +177,7 @@ public void testNullGuardEq() throws Throwable { } public void testNullGuardEqWhenCached() throws Throwable { - DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition, + DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "eq", MethodType.methodType(boolean.class, Object.class, Object.class), @@ -194,7 +195,7 @@ public void testNullGuardEqWhenCached() throws Throwable { // and can be disabled in some circumstances. public void testNoNullGuardAdd() throws Throwable { - DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition, + DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, int.class, Object.class), @@ -208,7 +209,7 @@ public void testNoNullGuardAdd() throws Throwable { } public void testNoNullGuardAddWhenCached() throws Throwable { - DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition, + DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(painlessLookup, MethodHandles.publicLookup(), "add", MethodType.methodType(Object.class, int.class, Object.class), diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java index 309b6be97f20b..5177d64cbdb06 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/PainlessDocGenerator.java @@ -23,9 +23,10 @@ import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.logging.ESLoggerFactory; -import org.elasticsearch.painless.Definition.Field; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessField; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessClass; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Modifier; @@ -44,15 +45,15 @@ import static org.elasticsearch.painless.spi.Whitelist.BASE_WHITELISTS; /** - * Generates an API reference from the method and type whitelists in {@link Definition}. + * Generates an API reference from the method and type whitelists in {@link PainlessLookup}. */ public class PainlessDocGenerator { - private static final Definition definition = new Definition(BASE_WHITELISTS); + private static final PainlessLookup PAINLESS_LOOKUP = new PainlessLookup(BASE_WHITELISTS); private static final Logger logger = ESLoggerFactory.getLogger(PainlessDocGenerator.class); - private static final Comparator FIELD_NAME = comparing(f -> f.name); - private static final Comparator METHOD_NAME = comparing(m -> m.name); - private static final Comparator NUMBER_OF_ARGS = comparing(m -> m.arguments.size()); + private static final Comparator FIELD_NAME = comparing(f -> f.name); + private static final Comparator METHOD_NAME = comparing(m -> m.name); + private static final Comparator NUMBER_OF_ARGS = comparing(m -> m.arguments.size()); public static void main(String[] args) throws IOException { Path apiRootPath = PathUtils.get(args[0]); @@ -67,8 +68,8 @@ public static void main(String[] args) throws IOException { Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), false, StandardCharsets.UTF_8.name())) { emitGeneratedWarning(indexStream); - List structs = definition.getStructs().stream().sorted(comparing(t -> t.name)).collect(toList()); - for (Struct struct : structs) { + List structs = PAINLESS_LOOKUP.getStructs().stream().sorted(comparing(t -> t.name)).collect(toList()); + for (PainlessClass struct : structs) { if (struct.clazz.isPrimitive()) { // Primitives don't have methods to reference continue; @@ -93,13 +94,13 @@ public static void main(String[] args) throws IOException { typeStream.print(struct.name); typeStream.println("++::"); - Consumer documentField = field -> PainlessDocGenerator.documentField(typeStream, field); - Consumer documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method); + Consumer documentField = field -> PainlessDocGenerator.documentField(typeStream, field); + Consumer documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method); struct.staticMembers.values().stream().sorted(FIELD_NAME).forEach(documentField); struct.members.values().stream().sorted(FIELD_NAME).forEach(documentField); struct.staticMethods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(documentMethod); struct.constructors.values().stream().sorted(NUMBER_OF_ARGS).forEach(documentMethod); - Map inherited = new TreeMap<>(); + Map inherited = new TreeMap<>(); struct.methods.values().stream().sorted(METHOD_NAME.thenComparing(NUMBER_OF_ARGS)).forEach(method -> { if (method.owner == struct) { documentMethod(typeStream, method); @@ -111,7 +112,7 @@ public static void main(String[] args) throws IOException { if (false == inherited.isEmpty()) { typeStream.print("* Inherits methods from "); boolean first = true; - for (Struct inheritsFrom : inherited.values()) { + for (PainlessClass inheritsFrom : inherited.values()) { if (first) { first = false; } else { @@ -129,7 +130,7 @@ public static void main(String[] args) throws IOException { logger.info("Done writing [index.asciidoc]"); } - private static void documentField(PrintStream stream, Field field) { + private static void documentField(PrintStream stream, PainlessField field) { stream.print("** [["); emitAnchor(stream, field); stream.print("]]"); @@ -159,7 +160,7 @@ private static void documentField(PrintStream stream, Field field) { /** * Document a method. */ - private static void documentMethod(PrintStream stream, Method method) { + private static void documentMethod(PrintStream stream, PainlessMethod method) { stream.print("* ++[["); emitAnchor(stream, method); stream.print("]]"); @@ -201,17 +202,17 @@ private static void documentMethod(PrintStream stream, Method method) { } /** - * Anchor text for a {@link Struct}. + * Anchor text for a {@link PainlessClass}. */ - private static void emitAnchor(PrintStream stream, Struct struct) { + private static void emitAnchor(PrintStream stream, PainlessClass struct) { stream.print("painless-api-reference-"); stream.print(struct.name.replace('.', '-')); } /** - * Anchor text for a {@link Method}. + * Anchor text for a {@link PainlessMethod}. */ - private static void emitAnchor(PrintStream stream, Method method) { + private static void emitAnchor(PrintStream stream, PainlessMethod method) { emitAnchor(stream, method.owner); stream.print('-'); stream.print(methodName(method)); @@ -220,15 +221,15 @@ private static void emitAnchor(PrintStream stream, Method method) { } /** - * Anchor text for a {@link Field}. + * Anchor text for a {@link PainlessField}. */ - private static void emitAnchor(PrintStream stream, Field field) { + private static void emitAnchor(PrintStream stream, PainlessField field) { emitAnchor(stream, field.owner); stream.print('-'); stream.print(field.name); } - private static String methodName(Method method) { + private static String methodName(PainlessMethod method) { return method.name.equals("") ? method.owner.name : method.name; } @@ -237,17 +238,17 @@ private static String methodName(Method method) { an internal link with the text. */ private static void emitType(PrintStream stream, Class clazz) { - emitStruct(stream, definition.getPainlessStructFromJavaClass(clazz)); + emitStruct(stream, PAINLESS_LOOKUP.getPainlessStructFromJavaClass(clazz)); while ((clazz = clazz.getComponentType()) != null) { stream.print("[]"); } } /** - * Emit a {@link Struct}. If the {@linkplain Struct} is primitive or def this just emits the name of the struct. Otherwise this emits - * an internal link with the name. + * Emit a {@link PainlessClass}. If the {@linkplain PainlessClass} is primitive or def this just emits the name of the struct. + * Otherwise this emits an internal link with the name. */ - private static void emitStruct(PrintStream stream, Struct struct) { + private static void emitStruct(PrintStream stream, PainlessClass struct) { if (false == struct.clazz.isPrimitive() && false == struct.name.equals("def")) { stream.print("<<"); emitAnchor(stream, struct); @@ -260,11 +261,11 @@ private static void emitStruct(PrintStream stream, Struct struct) { } /** - * Emit an external link to Javadoc for a {@link Method}. + * Emit an external link to Javadoc for a {@link PainlessMethod}. * * @param root name of the root uri variable */ - private static void emitJavadocLink(PrintStream stream, String root, Method method) { + private static void emitJavadocLink(PrintStream stream, String root, PainlessMethod method) { stream.print("link:{"); stream.print(root); stream.print("-javadoc}/"); @@ -292,11 +293,11 @@ private static void emitJavadocLink(PrintStream stream, String root, Method meth } /** - * Emit an external link to Javadoc for a {@link Field}. + * Emit an external link to Javadoc for a {@link PainlessField}. * * @param root name of the root uri variable */ - private static void emitJavadocLink(PrintStream stream, String root, Field field) { + private static void emitJavadocLink(PrintStream stream, String root, PainlessField field) { stream.print("link:{"); stream.print(root); stream.print("-javadoc}/"); @@ -306,9 +307,9 @@ private static void emitJavadocLink(PrintStream stream, String root, Field field } /** - * Pick the javadoc root for a {@link Method}. + * Pick the javadoc root for a {@link PainlessMethod}. */ - private static String javadocRoot(Method method) { + private static String javadocRoot(PainlessMethod method) { if (method.augmentation != null) { return "painless"; } @@ -316,16 +317,16 @@ private static String javadocRoot(Method method) { } /** - * Pick the javadoc root for a {@link Field}. + * Pick the javadoc root for a {@link PainlessField}. */ - private static String javadocRoot(Field field) { + private static String javadocRoot(PainlessField field) { return javadocRoot(field.owner); } /** - * Pick the javadoc root for a {@link Struct}. + * Pick the javadoc root for a {@link PainlessClass}. */ - private static String javadocRoot(Struct struct) { + private static String javadocRoot(PainlessClass struct) { String classPackage = struct.clazz.getPackage().getName(); if (classPackage.startsWith("java")) { return "java8"; diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java index ea1d2275b3e8d..1a4770e560a7e 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.lucene.ScorerAware; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.painless.antlr.Walker; +import org.elasticsearch.painless.lookup.PainlessLookup; import org.elasticsearch.painless.spi.Whitelist; import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ScriptContext; @@ -90,12 +91,12 @@ public Object exec(String script, Map vars, boolean picky) { public Object exec(String script, Map vars, Map compileParams, Scorer scorer, boolean picky) { // test for ambiguity errors before running the actual script if picky is true if (picky) { - Definition definition = new Definition(Whitelist.BASE_WHITELISTS); - ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, GenericElasticsearchScript.class); + PainlessLookup painlessLookup = new PainlessLookup(Whitelist.BASE_WHITELISTS); + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, GenericElasticsearchScript.class); CompilerSettings pickySettings = new CompilerSettings(); pickySettings.setPicky(true); pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(scriptEngineSettings())); - Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), getTestName(), script, pickySettings, definition, null); + Walker.buildPainlessTree(scriptClassInfo, new MainMethodReserved(), getTestName(), script, pickySettings, painlessLookup, null); } // test actual script execution ExecutableScript.Factory factory = scriptEngine.compile(null, script, ExecutableScript.CONTEXT, compileParams); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java index fd8190aa2c2eb..3e9f724743faa 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java @@ -20,12 +20,12 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.CompilerSettings; -import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Cast; -import org.elasticsearch.painless.Definition.Field; -import org.elasticsearch.painless.Definition.Method; -import org.elasticsearch.painless.Definition.MethodKey; -import org.elasticsearch.painless.Definition.Struct; +import org.elasticsearch.painless.lookup.PainlessLookup; +import org.elasticsearch.painless.lookup.PainlessCast; +import org.elasticsearch.painless.lookup.PainlessField; +import org.elasticsearch.painless.lookup.PainlessMethod; +import org.elasticsearch.painless.lookup.PainlessMethodKey; +import org.elasticsearch.painless.lookup.PainlessClass; import org.elasticsearch.painless.FeatureTest; import org.elasticsearch.painless.GenericElasticsearchScript; import org.elasticsearch.painless.Locals.Variable; @@ -48,7 +48,7 @@ * Tests {@link Object#toString} implementations on all extensions of {@link ANode}. */ public class NodeToStringTests extends ESTestCase { - private final Definition definition = new Definition(Whitelist.BASE_WHITELISTS); + private final PainlessLookup painlessLookup = new PainlessLookup(Whitelist.BASE_WHITELISTS); public void testEAssignment() { assertToString( @@ -161,12 +161,12 @@ public void testECapturingFunctionRef() { public void testECast() { Location l = new Location(getTestName(), 0); AExpression child = new EConstant(l, "test"); - Cast cast = Cast.standard(String.class, Integer.class, true); + PainlessCast cast = PainlessCast.standard(String.class, Integer.class, true); assertEquals("(ECast java.lang.Integer (EConstant String 'test'))", new ECast(l, child, cast).toString()); l = new Location(getTestName(), 1); child = new EBinary(l, Operation.ADD, new EConstant(l, "test"), new EConstant(l, 12)); - cast = Cast.standard(Integer.class, Boolean.class, true); + cast = PainlessCast.standard(Integer.class, Boolean.class, true); assertEquals("(ECast java.lang.Boolean (EBinary (EConstant String 'test') + (EConstant Integer 12)))", new ECast(l, child, cast).toString()); } @@ -403,15 +403,15 @@ public void testPSubBrace() { public void testPSubCallInvoke() { Location l = new Location(getTestName(), 0); - Struct c = definition.getPainlessStructFromJavaClass(Integer.class); - Method m = c.methods.get(new MethodKey("toString", 0)); + PainlessClass c = painlessLookup.getPainlessStructFromJavaClass(Integer.class); + PainlessMethod m = c.methods.get(new PainlessMethodKey("toString", 0)); PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList()); node.prefix = new EVariable(l, "a"); assertEquals("(PSubCallInvoke (EVariable a) toString)", node.toString()); assertEquals("(PSubNullSafeCallInvoke (PSubCallInvoke (EVariable a) toString))", new PSubNullSafeCallInvoke(l, node).toString()); l = new Location(getTestName(), 1); - m = c.methods.get(new MethodKey("equals", 1)); + m = c.methods.get(new PainlessMethodKey("equals", 1)); node = new PSubCallInvoke(l, m, null, singletonList(new EVariable(l, "b"))); node.prefix = new EVariable(l, "a"); assertEquals("(PSubCallInvoke (EVariable a) equals (Args (EVariable b)))", node.toString()); @@ -458,8 +458,8 @@ public void testPSubDefField() { public void testPSubField() { Location l = new Location(getTestName(), 0); - Struct s = definition.getPainlessStructFromJavaClass(Boolean.class); - Field f = s.staticMembers.get("TRUE"); + PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(Boolean.class); + PainlessField f = s.staticMembers.get("TRUE"); PSubField node = new PSubField(l, f); node.prefix = new EStatic(l, "Boolean"); assertEquals("(PSubField (EStatic Boolean) TRUE)", node.toString()); @@ -468,7 +468,7 @@ public void testPSubField() { public void testPSubListShortcut() { Location l = new Location(getTestName(), 0); - Struct s = definition.getPainlessStructFromJavaClass(List.class); + PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(List.class); PSubListShortcut node = new PSubListShortcut(l, s, new EConstant(l, 1)); node.prefix = new EVariable(l, "a"); assertEquals("(PSubListShortcut (EVariable a) (EConstant Integer 1))", node.toString()); @@ -476,7 +476,7 @@ public void testPSubListShortcut() { new PSubNullSafeCallInvoke(l, node).toString()); l = new Location(getTestName(), 0); - s = definition.getPainlessStructFromJavaClass(List.class); + s = painlessLookup.getPainlessStructFromJavaClass(List.class); node = new PSubListShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4))); node.prefix = new EVariable(l, "a"); assertEquals("(PSubListShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString()); @@ -484,7 +484,7 @@ public void testPSubListShortcut() { public void testPSubMapShortcut() { Location l = new Location(getTestName(), 0); - Struct s = definition.getPainlessStructFromJavaClass(Map.class); + PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(Map.class); PSubMapShortcut node = new PSubMapShortcut(l, s, new EConstant(l, "cat")); node.prefix = new EVariable(l, "a"); assertEquals("(PSubMapShortcut (EVariable a) (EConstant String 'cat'))", node.toString()); @@ -492,7 +492,7 @@ public void testPSubMapShortcut() { new PSubNullSafeCallInvoke(l, node).toString()); l = new Location(getTestName(), 1); - s = definition.getPainlessStructFromJavaClass(Map.class); + s = painlessLookup.getPainlessStructFromJavaClass(Map.class); node = new PSubMapShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4))); node.prefix = new EVariable(l, "a"); assertEquals("(PSubMapShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString()); @@ -500,9 +500,9 @@ public void testPSubMapShortcut() { public void testPSubShortcut() { Location l = new Location(getTestName(), 0); - Struct s = definition.getPainlessStructFromJavaClass(FeatureTest.class); - Method getter = s.methods.get(new MethodKey("getX", 0)); - Method setter = s.methods.get(new MethodKey("setX", 1)); + PainlessClass s = painlessLookup.getPainlessStructFromJavaClass(FeatureTest.class); + PainlessMethod getter = s.methods.get(new PainlessMethodKey("getX", 0)); + PainlessMethod setter = s.methods.get(new PainlessMethodKey("setX", 1)); PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter); node.prefix = new EVariable(l, "a"); assertEquals("(PSubShortcut (EVariable a) x)", node.toString()); @@ -900,12 +900,12 @@ private void assertToString(String expected, String code) { } private SSource walk(String code) { - ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, GenericElasticsearchScript.class); + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(painlessLookup, GenericElasticsearchScript.class); CompilerSettings compilerSettings = new CompilerSettings(); compilerSettings.setRegexesEnabled(true); try { return Walker.buildPainlessTree( - scriptClassInfo, new MainMethodReserved(), getTestName(), code, compilerSettings, definition, null); + scriptClassInfo, new MainMethodReserved(), getTestName(), code, compilerSettings, painlessLookup, null); } catch (Exception e) { throw new AssertionError("Failed to compile: " + code, e); }