You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2021/11/20 18:09:09 UTC
[groovy] 01/02: refactor `BinaryExpressionTransformer`
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 2c1608903b946430cb055acc948a10d70d40e695
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Nov 20 11:57:31 2021 -0600
refactor `BinaryExpressionTransformer`
---
.../transformers/BinaryExpressionTransformer.java | 583 +++++++++++----------
1 file changed, 296 insertions(+), 287 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
index 3dfd7c8..8ec2ec7 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
@@ -62,14 +62,6 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.isNullX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ternaryX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
-import static org.codehaus.groovy.ast.ClassHelper.isBigDecimalType;
-import static org.codehaus.groovy.ast.ClassHelper.isBigIntegerType;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperByte;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperDouble;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperFloat;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperInteger;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperLong;
-import static org.codehaus.groovy.ast.ClassHelper.isWrapperShort;
public class BinaryExpressionTransformer {
private static final MethodNode COMPARE_TO_METHOD = ClassHelper.COMPARABLE_TYPE.getMethods("compareTo").get(0);
@@ -86,250 +78,319 @@ public class BinaryExpressionTransformer {
}
public Expression transformBinaryExpression(final BinaryExpression bin) {
- if (bin instanceof DeclarationExpression) {
- Expression optimized = transformDeclarationExpression(bin);
- if (optimized != null) {
- return optimized;
- }
- }
-
- Token operation = bin.getOperation();
- int operationType = operation.getType();
Expression leftExpression = bin.getLeftExpression();
Expression rightExpression = bin.getRightExpression();
+
if (bin instanceof DeclarationExpression
&& leftExpression instanceof VariableExpression
- && rightExpression instanceof ConstantExpression) {
+ && rightExpression instanceof ConstantExpression
+ && !((ConstantExpression) rightExpression).isNullExpression()) {
ClassNode declarationType = ((VariableExpression) leftExpression).getOriginType();
+ // for "char x = 'c'" change 'c' from String to char
+ if (declarationType.equals(ClassHelper.char_TYPE)) {
+ Character c = tryCharConstant(rightExpression);
+ if (c != null)
+ return transformCharacterInitialization(bin, c);
+ }
+ // for "float|double|BigDecimal x = n" change n's type
if (!rightExpression.getType().equals(declarationType)
&& ClassHelper.getWrapper(declarationType).isDerivedFrom(ClassHelper.Number_TYPE)
&& WideningCategories.isDoubleCategory(ClassHelper.getUnwrapper(declarationType))) {
- ConstantExpression constant = (ConstantExpression) rightExpression;
- if (!constant.isNullExpression()) {
- return optimizeConstantInitialization(bin, operation, constant, leftExpression, declarationType);
- }
+ return transformFloatingPointInitialization(bin, (Number) ((ConstantExpression) rightExpression).getValue(), declarationType);
}
}
- if (operationType == Types.ASSIGN) {
- // GROOVY-10029: add "?.toArray(new T[0])" to "T[] array = collectionOfT" assignments
- ClassNode leftType = findType(leftExpression), rightType = findType(rightExpression);
- if (leftType.isArray() && !(rightExpression instanceof ListExpression) && isOrImplements(rightType, ClassHelper.COLLECTION_TYPE)) {
- ArrayExpression emptyArray = new ArrayExpression(leftType.getComponentType(), null, Collections.singletonList(CONSTANT_ZERO));
- rightExpression = callX(rightExpression, "toArray", args(emptyArray));
- rightExpression.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, leftType);
- ((MethodCallExpression) rightExpression).setMethodTarget(
- rightType.getMethod("toArray", new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "a")}));
- ((MethodCallExpression) rightExpression).setImplicitThis(false);
- ((MethodCallExpression) rightExpression).setSafe(true);
- }
- MethodNode directMCT = leftExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
- if (directMCT != null) {
- Expression left = staticCompilationTransformer.transform(leftExpression);
- Expression right = staticCompilationTransformer.transform(rightExpression);
- if (left instanceof PropertyExpression) {
- // transform "a.x = val" into "def tmp = val; a.setX(tmp); tmp"
- PropertyExpression pe = (PropertyExpression) left;
- return transformAssignmentToSetterCall(
- pe.getObjectExpression(), // "a"
- directMCT, // "setX"
- right, // "val"
- false,
- pe.isSafe(),
- pe.getProperty(), // "x"
- bin // "a.x = val"
- );
- } else if (left instanceof VariableExpression) {
- // transform "x = val" into "def tmp = val; this.setX(tmp); tmp"
- return transformAssignmentToSetterCall(
- varX("this"),
- directMCT, // "setX"
- right, // "val"
- true,
- false,
- left, // "x"
- bin // "x = val"
- );
- }
+ boolean equal = false;
+ switch (bin.getOperation().getType()) {
+ case Types.ASSIGN:
+ optimizeArrayCollectionAssignment(bin); // GROOVY-10029
+ Expression expr = transformAssignmentToSetterCall(bin);
+ if (expr != null) return expr;
+ if (leftExpression instanceof TupleExpression
+ && rightExpression instanceof ListExpression) {
+ return transformMultipleAssignment(bin);
}
+ break;
+ case Types.KEYWORD_IN:
+ return transformInOperation(bin);
+ case Types.COMPARE_EQUAL:
+ equal = true; //fallthrough
+ case Types.COMPARE_NOT_EQUAL:
+ expr = transformEqualityComparison(bin, equal);
+ if (expr != null) return expr;
+ }
- // if not transformed to setter call but RHS has been transformed...
- if (rightExpression != bin.getRightExpression()) {
- bin.setRightExpression(rightExpression);
- }
- } else if (operationType == Types.COMPARE_EQUAL || operationType == Types.COMPARE_NOT_EQUAL) {
- // let's check if one of the operands is the null constant
- CompareToNullExpression compareToNullExpression = null;
- if (isNullConstant(leftExpression)) {
- compareToNullExpression = new CompareToNullExpression(staticCompilationTransformer.transform(rightExpression), operationType == Types.COMPARE_EQUAL);
- } else if (isNullConstant(rightExpression)) {
- compareToNullExpression = new CompareToNullExpression(staticCompilationTransformer.transform(leftExpression), operationType == Types.COMPARE_EQUAL);
- }
- if (compareToNullExpression != null) {
- compareToNullExpression.setSourcePosition(bin);
- return compareToNullExpression;
- }
- } else if (operationType == Types.KEYWORD_IN) {
- // transform "left in right" into "right.isCase(left)"
- MethodCallExpression call = callX(rightExpression, "isCase", leftExpression);
- call.setImplicitThis(false); call.setSourcePosition(bin); call.copyNodeMetaData(bin);
- call.setMethodTarget(bin.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
- // GROOVY-7473: no null test for simple cases
- if (rightExpression instanceof ListExpression
- || rightExpression instanceof MapExpression
- || rightExpression instanceof RangeExpression
- || rightExpression instanceof ConstantExpression
- && !isNullConstant(rightExpression))
- return staticCompilationTransformer.transform(call);
-
- // then "right == null ? left == null : right.isCase(left)" for null safety
- return staticCompilationTransformer.transform(ternaryX(isNullX(rightExpression), isNullX(leftExpression), call));
+ Object[] array = bin.getNodeMetaData(StaticCompilationMetadataKeys.BINARY_EXP_TARGET);
+ if (array != null) {
+ return transformToTargetMethodCall(bin, (MethodNode) array[0], (String) array[1]);
}
- Object[] list = bin.getNodeMetaData(StaticCompilationMetadataKeys.BINARY_EXP_TARGET);
- if (list != null) {
- MethodCallExpression call;
- Expression left = staticCompilationTransformer.transform(leftExpression);
- Expression right = staticCompilationTransformer.transform(rightExpression);
-
- if (operationType == Types.COMPARE_TO
- && findType(left).implementsInterface(ClassHelper.COMPARABLE_TYPE)
- && findType(right).implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
- call = callX(left, "compareTo", args(right));
- call.setImplicitThis(false);
- call.setMethodTarget(COMPARE_TO_METHOD);
- call.setSourcePosition(bin);
-
- // right == null ? 1 : left.compareTo(right)
- Expression expr = ternaryX(
- new CompareToNullExpression(right, true),
- CONSTANT_ONE,
- call
- );
- expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
-
- // left == null ? -1 : (right == null ? 1 : left.compareTo(right))
- expr = ternaryX(
- new CompareToNullExpression(left, true),
- CONSTANT_MINUS_ONE,
- expr
- );
- expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
-
- // left === right ? 0 : (left == null ? -1 : (right == null ? 1 : left.compareTo(right)))
- expr = ternaryX(
- new CompareIdentityExpression(left, right),
- CONSTANT_ZERO,
- expr
- );
- expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
-
- return expr;
- }
+ return staticCompilationTransformer.superTransform(bin);
+ }
- Expression expr = tryOptimizeCharComparison(left, right, bin);
- if (expr != null) {
- expr.removeNodeMetaData(StaticCompilationMetadataKeys.BINARY_EXP_TARGET);
- expr.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
- return expr;
- }
+ private Expression transformCharacterInitialization(final BinaryExpression bin, final Character rhs) {
+ Expression ce = constX(rhs, true);
+ ce.setSourcePosition(bin.getRightExpression());
+
+ bin.setRightExpression(ce);
+ return bin;
+ }
+
+ private Expression transformFloatingPointInitialization(final BinaryExpression bin, final Number rhs, final ClassNode lhsType) {
+ Expression ce = constX(convertConstant(rhs, ClassHelper.getWrapper(lhsType)), true);
+ ce.setSourcePosition(bin.getRightExpression());
+ ce.setType(lhsType);
+
+ bin.setRightExpression(ce);
+ return bin;
+ }
- // replace the binary expression with a method call to ScriptBytecodeAdapter or something else
- MethodNode adapter = StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
- if (adapter != null) {
- Expression sba = classX(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
- call = callX(sba, adapter.getName(), args(left, right));
- call.setMethodTarget(adapter);
- } else {
- call = callX(left, (String) list[1], args(right));
- call.setMethodTarget((MethodNode) list[0]);
+ /**
+ * Adds "?.toArray(new T[0])" to "T[] array = collectionOfT" assignments.
+ */
+ private void optimizeArrayCollectionAssignment(final BinaryExpression bin) {
+ Expression rightExpression = bin.getRightExpression();
+ ClassNode leftType = findType(bin.getLeftExpression()), rightType = findType(rightExpression);
+ if (leftType.isArray() && !(rightExpression instanceof ListExpression) && isOrImplements(rightType, ClassHelper.COLLECTION_TYPE)) {
+ ArrayExpression emptyArray = new ArrayExpression(leftType.getComponentType(), null, Collections.singletonList(CONSTANT_ZERO));
+ rightExpression = callX(rightExpression, "toArray", args(emptyArray));
+ rightExpression.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, leftType);
+ ((MethodCallExpression) rightExpression).setMethodTarget(
+ rightType.getMethod("toArray", new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "a")}));
+ ((MethodCallExpression) rightExpression).setImplicitThis(false);
+ ((MethodCallExpression) rightExpression).setSafe(true);
+ bin.setRightExpression(rightExpression);
+ }
+ }
+
+ private Expression transformAssignmentToSetterCall(final BinaryExpression bin) {
+ MethodNode directMCT = bin.getLeftExpression().getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ if (directMCT != null) {
+ Expression left = staticCompilationTransformer.transform(bin.getLeftExpression());
+ Expression right = staticCompilationTransformer.transform(bin.getRightExpression());
+ if (left instanceof PropertyExpression) {
+ // transform "a.x = val" into "def tmp = val; a.setX(tmp); tmp"
+ PropertyExpression pe = (PropertyExpression) left;
+ return transformAssignmentToSetterCall(
+ pe.getObjectExpression(), // "a"
+ directMCT, // "setX"
+ right, // "val"
+ false,
+ pe.isSafe(),
+ pe.getProperty(), // "x"
+ bin);
}
- call.setImplicitThis(false);
- if (Types.isAssignment(operationType)) { // +=, -=, /=, ...
- // GROOVY-5746: one execution of receiver and subscript
- if (left instanceof BinaryExpression) {
- BinaryExpression be = (BinaryExpression) left;
- if (be.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
- be.setLeftExpression(new TemporaryVariableExpression(be.getLeftExpression()));
- be.setRightExpression(new TemporaryVariableExpression(be.getRightExpression()));
- }
- }
- // call handles the operation, so we must add the assignment now
- expr = binX(left, Token.newSymbol(Types.ASSIGN, operation.getStartLine(), operation.getStartColumn()), call);
- } else {
- expr = call;
+ if (left instanceof VariableExpression) {
+ // transform "x = val" into "def tmp = val; this.setX(tmp); tmp"
+ return transformAssignmentToSetterCall(
+ varX("this"),
+ directMCT, // "setX"
+ right, // "val"
+ true,
+ false,
+ left, // "x"
+ bin);
}
- expr.setSourcePosition(bin);
- return expr;
}
+ return null;
+ }
- if (operationType == Types.ASSIGN && leftExpression instanceof TupleExpression && rightExpression instanceof ListExpression) {
- // multiple assignment
- ListOfExpressionsExpression cle = new ListOfExpressionsExpression();
- boolean isDeclaration = (bin instanceof DeclarationExpression);
- List<Expression> leftExpressions = ((TupleExpression) leftExpression).getExpressions();
- List<Expression> rightExpressions = ((ListExpression) rightExpression).getExpressions();
- Iterator<Expression> leftIt = leftExpressions.iterator();
- Iterator<Expression> rightIt = rightExpressions.iterator();
- if (isDeclaration) {
- while (leftIt.hasNext()) {
- Expression left = leftIt.next();
- if (rightIt.hasNext()) {
- Expression right = rightIt.next();
- BinaryExpression bexp = new DeclarationExpression(left, operation, right);
- bexp.setSourcePosition(right);
- cle.addExpression(bexp);
- }
- }
- } else {
- // (next, result) = [ result, next+result ]
- // -->
- // def tmp1 = result
- // def tmp2 = next+result
- // next = tmp1
- // result = tmp2
- int size = rightExpressions.size();
- List<Expression> tmpAssignments = new ArrayList<>(size);
- List<Expression> finalAssignments = new ArrayList<>(size);
- for (int i = 0, n = Math.min(size, leftExpressions.size()); i < n; i += 1) {
- Expression left = leftIt.next();
+ /**
+ * Adapter for {@link StaticPropertyAccessHelper#transformToSetterCall}.
+ */
+ private static Expression transformAssignmentToSetterCall(
+ final Expression receiver,
+ final MethodNode setterMethod,
+ final Expression valueExpression,
+ final boolean implicitThis,
+ final boolean safeNavigation,
+ final Expression nameExpression,
+ final Expression sourceExpression) {
+ // expression that will transfer assignment and name positions
+ Expression pos = new PropertyExpression(null, nameExpression);
+ pos.setSourcePosition(sourceExpression);
+
+ return StaticPropertyAccessHelper.transformToSetterCall(
+ receiver,
+ setterMethod,
+ valueExpression,
+ implicitThis,
+ safeNavigation,
+ false, // spreadSafe
+ true, // TODO: replace with a proper test whether a return value is required or not
+ pos);
+ }
+
+ private Expression transformInOperation(final BinaryExpression bin) {
+ Expression leftExpression = bin.getLeftExpression();
+ Expression rightExpression = bin.getRightExpression();
+
+ // transform "left in right" into "right.isCase(left)"
+ MethodCallExpression call = callX(rightExpression, "isCase", leftExpression);
+ call.setImplicitThis(false); call.setSourcePosition(bin); call.copyNodeMetaData(bin);
+ call.setMethodTarget(bin.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET));
+ // GROOVY-7473: no null test for simple cases
+ if (rightExpression instanceof ListExpression
+ || rightExpression instanceof MapExpression
+ || rightExpression instanceof RangeExpression
+ || rightExpression instanceof ConstantExpression
+ && !isNullConstant(rightExpression))
+ return staticCompilationTransformer.transform(call);
+
+ // then "right == null ? left == null : right.isCase(left)" for null safety
+ return staticCompilationTransformer.transform(ternaryX(isNullX(rightExpression), isNullX(leftExpression), call));
+ }
+
+ private Expression transformEqualityComparison(final BinaryExpression bin, final boolean eq) {
+ if (isNullConstant(bin.getRightExpression())) {
+ Expression ctn = new CompareToNullExpression(staticCompilationTransformer.transform(bin.getLeftExpression()), eq);
+ ctn.setSourcePosition(bin);
+ return ctn;
+ }
+ if (isNullConstant(bin.getLeftExpression())) {
+ Expression ctn = new CompareToNullExpression(staticCompilationTransformer.transform(bin.getRightExpression()), eq);
+ ctn.setSourcePosition(bin);
+ return ctn;
+ }
+ return null;
+ }
+
+ private Expression transformMultipleAssignment(final BinaryExpression bin) {
+ ListOfExpressionsExpression list = new ListOfExpressionsExpression();
+ List<Expression> leftExpressions = ((TupleExpression) bin.getLeftExpression()).getExpressions();
+ List<Expression> rightExpressions = ((ListExpression) bin.getRightExpression()).getExpressions();
+ Iterator<Expression> leftIt = leftExpressions.iterator();
+ Iterator<Expression> rightIt = rightExpressions.iterator();
+ if (bin instanceof DeclarationExpression) {
+ while (leftIt.hasNext()) {
+ Expression left = leftIt.next();
+ if (rightIt.hasNext()) {
Expression right = rightIt.next();
- VariableExpression tmpVar = varX("$tmpVar$" + tmpVarCounter++);
- BinaryExpression bexp = new DeclarationExpression(tmpVar, operation, right);
+ BinaryExpression bexp = new DeclarationExpression(left, bin.getOperation(), right);
bexp.setSourcePosition(right);
- tmpAssignments.add(bexp);
- bexp = binX(left, operation, varX(tmpVar));
- bexp.setSourcePosition(left);
- finalAssignments.add(bexp);
- }
- for (Expression tmpAssignment : tmpAssignments) {
- cle.addExpression(tmpAssignment);
- }
- for (Expression finalAssignment : finalAssignments) {
- cle.addExpression(finalAssignment);
+ list.addExpression(bexp);
}
}
- return staticCompilationTransformer.transform(cle);
+ } else {
+ // (next, result) = [ result, next+result ]
+ // -->
+ // def tmp1 = result
+ // def tmp2 = next+result
+ // next = tmp1
+ // result = tmp2
+ int size = rightExpressions.size();
+ List<Expression> tmpAssignments = new ArrayList<>(size);
+ List<Expression> finalAssignments = new ArrayList<>(size);
+ for (int i = 0, n = Math.min(size, leftExpressions.size()); i < n; i += 1) {
+ Expression left = leftIt.next();
+ Expression right = rightIt.next();
+ VariableExpression tmpVar = varX("$tmpVar$" + tmpVarCounter++);
+ BinaryExpression bexp = new DeclarationExpression(tmpVar, bin.getOperation(), right);
+ bexp.setSourcePosition(right);
+ tmpAssignments.add(bexp);
+ bexp = binX(left, bin.getOperation(), varX(tmpVar));
+ bexp.setSourcePosition(left);
+ finalAssignments.add(bexp);
+ }
+ for (Expression tmpAssignment : tmpAssignments) {
+ list.addExpression(tmpAssignment);
+ }
+ for (Expression finalAssignment : finalAssignments) {
+ list.addExpression(finalAssignment);
+ }
}
- return staticCompilationTransformer.superTransform(bin);
+ return staticCompilationTransformer.transform(list);
}
- private ClassNode findType(final Expression expression) {
- ClassNode classNode = staticCompilationTransformer.getClassNode();
- return staticCompilationTransformer.getTypeChooser().resolveType(expression, classNode);
+ private Expression transformToTargetMethodCall(final BinaryExpression bin, final MethodNode node, final String name) {
+ MethodCallExpression call;
+ Token operation = bin.getOperation();
+ int operationType = operation.getType();
+ Expression left = staticCompilationTransformer.transform(bin.getLeftExpression());
+ Expression right = staticCompilationTransformer.transform(bin.getRightExpression());
+
+ if (operationType == Types.COMPARE_TO
+ && findType(left).implementsInterface(ClassHelper.COMPARABLE_TYPE)
+ && findType(right).implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
+ call = callX(left, "compareTo", args(right));
+ call.setImplicitThis(false);
+ call.setMethodTarget(COMPARE_TO_METHOD);
+ call.setSourcePosition(bin);
+
+ // right == null ? 1 : left.compareTo(right)
+ Expression expr = ternaryX(
+ new CompareToNullExpression(right, true),
+ CONSTANT_ONE,
+ call
+ );
+ expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
+
+ // left == null ? -1 : (right == null ? 1 : left.compareTo(right))
+ expr = ternaryX(
+ new CompareToNullExpression(left, true),
+ CONSTANT_MINUS_ONE,
+ expr
+ );
+ expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
+
+ // left === right ? 0 : (left == null ? -1 : (right == null ? 1 : left.compareTo(right)))
+ expr = ternaryX(
+ new CompareIdentityExpression(left, right),
+ CONSTANT_ZERO,
+ expr
+ );
+ expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
+
+ return expr;
+ }
+
+ Expression expr = tryOptimizeCharComparison(left, right, bin);
+ if (expr != null) {
+ expr.removeNodeMetaData(StaticCompilationMetadataKeys.BINARY_EXP_TARGET);
+ expr.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ return expr;
+ }
+
+ // replace the binary expression with a method call to ScriptBytecodeAdapter or something else
+ MethodNode adapter = StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
+ if (adapter != null) {
+ Expression sba = classX(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
+ call = callX(sba, adapter.getName(), args(left, right));
+ call.setMethodTarget(adapter);
+ } else {
+ call = callX(left, name, args(right));
+ call.setMethodTarget(node);
+ }
+ call.setImplicitThis(false);
+ if (Types.isAssignment(operationType)) { // +=, -=, /=, ...
+ // GROOVY-5746: one execution of receiver and subscript
+ if (left instanceof BinaryExpression) {
+ BinaryExpression be = (BinaryExpression) left;
+ if (be.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
+ be.setLeftExpression(new TemporaryVariableExpression(be.getLeftExpression()));
+ be.setRightExpression(new TemporaryVariableExpression(be.getRightExpression()));
+ }
+ }
+ // call handles the operation, so we must add the assignment now
+ expr = binX(left, Token.newSymbol(Types.ASSIGN, operation.getStartLine(), operation.getStartColumn()), call);
+ } else {
+ expr = call;
+ }
+ expr.setSourcePosition(bin);
+ return expr;
}
- private static BinaryExpression tryOptimizeCharComparison(final Expression left, final Expression right, final BinaryExpression bin) {
+ private BinaryExpression tryOptimizeCharComparison(final Expression left, final Expression right, final BinaryExpression bin) {
int op = bin.getOperation().getType();
if (StaticTypeCheckingSupport.isCompareToBoolean(op) || op == Types.COMPARE_EQUAL || op == Types.COMPARE_NOT_EQUAL) {
Character cLeft = tryCharConstant(left);
Character cRight = tryCharConstant(right);
if (cLeft != null || cRight != null) {
Expression oLeft = (cLeft == null ? left : constX(cLeft, true));
- if (oLeft instanceof PropertyExpression && !hasCharType((PropertyExpression)oLeft)) return null;
+ if (oLeft instanceof PropertyExpression && !hasCharType(oLeft)) return null;
oLeft.setSourcePosition(left);
Expression oRight = (cRight == null ? right : constX(cRight, true));
- if (oRight instanceof PropertyExpression && !hasCharType((PropertyExpression)oRight)) return null;
+ if (oRight instanceof PropertyExpression && !hasCharType(oRight)) return null;
oRight.setSourcePosition(right);
bin.setLeftExpression(oLeft);
bin.setRightExpression(oRight);
@@ -339,104 +400,52 @@ public class BinaryExpressionTransformer {
return null;
}
- private static boolean hasCharType(PropertyExpression pe) {
- ClassNode inferredType = pe.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE);
- return inferredType != null && ClassHelper.Character_TYPE.equals(ClassHelper.getWrapper(inferredType));
+ //--------------------------------------------------------------------------
+
+ private ClassNode findType(final Expression e) {
+ return staticCompilationTransformer.getTypeChooser().resolveType(e, staticCompilationTransformer.getClassNode());
}
- private static Character tryCharConstant(final Expression expr) {
- if (expr instanceof ConstantExpression && ClassHelper.STRING_TYPE.equals(expr.getType())) {
- String value = (String) ((ConstantExpression) expr).getValue();
- if (value != null && value.length() == 1) {
- return value.charAt(0);
- }
- }
- return null;
+ private boolean hasCharType(final Expression e) {
+ ClassNode inferredType = e.getNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE); //TODO:findType(e);
+ return inferredType != null && ClassHelper.getWrapper(inferredType).equals(ClassHelper.Character_TYPE);
}
- private static Expression transformDeclarationExpression(final BinaryExpression bin) {
- Expression leftExpression = bin.getLeftExpression();
- if (leftExpression instanceof VariableExpression) {
- if (ClassHelper.char_TYPE.equals(((VariableExpression) leftExpression).getOriginType())) {
- Expression rightExpression = bin.getRightExpression();
- Character c = tryCharConstant(rightExpression);
- if (c != null) {
- Expression ce = constX(c, true);
- ce.setSourcePosition(rightExpression);
- bin.setRightExpression(ce);
- return bin;
- }
+ private static Character tryCharConstant(final Expression e) {
+ if (e instanceof ConstantExpression && ClassHelper.STRING_TYPE.equals(e.getType())) {
+ String value = (String) ((ConstantExpression) e).getValue();
+ if (value != null && value.length() == 1) {
+ return value.charAt(0);
}
}
return null;
}
- private static DeclarationExpression optimizeConstantInitialization(final BinaryExpression originalDeclaration, final Token operation, final ConstantExpression constant, final Expression leftExpression, final ClassNode declarationType) {
- Expression cexp = constX(convertConstant((Number) constant.getValue(), ClassHelper.getWrapper(declarationType)), true);
- cexp.setType(declarationType);
- cexp.setSourcePosition(constant);
- DeclarationExpression result = new DeclarationExpression(
- leftExpression,
- operation,
- cexp
- );
- result.setSourcePosition(originalDeclaration);
- result.copyNodeMetaData(originalDeclaration);
- return result;
- }
-
private static Object convertConstant(final Number source, final ClassNode target) {
- if (isWrapperByte(target)) {
+ if (ClassHelper.isWrapperByte(target)) {
return source.byteValue();
}
- if (isWrapperShort(target)) {
+ if (ClassHelper.isWrapperShort(target)) {
return source.shortValue();
}
- if (isWrapperInteger(target)) {
+ if (ClassHelper.isWrapperInteger(target)) {
return source.intValue();
}
- if (isWrapperLong(target)) {
+ if (ClassHelper.isWrapperLong(target)) {
return source.longValue();
}
- if (isWrapperFloat(target)) {
+ if (ClassHelper.isWrapperFloat(target)) {
return source.floatValue();
}
- if (isWrapperDouble(target)) {
+ if (ClassHelper.isWrapperDouble(target)) {
return source.doubleValue();
}
- if (isBigIntegerType(target)) {
+ if (ClassHelper.isBigIntegerType(target)) {
return DefaultGroovyMethods.asType(source, BigInteger.class);
}
- if (isBigDecimalType(target)) {
+ if (ClassHelper.isBigDecimalType(target)) {
return DefaultGroovyMethods.asType(source, BigDecimal.class);
}
- throw new IllegalArgumentException("Unsupported conversion");
- }
-
- /**
- * Adapter for {@link StaticPropertyAccessHelper#transformToSetterCall}.
- */
- private static Expression transformAssignmentToSetterCall(
- final Expression receiver,
- final MethodNode setterMethod,
- final Expression valueExpression,
- final boolean implicitThis,
- final boolean safeNavigation,
- final Expression nameExpression,
- final Expression binaryExpression) {
- // expression that will transfer assignment and name positions
- Expression pos = new PropertyExpression(null, nameExpression);
- pos.setSourcePosition(binaryExpression);
-
- return StaticPropertyAccessHelper.transformToSetterCall(
- receiver,
- setterMethod,
- valueExpression,
- implicitThis,
- safeNavigation,
- false, // spreadSafe
- true, // TODO: replace with a proper test whether a return value is required or not
- pos
- );
+ throw new IllegalArgumentException("Unsupported numerical conversion");
}
}