|
@@ -20,8 +20,6 @@
|
|
|
package org.elasticsearch.painless.node;
|
|
|
|
|
|
import org.elasticsearch.painless.Location;
|
|
|
-import org.elasticsearch.painless.symbol.ScriptScope;
|
|
|
-import org.elasticsearch.painless.symbol.SemanticScope;
|
|
|
import org.elasticsearch.painless.ir.ClassNode;
|
|
|
import org.elasticsearch.painless.ir.ConstantNode;
|
|
|
import org.elasticsearch.painless.ir.DotNode;
|
|
@@ -38,6 +36,16 @@ import org.elasticsearch.painless.lookup.PainlessField;
|
|
|
import org.elasticsearch.painless.lookup.PainlessLookupUtility;
|
|
|
import org.elasticsearch.painless.lookup.PainlessMethod;
|
|
|
import org.elasticsearch.painless.lookup.def;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.DefOptimized;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.Explicit;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.PartialCanonicalTypeName;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.Read;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.StaticType;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.TargetType;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.ValueType;
|
|
|
+import org.elasticsearch.painless.symbol.Decorations.Write;
|
|
|
+import org.elasticsearch.painless.symbol.ScriptScope;
|
|
|
+import org.elasticsearch.painless.symbol.SemanticScope;
|
|
|
|
|
|
import java.lang.reflect.Modifier;
|
|
|
import java.time.ZonedDateTime;
|
|
@@ -45,8 +53,6 @@ import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
|
|
|
-import static org.elasticsearch.painless.lookup.PainlessLookupUtility.typeToCanonicalTypeName;
|
|
|
-
|
|
|
/**
|
|
|
* Represents a field load/store and defers to a child subnode.
|
|
|
*/
|
|
@@ -77,152 +83,167 @@ public class EDot extends AExpression {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- Output analyze(ClassNode classNode, SemanticScope semanticScope, Input input) {
|
|
|
- if (input.read == false && input.write == false) {
|
|
|
+ Output analyze(ClassNode classNode, SemanticScope semanticScope) {
|
|
|
+ boolean read = semanticScope.getCondition(this, Read.class);
|
|
|
+ boolean write = semanticScope.getCondition(this, Write.class);
|
|
|
+
|
|
|
+ if (read == false && write == false) {
|
|
|
throw createError(new IllegalArgumentException("not a statement: result of dot operator [.] not used"));
|
|
|
}
|
|
|
|
|
|
ScriptScope scriptScope = semanticScope.getScriptScope();
|
|
|
|
|
|
Output output = new Output();
|
|
|
- Output prefixOutput = prefixNode.analyze(classNode, semanticScope, new Input());
|
|
|
|
|
|
- if (prefixOutput.partialCanonicalTypeName != null) {
|
|
|
- if (output.isStaticType) {
|
|
|
- throw createError(new IllegalArgumentException("value required: " +
|
|
|
- "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
|
|
|
+ semanticScope.setCondition(prefixNode, Read.class);
|
|
|
+ Output prefixOutput = prefixNode.analyze(classNode, semanticScope);
|
|
|
+ ValueType prefixValueType = semanticScope.getDecoration(prefixNode, ValueType.class);
|
|
|
+ StaticType prefixStaticType = semanticScope.getDecoration(prefixNode, StaticType.class);
|
|
|
+
|
|
|
+ if (prefixValueType != null && prefixStaticType != null) {
|
|
|
+ throw createError(new IllegalStateException("cannot have both " +
|
|
|
+ "value [" + prefixValueType.getValueCanonicalTypeName() + "] " +
|
|
|
+ "and type [" + prefixStaticType.getStaticCanonicalTypeName() + "]"));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (semanticScope.hasDecoration(prefixNode, PartialCanonicalTypeName.class)) {
|
|
|
+ if (prefixValueType != null) {
|
|
|
+ throw createError(new IllegalArgumentException("value required: instead found unexpected type " +
|
|
|
+ "[" + prefixValueType.getValueCanonicalTypeName() + "]"));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (prefixStaticType != null) {
|
|
|
+ throw createError(new IllegalArgumentException("value required: instead found unexpected type " +
|
|
|
+ "[" + prefixStaticType.getStaticType() + "]"));
|
|
|
}
|
|
|
|
|
|
- String canonicalTypeName = prefixOutput.partialCanonicalTypeName + "." + index;
|
|
|
+ String canonicalTypeName =
|
|
|
+ semanticScope.getDecoration(prefixNode, PartialCanonicalTypeName.class).getPartialCanonicalTypeName() + "." + index;
|
|
|
Class<?> type = scriptScope.getPainlessLookup().canonicalTypeNameToType(canonicalTypeName);
|
|
|
|
|
|
if (type == null) {
|
|
|
- output.partialCanonicalTypeName = canonicalTypeName;
|
|
|
+ semanticScope.putDecoration(this, new PartialCanonicalTypeName(canonicalTypeName));
|
|
|
} else {
|
|
|
- if (input.write) {
|
|
|
+ if (write) {
|
|
|
throw createError(new IllegalArgumentException("invalid assignment: " +
|
|
|
"cannot write a value to a static type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "]"));
|
|
|
}
|
|
|
|
|
|
- if (input.read == false) {
|
|
|
- throw createError(new IllegalArgumentException(
|
|
|
- "not a statement: static type [" + PainlessLookupUtility.typeToCanonicalTypeName(type) + "] not used"));
|
|
|
- }
|
|
|
-
|
|
|
- output.actual = type;
|
|
|
- output.isStaticType = true;
|
|
|
+ semanticScope.putDecoration(this, new StaticType(type));
|
|
|
|
|
|
StaticNode staticNode = new StaticNode();
|
|
|
-
|
|
|
staticNode.setLocation(getLocation());
|
|
|
- staticNode.setExpressionType(output.actual);
|
|
|
-
|
|
|
+ staticNode.setExpressionType(type);
|
|
|
output.expressionNode = staticNode;
|
|
|
}
|
|
|
} else {
|
|
|
- Class<?> targetType = prefixOutput.actual;
|
|
|
- String targetCanonicalTypeName = PainlessLookupUtility.typeToCanonicalTypeName(targetType);
|
|
|
-
|
|
|
ExpressionNode expressionNode = null;
|
|
|
+ Class<?> valueType = null;
|
|
|
|
|
|
- if (prefixOutput.actual.isArray()) {
|
|
|
- if (output.isStaticType) {
|
|
|
- throw createError(new IllegalArgumentException("value required: " +
|
|
|
- "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
|
|
|
- }
|
|
|
-
|
|
|
+ if (prefixValueType != null && prefixValueType.getValueType().isArray()) {
|
|
|
if ("length".equals(index)) {
|
|
|
- if (input.write) {
|
|
|
+ if (write) {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
"invalid assignment: cannot assign a value write to read-only field [length] for an array."));
|
|
|
}
|
|
|
|
|
|
- output.actual = int.class;
|
|
|
+ valueType = int.class;
|
|
|
} else {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Field [" + index + "] does not exist for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Field [" + index + "] does not exist for type [" + prefixValueType.getValueCanonicalTypeName() + "]."));
|
|
|
}
|
|
|
|
|
|
DotSubArrayLengthNode dotSubArrayLengthNode = new DotSubArrayLengthNode();
|
|
|
dotSubArrayLengthNode.setLocation(getLocation());
|
|
|
- dotSubArrayLengthNode.setExpressionType(output.actual);
|
|
|
+ dotSubArrayLengthNode.setExpressionType(int.class);
|
|
|
expressionNode = dotSubArrayLengthNode;
|
|
|
- } else if (prefixOutput.actual == def.class) {
|
|
|
- if (output.isStaticType) {
|
|
|
- throw createError(new IllegalArgumentException("value required: " +
|
|
|
- "instead found unexpected type [" + PainlessLookupUtility.typeToCanonicalTypeName(output.actual) + "]"));
|
|
|
- }
|
|
|
-
|
|
|
+ } else if (prefixValueType != null && prefixValueType.getValueType() == def.class) {
|
|
|
+ TargetType targetType = semanticScope.getDecoration(this, TargetType.class);
|
|
|
// TODO: remove ZonedDateTime exception when JodaCompatibleDateTime is removed
|
|
|
- output.actual =
|
|
|
- input.expected == null || input.expected == ZonedDateTime.class || input.explicit ? def.class : input.expected;
|
|
|
- output.isDefOptimized = true;
|
|
|
+ valueType = targetType == null || targetType.getTargetType() == ZonedDateTime.class ||
|
|
|
+ semanticScope.getCondition(this, Explicit.class) ? def.class : targetType.getTargetType();
|
|
|
+ semanticScope.setCondition(this, DefOptimized.class);
|
|
|
|
|
|
DotSubDefNode dotSubDefNode = new DotSubDefNode();
|
|
|
dotSubDefNode.setLocation(getLocation());
|
|
|
- dotSubDefNode.setExpressionType(output.actual);
|
|
|
+ dotSubDefNode.setExpressionType(valueType);
|
|
|
dotSubDefNode.setValue(index);
|
|
|
expressionNode = dotSubDefNode;
|
|
|
} else {
|
|
|
- PainlessField field =
|
|
|
- scriptScope.getPainlessLookup().lookupPainlessField(prefixOutput.actual, prefixOutput.isStaticType, index);
|
|
|
+ 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 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(prefixOutput.actual, false,
|
|
|
+ getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
|
|
"get" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
|
|
|
|
|
if (getter == null) {
|
|
|
- getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
|
|
|
+ getter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixType, isStatic,
|
|
|
"is" + Character.toUpperCase(index.charAt(0)) + index.substring(1), 0);
|
|
|
}
|
|
|
|
|
|
- setter = scriptScope.getPainlessLookup().lookupPainlessMethod(prefixOutput.actual, false,
|
|
|
+ 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 createError(new IllegalArgumentException(
|
|
|
- "Illegal get shortcut on field [" + index + "] for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal get shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
if (setter != null && (setter.returnType != void.class || setter.typeParameters.size() != 1)) {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Illegal set shortcut on field [" + index + "] for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal set shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
if (getter != null && setter != null && setter.typeParameters.get(0) != getter.returnType) {
|
|
|
throw createError(new IllegalArgumentException("Shortcut argument types must match."));
|
|
|
}
|
|
|
|
|
|
- if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
|
|
|
- output.actual = setter != null ? setter.typeParameters.get(0) : getter.returnType;
|
|
|
+ if ((read == false || getter != null) && (write == false || setter != null)) {
|
|
|
+ valueType = setter != null ? setter.typeParameters.get(0) : getter.returnType;
|
|
|
} else {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Illegal shortcut on field [" + index + "] for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal shortcut on field [" + index + "] for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
DotSubShortcutNode dotSubShortcutNode = new DotSubShortcutNode();
|
|
|
dotSubShortcutNode.setLocation(getLocation());
|
|
|
- dotSubShortcutNode.setExpressionType(output.actual);
|
|
|
+ dotSubShortcutNode.setExpressionType(valueType);
|
|
|
dotSubShortcutNode.setGetter(getter);
|
|
|
dotSubShortcutNode.setSetter(setter);
|
|
|
expressionNode = dotSubShortcutNode;
|
|
|
- } else {
|
|
|
- if (Map.class.isAssignableFrom(prefixOutput.actual)) {
|
|
|
- getter = scriptScope.getPainlessLookup().lookupPainlessMethod(targetType, false, "get", 1);
|
|
|
- setter = scriptScope.getPainlessLookup().lookupPainlessMethod(targetType, false, "put", 2);
|
|
|
+ } 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 createError(new IllegalArgumentException(
|
|
|
- "Illegal map get shortcut for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal map get shortcut for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
if (setter != null && setter.typeParameters.size() != 2) {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Illegal map set shortcut for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal map set shortcut for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
if (getter != null && setter != null && (!getter.typeParameters.get(0).equals(setter.typeParameters.get(0)) ||
|
|
@@ -230,11 +251,11 @@ public class EDot extends AExpression {
|
|
|
throw createError(new IllegalArgumentException("Shortcut argument types must match."));
|
|
|
}
|
|
|
|
|
|
- if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
|
|
|
- output.actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
|
|
+ if ((read == false || getter != null) && (write == false || setter != null)) {
|
|
|
+ valueType = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
|
|
} else {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Illegal map shortcut for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal map shortcut for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
ConstantNode constantNode = new ConstantNode();
|
|
@@ -245,13 +266,13 @@ public class EDot extends AExpression {
|
|
|
MapSubShortcutNode mapSubShortcutNode = new MapSubShortcutNode();
|
|
|
mapSubShortcutNode.setChildNode(constantNode);
|
|
|
mapSubShortcutNode.setLocation(getLocation());
|
|
|
- mapSubShortcutNode.setExpressionType(output.actual);
|
|
|
+ mapSubShortcutNode.setExpressionType(valueType);
|
|
|
mapSubShortcutNode.setGetter(getter);
|
|
|
mapSubShortcutNode.setSetter(setter);
|
|
|
expressionNode = mapSubShortcutNode;
|
|
|
}
|
|
|
|
|
|
- if (List.class.isAssignableFrom(prefixOutput.actual)) {
|
|
|
+ if (List.class.isAssignableFrom(prefixType)) {
|
|
|
int index;
|
|
|
|
|
|
try {
|
|
@@ -260,30 +281,30 @@ public class EDot extends AExpression {
|
|
|
throw createError(new IllegalArgumentException("invalid list index [" + this.index + "]"));
|
|
|
}
|
|
|
|
|
|
- getter = scriptScope.getPainlessLookup().lookupPainlessMethod(targetType, false, "get", 1);
|
|
|
- setter = scriptScope.getPainlessLookup().lookupPainlessMethod(targetType, false, "set", 2);
|
|
|
+ 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 createError(new IllegalArgumentException(
|
|
|
- "Illegal list get shortcut for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal list get shortcut for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
if (setter != null && (setter.typeParameters.size() != 2 || setter.typeParameters.get(0) != int.class)) {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Illegal list set shortcut for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "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 createError(new IllegalArgumentException("Shortcut argument types must match."));
|
|
|
}
|
|
|
-
|
|
|
- if ((input.read == false || getter != null) && (input.write == false || setter != null)) {
|
|
|
- output.actual = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
|
|
+
|
|
|
+ if ((read == false || getter != null) && (write == false || setter != null)) {
|
|
|
+ valueType = setter != null ? setter.typeParameters.get(1) : getter.returnType;
|
|
|
} else {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
- "Illegal list shortcut for type [" + targetCanonicalTypeName + "]."));
|
|
|
+ "Illegal list shortcut for type [" + prefixCanonicalTypeName + "]."));
|
|
|
}
|
|
|
|
|
|
ConstantNode constantNode = new ConstantNode();
|
|
@@ -294,47 +315,54 @@ public class EDot extends AExpression {
|
|
|
ListSubShortcutNode listSubShortcutNode = new ListSubShortcutNode();
|
|
|
listSubShortcutNode.setChildNode(constantNode);
|
|
|
listSubShortcutNode.setLocation(getLocation());
|
|
|
- listSubShortcutNode.setExpressionType(output.actual);
|
|
|
+ listSubShortcutNode.setExpressionType(valueType);
|
|
|
listSubShortcutNode.setGetter(getter);
|
|
|
listSubShortcutNode.setSetter(setter);
|
|
|
expressionNode = listSubShortcutNode;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (expressionNode == null) {
|
|
|
- throw createError(new IllegalArgumentException(
|
|
|
- "field [" + typeToCanonicalTypeName(prefixOutput.actual) + ", " + index + "] not found"));
|
|
|
+ if (valueType == null) {
|
|
|
+ if (prefixValueType != null) {
|
|
|
+ throw createError(new IllegalArgumentException(
|
|
|
+ "field [" + prefixValueType.getValueCanonicalTypeName() + ", " + index + "] not found"));
|
|
|
+ } else {
|
|
|
+ throw createError(new IllegalArgumentException(
|
|
|
+ "field [" + prefixStaticType.getStaticCanonicalTypeName() + ", " + index + "] not found"));
|
|
|
+ }
|
|
|
}
|
|
|
} else {
|
|
|
- if (input.write && Modifier.isFinal(field.javaField.getModifiers())) {
|
|
|
+ if (write && Modifier.isFinal(field.javaField.getModifiers())) {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
"invalid assignment: cannot assign a value to read-only field [" + field.javaField.getName() + "]"));
|
|
|
}
|
|
|
|
|
|
- output.actual = field.typeParameter;
|
|
|
+ valueType = field.typeParameter;
|
|
|
|
|
|
DotSubNode dotSubNode = new DotSubNode();
|
|
|
dotSubNode.setLocation(getLocation());
|
|
|
- dotSubNode.setExpressionType(output.actual);
|
|
|
+ dotSubNode.setExpressionType(valueType);
|
|
|
dotSubNode.setField(field);
|
|
|
expressionNode = dotSubNode;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ semanticScope.putDecoration(this, new ValueType(valueType));
|
|
|
+
|
|
|
if (isNullSafe) {
|
|
|
- if (input.write) {
|
|
|
+ if (write) {
|
|
|
throw createError(new IllegalArgumentException(
|
|
|
"invalid assignment: cannot assign a value to a null safe operation [?.]"));
|
|
|
}
|
|
|
|
|
|
- if (output.actual.isPrimitive()) {
|
|
|
+ if (valueType.isPrimitive()) {
|
|
|
throw new IllegalArgumentException("Result of null safe operator must be nullable");
|
|
|
}
|
|
|
|
|
|
NullSafeSubNode nullSafeSubNode = new NullSafeSubNode();
|
|
|
nullSafeSubNode.setChildNode(expressionNode);
|
|
|
nullSafeSubNode.setLocation(getLocation());
|
|
|
- nullSafeSubNode.setExpressionType(output.actual);
|
|
|
+ nullSafeSubNode.setExpressionType(valueType);
|
|
|
expressionNode = nullSafeSubNode;
|
|
|
}
|
|
|
|
|
@@ -342,7 +370,7 @@ public class EDot extends AExpression {
|
|
|
dotNode.setLeftNode(prefixOutput.expressionNode);
|
|
|
dotNode.setRightNode(expressionNode);
|
|
|
dotNode.setLocation(getLocation());
|
|
|
- dotNode.setExpressionType(output.actual);
|
|
|
+ dotNode.setExpressionType(valueType);
|
|
|
output.expressionNode = dotNode;
|
|
|
}
|
|
|
|