mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-09 14:34:43 +00:00
Start on custom whitelists for Painless (#23563)
We'd like to be able to support context-sensitive whitelists in Painless but we can't now because the whitelist is a static thing. This begins to de-static the whitelist, in particular removing the static keyword from most of the methods on `Definition` and plumbing the static instance into the appropriate spots as though it weren't static. Once we de-static all the methods we should be able to fairly simply build context-sensitive whitelists. The only "fun" bit of this is that I added another layer in the chain of methods that bootstraps `def` calls. Instead of running `invokedynamic` directly on `DefBootstrap` we now `invokedynamic` `$bootstrapDef` on the script itself loads the `Definition` that the script was compiled against and then calls `DefBootstrap`. I chose to put `Definition` into `Locals` so I didn't have to change the signature of all the `analyze` methods. I could have do it another way, but that seems ok for now.
This commit is contained in:
parent
8f540346a9
commit
0b15fde27a
modules/lang-painless/src
main/java/org/elasticsearch/painless
Compiler.javaDef.javaDefBootstrap.javaDefinition.javaFunctionRef.javaLocals.javaPainlessExplainError.javaScriptInterface.javaWriterConstants.java
antlr
node
test/java/org/elasticsearch/painless
@ -100,15 +100,18 @@ final class Compiler {
|
|||||||
" characters. The passed in script is " + source.length() + " characters. Consider using a" +
|
" characters. The passed in script is " + source.length() + " characters. Consider using a" +
|
||||||
" plugin if a script longer than this length is a requirement.");
|
" plugin if a script longer than this length is a requirement.");
|
||||||
}
|
}
|
||||||
ScriptInterface scriptInterface = new ScriptInterface(iface);
|
Definition definition = Definition.BUILTINS;
|
||||||
|
ScriptInterface scriptInterface = new ScriptInterface(definition, iface);
|
||||||
|
|
||||||
SSource root = Walker.buildPainlessTree(scriptInterface, name, source, settings, null);
|
SSource root = Walker.buildPainlessTree(scriptInterface, name, source, settings, definition,
|
||||||
|
null);
|
||||||
|
|
||||||
root.analyze();
|
root.analyze(definition);
|
||||||
root.write();
|
root.write();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<? extends PainlessScript> clazz = loader.define(CLASS_NAME, root.getBytes());
|
Class<? extends PainlessScript> clazz = loader.define(CLASS_NAME, root.getBytes());
|
||||||
|
clazz.getField("$DEFINITION").set(null, definition);
|
||||||
java.lang.reflect.Constructor<? extends PainlessScript> constructor =
|
java.lang.reflect.Constructor<? extends PainlessScript> constructor =
|
||||||
clazz.getConstructor(String.class, String.class, BitSet.class);
|
clazz.getConstructor(String.class, String.class, BitSet.class);
|
||||||
|
|
||||||
@ -131,11 +134,13 @@ final class Compiler {
|
|||||||
" characters. The passed in script is " + source.length() + " characters. Consider using a" +
|
" characters. The passed in script is " + source.length() + " characters. Consider using a" +
|
||||||
" plugin if a script longer than this length is a requirement.");
|
" plugin if a script longer than this length is a requirement.");
|
||||||
}
|
}
|
||||||
ScriptInterface scriptInterface = new ScriptInterface(iface);
|
Definition definition = Definition.BUILTINS;
|
||||||
|
ScriptInterface scriptInterface = new ScriptInterface(definition, iface);
|
||||||
|
|
||||||
SSource root = Walker.buildPainlessTree(scriptInterface, name, source, settings, debugStream);
|
SSource root = Walker.buildPainlessTree(scriptInterface, name, source, settings, definition,
|
||||||
|
debugStream);
|
||||||
|
|
||||||
root.analyze();
|
root.analyze(definition);
|
||||||
root.write();
|
root.write();
|
||||||
|
|
||||||
return root.getBytes();
|
return root.getBytes();
|
||||||
|
@ -175,17 +175,18 @@ public final class Def {
|
|||||||
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
|
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
|
||||||
* Otherwise it returns the matching method.
|
* Otherwise it returns the matching method.
|
||||||
* <p>
|
* <p>
|
||||||
|
* @params definition the whitelist
|
||||||
* @param receiverClass Class of the object to invoke the method on.
|
* @param receiverClass Class of the object to invoke the method on.
|
||||||
* @param name Name of the method.
|
* @param name Name of the method.
|
||||||
* @param arity arity of method
|
* @param arity arity of method
|
||||||
* @return matching method to invoke. never returns null.
|
* @return matching method to invoke. never returns null.
|
||||||
* @throws IllegalArgumentException if no matching whitelisted method was found.
|
* @throws IllegalArgumentException if no matching whitelisted method was found.
|
||||||
*/
|
*/
|
||||||
static Method lookupMethodInternal(Class<?> receiverClass, String name, int arity) {
|
static Method lookupMethodInternal(Definition definition, Class<?> receiverClass, String name, int arity) {
|
||||||
Definition.MethodKey key = new Definition.MethodKey(name, arity);
|
Definition.MethodKey key = new Definition.MethodKey(name, arity);
|
||||||
// check whitelist for matching method
|
// check whitelist for matching method
|
||||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||||
RuntimeClass struct = Definition.getRuntimeClass(clazz);
|
RuntimeClass struct = definition.getRuntimeClass(clazz);
|
||||||
|
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
Method method = struct.methods.get(key);
|
Method method = struct.methods.get(key);
|
||||||
@ -195,7 +196,7 @@ public final class Def {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Class<?> iface : clazz.getInterfaces()) {
|
for (Class<?> iface : clazz.getInterfaces()) {
|
||||||
struct = Definition.getRuntimeClass(iface);
|
struct = definition.getRuntimeClass(iface);
|
||||||
|
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
Method method = struct.methods.get(key);
|
Method method = struct.methods.get(key);
|
||||||
@ -220,6 +221,7 @@ public final class Def {
|
|||||||
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
|
* 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.
|
* Otherwise it returns a handle to the matching method.
|
||||||
* <p>
|
* <p>
|
||||||
|
* @param definition the whitelist
|
||||||
* @param lookup caller's lookup
|
* @param lookup caller's lookup
|
||||||
* @param callSiteType callsite's type
|
* @param callSiteType callsite's type
|
||||||
* @param receiverClass Class of the object to invoke the method on.
|
* @param receiverClass Class of the object to invoke the method on.
|
||||||
@ -229,13 +231,13 @@ public final class Def {
|
|||||||
* @throws IllegalArgumentException if no matching whitelisted method was found.
|
* @throws IllegalArgumentException if no matching whitelisted method was found.
|
||||||
* @throws Throwable if a method reference cannot be converted to an functional interface
|
* @throws Throwable if a method reference cannot be converted to an functional interface
|
||||||
*/
|
*/
|
||||||
static MethodHandle lookupMethod(Lookup lookup, MethodType callSiteType,
|
static MethodHandle lookupMethod(Definition definition, Lookup lookup, MethodType callSiteType,
|
||||||
Class<?> receiverClass, String name, Object args[]) throws Throwable {
|
Class<?> receiverClass, String name, Object args[]) throws Throwable {
|
||||||
String recipeString = (String) args[0];
|
String recipeString = (String) args[0];
|
||||||
int numArguments = callSiteType.parameterCount();
|
int numArguments = callSiteType.parameterCount();
|
||||||
// simple case: no lambdas
|
// simple case: no lambdas
|
||||||
if (recipeString.isEmpty()) {
|
if (recipeString.isEmpty()) {
|
||||||
return lookupMethodInternal(receiverClass, name, numArguments - 1).handle;
|
return lookupMethodInternal(definition, receiverClass, name, numArguments - 1).handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert recipe string to a bitset for convenience (the code below should be refactored...)
|
// convert recipe string to a bitset for convenience (the code below should be refactored...)
|
||||||
@ -258,7 +260,7 @@ public final class Def {
|
|||||||
|
|
||||||
// lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
|
// 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.
|
// based on these we can finally link any remaining lambdas that were deferred.
|
||||||
Method method = lookupMethodInternal(receiverClass, name, arity);
|
Method method = lookupMethodInternal(definition, receiverClass, name, arity);
|
||||||
MethodHandle handle = method.handle;
|
MethodHandle handle = method.handle;
|
||||||
|
|
||||||
int replaced = 0;
|
int replaced = 0;
|
||||||
@ -282,7 +284,8 @@ public final class Def {
|
|||||||
if (signature.charAt(0) == 'S') {
|
if (signature.charAt(0) == 'S') {
|
||||||
// the implementation is strongly typed, now that we know the interface type,
|
// the implementation is strongly typed, now that we know the interface type,
|
||||||
// we have everything.
|
// we have everything.
|
||||||
filter = lookupReferenceInternal(lookup,
|
filter = lookupReferenceInternal(definition,
|
||||||
|
lookup,
|
||||||
interfaceType,
|
interfaceType,
|
||||||
type,
|
type,
|
||||||
call,
|
call,
|
||||||
@ -292,7 +295,8 @@ public final class Def {
|
|||||||
// this is dynamically based on the receiver type (and cached separately, underneath
|
// 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)
|
// this cache). It won't blow up since we never nest here (just references)
|
||||||
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
|
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
|
||||||
CallSite nested = DefBootstrap.bootstrap(lookup,
|
CallSite nested = DefBootstrap.bootstrap(definition,
|
||||||
|
lookup,
|
||||||
call,
|
call,
|
||||||
nestedType,
|
nestedType,
|
||||||
0,
|
0,
|
||||||
@ -319,21 +323,23 @@ public final class Def {
|
|||||||
* This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known,
|
* 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.
|
* so we simply need to lookup the matching implementation method based on receiver type.
|
||||||
*/
|
*/
|
||||||
static MethodHandle lookupReference(Lookup lookup, String interfaceClass,
|
static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
|
||||||
Class<?> receiverClass, String name) throws Throwable {
|
Class<?> receiverClass, String name) throws Throwable {
|
||||||
Definition.Type interfaceType = Definition.getType(interfaceClass);
|
Definition.Type interfaceType = definition.getType(interfaceClass);
|
||||||
Method interfaceMethod = interfaceType.struct.getFunctionalMethod();
|
Method interfaceMethod = interfaceType.struct.getFunctionalMethod();
|
||||||
if (interfaceMethod == null) {
|
if (interfaceMethod == null) {
|
||||||
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
|
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
|
||||||
}
|
}
|
||||||
int arity = interfaceMethod.arguments.size();
|
int arity = interfaceMethod.arguments.size();
|
||||||
Method implMethod = lookupMethodInternal(receiverClass, name, arity);
|
Method implMethod = lookupMethodInternal(definition, receiverClass, name, arity);
|
||||||
return lookupReferenceInternal(lookup, interfaceType, implMethod.owner.name, implMethod.name, receiverClass);
|
return lookupReferenceInternal(definition, lookup, interfaceType, implMethod.owner.name,
|
||||||
|
implMethod.name, receiverClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a method handle to an implementation of clazz, given method reference signature. */
|
/** Returns a method handle to an implementation of clazz, given method reference signature. */
|
||||||
private static MethodHandle lookupReferenceInternal(Lookup lookup, Definition.Type clazz, String type,
|
private static MethodHandle lookupReferenceInternal(Definition definition, Lookup lookup,
|
||||||
String call, Class<?>... captures) throws Throwable {
|
Definition.Type clazz, String type, String call, Class<?>... captures)
|
||||||
|
throws Throwable {
|
||||||
final FunctionRef ref;
|
final FunctionRef ref;
|
||||||
if ("this".equals(type)) {
|
if ("this".equals(type)) {
|
||||||
// user written method
|
// user written method
|
||||||
@ -361,7 +367,7 @@ public final class Def {
|
|||||||
ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length);
|
ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length);
|
||||||
} else {
|
} else {
|
||||||
// whitelist lookup
|
// whitelist lookup
|
||||||
ref = new FunctionRef(clazz, type, call, captures.length);
|
ref = new FunctionRef(definition, clazz, type, call, captures.length);
|
||||||
}
|
}
|
||||||
final CallSite callSite;
|
final CallSite callSite;
|
||||||
if (ref.needsBridges()) {
|
if (ref.needsBridges()) {
|
||||||
@ -411,15 +417,16 @@ public final class Def {
|
|||||||
* until it finds a matching whitelisted getter. If one is not found, it throws an exception.
|
* 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.
|
* Otherwise it returns a handle to the matching getter.
|
||||||
* <p>
|
* <p>
|
||||||
|
* @param definition the whitelist
|
||||||
* @param receiverClass Class of the object to retrieve the field from.
|
* @param receiverClass Class of the object to retrieve the field from.
|
||||||
* @param name Name of the field.
|
* @param name Name of the field.
|
||||||
* @return pointer to matching field. never returns null.
|
* @return pointer to matching field. never returns null.
|
||||||
* @throws IllegalArgumentException if no matching whitelisted field was found.
|
* @throws IllegalArgumentException if no matching whitelisted field was found.
|
||||||
*/
|
*/
|
||||||
static MethodHandle lookupGetter(Class<?> receiverClass, String name) {
|
static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
|
||||||
// first try whitelist
|
// first try whitelist
|
||||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||||
RuntimeClass struct = Definition.getRuntimeClass(clazz);
|
RuntimeClass struct = definition.getRuntimeClass(clazz);
|
||||||
|
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
MethodHandle handle = struct.getters.get(name);
|
MethodHandle handle = struct.getters.get(name);
|
||||||
@ -429,7 +436,7 @@ public final class Def {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||||
struct = Definition.getRuntimeClass(iface);
|
struct = definition.getRuntimeClass(iface);
|
||||||
|
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
MethodHandle handle = struct.getters.get(name);
|
MethodHandle handle = struct.getters.get(name);
|
||||||
@ -481,15 +488,16 @@ public final class Def {
|
|||||||
* until it finds a matching whitelisted setter. If one is not found, it throws an exception.
|
* 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.
|
* Otherwise it returns a handle to the matching setter.
|
||||||
* <p>
|
* <p>
|
||||||
|
* @param definition the whitelist
|
||||||
* @param receiverClass Class of the object to retrieve the field from.
|
* @param receiverClass Class of the object to retrieve the field from.
|
||||||
* @param name Name of the field.
|
* @param name Name of the field.
|
||||||
* @return pointer to matching field. never returns null.
|
* @return pointer to matching field. never returns null.
|
||||||
* @throws IllegalArgumentException if no matching whitelisted field was found.
|
* @throws IllegalArgumentException if no matching whitelisted field was found.
|
||||||
*/
|
*/
|
||||||
static MethodHandle lookupSetter(Class<?> receiverClass, String name) {
|
static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
|
||||||
// first try whitelist
|
// first try whitelist
|
||||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||||
RuntimeClass struct = Definition.getRuntimeClass(clazz);
|
RuntimeClass struct = definition.getRuntimeClass(clazz);
|
||||||
|
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
MethodHandle handle = struct.setters.get(name);
|
MethodHandle handle = struct.setters.get(name);
|
||||||
@ -499,7 +507,7 @@ public final class Def {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||||
struct = Definition.getRuntimeClass(iface);
|
struct = definition.getRuntimeClass(iface);
|
||||||
|
|
||||||
if (struct != null) {
|
if (struct != null) {
|
||||||
MethodHandle handle = struct.setters.get(name);
|
MethodHandle handle = struct.setters.get(name);
|
||||||
|
@ -104,17 +104,19 @@ public final class DefBootstrap {
|
|||||||
/** maximum number of types before we go megamorphic */
|
/** maximum number of types before we go megamorphic */
|
||||||
static final int MAX_DEPTH = 5;
|
static final int MAX_DEPTH = 5;
|
||||||
|
|
||||||
|
private final Definition definition;
|
||||||
private final Lookup lookup;
|
private final Lookup lookup;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int flavor;
|
private final int flavor;
|
||||||
private final Object[] args;
|
private final Object[] args;
|
||||||
int depth; // pkg-protected for testing
|
int depth; // pkg-protected for testing
|
||||||
|
|
||||||
PIC(Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) {
|
PIC(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object[] args) {
|
||||||
super(type);
|
super(type);
|
||||||
if (type.parameterType(0) != Object.class) {
|
if (type.parameterType(0) != Object.class) {
|
||||||
throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object.");
|
throw new BootstrapMethodError("The receiver type (1st arg) of invokedynamic descriptor must be Object.");
|
||||||
}
|
}
|
||||||
|
this.definition = definition;
|
||||||
this.lookup = lookup;
|
this.lookup = lookup;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.flavor = flavor;
|
this.flavor = flavor;
|
||||||
@ -142,11 +144,11 @@ public final class DefBootstrap {
|
|||||||
private MethodHandle lookup(int flavor, String name, Class<?> receiver) throws Throwable {
|
private MethodHandle lookup(int flavor, String name, Class<?> receiver) throws Throwable {
|
||||||
switch(flavor) {
|
switch(flavor) {
|
||||||
case METHOD_CALL:
|
case METHOD_CALL:
|
||||||
return Def.lookupMethod(lookup, type(), receiver, name, args);
|
return Def.lookupMethod(definition, lookup, type(), receiver, name, args);
|
||||||
case LOAD:
|
case LOAD:
|
||||||
return Def.lookupGetter(receiver, name);
|
return Def.lookupGetter(definition, receiver, name);
|
||||||
case STORE:
|
case STORE:
|
||||||
return Def.lookupSetter(receiver, name);
|
return Def.lookupSetter(definition, receiver, name);
|
||||||
case ARRAY_LOAD:
|
case ARRAY_LOAD:
|
||||||
return Def.lookupArrayLoad(receiver);
|
return Def.lookupArrayLoad(receiver);
|
||||||
case ARRAY_STORE:
|
case ARRAY_STORE:
|
||||||
@ -154,7 +156,7 @@ public final class DefBootstrap {
|
|||||||
case ITERATOR:
|
case ITERATOR:
|
||||||
return Def.lookupIterator(receiver);
|
return Def.lookupIterator(receiver);
|
||||||
case REFERENCE:
|
case REFERENCE:
|
||||||
return Def.lookupReference(lookup, (String) args[0], receiver, name);
|
return Def.lookupReference(definition, lookup, (String) args[0], receiver, name);
|
||||||
case INDEX_NORMALIZE:
|
case INDEX_NORMALIZE:
|
||||||
return Def.lookupIndexNormalize(receiver);
|
return Def.lookupIndexNormalize(receiver);
|
||||||
default: throw new AssertionError();
|
default: throw new AssertionError();
|
||||||
@ -419,16 +421,18 @@ public final class DefBootstrap {
|
|||||||
/**
|
/**
|
||||||
* invokeDynamic bootstrap method
|
* invokeDynamic bootstrap method
|
||||||
* <p>
|
* <p>
|
||||||
* In addition to ordinary parameters, we also take some static parameters:
|
* In addition to ordinary parameters, we also take some parameters defined at the call site:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code initialDepth}: initial call site depth. this is used to exercise megamorphic fallback.
|
* <li>{@code initialDepth}: initial call site depth. this is used to exercise megamorphic fallback.
|
||||||
* <li>{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
|
* <li>{@code flavor}: type of dynamic call it is (and which part of whitelist to look at).
|
||||||
* <li>{@code args}: flavor-specific args.
|
* <li>{@code args}: flavor-specific args.
|
||||||
* </ul>
|
* </ul>
|
||||||
|
* And we take the {@link Definition} used to compile the script for whitelist checking.
|
||||||
* <p>
|
* <p>
|
||||||
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
|
||||||
*/
|
*/
|
||||||
public static CallSite bootstrap(Lookup lookup, String name, MethodType type, int initialDepth, int flavor, Object... args) {
|
public static CallSite bootstrap(Definition definition, Lookup lookup, String name, MethodType type, int initialDepth, int flavor,
|
||||||
|
Object... args) {
|
||||||
// validate arguments
|
// validate arguments
|
||||||
switch(flavor) {
|
switch(flavor) {
|
||||||
// "function-call" like things get a polymorphic cache
|
// "function-call" like things get a polymorphic cache
|
||||||
@ -447,7 +451,7 @@ public final class DefBootstrap {
|
|||||||
if (args.length != numLambdas + 1) {
|
if (args.length != numLambdas + 1) {
|
||||||
throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references");
|
throw new BootstrapMethodError("Illegal number of parameters: expected " + numLambdas + " references");
|
||||||
}
|
}
|
||||||
return new PIC(lookup, name, type, initialDepth, flavor, args);
|
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
|
||||||
case LOAD:
|
case LOAD:
|
||||||
case STORE:
|
case STORE:
|
||||||
case ARRAY_LOAD:
|
case ARRAY_LOAD:
|
||||||
@ -457,7 +461,7 @@ public final class DefBootstrap {
|
|||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor);
|
throw new BootstrapMethodError("Illegal static bootstrap parameters for flavor: " + flavor);
|
||||||
}
|
}
|
||||||
return new PIC(lookup, name, type, initialDepth, flavor, args);
|
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
|
||||||
case REFERENCE:
|
case REFERENCE:
|
||||||
if (args.length != 1) {
|
if (args.length != 1) {
|
||||||
throw new BootstrapMethodError("Invalid number of parameters for reference call");
|
throw new BootstrapMethodError("Invalid number of parameters for reference call");
|
||||||
@ -465,7 +469,7 @@ public final class DefBootstrap {
|
|||||||
if (args[0] instanceof String == false) {
|
if (args[0] instanceof String == false) {
|
||||||
throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]);
|
throw new BootstrapMethodError("Illegal parameter for reference call: " + args[0]);
|
||||||
}
|
}
|
||||||
return new PIC(lookup, name, type, initialDepth, flavor, args);
|
return new PIC(definition, lookup, name, type, initialDepth, flavor, args);
|
||||||
|
|
||||||
// operators get monomorphic cache, with a generic impl for a fallback
|
// operators get monomorphic cache, with a generic impl for a fallback
|
||||||
case UNARY_OPERATOR:
|
case UNARY_OPERATOR:
|
||||||
|
@ -65,33 +65,39 @@ public final class Definition {
|
|||||||
"java.util.stream.txt",
|
"java.util.stream.txt",
|
||||||
"joda.time.txt"));
|
"joda.time.txt"));
|
||||||
|
|
||||||
private static final Definition INSTANCE = new Definition();
|
/**
|
||||||
|
* Whitelist that is "built in" to Painless and required by all scripts.
|
||||||
|
*/
|
||||||
|
public static final Definition BUILTINS = new Definition();
|
||||||
|
|
||||||
/** Some native types as constants: */
|
/** Some native types as constants: */
|
||||||
public static final Type VOID_TYPE = getType("void");
|
public static final Type VOID_TYPE = BUILTINS.getType("void");
|
||||||
public static final Type BOOLEAN_TYPE = getType("boolean");
|
public static final Type BOOLEAN_TYPE = BUILTINS.getType("boolean");
|
||||||
public static final Type BOOLEAN_OBJ_TYPE = getType("Boolean");
|
public static final Type BOOLEAN_OBJ_TYPE = BUILTINS.getType("Boolean");
|
||||||
public static final Type BYTE_TYPE = getType("byte");
|
public static final Type BYTE_TYPE = BUILTINS.getType("byte");
|
||||||
public static final Type BYTE_OBJ_TYPE = getType("Byte");
|
public static final Type BYTE_OBJ_TYPE = BUILTINS.getType("Byte");
|
||||||
public static final Type SHORT_TYPE = getType("short");
|
public static final Type SHORT_TYPE = BUILTINS.getType("short");
|
||||||
public static final Type SHORT_OBJ_TYPE = getType("Short");
|
public static final Type SHORT_OBJ_TYPE = BUILTINS.getType("Short");
|
||||||
public static final Type INT_TYPE = getType("int");
|
public static final Type INT_TYPE = BUILTINS.getType("int");
|
||||||
public static final Type INT_OBJ_TYPE = getType("Integer");
|
public static final Type INT_OBJ_TYPE = BUILTINS.getType("Integer");
|
||||||
public static final Type LONG_TYPE = getType("long");
|
public static final Type LONG_TYPE = BUILTINS.getType("long");
|
||||||
public static final Type LONG_OBJ_TYPE = getType("Long");
|
public static final Type LONG_OBJ_TYPE = BUILTINS.getType("Long");
|
||||||
public static final Type FLOAT_TYPE = getType("float");
|
public static final Type FLOAT_TYPE = BUILTINS.getType("float");
|
||||||
public static final Type FLOAT_OBJ_TYPE = getType("Float");
|
public static final Type FLOAT_OBJ_TYPE = BUILTINS.getType("Float");
|
||||||
public static final Type DOUBLE_TYPE = getType("double");
|
public static final Type DOUBLE_TYPE = BUILTINS.getType("double");
|
||||||
public static final Type DOUBLE_OBJ_TYPE = getType("Double");
|
public static final Type DOUBLE_OBJ_TYPE = BUILTINS.getType("Double");
|
||||||
public static final Type CHAR_TYPE = getType("char");
|
public static final Type CHAR_TYPE = BUILTINS.getType("char");
|
||||||
public static final Type CHAR_OBJ_TYPE = getType("Character");
|
public static final Type CHAR_OBJ_TYPE = BUILTINS.getType("Character");
|
||||||
public static final Type OBJECT_TYPE = getType("Object");
|
public static final Type OBJECT_TYPE = BUILTINS.getType("Object");
|
||||||
public static final Type DEF_TYPE = getType("def");
|
public static final Type DEF_TYPE = BUILTINS.getType("def");
|
||||||
public static final Type NUMBER_TYPE = getType("Number");
|
public static final Type NUMBER_TYPE = BUILTINS.getType("Number");
|
||||||
public static final Type STRING_TYPE = getType("String");
|
public static final Type STRING_TYPE = BUILTINS.getType("String");
|
||||||
public static final Type EXCEPTION_TYPE = getType("Exception");
|
public static final Type EXCEPTION_TYPE = BUILTINS.getType("Exception");
|
||||||
public static final Type PATTERN_TYPE = getType("Pattern");
|
public static final Type PATTERN_TYPE = BUILTINS.getType("Pattern");
|
||||||
public static final Type MATCHER_TYPE = getType("Matcher");
|
public static final Type MATCHER_TYPE = BUILTINS.getType("Matcher");
|
||||||
|
public static final Type ITERATOR_TYPE = BUILTINS.getType("Iterator");
|
||||||
|
public static final Type ARRAY_LIST_TYPE = BUILTINS.getType("ArrayList");
|
||||||
|
public static final Type HASH_MAP_TYPE = BUILTINS.getType("HashMap");
|
||||||
|
|
||||||
public enum Sort {
|
public enum Sort {
|
||||||
VOID( void.class , Void.class , null , 0 , true , false , false , false ),
|
VOID( void.class , Void.class , null , 0 , true , false , false , false ),
|
||||||
@ -483,38 +489,27 @@ public final class Definition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether or not a non-array type exists. */
|
/** Returns whether or not a non-array type exists. */
|
||||||
public static boolean isSimpleType(final String name) {
|
public boolean isSimpleType(final String name) {
|
||||||
return INSTANCE.structsMap.containsKey(name);
|
return BUILTINS.structsMap.containsKey(name);
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns whether or not a type exists without an exception. */
|
|
||||||
public static boolean isType(final String name) {
|
|
||||||
try {
|
|
||||||
INSTANCE.getTypeInternal(name);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (IllegalArgumentException exception) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the type given by its name */
|
/** Gets the type given by its name */
|
||||||
public static Type getType(final String name) {
|
public Type getType(final String name) {
|
||||||
return INSTANCE.getTypeInternal(name);
|
return BUILTINS.getTypeInternal(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an array type from the given Struct. */
|
/** Creates an array type from the given Struct. */
|
||||||
public static Type getType(final Struct struct, final int dimensions) {
|
public Type getType(final Struct struct, final int dimensions) {
|
||||||
return INSTANCE.getTypeInternal(struct, dimensions);
|
return BUILTINS.getTypeInternal(struct, dimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RuntimeClass getRuntimeClass(Class<?> clazz) {
|
public RuntimeClass getRuntimeClass(Class<?> clazz) {
|
||||||
return INSTANCE.runtimeMap.get(clazz);
|
return BUILTINS.runtimeMap.get(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Collection of all simple types. Used by {@code PainlessDocGenerator} to generate an API reference. */
|
/** Collection of all simple types. Used by {@code PainlessDocGenerator} to generate an API reference. */
|
||||||
static Collection<Type> allSimpleTypes() {
|
static Collection<Type> allSimpleTypes() {
|
||||||
return INSTANCE.simpleTypesMap.values();
|
return BUILTINS.simpleTypesMap.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERNAL IMPLEMENTATION:
|
// INTERNAL IMPLEMENTATION:
|
||||||
|
@ -50,13 +50,16 @@ public class FunctionRef {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new FunctionRef, which will resolve {@code type::call} from the whitelist.
|
* 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 expected interface type to implement.
|
* @param expected interface type to implement.
|
||||||
* @param type the left hand side of a method reference expression
|
* @param type the left hand side of a method reference expression
|
||||||
* @param call the right hand side of a method reference expression
|
* @param call the right hand side of a method reference expression
|
||||||
* @param numCaptures number of captured arguments
|
* @param numCaptures number of captured arguments
|
||||||
*/
|
*/
|
||||||
public FunctionRef(Definition.Type expected, String type, String call, int numCaptures) {
|
public FunctionRef(Definition definition, Definition.Type expected, String type, String call,
|
||||||
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, numCaptures > 0), numCaptures);
|
int numCaptures) {
|
||||||
|
this(expected, expected.struct.getFunctionalMethod(),
|
||||||
|
lookup(definition, expected, type, call, numCaptures > 0), numCaptures);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,7 +137,8 @@ public class FunctionRef {
|
|||||||
/**
|
/**
|
||||||
* Looks up {@code type::call} from the whitelist, and returns a matching method.
|
* Looks up {@code type::call} from the whitelist, and returns a matching method.
|
||||||
*/
|
*/
|
||||||
private static Definition.Method lookup(Definition.Type expected, String type, String call, boolean receiverCaptured) {
|
private static Definition.Method lookup(Definition definition, Definition.Type expected,
|
||||||
|
String type, String call, boolean receiverCaptured) {
|
||||||
// check its really a functional interface
|
// check its really a functional interface
|
||||||
// for e.g. Comparable
|
// for e.g. Comparable
|
||||||
Method method = expected.struct.getFunctionalMethod();
|
Method method = expected.struct.getFunctionalMethod();
|
||||||
@ -144,7 +148,7 @@ public class FunctionRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lookup requested method
|
// lookup requested method
|
||||||
Definition.Struct struct = Definition.getType(type).struct;
|
Definition.Struct struct = definition.getType(type).struct;
|
||||||
final Definition.Method impl;
|
final Definition.Method impl;
|
||||||
// ctor ref
|
// ctor ref
|
||||||
if ("new".equals(call)) {
|
if ("new".equals(call)) {
|
||||||
|
@ -60,7 +60,7 @@ public final class Locals {
|
|||||||
*/
|
*/
|
||||||
public static Locals newLambdaScope(Locals programScope, Type returnType, List<Parameter> parameters,
|
public static Locals newLambdaScope(Locals programScope, Type returnType, List<Parameter> parameters,
|
||||||
int captureCount, int maxLoopCounter) {
|
int captureCount, int maxLoopCounter) {
|
||||||
Locals locals = new Locals(programScope, returnType, KEYWORDS);
|
Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS);
|
||||||
for (int i = 0; i < parameters.size(); i++) {
|
for (int i = 0; i < parameters.size(); i++) {
|
||||||
Parameter parameter = parameters.get(i);
|
Parameter parameter = parameters.get(i);
|
||||||
// TODO: allow non-captures to be r/w:
|
// TODO: allow non-captures to be r/w:
|
||||||
@ -79,7 +79,7 @@ public final class Locals {
|
|||||||
|
|
||||||
/** Creates a new function scope inside the current scope */
|
/** Creates a new function scope inside the current scope */
|
||||||
public static Locals newFunctionScope(Locals programScope, Type returnType, List<Parameter> parameters, int maxLoopCounter) {
|
public static Locals newFunctionScope(Locals programScope, Type returnType, List<Parameter> parameters, int maxLoopCounter) {
|
||||||
Locals locals = new Locals(programScope, returnType, KEYWORDS);
|
Locals locals = new Locals(programScope, programScope.definition, returnType, KEYWORDS);
|
||||||
for (Parameter parameter : parameters) {
|
for (Parameter parameter : parameters) {
|
||||||
locals.addVariable(parameter.location, parameter.type, parameter.name, false);
|
locals.addVariable(parameter.location, parameter.type, parameter.name, false);
|
||||||
}
|
}
|
||||||
@ -92,9 +92,10 @@ public final class Locals {
|
|||||||
|
|
||||||
/** Creates a new main method scope */
|
/** Creates a new main method scope */
|
||||||
public static Locals newMainMethodScope(ScriptInterface scriptInterface, Locals programScope, int maxLoopCounter) {
|
public static Locals newMainMethodScope(ScriptInterface scriptInterface, Locals programScope, int maxLoopCounter) {
|
||||||
Locals locals = new Locals(programScope, scriptInterface.getExecuteMethodReturnType(), KEYWORDS);
|
Locals locals = new Locals(programScope, programScope.definition,
|
||||||
|
scriptInterface.getExecuteMethodReturnType(), KEYWORDS);
|
||||||
// This reference. Internal use only.
|
// This reference. Internal use only.
|
||||||
locals.defineVariable(null, Definition.getType("Object"), THIS, true);
|
locals.defineVariable(null, programScope.definition.getType("Object"), THIS, true);
|
||||||
|
|
||||||
// Method arguments
|
// Method arguments
|
||||||
for (MethodArgument arg : scriptInterface.getExecuteArguments()) {
|
for (MethodArgument arg : scriptInterface.getExecuteArguments()) {
|
||||||
@ -109,8 +110,8 @@ public final class Locals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a new program scope: the list of methods. It is the parent for all methods */
|
/** Creates a new program scope: the list of methods. It is the parent for all methods */
|
||||||
public static Locals newProgramScope(Collection<Method> methods) {
|
public static Locals newProgramScope(Definition definition, Collection<Method> methods) {
|
||||||
Locals locals = new Locals(null, null, null);
|
Locals locals = new Locals(null, definition, null, null);
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
locals.addMethod(method);
|
locals.addMethod(method);
|
||||||
}
|
}
|
||||||
@ -178,8 +179,15 @@ public final class Locals {
|
|||||||
return locals;
|
return locals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Whitelist against which this script is being compiled. */
|
||||||
|
public Definition getDefinition() {
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
///// private impl
|
///// private impl
|
||||||
|
|
||||||
|
/** Whitelist against which thhis script is being compiled. */
|
||||||
|
private final Definition definition;
|
||||||
// parent scope
|
// parent scope
|
||||||
private final Locals parent;
|
private final Locals parent;
|
||||||
// return type of this scope
|
// return type of this scope
|
||||||
@ -197,14 +205,15 @@ public final class Locals {
|
|||||||
* Create a new Locals
|
* Create a new Locals
|
||||||
*/
|
*/
|
||||||
private Locals(Locals parent) {
|
private Locals(Locals parent) {
|
||||||
this(parent, parent.returnType, parent.keywords);
|
this(parent, parent.definition, parent.returnType, parent.keywords);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Locals with specified return type
|
* Create a new Locals with specified return type
|
||||||
*/
|
*/
|
||||||
private Locals(Locals parent, Type returnType, Set<String> keywords) {
|
private Locals(Locals parent, Definition definition, Type returnType, Set<String> keywords) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
this.definition = definition;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.keywords = keywords;
|
this.keywords = keywords;
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
|
@ -46,7 +46,7 @@ public class PainlessExplainError extends Error {
|
|||||||
/**
|
/**
|
||||||
* Headers to be added to the {@link ScriptException} for structured rendering.
|
* Headers to be added to the {@link ScriptException} for structured rendering.
|
||||||
*/
|
*/
|
||||||
public Map<String, List<String>> getHeaders() {
|
public Map<String, List<String>> getHeaders(Definition definition) {
|
||||||
Map<String, List<String>> headers = new TreeMap<>();
|
Map<String, List<String>> headers = new TreeMap<>();
|
||||||
String toString = "null";
|
String toString = "null";
|
||||||
String javaClassName = null;
|
String javaClassName = null;
|
||||||
@ -54,7 +54,7 @@ public class PainlessExplainError extends Error {
|
|||||||
if (objectToExplain != null) {
|
if (objectToExplain != null) {
|
||||||
toString = objectToExplain.toString();
|
toString = objectToExplain.toString();
|
||||||
javaClassName = objectToExplain.getClass().getName();
|
javaClassName = objectToExplain.getClass().getName();
|
||||||
Definition.RuntimeClass runtimeClass = Definition.getRuntimeClass(objectToExplain.getClass());
|
Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(objectToExplain.getClass());
|
||||||
if (runtimeClass != null) {
|
if (runtimeClass != null) {
|
||||||
painlessClassName = runtimeClass.getStruct().name;
|
painlessClassName = runtimeClass.getStruct().name;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class ScriptInterface {
|
|||||||
private final List<MethodArgument> executeArguments;
|
private final List<MethodArgument> executeArguments;
|
||||||
private final List<org.objectweb.asm.commons.Method> usesMethods;
|
private final List<org.objectweb.asm.commons.Method> usesMethods;
|
||||||
|
|
||||||
public ScriptInterface(Class<?> iface) {
|
public ScriptInterface(Definition definition, Class<?> iface) {
|
||||||
this.iface = iface;
|
this.iface = iface;
|
||||||
|
|
||||||
// Find the main method and the uses$argName methods
|
// Find the main method and the uses$argName methods
|
||||||
@ -77,7 +77,7 @@ public class ScriptInterface {
|
|||||||
}
|
}
|
||||||
MethodType methodType = MethodType.methodType(executeMethod.getReturnType(), executeMethod.getParameterTypes());
|
MethodType methodType = MethodType.methodType(executeMethod.getReturnType(), executeMethod.getParameterTypes());
|
||||||
this.executeMethod = new org.objectweb.asm.commons.Method(executeMethod.getName(), methodType.toMethodDescriptorString());
|
this.executeMethod = new org.objectweb.asm.commons.Method(executeMethod.getName(), methodType.toMethodDescriptorString());
|
||||||
executeMethodReturnType = definitionTypeForClass(executeMethod.getReturnType(),
|
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 [" + iface.getName()
|
||||||
+ "#execute] returns [" + componentType.getName() + "] which isn't whitelisted.");
|
+ "#execute] returns [" + componentType.getName() + "] which isn't whitelisted.");
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ public class ScriptInterface {
|
|||||||
+ iface.getName() + "#execute] takes [1] argument.");
|
+ iface.getName() + "#execute] takes [1] argument.");
|
||||||
}
|
}
|
||||||
for (int arg = 0; arg < types.length; arg++) {
|
for (int arg = 0; arg < types.length; arg++) {
|
||||||
arguments.add(methodArgument(types[arg], argumentNamesConstant[arg]));
|
arguments.add(methodArgument(definition, types[arg], argumentNamesConstant[arg]));
|
||||||
argumentNames.add(argumentNamesConstant[arg]);
|
argumentNames.add(argumentNamesConstant[arg]);
|
||||||
}
|
}
|
||||||
this.executeArguments = unmodifiableList(arguments);
|
this.executeArguments = unmodifiableList(arguments);
|
||||||
@ -164,13 +164,14 @@ public class ScriptInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodArgument methodArgument(Class<?> type, String argName) {
|
private MethodArgument methodArgument(Definition definition, Class<?> type, String argName) {
|
||||||
Definition.Type defType = definitionTypeForClass(type, componentType -> "[" + argName + "] is of unknown type ["
|
Definition.Type defType = definitionTypeForClass(definition, type, componentType -> "[" + argName + "] is of unknown type ["
|
||||||
+ componentType.getName() + ". Painless interfaces can only accept arguments that are of whitelisted types.");
|
+ componentType.getName() + ". Painless interfaces can only accept arguments that are of whitelisted types.");
|
||||||
return new MethodArgument(defType, argName);
|
return new MethodArgument(defType, argName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Definition.Type definitionTypeForClass(Class<?> type, Function<Class<?>, String> unknownErrorMessageSource) {
|
private static Definition.Type definitionTypeForClass(Definition definition, Class<?> type,
|
||||||
|
Function<Class<?>, String> unknownErrorMessageSource) {
|
||||||
int dimensions = 0;
|
int dimensions = 0;
|
||||||
Class<?> componentType = type;
|
Class<?> componentType = type;
|
||||||
while (componentType.isArray()) {
|
while (componentType.isArray()) {
|
||||||
@ -181,13 +182,13 @@ public class ScriptInterface {
|
|||||||
if (componentType.equals(Object.class)) {
|
if (componentType.equals(Object.class)) {
|
||||||
struct = Definition.DEF_TYPE.struct;
|
struct = Definition.DEF_TYPE.struct;
|
||||||
} else {
|
} else {
|
||||||
Definition.RuntimeClass runtimeClass = Definition.getRuntimeClass(componentType);
|
Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(componentType);
|
||||||
if (runtimeClass == null) {
|
if (runtimeClass == null) {
|
||||||
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
|
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
|
||||||
}
|
}
|
||||||
struct = runtimeClass.getStruct();
|
struct = runtimeClass.getStruct();
|
||||||
}
|
}
|
||||||
return Definition.getType(struct, dimensions);
|
return definition.getType(struct, dimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] readArgumentNamesConstant(Class<?> iface) {
|
private static String[] readArgumentNamesConstant(Class<?> iface) {
|
||||||
|
@ -65,7 +65,9 @@ public final class WriterConstants {
|
|||||||
public static final Type STACK_OVERFLOW_ERROR_TYPE = Type.getType(StackOverflowError.class);
|
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 EXCEPTION_TYPE = Type.getType(Exception.class);
|
||||||
public static final Type PAINLESS_EXPLAIN_ERROR_TYPE = Type.getType(PainlessExplainError.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");
|
public static final Method PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD = getAsmMethod(Map.class, "getHeaders", Definition.class);
|
||||||
|
|
||||||
|
public static final Type DEFINITION_TYPE = Type.getType(Definition.class);
|
||||||
|
|
||||||
public static final Type COLLECTIONS_TYPE = Type.getType(Collections.class);
|
public static final Type COLLECTIONS_TYPE = Type.getType(Collections.class);
|
||||||
public static final Method EMPTY_MAP_METHOD = getAsmMethod(Map.class, "emptyMap");
|
public static final Method EMPTY_MAP_METHOD = getAsmMethod(Map.class, "emptyMap");
|
||||||
@ -83,6 +85,8 @@ public final class WriterConstants {
|
|||||||
public static final Method STRING_TO_CHAR = getAsmMethod(char.class, "StringTochar", String.class);
|
public static final Method STRING_TO_CHAR = getAsmMethod(char.class, "StringTochar", String.class);
|
||||||
public static final Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class);
|
public static final Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class);
|
||||||
|
|
||||||
|
public static final Type OBJECT_ARRAY_TYPE = Type.getType("[Ljava/lang/Object;");
|
||||||
|
|
||||||
public static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
|
public static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);
|
||||||
|
|
||||||
public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);
|
public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);
|
||||||
@ -98,13 +102,14 @@ public final class WriterConstants {
|
|||||||
public static final Method MATCHER_MATCHES = getAsmMethod(boolean.class, "matches");
|
public static final Method MATCHER_MATCHES = getAsmMethod(boolean.class, "matches");
|
||||||
public static final Method MATCHER_FIND = getAsmMethod(boolean.class, "find");
|
public static final Method MATCHER_FIND = getAsmMethod(boolean.class, "find");
|
||||||
|
|
||||||
/** dynamic callsite bootstrap signature */
|
public static final Method DEF_BOOTSTRAP_METHOD = getAsmMethod(CallSite.class, "$bootstrapDef", MethodHandles.Lookup.class,
|
||||||
static final MethodType DEF_BOOTSTRAP_TYPE =
|
String.class, MethodType.class, int.class, int.class, Object[].class);
|
||||||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class,
|
static final Handle DEF_BOOTSTRAP_HANDLE = new Handle(Opcodes.H_INVOKESTATIC, CLASS_TYPE.getInternalName(), "$bootstrapDef",
|
||||||
int.class, int.class, Object[].class);
|
DEF_BOOTSTRAP_METHOD.getDescriptor(), false);
|
||||||
static final Handle DEF_BOOTSTRAP_HANDLE =
|
public static final Type DEF_BOOTSTRAP_DELEGATE_TYPE = Type.getType(DefBootstrap.class);
|
||||||
new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(DefBootstrap.class),
|
public static final Method DEF_BOOTSTRAP_DELEGATE_METHOD = getAsmMethod(CallSite.class, "bootstrap", Definition.class,
|
||||||
"bootstrap", DEF_BOOTSTRAP_TYPE.toMethodDescriptorString(), false);
|
MethodHandles.Lookup.class, String.class, MethodType.class, int.class, int.class, Object[].class);
|
||||||
|
|
||||||
|
|
||||||
public static final Type DEF_UTIL_TYPE = Type.getType(Def.class);
|
public static final Type DEF_UTIL_TYPE = Type.getType(Def.class);
|
||||||
public static final Method DEF_TO_BOOLEAN = getAsmMethod(boolean.class, "DefToboolean" , Object.class);
|
public static final Method DEF_TO_BOOLEAN = getAsmMethod(boolean.class, "DefToboolean" , Object.class);
|
||||||
|
@ -41,13 +41,16 @@ import org.elasticsearch.painless.Location;
|
|||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
final class EnhancedPainlessLexer extends PainlessLexer {
|
final class EnhancedPainlessLexer extends PainlessLexer {
|
||||||
final String sourceName;
|
private final String sourceName;
|
||||||
|
private final Definition definition;
|
||||||
|
|
||||||
private Token stashedNext = null;
|
private Token stashedNext = null;
|
||||||
private Token previous = null;
|
private Token previous = null;
|
||||||
|
|
||||||
EnhancedPainlessLexer(CharStream charStream, String sourceName) {
|
EnhancedPainlessLexer(CharStream charStream, String sourceName, Definition definition) {
|
||||||
super(charStream);
|
super(charStream);
|
||||||
this.sourceName = sourceName;
|
this.sourceName = sourceName;
|
||||||
|
this.definition = definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Token getPreviousToken() {
|
public Token getPreviousToken() {
|
||||||
@ -93,7 +96,7 @@ final class EnhancedPainlessLexer extends PainlessLexer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isSimpleType(String name) {
|
protected boolean isSimpleType(String name) {
|
||||||
return Definition.isSimpleType(name);
|
return definition.isSimpleType(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,10 +29,11 @@ import org.antlr.v4.runtime.Recognizer;
|
|||||||
import org.antlr.v4.runtime.atn.PredictionMode;
|
import org.antlr.v4.runtime.atn.PredictionMode;
|
||||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
import org.elasticsearch.painless.CompilerSettings;
|
import org.elasticsearch.painless.CompilerSettings;
|
||||||
|
import org.elasticsearch.painless.Definition;
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
import org.elasticsearch.painless.Location;
|
import org.elasticsearch.painless.Location;
|
||||||
import org.elasticsearch.painless.ScriptInterface;
|
|
||||||
import org.elasticsearch.painless.Operation;
|
import org.elasticsearch.painless.Operation;
|
||||||
|
import org.elasticsearch.painless.ScriptInterface;
|
||||||
import org.elasticsearch.painless.antlr.PainlessParser.AfterthoughtContext;
|
import org.elasticsearch.painless.antlr.PainlessParser.AfterthoughtContext;
|
||||||
import org.elasticsearch.painless.antlr.PainlessParser.ArgumentContext;
|
import org.elasticsearch.painless.antlr.PainlessParser.ArgumentContext;
|
||||||
import org.elasticsearch.painless.antlr.PainlessParser.ArgumentsContext;
|
import org.elasticsearch.painless.antlr.PainlessParser.ArgumentsContext;
|
||||||
@ -173,9 +174,11 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public final class Walker extends PainlessParserBaseVisitor<ANode> {
|
public final class Walker extends PainlessParserBaseVisitor<ANode> {
|
||||||
|
|
||||||
public static SSource buildPainlessTree(ScriptInterface mainMethod, String sourceName, String sourceText, CompilerSettings settings,
|
public static SSource buildPainlessTree(ScriptInterface mainMethod, String sourceName,
|
||||||
|
String sourceText, CompilerSettings settings, Definition definition,
|
||||||
Printer debugStream) {
|
Printer debugStream) {
|
||||||
return new Walker(mainMethod, sourceName, sourceText, settings, debugStream).source;
|
return new Walker(mainMethod, sourceName, sourceText, settings, definition,
|
||||||
|
debugStream).source;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ScriptInterface scriptInterface;
|
private final ScriptInterface scriptInterface;
|
||||||
@ -184,24 +187,27 @@ public final class Walker extends PainlessParserBaseVisitor<ANode> {
|
|||||||
private final Printer debugStream;
|
private final Printer debugStream;
|
||||||
private final String sourceName;
|
private final String sourceName;
|
||||||
private final String sourceText;
|
private final String sourceText;
|
||||||
|
private final Definition definition;
|
||||||
|
|
||||||
private final Deque<Reserved> reserved = new ArrayDeque<>();
|
private final Deque<Reserved> reserved = new ArrayDeque<>();
|
||||||
private final Globals globals;
|
private final Globals globals;
|
||||||
private int syntheticCounter = 0;
|
private int syntheticCounter = 0;
|
||||||
|
|
||||||
private Walker(ScriptInterface scriptInterface, String sourceName, String sourceText, CompilerSettings settings, Printer debugStream) {
|
private Walker(ScriptInterface scriptInterface, String sourceName, String sourceText,
|
||||||
|
CompilerSettings settings, Definition definition, Printer debugStream) {
|
||||||
this.scriptInterface = scriptInterface;
|
this.scriptInterface = scriptInterface;
|
||||||
this.debugStream = debugStream;
|
this.debugStream = debugStream;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.sourceName = Location.computeSourceName(sourceName, sourceText);
|
this.sourceName = Location.computeSourceName(sourceName, sourceText);
|
||||||
this.sourceText = sourceText;
|
this.sourceText = sourceText;
|
||||||
this.globals = new Globals(new BitSet(sourceText.length()));
|
this.globals = new Globals(new BitSet(sourceText.length()));
|
||||||
|
this.definition = definition;
|
||||||
this.source = (SSource)visit(buildAntlrTree(sourceText));
|
this.source = (SSource)visit(buildAntlrTree(sourceText));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SourceContext buildAntlrTree(String source) {
|
private SourceContext buildAntlrTree(String source) {
|
||||||
ANTLRInputStream stream = new ANTLRInputStream(source);
|
ANTLRInputStream stream = new ANTLRInputStream(source);
|
||||||
PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName);
|
PainlessLexer lexer = new EnhancedPainlessLexer(stream, sourceName, definition);
|
||||||
PainlessParser parser = new PainlessParser(new CommonTokenStream(lexer));
|
PainlessParser parser = new PainlessParser(new CommonTokenStream(lexer));
|
||||||
ParserErrorStrategy strategy = new ParserErrorStrategy(sourceName);
|
ParserErrorStrategy strategy = new ParserErrorStrategy(sourceName);
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void analyze(Locals variables) {
|
void analyze(Locals locals) {
|
||||||
captured = variables.getVariable(location, variable);
|
captured = locals.getVariable(location, variable);
|
||||||
if (expected == null) {
|
if (expected == null) {
|
||||||
if (captured.type.sort == Definition.Sort.DEF) {
|
if (captured.type.sort == Definition.Sort.DEF) {
|
||||||
// dynamic implementation
|
// dynamic implementation
|
||||||
@ -70,13 +70,13 @@ public final class ECapturingFunctionRef extends AExpression implements ILambda
|
|||||||
// typed implementation
|
// typed implementation
|
||||||
defPointer = "S" + captured.type.name + "." + call + ",1";
|
defPointer = "S" + captured.type.name + "." + call + ",1";
|
||||||
}
|
}
|
||||||
actual = Definition.getType("String");
|
actual = locals.getDefinition().getType("String");
|
||||||
} else {
|
} else {
|
||||||
defPointer = null;
|
defPointer = null;
|
||||||
// static case
|
// static case
|
||||||
if (captured.type.sort != Definition.Sort.DEF) {
|
if (captured.type.sort != Definition.Sort.DEF) {
|
||||||
try {
|
try {
|
||||||
ref = new FunctionRef(expected, captured.type.name, call, 1);
|
ref = new FunctionRef(locals.getDefinition(), expected, captured.type.name, call, 1);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw createError(e);
|
throw createError(e);
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Definition;
|
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
import org.elasticsearch.painless.Location;
|
|
||||||
import org.elasticsearch.painless.Locals;
|
import org.elasticsearch.painless.Locals;
|
||||||
|
import org.elasticsearch.painless.Location;
|
||||||
import org.elasticsearch.painless.MethodWriter;
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -51,7 +50,7 @@ public final class EExplicit extends AExpression {
|
|||||||
@Override
|
@Override
|
||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
try {
|
try {
|
||||||
actual = Definition.getType(this.type);
|
actual = locals.getDefinition().getType(type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
if (expected == null) {
|
if (expected == null) {
|
||||||
ref = null;
|
ref = null;
|
||||||
actual = Definition.getType("String");
|
actual = locals.getDefinition().getType("String");
|
||||||
defPointer = "S" + type + "." + call + ",0";
|
defPointer = "S" + type + "." + call + ",0";
|
||||||
} else {
|
} else {
|
||||||
defPointer = null;
|
defPointer = null;
|
||||||
@ -79,7 +79,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||||||
ref = new FunctionRef(expected, interfaceMethod, implMethod, 0);
|
ref = new FunctionRef(expected, interfaceMethod, implMethod, 0);
|
||||||
} else {
|
} else {
|
||||||
// whitelist lookup
|
// whitelist lookup
|
||||||
ref = new FunctionRef(expected, type, call, 0);
|
ref = new FunctionRef(locals.getDefinition(), expected, type, call, 0);
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw createError(e);
|
throw createError(e);
|
||||||
|
@ -59,7 +59,7 @@ public final class EInstanceof extends AExpression {
|
|||||||
|
|
||||||
// ensure the specified type is part of the definition
|
// ensure the specified type is part of the definition
|
||||||
try {
|
try {
|
||||||
type = Definition.getType(this.type);
|
type = locals.getDefinition().getType(this.type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -164,14 +164,14 @@ public final class ELambda extends AExpression implements ILambda {
|
|||||||
// desugar lambda body into a synthetic method
|
// desugar lambda body into a synthetic method
|
||||||
desugared = new SFunction(reserved, location, returnType.name, name,
|
desugared = new SFunction(reserved, location, returnType.name, name,
|
||||||
paramTypes, paramNames, statements, true);
|
paramTypes, paramNames, statements, true);
|
||||||
desugared.generateSignature();
|
desugared.generateSignature(locals.getDefinition());
|
||||||
desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType, desugared.parameters,
|
desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType, desugared.parameters,
|
||||||
captures.size(), reserved.getMaxLoopCounter()));
|
captures.size(), reserved.getMaxLoopCounter()));
|
||||||
|
|
||||||
// setup method reference to synthetic method
|
// setup method reference to synthetic method
|
||||||
if (expected == null) {
|
if (expected == null) {
|
||||||
ref = null;
|
ref = null;
|
||||||
actual = Definition.getType("String");
|
actual = locals.getDefinition().getType("String");
|
||||||
defPointer = "Sthis." + name + "," + captures.size();
|
defPointer = "Sthis." + name + "," + captures.size();
|
||||||
} else {
|
} else {
|
||||||
defPointer = null;
|
defPointer = null;
|
||||||
|
@ -58,11 +58,7 @@ public final class EListInit extends AExpression {
|
|||||||
throw createError(new IllegalArgumentException("Must read from list initializer."));
|
throw createError(new IllegalArgumentException("Must read from list initializer."));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
actual = Definition.ARRAY_LIST_TYPE;
|
||||||
actual = Definition.getType("ArrayList");
|
|
||||||
} catch (IllegalArgumentException exception) {
|
|
||||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor = actual.struct.constructors.get(new MethodKey("<init>", 0));
|
constructor = actual.struct.constructors.get(new MethodKey("<init>", 0));
|
||||||
|
|
||||||
|
@ -64,11 +64,7 @@ public final class EMapInit extends AExpression {
|
|||||||
throw createError(new IllegalArgumentException("Must read from map initializer."));
|
throw createError(new IllegalArgumentException("Must read from map initializer."));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
actual = Definition.HASH_MAP_TYPE;
|
||||||
actual = Definition.getType("HashMap");
|
|
||||||
} catch (IllegalArgumentException exception) {
|
|
||||||
throw createError(new IllegalStateException("Illegal tree structure."));
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor = actual.struct.constructors.get(new MethodKey("<init>", 0));
|
constructor = actual.struct.constructors.get(new MethodKey("<init>", 0));
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public final class ENewArray extends AExpression {
|
|||||||
final Type type;
|
final Type type;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = Definition.getType(this.type);
|
type = locals.getDefinition().getType(this.type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
@ -71,13 +71,14 @@ public final class ENewArray extends AExpression {
|
|||||||
for (int argument = 0; argument < arguments.size(); ++argument) {
|
for (int argument = 0; argument < arguments.size(); ++argument) {
|
||||||
AExpression expression = arguments.get(argument);
|
AExpression expression = arguments.get(argument);
|
||||||
|
|
||||||
expression.expected = initialize ? Definition.getType(type.struct, 0) : Definition.INT_TYPE;
|
expression.expected = initialize ? locals.getDefinition().getType(type.struct, 0)
|
||||||
|
: Definition.INT_TYPE;
|
||||||
expression.internal = true;
|
expression.internal = true;
|
||||||
expression.analyze(locals);
|
expression.analyze(locals);
|
||||||
arguments.set(argument, expression.cast(locals));
|
arguments.set(argument, expression.cast(locals));
|
||||||
}
|
}
|
||||||
|
|
||||||
actual = Definition.getType(type.struct, initialize ? 1 : arguments.size());
|
actual = locals.getDefinition().getType(type.struct, initialize ? 1 : arguments.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -86,7 +87,7 @@ public final class ENewArray extends AExpression {
|
|||||||
|
|
||||||
if (initialize) {
|
if (initialize) {
|
||||||
writer.push(arguments.size());
|
writer.push(arguments.size());
|
||||||
writer.newArray(Definition.getType(actual.struct, 0).type);
|
writer.newArray(actual.struct.type);
|
||||||
|
|
||||||
for (int index = 0; index < arguments.size(); ++index) {
|
for (int index = 0; index < arguments.size(); ++index) {
|
||||||
AExpression argument = arguments.get(index);
|
AExpression argument = arguments.get(index);
|
||||||
@ -94,7 +95,7 @@ public final class ENewArray extends AExpression {
|
|||||||
writer.dup();
|
writer.dup();
|
||||||
writer.push(index);
|
writer.push(index);
|
||||||
argument.write(writer, globals);
|
argument.write(writer, globals);
|
||||||
writer.arrayStore(Definition.getType(actual.struct, 0).type);
|
writer.arrayStore(actual.struct.type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (AExpression argument : arguments) {
|
for (AExpression argument : arguments) {
|
||||||
@ -104,7 +105,7 @@ public final class ENewArray extends AExpression {
|
|||||||
if (arguments.size() > 1) {
|
if (arguments.size() > 1) {
|
||||||
writer.visitMultiANewArrayInsn(actual.type.getDescriptor(), actual.type.getDimensions());
|
writer.visitMultiANewArrayInsn(actual.type.getDescriptor(), actual.type.getDimensions());
|
||||||
} else {
|
} else {
|
||||||
writer.newArray(Definition.getType(actual.struct, 0).type);
|
writer.newArray(actual.struct.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ public final class ENewObj extends AExpression {
|
|||||||
final Type type;
|
final Type type;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = Definition.getType(this.type);
|
type = locals.getDefinition().getType(this.type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,14 @@
|
|||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Definition;
|
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
|
import org.elasticsearch.painless.Locals;
|
||||||
import org.elasticsearch.painless.Location;
|
import org.elasticsearch.painless.Location;
|
||||||
import org.elasticsearch.painless.MethodWriter;
|
import org.elasticsearch.painless.MethodWriter;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Locals;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a static type target.
|
* Represents a static type target.
|
||||||
*/
|
*/
|
||||||
@ -50,7 +48,7 @@ public final class EStatic extends AExpression {
|
|||||||
@Override
|
@Override
|
||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
try {
|
try {
|
||||||
actual = Definition.getType(type);
|
actual = locals.getDefinition().getType(type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Definition;
|
|
||||||
import org.elasticsearch.painless.Definition.Method;
|
import org.elasticsearch.painless.Definition.Method;
|
||||||
import org.elasticsearch.painless.Definition.MethodKey;
|
import org.elasticsearch.painless.Definition.MethodKey;
|
||||||
import org.elasticsearch.painless.Definition.Sort;
|
import org.elasticsearch.painless.Definition.Sort;
|
||||||
@ -74,7 +73,7 @@ public final class PCallInvoke extends AExpression {
|
|||||||
Struct struct = prefix.actual.struct;
|
Struct struct = prefix.actual.struct;
|
||||||
|
|
||||||
if (prefix.actual.sort.primitive) {
|
if (prefix.actual.sort.primitive) {
|
||||||
struct = Definition.getType(prefix.actual.sort.boxed.getSimpleName()).struct;
|
struct = locals.getDefinition().getType(prefix.actual.sort.boxed.getSimpleName()).struct;
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodKey methodKey = new MethodKey(name, arguments.size());
|
MethodKey methodKey = new MethodKey(name, arguments.size());
|
||||||
|
@ -55,7 +55,7 @@ final class PSubBrace extends AStoreable {
|
|||||||
index.analyze(locals);
|
index.analyze(locals);
|
||||||
index = index.cast(locals);
|
index = index.cast(locals);
|
||||||
|
|
||||||
actual = Definition.getType(type.struct, type.dimensions - 1);
|
actual = locals.getDefinition().getType(type.struct, type.dimensions - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Definition;
|
|
||||||
import org.elasticsearch.painless.Definition.Type;
|
import org.elasticsearch.painless.Definition.Type;
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
import org.elasticsearch.painless.Locals;
|
import org.elasticsearch.painless.Locals;
|
||||||
@ -69,7 +68,7 @@ public final class SCatch extends AStatement {
|
|||||||
final Type type;
|
final Type type;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = Definition.getType(this.type);
|
type = locals.getDefinition().getType(this.type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Definition;
|
|
||||||
import org.elasticsearch.painless.Definition.Type;
|
import org.elasticsearch.painless.Definition.Type;
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
import org.elasticsearch.painless.Locals;
|
import org.elasticsearch.painless.Locals;
|
||||||
@ -64,7 +63,7 @@ public final class SDeclaration extends AStatement {
|
|||||||
final Type type;
|
final Type type;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = Definition.getType(this.type);
|
type = locals.getDefinition().getType(this.type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.elasticsearch.painless.node;
|
package org.elasticsearch.painless.node;
|
||||||
|
|
||||||
import org.elasticsearch.painless.Definition;
|
|
||||||
import org.elasticsearch.painless.Definition.Sort;
|
import org.elasticsearch.painless.Definition.Sort;
|
||||||
import org.elasticsearch.painless.Definition.Type;
|
import org.elasticsearch.painless.Definition.Type;
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
@ -72,7 +71,7 @@ public class SEach extends AStatement {
|
|||||||
final Type type;
|
final Type type;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
type = Definition.getType(this.type);
|
type = locals.getDefinition().getType(this.type);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
throw createError(new IllegalArgumentException("Not a type [" + this.type + "]."));
|
||||||
}
|
}
|
||||||
|
@ -106,9 +106,9 @@ public final class SFunction extends AStatement {
|
|||||||
throw new IllegalStateException("Illegal tree structure");
|
throw new IllegalStateException("Illegal tree structure");
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateSignature() {
|
void generateSignature(Definition definition) {
|
||||||
try {
|
try {
|
||||||
rtnType = Definition.getType(rtnTypeStr);
|
rtnType = definition.getType(rtnTypeStr);
|
||||||
} catch (IllegalArgumentException exception) {
|
} catch (IllegalArgumentException exception) {
|
||||||
throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "]."));
|
throw createError(new IllegalArgumentException("Illegal return type [" + rtnTypeStr + "] for function [" + name + "]."));
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ public final class SFunction extends AStatement {
|
|||||||
|
|
||||||
for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
|
for (int param = 0; param < this.paramTypeStrs.size(); ++param) {
|
||||||
try {
|
try {
|
||||||
Type paramType = Definition.getType(this.paramTypeStrs.get(param));
|
Type paramType = definition.getType(this.paramTypeStrs.get(param));
|
||||||
|
|
||||||
paramClasses[param] = paramType.clazz;
|
paramClasses[param] = paramType.clazz;
|
||||||
paramTypes.add(paramType);
|
paramTypes.add(paramType);
|
||||||
|
@ -21,6 +21,7 @@ package org.elasticsearch.painless.node;
|
|||||||
|
|
||||||
import org.elasticsearch.painless.CompilerSettings;
|
import org.elasticsearch.painless.CompilerSettings;
|
||||||
import org.elasticsearch.painless.Constant;
|
import org.elasticsearch.painless.Constant;
|
||||||
|
import org.elasticsearch.painless.Definition;
|
||||||
import org.elasticsearch.painless.Definition.Method;
|
import org.elasticsearch.painless.Definition.Method;
|
||||||
import org.elasticsearch.painless.Definition.MethodKey;
|
import org.elasticsearch.painless.Definition.MethodKey;
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
@ -58,6 +59,10 @@ import static org.elasticsearch.painless.WriterConstants.CLASS_TYPE;
|
|||||||
import static org.elasticsearch.painless.WriterConstants.COLLECTIONS_TYPE;
|
import static org.elasticsearch.painless.WriterConstants.COLLECTIONS_TYPE;
|
||||||
import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR;
|
import static org.elasticsearch.painless.WriterConstants.CONSTRUCTOR;
|
||||||
import static org.elasticsearch.painless.WriterConstants.CONVERT_TO_SCRIPT_EXCEPTION_METHOD;
|
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;
|
||||||
|
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_DELEGATE_TYPE;
|
||||||
|
import static org.elasticsearch.painless.WriterConstants.DEF_BOOTSTRAP_METHOD;
|
||||||
import static org.elasticsearch.painless.WriterConstants.EMPTY_MAP_METHOD;
|
import static org.elasticsearch.painless.WriterConstants.EMPTY_MAP_METHOD;
|
||||||
import static org.elasticsearch.painless.WriterConstants.EXCEPTION_TYPE;
|
import static org.elasticsearch.painless.WriterConstants.EXCEPTION_TYPE;
|
||||||
import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE;
|
import static org.elasticsearch.painless.WriterConstants.OUT_OF_MEMORY_ERROR_TYPE;
|
||||||
@ -145,11 +150,11 @@ public final class SSource extends AStatement {
|
|||||||
throw new IllegalStateException("Illegal tree structure.");
|
throw new IllegalStateException("Illegal tree structure.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void analyze() {
|
public void analyze(Definition definition) {
|
||||||
Map<MethodKey, Method> methods = new HashMap<>();
|
Map<MethodKey, Method> methods = new HashMap<>();
|
||||||
|
|
||||||
for (SFunction function : functions) {
|
for (SFunction function : functions) {
|
||||||
function.generateSignature();
|
function.generateSignature(definition);
|
||||||
|
|
||||||
MethodKey key = new MethodKey(function.name, function.parameters.size());
|
MethodKey key = new MethodKey(function.name, function.parameters.size());
|
||||||
|
|
||||||
@ -158,7 +163,7 @@ public final class SSource extends AStatement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analyze(Locals.newProgramScope(methods.values()));
|
analyze(Locals.newProgramScope(definition, methods.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -216,6 +221,19 @@ public final class SSource extends AStatement {
|
|||||||
visitor.visit(WriterConstants.CLASS_VERSION, classAccess, className, null, classBase, classInterfaces);
|
visitor.visit(WriterConstants.CLASS_VERSION, classAccess, className, null, classBase, classInterfaces);
|
||||||
visitor.visitSource(Location.computeSourceName(name, source), null);
|
visitor.visitSource(Location.computeSourceName(name, source), null);
|
||||||
|
|
||||||
|
// Write the a method to bootstrap def calls
|
||||||
|
MethodWriter bootstrapDef = new MethodWriter(Opcodes.ACC_STATIC | Opcodes.ACC_VARARGS, DEF_BOOTSTRAP_METHOD, visitor,
|
||||||
|
globals.getStatements(), settings);
|
||||||
|
bootstrapDef.visitCode();
|
||||||
|
bootstrapDef.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE);
|
||||||
|
bootstrapDef.loadArgs();
|
||||||
|
bootstrapDef.invokeStatic(DEF_BOOTSTRAP_DELEGATE_TYPE, DEF_BOOTSTRAP_DELEGATE_METHOD);
|
||||||
|
bootstrapDef.returnValue();
|
||||||
|
bootstrapDef.endMethod();
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
// Write the constructor:
|
// Write the constructor:
|
||||||
MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, visitor, globals.getStatements(), settings);
|
MethodWriter constructor = new MethodWriter(Opcodes.ACC_PUBLIC, CONSTRUCTOR, visitor, globals.getStatements(), settings);
|
||||||
constructor.visitCode();
|
constructor.visitCode();
|
||||||
@ -330,13 +348,14 @@ public final class SSource extends AStatement {
|
|||||||
writer.goTo(endCatch);
|
writer.goTo(endCatch);
|
||||||
// This looks like:
|
// This looks like:
|
||||||
// } catch (PainlessExplainError e) {
|
// } catch (PainlessExplainError e) {
|
||||||
// throw this.convertToScriptException(e, e.getHeaders())
|
// throw this.convertToScriptException(e, e.getHeaders($DEFINITION))
|
||||||
// }
|
// }
|
||||||
writer.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName());
|
writer.visitTryCatchBlock(startTry, endTry, startExplainCatch, PAINLESS_EXPLAIN_ERROR_TYPE.getInternalName());
|
||||||
writer.mark(startExplainCatch);
|
writer.mark(startExplainCatch);
|
||||||
writer.loadThis();
|
writer.loadThis();
|
||||||
writer.swap();
|
writer.swap();
|
||||||
writer.dup();
|
writer.dup();
|
||||||
|
writer.getStatic(CLASS_TYPE, "$DEFINITION", DEFINITION_TYPE);
|
||||||
writer.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD);
|
writer.invokeVirtual(PAINLESS_EXPLAIN_ERROR_TYPE, PAINLESS_EXPLAIN_ERROR_GET_HEADERS_METHOD);
|
||||||
writer.invokeVirtual(BASE_CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD);
|
writer.invokeVirtual(BASE_CLASS_TYPE, CONVERT_TO_SCRIPT_EXCEPTION_METHOD);
|
||||||
writer.throwException();
|
writer.throwException();
|
||||||
|
@ -64,9 +64,12 @@ final class SSubEachArray extends AStatement {
|
|||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
// We must store the array and index as variables for securing slots on the stack, and
|
// We must store the array and index as variables for securing slots on the stack, and
|
||||||
// also add the location offset to make the names unique in case of nested for each loops.
|
// also add the location offset to make the names unique in case of nested for each loops.
|
||||||
array = locals.addVariable(location, expression.actual, "#array" + location.getOffset(), true);
|
array = locals.addVariable(location, expression.actual, "#array" + location.getOffset(),
|
||||||
index = locals.addVariable(location, Definition.INT_TYPE, "#index" + location.getOffset(), true);
|
true);
|
||||||
indexed = Definition.getType(expression.actual.struct, expression.actual.dimensions - 1);
|
index = locals.addVariable(location, Definition.INT_TYPE, "#index" + location.getOffset(),
|
||||||
|
true);
|
||||||
|
indexed = locals.getDefinition().getType(expression.actual.struct,
|
||||||
|
expression.actual.dimensions - 1);
|
||||||
cast = AnalyzerCaster.getLegalCast(location, indexed, variable.type, true, true);
|
cast = AnalyzerCaster.getLegalCast(location, indexed, variable.type, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import org.elasticsearch.painless.Definition.Cast;
|
|||||||
import org.elasticsearch.painless.Definition.Method;
|
import org.elasticsearch.painless.Definition.Method;
|
||||||
import org.elasticsearch.painless.Definition.MethodKey;
|
import org.elasticsearch.painless.Definition.MethodKey;
|
||||||
import org.elasticsearch.painless.Definition.Sort;
|
import org.elasticsearch.painless.Definition.Sort;
|
||||||
import org.elasticsearch.painless.Definition.Type;
|
|
||||||
import org.elasticsearch.painless.Globals;
|
import org.elasticsearch.painless.Globals;
|
||||||
import org.elasticsearch.painless.Locals;
|
import org.elasticsearch.painless.Locals;
|
||||||
import org.elasticsearch.painless.Locals.Variable;
|
import org.elasticsearch.painless.Locals.Variable;
|
||||||
@ -72,7 +71,8 @@ final class SSubEachIterable extends AStatement {
|
|||||||
void analyze(Locals locals) {
|
void analyze(Locals locals) {
|
||||||
// We must store the iterator as a variable for securing a slot on the stack, and
|
// We must store the iterator as a variable for securing a slot on the stack, and
|
||||||
// also add the location offset to make the name unique in case of nested for each loops.
|
// also add the location offset to make the name unique in case of nested for each loops.
|
||||||
iterator = locals.addVariable(location, Definition.getType("Iterator"), "#itr" + location.getOffset(), true);
|
iterator = locals.addVariable(location, locals.getDefinition().getType("Iterator"),
|
||||||
|
"#itr" + location.getOffset(), true);
|
||||||
|
|
||||||
if (expression.actual.sort == Sort.DEF) {
|
if (expression.actual.sort == Sort.DEF) {
|
||||||
method = null;
|
method = null;
|
||||||
@ -95,8 +95,8 @@ final class SSubEachIterable extends AStatement {
|
|||||||
expression.write(writer, globals);
|
expression.write(writer, globals);
|
||||||
|
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
Type itr = Definition.getType("Iterator");
|
org.objectweb.asm.Type methodType = org.objectweb.asm.Type
|
||||||
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(itr.type, Definition.DEF_TYPE.type);
|
.getMethodType(Definition.ITERATOR_TYPE.type, Definition.DEF_TYPE.type);
|
||||||
writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
|
writer.invokeDefCall("iterator", methodType, DefBootstrap.ITERATOR);
|
||||||
} else {
|
} else {
|
||||||
method.write(writer);
|
method.write(writer);
|
||||||
|
@ -34,22 +34,24 @@ import static org.hamcrest.Matchers.hasKey;
|
|||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
public class DebugTests extends ScriptTestCase {
|
public class DebugTests extends ScriptTestCase {
|
||||||
|
private final Definition definition = Definition.BUILTINS;
|
||||||
|
|
||||||
public void testExplain() {
|
public void testExplain() {
|
||||||
// Debug.explain can explain an object
|
// Debug.explain can explain an object
|
||||||
Object dummy = new Object();
|
Object dummy = new Object();
|
||||||
PainlessExplainError e = expectScriptThrows(PainlessExplainError.class, () -> exec(
|
PainlessExplainError e = expectScriptThrows(PainlessExplainError.class, () -> exec(
|
||||||
"Debug.explain(params.a)", singletonMap("a", dummy), true));
|
"Debug.explain(params.a)", singletonMap("a", dummy), true));
|
||||||
assertSame(dummy, e.getObjectToExplain());
|
assertSame(dummy, e.getObjectToExplain());
|
||||||
assertThat(e.getHeaders(), hasEntry("es.to_string", singletonList(dummy.toString())));
|
assertThat(e.getHeaders(definition), hasEntry("es.to_string", singletonList(dummy.toString())));
|
||||||
assertThat(e.getHeaders(), hasEntry("es.java_class", singletonList("java.lang.Object")));
|
assertThat(e.getHeaders(definition), hasEntry("es.java_class", singletonList("java.lang.Object")));
|
||||||
assertThat(e.getHeaders(), hasEntry("es.painless_class", singletonList("Object")));
|
assertThat(e.getHeaders(definition), hasEntry("es.painless_class", singletonList("Object")));
|
||||||
|
|
||||||
// Null should be ok
|
// Null should be ok
|
||||||
e = expectScriptThrows(PainlessExplainError.class, () -> exec("Debug.explain(null)"));
|
e = expectScriptThrows(PainlessExplainError.class, () -> exec("Debug.explain(null)"));
|
||||||
assertNull(e.getObjectToExplain());
|
assertNull(e.getObjectToExplain());
|
||||||
assertThat(e.getHeaders(), hasEntry("es.to_string", singletonList("null")));
|
assertThat(e.getHeaders(definition), hasEntry("es.to_string", singletonList("null")));
|
||||||
assertThat(e.getHeaders(), not(hasKey("es.java_class")));
|
assertThat(e.getHeaders(definition), not(hasKey("es.java_class")));
|
||||||
assertThat(e.getHeaders(), not(hasKey("es.painless_class")));
|
assertThat(e.getHeaders(definition), not(hasKey("es.painless_class")));
|
||||||
|
|
||||||
// You can't catch the explain exception
|
// You can't catch the explain exception
|
||||||
e = expectScriptThrows(PainlessExplainError.class, () -> exec(
|
e = expectScriptThrows(PainlessExplainError.class, () -> exec(
|
||||||
|
@ -30,14 +30,17 @@ import java.util.HashMap;
|
|||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
public class DefBootstrapTests extends ESTestCase {
|
public class DefBootstrapTests extends ESTestCase {
|
||||||
|
private final Definition definition = Definition.BUILTINS;
|
||||||
|
|
||||||
/** calls toString() on integers, twice */
|
/** calls toString() on integers, twice */
|
||||||
public void testOneType() throws Throwable {
|
public void testOneType() throws Throwable {
|
||||||
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
CallSite site = DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"toString",
|
"toString",
|
||||||
MethodType.methodType(String.class, Object.class),
|
MethodType.methodType(String.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.METHOD_CALL, "");
|
DefBootstrap.METHOD_CALL,
|
||||||
|
"");
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertDepthEquals(site, 0);
|
assertDepthEquals(site, 0);
|
||||||
|
|
||||||
@ -51,11 +54,13 @@ public class DefBootstrapTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testTwoTypes() throws Throwable {
|
public void testTwoTypes() throws Throwable {
|
||||||
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
CallSite site = DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"toString",
|
"toString",
|
||||||
MethodType.methodType(String.class, Object.class),
|
MethodType.methodType(String.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.METHOD_CALL, "");
|
DefBootstrap.METHOD_CALL,
|
||||||
|
"");
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertDepthEquals(site, 0);
|
assertDepthEquals(site, 0);
|
||||||
|
|
||||||
@ -74,11 +79,13 @@ public class DefBootstrapTests extends ESTestCase {
|
|||||||
public void testTooManyTypes() throws Throwable {
|
public void testTooManyTypes() throws Throwable {
|
||||||
// if this changes, test must be rewritten
|
// if this changes, test must be rewritten
|
||||||
assertEquals(5, DefBootstrap.PIC.MAX_DEPTH);
|
assertEquals(5, DefBootstrap.PIC.MAX_DEPTH);
|
||||||
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
CallSite site = DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"toString",
|
"toString",
|
||||||
MethodType.methodType(String.class, Object.class),
|
MethodType.methodType(String.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.METHOD_CALL, "");
|
DefBootstrap.METHOD_CALL,
|
||||||
|
"");
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertDepthEquals(site, 0);
|
assertDepthEquals(site, 0);
|
||||||
|
|
||||||
@ -98,11 +105,13 @@ public class DefBootstrapTests extends ESTestCase {
|
|||||||
|
|
||||||
/** test that we revert to the megamorphic classvalue cache and that it works as expected */
|
/** test that we revert to the megamorphic classvalue cache and that it works as expected */
|
||||||
public void testMegamorphic() throws Throwable {
|
public void testMegamorphic() throws Throwable {
|
||||||
DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"size",
|
"size",
|
||||||
MethodType.methodType(int.class, Object.class),
|
MethodType.methodType(int.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.METHOD_CALL, "");
|
DefBootstrap.METHOD_CALL,
|
||||||
|
"");
|
||||||
site.depth = DefBootstrap.PIC.MAX_DEPTH; // mark megamorphic
|
site.depth = DefBootstrap.PIC.MAX_DEPTH; // mark megamorphic
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals(2, (int)handle.invokeExact((Object) Arrays.asList("1", "2")));
|
assertEquals(2, (int)handle.invokeExact((Object) Arrays.asList("1", "2")));
|
||||||
@ -128,43 +137,51 @@ public class DefBootstrapTests extends ESTestCase {
|
|||||||
// test operators with null guards
|
// test operators with null guards
|
||||||
|
|
||||||
public void testNullGuardAdd() throws Throwable {
|
public void testNullGuardAdd() throws Throwable {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, Object.class, Object.class),
|
MethodType.methodType(Object.class, Object.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR,
|
||||||
|
DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals("nulltest", (Object)handle.invokeExact((Object)null, (Object)"test"));
|
assertEquals("nulltest", (Object)handle.invokeExact((Object)null, (Object)"test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullGuardAddWhenCached() throws Throwable {
|
public void testNullGuardAddWhenCached() throws Throwable {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, Object.class, Object.class),
|
MethodType.methodType(Object.class, Object.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR,
|
||||||
|
DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals(2, (Object)handle.invokeExact((Object)1, (Object)1));
|
assertEquals(2, (Object)handle.invokeExact((Object)1, (Object)1));
|
||||||
assertEquals("nulltest", (Object)handle.invokeExact((Object)null, (Object)"test"));
|
assertEquals("nulltest", (Object)handle.invokeExact((Object)null, (Object)"test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullGuardEq() throws Throwable {
|
public void testNullGuardEq() throws Throwable {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"eq",
|
"eq",
|
||||||
MethodType.methodType(boolean.class, Object.class, Object.class),
|
MethodType.methodType(boolean.class, Object.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR,
|
||||||
|
DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertFalse((boolean) handle.invokeExact((Object)null, (Object)"test"));
|
assertFalse((boolean) handle.invokeExact((Object)null, (Object)"test"));
|
||||||
assertTrue((boolean) handle.invokeExact((Object)null, (Object)null));
|
assertTrue((boolean) handle.invokeExact((Object)null, (Object)null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullGuardEqWhenCached() throws Throwable {
|
public void testNullGuardEqWhenCached() throws Throwable {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"eq",
|
"eq",
|
||||||
MethodType.methodType(boolean.class, Object.class, Object.class),
|
MethodType.methodType(boolean.class, Object.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, DefBootstrap.OPERATOR_ALLOWS_NULL);
|
DefBootstrap.BINARY_OPERATOR,
|
||||||
|
DefBootstrap.OPERATOR_ALLOWS_NULL);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertTrue((boolean) handle.invokeExact((Object)1, (Object)1));
|
assertTrue((boolean) handle.invokeExact((Object)1, (Object)1));
|
||||||
assertFalse((boolean) handle.invokeExact((Object)null, (Object)"test"));
|
assertFalse((boolean) handle.invokeExact((Object)null, (Object)"test"));
|
||||||
@ -176,11 +193,13 @@ public class DefBootstrapTests extends ESTestCase {
|
|||||||
// and can be disabled in some circumstances.
|
// and can be disabled in some circumstances.
|
||||||
|
|
||||||
public void testNoNullGuardAdd() throws Throwable {
|
public void testNoNullGuardAdd() throws Throwable {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, int.class, Object.class),
|
MethodType.methodType(Object.class, int.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, 0);
|
DefBootstrap.BINARY_OPERATOR,
|
||||||
|
0);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
expectThrows(NullPointerException.class, () -> {
|
expectThrows(NullPointerException.class, () -> {
|
||||||
assertNotNull((Object)handle.invokeExact(5, (Object)null));
|
assertNotNull((Object)handle.invokeExact(5, (Object)null));
|
||||||
@ -188,11 +207,13 @@ public class DefBootstrapTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testNoNullGuardAddWhenCached() throws Throwable {
|
public void testNoNullGuardAddWhenCached() throws Throwable {
|
||||||
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
|
DefBootstrap.MIC site = (DefBootstrap.MIC) DefBootstrap.bootstrap(definition,
|
||||||
|
MethodHandles.publicLookup(),
|
||||||
"add",
|
"add",
|
||||||
MethodType.methodType(Object.class, int.class, Object.class),
|
MethodType.methodType(Object.class, int.class, Object.class),
|
||||||
0,
|
0,
|
||||||
DefBootstrap.BINARY_OPERATOR, 0);
|
DefBootstrap.BINARY_OPERATOR,
|
||||||
|
0);
|
||||||
MethodHandle handle = site.dynamicInvoker();
|
MethodHandle handle = site.dynamicInvoker();
|
||||||
assertEquals(2, (Object)handle.invokeExact(1, (Object)1));
|
assertEquals(2, (Object)handle.invokeExact(1, (Object)1));
|
||||||
expectThrows(NullPointerException.class, () -> {
|
expectThrows(NullPointerException.class, () -> {
|
||||||
|
@ -76,11 +76,13 @@ public abstract class ScriptTestCase extends ESTestCase {
|
|||||||
public Object exec(String script, Map<String, Object> vars, Map<String,String> compileParams, Scorer scorer, boolean picky) {
|
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
|
// test for ambiguity errors before running the actual script if picky is true
|
||||||
if (picky) {
|
if (picky) {
|
||||||
ScriptInterface scriptInterface = new ScriptInterface(GenericElasticsearchScript.class);
|
Definition definition = Definition.BUILTINS;
|
||||||
|
ScriptInterface scriptInterface = new ScriptInterface(definition, GenericElasticsearchScript.class);
|
||||||
CompilerSettings pickySettings = new CompilerSettings();
|
CompilerSettings pickySettings = new CompilerSettings();
|
||||||
pickySettings.setPicky(true);
|
pickySettings.setPicky(true);
|
||||||
pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(scriptEngineSettings()));
|
pickySettings.setRegexesEnabled(CompilerSettings.REGEX_ENABLED.get(scriptEngineSettings()));
|
||||||
Walker.buildPainlessTree(scriptInterface, getTestName(), script, pickySettings, null);
|
Walker.buildPainlessTree(scriptInterface, getTestName(), script, pickySettings,
|
||||||
|
definition, null);
|
||||||
}
|
}
|
||||||
// test actual script execution
|
// test actual script execution
|
||||||
Object object = scriptEngine.compile(null, script, compileParams);
|
Object object = scriptEngine.compile(null, script, compileParams);
|
||||||
|
@ -47,6 +47,8 @@ import static java.util.Collections.singletonList;
|
|||||||
* Tests {@link Object#toString} implementations on all extensions of {@link ANode}.
|
* Tests {@link Object#toString} implementations on all extensions of {@link ANode}.
|
||||||
*/
|
*/
|
||||||
public class NodeToStringTests extends ESTestCase {
|
public class NodeToStringTests extends ESTestCase {
|
||||||
|
private final Definition definition = Definition.BUILTINS;
|
||||||
|
|
||||||
public void testEAssignment() {
|
public void testEAssignment() {
|
||||||
assertToString(
|
assertToString(
|
||||||
"(SSource\n"
|
"(SSource\n"
|
||||||
@ -399,7 +401,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testPSubCallInvoke() {
|
public void testPSubCallInvoke() {
|
||||||
Location l = new Location(getTestName(), 0);
|
Location l = new Location(getTestName(), 0);
|
||||||
RuntimeClass c = Definition.getRuntimeClass(Integer.class);
|
RuntimeClass c = definition.getRuntimeClass(Integer.class);
|
||||||
Method m = c.methods.get(new MethodKey("toString", 0));
|
Method m = c.methods.get(new MethodKey("toString", 0));
|
||||||
PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList());
|
PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList());
|
||||||
node.prefix = new EVariable(l, "a");
|
node.prefix = new EVariable(l, "a");
|
||||||
@ -454,7 +456,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testPSubField() {
|
public void testPSubField() {
|
||||||
Location l = new Location(getTestName(), 0);
|
Location l = new Location(getTestName(), 0);
|
||||||
Struct s = Definition.getType(Boolean.class.getSimpleName()).struct;
|
Struct s = definition.getType(Boolean.class.getSimpleName()).struct;
|
||||||
Field f = s.staticMembers.get("TRUE");
|
Field f = s.staticMembers.get("TRUE");
|
||||||
PSubField node = new PSubField(l, f);
|
PSubField node = new PSubField(l, f);
|
||||||
node.prefix = new EStatic(l, "Boolean");
|
node.prefix = new EStatic(l, "Boolean");
|
||||||
@ -464,7 +466,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testPSubListShortcut() {
|
public void testPSubListShortcut() {
|
||||||
Location l = new Location(getTestName(), 0);
|
Location l = new Location(getTestName(), 0);
|
||||||
Struct s = Definition.getType(List.class.getSimpleName()).struct;
|
Struct s = definition.getType(List.class.getSimpleName()).struct;
|
||||||
PSubListShortcut node = new PSubListShortcut(l, s, new EConstant(l, 1));
|
PSubListShortcut node = new PSubListShortcut(l, s, new EConstant(l, 1));
|
||||||
node.prefix = new EVariable(l, "a");
|
node.prefix = new EVariable(l, "a");
|
||||||
assertEquals("(PSubListShortcut (EVariable a) (EConstant Integer 1))", node.toString());
|
assertEquals("(PSubListShortcut (EVariable a) (EConstant Integer 1))", node.toString());
|
||||||
@ -472,7 +474,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
new PSubNullSafeCallInvoke(l, node).toString());
|
new PSubNullSafeCallInvoke(l, node).toString());
|
||||||
|
|
||||||
l = new Location(getTestName(), 0);
|
l = new Location(getTestName(), 0);
|
||||||
s = Definition.getType(List.class.getSimpleName()).struct;
|
s = definition.getType(List.class.getSimpleName()).struct;
|
||||||
node = new PSubListShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
|
node = new PSubListShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
|
||||||
node.prefix = new EVariable(l, "a");
|
node.prefix = new EVariable(l, "a");
|
||||||
assertEquals("(PSubListShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
|
assertEquals("(PSubListShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
|
||||||
@ -480,7 +482,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testPSubMapShortcut() {
|
public void testPSubMapShortcut() {
|
||||||
Location l = new Location(getTestName(), 0);
|
Location l = new Location(getTestName(), 0);
|
||||||
Struct s = Definition.getType(Map.class.getSimpleName()).struct;
|
Struct s = definition.getType(Map.class.getSimpleName()).struct;
|
||||||
PSubMapShortcut node = new PSubMapShortcut(l, s, new EConstant(l, "cat"));
|
PSubMapShortcut node = new PSubMapShortcut(l, s, new EConstant(l, "cat"));
|
||||||
node.prefix = new EVariable(l, "a");
|
node.prefix = new EVariable(l, "a");
|
||||||
assertEquals("(PSubMapShortcut (EVariable a) (EConstant String 'cat'))", node.toString());
|
assertEquals("(PSubMapShortcut (EVariable a) (EConstant String 'cat'))", node.toString());
|
||||||
@ -488,7 +490,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
new PSubNullSafeCallInvoke(l, node).toString());
|
new PSubNullSafeCallInvoke(l, node).toString());
|
||||||
|
|
||||||
l = new Location(getTestName(), 1);
|
l = new Location(getTestName(), 1);
|
||||||
s = Definition.getType(Map.class.getSimpleName()).struct;
|
s = definition.getType(Map.class.getSimpleName()).struct;
|
||||||
node = new PSubMapShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
|
node = new PSubMapShortcut(l, s, new EBinary(l, Operation.ADD, new EConstant(l, 1), new EConstant(l, 4)));
|
||||||
node.prefix = new EVariable(l, "a");
|
node.prefix = new EVariable(l, "a");
|
||||||
assertEquals("(PSubMapShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
|
assertEquals("(PSubMapShortcut (EVariable a) (EBinary (EConstant Integer 1) + (EConstant Integer 4)))", node.toString());
|
||||||
@ -496,7 +498,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testPSubShortcut() {
|
public void testPSubShortcut() {
|
||||||
Location l = new Location(getTestName(), 0);
|
Location l = new Location(getTestName(), 0);
|
||||||
Struct s = Definition.getType(FeatureTest.class.getName()).struct;
|
Struct s = definition.getType(FeatureTest.class.getName()).struct;
|
||||||
Method getter = s.methods.get(new MethodKey("getX", 0));
|
Method getter = s.methods.get(new MethodKey("getX", 0));
|
||||||
Method setter = s.methods.get(new MethodKey("setX", 1));
|
Method setter = s.methods.get(new MethodKey("setX", 1));
|
||||||
PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter);
|
PSubShortcut node = new PSubShortcut(l, "x", FeatureTest.class.getName(), getter, setter);
|
||||||
@ -896,11 +898,12 @@ public class NodeToStringTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SSource walk(String code) {
|
private SSource walk(String code) {
|
||||||
ScriptInterface scriptInterface = new ScriptInterface(GenericElasticsearchScript.class);
|
ScriptInterface scriptInterface = new ScriptInterface(definition, GenericElasticsearchScript.class);
|
||||||
CompilerSettings compilerSettings = new CompilerSettings();
|
CompilerSettings compilerSettings = new CompilerSettings();
|
||||||
compilerSettings.setRegexesEnabled(true);
|
compilerSettings.setRegexesEnabled(true);
|
||||||
try {
|
try {
|
||||||
return Walker.buildPainlessTree(scriptInterface, getTestName(), code, compilerSettings, null);
|
return Walker.buildPainlessTree(scriptInterface, getTestName(), code, compilerSettings,
|
||||||
|
definition, null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Failed to compile: " + code, e);
|
throw new AssertionError("Failed to compile: " + code, e);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user