|
@@ -9,6 +9,10 @@
|
|
|
package org.elasticsearch.painless.lookup;
|
|
|
|
|
|
import java.lang.invoke.MethodHandle;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.Set;
|
|
@@ -25,6 +29,7 @@ public final class PainlessLookup {
|
|
|
private final Map<String, Class<?>> javaClassNamesToClasses;
|
|
|
private final Map<String, Class<?>> canonicalClassNamesToClasses;
|
|
|
private final Map<Class<?>, PainlessClass> classesToPainlessClasses;
|
|
|
+ private final Map<Class<?>, Set<Class<?>>> classesToDirectSubClasses;
|
|
|
|
|
|
private final Map<String, PainlessMethod> painlessMethodKeysToImportedPainlessMethods;
|
|
|
private final Map<String, PainlessClassBinding> painlessMethodKeysToPainlessClassBindings;
|
|
@@ -34,6 +39,7 @@ public final class PainlessLookup {
|
|
|
Map<String, Class<?>> javaClassNamesToClasses,
|
|
|
Map<String, Class<?>> canonicalClassNamesToClasses,
|
|
|
Map<Class<?>, PainlessClass> classesToPainlessClasses,
|
|
|
+ Map<Class<?>, Set<Class<?>>> classesToDirectSubClasses,
|
|
|
Map<String, PainlessMethod> painlessMethodKeysToImportedPainlessMethods,
|
|
|
Map<String, PainlessClassBinding> painlessMethodKeysToPainlessClassBindings,
|
|
|
Map<String, PainlessInstanceBinding> painlessMethodKeysToPainlessInstanceBindings) {
|
|
@@ -41,6 +47,7 @@ public final class PainlessLookup {
|
|
|
Objects.requireNonNull(javaClassNamesToClasses);
|
|
|
Objects.requireNonNull(canonicalClassNamesToClasses);
|
|
|
Objects.requireNonNull(classesToPainlessClasses);
|
|
|
+ Objects.requireNonNull(classesToDirectSubClasses);
|
|
|
|
|
|
Objects.requireNonNull(painlessMethodKeysToImportedPainlessMethods);
|
|
|
Objects.requireNonNull(painlessMethodKeysToPainlessClassBindings);
|
|
@@ -49,6 +56,7 @@ public final class PainlessLookup {
|
|
|
this.javaClassNamesToClasses = javaClassNamesToClasses;
|
|
|
this.canonicalClassNamesToClasses = Map.copyOf(canonicalClassNamesToClasses);
|
|
|
this.classesToPainlessClasses = Map.copyOf(classesToPainlessClasses);
|
|
|
+ this.classesToDirectSubClasses = Map.copyOf(classesToDirectSubClasses);
|
|
|
|
|
|
this.painlessMethodKeysToImportedPainlessMethods = Map.copyOf(painlessMethodKeysToImportedPainlessMethods);
|
|
|
this.painlessMethodKeysToPainlessClassBindings = Map.copyOf(painlessMethodKeysToPainlessClassBindings);
|
|
@@ -75,6 +83,10 @@ public final class PainlessLookup {
|
|
|
return classesToPainlessClasses.keySet();
|
|
|
}
|
|
|
|
|
|
+ public Set<Class<?>> getDirectSubClasses(Class<?> superClass) {
|
|
|
+ return classesToDirectSubClasses.get(superClass);
|
|
|
+ }
|
|
|
+
|
|
|
public Set<String> getImportedPainlessMethodsKeys() {
|
|
|
return painlessMethodKeysToImportedPainlessMethods.keySet();
|
|
|
}
|
|
@@ -142,16 +154,12 @@ public final class PainlessLookup {
|
|
|
targetClass = typeToBoxedType(targetClass);
|
|
|
}
|
|
|
|
|
|
- PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
|
|
|
String painlessMethodKey = buildPainlessMethodKey(methodName, methodArity);
|
|
|
+ Function<PainlessClass, PainlessMethod> objectLookup = isStatic ?
|
|
|
+ targetPainlessClass -> targetPainlessClass.staticMethods.get(painlessMethodKey) :
|
|
|
+ targetPainlessClass -> targetPainlessClass.methods.get(painlessMethodKey);
|
|
|
|
|
|
- if (targetPainlessClass == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return isStatic ?
|
|
|
- targetPainlessClass.staticMethods.get(painlessMethodKey) :
|
|
|
- targetPainlessClass.methods.get(painlessMethodKey);
|
|
|
+ return lookupPainlessObject(targetClass, objectLookup);
|
|
|
}
|
|
|
|
|
|
public PainlessField lookupPainlessField(String targetCanonicalClassName, boolean isStatic, String fieldName) {
|
|
@@ -170,22 +178,12 @@ public final class PainlessLookup {
|
|
|
Objects.requireNonNull(targetClass);
|
|
|
Objects.requireNonNull(fieldName);
|
|
|
|
|
|
- PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetClass);
|
|
|
String painlessFieldKey = buildPainlessFieldKey(fieldName);
|
|
|
+ Function<PainlessClass, PainlessField> objectLookup = isStatic ?
|
|
|
+ targetPainlessClass -> targetPainlessClass.staticFields.get(painlessFieldKey) :
|
|
|
+ targetPainlessClass -> targetPainlessClass.fields.get(painlessFieldKey);
|
|
|
|
|
|
- if (targetPainlessClass == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- PainlessField painlessField = isStatic ?
|
|
|
- targetPainlessClass.staticFields.get(painlessFieldKey) :
|
|
|
- targetPainlessClass.fields.get(painlessFieldKey);
|
|
|
-
|
|
|
- if (painlessField == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return painlessField;
|
|
|
+ return lookupPainlessObject(targetClass, objectLookup);
|
|
|
}
|
|
|
|
|
|
public PainlessMethod lookupImportedPainlessMethod(String methodName, int arity) {
|
|
@@ -230,7 +228,7 @@ public final class PainlessLookup {
|
|
|
Function<PainlessClass, PainlessMethod> objectLookup =
|
|
|
targetPainlessClass -> targetPainlessClass.runtimeMethods.get(painlessMethodKey);
|
|
|
|
|
|
- return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
|
|
|
+ return lookupPainlessObject(originalTargetClass, objectLookup);
|
|
|
}
|
|
|
|
|
|
public MethodHandle lookupRuntimeGetterMethodHandle(Class<?> originalTargetClass, String getterName) {
|
|
@@ -239,7 +237,7 @@ public final class PainlessLookup {
|
|
|
|
|
|
Function<PainlessClass, MethodHandle> objectLookup = targetPainlessClass -> targetPainlessClass.getterMethodHandles.get(getterName);
|
|
|
|
|
|
- return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
|
|
|
+ return lookupPainlessObject(originalTargetClass, objectLookup);
|
|
|
}
|
|
|
|
|
|
public MethodHandle lookupRuntimeSetterMethodHandle(Class<?> originalTargetClass, String setterName) {
|
|
@@ -248,10 +246,13 @@ public final class PainlessLookup {
|
|
|
|
|
|
Function<PainlessClass, MethodHandle> objectLookup = targetPainlessClass -> targetPainlessClass.setterMethodHandles.get(setterName);
|
|
|
|
|
|
- return lookupRuntimePainlessObject(originalTargetClass, objectLookup);
|
|
|
+ return lookupPainlessObject(originalTargetClass, objectLookup);
|
|
|
}
|
|
|
|
|
|
- private <T> T lookupRuntimePainlessObject(Class<?> originalTargetClass, Function<PainlessClass, T> objectLookup) {
|
|
|
+ private <T> T lookupPainlessObject(Class<?> originalTargetClass, Function<PainlessClass, T> objectLookup) {
|
|
|
+ Objects.requireNonNull(originalTargetClass);
|
|
|
+ Objects.requireNonNull(objectLookup);
|
|
|
+
|
|
|
Class<?> currentTargetClass = originalTargetClass;
|
|
|
|
|
|
while (currentTargetClass != null) {
|
|
@@ -268,17 +269,38 @@ public final class PainlessLookup {
|
|
|
currentTargetClass = currentTargetClass.getSuperclass();
|
|
|
}
|
|
|
|
|
|
+ if (originalTargetClass.isInterface()) {
|
|
|
+ PainlessClass targetPainlessClass = classesToPainlessClasses.get(Object.class);
|
|
|
+
|
|
|
+ if (targetPainlessClass != null) {
|
|
|
+ T painlessObject = objectLookup.apply(targetPainlessClass);
|
|
|
+
|
|
|
+ if (painlessObject != null) {
|
|
|
+ return painlessObject;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
currentTargetClass = originalTargetClass;
|
|
|
+ Set<Class<?>> resolvedInterfaces = new HashSet<>();
|
|
|
|
|
|
while (currentTargetClass != null) {
|
|
|
- for (Class<?> targetInterface : currentTargetClass.getInterfaces()) {
|
|
|
- PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetInterface);
|
|
|
+ List<Class<?>> targetInterfaces = new ArrayList<>(Arrays.asList(currentTargetClass.getInterfaces()));
|
|
|
+
|
|
|
+ while (targetInterfaces.isEmpty() == false) {
|
|
|
+ Class<?> targetInterface = targetInterfaces.remove(0);
|
|
|
+
|
|
|
+ if (resolvedInterfaces.add(targetInterface)) {
|
|
|
+ PainlessClass targetPainlessClass = classesToPainlessClasses.get(targetInterface);
|
|
|
+
|
|
|
+ if (targetPainlessClass != null) {
|
|
|
+ T painlessObject = objectLookup.apply(targetPainlessClass);
|
|
|
|
|
|
- if (targetPainlessClass != null) {
|
|
|
- T painlessObject = objectLookup.apply(targetPainlessClass);
|
|
|
+ if (painlessObject != null) {
|
|
|
+ return painlessObject;
|
|
|
+ }
|
|
|
|
|
|
- if (painlessObject != null) {
|
|
|
- return painlessObject;
|
|
|
+ targetInterfaces.addAll(Arrays.asList(targetInterface.getInterfaces()));
|
|
|
}
|
|
|
}
|
|
|
}
|