From 37fa45dc346aa582d4cb694933a380ae77ce2074 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 18 Jul 2017 16:42:30 +0300 Subject: [PATCH] Add mechanism for type coercion in JS Use it for char boxing/unboxing and unit materialization. Possible to use for other purposes, for example, to add type checks to dynamics. See KT-18793, KT-17915, KT-19081, KT-18216, KT-12970, KT-17014, KT-13932, KT-13930 --- .../codegen/box/casts/lambdaToUnitCast.kt | 3 - .../testData/codegen/box/casts/unitAsAny.kt | 3 - .../codegen/box/casts/unitAsSafeAny.kt | 3 - .../unitTypeReturn/suspendNonLocalReturn.kt | 1 - .../generated/primitiveEqObjectChar.kt | 1 - .../testData/codegen/box/properties/kt4383.kt | 3 - .../testData/codegen/box/unit/UnitValue.kt | 3 - compiler/testData/codegen/box/unit/kt4212.kt | 3 - .../ast/metadata/metadataProperties.kt | 13 +- js/js.libraries/src/builtins/arrayUtils.kt | 19 +- js/js.libraries/src/builtins/hacks.kt | 2 +- .../js/test/JsLineNumberTestGenerated.java | 6 - .../js/test/semantics/BoxJsTestGenerated.java | 117 ++++++++++++ .../semantics/JsCodegenBoxTestGenerated.java | 64 +------ .../js/translate/callTranslator/CallInfo.kt | 36 ++-- .../callTranslator/CallTranslator.kt | 19 +- .../context/TemporaryConstVariable.java | 2 +- .../translate/context/TemporaryVariable.java | 11 +- .../declaration/PropertyTranslator.kt | 3 +- .../DestructuringDeclarationTranslator.java | 3 +- .../expression/ExpressionVisitor.java | 37 +++- .../expression/FunctionTranslator.kt | 5 +- .../js/translate/expression/LoopTranslator.kt | 9 +- .../expression/PatternTranslator.java | 21 +-- .../translate/expression/WhenTranslator.java | 16 +- .../js/translate/general/Translation.java | 41 ++-- .../functions/FunctionIntrinsics.java | 1 + .../intrinsic/functions/factories/ArrayFIF.kt | 11 +- .../functions/factories/AsDynamicFIF.kt | 33 ++++ .../functions/factories/TopLevelFIF.java | 4 +- .../intrinsic/operation/EqualsBOIF.kt | 21 ++- .../operation/BinaryOperationTranslator.java | 11 +- .../IntrinsicAssignmentTranslator.java | 2 +- .../reference/CallArgumentTranslator.kt | 22 +-- .../reference/CallableReferenceTranslator.kt | 10 +- .../reference/ReferenceTranslator.java | 39 ++++ .../utils/FunctionBodyTranslator.java | 20 +- .../js/translate/utils/TranslationUtils.java | 176 +++++++++++++----- .../utils/mutator/CoercionMutator.java | 44 +++++ .../testData/box/char/charEquals.kt | 12 +- .../testData/box/coercion/classProperty.kt | 18 ++ .../coercion/derivedFunctionReturningChar.kt | 17 ++ .../coercion/derivedFunctionReturningUnit.kt | 20 ++ .../box/coercion/destructuringToUnit.kt | 20 ++ .../box/coercion/extensionReceiver.kt | 14 ++ .../testData/box/coercion/ifWithUnit.kt | 19 ++ .../box/coercion/inlineFunReturningUnit.kt | 12 ++ .../testData/box/coercion/lambdaParameters.kt | 22 +++ .../testData/box/coercion/loopOverUnits.kt | 25 +++ .../box/coercion/receiverSmartCast.kt | 32 ++++ .../box/coercion/safeCallLetReturningUnit.kt | 19 ++ .../testData/box/coercion/topLevelProperty.kt | 15 ++ .../box/coercion/tryWithEmptyCatch.kt | 19 ++ .../box/coercion/unitAsExtensionReceiver.kt | 17 ++ .../testData/box/coercion/unitIsAs.kt | 14 ++ .../testData/box/coercion/unitSafeCall.kt | 14 ++ .../testData/box/coercion/whenWithUnit.kt | 22 +++ .../nameClashes/propertyAndNativeMethod.kt | 2 +- .../box/standardClasses/charArrayGetSet.kt | 26 +++ .../testData/lineNumbers/charBoxing.kt | 20 -- .../testData/lineNumbers/destructuring.kt | 2 +- .../lineNumbers/destructuringInline.kt | 2 +- .../testData/lineNumbers/lambdaWithClosure.kt | 2 +- 63 files changed, 959 insertions(+), 264 deletions(-) create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/AsDynamicFIF.kt create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/mutator/CoercionMutator.java create mode 100644 js/js.translator/testData/box/coercion/classProperty.kt create mode 100644 js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt create mode 100644 js/js.translator/testData/box/coercion/derivedFunctionReturningUnit.kt create mode 100644 js/js.translator/testData/box/coercion/destructuringToUnit.kt create mode 100644 js/js.translator/testData/box/coercion/extensionReceiver.kt create mode 100644 js/js.translator/testData/box/coercion/ifWithUnit.kt create mode 100644 js/js.translator/testData/box/coercion/inlineFunReturningUnit.kt create mode 100644 js/js.translator/testData/box/coercion/lambdaParameters.kt create mode 100644 js/js.translator/testData/box/coercion/loopOverUnits.kt create mode 100644 js/js.translator/testData/box/coercion/receiverSmartCast.kt create mode 100644 js/js.translator/testData/box/coercion/safeCallLetReturningUnit.kt create mode 100644 js/js.translator/testData/box/coercion/topLevelProperty.kt create mode 100644 js/js.translator/testData/box/coercion/tryWithEmptyCatch.kt create mode 100644 js/js.translator/testData/box/coercion/unitAsExtensionReceiver.kt create mode 100644 js/js.translator/testData/box/coercion/unitIsAs.kt create mode 100644 js/js.translator/testData/box/coercion/unitSafeCall.kt create mode 100644 js/js.translator/testData/box/coercion/whenWithUnit.kt create mode 100644 js/js.translator/testData/box/standardClasses/charArrayGetSet.kt delete mode 100644 js/js.translator/testData/lineNumbers/charBoxing.kt diff --git a/compiler/testData/codegen/box/casts/lambdaToUnitCast.kt b/compiler/testData/codegen/box/casts/lambdaToUnitCast.kt index 42a86dcbb07..94ddfb05c00 100644 --- a/compiler/testData/codegen/box/casts/lambdaToUnitCast.kt +++ b/compiler/testData/codegen/box/casts/lambdaToUnitCast.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JS -// JS backend does not support Unit well. See KT-13932 - val foo: () -> Unit = {} fun box(): String { diff --git a/compiler/testData/codegen/box/casts/unitAsAny.kt b/compiler/testData/codegen/box/casts/unitAsAny.kt index d05da99581f..74326356cba 100644 --- a/compiler/testData/codegen/box/casts/unitAsAny.kt +++ b/compiler/testData/codegen/box/casts/unitAsAny.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - fun println(s: String) { } diff --git a/compiler/testData/codegen/box/casts/unitAsSafeAny.kt b/compiler/testData/codegen/box/casts/unitAsSafeAny.kt index 6412c65526f..81d5a495425 100644 --- a/compiler/testData/codegen/box/casts/unitAsSafeAny.kt +++ b/compiler/testData/codegen/box/casts/unitAsSafeAny.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - fun println(s: String) { } diff --git a/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt b/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt index 4b1dbf6ddcc..a56b3e5e4ed 100644 --- a/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt +++ b/compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt @@ -1,6 +1,5 @@ // WITH_RUNTIME // WITH_COROUTINES -// IGNORE_BACKEND: JS import helpers.* import kotlin.coroutines.experimental.* import kotlin.coroutines.experimental.intrinsics.* diff --git a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt index c10e9d0249e..6122439bcb6 100644 --- a/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt +++ b/compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! val nx: Any? = '0' diff --git a/compiler/testData/codegen/box/properties/kt4383.kt b/compiler/testData/codegen/box/properties/kt4383.kt index 4b26b927544..3a8d6e0a1e3 100644 --- a/compiler/testData/codegen/box/properties/kt4383.kt +++ b/compiler/testData/codegen/box/properties/kt4383.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - import kotlin.reflect.KProperty class D { diff --git a/compiler/testData/codegen/box/unit/UnitValue.kt b/compiler/testData/codegen/box/unit/UnitValue.kt index 1fef505f0af..243b563dba8 100644 --- a/compiler/testData/codegen/box/unit/UnitValue.kt +++ b/compiler/testData/codegen/box/unit/UnitValue.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - fun foo() {} fun box(): String { diff --git a/compiler/testData/codegen/box/unit/kt4212.kt b/compiler/testData/codegen/box/unit/kt4212.kt index e0c04071aca..5d9db4097e7 100644 --- a/compiler/testData/codegen/box/unit/kt4212.kt +++ b/compiler/testData/codegen/box/unit/kt4212.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - fun foo(): Any? = bar() fun bar() {} diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt index 9e552a7df79..1a54f7ff717 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.resolve.inline.InlineStrategy +import org.jetbrains.kotlin.types.KotlinType var JsName.staticRef: JsNode? by MetadataProperty(default = null) @@ -56,12 +57,16 @@ var JsParameter.hasDefaultValue: Boolean by MetadataProperty(default = false) var JsInvocation.typeCheck: TypeCheck? by MetadataProperty(default = null) -var JsInvocation.boxing: Boolean by MetadataProperty(default = false) +var JsInvocation.boxing: BoxingKind by MetadataProperty(default = BoxingKind.NONE) var JsVars.exportedPackage: String? by MetadataProperty(default = null) var JsExpressionStatement.exportedTag: String? by MetadataProperty(default = null) +var JsExpression.type: KotlinType? by MetadataProperty(default = null) + +var JsExpression.isUnit: Boolean by MetadataProperty(default = false) + /** * For function and lambda bodies indicates what declaration corresponds to. * When absent (`null`) on body of a named function, this function is from external JS module. @@ -142,4 +147,10 @@ enum class SpecialFunction(val suggestedName: String) { WRAP_FUNCTION("wrapFunction"), TO_BOXED_CHAR("toBoxedChar"), UNBOX_CHAR("unboxChar"), +} + +enum class BoxingKind { + NONE, + BOXING, + UNBOXING } \ No newline at end of file diff --git a/js/js.libraries/src/builtins/arrayUtils.kt b/js/js.libraries/src/builtins/arrayUtils.kt index 189eb079e5e..b51a3e4dbf5 100644 --- a/js/js.libraries/src/builtins/arrayUtils.kt +++ b/js/js.libraries/src/builtins/arrayUtils.kt @@ -59,7 +59,24 @@ fun charArray(size: Int, init: dynamic): Array { } @JsName("charArrayF") -inline fun charArrayWithFun(size: Int, init: (Int) -> Char): Array = fillArrayFun(charArray(size, null), init) +inline fun charArrayWithFun(size: Int, init: (Int) -> Char): Array { + val array = charArray(size, null) + for (i in 0..array.size - 1) { + val value = init(i) + js("array[i] = value;") + } + return array +} + +@JsName("untypedCharArrayF") +inline fun untypedCharArrayWithFun(size: Int, init: (Int) -> Char): Array { + val array = Array(size) + for (i in 0..array.size - 1) { + val value = init(i) + js("array[i] = value;") + } + return array +} @JsName("longArray") fun longArray(size: Int, init: dynamic): Array { diff --git a/js/js.libraries/src/builtins/hacks.kt b/js/js.libraries/src/builtins/hacks.kt index f2764d4205f..b9e95a2dcb7 100644 --- a/js/js.libraries/src/builtins/hacks.kt +++ b/js/js.libraries/src/builtins/hacks.kt @@ -24,4 +24,4 @@ internal external annotation class JsName(val name: String) internal external annotation class native -internal external fun js(code: String): dynamic \ No newline at end of file +external fun js(code: String): dynamic \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java index 6e5affbfc7c..ced2d2bc304 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java @@ -60,12 +60,6 @@ public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest { doTest(fileName); } - @TestMetadata("charBoxing.kt") - public void testCharBoxing() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/charBoxing.kt"); - doTest(fileName); - } - @TestMetadata("classCapturingLocals.kt") public void testClassCapturingLocals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/classCapturingLocals.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 5ab5dde651c..93f636c185a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -795,6 +795,117 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { } } + @TestMetadata("js/js.translator/testData/box/coercion") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Coercion extends AbstractBoxJsTest { + public void testAllFilesPresentInCoercion() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/coercion"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("classProperty.kt") + public void testClassProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/classProperty.kt"); + doTest(fileName); + } + + @TestMetadata("derivedFunctionReturningChar.kt") + public void testDerivedFunctionReturningChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt"); + doTest(fileName); + } + + @TestMetadata("derivedFunctionReturningUnit.kt") + public void testDerivedFunctionReturningUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/derivedFunctionReturningUnit.kt"); + doTest(fileName); + } + + @TestMetadata("destructuringToUnit.kt") + public void testDestructuringToUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/destructuringToUnit.kt"); + doTest(fileName); + } + + @TestMetadata("extensionReceiver.kt") + public void testExtensionReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/extensionReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("ifWithUnit.kt") + public void testIfWithUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/ifWithUnit.kt"); + doTest(fileName); + } + + @TestMetadata("inlineFunReturningUnit.kt") + public void testInlineFunReturningUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/inlineFunReturningUnit.kt"); + doTest(fileName); + } + + @TestMetadata("lambdaParameters.kt") + public void testLambdaParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/lambdaParameters.kt"); + doTest(fileName); + } + + @TestMetadata("loopOverUnits.kt") + public void testLoopOverUnits() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/loopOverUnits.kt"); + doTest(fileName); + } + + @TestMetadata("receiverSmartCast.kt") + public void testReceiverSmartCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/receiverSmartCast.kt"); + doTest(fileName); + } + + @TestMetadata("safeCallLetReturningUnit.kt") + public void testSafeCallLetReturningUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/safeCallLetReturningUnit.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelProperty.kt") + public void testTopLevelProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/topLevelProperty.kt"); + doTest(fileName); + } + + @TestMetadata("tryWithEmptyCatch.kt") + public void testTryWithEmptyCatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/tryWithEmptyCatch.kt"); + doTest(fileName); + } + + @TestMetadata("unitAsExtensionReceiver.kt") + public void testUnitAsExtensionReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/unitAsExtensionReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("unitIsAs.kt") + public void testUnitIsAs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/unitIsAs.kt"); + doTest(fileName); + } + + @TestMetadata("unitSafeCall.kt") + public void testUnitSafeCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/unitSafeCall.kt"); + doTest(fileName); + } + + @TestMetadata("whenWithUnit.kt") + public void testWhenWithUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/whenWithUnit.kt"); + doTest(fileName); + } + } + @TestMetadata("js/js.translator/testData/box/crossModuleRef") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) @@ -7802,6 +7913,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("charArrayGetSet.kt") + public void testCharArrayGetSet() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/charArrayGetSet.kt"); + doTest(fileName); + } + @TestMetadata("hashMapTypeOfElement.kt") public void testHashMapTypeOfElement() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/hashMapTypeOfElement.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index d9d092a50c6..58cddd5e8a8 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -2966,13 +2966,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("lambdaToUnitCast.kt") public void testLambdaToUnitCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("notIs.kt") @@ -2990,13 +2984,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsAny.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("unitAsInt.kt") @@ -3014,13 +3002,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("unitAsSafeAny.kt") public void testUnitAsSafeAny() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsSafeAny.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("unitNullableCast.kt") @@ -6694,13 +6676,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("suspendNonLocalReturn.kt") public void testSuspendNonLocalReturn() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("suspendReturn.kt") @@ -14247,13 +14223,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("primitiveEqObjectChar.kt") public void testPrimitiveEqObjectChar() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("primitiveEqObjectInt.kt") @@ -14792,13 +14762,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("kt4383.kt") public void testKt4383() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/kt4383.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("kt613.kt") @@ -23603,13 +23567,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("kt4212.kt") public void testKt4212() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/kt4212.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("kt4265.kt") @@ -23651,13 +23609,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("UnitValue.kt") public void testUnitValue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/UnitValue.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt index 99754cf6a5d..72ae2efc6e3 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallInfo.kt @@ -17,10 +17,14 @@ package org.jetbrains.kotlin.js.translate.callTranslator import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticUtils -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor -import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.JsBlock +import org.jetbrains.kotlin.js.backend.ast.JsConditional +import org.jetbrains.kotlin.js.backend.ast.JsExpression +import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral +import org.jetbrains.kotlin.js.backend.ast.metadata.type import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator @@ -31,6 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.typeUtil.makeNullable interface CallInfo { val context: TranslationContext @@ -99,10 +104,6 @@ fun TranslationContext.getCallInfo( return FunctionCallInfo(callInfo, argumentsInfo) } -private fun TranslationContext.boxIfNeedeed(v: ReceiverValue?, d: ReceiverParameterDescriptor?, r: JsExpression?): JsExpression? { - return r?.let { receiver -> TranslationUtils.boxCastIfNeeded(this, receiver, v?.type, d?.type) } -} - private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue): JsExpression { return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue)) } @@ -134,6 +135,10 @@ private fun TranslationContext.createCallInfo( } var dispatchReceiver = getDispatchReceiver() + var dispatchReceiverType = resolvedCall.smartCastDispatchReceiverType ?: resolvedCall.dispatchReceiver?.type + if (dispatchReceiverType != null && (resolvedCall.resultingDescriptor as? FunctionDescriptor)?.kind?.isReal == false) { + dispatchReceiverType = TranslationUtils.getDispatchReceiverTypeForCoercion(resolvedCall.resultingDescriptor) + } var extensionReceiver = getExtensionReceiver() var notNullConditional: JsConditional? = null @@ -154,16 +159,19 @@ private fun TranslationContext.createCallInfo( val container = resolvedCall.resultingDescriptor.containingDeclaration if (DescriptorUtils.isObject(container)) { dispatchReceiver = ReferenceTranslator.translateAsValueReference(container, this) + dispatchReceiverType = (container as ClassDescriptor).defaultType } } - dispatchReceiver = boxIfNeedeed(resolvedCall.dispatchReceiver, - resolvedCall.candidateDescriptor.dispatchReceiverParameter, - dispatchReceiver) + if (dispatchReceiverType != null) { + dispatchReceiver = dispatchReceiver?.let { + TranslationUtils.coerce(this, it, dispatchReceiverType!!) + } + } - extensionReceiver = boxIfNeedeed(resolvedCall.extensionReceiver, - resolvedCall.candidateDescriptor.extensionReceiverParameter, - extensionReceiver) + extensionReceiver = extensionReceiver?.let { + TranslationUtils.coerce(this, it, resolvedCall.candidateDescriptor.extensionReceiverParameter!!.type) + } return object : AbstractCallInfo(), CallInfo { @@ -179,7 +187,9 @@ private fun TranslationContext.createCallInfo( result } else { - notNullConditionalForSafeCall.thenExpression = result + val type = resolvedCall.getReturnType() + result.type = type + notNullConditionalForSafeCall.thenExpression = TranslationUtils.coerce(context, result, type.makeNullable()) notNullConditionalForSafeCall } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt index 0259fa5ea4c..87921331733 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/callTranslator/CallTranslator.kt @@ -31,11 +31,14 @@ import org.jetbrains.kotlin.js.translate.utils.* import org.jetbrains.kotlin.psi.Call.CallType import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariable +import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.makeNullable object CallTranslator { @JvmOverloads @@ -52,7 +55,9 @@ object CallTranslator { extensionOrDispatchReceiver: JsExpression? = null ): JsExpression { val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), null) - return variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement) + val result = variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement) + result.type = TranslationUtils.getReturnTypeForCoercion(resolvedCall.resultingDescriptor.original) + return result } fun translateSet(context: TranslationContext, @@ -61,7 +66,9 @@ object CallTranslator { extensionOrDispatchReceiver: JsExpression? = null ): JsExpression { val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), value) - return variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement) + val result = variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement) + result.type = context.currentModule.builtIns.unitType + return result } fun buildCall(context: TranslationContext, @@ -95,7 +102,7 @@ private fun translateCall( assert(explicitReceivers.extensionReceiver == null) { "VariableAsFunctionResolvedCall must have one receiver" } val variableCall = resolvedCall.variableCall - return if (variableCall.expectedReceivers()) { + val result = if (variableCall.expectedReceivers()) { val newReceiver = CallTranslator.translateGet(context, variableCall, explicitReceivers.extensionOrDispatchReceiver) translateFunctionCall(context, resolvedCall.functionCall, resolvedCall.variableCall, ExplicitReceivers(newReceiver)) } else { @@ -110,6 +117,8 @@ private fun translateCall( ExplicitReceivers(dispatchReceiver, explicitReceivers.extensionOrDispatchReceiver)) } } + + return result } val call = resolvedCall.call @@ -151,9 +160,13 @@ private fun translateFunctionCall( callExpression.isTailCallSuspend = true } } + + callExpression.type = resolvedCall.getReturnType().let { if (resolvedCall.call.isSafeCall()) it.makeNullable() else it } return callExpression } +fun ResolvedCall.getReturnType(): KotlinType = TranslationUtils.getReturnTypeForCoercion(resultingDescriptor) + private val TranslationContext.isInStateMachine get() = (declarationDescriptor as? FunctionDescriptor)?.requiresStateMachineTransformation(this) == true diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryConstVariable.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryConstVariable.java index c6ddc1a9f33..433551aae9d 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryConstVariable.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryConstVariable.java @@ -24,7 +24,7 @@ public final class TemporaryConstVariable extends TemporaryVariable{ private boolean initialized = false; public TemporaryConstVariable(@NotNull JsName variableName, @NotNull JsExpression assignmentExpression) { - super(variableName, assignmentExpression); + super(variableName, assignmentExpression, null); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryVariable.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryVariable.java index 6ff39de0b8e..0634c5aaa07 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryVariable.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TemporaryVariable.java @@ -21,33 +21,40 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; +import org.jetbrains.kotlin.types.KotlinType; public class TemporaryVariable { /*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) { JsBinaryOperation rhs = null; + KotlinType type = null; if (initExpression != null) { rhs = JsAstUtils.assignment(temporaryName.makeRef(), initExpression); rhs.source(initExpression.getSource()); MetadataProperties.setSynthetic(rhs, true); + type = MetadataProperties.getType(initExpression); } - return new TemporaryVariable(temporaryName, rhs); + return new TemporaryVariable(temporaryName, rhs, type); } @Nullable private final JsExpression assignmentExpression; @NotNull private final JsName variableName; + @Nullable + private final KotlinType type; - protected TemporaryVariable(@NotNull JsName temporaryName, @Nullable JsExpression assignmentExpression) { + protected TemporaryVariable(@NotNull JsName temporaryName, @Nullable JsExpression assignmentExpression, @Nullable KotlinType type) { this.variableName = temporaryName; this.assignmentExpression = assignmentExpression; + this.type = type; } @NotNull public JsNameRef reference() { JsNameRef result = variableName.makeRef(); MetadataProperties.setSynthetic(result, true); + MetadataProperties.setType(result, type); return result; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt index 50d0c8815ea..14e0f170618 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PropertyTranslator.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.utils.BindingUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.* import org.jetbrains.kotlin.js.translate.utils.finalElement import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter @@ -172,7 +173,7 @@ fun TranslationContext.translateDelegateOrInitializerExpression(expression: KtPr CallTranslator.translate(innerContext, provideDelegateCall, initializer) } else { - initializer + TranslationUtils.coerce(this, initializer, propertyDescriptor.type) } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java index 5d20892488b..1c5b8516369 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/DestructuringDeclarationTranslator.java @@ -84,8 +84,7 @@ public class DestructuringDeclarationTranslator extends AbstractTranslator { setInlineCallMetadata(entryInitializer, entry, entryInitCall, context()); } - entryInitializer = TranslationUtils.boxCastIfNeeded(context(), entryInitializer, candidateDescriptor.getReturnType(), - descriptor.getType()); + entryInitializer = TranslationUtils.coerce(context(), entryInitializer, descriptor.getType()); JsName name = context().getNameForDescriptor(descriptor); if (isVarCapturedInClosure(context().bindingContext(), descriptor)) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 38b0d9fbf53..2c0f36ff01f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; 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.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention; @@ -39,6 +38,8 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.UtilsKt; +import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator; +import org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.resolve.BindingContext; @@ -107,6 +108,11 @@ public final class ExpressionVisitor extends TranslatorVisitor { jsBlock.getStatements().add(jsStatement); } } + if (statements.isEmpty()) { + ClassDescriptor unitClass = context.getCurrentModule().getBuiltIns().getUnit(); + jsBlock.getStatements().add(JsAstUtils.asSyntheticStatement( + ReferenceTranslator.translateAsValueReference(unitClass, context))); + } return jsBlock; } @@ -135,6 +141,8 @@ public final class ExpressionVisitor extends TranslatorVisitor { return new JsReturn(ref.source(jetReturnExpression)); } + FunctionDescriptor returnTarget = getNonLocalReturnTarget(jetReturnExpression, context); + JsReturn jsReturn; if (returned == null) { jsReturn = new JsReturn(null); @@ -146,15 +154,19 @@ public final class ExpressionVisitor extends TranslatorVisitor { assert returnedType != null : "Resolved return expression is expected to have type: " + PsiUtilsKt.getTextWithLocation(jetReturnExpression); - if (KotlinBuiltIns.isCharOrNullableChar(returnedType) && - TranslationUtils.shouldBoxReturnValue((CallableDescriptor)context.getDeclarationDescriptor())) { - jsReturnExpression = TranslationUtils.charToBoxedChar(context, jsReturnExpression); + CallableDescriptor returnTargetOrCurrentFunction = returnTarget; + if (returnTargetOrCurrentFunction == null) { + returnTargetOrCurrentFunction = (CallableDescriptor) context.getDeclarationDescriptor(); + } + if (returnTargetOrCurrentFunction != null) { + jsReturnExpression = TranslationUtils.coerce(context, jsReturnExpression, + TranslationUtils.getReturnTypeForCoercion(returnTargetOrCurrentFunction)); } jsReturn = new JsReturn(jsReturnExpression); } - MetadataProperties.setReturnTarget(jsReturn, getNonLocalReturnTarget(jetReturnExpression, context)); + MetadataProperties.setReturnTarget(jsReturn, returnTarget); return jsReturn.source(jetReturnExpression); } @@ -245,10 +257,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObjectQualifier()) { JsExpression receiver = translateAsExpression(receiverExpression, context); - KotlinType type = context.bindingContext().getType(receiverExpression); - if (type != null && KotlinBuiltIns.isChar(type)) { - receiver = TranslationUtils.charToBoxedChar(context, receiver); - } + receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getAnyType()); return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver); } @@ -270,6 +279,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) { assert expression.getCondition() != null : "condition should not ne null: " + expression.getText(); JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context); + KotlinType type = context.bindingContext().getType(expression); boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext()); @@ -281,6 +291,15 @@ public final class ExpressionVisitor extends TranslatorVisitor { JsStatement elseStatement = elseExpression != null ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression, context) : null; + if (type != null) { + if (thenStatement != null) { + thenStatement = LastExpressionMutator.mutateLastExpression(thenStatement, new CoercionMutator(type, context)); + } + if (elseStatement != null) { + elseStatement = LastExpressionMutator.mutateLastExpression(elseStatement, new CoercionMutator(type, context)); + } + } + if (isKotlinExpression) { JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement); JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt index c75f7737984..73f55169dc5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor import org.jetbrains.kotlin.js.backend.ast.metadata.hasDefaultValue +import org.jetbrains.kotlin.js.backend.ast.metadata.type import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.descriptorUtils.shouldBeExported import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper @@ -58,7 +59,9 @@ fun TranslationContext.translateAndAliasParameters( if (descriptor.requiresExtensionReceiverParameter) { val receiverParameterName = JsScope.declareTemporaryName(Namer.getReceiverParameterName()) - aliases[descriptor.extensionReceiverParameter!!] = receiverParameterName.makeRef() + val receiverRef = receiverParameterName.makeRef() + receiverRef.type = descriptor.extensionReceiverParameter!!.type + aliases[descriptor.extensionReceiverParameter!!] = receiverRef targetList += JsParameter(receiverParameterName) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt index 27b7e6b5963..9fb23d3e8c8 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.expression import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator @@ -29,6 +30,7 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils.* import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.* import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -142,7 +144,10 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont val currentVarInit = if (destructuringParameter == null) { - newVar(parameterName, itemValue).apply { source = expression.loopRange } + val loopParameterDescriptor = (getDescriptorForElement(context.bindingContext(), loopParameter) as CallableDescriptor) + val loopParameterType = loopParameterDescriptor.returnType ?: context.currentModule.builtIns.anyType + val coercedItemValue = itemValue?.let { TranslationUtils.coerce(context, it, loopParameterType) } + newVar(parameterName, coercedItemValue).apply { source = expression.loopRange } } else { val innerBlockContext = context.innerBlock(block) @@ -224,7 +229,7 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont fun translateForOverArray(): JsStatement { val rangeExpression = context.defineTemporary(Translation.translateAsExpression(loopRange, context)) - val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf(), context) + val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf(), context) val end = context.defineTemporary(length) val index = context.declareTemporary(JsIntLiteral(0), expression) 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 188ec4fb0d9..30c29147da2 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 @@ -81,10 +81,8 @@ public final class PatternTranslator extends AbstractTranslator { KtTypeReference typeReference = expression.getRight(); assert typeReference != null: "Cast expression must have type reference"; - KotlinType leftType = context().bindingContext().getType(left); - if (leftType != null && KotlinBuiltIns.isChar(leftType)) { - expressionToCast = TranslationUtils.charToBoxedChar(context(), expressionToCast); - } + KotlinType anyType = context().getCurrentModule().getBuiltIns().getAnyType(); + expressionToCast = TranslationUtils.coerce(context(), expressionToCast, anyType); TemporaryVariable temporary = context().declareTemporary(expressionToCast, expression); JsExpression isCheck = translateIsCheck(temporary.assignmentExpression(), typeReference); @@ -102,12 +100,11 @@ public final class PatternTranslator extends AbstractTranslator { JsExpression result = new JsConditional(isCheck, temporary.reference(), onFail); - KotlinType expressionType = context().bindingContext().getType(expression); - if (expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType)) { - result = TranslationUtils.boxedCharToChar(context(), result); + KotlinType targetType = getTypeByReference(bindingContext(), typeReference); + if (isSafeCast(expression)) { + targetType = targetType.unwrap().makeNullableAsSpecified(true); } - - return result; + return TranslationUtils.coerce(context(), result, targetType); } @NotNull @@ -115,10 +112,8 @@ public final class PatternTranslator extends AbstractTranslator { KtExpression left = expression.getLeftHandSide(); JsExpression expressionToCheck = Translation.translateAsExpression(left, context()); - KotlinType leftType = context().bindingContext().getType(left); - if (leftType != null && KotlinBuiltIns.isChar(leftType)) { - expressionToCheck = TranslationUtils.charToBoxedChar(context(), expressionToCheck); - } + KotlinType anyType = context().getCurrentModule().getBuiltIns().getAnyType(); + expressionToCheck = TranslationUtils.coerce(context(), expressionToCheck, anyType); KtTypeReference typeReference = expression.getTypeReference(); assert typeReference != null; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java index 5dddd5998ee..d9c24380f3a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/WhenTranslator.java @@ -28,6 +28,8 @@ import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.operation.InOperationTranslator; import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; +import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator; +import org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; @@ -50,6 +52,9 @@ public final class WhenTranslator extends AbstractTranslator { @Nullable private final JsExpression expressionToMatch; + @Nullable + private final KotlinType type; + private WhenTranslator(@NotNull KtWhenExpression expression, @NotNull TranslationContext context) { super(context); @@ -57,6 +62,8 @@ public final class WhenTranslator extends AbstractTranslator { KtExpression subject = expression.getSubjectExpression(); expressionToMatch = subject != null ? context.defineTemporary(Translation.translateAsExpression(subject, context)) : null; + + type = context().bindingContext().getType(expression); } private JsNode translate() { @@ -106,13 +113,18 @@ public final class WhenTranslator extends AbstractTranslator { } @NotNull - private static JsStatement translateEntryExpression( + private JsStatement translateEntryExpression( @NotNull KtWhenEntry entry, @NotNull TranslationContext context, @NotNull JsBlock block) { KtExpression expressionToExecute = entry.getExpression(); assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; - return Translation.translateAsStatement(expressionToExecute, context, block); + JsStatement result = Translation.translateAsStatement(expressionToExecute, context, block); + if (type != null) { + return LastExpressionMutator.mutateLastExpression(result, new CoercionMutator(type, context)); + } + + return result; } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index 1396a2ca67a..9122698d1ec 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.idea.MainFunctionDetector; import org.jetbrains.kotlin.js.backend.ast.*; +import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.facade.MainCallParameters; import org.jetbrains.kotlin.js.facade.TranslationUnit; @@ -123,6 +124,15 @@ public final class Translation { ) { KotlinType expectedType = context.bindingContext().getType(expression); ConstantValue constant = compileTimeValue.toConstantValue(expectedType != null ? expectedType : TypeUtils.NO_EXPECTED_TYPE); + JsExpression result = translateConstantWithoutType(constant); + if (result != null) { + MetadataProperties.setType(result, expectedType); + } + return result; + } + + @Nullable + private static JsExpression translateConstantWithoutType(@NotNull ConstantValue constant) { if (constant instanceof NullValue) { return new JsNullLiteral(); } @@ -187,10 +197,17 @@ public final class Translation { @NotNull JsBlock block ) { JsNode jsNode = translateExpression(expression, context, block); + if (jsNode instanceof JsExpression) { - KotlinType expressionType = context.bindingContext().getType(expression); - return unboxIfNeeded(context, (JsExpression) jsNode, - expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType)); + JsExpression jsExpression = (JsExpression) jsNode; + KotlinType type = context.bindingContext().getType(expression); + if (MetadataProperties.getType(jsExpression) == null) { + MetadataProperties.setType(jsExpression, type); + } + else if (type != null) { + jsExpression = TranslationUtils.coerce(context, jsExpression, type); + } + return jsExpression; } assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString(); @@ -198,27 +215,15 @@ public final class Translation { TemporaryVariable result = context.declareTemporary(null, expression); AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference()); block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator)); - return result.reference(); + JsExpression tmpVar = result.reference(); + MetadataProperties.setType(tmpVar, context.bindingContext().getType(expression)); + return tmpVar; } block.getStatements().add(convertToStatement(jsNode)); return new JsNullLiteral().source(expression); } - @NotNull - public static JsExpression unboxIfNeeded( - @NotNull TranslationContext context, - @NotNull JsExpression expression, - boolean charOrNullableChar - ) { - if (charOrNullableChar && - (expression instanceof JsInvocation || expression instanceof JsNameRef || expression instanceof JsArrayAccess) - ) { - expression = TranslationUtils.boxedCharToChar(context, expression); - } - return expression; - } - @NotNull public static JsStatement translateAsStatement(@NotNull KtExpression expression, @NotNull TranslationContext context) { return translateAsStatement(expression, context, context.dynamicContext().jsBlock()); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java index f29afa5adbc..3523c2fcd77 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java @@ -50,6 +50,7 @@ public final class FunctionIntrinsics { register(NumberAndCharConversionFIF.INSTANCE); register(ThrowableConstructorIntrinsicFactory.INSTANCE); register(ExceptionPropertyIntrinsicFactory.INSTANCE); + register(AsDynamicFIF.INSTANCE); } private void register(@NotNull FunctionIntrinsicFactory instance) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt index 78fbb43e19b..3c78a54485a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ArrayFIF.kt @@ -43,7 +43,7 @@ import java.util.* object ArrayFIF : CompositeFIF() { @JvmField - val GET_INTRINSIC = intrinsify { callInfo, arguments, _ -> + val GET_INTRINSIC = intrinsify { callInfo, arguments, context -> assert(arguments.size == 1) { "Array get expression must have one argument." } val (indexExpression) = arguments JsArrayAccess(callInfo.dispatchReceiver, indexExpression) @@ -108,7 +108,12 @@ object ArrayFIF : CompositeFIF() { } } else { - "kotlin.newArrayF" + if (primitiveType == CHAR) { + "kotlin.untypedCharArrayF" + } + else { + "kotlin.newArrayF" + } } } @@ -183,7 +188,7 @@ object ArrayFIF : CompositeFIF() { } } else { - JsAstUtils.invokeKotlinFunction("newArrayF", size, fn) + JsAstUtils.invokeKotlinFunction(if (type == CHAR) "untypedCharArrayF" else "newArrayF", size, fn) } invocation.inlineStrategy = InlineStrategy.IN_PLACE val descriptor = callInfo.resolvedCall.resultingDescriptor.original diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/AsDynamicFIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/AsDynamicFIF.kt new file mode 100644 index 00000000000..1af5f7515ac --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/AsDynamicFIF.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2017 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.kotlin.js.translate.intrinsic.functions.factories + +import org.jetbrains.kotlin.js.backend.ast.JsExpression +import org.jetbrains.kotlin.js.patterns.PatternBuilder +import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo +import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils + +object AsDynamicFIF : CompositeFIF() { + init { + add(PatternBuilder.pattern("kotlin.js.asDynamic()"), object : FunctionIntrinsic() { + override fun apply(callInfo: CallInfo, arguments: List, context: TranslationContext): JsExpression = + TranslationUtils.coerce(context, callInfo.extensionReceiver!!, callInfo.resolvedCall.extensionReceiver!!.type) + }) + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java index cc74aa22ec9..af13c6949ff 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic; import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.UtilsKt; import org.jetbrains.kotlin.resolve.DescriptorFactory; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; @@ -166,13 +167,14 @@ public final class TopLevelFIF extends CompositeFIF { public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString"); @NotNull - public static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() { + private static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() { @NotNull @Override public JsExpression apply( @Nullable JsExpression receiver, @NotNull List arguments, @NotNull TranslationContext context ) { assert receiver != null; + receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getCharType()); return JsAstUtils.charToString(receiver); } }; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt index 27c501eb556..a2d4ff3d6a1 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/operation/EqualsBOIF.kt @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator import org.jetbrains.kotlin.js.backend.ast.JsExpression import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral import org.jetbrains.kotlin.js.translate.context.TranslationContext -import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken @@ -57,13 +56,15 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory { val ktLeft = checkNotNull(expression.left) { "No left-hand side: " + expression.text } val ktRight = checkNotNull(expression.right) { "No right-hand side: " + expression.text } - val leftType = getRefinedType(ktLeft, context)?.let { KotlinBuiltIns.getPrimitiveType(it) } - val rightType = getRefinedType(ktRight, context)?.let { KotlinBuiltIns.getPrimitiveType(it) } + val leftKotlinType = getRefinedType(ktLeft, context) + val rightKotlinType = getRefinedType(ktRight, context) + val leftType = leftKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) } + val rightType = rightKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) } if (leftType != null && (leftType in SIMPLE_PRIMITIVES || leftType == rightType && leftType != PrimitiveType.LONG)) { - return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, - Translation.unboxIfNeeded(context, left, leftType == PrimitiveType.CHAR), - Translation.unboxIfNeeded(context, right, rightType == PrimitiveType.CHAR)) + val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType) + val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType!!) + return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, coercedLeft, coercedRight) } val resolvedCall = expression.getResolvedCall(context.bindingContext()) @@ -77,10 +78,10 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory { return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right) } - val maybeBoxedLeft = if (leftType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, left) else left - val maybeBoxedRight = if (rightType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, right) else right - - val result = TopLevelFIF.KOTLIN_EQUALS.apply(maybeBoxedLeft, Arrays.asList(maybeBoxedRight), context) + val anyType = context.currentModule.builtIns.anyType + val coercedLeft = TranslationUtils.coerce(context, left, anyType) + val coercedRight = TranslationUtils.coerce(context, right, anyType) + val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context) return if (isNegated) JsAstUtils.not(result) else result } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/BinaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/BinaryOperationTranslator.java index 5910183bc29..cdab0f69c03 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/BinaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/BinaryOperationTranslator.java @@ -126,15 +126,14 @@ public final class BinaryOperationTranslator extends AbstractTranslator { @NotNull private JsExpression translateElvis() { KotlinType expressionType = context().bindingContext().getType(expression); + assert expressionType != null; - JsExpression leftExpression = TranslationUtils.boxCastIfNeeded( - context(), Translation.translateAsExpression(leftKtExpression, context()), - context().bindingContext().getType(leftKtExpression), expressionType); + JsExpression leftExpression = TranslationUtils.coerce( + context(), Translation.translateAsExpression(leftKtExpression, context()), expressionType); JsBlock rightBlock = new JsBlock(); - JsExpression rightExpression = TranslationUtils.boxCastIfNeeded( - context(),Translation.translateAsExpression(rightKtExpression, context(), rightBlock), - context().bindingContext().getType(rightKtExpression), expressionType); + JsExpression rightExpression = TranslationUtils.coerce( + context(), Translation.translateAsExpression(rightKtExpression, context(), rightBlock), expressionType); if (rightBlock.isEmpty()) { return TranslationUtils.notNullConditional(leftExpression, rightExpression, context()); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java index 5c8c01eed08..41718b774dd 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/IntrinsicAssignmentTranslator.java @@ -66,7 +66,7 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator { if (leftType != null && KotlinBuiltIns.isStringOrNullableString(leftType)) { result = JsAstUtils.charToString(result); } - else if (leftType == null || !KotlinBuiltIns.isCharOrNullableChar(leftType)) { + else if (leftType != null && !KotlinBuiltIns.isCharOrNullableChar(leftType)) { result = TranslationUtils.charToBoxedChar(context, result); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt index d2b2de76765..263544c513a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallArgumentTranslator.kt @@ -17,9 +17,10 @@ package org.jetbrains.kotlin.js.translate.reference import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineOrReturn -import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor +import org.jetbrains.kotlin.builtins.getFunctionalClassKind import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.js.backend.ast.* @@ -36,9 +37,9 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass -import org.jetbrains.kotlin.psi.Call import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.components.isVararg import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument @@ -191,18 +192,15 @@ class CallArgumentTranslator private constructor( val parenthisedArgumentExpression = arg.getArgumentExpression() val param = argsToParameters[arg]!!.original - val parameterType = if (resolvedCall.call.callType == Call.CallType.INVOKE) { - DefaultBuiltIns.Instance.anyType - } - else { - param.varargElementType ?: param.type + val isLambda = resolvedCall.resultingDescriptor.let { it.getFunctionalClassKind() != null || it is FunctionInvokeDescriptor } + val parameterType = if (!isLambda) param.varargElementType ?: param.type else context.currentModule.builtIns.anyType + + var argJs = Translation.translateAsExpression(parenthisedArgumentExpression!!, argumentContext) + if (!param.isVararg || arg.getSpreadElement() == null) { + argJs = TranslationUtils.coerce(context, argJs, parameterType) } - val argType = context.bindingContext().getType(parenthisedArgumentExpression!!) - - val argJs = Translation.translateAsExpression(parenthisedArgumentExpression, argumentContext) - - arg to TranslationUtils.boxCastIfNeeded(context, argJs, argType, parameterType) + arg to argJs } val resolvedOrder = resolvedCall.valueArgumentsByIndex.orEmpty() diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index 258a6d0f371..306711139d5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -22,11 +22,13 @@ import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects +import org.jetbrains.kotlin.js.backend.ast.metadata.type import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.utils.BindingUtils +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.finalElement import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtExpression @@ -107,15 +109,19 @@ object CallableReferenceTranslator { null } + val functionDescriptor = realResolvedCall.resultingDescriptor val aliases = mutableMapOf() for ((index, valueArg) in fakeCall.valueArguments.withIndex()) { val paramName = JsScope.declareTemporaryName(descriptor.valueParameters[index].name.asString()) function.parameters += JsParameter(paramName) - aliases[valueArg.getArgumentExpression()!!] = paramName.makeRef() + val paramRef = paramName.makeRef() + paramRef.type = context.currentModule.builtIns.anyType + val type = functionDescriptor.valueParameters[index].type + aliases[valueArg.getArgumentExpression()!!] = TranslationUtils.coerce(context, paramRef, type) } val functionContext = context.innerBlock(function.body).innerContextWithAliasesForExpressions(aliases) val invocation = CallTranslator.translate(functionContext, fakeResolvedCall, receiverParam) - function.body.statements += JsReturn(invocation) + function.body.statements += JsReturn(TranslationUtils.coerce(context, invocation, context.currentModule.builtIns.anyType)) val rawCallableRef = bindIfNecessary(function, receiver) return wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java index 218abd91f66..38f0baad515 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/ReferenceTranslator.java @@ -17,7 +17,10 @@ package org.jetbrains.kotlin.js.translate.reference; 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.descriptors.impl.AnonymousFunctionDescriptor; import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.backend.ast.JsInvocation; import org.jetbrains.kotlin.js.backend.ast.JsName; @@ -32,6 +35,7 @@ import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtQualifiedExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.types.KotlinType; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName; @@ -49,6 +53,41 @@ public final class ReferenceTranslator { @NotNull public static JsExpression translateAsValueReference(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) { + JsExpression result = translateAsValueReferenceWithoutType(descriptor, context); + MetadataProperties.setType(result, getType(descriptor)); + if (descriptor instanceof ClassDescriptor) { + if (KotlinBuiltIns.isUnit(((ClassDescriptor) descriptor).getDefaultType())) { + MetadataProperties.setUnit(result, true); + MetadataProperties.setSideEffects(result, SideEffectKind.PURE); + MetadataProperties.setSynthetic(result, true); + } + } + return result; + } + + @Nullable + private static KotlinType getType(@NotNull DeclarationDescriptor descriptor) { + if (descriptor instanceof ClassDescriptor) { + return ((ClassDescriptor) descriptor).getDefaultType(); + } + else if (descriptor instanceof CallableDescriptor) { + if (descriptor instanceof ValueParameterDescriptor) { + ValueParameterDescriptor parameter = (ValueParameterDescriptor) descriptor; + if (parameter.getContainingDeclaration() instanceof AnonymousFunctionDescriptor) { + return DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType(); + } + } + return ((CallableDescriptor) descriptor).getReturnType(); + } + + return null; + } + + @NotNull + private static JsExpression translateAsValueReferenceWithoutType( + @NotNull DeclarationDescriptor descriptor, + @NotNull TranslationContext context + ) { if (AnnotationsUtils.isNativeObject(descriptor) || AnnotationsUtils.isLibraryObject(descriptor)) { return context.getInnerReference(descriptor); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java index 56e2105a416..0e685494856 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.utils; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; +import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; @@ -31,6 +32,7 @@ import org.jetbrains.kotlin.js.translate.expression.LocalFunctionCollector; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; +import org.jetbrains.kotlin.psi.KtBlockExpression; import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; @@ -120,6 +122,16 @@ public final class FunctionBodyTranslator extends AbstractTranslator { JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock); jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements()); + + if (jetBodyExpression instanceof KtBlockExpression && + descriptor.getReturnType() != null && KotlinBuiltIns.isUnit(descriptor.getReturnType()) && + !KotlinBuiltIns.isUnit(TranslationUtils.getReturnTypeForCoercion(descriptor))) { + ClassDescriptor unit = context().getCurrentModule().getBuiltIns().getUnit(); + JsReturn jsReturn = new JsReturn(ReferenceTranslator.translateAsValueReference(unit, context())); + jsReturn.setSource(UtilsKt.getFinalElement(declaration)); + jsBlock.getStatements().add(jsReturn); + } + return jsBlock; } @@ -145,12 +157,8 @@ public final class FunctionBodyTranslator extends AbstractTranslator { } assert declaration.getBodyExpression() != null; - assert descriptor.getReturnType() != null; - KotlinType bodyType = context().bindingContext().getType(declaration.getBodyExpression()); - if (bodyType == null && KotlinBuiltIns.isCharOrNullableChar(descriptor.getReturnType()) || - bodyType != null && KotlinBuiltIns.isCharOrNullableChar(bodyType) && TranslationUtils.shouldBoxReturnValue(descriptor)) { - node = TranslationUtils.charToBoxedChar(context(), (JsExpression) node); - } + KotlinType returnType = TranslationUtils.getReturnTypeForCoercion(descriptor); + node = TranslationUtils.coerce(context(), (JsExpression) node, returnType); JsReturn jsReturn = new JsReturn((JsExpression) node); jsReturn.setSource(declaration.getBodyExpression()); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index b5c224d02ab..9aec9041bab 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -19,12 +19,15 @@ package org.jetbrains.kotlin.js.translate.utils; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.FunctionTypesKt; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.js.backend.ast.*; +import org.jetbrains.kotlin.js.backend.ast.metadata.BoxingKind; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction; import org.jetbrains.kotlin.js.translate.context.Namer; @@ -43,11 +46,11 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; +import org.jetbrains.kotlin.types.DynamicTypesKt; import org.jetbrains.kotlin.types.KotlinType; -import org.jetbrains.kotlin.types.TypeUtils; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression; @@ -186,7 +189,11 @@ public final class TranslationUtils { else { receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor)); } - return new JsNameRef(backingFieldName, receiver); + + JsNameRef result = new JsNameRef(backingFieldName, receiver); + MetadataProperties.setType(result, getReturnTypeForCoercion(descriptor)); + + return result; } @NotNull @@ -205,10 +212,9 @@ public final class TranslationUtils { if (initializer != null) { jsInitExpression = Translation.translateAsExpression(initializer, context); - KotlinType propertyType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, declaration).getType(); - KotlinType initType = context.bindingContext().getType(initializer); - - jsInitExpression = boxCastIfNeeded(context, jsInitExpression, initType, propertyType); + KotlinType propertyType = BindingContextUtils.getNotNull( + context.bindingContext(), BindingContext.VARIABLE, declaration).getType(); + jsInitExpression = coerce(context, jsInitExpression, propertyType); } return jsInitExpression; } @@ -394,61 +400,139 @@ public final class TranslationUtils { ModalityKt.isOverridable(descriptor); } - private static boolean overridesReturnAny(CallableDescriptor c) { - KotlinType returnType = c.getOriginal().getReturnType(); - assert returnType != null; - if (KotlinBuiltIns.isAnyOrNullableAny(returnType) || TypeUtils.isTypeParameter(returnType)) return true; - for (CallableDescriptor o : c.getOverriddenDescriptors()) { - if (overridesReturnAny(o)) return true; + @NotNull + public static KotlinType getReturnTypeForCoercion(@NotNull CallableDescriptor descriptor) { + descriptor = descriptor.getOriginal(); + + if (FunctionTypesKt.getFunctionalClassKind(descriptor) != null || descriptor instanceof AnonymousFunctionDescriptor) { + return DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType(); } - return false; - } - - public static boolean shouldBoxReturnValue(CallableDescriptor c) { - return overridesReturnAny(c) || c instanceof CallableMemberDescriptor && ModalityKt.isOverridable((CallableMemberDescriptor)c); - } - - @NotNull - public static JsExpression boxCastIfNeeded( - @NotNull TranslationContext context, - @NotNull JsExpression e, - @Nullable KotlinType castFrom, @Nullable KotlinType castTo - ) { - if (castFrom != null && KotlinBuiltIns.isCharOrNullableChar(castFrom) && - castTo != null && !KotlinBuiltIns.isCharOrNullableChar(castTo) - ) { - return charToBoxedChar(context, e); + Collection overridden = descriptor.getOverriddenDescriptors(); + if (overridden.isEmpty()) { + return descriptor.getReturnType() != null ? + descriptor.getReturnType() : + DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType(); } - return e; + + Set typesFromOverriddenCallables = overridden.stream() + .map(TranslationUtils::getReturnTypeForCoercion) + .collect(Collectors.toSet()); + return typesFromOverriddenCallables.size() == 1 + ? typesFromOverriddenCallables.iterator().next() + : DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType(); } @NotNull - public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) { - JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.TO_BOXED_CHAR, unnestBoxing(expression)); - invocation.setSource(expression.getSource()); - return withBoxingMetadata(invocation); + public static KotlinType getDispatchReceiverTypeForCoercion(@NotNull CallableDescriptor descriptor) { + descriptor = descriptor.getOriginal(); + if (descriptor.getDispatchReceiverParameter() == null) { + throw new IllegalArgumentException("This method can only be used for class members; " + + "given descriptor is not a member of a class " + descriptor); + } + + Collection overridden = descriptor.getOverriddenDescriptors(); + if (overridden.isEmpty()) { + return descriptor.getDispatchReceiverParameter().getType(); + } + + Set typesFromOverriddenCallables = overridden.stream() + .map(TranslationUtils::getDispatchReceiverTypeForCoercion) + .collect(Collectors.toSet()); + return typesFromOverriddenCallables.size() == 1 + ? typesFromOverriddenCallables.iterator().next() + : DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType(); } @NotNull - public static JsExpression boxedCharToChar(@NotNull TranslationContext context, @NotNull JsExpression expression) { - JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.UNBOX_CHAR, unnestBoxing(expression)); - invocation.setSource(expression.getSource()); - return withBoxingMetadata(invocation); + public static JsExpression coerce(@NotNull TranslationContext context, @NotNull JsExpression value, @NotNull KotlinType to) { + if (DynamicTypesKt.isDynamic(to)) return value; + + KotlinType from = MetadataProperties.getType(value); + if (from == null) { + from = context.getCurrentModule().getBuiltIns().getAnyType(); + } + + if (from.equals(to)) return value; + + if (KotlinBuiltIns.isCharOrNullableChar(to)) { + if (!KotlinBuiltIns.isCharOrNullableChar(from) && !(value instanceof JsNullLiteral)) { + value = boxedCharToChar(context, value); + } + } + else if (KotlinBuiltIns.isUnit(to)) { + if (!KotlinBuiltIns.isUnit(from)) { + value = unitToVoid(value); + } + } + else if (KotlinBuiltIns.isCharOrNullableChar(from)) { + if (!KotlinBuiltIns.isCharOrNullableChar(to) && !(value instanceof JsNullLiteral)) { + value = charToBoxedChar(context, value); + } + } + else if (KotlinBuiltIns.isUnit(from)) { + if (!KotlinBuiltIns.isUnit(to) && !MetadataProperties.isUnit(value)) { + ClassDescriptor unit = context.getCurrentModule().getBuiltIns().getUnit(); + JsExpression unitRef = ReferenceTranslator.translateAsValueReference(unit, context); + value = JsAstUtils.newSequence(Arrays.asList(value, unitRef)); + } + } + + MetadataProperties.setType(value, to); + return value; } @NotNull - private static JsExpression unnestBoxing(@NotNull JsExpression expression) { - if (expression instanceof JsInvocation && MetadataProperties.getBoxing((JsInvocation) expression)) { - return ((JsInvocation) expression).getArguments().get(0); + private static JsExpression unitToVoid(@NotNull JsExpression expression) { + if (expression instanceof JsBinaryOperation) { + JsBinaryOperation binary = (JsBinaryOperation) expression; + if (binary.getOperator() == JsBinaryOperator.COMMA && MetadataProperties.isUnit(binary.getArg2())) { + return binary.getArg1(); + } } return expression; } @NotNull - private static JsInvocation withBoxingMetadata(@NotNull JsInvocation call) { - MetadataProperties.setBoxing(call, true); - return call; + public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) { + if (expression instanceof JsInvocation) { + JsInvocation invocation = (JsInvocation) expression; + BoxingKind existingKind = MetadataProperties.getBoxing(invocation); + switch (existingKind) { + case UNBOXING: + return invocation.getArguments().get(0); + case BOXING: + return expression; + case NONE: + break; + } + } + + JsInvocation result = invokeSpecialFunction(context, SpecialFunction.TO_BOXED_CHAR, expression); + result.setSource(expression.getSource()); + MetadataProperties.setBoxing(result, BoxingKind.BOXING); + return result; + } + + @NotNull + private static JsExpression boxedCharToChar(@NotNull TranslationContext context, @NotNull JsExpression expression) { + if (expression instanceof JsInvocation) { + JsInvocation invocation = (JsInvocation) expression; + BoxingKind existingKind = MetadataProperties.getBoxing(invocation); + switch (existingKind) { + case BOXING: + return invocation.getArguments().get(0); + case UNBOXING: + return expression; + case NONE: + break; + } + } + + JsInvocation result = invokeSpecialFunction(context, SpecialFunction.UNBOX_CHAR, expression); + result.setSource(expression.getSource()); + MetadataProperties.setBoxing(result, BoxingKind.UNBOXING); + return result; } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/mutator/CoercionMutator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/mutator/CoercionMutator.java new file mode 100644 index 00000000000..c236dc7a1fe --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/mutator/CoercionMutator.java @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2017 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.kotlin.js.translate.utils.mutator; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.js.backend.ast.JsExpression; +import org.jetbrains.kotlin.js.backend.ast.JsNode; +import org.jetbrains.kotlin.js.translate.context.TranslationContext; +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; +import org.jetbrains.kotlin.types.KotlinType; + +public class CoercionMutator implements Mutator { + private final KotlinType targetType; + private final TranslationContext context; + + public CoercionMutator(@NotNull KotlinType targetType, @NotNull TranslationContext context) { + this.targetType = targetType; + this.context = context; + } + + @NotNull + @Override + public JsNode mutate(@NotNull JsNode node) { + if (node instanceof JsExpression) { + return TranslationUtils.coerce(context, (JsExpression) node, targetType); + } + + return node; + } +} diff --git a/js/js.translator/testData/box/char/charEquals.kt b/js/js.translator/testData/box/char/charEquals.kt index 4ae599ca9c7..241b5b67e5b 100644 --- a/js/js.translator/testData/box/char/charEquals.kt +++ b/js/js.translator/testData/box/char/charEquals.kt @@ -9,5 +9,15 @@ fun box(): String { assertEquals(false, 'A'== 'B') assertEquals(false, ('A' as Any) == (65 as Any)) + assertTrue(bar('Q')) + assertFalse(bar('W')) + + assertTrue(baz('Q')) + assertFalse(baz('W')) + return "OK" -} \ No newline at end of file +} + +fun bar(x: Char) = x.equals('Q') + +fun baz(x: Any) = x.equals('Q') \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/classProperty.kt b/js/js.translator/testData/box/coercion/classProperty.kt new file mode 100644 index 00000000000..c05b8a7bdea --- /dev/null +++ b/js/js.translator/testData/box/coercion/classProperty.kt @@ -0,0 +1,18 @@ +// EXPECTED_REACHABLE_NODES: 1004 +class A { + var log = "" + + fun foo() { + log += "foo()" + } + + val bar: Any = foo() +} + +fun box(): String { + val a = A() + if (a.bar != Unit) return "fail1: ${a.bar}" + if (a.log != "foo()") return "fail2: ${a.log}" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt b/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt new file mode 100644 index 00000000000..5f91294040f --- /dev/null +++ b/js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt @@ -0,0 +1,17 @@ +// EXPECTED_REACHABLE_NODES: 1006 +abstract class A { + abstract fun foo(): T +} + +class B() : A() { + override fun foo() = 'Q' +} + +private fun typeOf(x: dynamic) = js("typeof x") + +fun box(): String { + val a: A = B() + if (typeOf(a.foo()) != "object") return "fail1" + if (typeOf(B().foo()) != "number") return "fail2" + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/derivedFunctionReturningUnit.kt b/js/js.translator/testData/box/coercion/derivedFunctionReturningUnit.kt new file mode 100644 index 00000000000..2c7228abaf3 --- /dev/null +++ b/js/js.translator/testData/box/coercion/derivedFunctionReturningUnit.kt @@ -0,0 +1,20 @@ +// EXPECTED_REACHABLE_NODES: 1012 +var log = "" + +abstract class A { + abstract fun foo(): T +} + +class B() : A() { + override fun foo() { + log += "B.foo()" + } +} + +fun box(): String { + val a: A = B() + if (a.foo() != Unit) return "fail1" + if (log != "B.foo()") return "fail2" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/destructuringToUnit.kt b/js/js.translator/testData/box/coercion/destructuringToUnit.kt new file mode 100644 index 00000000000..1ae6aa51494 --- /dev/null +++ b/js/js.translator/testData/box/coercion/destructuringToUnit.kt @@ -0,0 +1,20 @@ +// EXPECTED_REACHABLE_NODES: 1006 +var log = "" + +class A { + operator fun component1() { + log += "A.component1()" + } + + operator fun component2() = 23 +} + +fun box(): String { + val (x: Any, y) = A() + + if (x != Unit) return "fail1: $x" + if (y != 23) return "fail2: $y" + if (log != "A.component1()") return "fail3: $log" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/extensionReceiver.kt b/js/js.translator/testData/box/coercion/extensionReceiver.kt new file mode 100644 index 00000000000..9003fadbf4d --- /dev/null +++ b/js/js.translator/testData/box/coercion/extensionReceiver.kt @@ -0,0 +1,14 @@ +// EXPECTED_REACHABLE_NODES: 996 +fun box(): String { + val a = 'Q'.foo() + if (a != "number") return "fail1: $a" + + val b = 'W'.bar() + if (b != "object") return "fail2: $b" + + return "OK" +} + +fun Char.foo() = jsTypeOf(this.asDynamic()) + +fun Any.bar() = jsTypeOf(this.asDynamic()) \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/ifWithUnit.kt b/js/js.translator/testData/box/coercion/ifWithUnit.kt new file mode 100644 index 00000000000..185e396eb31 --- /dev/null +++ b/js/js.translator/testData/box/coercion/ifWithUnit.kt @@ -0,0 +1,19 @@ +// EXPECTED_REACHABLE_NODES: 1005 +var log = "" + +fun foo() { + log += "foo()" +} + +fun test(x: Int) = if (x < 10) foo() else 55 + +fun box(): String { + val a = test(20) + if (a !is Int) return "fail1: $a" + + val b = test(5) + if (b !is Unit) return "fail2: $b" + if (log != "foo()") return "fail3: $log" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/inlineFunReturningUnit.kt b/js/js.translator/testData/box/coercion/inlineFunReturningUnit.kt new file mode 100644 index 00000000000..c59ad5ffde8 --- /dev/null +++ b/js/js.translator/testData/box/coercion/inlineFunReturningUnit.kt @@ -0,0 +1,12 @@ +// EXPECTED_REACHABLE_NODES: 1000 +inline fun foo(i : Int) = if (i % 2 == 0) {} else i + +fun box(): String { + val a = foo(1) + if (a != 1) return "fail1: $a" + + val b = foo(2) + if (b != Unit) return "fail2: $b" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/lambdaParameters.kt b/js/js.translator/testData/box/coercion/lambdaParameters.kt new file mode 100644 index 00000000000..dee6d39dd98 --- /dev/null +++ b/js/js.translator/testData/box/coercion/lambdaParameters.kt @@ -0,0 +1,22 @@ +// EXPECTED_REACHABLE_NODES: 997 +// CHECK_NOT_CALLED_IN_SCOPE: function=toBoxedChar scope=box$lambda +// CHECK_CALLED_IN_SCOPE: function=unboxChar scope=box$lambda +// CHECK_CALLED_IN_SCOPE: function=toBoxedChar scope=box +// CHECK_NOT_CALLED_IN_SCOPE: function=unboxChar scope=box + +fun bar(x: T, y: (T) -> Boolean): Boolean = y(x) && jsTypeOf(x.asDynamic()) != "number" + +fun typeOf(x: dynamic) = js("typeof x") + +fun box(): String { + val f = { x: Char -> + val a: Char = x + val b: Any = x + typeOf(a) == "number" && typeOf(b) == "object" + } + + if (!f('Q')) return "fail1" + if (!bar('W', f)) return "fail2" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/loopOverUnits.kt b/js/js.translator/testData/box/coercion/loopOverUnits.kt new file mode 100644 index 00000000000..3f5e18bddee --- /dev/null +++ b/js/js.translator/testData/box/coercion/loopOverUnits.kt @@ -0,0 +1,25 @@ +// EXPECTED_REACHABLE_NODES: 1009 +class A { + operator fun iterator() = B() +} + +class B() { + private var count = 0 + + operator fun next() { + count++ + } + + operator fun hasNext() = count < 5 +} + +fun box(): String { + var i = 0 + for (x: Any in A()) { + if (x != Unit) return "fail1: $x" + i++ + } + if (i != 5) return "fail2: $i" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/receiverSmartCast.kt b/js/js.translator/testData/box/coercion/receiverSmartCast.kt new file mode 100644 index 00000000000..9c30fd3cde0 --- /dev/null +++ b/js/js.translator/testData/box/coercion/receiverSmartCast.kt @@ -0,0 +1,32 @@ +// EXPECTED_REACHABLE_NODES: 997 +fun foo(x: Any): String { + return when (x) { + is Char -> "char: ${x.toInt()}" + else -> "other: $x" + } +} + +fun bar(x: Any): String { + return when (x) { + is Char -> "char: ${x.baz()}" + else -> "other: $x" + } +} + +fun Char.baz(): Boolean = jsTypeOf(asDynamic()) == "number" + +fun box(): String { + val a = foo('0') + if (a != "char: 48") return "fail1: $a" + + val b = foo(23) + if (b != "other: 23") return "fail2: $b" + + val c = bar('0') + if (c != "char: true") return "fail3: $c" + + val d = bar(23) + if (d != "other: 23") return "fail4: $d" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/safeCallLetReturningUnit.kt b/js/js.translator/testData/box/coercion/safeCallLetReturningUnit.kt new file mode 100644 index 00000000000..3ff896978b8 --- /dev/null +++ b/js/js.translator/testData/box/coercion/safeCallLetReturningUnit.kt @@ -0,0 +1,19 @@ +// EXPECTED_REACHABLE_NODES: 1004 +var log = "" + +fun test(param: Any?) { + param?.let { + log += "test($param);" + } ?: run { + log += "test-null;" + } +} + +fun box(): String { + test(null) + test(23) + + if (log != "test-null;test(23);") return "fail: $log" + + return "OK" +} diff --git a/js/js.translator/testData/box/coercion/topLevelProperty.kt b/js/js.translator/testData/box/coercion/topLevelProperty.kt new file mode 100644 index 00000000000..6b0dfe77fd0 --- /dev/null +++ b/js/js.translator/testData/box/coercion/topLevelProperty.kt @@ -0,0 +1,15 @@ +// EXPECTED_REACHABLE_NODES: 1003 +var log = "" + +fun foo() { + log += "foo()" +} + +val bar: Any = foo() + +fun box(): String { + if (bar != Unit) return "fail1: $bar" + if (log != "foo()") return "fail2: $log" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/tryWithEmptyCatch.kt b/js/js.translator/testData/box/coercion/tryWithEmptyCatch.kt new file mode 100644 index 00000000000..1da50206a17 --- /dev/null +++ b/js/js.translator/testData/box/coercion/tryWithEmptyCatch.kt @@ -0,0 +1,19 @@ +// EXPECTED_REACHABLE_NODES: 1004 +fun test(x: Int): Any { + return try { + if (x % 2 == 0) throw RuntimeException() + x + } + catch (e: RuntimeException) { + } +} + +fun box(): String { + val a = test(1) + if (a != 1) return "fail1: $a" + + val b = test(2) + if (b != Unit) return "fail2: $b" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/unitAsExtensionReceiver.kt b/js/js.translator/testData/box/coercion/unitAsExtensionReceiver.kt new file mode 100644 index 00000000000..6238d7040b1 --- /dev/null +++ b/js/js.translator/testData/box/coercion/unitAsExtensionReceiver.kt @@ -0,0 +1,17 @@ +// EXPECTED_REACHABLE_NODES: 1004 +var log = "" + +fun foo() { + log += "foo" +} + +fun Unit.bar() = jsTypeOf(this.asDynamic()) == "undefined" + +fun Any.baz() = jsTypeOf(this.asDynamic()) == "object" + +fun box(): String { + if (!foo().bar()) return "fail1" + if (!foo().baz()) return "fail2" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/unitIsAs.kt b/js/js.translator/testData/box/coercion/unitIsAs.kt new file mode 100644 index 00000000000..7086238bf5b --- /dev/null +++ b/js/js.translator/testData/box/coercion/unitIsAs.kt @@ -0,0 +1,14 @@ +// EXPECTED_REACHABLE_NODES: 1002 +var log = "" + +fun foo(): Unit { + log += "foo();" +} + +fun box(): String { + if (foo() !is Any) return "fail1" + if (foo() as Any != Unit) return "fail2" + if (log != "foo();foo();") return "fail3: $log" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/unitSafeCall.kt b/js/js.translator/testData/box/coercion/unitSafeCall.kt new file mode 100644 index 00000000000..ceeaf043915 --- /dev/null +++ b/js/js.translator/testData/box/coercion/unitSafeCall.kt @@ -0,0 +1,14 @@ +// EXPECTED_REACHABLE_NODES: 1004 +class C { + fun foo() {} +} + +fun box(): String { + val a: C? = C() + val b: C? = null + + if (a?.foo() != Unit) return "fail1: ${a?.foo()}" + if (b?.foo() != null) return "fail2: ${b?.foo()}" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/coercion/whenWithUnit.kt b/js/js.translator/testData/box/coercion/whenWithUnit.kt new file mode 100644 index 00000000000..b57b8e0c842 --- /dev/null +++ b/js/js.translator/testData/box/coercion/whenWithUnit.kt @@ -0,0 +1,22 @@ +// EXPECTED_REACHABLE_NODES: 1005 +var log = "" + +fun foo() { + log += "foo()" +} + +fun test(x: Int) = when (x) { + in 0..10 -> foo() + else -> 55 +} + +fun box(): String { + val a = test(20) + if (a !is Int) return "fail1: $a" + + val b = test(5) + if (b !is Unit) return "fail2: $b" + if (log != "foo()") return "fail3: $log" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt b/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt index 926ed6fd0e9..9b58567873e 100644 --- a/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt +++ b/js/js.translator/testData/box/nameClashes/propertyAndNativeMethod.kt @@ -3,7 +3,7 @@ // FILE: lib.kt package lib -external fun bar() +external fun bar(): Int val bar = 32 diff --git a/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt b/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt new file mode 100644 index 00000000000..211cbe3d792 --- /dev/null +++ b/js/js.translator/testData/box/standardClasses/charArrayGetSet.kt @@ -0,0 +1,26 @@ +// EXPECTED_REACHABLE_NODES: 997 +fun box(): String { + val a = CharArray(1) + val aType = jsTypeOf(a.asDynamic()[0]) + if (aType != "number") return "fail1: $aType" + + a[0] = 'Q' + val aType2 = jsTypeOf(a.asDynamic()[0]) + if (aType2 != "number") return "fail2: $aType2" + + val aType3 = jsTypeOf(a[0].asDynamic()) + if (aType3 != "number") return "fail3: $aType3" + + val b = Array(1) { 'Q' } + val bType = jsTypeOf(b.asDynamic()[0]) + if (bType != "object") return "fail4: $bType" + + b[0] = 'W' + val bType2 = jsTypeOf(b.asDynamic()[0]) + if (bType2 != "object") return "fail5: $bType2" + + val bType3 = jsTypeOf(b[0].asDynamic()) + if (bType3 != "number") return "fail6: $bType3" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/lineNumbers/charBoxing.kt b/js/js.translator/testData/lineNumbers/charBoxing.kt deleted file mode 100644 index b7f2494ee8b..00000000000 --- a/js/js.translator/testData/lineNumbers/charBoxing.kt +++ /dev/null @@ -1,20 +0,0 @@ -var log = "" - -inline fun String.foo(a: String): Int { - log += "foo1" - return asDynamic().indexOf(a) -} - -inline fun String.foo(a: Char): Int { - log += "foo2" - return indexOf(a.toString()) -} - -fun bar(a: String, b: Char, x: Int) { - log += if (x > 0) - 23 - else - a.foo(b) -} - -// LINES: 6 4 4 5 5 8 8 8 8 8 8 8 11 9 9 10 10 8 8 8 8 18 14 14 14 14 14 15 17 17 9 9 14 10 17 10 14 14 * 1 * 1 \ No newline at end of file diff --git a/js/js.translator/testData/lineNumbers/destructuring.kt b/js/js.translator/testData/lineNumbers/destructuring.kt index a29bb97b188..0f723b38014 100644 --- a/js/js.translator/testData/lineNumbers/destructuring.kt +++ b/js/js.translator/testData/lineNumbers/destructuring.kt @@ -27,4 +27,4 @@ fun bar(f: (Pair) -> Unit) { } -// LINES: 22 16 16 17 18 20 20 21 21 23 9 9 9 9 4 9 9 9 5 5 6 7 11 11 12 12 15 15 27 26 26 * 1 * 1 \ No newline at end of file +// LINES: 22 16 16 17 18 20 20 21 21 22 22 23 9 9 9 9 4 9 9 9 5 5 6 7 11 11 12 12 15 15 27 26 26 * 1 * 1 \ No newline at end of file diff --git a/js/js.translator/testData/lineNumbers/destructuringInline.kt b/js/js.translator/testData/lineNumbers/destructuringInline.kt index e64423be376..f095584150c 100644 --- a/js/js.translator/testData/lineNumbers/destructuringInline.kt +++ b/js/js.translator/testData/lineNumbers/destructuringInline.kt @@ -32,4 +32,4 @@ inline operator fun P.component1() = a inline operator fun P.component2() = b -// LINES: 22 17 17 31 18 18 33 20 20 21 21 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1 \ No newline at end of file +// LINES: 22 17 17 31 18 18 33 20 20 21 21 22 22 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1 \ No newline at end of file diff --git a/js/js.translator/testData/lineNumbers/lambdaWithClosure.kt b/js/js.translator/testData/lineNumbers/lambdaWithClosure.kt index 719b39ce770..e1375ae8f65 100644 --- a/js/js.translator/testData/lineNumbers/lambdaWithClosure.kt +++ b/js/js.translator/testData/lineNumbers/lambdaWithClosure.kt @@ -4,4 +4,4 @@ fun foo(x: Int): () -> Unit = { fun bar() = 23 -// LINES: 1 1 1 2 2 1 1 1 5 5 5 \ No newline at end of file +// LINES: 1 1 1 2 2 3 3 1 1 1 5 5 5 \ No newline at end of file