diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/IdentityEqualsTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/IdentityEqualsTest.java new file mode 100644 index 00000000000..78a6d8a64d5 --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/IdentityEqualsTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.test.semantics; + +public class IdentityEqualsTest extends AbstractExpressionTest { + public IdentityEqualsTest() { + super("identityEquals/"); + } + + public void testIdentityEqualsMethod() throws Exception { + checkFooBoxIsOk(); + } + + public void testIdentityEqualsMethodForPrimitives() throws Exception { + checkFooBoxIsOk(); + } +} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java index 267fe816782..8e8bc7eb1bb 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java @@ -16,9 +16,7 @@ package org.jetbrains.k2js.translate.intrinsic.functions.factories; -import com.google.dart.compiler.backend.js.ast.JsExpression; -import com.google.dart.compiler.backend.js.ast.JsInvocation; -import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; @@ -53,6 +51,17 @@ public final class TopLevelFIF extends CompositeFIF { @NotNull public static final KotlinFunctionIntrinsic EQUALS = new KotlinFunctionIntrinsic("equals"); @NotNull + public static final FunctionIntrinsic IDENTITY_EQUALS = new FunctionIntrinsic() { + @NotNull + @Override + public JsExpression apply( + @Nullable JsExpression receiver, @NotNull List arguments, @NotNull TranslationContext context + ) { + assert arguments.size() == 1 : "Unexpected argument size for jet.identityEquals: " + arguments.size(); + return new JsBinaryOperation(JsBinaryOperator.REF_EQ, receiver, arguments.get(0)); + } + }; + @NotNull private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsic() { @NotNull @Override @@ -120,6 +129,7 @@ public final class TopLevelFIF extends CompositeFIF { private TopLevelFIF() { add(pattern("jet", "toString").receiverExists(), new KotlinFunctionIntrinsic("toString")); add(pattern("jet", "equals").receiverExists(), EQUALS); + add(pattern("jet", "identityEquals").receiverExists(), IDENTITY_EQUALS); add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), EQUALS); add(pattern("String|Boolean|Char|Number.equals"), EQUALS); add(pattern("jet", "arrayOfNulls"), new KotlinFunctionIntrinsic("nullArray")); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java index fb28a1c94c9..e4e2f86ac3f 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java @@ -16,6 +16,7 @@ package org.jetbrains.k2js.translate.operation; +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation; import com.google.dart.compiler.backend.js.ast.JsExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetUnaryExpression; @@ -31,7 +32,9 @@ import static org.jetbrains.k2js.translate.general.Translation.translateAsExpres import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall; import static org.jetbrains.k2js.translate.utils.PsiUtils.getBaseExpression; import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.isEqualLikeOperator; import static org.jetbrains.k2js.translate.utils.TranslationUtils.sure; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateExclForBinaryEqualLikeExpr; public final class UnaryOperationTranslator { private UnaryOperationTranslator() { @@ -46,7 +49,12 @@ public final class UnaryOperationTranslator { if (IncrementTranslator.isIncrement(expression)) { return IncrementTranslator.translate(expression, context); } - return translateAsCall(expression, context); + + JsExpression baseExpression = TranslationUtils.translateBaseExpression(context, expression); + if (isExclForBinaryEqualLikeExpr(expression, baseExpression)) { + return translateExclForBinaryEqualLikeExpr((JsBinaryOperation) baseExpression); + } + return translateAsCall(expression, context, baseExpression); } private static boolean isExclExcl(@NotNull JetUnaryExpression expression) { @@ -58,11 +66,21 @@ public final class UnaryOperationTranslator { return sure(translateAsExpression(getBaseExpression(expression), context), context); } + private static boolean isExclForBinaryEqualLikeExpr(@NotNull JetUnaryExpression expression, @NotNull JsExpression baseExpression) { + if (getOperationToken(expression).equals(JetTokens.EXCL)) { + if (baseExpression instanceof JsBinaryOperation) { + return isEqualLikeOperator(((JsBinaryOperation) baseExpression).getOperator()); + } + } + return false; + } + @NotNull private static JsExpression translateAsCall(@NotNull JetUnaryExpression expression, - @NotNull TranslationContext context) { + @NotNull TranslationContext context, + @NotNull JsExpression baseExpression) { return CallBuilder.build(context) - .receiver(TranslationUtils.translateBaseExpression(context, expression)) + .receiver(baseExpression) .args(Collections.emptyList()) .resolvedCall(getResolvedCall(context.bindingContext(), expression.getOperationReference())) .type(CallType.NORMAL).translate(); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index 7810b588f68..04104b92d0d 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment; import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor; @@ -62,6 +63,31 @@ public final class TranslationUtils { return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta); } + @NotNull + public static JsExpression translateExclForBinaryEqualLikeExpr(@NotNull JsBinaryOperation baseBinaryExpression) { + return new JsBinaryOperation(notOperator(baseBinaryExpression.getOperator()), baseBinaryExpression.getArg1(), baseBinaryExpression.getArg2()); + } + + public static boolean isEqualLikeOperator(@NotNull JsBinaryOperator operator) { + return notOperator(operator) != null; + } + + @Nullable + private static JsBinaryOperator notOperator(@NotNull JsBinaryOperator operator) { + switch (operator) { + case REF_EQ: + return REF_NEQ; + case REF_NEQ: + return REF_EQ; + case EQ: + return NEQ; + case NEQ: + return EQ; + default: + return null; + } + } + @NotNull public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) { return nullCheck(expressionToCheck, false); diff --git a/js/js.translator/testFiles/expression/identityEquals/cases/identityEqualsMethod.kt b/js/js.translator/testFiles/expression/identityEquals/cases/identityEqualsMethod.kt new file mode 100644 index 00000000000..9f90f0daf61 --- /dev/null +++ b/js/js.translator/testFiles/expression/identityEquals/cases/identityEqualsMethod.kt @@ -0,0 +1,21 @@ +package foo + +class X + +fun box(): String { + val a = X() + val b = X() + if (!a.identityEquals(a)) return "a !== a" + if (a.identityEquals(b)) return "X() === X()" + val c = a + if (!c.identityEquals(a)) return "c = a; c !== a" + + if (X() identityEquals a) return "X() identityEquals a" + + val t = !(X() identityEquals a) + if (!t) return "t = !(X() identityEquals a); t == false" + + val f = !!(X() identityEquals a) + if (f) return "f = !!(X() identityEquals null); f == true" + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testFiles/expression/identityEquals/cases/identityEqualsMethodForPrimitives.kt b/js/js.translator/testFiles/expression/identityEquals/cases/identityEqualsMethodForPrimitives.kt new file mode 100644 index 00000000000..857edfc23e3 --- /dev/null +++ b/js/js.translator/testFiles/expression/identityEquals/cases/identityEqualsMethodForPrimitives.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + if (!null.identityEquals(null)) return "null !== null" + if (!("ab" identityEquals "ab")) return "ab !== ab" + if (("ab" identityEquals "a")) return "ab === a" + + if ("0" identityEquals 0) return "'0' === 0" + if (!(0 identityEquals 0)) return "0 !== 0" + if (0 identityEquals 1) return "0 === 1" + + + return "OK"; +} \ No newline at end of file