diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt index 021cf758878..8da1adf0074 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt @@ -62,5 +62,6 @@ var HasMetadata.sideEffects: Boolean by MetadataProperty(default = true) enum class TypeCheck { TYPEOF, INSTANCEOF, - OR_NULL + OR_NULL, + IS_ANY } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java index 0df6bfd3f07..d0fa327ab62 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/CastTestGenerated.java @@ -35,6 +35,12 @@ public class CastTestGenerated extends AbstractCastTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/expression/cast/cases"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("castToAny.kt") + public void testCastToAny() throws Exception { + String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToAny.kt"); + doTest(fileName); + } + @TestMetadata("castToGenericType.kt") public void testCastToGenericType() throws Exception { String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/expression/cast/cases/castToGenericType.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index cb5590502e0..6f301fefdf1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -22,6 +22,8 @@ import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties; import com.google.dart.compiler.backend.js.ast.metadata.TypeCheck; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; @@ -399,13 +401,23 @@ public final class Namer { return invokeFunctionAndSetTypeCheckMetadata("orNull", callable, TypeCheck.OR_NULL); } + @NotNull + public JsExpression isAny() { + return invokeFunctionAndSetTypeCheckMetadata("isAny", null, TypeCheck.IS_ANY); + } + @NotNull private JsExpression invokeFunctionAndSetTypeCheckMetadata( @NotNull String functionName, - @NotNull JsExpression argument, + @Nullable JsExpression argument, @NotNull TypeCheck metadata ) { - JsInvocation invocation = new JsInvocation(kotlin(functionName), argument); + JsInvocation invocation = new JsInvocation(kotlin(functionName)); + + if (argument != null) { + invocation.getArguments().add(argument); + } + MetadataProperties.setTypeCheck(invocation, metadata); MetadataProperties.setSideEffects(invocation, false); return invocation; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index ec96e96573c..a7b7c3b5024 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -25,6 +25,7 @@ import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.metadata.MetadataPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt; import org.jetbrains.kotlin.js.patterns.NamePredicate; @@ -116,6 +117,8 @@ public final class PatternTranslator extends AbstractTranslator { @NotNull private JsExpression doGetIsTypeCheckCallable(@NotNull KotlinType type) { + if (KotlinBuiltIns.isAnyOrNullableAny(type)) return namer().isAny(); + JsExpression builtinCheck = getIsTypeCheckCallableForBuiltin(type); if (builtinCheck != null) return builtinCheck; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt index 7535502a7f6..ea459fb3053 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt @@ -68,7 +68,7 @@ private class TypeCheckRewritingVisitor(private val context: TranslationContext) val calleeArgument = callee?.arguments?.firstOrNull() val argument = x.arguments.firstOrNull() - if (callee != null && calleeArgument != null && argument != null) { + if (callee != null && argument != null) { val replacement = getReplacement(callee, calleeArgument, argument) if (replacement != null) { @@ -80,7 +80,16 @@ private class TypeCheckRewritingVisitor(private val context: TranslationContext) return true } - private fun getReplacement(callee: JsInvocation, calleeArgument: JsExpression, argument: JsExpression): JsExpression? { + private fun getReplacement(callee: JsInvocation, calleeArgument: JsExpression?, argument: JsExpression): JsExpression? { + if (calleeArgument == null) { + // Kotlin.isAny()(argument) -> argument != null + if (callee.typeCheck == TypeCheck.IS_ANY) { + return TranslationUtils.isNotNullCheck(argument) + } + + return null + } + // Kotlin.isTypeOf(calleeArgument)(argument) -> typeOf argument === calleeArgument if (callee.typeCheck == TypeCheck.TYPEOF) { return typeOfIs(argument, calleeArgument as JsStringLiteral) @@ -93,8 +102,10 @@ private class TypeCheckRewritingVisitor(private val context: TranslationContext) // Kotlin.orNull(calleeArgument)(argument) -> (tmp = argument) == null || calleeArgument(tmp) if (callee.typeCheck == TypeCheck.OR_NULL) { - if (calleeArgument is JsInvocation && calleeArgument.typeCheck == TypeCheck.OR_NULL) { - return JsInvocation(calleeArgument, argument) + if (calleeArgument is JsInvocation) { + if (calleeArgument.typeCheck == TypeCheck.OR_NULL) return JsInvocation(calleeArgument, argument) + + if (calleeArgument.typeCheck == TypeCheck.IS_ANY) return argument } var nullCheckTarget = argument diff --git a/js/js.translator/testData/expression/cast/cases/castToAny.kt b/js/js.translator/testData/expression/cast/cases/castToAny.kt new file mode 100644 index 00000000000..b5c3261b61c --- /dev/null +++ b/js/js.translator/testData/expression/cast/cases/castToAny.kt @@ -0,0 +1,12 @@ +package foo + +fun box(): String { + val x = 0 + val y = 1 + val z = 2 + val xs = arrayListOf(z, y, x) + xs.remove(x as Any) + assertEquals(listOf(z, y), xs) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/kotlin_lib_ecma5.js b/js/js.translator/testData/kotlin_lib_ecma5.js index fc790405024..436a5abdad5 100644 --- a/js/js.translator/testData/kotlin_lib_ecma5.js +++ b/js/js.translator/testData/kotlin_lib_ecma5.js @@ -555,6 +555,12 @@ var Kotlin = {}; } }; + Kotlin.isAny = function () { + return function (object) { + return object != null; + } + }; + Kotlin.kotlinModuleMetadata = function (abiVersion, moduleName, data) { };