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); }