Painless: Restructure Definition/Whitelist (#31879)

Create lookup package
rename Definition to PainlessLookup and move to lookup package
rename Definition.Method to PainlessMethod
rename Definition.MethodKey to PainlessMethod
rename Definition.Field to PainlessField
rename Definition.Struct to PainlessClass
rename Definition.Cast to PainlessCast
rename Whitelist.Struct to WhitelistClass
rename Whitelist.Constructor to WhitelistConstructor
rename Whitelist.Method to WhitelistMethod
rename Whitelist.Field to WhitelistField
This commit is contained in:
Jack Conradson 2018-07-08 12:00:23 -07:00 committed by GitHub
parent fb27f3e7f0
commit d9a92011bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1510 additions and 1266 deletions

View File

@ -24,13 +24,14 @@ import java.util.List;
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<Whitelist> 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<Constructor> whitelistConstructors;
/** The {@link List} of white-listed ({@link Method}s) available to this struct. */
public final List<Method> whitelistMethods;
/** The {@link List} of white-listed ({@link Field}s) available to this struct. */
public final List<Field> whitelistFields;
/** Standard constructor. All values must be not {@code null}. */
public Struct(String origin, String javaClassName, boolean onlyFQNJavaClassName,
List<Constructor> whitelistConstructors, List<Method> whitelistMethods, List<Field> 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<String> painlessParameterTypeNames;
/** Standard constructor. All values must be not {@code null}. */
public Constructor(String origin, List<String> 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<String> 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<String> 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<Struct> whitelistStructs;
/** The {@link List} of all the whitelisted Painless classes. */
public final List<WhitelistClass> whitelistStructs;
/** Standard constructor. All values must be not {@code null}. */
public Whitelist(ClassLoader javaClassLoader, List<Struct> whitelistStructs) {
public Whitelist(ClassLoader javaClassLoader, List<WhitelistClass> whitelistStructs) {
this.javaClassLoader = Objects.requireNonNull(javaClassLoader);
this.whitelistStructs = Collections.unmodifiableList(Objects.requireNonNull(whitelistStructs));
}

View File

@ -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<WhitelistConstructor> whitelistConstructors;
/** The {@link List} of whitelisted ({@link WhitelistMethod}s) available to this class. */
public final List<WhitelistMethod> whitelistMethods;
/** The {@link List} of whitelisted ({@link WhitelistField}s) available to this class. */
public final List<WhitelistField> whitelistFields;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistClass(String origin, String javaClassName, boolean onlyFQNJavaClassName,
List<WhitelistConstructor> whitelistConstructors,
List<WhitelistMethod> whitelistMethods,
List<WhitelistField> 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));
}
}

View File

@ -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<String> painlessParameterTypeNames;
/** Standard constructor. All values must be not {@code null}. */
public WhitelistConstructor(String origin, List<String> painlessParameterTypeNames) {
this.origin = Objects.requireNonNull(origin);
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
}
}

View File

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

View File

