Remove RuntimeClass from Painless Definition in favor of just Painless Struct. (#28486)

This commit is contained in:
Jack Conradson 2018-02-02 10:26:02 -08:00 committed by GitHub
parent ab8f5ea54c
commit 90c74a7e09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 58 additions and 95 deletions

View File

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

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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