in modules/lang-painless/src/main/java/org/elasticsearch/painless/phase/DefaultSemanticAnalysisPhase.java [2677:3031]
public void visitDot(EDot userDotNode, SemanticScope semanticScope) {
boolean read = semanticScope.getCondition(userDotNode, Read.class);
boolean write = semanticScope.getCondition(userDotNode, Write.class);
if (read == false && write == false) {
throw userDotNode.createError(new IllegalArgumentException("not a statement: result of dot operator [.] not used"));
}
ScriptScope scriptScope = semanticScope.getScriptScope();
String index = userDotNode.getIndex();
AExpression userPrefixNode = userDotNode.getPrefixNode();
semanticScope.setCondition(userPrefixNode, Read.class);
visit(userPrefixNode, semanticScope);
ValueType prefixValueType = semanticScope.getDecoration(userPrefixNode, ValueType.class);
StaticType prefixStaticType = semanticScope.getDecoration(userPrefixNode, StaticType.class);
if (prefixValueType != null && prefixStaticType != null) {
throw userDotNode.createError(
new IllegalStateException(
Strings.format(
"cannot have both value [%s] and type [%s]",
prefixValueType.getValueCanonicalTypeName(),
prefixStaticType.getStaticCanonicalTypeName()
)
)
);
}
if (semanticScope.hasDecoration(userPrefixNode, PartialCanonicalTypeName.class)) {
if (prefixValueType != null) {
throw userDotNode.createError(
new IllegalArgumentException(
"value required: instead found unexpected type " + "[" + prefixValueType.getValueCanonicalTypeName() + "]"
)
);
}
if (prefixStaticType != null) {
throw userDotNode.createError(
new IllegalArgumentException(
"value required: instead found unexpected type " + "[" + prefixStaticType.staticType() + "]"
)
);
}
String canonicalTypeName = semanticScope.getDecoration(userPrefixNode, PartialCanonicalTypeName.class)
.partialCanonicalTypeName()
+ "."
+ index;
Class<?> staticType = scriptScope.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
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(staticType)
+ "]"
)
);
}
semanticScope.putDecoration(userDotNode, new StaticType(staticType));
}
} else {
Class<?> staticType = null;
if (prefixStaticType != null) {
String staticCanonicalTypeName = prefixStaticType.getStaticCanonicalTypeName() + "." + userDotNode.getIndex();
staticType = scriptScope.getPainlessLookup().canonicalTypeNameToType(staticCanonicalTypeName);
}
if (staticType != null) {
if (write) {
throw userDotNode.createError(
new IllegalArgumentException(
"invalid assignment: "
+ "cannot write a value to a static type ["
+ PainlessLookupUtility.typeToCanonicalTypeName(staticType)
+ "]"
)
);
}
semanticScope.putDecoration(userDotNode, new StaticType(staticType));
} else {
Class<?> valueType = null;
if (prefixValueType != null && prefixValueType.valueType().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.valueType() == def.class) {
TargetType targetType = userDotNode.isNullSafe() ? null : semanticScope.getDecoration(userDotNode, TargetType.class);
valueType = targetType == null || semanticScope.getCondition(userDotNode, Explicit.class)
? def.class
: targetType.targetType();
if (write) {
semanticScope.setCondition(userDotNode, DefOptimized.class);
}
} else {
Class<?> prefixType;
String prefixCanonicalTypeName;
boolean isStatic;
if (prefixValueType != null) {
prefixType = prefixValueType.valueType();
prefixCanonicalTypeName = prefixValueType.getValueCanonicalTypeName();
isStatic = false;
} else if (prefixStaticType != null) {
prefixType = prefixStaticType.staticType();
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),
1
);
if (getter != null || setter != null) {
if (getter != null && (getter.returnType() == void.class || getter.typeParameters().isEmpty() == false)) {
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.valueType())) {
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)) == false
|| 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)) == false
|| 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 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");
}
}
}
}
}