@ -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:
* <ul>
* <li> def - The Painless dynamic type which is automatically included without a need to be
* white-listed. </li>
* <li> fully-qualified Java type name - Any white-listed Java class will have the equivalent name as
* whitelisted. </li>
* <li> 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. </li>
* <li> 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. </li>
* </ul>
*
* The following can be parsed from each white-list text file:
* The following can be parsed from each whitelist text file:
* <ul>
* <li> Blank lines will be ignored by the parser. </li>
* <li> Comments may be created starting with a pound '#' symbol and end with a newline. These will
@ -71,19 +71,19 @@ public final class WhitelistLoader {
* <ul>
* <li> 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. </li>
* <li> 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. </li>
* (these must be whitelisted as well), a closing parenthesis, and a newline. </li>
* <li> 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. </li>
* <li>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<Whitelist.Struct> whitelistStructs = new ArrayList<>();
List<WhitelistClass> 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 final class WhitelistLoader {
String whitelistStructOrigin = null;
String javaClassName = null;
boolean onlyFQNJavaClassName = false;
List<Whitelist.Constructor> whitelistConstructors = null;
List<Whitelist.Method> whitelistMethods = null;
List<Whitelist.Field> whitelistFields = null;
List<WhitelistConstructor> whitelistConstructors = null;
List<WhitelistMethod> whitelistMethods = null;
List<WhitelistField> whitelistFields = null;
while ((line = reader.readLine()) != null) {
number = reader.getLineNumber();
@ -159,13 +159,13 @@ public final class WhitelistLoader {
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 final class WhitelistLoader {
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 final class WhitelistLoader {
// 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 final class WhitelistLoader {
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 final class WhitelistLoader {
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 final class WhitelistLoader {
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);

View File

@ -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<String> 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<String> painlessParameterTypeNames) {
this.origin = Objects.requireNonNull(origin);
this.javaAugmentedClassName = javaAugmentedClassName;
this.javaMethodName = javaMethodName;
this.painlessReturnTypeName = Objects.requireNonNull(painlessReturnTypeName);
this.painlessParameterTypeNames = Collections.unmodifiableList(Objects.requireNonNull(painlessParameterTypeNames));
}
}

View File

@ -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 @@ import java.util.Objects;
*/
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 final class AnalyzerCaster {
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 final class AnalyzerCaster {
(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;

View File

@ -21,6 +21,7 @@ package org.elasticsearch.painless;
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 @@ final class Compiler {
/**
* 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 @@ final class Compiler {
/**
* 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 @@ final class Compiler {
" 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 @@ final class Compiler {
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 @@ final class Compiler {
" 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();

View File

@ -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<Class<?>,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 @@ public final class Def {
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 @@ public final class Def {
// 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 @@ public final class Def {
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
* Otherwise it returns the matching method.
* <p>
* @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 @@ public final class Def {
* 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.
* <p>
* @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 @@ public final class Def {
* @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 @@ public final class Def {
// 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 @@ public final class Def {
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 @@ public final class Def {
// 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 @@ public final class Def {
* 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 @@ public final class Def {
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 final class Def {
* 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.
* <p>
* @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 @@ public final class Def {
}
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 @@ public final class Def {
* 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.
* <p>
* @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 @@ public final class Def {
}
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 @@ public final class Def {
*/
@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<Class<?>,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 final class Def {
*/
@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<Class<?>,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);

View File

@ -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 @@ public final class DefBootstrap {
/** 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 @@ public final class DefBootstrap {
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 @@ public final class DefBootstrap {
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 @@ public final class DefBootstrap {
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 @@ public final class DefBootstrap {
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 @@ public final class DefBootstrap {
* <li>{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
* <li>{@code args}: flavor-specific args.
* </ul>
* 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.
* <p>
* 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 final class DefBootstrap {
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 final class DefBootstrap {
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 final class DefBootstrap {
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:

View File

@ -21,7 +21,6 @@ package org.elasticsearch.painless;
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 @@ public class DefMath {
}
}
private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
private static final MethodHandles.Lookup PRIVATE_METHOD_HANDLES_LOOKUP = MethodHandles.lookup();
private static final Map<Class<?>,Map<String,MethodHandle>> TYPE_OP_MAPPING = Collections.unmodifiableMap(
Stream.of(boolean.class, int.class, long.class, float.class, double.class, Object.class)
@ -1081,26 +1080,26 @@ public class DefMath {
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 @@ public class DefMath {
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);
}

View File

@ -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 class FunctionRef {
* @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 class FunctionRef {
* 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 class FunctionRef {
/**
* 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("<init>", method.arguments.size()));
impl = struct.constructors.get(new PainlessMethodKey("<init>", 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 @@ public class FunctionRef {
// 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;
}

View File

@ -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 final class Locals {
*/
public static Locals newLambdaScope(Locals programScope, Class<?> returnType, List<Parameter> 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 final class Locals {
/** Creates a new function scope inside the current scope */
public static Locals newFunctionScope(Locals programScope, Class<?> returnType, List<Parameter> 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 final class Locals {
/** 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 final class Locals {
}
/** Creates a new program scope: the list of methods. It is the parent for all methods */
public static Locals newProgramScope(Definition definition, Collection<Method> methods) {
Locals locals = new Locals(null, definition, null, null);
for (Method method : methods) {
public static Locals newProgramScope(PainlessLookup painlessLookup, Collection<PainlessMethod> methods) {
Locals locals = new Locals(null, painlessLookup, null, null);
for (PainlessMethod method : methods) {
locals.addMethod(method);
}
return locals;
@ -142,8 +143,8 @@ public final class Locals {
}
/** 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 final class Locals {
}
/** 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 final class Locals {
// variable name -> variable
private Map<String,Variable> variables;
// method name+arity -> methods
private Map<MethodKey,Method> methods;
private Map<PainlessMethodKey,PainlessMethod> 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<String> keywords) {
private Locals(Locals parent, PainlessLookup painlessLookup, Class<?> returnType, Set<String> keywords) {
this.parent = parent;
this.definition = definition;
this.painlessLookup = painlessLookup;
this.returnType = returnType;
this.keywords = keywords;
if (parent == null) {
@ -236,7 +237,7 @@ public final class Locals {
}
/** 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 @@ public final class Locals {
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 final class Locals {
@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) {

View File

@ -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 final class MethodWriter extends GeneratorAdapter {
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);

View File

@ -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 @@ public class PainlessExplainError extends Error {
/**
* Headers to be added to the {@link ScriptException} for structured rendering.
*/
public Map<String, List<String>> getHeaders(Definition definition) {
public Map<String, List<String>> getHeaders(PainlessLookup painlessLookup) {
Map<String, List<String>> headers = new TreeMap<>();
String toString = "null";
String javaClassName = null;
@ -54,7 +56,7 @@ public class PainlessExplainError extends Error {
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;
}

View File

@ -24,6 +24,7 @@ import org.elasticsearch.SpecialPermission;
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 final class PainlessScriptEngine extends AbstractComponent implements Scr
for (Map.Entry<ScriptContext<?>, List<Whitelist>> 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())));
}
}

View File

@ -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<org.objectweb.asm.commons.Method> getMethods;
private final List<Class<?>> 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 class ScriptClassInfo {
}
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 class ScriptClassInfo {
}
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 class ScriptClassInfo {
+ 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 class ScriptClassInfo {
}
}
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<Class<?>, String> unknownErrorMessageSource) {
type = Definition.ObjectClassTodefClass(type);
private static Class<?> definitionTypeForClass(PainlessLookup painlessLookup, Class<?> type,
Function<Class<?>, 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));
}

View File

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

View File

@ -23,7 +23,7 @@ import org.antlr.v4.runtime.CharStream;
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 @@ import org.elasticsearch.painless.Location;
*/
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 @@ final class EnhancedPainlessLexer extends PainlessLexer {
@Override
protected boolean isType(String name) {
return definition.isSimplePainlessType(name);
return painlessLookup.isSimplePainlessType(name);
}
@Override

View File

@ -29,7 +29,7 @@ import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.elasticsearch.painless.CompilerSettings;
import org.elasticsearch.painless.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 @@ import java.util.List;
public final class Walker extends PainlessParserBaseVisitor<ANode> {
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 final class Walker extends PainlessParserBaseVisitor<ANode> {
private final Printer debugStream;
private final String sourceName;
private final String sourceText;
private final Definition definition;
private final PainlessLookup painlessLookup;
private final Deque<Reserved> 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 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
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);

View File

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

View File

@ -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<PainlessMethodKey, PainlessMethod> constructors;
public final Map<PainlessMethodKey, PainlessMethod> staticMethods;
public final Map<PainlessMethodKey, PainlessMethod> methods;
public final Map<String, PainlessField> staticMembers;
public final Map<String, PainlessField> members;
public final Map<String, MethodHandle> getters;
public final Map<String, MethodHandle> 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();
}
}

View File

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

View File

@ -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.Collections;
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 @@ import java.util.regex.Pattern;
* 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<String, Method> methodCache = new HashMap<>();
private static final Map<String, Field> fieldCache = new HashMap<>();
private static final Map<String, PainlessMethod> methodCache = new HashMap<>();
private static final Map<String, PainlessField> fieldCache = new HashMap<>();
private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("^[_a-zA-Z][._a-zA-Z0-9]*$");
@ -56,306 +57,6 @@ public final class Definition {
}
}
public static class Method {
public final String name;
public final Struct owner;
public final Class<?> augmentation;
public final Class<?> rtn;
public final List<Class<?>> 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<Class<?>> 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.
* <p>
* 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 ("<init>".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.
* <p>
* 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)}.
* <p>
* 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<MethodKey, Method> constructors;
public final Map<MethodKey, Method> staticMethods;
public final Map<MethodKey, Method> methods;
public final Map<String, Field> staticMembers;
public final Map<String, Field> members;
public final Map<String, MethodHandle> getters;
public final Map<String, MethodHandle> 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 @@ public final class Definition {
return structName + fieldName + typeName;
}
public Collection<Struct> getStructs() {
public Collection<PainlessClass> getStructs() {
return javaClassesToPainlessStructs.values();
}
private final Map<String, Class<?>> painlessTypesToJavaClasses;
private final Map<Class<?>, Struct> javaClassesToPainlessStructs;
private final Map<Class<?>, PainlessClass> javaClassesToPainlessStructs;
public Definition(List<Whitelist> whitelists) {
public PainlessLookup(List<Whitelist> 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 final class Definition {
// 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 final class Definition {
// 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<String> painlessSuperStructs = new ArrayList<>();
Class<?> javaSuperClass = painlessStruct.clazz.getSuperclass();
@ -598,7 +299,7 @@ public final class Definition {
// 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 final class Definition {
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 final class Definition {
// 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 final class Definition {
}
// 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<Class<?>,Struct> entry : javaClassesToPainlessStructs.entrySet()) {
for (Map.Entry<Class<?>,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 @@ public final class Definition {
}
}
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 @@ public final class Definition {
}
}
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 @@ public final class Definition {
" with constructor parameters " + whitelistConstructor.painlessParameterTypeNames, exception);
}
MethodKey painlessMethodKey = new MethodKey("<init>", whitelistConstructor.painlessParameterTypeNames.size());
Method painlessConstructor = ownerStruct.constructors.get(painlessMethodKey);
PainlessMethodKey painlessMethodKey = new PainlessMethodKey("<init>", 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 @@ public final class Definition {
}
painlessConstructor = methodCache.computeIfAbsent(buildMethodCacheKey(ownerStruct.name, "<init>", painlessParametersTypes),
key -> new Method("<init>", ownerStruct, null, void.class, painlessParametersTypes,
key -> new PainlessMethod("<init>", 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 @@ public final class Definition {
}
}
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 @@ public final class Definition {
"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 @@ public final class Definition {
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 @@ public final class Definition {
"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 @@ public final class Definition {
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 @@ public final class Definition {
}
}
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 @@ public final class Definition {
"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 @@ public final class Definition {
" 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 @@ public final class Definition {
}
private void copyStruct(String struct, List<String> 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 @@ public final class Definition {
" is not a super type of owner struct [" + owner.name + "] in copy.");
}
for (Map.Entry<MethodKey,Method> kvPair : child.methods.entrySet()) {
MethodKey methodKey = kvPair.getKey();
Method method = kvPair.getValue();
for (Map.Entry<PainlessMethodKey,PainlessMethod> 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 @@ public final class Definition {
}
}
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 @@ public final class Definition {
/**
* 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<MethodKey, Method> method : struct.methods.entrySet()) {
for (Map.Entry<PainlessMethodKey, PainlessMethod> 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 @@ public final class Definition {
}
// add all members
for (Map.Entry<String, Field> member : struct.members.entrySet()) {
for (Map.Entry<String, PainlessField> 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 @@ public final class Definition {
}
// 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 final class Definition {
return painlessTypesToJavaClasses.containsKey(painlessType);
}
public Struct getPainlessStructFromJavaClass(Class<?> clazz) {
public PainlessClass getPainlessStructFromJavaClass(Class<?> clazz) {
return javaClassesToPainlessStructs.get(clazz);
}

View File

@ -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<Class<?>> 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<Class<?>> 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.
* <p>
* 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 ("<init>".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);
}
}
}

View File

@ -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.
* <p>
* 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)}.
* <p>
* 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();
}
}

View File

@ -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 @@ public abstract class AExpression extends ANode {
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

View File

@ -22,8 +22,8 @@ package org.elasticsearch.painless.node;
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);

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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 @@ public final class EBinary extends AExpression {
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;

View File

@ -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<AExpression> arguments;
private Method method = null;
private PainlessMethod method = null;
public ECallLocal(Location location, String name, List<AExpression> arguments) {
super(location);
@ -58,7 +58,7 @@ public final class ECallLocal extends AExpression {
@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) {

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
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 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
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 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
// 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 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
// 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());

View File

@ -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 @@ import java.util.Set;
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 @@ final class ECast extends AExpression {
@Override
public String toString() {
return singleLineToString(Definition.ClassToName(cast.to), child);
return singleLineToString(PainlessLookup.ClassToName(cast.to), child);
}
}

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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 @@ public final class EComp extends AExpression {
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) {

View File

@ -50,7 +50,7 @@ public final class EExplicit extends AExpression {
@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 + "]."));
}

View File

@ -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 @@ public final class EFunctionRef extends AExpression implements ILambda {
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 @@ public final class EFunctionRef extends AExpression implements ILambda {
}
} else {
// whitelist lookup
ref = new FunctionRef(locals.getDefinition(), expected, type, call, 0);
ref = new FunctionRef(locals.getPainlessLookup(), expected, type, call, 0);
}
} catch (IllegalArgumentException e) {

View File

@ -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 @@ public final class EInstanceof extends AExpression {
// 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 @@ public final class EInstanceof extends AExpression {
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;
}

View File

@ -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 @@ public final class ELambda extends AExpression implements ILambda {
void analyze(Locals locals) {
Class<?> returnType;
List<String> 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 @@ public final class ELambda extends AExpression implements ILambda {
}
} 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 @@ public final class ELambda extends AExpression implements ILambda {
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 @@ public final class ELambda extends AExpression implements ILambda {
List<String> paramTypes = new ArrayList<>(captures.size() + actualParamTypeStrs.size());
List<String> 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()));

View File

@ -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 @@ import java.util.Set;
public final class EListInit extends AExpression {
private final List<AExpression> values;
private Method constructor = null;
private Method method = null;
private PainlessMethod constructor = null;
private PainlessMethod method = null;
public EListInit(Location location, List<AExpression> values) {
super(location);
@ -61,13 +61,14 @@ public final class EListInit extends AExpression {
actual = ArrayList.class;
constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
constructor =
locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("<init>", 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."));

View File

@ -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<AExpression> keys;
private final List<AExpression> values;
private Method constructor = null;
private Method method = null;
private PainlessMethod constructor = null;
private PainlessMethod method = null;
public EMapInit(Location location, List<AExpression> keys, List<AExpression> values) {
super(location);
@ -67,13 +67,14 @@ public final class EMapInit extends AExpression {
actual = HashMap.class;
constructor = locals.getDefinition().getPainlessStructFromJavaClass(actual).constructors.get(new MethodKey("<init>", 0));
constructor =
locals.getPainlessLookup().getPainlessStructFromJavaClass(actual).constructors.get(new PainlessMethodKey("<init>", 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."));

View File

@ -61,7 +61,7 @@ public final class ENewArray extends AExpression {
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 + "]."));
}

View File

@ -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<AExpression> arguments;
private Method constructor;
private PainlessMethod constructor;
public ENewObj(Location location, String type, List<AExpression> arguments) {
super(location);
@ -58,13 +58,13 @@ public final class ENewObj extends AExpression {
@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("<init>", arguments.size()));
PainlessClass struct = locals.getPainlessLookup().getPainlessStructFromJavaClass(actual);
constructor = struct.constructors.get(new PainlessMethodKey("<init>", arguments.size()));
if (constructor != null) {
Class<?>[] types = new Class<?>[constructor.arguments.size()];

View File

@ -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 @@ public final class ENull extends AExpression {
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;

View File

@ -48,7 +48,7 @@ public final class EStatic extends AExpression {
@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 + "]."));
}

View File

@ -21,8 +21,8 @@ package org.elasticsearch.painless.node;
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 @@ public final class EUnary extends AExpression {
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 @@ public final class EUnary extends AExpression {
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 @@ public final class EUnary extends AExpression {
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;

View File

@ -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 @@ public final class PBrace extends AStoreable {
} 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;

View File

@ -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 @@ public final class PCallInvoke extends AExpression {
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);

View File

@ -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 @@ public final class PField extends AStoreable {
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 @@ public final class PField extends AStoreable {
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) {

View File

@ -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 @@ import java.util.Set;
*/
final class PSubCallInvoke extends AExpression {
private final Method method;
private final PainlessMethod method;
private final Class<?> box;
private final List<AExpression> arguments;
PSubCallInvoke(Location location, Method method, Class<?> box, List<AExpression> arguments) {
PSubCallInvoke(Location location, PainlessMethod method, Class<?> box, List<AExpression> arguments) {
super(location);
this.method = Objects.requireNonNull(method);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 @@ import java.util.Set;
*/
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 @@ final class PSubField extends AStoreable {
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;

View File

@ -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 @@ import java.util.Set;
*/
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 @@ final class PSubListShortcut extends AStoreable {
@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)) {

View File

@ -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 @@ import java.util.Set;
*/
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 @@ final class PSubMapShortcut extends AStoreable {
@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 + "]."));

View File

@ -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;

View File

@ -67,7 +67,7 @@ public final class SCatch extends AStatement {
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 + "]."));
}

View File

@ -62,7 +62,7 @@ public final class SDeclaration extends AStatement {
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 + "]."));
}

View File

@ -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 @@ public class SEach extends AStatement {
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 @@ public class SEach extends AStatement {
} 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);

View File

@ -22,8 +22,8 @@ package org.elasticsearch.painless.node;
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 final class SFunction extends AStatement {
Class<?> rtnType = null;
List<Parameter> parameters = new ArrayList<>();
Method method = null;
PainlessMethod method = null;
private Variable loop = null;
@ -117,9 +117,9 @@ public final class SFunction extends AStatement {
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 @@ public final class SFunction extends AStatement {
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 @@ public final class SFunction extends AStatement {
}
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

View File

@ -21,9 +21,9 @@ package org.elasticsearch.painless.node;
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 @@ public final class SSource extends AStatement {
throw new IllegalStateException("Illegal tree structure.");
}
public void analyze(Definition definition) {
Map<MethodKey, Method> methods = new HashMap<>();
public void analyze(PainlessLookup painlessLookup) {
Map<PainlessMethodKey, PainlessMethod> 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

View File

@ -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 @@ final class SSubEachArray extends AStatement {
@Override
public String toString() {
return singleLineToString(Definition.ClassToName(variable.clazz), variable.name, expression, block);
return singleLineToString(PainlessLookup.ClassToName(variable.clazz), variable.name, expression, block);
}
}

View File

@ -21,11 +21,11 @@ package org.elasticsearch.painless.node;
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 @@ final class SSubEachIterable extends AStatement {
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 @@ final class SSubEachIterable extends AStatement {
@Override
public String toString() {
return singleLineToString(Definition.ClassToName(variable.clazz), variable.name, expression, block);
return singleLineToString(PainlessLookup.ClassToName(variable.clazz), variable.name, expression, block);
}
}

View File

@ -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 @@ public class AnalyzerCasterTests extends ESTestCase {
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);

View File

@ -23,6 +23,7 @@ import java.util.Collections;
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 @@ import static org.hamcrest.Matchers.startsWith;
*/
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 class BaseClassTests extends ScriptTestCase {
}
public void testGets() {
Compiler compiler = new Compiler(Gets.class, definition);
Compiler compiler = new Compiler(Gets.class, painlessLookup);
Map<String, Object> map = new HashMap<>();
map.put("s", 1);
@ -85,7 +86,7 @@ public class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
}
}
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 class BaseClassTests extends ScriptTestCase {
public abstract void execute(Map<String, Object> map);
}
public void testReturnsVoid() {
Compiler compiler = new Compiler(ReturnsVoid.class, definition);
Compiler compiler = new Compiler(ReturnsVoid.class, painlessLookup);
Map<String, Object> map = new HashMap<>();
((ReturnsVoid)scriptEngine.compile(compiler, null, "map.a = 'foo'", emptyMap())).execute(map);
assertEquals(singletonMap("a", "foo"), map);
@ -245,7 +246,7 @@ public class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 class BaseClassTests extends ScriptTestCase {
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 ["

View File

@ -22,6 +22,7 @@ package org.elasticsearch.painless;
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.hasKey;
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 class DebugTests extends ScriptTestCase {
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(

View File

@ -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 @@ final class Debugger {
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("<debugging>", source, settings, textifier);
} catch (RuntimeException e) {
textifier.print(outputWriter);

View File

@ -27,15 +27,16 @@ import java.util.Arrays;
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 class DefBootstrapTests extends ESTestCase {
}
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 class DefBootstrapTests extends ESTestCase {
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 class DefBootstrapTests extends ESTestCase {
/** 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 class DefBootstrapTests extends ESTestCase {
// 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 class DefBootstrapTests extends ESTestCase {
}
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 class DefBootstrapTests extends ESTestCase {
}
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 class DefBootstrapTests extends ESTestCase {
}
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 class DefBootstrapTests extends ESTestCase {
// 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 class DefBootstrapTests extends ESTestCase {
}
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),

View File

@ -23,9 +23,10 @@ import org.apache.logging.log4j.Logger;
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 java.util.stream.Collectors.toList;
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> FIELD_NAME = comparing(f -> f.name);
private static final Comparator<Method> METHOD_NAME = comparing(m -> m.name);
private static final Comparator<Method> NUMBER_OF_ARGS = comparing(m -> m.arguments.size());
private static final Comparator<PainlessField> FIELD_NAME = comparing(f -> f.name);
private static final Comparator<PainlessMethod> METHOD_NAME = comparing(m -> m.name);
private static final Comparator<PainlessMethod> 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 class PainlessDocGenerator {
Files.newOutputStream(indexPath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE),
false, StandardCharsets.UTF_8.name())) {
emitGeneratedWarning(indexStream);
List<Struct> structs = definition.getStructs().stream().sorted(comparing(t -> t.name)).collect(toList());
for (Struct struct : structs) {
List<PainlessClass> 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 class PainlessDocGenerator {
typeStream.print(struct.name);
typeStream.println("++::");
Consumer<Field> documentField = field -> PainlessDocGenerator.documentField(typeStream, field);
Consumer<Method> documentMethod = method -> PainlessDocGenerator.documentMethod(typeStream, method);
Consumer<PainlessField> documentField = field -> PainlessDocGenerator.documentField(typeStream, field);
Consumer<PainlessMethod> 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<String, Struct> inherited = new TreeMap<>();
Map<String, PainlessClass> 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 class PainlessDocGenerator {
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 class PainlessDocGenerator {
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 @@ public class PainlessDocGenerator {
/**
* 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 @@ public class PainlessDocGenerator {
}
/**
* 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 @@ public class PainlessDocGenerator {
}
/**
* 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("<init>") ? method.owner.name : method.name;
}
@ -237,17 +238,17 @@ public class PainlessDocGenerator {
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 @@ public class PainlessDocGenerator {
}
/**
* 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 @@ public class PainlessDocGenerator {
}
/**
* 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 @@ public class PainlessDocGenerator {
}
/**
* 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 @@ public class PainlessDocGenerator {
}
/**
* 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";

View File

@ -24,6 +24,7 @@ import org.apache.lucene.search.Scorer;
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 abstract class ScriptTestCase extends ESTestCase {
public Object exec(String script, Map<String, Object> vars, Map<String,String> 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);

View File

@ -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 @@ import static org.elasticsearch.painless.node.SSource.MainMethodReserved;
* 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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 class NodeToStringTests extends ESTestCase {
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 @@ public class NodeToStringTests extends ESTestCase {
}
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);
}