Fix static inner class resolution in Painless (#67027)
When removing the "lexer hack" to remove type context from the lexer, static inner class resolution wasn't properly accounted for. This change adds code to handle static inner class resolution.
This commit is contained in:
parent
67f43ffc5f
commit
8e294d53bc
|
@ -2461,220 +2461,238 @@ public class DefaultSemanticAnalysisPhase extends UserTreeBaseVisitor<SemanticSc
|
|||
|
||||
String canonicalTypeName =
|
||||
semanticScope.getDecoration(userPrefixNode, PartialCanonicalTypeName.class).getPartialCanonicalTypeName() + "." + index;
|
||||
Class<?> type = scriptScope.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
|
||||
Class<?> staticType = scriptScope.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
|
||||
|
||||
if (type == null) {
|
||||
if (staticType == null) {
|
||||
semanticScope.putDecoration(userDotNode, new PartialCanonicalTypeName(canonicalTypeName));
|
||||
} else {
|
||||
if (write) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("invalid assignment: " +
|
||||
"cannot write a value to a static type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "]"));
|
||||
"cannot write a value to a static type [" + PainlessLookupUtility.typeToCanonicalTypeName(staticType) + "]"));
|
||||
}
|
||||
|
||||
semanticScope.putDecoration(userDotNode, new StaticType(type));
|
||||
semanticScope.putDecoration(userDotNode, new StaticType(staticType));
|
||||
}
|
||||
} else {
|
||||
Class<?> valueType = null;
|
||||
Class<?> staticType = null;
|
||||
|
||||
if (prefixValueType != null && prefixValueType.getValueType().isArray()) {
|
||||
if ("length".equals(index)) {
|
||||
if (write) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"invalid assignment: cannot assign a value write to read-only field [length] for an array."));
|
||||
}
|
||||
|
||||
valueType = int.class;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Field [" + index + "] does not exist for type [" + prefixValueType.getValueCanonicalTypeName() + "]."));
|
||||
}
|
||||
} else if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
|
||||
TargetType targetType = userDotNode.isNullSafe() ? null : semanticScope.getDecoration(userDotNode, TargetType.class);
|
||||
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
|
||||
valueType = targetType == null || targetType.getTargetType() == ZonedDateTime.class ||
|
||||
semanticScope.getCondition(userDotNode, Explicit.class) ? def.class : targetType.getTargetType();
|
||||
|
||||
if (write) {
|
||||
semanticScope.setCondition(userDotNode, DefOptimized.class);
|
||||
}
|
||||
} else {
|
||||
Class<?> prefixType;
|
||||
String prefixCanonicalTypeName;
|
||||
boolean isStatic;
|
||||
|
||||
if (prefixValueType != null) {
|
||||
prefixType = prefixValueType.getValueType();
|
||||
prefixCanonicalTypeName = prefixValueType.getValueCanonicalTypeName();
|
||||
isStatic = false;
|
||||
} else if (prefixStaticType != null) {
|
||||
prefixType = prefixStaticType.getStaticType();
|
||||
prefixCanonicalTypeName = prefixStaticType.getStaticCanonicalTypeName();
|
||||
isStatic = true;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalStateException("value required: instead found no value"));
|
||||
}
|
||||
|
||||
PainlessField field = semanticScope.getScriptScope().getPainlessLookup().lookupPainlessField(prefixType, isStatic, index);
|
||||
|
||||
if (field == null) {
|
||||
PainlessMethod getter;
|
||||
PainlessMethod setter;
|
||||
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
||||
"get" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
||||
|
||||
if (getter == null) {
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
||||
"is" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
||||
}
|
||||
|
||||
setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
||||
"set" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
||||
|
||||
if (getter != null || setter != null) {
|
||||
if (getter != null && (getter.returnType == void.class || !getter.typeParameters.isEmpty())) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal get shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal set shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read == false || getter != null) && (write == false || setter != null)) {
|
||||
valueType = setter != null ? setter.typeParameters.get(0) : getter.returnType;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
|
||||
}
|
||||
|
||||
semanticScope.setCondition(userDotNode, Shortcut.class);
|
||||
} else if (isStatic == false) {
|
||||
if (Map.class.isAssignableFrom(prefixValueType.getValueType())) {
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "get", 1);
|
||||
setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "put", 2);
|
||||
|
||||
if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal map get shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && setter.typeParameters.size() != 2) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal map set shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0)) ||
|
||||
getter.returnType.equals(setter.typeParameters.get(1)) == false)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read == false || getter != null) && (write == false || setter != null)) {
|
||||
valueType = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal map shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
|
||||
}
|
||||
|
||||
semanticScope.setCondition(userDotNode, MapShortcut.class);
|
||||
}
|
||||
|
||||
if (List.class.isAssignableFrom(prefixType)) {
|
||||
try {
|
||||
scriptScope.putDecoration(userDotNode, new StandardConstant(Integer.parseInt(index)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("invalid list index [" + index + "]", nfe));
|
||||
}
|
||||
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "get", 1);
|
||||
setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "set", 2);
|
||||
|
||||
if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 ||
|
||||
getter.typeParameters.get(0) != int.class)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal list get shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && (setter.typeParameters.size() != 2 || setter.typeParameters.get(0) != int.class)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal list set shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0))
|
||||
|| !getter.returnType.equals(setter.typeParameters.get(1)))) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read == false || getter != null) && (write == false || setter != null)) {
|
||||
valueType = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal list shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
|
||||
}
|
||||
|
||||
semanticScope.setCondition(userDotNode, ListShortcut.class);
|
||||
}
|
||||
}
|
||||
|
||||
if (valueType == null) {
|
||||
if (prefixValueType != null) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"field [" + prefixValueType.getValueCanonicalTypeName() + ", " + index + "] not found"));
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"field [" + prefixStaticType.getStaticCanonicalTypeName() + ", " + index + "] not found"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (write && Modifier.isFinal(field.javaField.getModifiers())) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"invalid assignment: cannot assign a value to read-only field [" + field.javaField.getName() + "]"));
|
||||
}
|
||||
|
||||
semanticScope.putDecoration(userDotNode, new StandardPainlessField(field));
|
||||
valueType = field.typeParameter;
|
||||
}
|
||||
if (prefixStaticType != null) {
|
||||
String staticCanonicalTypeName = prefixStaticType.getStaticCanonicalTypeName() + "." + userDotNode.getIndex();
|
||||
staticType = scriptScope.getPainlessLookup().canonicalTypeNameToType(staticCanonicalTypeName);
|
||||
}
|
||||
|
||||
semanticScope.putDecoration(userDotNode, new ValueType(valueType));
|
||||
|
||||
if (userDotNode.isNullSafe()) {
|
||||
if (staticType != null) {
|
||||
if (write) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"invalid assignment: cannot assign a value to a null safe operation [?.]"));
|
||||
throw userDotNode.createError(new IllegalArgumentException("invalid assignment: " +
|
||||
"cannot write a value to a static type [" + PainlessLookupUtility.typeToCanonicalTypeName(staticType) + "]"));
|
||||
}
|
||||
|
||||
if (valueType.isPrimitive()) {
|
||||
throw new IllegalArgumentException("Result of null safe operator must be nullable");
|
||||
semanticScope.putDecoration(userDotNode, new StaticType(staticType));
|
||||
} else {
|
||||
Class<?> valueType = null;
|
||||
|
||||
if (prefixValueType != null && prefixValueType.getValueType().isArray()) {
|
||||
if ("length".equals(index)) {
|
||||
if (write) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"invalid assignment: cannot assign a value write to read-only field [length] for an array."));
|
||||
}
|
||||
|
||||
valueType = int.class;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Field [" + index + "] does not exist for type [" + prefixValueType.getValueCanonicalTypeName() + "]."));
|
||||
}
|
||||
} else if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
|
||||
TargetType targetType = userDotNode.isNullSafe() ? null : semanticScope.getDecoration(userDotNode, TargetType.class);
|
||||
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
|
||||
valueType = targetType == null || targetType.getTargetType() == ZonedDateTime.class ||
|
||||
semanticScope.getCondition(userDotNode, Explicit.class) ? def.class : targetType.getTargetType();
|
||||
|
||||
if (write) {
|
||||
semanticScope.setCondition(userDotNode, DefOptimized.class);
|
||||
}
|
||||
} else {
|
||||
Class<?> prefixType;
|
||||
String prefixCanonicalTypeName;
|
||||
boolean isStatic;
|
||||
|
||||
if (prefixValueType != null) {
|
||||
prefixType = prefixValueType.getValueType();
|
||||
prefixCanonicalTypeName = prefixValueType.getValueCanonicalTypeName();
|
||||
isStatic = false;
|
||||
} else if (prefixStaticType != null) {
|
||||
prefixType = prefixStaticType.getStaticType();
|
||||
prefixCanonicalTypeName = prefixStaticType.getStaticCanonicalTypeName();
|
||||
isStatic = true;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalStateException("value required: instead found no value"));
|
||||
}
|
||||
|
||||
PainlessField field =
|
||||
semanticScope.getScriptScope().getPainlessLookup().lookupPainlessField(prefixType, isStatic, index);
|
||||
|
||||
if (field == null) {
|
||||
PainlessMethod getter;
|
||||
PainlessMethod setter;
|
||||
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
||||
"get" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
||||
|
||||
if (getter == null) {
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
||||
"is" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
||||
}
|
||||
|
||||
setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
||||
"set" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
||||
|
||||
if (getter != null || setter != null) {
|
||||
if (getter != null && (getter.returnType == void.class || !getter.typeParameters.isEmpty())) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal get shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal set shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read == false || getter != null) && (write == false || setter != null)) {
|
||||
valueType = setter != null ? setter.typeParameters.get(0) : getter.returnType;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
|
||||
}
|
||||
|
||||
semanticScope.setCondition(userDotNode, Shortcut.class);
|
||||
} else if (isStatic == false) {
|
||||
if (Map.class.isAssignableFrom(prefixValueType.getValueType())) {
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "get", 1);
|
||||
setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "put", 2);
|
||||
|
||||
if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal map get shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && setter.typeParameters.size() != 2) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal map set shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null &&
|
||||
(!getter.typeParameters.get(0).equals(setter.typeParameters.get(0)) ||
|
||||
getter.returnType.equals(setter.typeParameters.get(1)) == false)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read == false || getter != null) && (write == false || setter != null)) {
|
||||
valueType = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal map shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
|
||||
}
|
||||
|
||||
semanticScope.setCondition(userDotNode, MapShortcut.class);
|
||||
}
|
||||
|
||||
if (List.class.isAssignableFrom(prefixType)) {
|
||||
try {
|
||||
scriptScope.putDecoration(userDotNode, new StandardConstant(Integer.parseInt(index)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("invalid list index [" + index + "]", nfe));
|
||||
}
|
||||
|
||||
getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "get", 1);
|
||||
setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, false, "set", 2);
|
||||
|
||||
if (getter != null && (getter.returnType == void.class || getter.typeParameters.size() != 1 ||
|
||||
getter.typeParameters.get(0) != int.class)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal list get shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (setter != null && (setter.typeParameters.size() != 2 || setter.typeParameters.get(0) != int.class)) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal list set shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0))
|
||||
|| !getter.returnType.equals(setter.typeParameters.get(1)))) {
|
||||
throw userDotNode.createError(new IllegalArgumentException("Shortcut argument types must match."));
|
||||
}
|
||||
|
||||
if ((read == false || getter != null) && (write == false || setter != null)) {
|
||||
valueType = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"Illegal list shortcut for type [" + prefixCanonicalTypeName + "]."));
|
||||
}
|
||||
|
||||
if (getter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new GetterPainlessMethod(getter));
|
||||
}
|
||||
|
||||
if (setter != null) {
|
||||
semanticScope.putDecoration(userDotNode, new SetterPainlessMethod(setter));
|
||||
}
|
||||
|
||||
semanticScope.setCondition(userDotNode, ListShortcut.class);
|
||||
}
|
||||
}
|
||||
|
||||
if (valueType == null) {
|
||||
if (prefixValueType != null) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"field [" + prefixValueType.getValueCanonicalTypeName() + ", " + index + "] not found"));
|
||||
} else {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"field [" + prefixStaticType.getStaticCanonicalTypeName() + ", " + index + "] not found"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (write && Modifier.isFinal(field.javaField.getModifiers())) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"invalid assignment: cannot assign a value to read-only field [" + field.javaField.getName() + "]"));
|
||||
}
|
||||
|
||||
semanticScope.putDecoration(userDotNode, new StandardPainlessField(field));
|
||||
valueType = field.typeParameter;
|
||||
}
|
||||
}
|
||||
|
||||
semanticScope.putDecoration(userDotNode, new ValueType(valueType));
|
||||
|
||||
if (userDotNode.isNullSafe()) {
|
||||
if (write) {
|
||||
throw userDotNode.createError(new IllegalArgumentException(
|
||||
"invalid assignment: cannot assign a value to a null safe operation [?.]"));
|
||||
}
|
||||
|
||||
if (valueType.isPrimitive()) {
|
||||
throw new IllegalArgumentException("Result of null safe operator must be nullable");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import java.text.MessageFormat.Field;
|
||||
import java.text.Normalizer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -166,4 +168,9 @@ public class BasicAPITests extends ScriptTestCase {
|
|||
).matches()
|
||||
);
|
||||
}
|
||||
|
||||
public void testStaticInnerClassResolution() {
|
||||
assertEquals(Field.ARGUMENT, exec("MessageFormat.Field.ARGUMENT"));
|
||||
assertEquals(Normalizer.Form.NFD, exec("Normalizer.Form.NFD"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue