From a926ace2e1e9c20dd93d94d129c616097e788e2f Mon Sep 17 00:00:00 2001 From: Jack Conradson Date: Fri, 2 Jun 2017 13:36:45 -0700 Subject: [PATCH] Update Painless to Use New Script Contexts (#25015) * All public methods starting with get will be added as local variables to the execute method. * The execute method on a ScriptContext must be both public and abstract. This method will be implemented by the Painless compiler. * A static list of parameter names for the execute method must be provided since the names will be eliminated at runtime. * The uses$ methods will still be implemented as before. * A single constructor may be provided by the ScriptContext. This constructor will be overridden by the Painless compiler to include the exact same arguments. This allows instances of a Painless script to potentially contain state. If a constructor is not provided it is assumed the default constructor with no arguments will be used. --- .../org/elasticsearch/painless/Compiler.java | 12 +- .../painless/GenericElasticsearchScript.java | 14 +- .../org/elasticsearch/painless/Locals.java | 11 +- .../painless/PainlessScriptEngine.java | 12 +- ...iptInterface.java => ScriptClassInfo.java} | 71 ++++-- .../painless/WriterConstants.java | 1 - .../elasticsearch/painless/antlr/Walker.java | 18 +- .../elasticsearch/painless/node/SSource.java | 62 +++-- ...terfacesTests.java => BaseClassTests.java} | 217 ++++++++++-------- .../painless/ScriptTestCase.java | 6 +- .../painless/node/NodeToStringTests.java | 6 +- 11 files changed, 264 insertions(+), 166 deletions(-) rename modules/lang-painless/src/main/java/org/elasticsearch/painless/{ScriptInterface.java => ScriptClassInfo.java} (75%) rename modules/lang-painless/src/test/java/org/elasticsearch/painless/{ImplementInterfacesTests.java => BaseClassTests.java} (78%) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java index 7d71724d97f..29f775901cb 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Compiler.java @@ -133,15 +133,15 @@ final class Compiler { * @param settings The CompilerSettings to be used during the compilation. * @return An executable script that implements both a specified interface and is a subclass of {@link PainlessScript} */ - Constructor compile(Loader loader, String name, String source, CompilerSettings settings) { + Constructor compile(Loader loader, String name, String source, CompilerSettings settings) { if (source.length() > MAXIMUM_SOURCE_LENGTH) { throw new IllegalArgumentException("Scripts may be no longer than " + MAXIMUM_SOURCE_LENGTH + " characters. The passed in script is " + source.length() + " characters. Consider using a" + " plugin if a script longer than this length is a requirement."); } - ScriptInterface scriptInterface = new ScriptInterface(definition, base); - SSource root = Walker.buildPainlessTree(scriptInterface, name, source, settings, definition, + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base); + SSource root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, definition, null); root.analyze(definition); root.write(); @@ -153,7 +153,7 @@ final class Compiler { clazz.getField("$STATEMENTS").set(null, root.getStatements()); clazz.getField("$DEFINITION").set(null, definition); - return clazz.getConstructor(); + return clazz.getConstructors()[0]; } catch (Exception exception) { // Catch everything to let the user know this is something caused internally. throw new IllegalStateException("An internal error occurred attempting to define the script [" + name + "].", exception); } @@ -172,8 +172,8 @@ final class Compiler { " plugin if a script longer than this length is a requirement."); } - ScriptInterface scriptInterface = new ScriptInterface(definition, base); - SSource root = Walker.buildPainlessTree(scriptInterface, name, source, settings, definition, + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, base); + SSource root = Walker.buildPainlessTree(scriptClassInfo, name, source, settings, definition, debugStream); root.analyze(definition); root.write(); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/GenericElasticsearchScript.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/GenericElasticsearchScript.java index 69f5462c228..06dd7df68cf 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/GenericElasticsearchScript.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/GenericElasticsearchScript.java @@ -26,10 +26,14 @@ import java.util.Map; /** * Generic script interface that Painless implements for all Elasticsearch scripts. */ -public interface GenericElasticsearchScript { - String[] ARGUMENTS = new String[] {"params", "_score", "doc", "_value", "ctx"}; - Object execute(Map params, double _score, Map> doc, Object _value, Map ctx); +public abstract class GenericElasticsearchScript { - boolean uses$_score(); - boolean uses$ctx(); + public GenericElasticsearchScript() {} + + public static final String[] PARAMETERS = new String[] {"params", "_score", "doc", "_value", "ctx"}; + public abstract Object execute( + Map params, double _score, Map> doc, Object _value, Map ctx); + + public abstract boolean uses$_score(); + public abstract boolean uses$ctx(); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java index 6eff9e3228b..b83a16df3ae 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Locals.java @@ -22,7 +22,7 @@ package org.elasticsearch.painless; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.MethodKey; import org.elasticsearch.painless.Definition.Type; -import org.elasticsearch.painless.ScriptInterface.MethodArgument; +import org.elasticsearch.painless.ScriptClassInfo.MethodArgument; import java.util.Arrays; import java.util.Collection; @@ -91,14 +91,14 @@ public final class Locals { } /** Creates a new main method scope */ - public static Locals newMainMethodScope(ScriptInterface scriptInterface, Locals programScope, int maxLoopCounter) { + public static Locals newMainMethodScope(ScriptClassInfo scriptClassInfo, Locals programScope, int maxLoopCounter) { Locals locals = new Locals(programScope, programScope.definition, - scriptInterface.getExecuteMethodReturnType(), KEYWORDS); + scriptClassInfo.getExecuteMethodReturnType(), KEYWORDS); // This reference. Internal use only. locals.defineVariable(null, programScope.definition.getType("Object"), THIS, true); // Method arguments - for (MethodArgument arg : scriptInterface.getExecuteArguments()) { + for (MethodArgument arg : scriptClassInfo.getExecuteArguments()) { locals.defineVariable(null, arg.getType(), arg.getName(), true); } @@ -186,7 +186,7 @@ public final class Locals { ///// private impl - /** Whitelist against which thhis script is being compiled. */ + /** Whitelist against which this script is being compiled. */ private final Definition definition; // parent scope private final Locals parent; @@ -275,6 +275,7 @@ public final class Locals { public final Type type; public final boolean readonly; private final int slot; + private boolean used; public Variable(Location location, String name, Type type, int slot, boolean readonly) { this.location = location; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java index 7093f41eeaf..c93017d489f 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/PainlessScriptEngine.java @@ -136,7 +136,7 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr throw new IllegalArgumentException("painless does not know how to handle context [" + context.name + "]"); } - PainlessScript compile(Compiler compiler, String scriptName, final String scriptSource, final Map params) { + Object compile(Compiler compiler, String scriptName, String source, Map params, Object... args) { final CompilerSettings compilerSettings; if (params.isEmpty()) { @@ -189,14 +189,14 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr try { // Drop all permissions to actually compile the code itself. - return AccessController.doPrivileged(new PrivilegedAction() { + return AccessController.doPrivileged(new PrivilegedAction() { @Override - public PainlessScript run() { + public Object run() { String name = scriptName == null ? INLINE_NAME : scriptName; - Constructor constructor = compiler.compile(loader, name, scriptSource, compilerSettings); + Constructor constructor = compiler.compile(loader, name, source, compilerSettings); try { - return constructor.newInstance(); + return constructor.newInstance(args); } catch (Exception exception) { // Catch everything to let the user know this is something caused internally. throw new IllegalStateException( "An internal error occurred attempting to define the script [" + name + "].", exception); @@ -205,7 +205,7 @@ public final class PainlessScriptEngine extends AbstractComponent implements Scr }, COMPILATION_CONTEXT); // Note that it is safe to catch any of the following errors since Painless is stateless. } catch (OutOfMemoryError | StackOverflowError | VerifyError | Exception e) { - throw convertToScriptException(scriptName == null ? scriptSource : scriptName, scriptSource, e); + throw convertToScriptException(scriptName == null ? source : scriptName, source, e); } } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInterface.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java similarity index 75% rename from modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInterface.java rename to modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java index 28fa6fd4280..3703aae2ab4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptInterface.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java @@ -21,6 +21,7 @@ package org.elasticsearch.painless; import java.lang.invoke.MethodType; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -33,20 +34,25 @@ import static org.elasticsearch.painless.WriterConstants.USES_PARAMETER_METHOD_T /** * Information about the interface being implemented by the painless script. */ -public class ScriptInterface { - private final Class iface; +public class ScriptClassInfo { + + private final Class baseClass; private final org.objectweb.asm.commons.Method executeMethod; private final Definition.Type executeMethodReturnType; private final List executeArguments; private final List usesMethods; + private final List getMethods; + private final List getReturns; - public ScriptInterface(Definition definition, Class iface) { - this.iface = iface; + public ScriptClassInfo(Definition definition, Class baseClass) { + this.baseClass = baseClass; // Find the main method and the uses$argName methods java.lang.reflect.Method executeMethod = null; List usesMethods = new ArrayList<>(); - for (java.lang.reflect.Method m : iface.getMethods()) { + List getMethods = new ArrayList<>(); + List getReturns = new ArrayList<>(); + for (java.lang.reflect.Method m : baseClass.getMethods()) { if (m.isDefault()) { continue; } @@ -55,40 +61,45 @@ public class ScriptInterface { executeMethod = m; } else { throw new IllegalArgumentException( - "Painless can only implement interfaces that have a single method named [execute] but [" + iface.getName() + "Painless can only implement interfaces that have a single method named [execute] but [" + baseClass.getName() + "] has more than one."); } - continue; } if (m.getName().startsWith("uses$")) { if (false == m.getReturnType().equals(boolean.class)) { throw new IllegalArgumentException("Painless can only implement uses$ methods that return boolean but [" - + iface.getName() + "#" + m.getName() + "] returns [" + m.getReturnType().getName() + "]."); + + baseClass.getName() + "#" + m.getName() + "] returns [" + m.getReturnType().getName() + "]."); } if (m.getParameterTypes().length > 0) { throw new IllegalArgumentException("Painless can only implement uses$ methods that do not take parameters but [" - + iface.getName() + "#" + m.getName() + "] does."); + + baseClass.getName() + "#" + m.getName() + "] does."); } usesMethods.add(new org.objectweb.asm.commons.Method(m.getName(), USES_PARAMETER_METHOD_TYPE.toMethodDescriptorString())); - continue; } - throw new IllegalArgumentException("Painless can only implement methods named [execute] and [uses$argName] but [" - + iface.getName() + "] contains a method named [" + m.getName() + "]"); + 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.")); + + getMethods.add(new org.objectweb.asm.commons.Method(m.getName(), + MethodType.methodType(m.getReturnType()).toMethodDescriptorString())); + + } } 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(), - componentType -> "Painless can only implement execute methods returning a whitelisted type but [" + iface.getName() + componentType -> "Painless can only implement execute methods returning a whitelisted type but [" + baseClass.getName() + "#execute] returns [" + componentType.getName() + "] which isn't whitelisted."); // Look up the argument names Set argumentNames = new LinkedHashSet<>(); List arguments = new ArrayList<>(); - String[] argumentNamesConstant = readArgumentNamesConstant(iface); + String[] argumentNamesConstant = readArgumentNamesConstant(baseClass); Class[] types = executeMethod.getParameterTypes(); if (argumentNamesConstant.length != types.length) { - throw new IllegalArgumentException("[" + iface.getName() + "#ARGUMENTS] has length [2] but [" - + iface.getName() + "#execute] takes [1] argument."); + throw new IllegalArgumentException("[" + baseClass.getName() + "#ARGUMENTS] has length [2] but [" + + baseClass.getName() + "#execute] takes [1] argument."); } for (int arg = 0; arg < types.length; arg++) { arguments.add(methodArgument(definition, types[arg], argumentNamesConstant[arg])); @@ -100,17 +111,19 @@ public class ScriptInterface { for (org.objectweb.asm.commons.Method usesMethod : usesMethods) { if (false == argumentNames.contains(usesMethod.getName().substring("uses$".length()))) { throw new IllegalArgumentException("Painless can only implement uses$ methods that match a parameter name but [" - + iface.getName() + "#" + usesMethod.getName() + "] doesn't match any of " + argumentNames + "."); + + baseClass.getName() + "#" + usesMethod.getName() + "] doesn't match any of " + argumentNames + "."); } } this.usesMethods = unmodifiableList(usesMethods); + this.getMethods = unmodifiableList(getMethods); + this.getReturns = unmodifiableList(getReturns); } /** * The interface that the Painless script should implement. */ - public Class getInterface() { - return iface; + public Class getBaseClass() { + return baseClass; } /** @@ -143,6 +156,20 @@ public class ScriptInterface { return usesMethods; } + /** + * The {@code getVarName} methods that must be implemented by Painless to complete implementing the interface. + */ + public List getGetMethods() { + return getMethods; + } + + /** + * The {@code getVarName} methods return types. + */ + public List getGetReturns() { + return getReturns; + } + /** * Painless {@link Definition.Type}s and name of the argument to the {@code execute} method. */ @@ -194,13 +221,13 @@ public class ScriptInterface { private static String[] readArgumentNamesConstant(Class iface) { Field argumentNamesField; try { - argumentNamesField = iface.getField("ARGUMENTS"); + argumentNamesField = iface.getField("PARAMETERS"); } catch (NoSuchFieldException e) { - throw new IllegalArgumentException("Painless needs a constant [String[] ARGUMENTS] on all interfaces it implements with the " + throw new IllegalArgumentException("Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the " + "names of the method arguments but [" + iface.getName() + "] doesn't have one.", e); } if (false == argumentNamesField.getType().equals(String[].class)) { - throw new IllegalArgumentException("Painless needs a constant [String[] ARGUMENTS] on all interfaces it implements with the " + throw new IllegalArgumentException("Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the " + "names of the method arguments but [" + iface.getName() + "] doesn't have one."); } try { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java index e2e235dfa4d..aca9973efa9 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/WriterConstants.java @@ -56,7 +56,6 @@ public final class WriterConstants { public static final String CTOR_METHOD_NAME = ""; - public static final Method CONSTRUCTOR = getAsmMethod(void.class, CTOR_METHOD_NAME); public static final Method CLINIT = getAsmMethod(void.class, ""); public static final String GET_NAME_NAME = "getName"; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java index 0210b7ce151..6d044dcd916 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/Walker.java @@ -33,7 +33,7 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.Operation; -import org.elasticsearch.painless.ScriptInterface; +import org.elasticsearch.painless.ScriptClassInfo; import org.elasticsearch.painless.antlr.PainlessParser.AfterthoughtContext; import org.elasticsearch.painless.antlr.PainlessParser.ArgumentContext; import org.elasticsearch.painless.antlr.PainlessParser.ArgumentsContext; @@ -174,14 +174,14 @@ import java.util.List; */ public final class Walker extends PainlessParserBaseVisitor { - public static SSource buildPainlessTree(ScriptInterface mainMethod, String sourceName, - String sourceText, CompilerSettings settings, Definition definition, - Printer debugStream) { + public static SSource buildPainlessTree(ScriptClassInfo mainMethod, String sourceName, + String sourceText, CompilerSettings settings, Definition definition, + Printer debugStream) { return new Walker(mainMethod, sourceName, sourceText, settings, definition, debugStream).source; } - private final ScriptInterface scriptInterface; + private final ScriptClassInfo scriptClassInfo; private final SSource source; private final CompilerSettings settings; private final Printer debugStream; @@ -193,9 +193,9 @@ public final class Walker extends PainlessParserBaseVisitor { private final Globals globals; private int syntheticCounter = 0; - private Walker(ScriptInterface scriptInterface, String sourceName, String sourceText, - CompilerSettings settings, Definition definition, Printer debugStream) { - this.scriptInterface = scriptInterface; + private Walker(ScriptClassInfo scriptClassInfo, String sourceName, String sourceText, + CompilerSettings settings, Definition definition, Printer debugStream) { + this.scriptClassInfo = scriptClassInfo; this.debugStream = debugStream; this.settings = settings; this.sourceName = Location.computeSourceName(sourceName, sourceText); @@ -266,7 +266,7 @@ public final class Walker extends PainlessParserBaseVisitor { statements.add((AStatement)visit(statement)); } - return new SSource(scriptInterface, settings, sourceName, sourceText, debugStream, (MainMethodReserved)reserved.pop(), + return new SSource(scriptClassInfo, settings, sourceName, sourceText, debugStream, (MainMethodReserved)reserved.pop(), location(ctx), functions, globals, statements); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java index 1ab39ea20e9..f7654897c23 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SSource.java @@ -29,7 +29,7 @@ import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; import org.elasticsearch.painless.Location; import org.elasticsearch.painless.MethodWriter; -import org.elasticsearch.painless.ScriptInterface; +import org.elasticsearch.painless.ScriptClassInfo; import org.elasticsearch.painless.SimpleChecksAdapter; import org.elasticsearch.painless.WriterConstants; import org.objectweb.asm.ClassVisitor; @@ -40,6 +40,7 @@ import org.objectweb.asm.Type; import org.objectweb.asm.util.Printer; import org.objectweb.asm.util.TraceClassVisitor; +import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; @@ -58,7 +59,6 @@ import static org.elasticsearch.painless.WriterConstants.BITSET_TYPE; import static org.elasticsearch.painless.WriterConstants.BOOTSTRAP_METHOD_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE; import static org.elasticsearch.painless.WriterConstants.COLLECTIONS_TYPE; -import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR; import static org.elasticsearch.painless.WriterConstants.CONVERT_TO_SCRIPT_EXCEPTION_METHOD; import static org.elasticsearch.painless.WriterConstants.DEFINITION_TYPE; import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_DELEGATE_METHOD; @@ -69,7 +69,6 @@ import static org.elasticsearch.painless.WriterConstants.EXCEPTION_TYPE; import static org.elasticsearch.painless.WriterConstants.GET_NAME_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_SOURCE_METHOD; import static org.elasticsearch.painless.WriterConstants.GET_STATEMENTS_METHOD; -import static org.elasticsearch.painless.WriterConstants.OBJECT_TYPE; import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_ERROR_TYPE; import static org.elasticsearch.painless.WriterConstants.PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD; @@ -119,7 +118,7 @@ public final class SSource extends AStatement { } } - private final ScriptInterface scriptInterface; + private final ScriptClassInfo scriptClassInfo; private final CompilerSettings settings; private final String name; private final String source; @@ -130,13 +129,14 @@ public final class SSource extends AStatement { private final List statements; private Locals mainMethod; + private final List getMethods; private byte[] bytes; - public SSource(ScriptInterface scriptInterface, CompilerSettings settings, String name, String source, Printer debugStream, + public SSource(ScriptClassInfo scriptClassInfo, CompilerSettings settings, String name, String source, Printer debugStream, MainMethodReserved reserved, Location location, List functions, Globals globals, List statements) { super(location); - this.scriptInterface = Objects.requireNonNull(scriptInterface); + this.scriptClassInfo = Objects.requireNonNull(scriptClassInfo); this.settings = Objects.requireNonNull(settings); this.name = Objects.requireNonNull(name); this.source = Objects.requireNonNull(source); @@ -148,6 +148,8 @@ public final class SSource extends AStatement { this.functions = Collections.unmodifiableList(functions); this.statements = Collections.unmodifiableList(statements); this.globals = globals; + + this.getMethods = new ArrayList<>(); } @Override @@ -184,7 +186,19 @@ public final class SSource extends AStatement { throw createError(new IllegalArgumentException("Cannot generate an empty script.")); } - mainMethod = Locals.newMainMethodScope(scriptInterface, program, reserved.getMaxLoopCounter()); + mainMethod = Locals.newMainMethodScope(scriptClassInfo, program, reserved.getMaxLoopCounter()); + + for (int get = 0; get < scriptClassInfo.getGetMethods().size(); ++get) { + org.objectweb.asm.commons.Method method = scriptClassInfo.getGetMethods().get(get); + String name = method.getName().substring(3); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + + if (reserved.getUsedVariables().contains(name)) { + Definition.Type rtn = scriptClassInfo.getGetReturns().get(get); + mainMethod.addVariable(new Location("getter [" + name + "]", 0), rtn, name, true); + getMethods.add(method); + } + } AStatement last = statements.get(statements.size() - 1); @@ -211,7 +225,7 @@ public final class SSource extends AStatement { int classAccess = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL; String interfaceBase = BASE_INTERFACE_TYPE.getInternalName(); String className = CLASS_TYPE.getInternalName(); - String classInterfaces[] = new String[] { interfaceBase, Type.getType(scriptInterface.getInterface()).getInternalName() }; + String classInterfaces[] = new String[] { interfaceBase }; ClassWriter writer = new ClassWriter(classFrames); ClassVisitor visitor = writer; @@ -224,7 +238,8 @@ public final class SSource extends AStatement { if (debugStream != null) { visitor = new TraceClassVisitor(visitor, debugStream, null); } - visitor.visit(WriterConstants.CLASS_VERSION, classAccess, className, null, OBJECT_TYPE.getInternalName(), classInterfaces); + visitor.visit(WriterConstants.CLASS_VERSION, classAccess, className, null, + Type.getType(scriptClassInfo.getBaseClass()).getInternalName(), classInterfaces); visitor.visitSource(Location.computeSourceName(name, source), null); // Write the a method to bootstrap def calls @@ -245,12 +260,21 @@ public final class SSource extends AStatement { // Write the static variable used by the method to bootstrap def calls visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$DEFINITION", DEFINITION_TYPE.getDescriptor(), null, null).visitEnd(); + org.objectweb.asm.commons.Method init; + + if (scriptClassInfo.getBaseClass().getConstructors().length == 0) { + init = new org.objectweb.asm.commons.Method("", MethodType.methodType(void.class).toMethodDescriptorString()); + } else { + init = new org.objectweb.asm.commons.Method("", MethodType.methodType(void.class, + scriptClassInfo.getBaseClass().getConstructors()[0].getParameterTypes()).toMethodDescriptorString()); + } + // Write the constructor: - MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, visitor, globals.getStatements(), settings); + MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, init, visitor, globals.getStatements(), settings); constructor.visitCode(); constructor.loadThis(); constructor.loadArgs(); - constructor.invokeConstructor(OBJECT_TYPE, CONSTRUCTOR); + constructor.invokeConstructor(Type.getType(scriptClassInfo.getBaseClass()), init); constructor.returnValue(); constructor.endMethod(); @@ -277,7 +301,7 @@ public final class SSource extends AStatement { statementsMethod.endMethod(); // Write the method defined in the interface: - MethodWriter executeMethod = new MethodWriter(Opcodes.ACC_PUBLIC, scriptInterface.getExecuteMethod(), visitor, + MethodWriter executeMethod = new MethodWriter(Opcodes.ACC_PUBLIC, scriptClassInfo.getExecuteMethod(), visitor, globals.getStatements(), settings); executeMethod.visitCode(); write(executeMethod, globals); @@ -324,7 +348,7 @@ public final class SSource extends AStatement { } // Write any uses$varName methods for used variables - for (org.objectweb.asm.commons.Method usesMethod : scriptInterface.getUsesMethods()) { + for (org.objectweb.asm.commons.Method usesMethod : scriptClassInfo.getUsesMethods()) { MethodWriter ifaceMethod = new MethodWriter(Opcodes.ACC_PUBLIC, usesMethod, visitor, globals.getStatements(), settings); ifaceMethod.visitCode(); ifaceMethod.push(reserved.getUsedVariables().contains(usesMethod.getName().substring("uses$".length()))); @@ -358,12 +382,22 @@ public final class SSource extends AStatement { writer.visitVarInsn(Opcodes.ISTORE, loop.getSlot()); } + for (org.objectweb.asm.commons.Method method : getMethods) { + String name = method.getName().substring(3); + name = Character.toLowerCase(name.charAt(0)) + name.substring(1); + Variable variable = mainMethod.getVariable(null, name); + + writer.loadThis(); + writer.invokeVirtual(Type.getType(scriptClassInfo.getBaseClass()), method); + writer.visitVarInsn(method.getReturnType().getOpcode(Opcodes.ISTORE), variable.getSlot()); + } + for (AStatement statement : statements) { statement.write(writer, globals); } if (!methodEscape) { - switch (scriptInterface.getExecuteMethod().getReturnType().getSort()) { + switch (scriptClassInfo.getExecuteMethod().getReturnType().getSort()) { case org.objectweb.asm.Type.VOID: break; case org.objectweb.asm.Type.BOOLEAN: writer.push(false); break; case org.objectweb.asm.Type.BYTE: writer.push(0); break; diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ImplementInterfacesTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java similarity index 78% rename from modules/lang-painless/src/test/java/org/elasticsearch/painless/ImplementInterfacesTests.java rename to modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java index 3d534bab86f..eef541590a4 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ImplementInterfacesTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/BaseClassTests.java @@ -22,6 +22,7 @@ package org.elasticsearch.painless; import org.elasticsearch.script.ScriptContext; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -34,10 +35,53 @@ import static org.hamcrest.Matchers.startsWith; /** * Tests for Painless implementing different interfaces. */ -public class ImplementInterfacesTests extends ScriptTestCase { - public interface NoArgs { - String[] ARGUMENTS = new String[] {}; - Object execute(); +public class BaseClassTests extends ScriptTestCase { + + public abstract static class Gets { + + private final String testString; + private final int testInt; + private final Map testMap; + + public Gets(String testString, int testInt, Map testMap) { + this.testString = testString; + this.testInt = testInt; + this.testMap = testMap; + } + + public static final String[] PARAMETERS = new String[] {}; + public abstract Object execute(); + + public String getTestString() { + return testString; + } + + public int getTestInt() { + return Math.abs(testInt); + } + + public Map getTestMap() { + return testMap == null ? new HashMap<>() : testMap; + } + } + + public void testGets() { + Compiler compiler = new Compiler(Gets.class, Definition.BUILTINS); + Map map = new HashMap<>(); + map.put("s", 1); + + assertEquals(1, ((Gets)scriptEngine.compile(compiler, null, "testInt", emptyMap(), "s", -1, null)).execute()); + assertEquals(Collections.emptyMap(), ((Gets)scriptEngine.compile(compiler, null, "testMap", emptyMap(), "s", -1, null)).execute()); + assertEquals(Collections.singletonMap("1", "1"), + ((Gets)scriptEngine.compile(compiler, null, "testMap", emptyMap(), "s", -1, Collections.singletonMap("1", "1"))).execute()); + assertEquals("s", ((Gets)scriptEngine.compile(compiler, null, "testString", emptyMap(), "s", -1, null)).execute()); + assertEquals(map, + ((Gets)scriptEngine.compile(compiler, null, "testMap.put(testString, testInt); testMap", emptyMap(), "s", -1, null)).execute()); + } + + public abstract static class NoArgs { + public static final String[] PARAMETERS = new String[] {}; + public abstract Object execute(); } public void testNoArgs() { Compiler compiler = new Compiler(NoArgs.class, Definition.BUILTINS); @@ -59,9 +103,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertThat(debug, containsString("ARETURN")); } - public interface OneArg { - String[] ARGUMENTS = new String[] {"arg"}; - Object execute(Object arg); + public abstract static class OneArg { + public static final String[] PARAMETERS = new String[] {"arg"}; + public abstract Object execute(Object arg); } public void testOneArg() { Compiler compiler = new Compiler(OneArg.class, Definition.BUILTINS); @@ -80,9 +124,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals("Variable [_score] is not defined.", e.getMessage()); } - public interface ArrayArg { - String[] ARGUMENTS = new String[] {"arg"}; - Object execute(String[] arg); + public abstract static class ArrayArg { + public static final String[] PARAMETERS = new String[] {"arg"}; + public abstract Object execute(String[] arg); } public void testArrayArg() { Compiler compiler = new Compiler(ArrayArg.class, Definition.BUILTINS); @@ -90,9 +134,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(rando, ((ArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new String[] {rando, "foo"})); } - public interface PrimitiveArrayArg { - String[] ARGUMENTS = new String[] {"arg"}; - Object execute(int[] arg); + public abstract static class PrimitiveArrayArg { + public static final String[] PARAMETERS = new String[] {"arg"}; + public abstract Object execute(int[] arg); } public void testPrimitiveArrayArg() { Compiler compiler = new Compiler(PrimitiveArrayArg.class, Definition.BUILTINS); @@ -100,9 +144,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(rando, ((PrimitiveArrayArg)scriptEngine.compile(compiler, null, "arg[0]", emptyMap())).execute(new int[] {rando, 10})); } - public interface DefArrayArg { - String[] ARGUMENTS = new String[] {"arg"}; - Object execute(Object[] arg); + public abstract static class DefArrayArg { + public static final String[] PARAMETERS = new String[] {"arg"}; + public abstract Object execute(Object[] arg); } public void testDefArrayArg() { Compiler compiler = new Compiler(DefArrayArg.class, Definition.BUILTINS); @@ -114,13 +158,13 @@ public class ImplementInterfacesTests extends ScriptTestCase { ((DefArrayArg)scriptEngine.compile(compiler, null, "arg[0].length()", emptyMap())).execute(new Object[] {rando, 10})); } - public interface ManyArgs { - String[] ARGUMENTS = new String[] {"a", "b", "c", "d"}; - Object execute(int a, int b, int c, int d); - boolean uses$a(); - boolean uses$b(); - boolean uses$c(); - boolean uses$d(); + public abstract static class ManyArgs { + public static final String[] PARAMETERS = new String[] {"a", "b", "c", "d"}; + public abstract Object execute(int a, int b, int c, int d); + public abstract boolean uses$a(); + public abstract boolean uses$b(); + public abstract boolean uses$c(); + public abstract boolean uses$d(); } public void testManyArgs() { Compiler compiler = new Compiler(ManyArgs.class, Definition.BUILTINS); @@ -146,9 +190,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertTrue(script.uses$d()); } - public interface VarargTest { - String[] ARGUMENTS = new String[] {"arg"}; - Object execute(String... arg); + public abstract static class VarargTest { + public static final String[] PARAMETERS = new String[] {"arg"}; + public abstract Object execute(String... arg); } public void testVararg() { Compiler compiler = new Compiler(VarargTest.class, Definition.BUILTINS); @@ -156,13 +200,13 @@ public class ImplementInterfacesTests extends ScriptTestCase { .execute("foo", "bar", "baz")); } - public interface DefaultMethods { - String[] ARGUMENTS = new String[] {"a", "b", "c", "d"}; - Object execute(int a, int b, int c, int d); - default Object executeWithOne() { + public abstract static class DefaultMethods { + public static final String[] PARAMETERS = new String[] {"a", "b", "c", "d"}; + public abstract Object execute(int a, int b, int c, int d); + public Object executeWithOne() { return execute(1, 1, 1, 1); } - default Object executeWithASingleOne(int a, int b, int c) { + public Object executeWithASingleOne(int a, int b, int c) { return execute(a, b, c, 1); } } @@ -176,9 +220,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(7, ((DefaultMethods)scriptEngine.compile(compiler, null, "a + b + c + d", emptyMap())).executeWithASingleOne(1, 2, 3)); } - public interface ReturnsVoid { - String[] ARGUMENTS = new String[] {"map"}; - void execute(Map map); + public abstract static class ReturnsVoid { + public static final String[] PARAMETERS = new String[] {"map"}; + public abstract void execute(Map map); } public void testReturnsVoid() { Compiler compiler = new Compiler(ReturnsVoid.class, Definition.BUILTINS); @@ -195,9 +239,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertThat(debug, not(containsString("ACONST_NULL"))); } - public interface ReturnsPrimitiveBoolean { - String[] ARGUMENTS = new String[] {}; - boolean execute(); + public abstract static class ReturnsPrimitiveBoolean { + public static final String[] PARAMETERS = new String[] {}; + public abstract boolean execute(); } public void testReturnsPrimitiveBoolean() { Compiler compiler = new Compiler(ReturnsPrimitiveBoolean.class, Definition.BUILTINS); @@ -237,9 +281,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(false, ((ReturnsPrimitiveBoolean)scriptEngine.compile(compiler, null, "int i = 0", emptyMap())).execute()); } - public interface ReturnsPrimitiveInt { - String[] ARGUMENTS = new String[] {}; - int execute(); + public abstract static class ReturnsPrimitiveInt { + public static final String[] PARAMETERS = new String[] {}; + public abstract int execute(); } public void testReturnsPrimitiveInt() { Compiler compiler = new Compiler(ReturnsPrimitiveInt.class, Definition.BUILTINS); @@ -279,9 +323,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(0, ((ReturnsPrimitiveInt)scriptEngine.compile(compiler, null, "int i = 0", emptyMap())).execute()); } - public interface ReturnsPrimitiveFloat { - String[] ARGUMENTS = new String[] {}; - float execute(); + public abstract static class ReturnsPrimitiveFloat { + public static final String[] PARAMETERS = new String[] {}; + public abstract float execute(); } public void testReturnsPrimitiveFloat() { Compiler compiler = new Compiler(ReturnsPrimitiveFloat.class, Definition.BUILTINS); @@ -310,9 +354,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(0.0f, ((ReturnsPrimitiveFloat)scriptEngine.compile(compiler, null, "int i = 0", emptyMap())).execute(), 0); } - public interface ReturnsPrimitiveDouble { - String[] ARGUMENTS = new String[] {}; - double execute(); + public abstract static class ReturnsPrimitiveDouble { + public static final String[] PARAMETERS = new String[] {}; + public abstract double execute(); } public void testReturnsPrimitiveDouble() { Compiler compiler = new Compiler(ReturnsPrimitiveDouble.class, Definition.BUILTINS); @@ -345,32 +389,34 @@ public class ImplementInterfacesTests extends ScriptTestCase { assertEquals(0.0, ((ReturnsPrimitiveDouble)scriptEngine.compile(compiler, null, "int i = 0", emptyMap())).execute(), 0); } - public interface NoArgumentsConstant { - Object execute(String foo); + public abstract static class NoArgumentsConstant { + public abstract Object execute(String foo); } public void testNoArgumentsConstant() { Compiler compiler = new Compiler(NoArgumentsConstant.class, Definition.BUILTINS); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); - assertThat(e.getMessage(), startsWith("Painless needs a constant [String[] ARGUMENTS] on all interfaces it implements with the " + assertThat(e.getMessage(), startsWith( + "Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the " + "names of the method arguments but [" + NoArgumentsConstant.class.getName() + "] doesn't have one.")); } - public interface WrongArgumentsConstant { - boolean[] ARGUMENTS = new boolean[] {false}; - Object execute(String foo); + public abstract static class WrongArgumentsConstant { + boolean[] PARAMETERS = new boolean[] {false}; + public abstract Object execute(String foo); } public void testWrongArgumentsConstant() { Compiler compiler = new Compiler(WrongArgumentsConstant.class, Definition.BUILTINS); Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> scriptEngine.compile(compiler, null, "1", emptyMap())); - assertThat(e.getMessage(), startsWith("Painless needs a constant [String[] ARGUMENTS] on all interfaces it implements with the " + assertThat(e.getMessage(), startsWith( + "Painless needs a constant [String[] PARAMETERS] on all interfaces it implements with the " + "names of the method arguments but [" + WrongArgumentsConstant.class.getName() + "] doesn't have one.")); } - public interface WrongLengthOfArgumentConstant { - String[] ARGUMENTS = new String[] {"foo", "bar"}; - Object execute(String foo); + public abstract static class WrongLengthOfArgumentConstant { + public static final String[] PARAMETERS = new String[] {"foo", "bar"}; + public abstract Object execute(String foo); } public void testWrongLengthOfArgumentConstant() { Compiler compiler = new Compiler(WrongLengthOfArgumentConstant.class, Definition.BUILTINS); @@ -380,9 +426,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { + WrongLengthOfArgumentConstant.class.getName() + "#execute] takes [1] argument.")); } - public interface UnknownArgType { - String[] ARGUMENTS = new String[] {"foo"}; - Object execute(UnknownArgType foo); + public abstract static class UnknownArgType { + public static final String[] PARAMETERS = new String[] {"foo"}; + public abstract Object execute(UnknownArgType foo); } public void testUnknownArgType() { Compiler compiler = new Compiler(UnknownArgType.class, Definition.BUILTINS); @@ -392,9 +438,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { + "that are of whitelisted types.", e.getMessage()); } - public interface UnknownReturnType { - String[] ARGUMENTS = new String[] {"foo"}; - UnknownReturnType execute(String foo); + public abstract static class UnknownReturnType { + public static final String[] PARAMETERS = new String[] {"foo"}; + public abstract UnknownReturnType execute(String foo); } public void testUnknownReturnType() { Compiler compiler = new Compiler(UnknownReturnType.class, Definition.BUILTINS); @@ -404,9 +450,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { + "#execute] returns [" + UnknownReturnType.class.getName() + "] which isn't whitelisted.", e.getMessage()); } - public interface UnknownArgTypeInArray { - String[] ARGUMENTS = new String[] {"foo"}; - Object execute(UnknownArgTypeInArray[] foo); + public abstract static class UnknownArgTypeInArray { + public static final String[] PARAMETERS = new String[] {"foo"}; + public abstract Object execute(UnknownArgTypeInArray[] foo); } public void testUnknownArgTypeInArray() { Compiler compiler = new Compiler(UnknownArgTypeInArray.class, Definition.BUILTINS); @@ -416,9 +462,9 @@ public class ImplementInterfacesTests extends ScriptTestCase { + "arguments that are of whitelisted types.", e.getMessage()); } - public interface TwoExecuteMethods { - Object execute(); - Object execute(boolean foo); + public abstract static class TwoExecuteMethods { + public abstract Object execute(); + public abstract Object execute(boolean foo); } public void testTwoExecuteMethods() { Compiler compiler = new Compiler(TwoExecuteMethods.class, Definition.BUILTINS); @@ -428,21 +474,10 @@ public class ImplementInterfacesTests extends ScriptTestCase { + TwoExecuteMethods.class.getName() + "] has more than one.", e.getMessage()); } - public interface BadMethod { - Object something(); - } - public void testBadMethod() { - Compiler compiler = new Compiler(BadMethod.class, Definition.BUILTINS); - Exception e = expectScriptThrows(IllegalArgumentException.class, false, () -> - scriptEngine.compile(compiler, null, "null", emptyMap())); - assertEquals("Painless can only implement methods named [execute] and [uses$argName] but [" + BadMethod.class.getName() - + "] contains a method named [something]", e.getMessage()); - } - - public interface BadUsesReturn { - String[] ARGUMENTS = new String[] {"foo"}; - Object execute(String foo); - Object uses$foo(); + public abstract static class BadUsesReturn { + public static final String[] PARAMETERS = new String[] {"foo"}; + public abstract Object execute(String foo); + public abstract Object uses$foo(); } public void testBadUsesReturn() { Compiler compiler = new Compiler(BadUsesReturn.class, Definition.BUILTINS); @@ -452,10 +487,10 @@ public class ImplementInterfacesTests extends ScriptTestCase { + "#uses$foo] returns [java.lang.Object].", e.getMessage()); } - public interface BadUsesParameter { - String[] ARGUMENTS = new String[] {"foo", "bar"}; - Object execute(String foo, String bar); - boolean uses$bar(boolean foo); + public abstract static class BadUsesParameter { + public static final String[] PARAMETERS = new String[] {"foo", "bar"}; + public abstract Object execute(String foo, String bar); + public abstract boolean uses$bar(boolean foo); } public void testBadUsesParameter() { Compiler compiler = new Compiler(BadUsesParameter.class, Definition.BUILTINS); @@ -465,10 +500,10 @@ public class ImplementInterfacesTests extends ScriptTestCase { + "#uses$bar] does.", e.getMessage()); } - public interface BadUsesName { - String[] ARGUMENTS = new String[] {"foo", "bar"}; - Object execute(String foo, String bar); - boolean uses$baz(); + public abstract static class BadUsesName { + public static final String[] PARAMETERS = new String[] {"foo", "bar"}; + public abstract Object execute(String foo, String bar); + public abstract boolean uses$baz(); } public void testBadUsesName() { Compiler compiler = new Compiler(BadUsesName.class, Definition.BUILTINS); diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java index 4e527f43e31..24cfd29547a 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/ScriptTestCase.java @@ -34,9 +34,7 @@ import org.junit.Before; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.hasSize; @@ -94,11 +92,11 @@ public abstract class ScriptTestCase extends ESTestCase { // test for ambiguity errors before running the actual script if picky is true if (picky) { Definition definition = Definition.BUILTINS; - ScriptInterface scriptInterface = new ScriptInterface(definition, GenericElasticsearchScript.class); + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, GenericElasticsearchScript.class); CompilerSettings pickySettings = new CompilerSettings(); pickySettings.setPicky(true); pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(scriptEngineSettings())); - Walker.buildPainlessTree(scriptInterface, getTestName(), script, pickySettings, + Walker.buildPainlessTree(scriptClassInfo, getTestName(), script, pickySettings, definition, null); } // test actual script execution diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java index 13d48bd2a01..7d3115fdb5e 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/node/NodeToStringTests.java @@ -31,7 +31,7 @@ import org.elasticsearch.painless.FeatureTest; import org.elasticsearch.painless.GenericElasticsearchScript; import org.elasticsearch.painless.Locals.Variable; import org.elasticsearch.painless.Location; -import org.elasticsearch.painless.ScriptInterface; +import org.elasticsearch.painless.ScriptClassInfo; import org.elasticsearch.painless.Operation; import org.elasticsearch.painless.antlr.Walker; import org.elasticsearch.test.ESTestCase; @@ -898,11 +898,11 @@ public class NodeToStringTests extends ESTestCase { } private SSource walk(String code) { - ScriptInterface scriptInterface = new ScriptInterface(definition, GenericElasticsearchScript.class); + ScriptClassInfo scriptClassInfo = new ScriptClassInfo(definition, GenericElasticsearchScript.class); CompilerSettings compilerSettings = new CompilerSettings(); compilerSettings.setRegexesEnabled(true); try { - return Walker.buildPainlessTree(scriptInterface, getTestName(), code, compilerSettings, + return Walker.buildPainlessTree(scriptClassInfo, getTestName(), code, compilerSettings, definition, null); } catch (Exception e) { throw new AssertionError("Failed to compile: " + code, e);