From 936e3f0d84733ab496800ea8ce03e2c9a95bb0be Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Tue, 13 Dec 2011 17:44:06 +0400 Subject: [PATCH] support for in operation syntax, refactor, test, range sugar syntax --- .../google/dart/compiler/util/AstUtil.java | 5 ++- .../k2js/translate/intrinsic/Intrinsics.java | 26 +++++++----- .../PrimitiveCompareToIntrinsic.java | 3 ++ .../primitive/PrimitiveEqualsIntrinsic.java | 3 ++ .../primitive/PrimitiveRangeToIntrinsic.java | 40 +++++++++++++++++++ .../operation/BinaryOperationTranslator.java | 14 +++++-- .../translate/reference/CallTranslator.java | 35 +++++++++++++--- .../k2js/translate/utils/PsiUtils.java | 4 ++ .../org/jetbrains/k2js/test/RangeTest.java | 10 +++++ .../testFiles/range/cases/intInRange.kt | 13 ++++++ .../testFiles/range/cases/rangeSugarSyntax.kt | 31 ++++++++++++++ 11 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java create mode 100644 translator/testFiles/range/cases/intInRange.kt create mode 100644 translator/testFiles/range/cases/rangeSugarSyntax.kt diff --git a/js/src/com/google/dart/compiler/util/AstUtil.java b/js/src/com/google/dart/compiler/util/AstUtil.java index 31ed38f1510..9bafb490b94 100644 --- a/js/src/com/google/dart/compiler/util/AstUtil.java +++ b/js/src/com/google/dart/compiler/util/AstUtil.java @@ -328,8 +328,11 @@ public class AstUtil { return equals(expression, program.getTrueLiteral()); } - public static JsBinaryOperation sum(JsExpression left, JsExpression right) { return new JsBinaryOperation(JsBinaryOperator.ADD, left, right); } + + public static JsBinaryOperation subtract(JsExpression left, JsExpression right) { + return new JsBinaryOperation(JsBinaryOperator.SUB, left, right); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java index 049b8659619..acabf4e06fc 100644 --- a/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java @@ -4,21 +4,18 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.types.JetStandardLibrary; -import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.k2js.translate.intrinsic.array.ArrayGetIntrinsic; import org.jetbrains.k2js.translate.intrinsic.array.ArrayNullConstructorIntrinsic; import org.jetbrains.k2js.translate.intrinsic.array.ArraySetIntrinsic; -import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveBinaryOperationIntrinsic; -import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveCompareToIntrinsic; -import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveEqualsIntrinsic; -import org.jetbrains.k2js.translate.intrinsic.primitive.PrimitiveUnaryOperationIntrinsic; +import org.jetbrains.k2js.translate.intrinsic.primitive.*; import org.jetbrains.k2js.translate.operation.OperatorTable; import org.jetbrains.k2js.translate.utils.DescriptorUtils; import java.util.HashMap; import java.util.Map; +import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getFunctionByName; /** @@ -55,6 +52,7 @@ public final class Intrinsics { private void declareOperatorIntrinsics() { IntrinsicDeclarationVisitor visitor = new IntrinsicDeclarationVisitor(this); for (DeclarationDescriptor descriptor : library.getLibraryScope().getAllDescriptors()) { + //noinspection NullableProblems descriptor.accept(visitor, null); } } @@ -75,25 +73,33 @@ public final class Intrinsics { addEqualsIntrinsics(descriptor); addUnaryIntrinsics(descriptor); addBinaryIntrinsics(descriptor); + addRangeToIntrinsics(descriptor); + } + + private void addRangeToIntrinsics(@NotNull FunctionDescriptor descriptor) { + String functionName = descriptor.getName(); + if (functionName.equals("rangeTo")) { + functionIntrinsics.put(descriptor, PrimitiveRangeToIntrinsic.newInstance()); + } } private void addCompareToIntrinsics(@NotNull FunctionDescriptor descriptor) { String functionName = descriptor.getName(); - if (functionName.equals("compareTo")) { + if (functionName.equals(COMPARE_TO)) { compareToIntrinsics.put(descriptor, PrimitiveCompareToIntrinsic.newInstance()); } } private void addEqualsIntrinsics(@NotNull FunctionDescriptor descriptor) { String functionName = descriptor.getName(); - if (functionName.equals("equals")) { + if (functionName.equals(EQUALS)) { equalsIntrinsics.put(descriptor, PrimitiveEqualsIntrinsic.newInstance()); } } private void addUnaryIntrinsics(@NotNull FunctionDescriptor descriptor) { String functionName = descriptor.getName(); - JetToken token = OperatorConventions.UNARY_OPERATION_NAMES.inverse().get(functionName); + JetToken token = UNARY_OPERATION_NAMES.inverse().get(functionName); if (token == null) return; boolean isUnary = !DescriptorUtils.hasParameters(descriptor); if (!isUnary) return; @@ -105,9 +111,9 @@ public final class Intrinsics { String functionName = descriptor.getName(); boolean isUnary = !DescriptorUtils.hasParameters(descriptor); if (isUnary) return; - JetToken token = OperatorConventions.BINARY_OPERATION_NAMES.inverse().get(functionName); + JetToken token = BINARY_OPERATION_NAMES.inverse().get(functionName); if (token == null) return; - //TODO: implement range and contains intrinsic + //TODO: implement contains intrinsic if (!OperatorTable.hasCorrespondingBinaryOperator(token)) return; functionIntrinsics.put(descriptor, PrimitiveBinaryOperationIntrinsic.newInstance(token)); } diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java index 0233c3bcf50..7f8241a4117 100644 --- a/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveCompareToIntrinsic.java @@ -20,6 +20,9 @@ public final class PrimitiveCompareToIntrinsic extends CompareToIntrinsic { return new PrimitiveCompareToIntrinsic(); } + private PrimitiveCompareToIntrinsic() { + } + @NotNull public JsExpression apply(@NotNull JsExpression receiver, @NotNull List arguments, @NotNull TranslationContext context) { diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java index f49c74cfa48..0f0b997d2da 100644 --- a/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveEqualsIntrinsic.java @@ -18,6 +18,9 @@ public final class PrimitiveEqualsIntrinsic extends EqualsIntrinsic { return new PrimitiveEqualsIntrinsic(); } + private PrimitiveEqualsIntrinsic() { + } + @NotNull public JsExpression apply(@NotNull JsExpression receiver, @NotNull List arguments, @NotNull TranslationContext context) { diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java new file mode 100644 index 00000000000..a3020724826 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/primitive/PrimitiveRangeToIntrinsic.java @@ -0,0 +1,40 @@ +package org.jetbrains.k2js.translate.intrinsic.primitive; + +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNew; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Talanov Pavel + */ +public final class PrimitiveRangeToIntrinsic implements FunctionIntrinsic { + + @NotNull + public static PrimitiveRangeToIntrinsic newInstance() { + return new PrimitiveRangeToIntrinsic(); + } + + private PrimitiveRangeToIntrinsic() { + } + + @NotNull + @Override + public JsExpression apply(@NotNull JsExpression rangeStart, @NotNull List arguments, + @NotNull TranslationContext context) { + assert arguments.size() == 1 : "RangeTo must have one argument."; + JsExpression rangeEnd = arguments.get(0); + JsBinaryOperation rangeSize = AstUtil.subtract(rangeEnd, rangeStart); + //TODO: provide a way not to hard code this value + JsNew numberRangeConstructorInvocation + = new JsNew(AstUtil.newQualifiedNameRef("Kotlin.NumberRange")); + numberRangeConstructorInvocation.setArguments(Arrays.asList(rangeStart, rangeSize)); + return numberRangeConstructorInvocation; + } +} diff --git a/translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java b/translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java index a33d8cb7a8f..0fd2d44c210 100644 --- a/translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java @@ -17,6 +17,8 @@ import org.jetbrains.k2js.translate.reference.CallTranslator; import java.util.Arrays; +import static org.jetbrains.k2js.translate.operation.AssignmentTranslator.isAssignmentOperator; +import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompareToCall; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isEquals; import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; @@ -51,21 +53,27 @@ public final class BinaryOperationTranslator extends AbstractTranslator { @NotNull private JsExpression translate() { - if (AssignmentTranslator.isAssignmentOperator(expression)) { + if (isAssignmentOperator(expression)) { return AssignmentTranslator.translate(expression, context()); } - if (operationDescriptor == null) { + if (isNotOverloadable()) { return translateAsUnOverloadableBinaryOperation(); } - if (CompareToTranslator.isCompareToCall(expression, context())) { + if (isCompareToCall(expression, context())) { return CompareToTranslator.translate(expression, context()); } + assert operationDescriptor != null : + "Overloadable operations must have not null descriptor"; if (isEquals(operationDescriptor)) { return translateAsEqualsCall(); } return CallTranslator.translate(expression, context()); } + private boolean isNotOverloadable() { + return operationDescriptor == null; + } + @NotNull private JsExpression translateAsEqualsCall() { assert operationDescriptor != null : "Equals operation must resolve to descriptor."; diff --git a/translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java b/translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java index 8205b7d8d26..0b80ef75cc2 100644 --- a/translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java @@ -23,11 +23,13 @@ import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForRe import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForCallExpression; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getVariableDescriptorForVariableAsFunction; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isConstructorDescriptor; +import static org.jetbrains.k2js.translate.utils.PsiUtils.isInOperation; import static org.jetbrains.k2js.translate.utils.TranslationUtils.*; /** * @author Talanov Pavel */ +//TODO: move translate() static method into builder (consider!) //TODO: write tests on calling backing fields as functions public final class CallTranslator extends AbstractTranslator { @@ -38,14 +40,14 @@ public final class CallTranslator extends AbstractTranslator { DeclarationDescriptor descriptor = getDescriptorForReferenceExpression (context.bindingContext(), unaryExpression.getOperationReference()); assert descriptor instanceof FunctionDescriptor; - return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate(); + return translate(context, receiver, arguments, (FunctionDescriptor) descriptor); } //TODO: method too long public static JsExpression translate(@NotNull JetDotQualifiedExpression dotExpression, @NotNull TranslationContext context) { - //TODO: look for duplication JsExpression receiver = translateReceiver(context, dotExpression); + //TODO: util? JetExpression selectorExpression = dotExpression.getSelectorExpression(); assert selectorExpression instanceof JetCallExpression; JetCallExpression callExpression = (JetCallExpression) selectorExpression; @@ -53,7 +55,7 @@ public final class CallTranslator extends AbstractTranslator { translateArgumentList(context, callExpression.getValueArguments()); FunctionDescriptor descriptor = getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression); - return (new CallTranslator(receiver, arguments, descriptor, context)).translate(); + return translate(context, receiver, arguments, descriptor); } public static JsExpression translate(@NotNull JetCallExpression callExpression, @@ -62,18 +64,39 @@ public final class CallTranslator extends AbstractTranslator { getFunctionDescriptorForCallExpression(context.bindingContext(), callExpression); JsExpression receiver = getImplicitReceiver(context, descriptor); List arguments = translateArgumentList(context, callExpression.getValueArguments()); - return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate(); + return translate(context, receiver, arguments, (FunctionDescriptor) descriptor); } + //TODO: refactor public static JsExpression translate(@NotNull JetBinaryExpression binaryExpression, @NotNull TranslationContext context) { JsExpression receiver = translateLeftExpression(context, binaryExpression); List arguments = Arrays.asList(translateRightExpression(context, binaryExpression)); - //TODO: use PSI util method to get operation reference DeclarationDescriptor descriptor = getDescriptorForReferenceExpression (context.bindingContext(), binaryExpression.getOperationReference()); assert descriptor instanceof FunctionDescriptor; - return (new CallTranslator(receiver, arguments, (FunctionDescriptor) descriptor, context)).translate(); + FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; + if (isInOperation(binaryExpression)) { + return translateWithReceiverAndArgumentSwapped(context, receiver, arguments, functionDescriptor); + } + return translate(context, receiver, arguments, functionDescriptor); + } + + @NotNull + private static JsExpression translateWithReceiverAndArgumentSwapped(@NotNull TranslationContext context, + @NotNull JsExpression receiver, + @NotNull List arguments, + @NotNull FunctionDescriptor functionDescriptor) { + assert arguments.size() == 1 : "Must have one argument."; + return translate(context, arguments.get(0), Arrays.asList(receiver), functionDescriptor); + } + + @NotNull + private static JsExpression translate(@NotNull TranslationContext context, + @Nullable JsExpression receiver, + @NotNull List arguments, + @NotNull FunctionDescriptor functionDescriptor) { + return (new CallTranslator(receiver, arguments, functionDescriptor, context)).translate(); } @Nullable diff --git a/translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java b/translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java index 34ff33e0ee8..e9588901a4f 100644 --- a/translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java +++ b/translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java @@ -52,6 +52,10 @@ public final class PsiUtils { return expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; } + public static boolean isInOperation(@NotNull JetBinaryExpression binaryExpression) { + return binaryExpression.getOperationToken() == JetTokens.IN_KEYWORD; + } + @NotNull /*package*/ static JetExpression getCallee(@NotNull JetCallExpression expression) { JetExpression calleeExpression = expression.getCalleeExpression(); diff --git a/translator/test/org/jetbrains/k2js/test/RangeTest.java b/translator/test/org/jetbrains/k2js/test/RangeTest.java index 34580e72619..5a9a8a383cd 100644 --- a/translator/test/org/jetbrains/k2js/test/RangeTest.java +++ b/translator/test/org/jetbrains/k2js/test/RangeTest.java @@ -19,5 +19,15 @@ public final class RangeTest extends TranslationTest { testFooBoxIsTrue("explicitRange.kt"); } + @Test + public void rangeSugarSyntax() throws Exception { + testFooBoxIsTrue("rangeSugarSyntax.kt"); + } + + @Test + public void intInRange() throws Exception { + testFooBoxIsTrue("intInRange.kt"); + } + } diff --git a/translator/testFiles/range/cases/intInRange.kt b/translator/testFiles/range/cases/intInRange.kt new file mode 100644 index 00000000000..575351d6e7b --- /dev/null +++ b/translator/testFiles/range/cases/intInRange.kt @@ -0,0 +1,13 @@ +namespace foo + +fun box() : Boolean { + + if (1 in -2..1) return false; + if (1 in -10..0) return false; + if (!(1 in 0..2)) return false; + + if (!(1 in 1..2)) return false; + if (!(1 in -2..5)) return false; + + return true; +} \ No newline at end of file diff --git a/translator/testFiles/range/cases/rangeSugarSyntax.kt b/translator/testFiles/range/cases/rangeSugarSyntax.kt new file mode 100644 index 00000000000..717cc6188ac --- /dev/null +++ b/translator/testFiles/range/cases/rangeSugarSyntax.kt @@ -0,0 +1,31 @@ +namespace foo + +fun box() : Boolean { + + var oneToFive = 1..5 + + if (oneToFive.contains(5)) return false; + if (oneToFive.contains(0)) return false; + if (oneToFive.contains(-100)) return false; + if (oneToFive.contains(10)) return false; + if (!oneToFive.contains(1)) return false; + if (!oneToFive.contains(2)) return false; + if (!oneToFive.contains(3)) return false; + if (!oneToFive.contains(4)) return false; + if (!(oneToFive.start == 1)) return false; + if (!(oneToFive.size == 4)) return false; + if (!(oneToFive.end == 5)) return false; + + var sum = 0; + for (i in oneToFive) { + sum += i; + } + for (i in oneToFive) { + System.out?.print(i) + } + + if (sum != 10) return false; + + return true; + +} \ No newline at end of file