Remove RuntimeClass from Painless Definition in favor of just Painless Struct. (#28486)
This commit is contained in:
parent
ab8f5ea54c
commit
90c74a7e09
|
@ -20,7 +20,7 @@
|
|||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.RuntimeClass;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
@ -185,7 +185,7 @@ public final class Def {
|
|||
Definition.MethodKey key = new Definition.MethodKey(name, arity);
|
||||
// check whitelist for matching method
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
RuntimeClass struct = definition.getRuntimeClass(clazz);
|
||||
Struct struct = definition.RuntimeClassToStruct(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
Method method = struct.methods.get(key);
|
||||
|
@ -195,7 +195,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = definition.getRuntimeClass(iface);
|
||||
struct = definition.RuntimeClassToStruct(iface);
|
||||
|
||||
if (struct != null) {
|
||||
Method method = struct.methods.get(key);
|
||||
|
@ -325,7 +325,7 @@ public final class Def {
|
|||
static MethodHandle lookupReference(Definition definition, Lookup lookup, String interfaceClass,
|
||||
Class<?> receiverClass, String name) throws Throwable {
|
||||
Definition.Type interfaceType = definition.getType(interfaceClass);
|
||||
Method interfaceMethod = interfaceType.struct.getFunctionalMethod();
|
||||
Method interfaceMethod = interfaceType.struct.functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ public final class Def {
|
|||
final FunctionRef ref;
|
||||
if ("this".equals(type)) {
|
||||
// user written method
|
||||
Method interfaceMethod = clazz.struct.getFunctionalMethod();
|
||||
Method interfaceMethod = clazz.struct.functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + clazz.name + "], not a functional interface");
|
||||
|
@ -415,7 +415,7 @@ public final class Def {
|
|||
static MethodHandle lookupGetter(Definition definition, Class<?> receiverClass, String name) {
|
||||
// first try whitelist
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
RuntimeClass struct = definition.getRuntimeClass(clazz);
|
||||
Struct struct = definition.RuntimeClassToStruct(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.getters.get(name);
|
||||
|
@ -425,7 +425,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = definition.getRuntimeClass(iface);
|
||||
struct = definition.RuntimeClassToStruct(iface);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.getters.get(name);
|
||||
|
@ -486,7 +486,7 @@ public final class Def {
|
|||
static MethodHandle lookupSetter(Definition definition, Class<?> receiverClass, String name) {
|
||||
// first try whitelist
|
||||
for (Class<?> clazz = receiverClass; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
RuntimeClass struct = definition.getRuntimeClass(clazz);
|
||||
Struct struct = definition.RuntimeClassToStruct(clazz);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.setters.get(name);
|
||||
|
@ -496,7 +496,7 @@ public final class Def {
|
|||
}
|
||||
|
||||
for (final Class<?> iface : clazz.getInterfaces()) {
|
||||
struct = definition.getRuntimeClass(iface);
|
||||
struct = definition.RuntimeClassToStruct(iface);
|
||||
|
||||
if (struct != null) {
|
||||
MethodHandle handle = struct.setters.get(name);
|
||||
|
|
|
@ -307,9 +307,12 @@ public final class Definition {
|
|||
public final Map<String, Field> staticMembers;
|
||||
public final Map<String, Field> members;
|
||||
|
||||
private final SetOnce<Method> functionalMethod;
|
||||
public final Map<String, MethodHandle> getters;
|
||||
public final Map<String, MethodHandle> setters;
|
||||
|
||||
private Struct(final String name, final Class<?> clazz, final org.objectweb.asm.Type type) {
|
||||
public final Method functionalMethod;
|
||||
|
||||
private Struct(String name, Class<?> clazz, org.objectweb.asm.Type type) {
|
||||
this.name = name;
|
||||
this.clazz = clazz;
|
||||
this.type = type;
|
||||
|
@ -321,10 +324,13 @@ public final class Definition {
|
|||
staticMembers = new HashMap<>();
|
||||
members = new HashMap<>();
|
||||
|
||||
functionalMethod = new SetOnce<>();
|
||||
getters = new HashMap<>();
|
||||
setters = new HashMap<>();
|
||||
|
||||
functionalMethod = null;
|
||||
}
|
||||
|
||||
private Struct(final Struct struct) {
|
||||
private Struct(Struct struct, Method functionalMethod) {
|
||||
name = struct.name;
|
||||
clazz = struct.clazz;
|
||||
type = struct.type;
|
||||
|
@ -336,11 +342,14 @@ public final class Definition {
|
|||
staticMembers = Collections.unmodifiableMap(struct.staticMembers);
|
||||
members = Collections.unmodifiableMap(struct.members);
|
||||
|
||||
functionalMethod = struct.functionalMethod;
|
||||
getters = Collections.unmodifiableMap(struct.getters);
|
||||
setters = Collections.unmodifiableMap(struct.setters);
|
||||
|
||||
this.functionalMethod = functionalMethod;
|
||||
}
|
||||
|
||||
private Struct freeze() {
|
||||
return new Struct(this);
|
||||
private Struct freeze(Method functionalMethod) {
|
||||
return new Struct(this, functionalMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -362,14 +371,6 @@ public final class Definition {
|
|||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this class is a functional interface according to JLS, returns its method.
|
||||
* Otherwise returns null.
|
||||
*/
|
||||
public Method getFunctionalMethod() {
|
||||
return functionalMethod.get();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Cast {
|
||||
|
@ -418,25 +419,6 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
public static final class RuntimeClass {
|
||||
private final Struct struct;
|
||||
public final Map<MethodKey, Method> methods;
|
||||
public final Map<String, MethodHandle> getters;
|
||||
public final Map<String, MethodHandle> setters;
|
||||
|
||||
private RuntimeClass(final Struct struct, final Map<MethodKey, Method> methods,
|
||||
final Map<String, MethodHandle> getters, final Map<String, MethodHandle> setters) {
|
||||
this.struct = struct;
|
||||
this.methods = Collections.unmodifiableMap(methods);
|
||||
this.getters = Collections.unmodifiableMap(getters);
|
||||
this.setters = Collections.unmodifiableMap(setters);
|
||||
}
|
||||
|
||||
public Struct getStruct() {
|
||||
return struct;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether or not a non-array type exists. */
|
||||
public boolean isSimpleType(final String name) {
|
||||
return structsMap.containsKey(name);
|
||||
|
@ -569,7 +551,9 @@ public final class Definition {
|
|||
}
|
||||
|
||||
public static String ClassToName(Class<?> clazz) {
|
||||
if (clazz.isArray()) {
|
||||
if (clazz.isLocalClass() || clazz.isAnonymousClass()) {
|
||||
return null;
|
||||
} else if (clazz.isArray()) {
|
||||
Class<?> component = clazz.getComponentType();
|
||||
int dimensions = 1;
|
||||
|
||||
|
@ -609,7 +593,7 @@ public final class Definition {
|
|||
if (component == def.class) {
|
||||
return getType(structsMap.get(def.class.getSimpleName()), dimensions);
|
||||
} else {
|
||||
return getType(runtimeMap.get(component).struct, dimensions);
|
||||
return getType(structsMap.get(ClassToName(component)), dimensions);
|
||||
}
|
||||
} else if (clazz == def.class) {
|
||||
return getType(structsMap.get(def.class.getSimpleName()), 0);
|
||||
|
@ -618,6 +602,10 @@ public final class Definition {
|
|||
return getType(structsMap.get(ClassToName(clazz)), 0);
|
||||
}
|
||||
|
||||
public Struct RuntimeClassToStruct(Class<?> clazz) {
|
||||
return structsMap.get(ClassToName(clazz));
|
||||
}
|
||||
|
||||
public static Class<?> TypeToClass(Type type) {
|
||||
if (def.class.getSimpleName().equals(type.struct.name)) {
|
||||
return ObjectClassTodefClass(type.clazz);
|
||||
|
@ -626,10 +614,6 @@ public final class Definition {
|
|||
return type.clazz;
|
||||
}
|
||||
|
||||
public RuntimeClass getRuntimeClass(Class<?> clazz) {
|
||||
return runtimeMap.get(clazz);
|
||||
}
|
||||
|
||||
public Class<?> getClassFromBinaryName(String name) {
|
||||
Struct struct = structsMap.get(name.replace('$', '.'));
|
||||
|
||||
|
@ -659,14 +643,12 @@ public final class Definition {
|
|||
|
||||
// INTERNAL IMPLEMENTATION:
|
||||
|
||||
private final Map<Class<?>, RuntimeClass> runtimeMap;
|
||||
private final Map<String, Struct> structsMap;
|
||||
private final Map<String, Type> simpleTypesMap;
|
||||
|
||||
public Definition(List<Whitelist> whitelists) {
|
||||
structsMap = new HashMap<>();
|
||||
simpleTypesMap = new HashMap<>();
|
||||
runtimeMap = new HashMap<>();
|
||||
|
||||
Map<Class<?>, Struct> javaClassesToPainlessStructs = new HashMap<>();
|
||||
String origin = null;
|
||||
|
@ -787,17 +769,6 @@ public final class Definition {
|
|||
}
|
||||
}
|
||||
|
||||
// mark functional interfaces (or set null, to mark class is not)
|
||||
for (String painlessStructName : structsMap.keySet()) {
|
||||
Struct painlessStruct = structsMap.get(painlessStructName);
|
||||
|
||||
if (painlessStruct.name.equals(painlessStructName) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
painlessStruct.functionalMethod.set(computeFunctionalInterfaceMethod(painlessStruct));
|
||||
}
|
||||
|
||||
// precompute runtime classes
|
||||
for (String painlessStructName : structsMap.keySet()) {
|
||||
Struct painlessStruct = structsMap.get(painlessStructName);
|
||||
|
@ -815,7 +786,7 @@ public final class Definition {
|
|||
continue;
|
||||
}
|
||||
|
||||
entry.setValue(entry.getValue().freeze());
|
||||
entry.setValue(entry.getValue().freeze(computeFunctionalInterfaceMethod(entry.getValue())));
|
||||
}
|
||||
|
||||
voidType = getType("void");
|
||||
|
@ -1272,51 +1243,45 @@ public final class Definition {
|
|||
* Precomputes a more efficient structure for dynamic method/field access.
|
||||
*/
|
||||
private void addRuntimeClass(final Struct struct) {
|
||||
final Map<MethodKey, Method> methods = struct.methods;
|
||||
final Map<String, MethodHandle> getters = new HashMap<>();
|
||||
final Map<String, MethodHandle> setters = new HashMap<>();
|
||||
|
||||
// add all members
|
||||
for (final Map.Entry<String, Field> member : struct.members.entrySet()) {
|
||||
getters.put(member.getKey(), member.getValue().getter);
|
||||
setters.put(member.getKey(), member.getValue().setter);
|
||||
}
|
||||
|
||||
// add all getters/setters
|
||||
for (final Map.Entry<MethodKey, Method> method : methods.entrySet()) {
|
||||
final String name = method.getKey().name;
|
||||
final Method m = method.getValue();
|
||||
for (Map.Entry<MethodKey, Method> method : struct.methods.entrySet()) {
|
||||
String name = method.getKey().name;
|
||||
Method m = method.getValue();
|
||||
|
||||
if (m.arguments.size() == 0 &&
|
||||
name.startsWith("get") &&
|
||||
name.length() > 3 &&
|
||||
Character.isUpperCase(name.charAt(3))) {
|
||||
final StringBuilder newName = new StringBuilder();
|
||||
StringBuilder newName = new StringBuilder();
|
||||
newName.append(Character.toLowerCase(name.charAt(3)));
|
||||
newName.append(name.substring(4));
|
||||
getters.putIfAbsent(newName.toString(), m.handle);
|
||||
struct.getters.putIfAbsent(newName.toString(), m.handle);
|
||||
} else if (m.arguments.size() == 0 &&
|
||||
name.startsWith("is") &&
|
||||
name.length() > 2 &&
|
||||
Character.isUpperCase(name.charAt(2))) {
|
||||
final StringBuilder newName = new StringBuilder();
|
||||
StringBuilder newName = new StringBuilder();
|
||||
newName.append(Character.toLowerCase(name.charAt(2)));
|
||||
newName.append(name.substring(3));
|
||||
getters.putIfAbsent(newName.toString(), m.handle);
|
||||
struct.getters.putIfAbsent(newName.toString(), m.handle);
|
||||
}
|
||||
|
||||
if (m.arguments.size() == 1 &&
|
||||
name.startsWith("set") &&
|
||||
name.length() > 3 &&
|
||||
Character.isUpperCase(name.charAt(3))) {
|
||||
final StringBuilder newName = new StringBuilder();
|
||||
StringBuilder newName = new StringBuilder();
|
||||
newName.append(Character.toLowerCase(name.charAt(3)));
|
||||
newName.append(name.substring(4));
|
||||
setters.putIfAbsent(newName.toString(), m.handle);
|
||||
struct.setters.putIfAbsent(newName.toString(), m.handle);
|
||||
}
|
||||
}
|
||||
|
||||
runtimeMap.put(struct.clazz, new RuntimeClass(struct, methods, getters, setters));
|
||||
// add all members
|
||||
for (Map.Entry<String, Field> member : struct.members.entrySet()) {
|
||||
struct.getters.put(member.getKey(), member.getValue().getter);
|
||||
struct.setters.put(member.getKey(), member.getValue().setter);
|
||||
}
|
||||
}
|
||||
|
||||
/** computes the functional interface method for a class, or returns null */
|
||||
|
|
|
@ -75,7 +75,7 @@ public class FunctionRef {
|
|||
* @param numCaptures number of captured arguments
|
||||
*/
|
||||
public FunctionRef(Definition definition, Class<?> expected, String type, String call, int numCaptures) {
|
||||
this(expected, definition.ClassToType(expected).struct.getFunctionalMethod(),
|
||||
this(expected, definition.ClassToType(expected).struct.functionalMethod,
|
||||
lookup(definition, expected, type, call, numCaptures > 0), numCaptures);
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ public class FunctionRef {
|
|||
String type, String call, boolean receiverCaptured) {
|
||||
// check its really a functional interface
|
||||
// for e.g. Comparable
|
||||
Method method = definition.ClassToType(expected).struct.getFunctionalMethod();
|
||||
Method method = definition.ClassToType(expected).struct.functionalMethod;
|
||||
if (method == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + Definition.ClassToName(expected) + "], not a functional interface");
|
||||
|
|
|
@ -54,9 +54,9 @@ public class PainlessExplainError extends Error {
|
|||
if (objectToExplain != null) {
|
||||
toString = objectToExplain.toString();
|
||||
javaClassName = objectToExplain.getClass().getName();
|
||||
Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(objectToExplain.getClass());
|
||||
if (runtimeClass != null) {
|
||||
painlessClassName = runtimeClass.getStruct().name;
|
||||
Definition.Struct struct = definition.ClassToType(objectToExplain.getClass()).struct;
|
||||
if (struct != null) {
|
||||
painlessClassName = struct.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -193,11 +193,10 @@ public class ScriptClassInfo {
|
|||
if (componentType == Object.class) {
|
||||
struct = definition.getType("def").struct;
|
||||
} else {
|
||||
Definition.RuntimeClass runtimeClass = definition.getRuntimeClass(componentType);
|
||||
if (runtimeClass == null) {
|
||||
if (definition.RuntimeClassToStruct(componentType) == null) {
|
||||
throw new IllegalArgumentException(unknownErrorMessageSource.apply(componentType));
|
||||
}
|
||||
struct = runtimeClass.getStruct();
|
||||
struct = definition.RuntimeClassToStruct(componentType);
|
||||
}
|
||||
return Definition.TypeToClass(definition.getType(struct, dimensions));
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public final class EFunctionRef extends AExpression implements ILambda {
|
|||
try {
|
||||
if ("this".equals(type)) {
|
||||
// user's own function
|
||||
Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.getFunctionalMethod();
|
||||
Method interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
|
||||
"to [" + Definition.ClassToName(expected) + "], not a functional interface");
|
||||
|
|
|
@ -120,7 +120,7 @@ public final class ELambda extends AExpression implements ILambda {
|
|||
}
|
||||
} else {
|
||||
// we know the method statically, infer return type and any unknown/def types
|
||||
interfaceMethod = locals.getDefinition().ClassToType(expected).struct.getFunctionalMethod();
|
||||
interfaceMethod = locals.getDefinition().ClassToType(expected).struct.functionalMethod;
|
||||
if (interfaceMethod == null) {
|
||||
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + Definition.ClassToName(expected) +
|
||||
"], not a functional interface"));
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.elasticsearch.painless.Definition.Cast;
|
|||
import org.elasticsearch.painless.Definition.Field;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
import org.elasticsearch.painless.Definition.MethodKey;
|
||||
import org.elasticsearch.painless.Definition.RuntimeClass;
|
||||
import org.elasticsearch.painless.Definition.Struct;
|
||||
import org.elasticsearch.painless.FeatureTest;
|
||||
import org.elasticsearch.painless.GenericElasticsearchScript;
|
||||
|
@ -404,7 +403,7 @@ public class NodeToStringTests extends ESTestCase {
|
|||
|
||||
public void testPSubCallInvoke() {
|
||||
Location l = new Location(getTestName(), 0);
|
||||
RuntimeClass c = definition.getRuntimeClass(Integer.class);
|
||||
Struct c = definition.ClassToType(Integer.class).struct;
|
||||
Method m = c.methods.get(new MethodKey("toString", 0));
|
||||
PSubCallInvoke node = new PSubCallInvoke(l, m, null, emptyList());
|
||||
node.prefix = new EVariable(l, "a");
|
||||
|
|
Loading…
Reference in New Issue