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
This commit is contained in:
Alexey Andreev
2017-07-18 16:42:30 +03:00
parent ae509d5980
commit 37fa45dc34
63 changed files with 959 additions and 264 deletions
@@ -1,6 +1,3 @@
// IGNORE_BACKEND: JS
// JS backend does not support Unit well. See KT-13932
val foo: () -> Unit = {} val foo: () -> Unit = {}
fun box(): String { fun box(): String {
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun println(s: String) { fun println(s: String) {
} }
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun println(s: String) { fun println(s: String) {
} }
@@ -1,6 +1,5 @@
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// IGNORE_BACKEND: JS
import helpers.* import helpers.*
import kotlin.coroutines.experimental.* import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.* import kotlin.coroutines.experimental.intrinsics.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit! // Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
val nx: Any? = '0' val nx: Any? = '0'
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
class D { class D {
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun foo() {} fun foo() {}
fun box(): String { fun box(): String {
-3
View File
@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun foo(): Any? = bar() fun foo(): Any? = bar()
fun bar() {} fun bar() {}
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.resolve.inline.InlineStrategy import org.jetbrains.kotlin.resolve.inline.InlineStrategy
import org.jetbrains.kotlin.types.KotlinType
var JsName.staticRef: JsNode? by MetadataProperty(default = null) 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.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 JsVars.exportedPackage: String? by MetadataProperty(default = null)
var JsExpressionStatement.exportedTag: 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. * 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. * When absent (`null`) on body of a named function, this function is from external JS module.
@@ -143,3 +148,9 @@ enum class SpecialFunction(val suggestedName: String) {
TO_BOXED_CHAR("toBoxedChar"), TO_BOXED_CHAR("toBoxedChar"),
UNBOX_CHAR("unboxChar"), UNBOX_CHAR("unboxChar"),
} }
enum class BoxingKind {
NONE,
BOXING,
UNBOXING
}
+18 -1
View File
@@ -59,7 +59,24 @@ fun charArray(size: Int, init: dynamic): Array<Char> {
} }
@JsName("charArrayF") @JsName("charArrayF")
inline fun charArrayWithFun(size: Int, init: (Int) -> Char): Array<Char> = fillArrayFun(charArray(size, null), init) inline fun charArrayWithFun(size: Int, init: (Int) -> Char): Array<Char> {
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<Char> {
val array = Array<Char>(size)
for (i in 0..array.size - 1) {
val value = init(i)
js("array[i] = value;")
}
return array
}
@JsName("longArray") @JsName("longArray")
fun longArray(size: Int, init: dynamic): Array<Long> { fun longArray(size: Int, init: dynamic): Array<Long> {
+1 -1
View File
@@ -24,4 +24,4 @@ internal external annotation class JsName(val name: String)
internal external annotation class native internal external annotation class native
internal external fun js(code: String): dynamic external fun js(code: String): dynamic
@@ -60,12 +60,6 @@ public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest {
doTest(fileName); 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") @TestMetadata("classCapturingLocals.kt")
public void testClassCapturingLocals() throws Exception { public void testClassCapturingLocals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/classCapturingLocals.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/classCapturingLocals.kt");
@@ -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") @TestMetadata("js/js.translator/testData/box/crossModuleRef")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -7802,6 +7913,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName); 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") @TestMetadata("hashMapTypeOfElement.kt")
public void testHashMapTypeOfElement() throws Exception { public void testHashMapTypeOfElement() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/hashMapTypeOfElement.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/hashMapTypeOfElement.kt");
@@ -2966,13 +2966,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("lambdaToUnitCast.kt") @TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception { public void testLambdaToUnitCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("notIs.kt") @TestMetadata("notIs.kt")
@@ -2990,13 +2984,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("unitAsAny.kt") @TestMetadata("unitAsAny.kt")
public void testUnitAsAny() throws Exception { public void testUnitAsAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsAny.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsAny.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("unitAsInt.kt") @TestMetadata("unitAsInt.kt")
@@ -3014,13 +3002,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("unitAsSafeAny.kt") @TestMetadata("unitAsSafeAny.kt")
public void testUnitAsSafeAny() throws Exception { public void testUnitAsSafeAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsSafeAny.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsSafeAny.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("unitNullableCast.kt") @TestMetadata("unitNullableCast.kt")
@@ -6694,13 +6676,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("suspendNonLocalReturn.kt") @TestMetadata("suspendNonLocalReturn.kt")
public void testSuspendNonLocalReturn() throws Exception { public void testSuspendNonLocalReturn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("suspendReturn.kt") @TestMetadata("suspendReturn.kt")
@@ -14247,13 +14223,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("primitiveEqObjectChar.kt") @TestMetadata("primitiveEqObjectChar.kt")
public void testPrimitiveEqObjectChar() throws Exception { public void testPrimitiveEqObjectChar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("primitiveEqObjectInt.kt") @TestMetadata("primitiveEqObjectInt.kt")
@@ -14792,13 +14762,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("kt4383.kt") @TestMetadata("kt4383.kt")
public void testKt4383() throws Exception { public void testKt4383() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/kt4383.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/kt4383.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("kt613.kt") @TestMetadata("kt613.kt")
@@ -23603,13 +23567,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("kt4212.kt") @TestMetadata("kt4212.kt")
public void testKt4212() throws Exception { public void testKt4212() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/kt4212.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/kt4212.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
@TestMetadata("kt4265.kt") @TestMetadata("kt4265.kt")
@@ -23651,13 +23609,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("UnitValue.kt") @TestMetadata("UnitValue.kt")
public void testUnitValue() throws Exception { public void testUnitValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/UnitValue.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/UnitValue.kt");
try { doTest(fileName);
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
} }
} }
@@ -17,10 +17,14 @@
package org.jetbrains.kotlin.js.translate.callTranslator package org.jetbrains.kotlin.js.translate.callTranslator
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.js.backend.ast.JsBlock
import org.jetbrains.kotlin.js.backend.ast.* 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.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator 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.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.typeUtil.makeNullable
interface CallInfo { interface CallInfo {
val context: TranslationContext val context: TranslationContext
@@ -99,10 +104,6 @@ fun TranslationContext.getCallInfo(
return FunctionCallInfo(callInfo, argumentsInfo) 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 { private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue): JsExpression {
return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue)) return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue))
} }
@@ -134,6 +135,10 @@ private fun TranslationContext.createCallInfo(
} }
var dispatchReceiver = getDispatchReceiver() 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 extensionReceiver = getExtensionReceiver()
var notNullConditional: JsConditional? = null var notNullConditional: JsConditional? = null
@@ -154,16 +159,19 @@ private fun TranslationContext.createCallInfo(
val container = resolvedCall.resultingDescriptor.containingDeclaration val container = resolvedCall.resultingDescriptor.containingDeclaration
if (DescriptorUtils.isObject(container)) { if (DescriptorUtils.isObject(container)) {
dispatchReceiver = ReferenceTranslator.translateAsValueReference(container, this) dispatchReceiver = ReferenceTranslator.translateAsValueReference(container, this)
dispatchReceiverType = (container as ClassDescriptor).defaultType
} }
} }
dispatchReceiver = boxIfNeedeed(resolvedCall.dispatchReceiver, if (dispatchReceiverType != null) {
resolvedCall.candidateDescriptor.dispatchReceiverParameter, dispatchReceiver = dispatchReceiver?.let {
dispatchReceiver) TranslationUtils.coerce(this, it, dispatchReceiverType!!)
}
}
extensionReceiver = boxIfNeedeed(resolvedCall.extensionReceiver, extensionReceiver = extensionReceiver?.let {
resolvedCall.candidateDescriptor.extensionReceiverParameter, TranslationUtils.coerce(this, it, resolvedCall.candidateDescriptor.extensionReceiverParameter!!.type)
extensionReceiver) }
return object : AbstractCallInfo(), CallInfo { return object : AbstractCallInfo(), CallInfo {
@@ -179,7 +187,9 @@ private fun TranslationContext.createCallInfo(
result result
} }
else { else {
notNullConditionalForSafeCall.thenExpression = result val type = resolvedCall.getReturnType()
result.type = type
notNullConditionalForSafeCall.thenExpression = TranslationUtils.coerce(context, result, type.makeNullable())
notNullConditionalForSafeCall notNullConditionalForSafeCall
} }
} }
@@ -31,11 +31,14 @@ import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.psi.Call.CallType import org.jetbrains.kotlin.psi.Call.CallType
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariable 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.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.* import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.makeNullable
object CallTranslator { object CallTranslator {
@JvmOverloads @JvmOverloads
@@ -52,7 +55,9 @@ object CallTranslator {
extensionOrDispatchReceiver: JsExpression? = null extensionOrDispatchReceiver: JsExpression? = null
): JsExpression { ): JsExpression {
val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), null) 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, fun translateSet(context: TranslationContext,
@@ -61,7 +66,9 @@ object CallTranslator {
extensionOrDispatchReceiver: JsExpression? = null extensionOrDispatchReceiver: JsExpression? = null
): JsExpression { ): JsExpression {
val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), value) 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, fun buildCall(context: TranslationContext,
@@ -95,7 +102,7 @@ private fun translateCall(
assert(explicitReceivers.extensionReceiver == null) { "VariableAsFunctionResolvedCall must have one receiver" } assert(explicitReceivers.extensionReceiver == null) { "VariableAsFunctionResolvedCall must have one receiver" }
val variableCall = resolvedCall.variableCall val variableCall = resolvedCall.variableCall
return if (variableCall.expectedReceivers()) { val result = if (variableCall.expectedReceivers()) {
val newReceiver = CallTranslator.translateGet(context, variableCall, explicitReceivers.extensionOrDispatchReceiver) val newReceiver = CallTranslator.translateGet(context, variableCall, explicitReceivers.extensionOrDispatchReceiver)
translateFunctionCall(context, resolvedCall.functionCall, resolvedCall.variableCall, ExplicitReceivers(newReceiver)) translateFunctionCall(context, resolvedCall.functionCall, resolvedCall.variableCall, ExplicitReceivers(newReceiver))
} else { } else {
@@ -110,6 +117,8 @@ private fun translateCall(
ExplicitReceivers(dispatchReceiver, explicitReceivers.extensionOrDispatchReceiver)) ExplicitReceivers(dispatchReceiver, explicitReceivers.extensionOrDispatchReceiver))
} }
} }
return result
} }
val call = resolvedCall.call val call = resolvedCall.call
@@ -151,9 +160,13 @@ private fun translateFunctionCall(
callExpression.isTailCallSuspend = true callExpression.isTailCallSuspend = true
} }
} }
callExpression.type = resolvedCall.getReturnType().let { if (resolvedCall.call.isSafeCall()) it.makeNullable() else it }
return callExpression return callExpression
} }
fun ResolvedCall<out CallableDescriptor>.getReturnType(): KotlinType = TranslationUtils.getReturnTypeForCoercion(resultingDescriptor)
private val TranslationContext.isInStateMachine private val TranslationContext.isInStateMachine
get() = (declarationDescriptor as? FunctionDescriptor)?.requiresStateMachineTransformation(this) == true get() = (declarationDescriptor as? FunctionDescriptor)?.requiresStateMachineTransformation(this) == true
@@ -24,7 +24,7 @@ public final class TemporaryConstVariable extends TemporaryVariable{
private boolean initialized = false; private boolean initialized = false;
public TemporaryConstVariable(@NotNull JsName variableName, @NotNull JsExpression assignmentExpression) { public TemporaryConstVariable(@NotNull JsName variableName, @NotNull JsExpression assignmentExpression) {
super(variableName, assignmentExpression); super(variableName, assignmentExpression, null);
} }
@NotNull @NotNull
@@ -21,33 +21,40 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.types.KotlinType;
public class TemporaryVariable { public class TemporaryVariable {
/*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) { /*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) {
JsBinaryOperation rhs = null; JsBinaryOperation rhs = null;
KotlinType type = null;
if (initExpression != null) { if (initExpression != null) {
rhs = JsAstUtils.assignment(temporaryName.makeRef(), initExpression); rhs = JsAstUtils.assignment(temporaryName.makeRef(), initExpression);
rhs.source(initExpression.getSource()); rhs.source(initExpression.getSource());
MetadataProperties.setSynthetic(rhs, true); MetadataProperties.setSynthetic(rhs, true);
type = MetadataProperties.getType(initExpression);
} }
return new TemporaryVariable(temporaryName, rhs); return new TemporaryVariable(temporaryName, rhs, type);
} }
@Nullable @Nullable
private final JsExpression assignmentExpression; private final JsExpression assignmentExpression;
@NotNull @NotNull
private final JsName variableName; 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.variableName = temporaryName;
this.assignmentExpression = assignmentExpression; this.assignmentExpression = assignmentExpression;
this.type = type;
} }
@NotNull @NotNull
public JsNameRef reference() { public JsNameRef reference() {
JsNameRef result = variableName.makeRef(); JsNameRef result = variableName.makeRef();
MetadataProperties.setSynthetic(result, true); MetadataProperties.setSynthetic(result, true);
MetadataProperties.setType(result, type);
return result; return result;
} }
@@ -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.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils 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.TranslationUtils.*
import org.jetbrains.kotlin.js.translate.utils.finalElement import org.jetbrains.kotlin.js.translate.utils.finalElement
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter
@@ -172,7 +173,7 @@ fun TranslationContext.translateDelegateOrInitializerExpression(expression: KtPr
CallTranslator.translate(innerContext, provideDelegateCall, initializer) CallTranslator.translate(innerContext, provideDelegateCall, initializer)
} }
else { else {
initializer TranslationUtils.coerce(this, initializer, propertyDescriptor.type)
} }
} }
@@ -84,8 +84,7 @@ public class DestructuringDeclarationTranslator extends AbstractTranslator {
setInlineCallMetadata(entryInitializer, entry, entryInitCall, context()); setInlineCallMetadata(entryInitializer, entry, entryInitCall, context());
} }
entryInitializer = TranslationUtils.boxCastIfNeeded(context(), entryInitializer, candidateDescriptor.getReturnType(), entryInitializer = TranslationUtils.coerce(context(), entryInitializer, descriptor.getType());
descriptor.getType());
JsName name = context().getNameForDescriptor(descriptor); JsName name = context().getNameForDescriptor(descriptor);
if (isVarCapturedInClosure(context().bindingContext(), descriptor)) { if (isVarCapturedInClosure(context().bindingContext(), descriptor)) {
@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention; 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.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt; 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.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
@@ -107,6 +108,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
jsBlock.getStatements().add(jsStatement); jsBlock.getStatements().add(jsStatement);
} }
} }
if (statements.isEmpty()) {
ClassDescriptor unitClass = context.getCurrentModule().getBuiltIns().getUnit();
jsBlock.getStatements().add(JsAstUtils.asSyntheticStatement(
ReferenceTranslator.translateAsValueReference(unitClass, context)));
}
return jsBlock; return jsBlock;
} }
@@ -135,6 +141,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return new JsReturn(ref.source(jetReturnExpression)); return new JsReturn(ref.source(jetReturnExpression));
} }
FunctionDescriptor returnTarget = getNonLocalReturnTarget(jetReturnExpression, context);
JsReturn jsReturn; JsReturn jsReturn;
if (returned == null) { if (returned == null) {
jsReturn = new JsReturn(null); jsReturn = new JsReturn(null);
@@ -146,15 +154,19 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
assert returnedType != null : "Resolved return expression is expected to have type: " + assert returnedType != null : "Resolved return expression is expected to have type: " +
PsiUtilsKt.getTextWithLocation(jetReturnExpression); PsiUtilsKt.getTextWithLocation(jetReturnExpression);
if (KotlinBuiltIns.isCharOrNullableChar(returnedType) && CallableDescriptor returnTargetOrCurrentFunction = returnTarget;
TranslationUtils.shouldBoxReturnValue((CallableDescriptor)context.getDeclarationDescriptor())) { if (returnTargetOrCurrentFunction == null) {
jsReturnExpression = TranslationUtils.charToBoxedChar(context, jsReturnExpression); returnTargetOrCurrentFunction = (CallableDescriptor) context.getDeclarationDescriptor();
}
if (returnTargetOrCurrentFunction != null) {
jsReturnExpression = TranslationUtils.coerce(context, jsReturnExpression,
TranslationUtils.getReturnTypeForCoercion(returnTargetOrCurrentFunction));
} }
jsReturn = new JsReturn(jsReturnExpression); jsReturn = new JsReturn(jsReturnExpression);
} }
MetadataProperties.setReturnTarget(jsReturn, getNonLocalReturnTarget(jetReturnExpression, context)); MetadataProperties.setReturnTarget(jsReturn, returnTarget);
return jsReturn.source(jetReturnExpression); return jsReturn.source(jetReturnExpression);
} }
@@ -245,10 +257,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObjectQualifier()) { if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObjectQualifier()) {
JsExpression receiver = translateAsExpression(receiverExpression, context); JsExpression receiver = translateAsExpression(receiverExpression, context);
KotlinType type = context.bindingContext().getType(receiverExpression); receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getAnyType());
if (type != null && KotlinBuiltIns.isChar(type)) {
receiver = TranslationUtils.charToBoxedChar(context, receiver);
}
return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver); return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver);
} }
@@ -270,6 +279,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) { public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) {
assert expression.getCondition() != null : "condition should not ne null: " + expression.getText(); assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context); JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
KotlinType type = context.bindingContext().getType(expression);
boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext()); boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext());
@@ -281,6 +291,15 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
JsStatement elseStatement = JsStatement elseStatement =
elseExpression != null ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression, context) : null; 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) { if (isKotlinExpression) {
JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement); JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement);
JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement); JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement);
@@ -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.descriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor 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.hasDefaultValue
import org.jetbrains.kotlin.js.backend.ast.metadata.type
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.descriptorUtils.shouldBeExported import org.jetbrains.kotlin.js.descriptorUtils.shouldBeExported
import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper
@@ -58,7 +59,9 @@ fun TranslationContext.translateAndAliasParameters(
if (descriptor.requiresExtensionReceiverParameter) { if (descriptor.requiresExtensionReceiverParameter) {
val receiverParameterName = JsScope.declareTemporaryName(Namer.getReceiverParameterName()) 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) targetList += JsParameter(receiverParameterName)
} }
@@ -19,6 +19,7 @@
package org.jetbrains.kotlin.js.translate.expression package org.jetbrains.kotlin.js.translate.expression
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator 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.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.PsiUtils.getLoopRange
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -142,7 +144,10 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val currentVarInit = val currentVarInit =
if (destructuringParameter == null) { 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 { else {
val innerBlockContext = context.innerBlock(block) val innerBlockContext = context.innerBlock(block)
@@ -224,7 +229,7 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
fun translateForOverArray(): JsStatement { fun translateForOverArray(): JsStatement {
val rangeExpression = context.defineTemporary(Translation.translateAsExpression(loopRange, context)) val rangeExpression = context.defineTemporary(Translation.translateAsExpression(loopRange, context))
val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf<JsExpression>(), context) val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf(), context)
val end = context.defineTemporary(length) val end = context.defineTemporary(length)
val index = context.declareTemporary(JsIntLiteral(0), expression) val index = context.declareTemporary(JsIntLiteral(0), expression)
@@ -81,10 +81,8 @@ public final class PatternTranslator extends AbstractTranslator {
KtTypeReference typeReference = expression.getRight(); KtTypeReference typeReference = expression.getRight();
assert typeReference != null: "Cast expression must have type reference"; assert typeReference != null: "Cast expression must have type reference";
KotlinType leftType = context().bindingContext().getType(left); KotlinType anyType = context().getCurrentModule().getBuiltIns().getAnyType();
if (leftType != null && KotlinBuiltIns.isChar(leftType)) { expressionToCast = TranslationUtils.coerce(context(), expressionToCast, anyType);
expressionToCast = TranslationUtils.charToBoxedChar(context(), expressionToCast);
}
TemporaryVariable temporary = context().declareTemporary(expressionToCast, expression); TemporaryVariable temporary = context().declareTemporary(expressionToCast, expression);
JsExpression isCheck = translateIsCheck(temporary.assignmentExpression(), typeReference); JsExpression isCheck = translateIsCheck(temporary.assignmentExpression(), typeReference);
@@ -102,12 +100,11 @@ public final class PatternTranslator extends AbstractTranslator {
JsExpression result = new JsConditional(isCheck, temporary.reference(), onFail); JsExpression result = new JsConditional(isCheck, temporary.reference(), onFail);
KotlinType expressionType = context().bindingContext().getType(expression); KotlinType targetType = getTypeByReference(bindingContext(), typeReference);
if (expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType)) { if (isSafeCast(expression)) {
result = TranslationUtils.boxedCharToChar(context(), result); targetType = targetType.unwrap().makeNullableAsSpecified(true);
} }
return TranslationUtils.coerce(context(), result, targetType);
return result;
} }
@NotNull @NotNull
@@ -115,10 +112,8 @@ public final class PatternTranslator extends AbstractTranslator {
KtExpression left = expression.getLeftHandSide(); KtExpression left = expression.getLeftHandSide();
JsExpression expressionToCheck = Translation.translateAsExpression(left, context()); JsExpression expressionToCheck = Translation.translateAsExpression(left, context());
KotlinType leftType = context().bindingContext().getType(left); KotlinType anyType = context().getCurrentModule().getBuiltIns().getAnyType();
if (leftType != null && KotlinBuiltIns.isChar(leftType)) { expressionToCheck = TranslationUtils.coerce(context(), expressionToCheck, anyType);
expressionToCheck = TranslationUtils.charToBoxedChar(context(), expressionToCheck);
}
KtTypeReference typeReference = expression.getTypeReference(); KtTypeReference typeReference = expression.getTypeReference();
assert typeReference != null; assert typeReference != null;
@@ -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.operation.InOperationTranslator;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils; 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.mutator.CoercionMutator;
import org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator;
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
@@ -50,6 +52,9 @@ public final class WhenTranslator extends AbstractTranslator {
@Nullable @Nullable
private final JsExpression expressionToMatch; private final JsExpression expressionToMatch;
@Nullable
private final KotlinType type;
private WhenTranslator(@NotNull KtWhenExpression expression, @NotNull TranslationContext context) { private WhenTranslator(@NotNull KtWhenExpression expression, @NotNull TranslationContext context) {
super(context); super(context);
@@ -57,6 +62,8 @@ public final class WhenTranslator extends AbstractTranslator {
KtExpression subject = expression.getSubjectExpression(); KtExpression subject = expression.getSubjectExpression();
expressionToMatch = subject != null ? context.defineTemporary(Translation.translateAsExpression(subject, context)) : null; expressionToMatch = subject != null ? context.defineTemporary(Translation.translateAsExpression(subject, context)) : null;
type = context().bindingContext().getType(expression);
} }
private JsNode translate() { private JsNode translate() {
@@ -106,13 +113,18 @@ public final class WhenTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
private static JsStatement translateEntryExpression( private JsStatement translateEntryExpression(
@NotNull KtWhenEntry entry, @NotNull KtWhenEntry entry,
@NotNull TranslationContext context, @NotNull TranslationContext context,
@NotNull JsBlock block) { @NotNull JsBlock block) {
KtExpression expressionToExecute = entry.getExpression(); KtExpression expressionToExecute = entry.getExpression();
assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; 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 @NotNull
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.idea.MainFunctionDetector; import org.jetbrains.kotlin.idea.MainFunctionDetector;
import org.jetbrains.kotlin.js.backend.ast.*; 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.config.JsConfig;
import org.jetbrains.kotlin.js.facade.MainCallParameters; import org.jetbrains.kotlin.js.facade.MainCallParameters;
import org.jetbrains.kotlin.js.facade.TranslationUnit; import org.jetbrains.kotlin.js.facade.TranslationUnit;
@@ -123,6 +124,15 @@ public final class Translation {
) { ) {
KotlinType expectedType = context.bindingContext().getType(expression); KotlinType expectedType = context.bindingContext().getType(expression);
ConstantValue<?> constant = compileTimeValue.toConstantValue(expectedType != null ? expectedType : TypeUtils.NO_EXPECTED_TYPE); 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) { if (constant instanceof NullValue) {
return new JsNullLiteral(); return new JsNullLiteral();
} }
@@ -187,10 +197,17 @@ public final class Translation {
@NotNull JsBlock block @NotNull JsBlock block
) { ) {
JsNode jsNode = translateExpression(expression, context, block); JsNode jsNode = translateExpression(expression, context, block);
if (jsNode instanceof JsExpression) { if (jsNode instanceof JsExpression) {
KotlinType expressionType = context.bindingContext().getType(expression); JsExpression jsExpression = (JsExpression) jsNode;
return unboxIfNeeded(context, (JsExpression) jsNode, KotlinType type = context.bindingContext().getType(expression);
expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType)); 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(); 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); TemporaryVariable result = context.declareTemporary(null, expression);
AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference()); AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator)); 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)); block.getStatements().add(convertToStatement(jsNode));
return new JsNullLiteral().source(expression); 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 @NotNull
public static JsStatement translateAsStatement(@NotNull KtExpression expression, @NotNull TranslationContext context) { public static JsStatement translateAsStatement(@NotNull KtExpression expression, @NotNull TranslationContext context) {
return translateAsStatement(expression, context, context.dynamicContext().jsBlock()); return translateAsStatement(expression, context, context.dynamicContext().jsBlock());
@@ -50,6 +50,7 @@ public final class FunctionIntrinsics {
register(NumberAndCharConversionFIF.INSTANCE); register(NumberAndCharConversionFIF.INSTANCE);
register(ThrowableConstructorIntrinsicFactory.INSTANCE); register(ThrowableConstructorIntrinsicFactory.INSTANCE);
register(ExceptionPropertyIntrinsicFactory.INSTANCE); register(ExceptionPropertyIntrinsicFactory.INSTANCE);
register(AsDynamicFIF.INSTANCE);
} }
private void register(@NotNull FunctionIntrinsicFactory instance) { private void register(@NotNull FunctionIntrinsicFactory instance) {
@@ -43,7 +43,7 @@ import java.util.*
object ArrayFIF : CompositeFIF() { object ArrayFIF : CompositeFIF() {
@JvmField @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." } assert(arguments.size == 1) { "Array get expression must have one argument." }
val (indexExpression) = arguments val (indexExpression) = arguments
JsArrayAccess(callInfo.dispatchReceiver, indexExpression) JsArrayAccess(callInfo.dispatchReceiver, indexExpression)
@@ -108,7 +108,12 @@ object ArrayFIF : CompositeFIF() {
} }
} }
else { else {
"kotlin.newArrayF" if (primitiveType == CHAR) {
"kotlin.untypedCharArrayF"
}
else {
"kotlin.newArrayF"
}
} }
} }
@@ -183,7 +188,7 @@ object ArrayFIF : CompositeFIF() {
} }
} }
else { else {
JsAstUtils.invokeKotlinFunction("newArrayF", size, fn) JsAstUtils.invokeKotlinFunction(if (type == CHAR) "untypedCharArrayF" else "newArrayF", size, fn)
} }
invocation.inlineStrategy = InlineStrategy.IN_PLACE invocation.inlineStrategy = InlineStrategy.IN_PLACE
val descriptor = callInfo.resolvedCall.resultingDescriptor.original val descriptor = callInfo.resolvedCall.resultingDescriptor.original
@@ -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<JsExpression>, context: TranslationContext): JsExpression =
TranslationUtils.coerce(context, callInfo.extensionReceiver!!, callInfo.resolvedCall.extensionReceiver!!.type)
})
}
}
@@ -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.FunctionIntrinsic;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed; 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.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt; import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.resolve.DescriptorFactory; import org.jetbrains.kotlin.resolve.DescriptorFactory;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; 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"); public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString");
@NotNull @NotNull
public static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() { private static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() {
@NotNull @NotNull
@Override @Override
public JsExpression apply( public JsExpression apply(
@Nullable JsExpression receiver, @NotNull List<? extends JsExpression> arguments, @NotNull TranslationContext context @Nullable JsExpression receiver, @NotNull List<? extends JsExpression> arguments, @NotNull TranslationContext context
) { ) {
assert receiver != null; assert receiver != null;
receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getCharType());
return JsAstUtils.charToString(receiver); return JsAstUtils.charToString(receiver);
} }
}; };
@@ -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.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral
import org.jetbrains.kotlin.js.translate.context.TranslationContext 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.intrinsic.functions.factories.TopLevelFIF
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken 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 ktLeft = checkNotNull(expression.left) { "No left-hand side: " + expression.text }
val ktRight = checkNotNull(expression.right) { "No right-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 leftKotlinType = getRefinedType(ktLeft, context)
val rightType = getRefinedType(ktRight, context)?.let { KotlinBuiltIns.getPrimitiveType(it) } 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)) { if (leftType != null && (leftType in SIMPLE_PRIMITIVES || leftType == rightType && leftType != PrimitiveType.LONG)) {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType)
Translation.unboxIfNeeded(context, left, leftType == PrimitiveType.CHAR), val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType!!)
Translation.unboxIfNeeded(context, right, rightType == PrimitiveType.CHAR)) return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, coercedLeft, coercedRight)
} }
val resolvedCall = expression.getResolvedCall(context.bindingContext()) val resolvedCall = expression.getResolvedCall(context.bindingContext())
@@ -77,10 +78,10 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right) return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
} }
val maybeBoxedLeft = if (leftType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, left) else left val anyType = context.currentModule.builtIns.anyType
val maybeBoxedRight = if (rightType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, right) else right val coercedLeft = TranslationUtils.coerce(context, left, anyType)
val coercedRight = TranslationUtils.coerce(context, right, anyType)
val result = TopLevelFIF.KOTLIN_EQUALS.apply(maybeBoxedLeft, Arrays.asList<JsExpression>(maybeBoxedRight), context) val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context)
return if (isNegated) JsAstUtils.not(result) else result return if (isNegated) JsAstUtils.not(result) else result
} }
@@ -126,15 +126,14 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression translateElvis() { private JsExpression translateElvis() {
KotlinType expressionType = context().bindingContext().getType(expression); KotlinType expressionType = context().bindingContext().getType(expression);
assert expressionType != null;
JsExpression leftExpression = TranslationUtils.boxCastIfNeeded( JsExpression leftExpression = TranslationUtils.coerce(
context(), Translation.translateAsExpression(leftKtExpression, context()), context(), Translation.translateAsExpression(leftKtExpression, context()), expressionType);
context().bindingContext().getType(leftKtExpression), expressionType);
JsBlock rightBlock = new JsBlock(); JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = TranslationUtils.boxCastIfNeeded( JsExpression rightExpression = TranslationUtils.coerce(
context(),Translation.translateAsExpression(rightKtExpression, context(), rightBlock), context(), Translation.translateAsExpression(rightKtExpression, context(), rightBlock), expressionType);
context().bindingContext().getType(rightKtExpression), expressionType);
if (rightBlock.isEmpty()) { if (rightBlock.isEmpty()) {
return TranslationUtils.notNullConditional(leftExpression, rightExpression, context()); return TranslationUtils.notNullConditional(leftExpression, rightExpression, context());
@@ -66,7 +66,7 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator {
if (leftType != null && KotlinBuiltIns.isStringOrNullableString(leftType)) { if (leftType != null && KotlinBuiltIns.isStringOrNullableString(leftType)) {
result = JsAstUtils.charToString(result); result = JsAstUtils.charToString(result);
} }
else if (leftType == null || !KotlinBuiltIns.isCharOrNullableChar(leftType)) { else if (leftType != null && !KotlinBuiltIns.isCharOrNullableChar(leftType)) {
result = TranslationUtils.charToBoxedChar(context, result); result = TranslationUtils.charToBoxedChar(context, result);
} }
} }
@@ -17,9 +17,10 @@
package org.jetbrains.kotlin.js.translate.reference package org.jetbrains.kotlin.js.translate.reference
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineOrReturn import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineOrReturn
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType 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.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.js.backend.ast.* 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.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.DescriptorUtils 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.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
@@ -191,18 +192,15 @@ class CallArgumentTranslator private constructor(
val parenthisedArgumentExpression = arg.getArgumentExpression() val parenthisedArgumentExpression = arg.getArgumentExpression()
val param = argsToParameters[arg]!!.original val param = argsToParameters[arg]!!.original
val parameterType = if (resolvedCall.call.callType == Call.CallType.INVOKE) { val isLambda = resolvedCall.resultingDescriptor.let { it.getFunctionalClassKind() != null || it is FunctionInvokeDescriptor }
DefaultBuiltIns.Instance.anyType val parameterType = if (!isLambda) param.varargElementType ?: param.type else context.currentModule.builtIns.anyType
}
else { var argJs = Translation.translateAsExpression(parenthisedArgumentExpression!!, argumentContext)
param.varargElementType ?: param.type if (!param.isVararg || arg.getSpreadElement() == null) {
argJs = TranslationUtils.coerce(context, argJs, parameterType)
} }
val argType = context.bindingContext().getType(parenthisedArgumentExpression!!) arg to argJs
val argJs = Translation.translateAsExpression(parenthisedArgumentExpression, argumentContext)
arg to TranslationUtils.boxCastIfNeeded(context, argJs, argType, parameterType)
} }
val resolvedOrder = resolvedCall.valueArgumentsByIndex.orEmpty() val resolvedOrder = resolvedCall.valueArgumentsByIndex.orEmpty()
@@ -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.SideEffectKind
import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference 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.sideEffects
import org.jetbrains.kotlin.js.backend.ast.metadata.type
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.BindingUtils 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.js.translate.utils.finalElement
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
@@ -107,15 +109,19 @@ object CallableReferenceTranslator {
null null
} }
val functionDescriptor = realResolvedCall.resultingDescriptor
val aliases = mutableMapOf<KtExpression, JsExpression>() val aliases = mutableMapOf<KtExpression, JsExpression>()
for ((index, valueArg) in fakeCall.valueArguments.withIndex()) { for ((index, valueArg) in fakeCall.valueArguments.withIndex()) {
val paramName = JsScope.declareTemporaryName(descriptor.valueParameters[index].name.asString()) val paramName = JsScope.declareTemporaryName(descriptor.valueParameters[index].name.asString())
function.parameters += JsParameter(paramName) 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 functionContext = context.innerBlock(function.body).innerContextWithAliasesForExpressions(aliases)
val invocation = CallTranslator.translate(functionContext, fakeResolvedCall, receiverParam) 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) val rawCallableRef = bindIfNecessary(function, receiver)
return wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef) return wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef)
@@ -17,7 +17,10 @@
package org.jetbrains.kotlin.js.translate.reference; package org.jetbrains.kotlin.js.translate.reference;
import org.jetbrains.annotations.NotNull; 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.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.backend.ast.JsExpression;
import org.jetbrains.kotlin.js.backend.ast.JsInvocation; import org.jetbrains.kotlin.js.backend.ast.JsInvocation;
import org.jetbrains.kotlin.js.backend.ast.JsName; 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.KtQualifiedExpression;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import org.jetbrains.kotlin.resolve.DescriptorUtils; 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.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName;
@@ -49,6 +53,41 @@ public final class ReferenceTranslator {
@NotNull @NotNull
public static JsExpression translateAsValueReference(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) { 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)) { if (AnnotationsUtils.isNativeObject(descriptor) || AnnotationsUtils.isLibraryObject(descriptor)) {
return context.getInnerReference(descriptor); return context.getInnerReference(descriptor);
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; 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.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; 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.KtDeclarationWithBody;
import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
@@ -120,6 +122,16 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock); JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements()); 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; return jsBlock;
} }
@@ -145,12 +157,8 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
} }
assert declaration.getBodyExpression() != null; assert declaration.getBodyExpression() != null;
assert descriptor.getReturnType() != null; KotlinType returnType = TranslationUtils.getReturnTypeForCoercion(descriptor);
KotlinType bodyType = context().bindingContext().getType(declaration.getBodyExpression()); node = TranslationUtils.coerce(context(), (JsExpression) node, returnType);
if (bodyType == null && KotlinBuiltIns.isCharOrNullableChar(descriptor.getReturnType()) ||
bodyType != null && KotlinBuiltIns.isCharOrNullableChar(bodyType) && TranslationUtils.shouldBoxReturnValue(descriptor)) {
node = TranslationUtils.charToBoxedChar(context(), (JsExpression) node);
}
JsReturn jsReturn = new JsReturn((JsExpression) node); JsReturn jsReturn = new JsReturn((JsExpression) node);
jsReturn.setSource(declaration.getBodyExpression()); jsReturn.setSource(declaration.getBodyExpression());
@@ -19,12 +19,15 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*; 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.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation; import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*; 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.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction; import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.translate.context.Namer; 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.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt; import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
import org.jetbrains.kotlin.types.DynamicTypesKt;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.stream.Collectors;
import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*; import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
@@ -186,7 +189,11 @@ public final class TranslationUtils {
else { else {
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor)); 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 @NotNull
@@ -205,10 +212,9 @@ public final class TranslationUtils {
if (initializer != null) { if (initializer != null) {
jsInitExpression = Translation.translateAsExpression(initializer, context); jsInitExpression = Translation.translateAsExpression(initializer, context);
KotlinType propertyType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, declaration).getType(); KotlinType propertyType = BindingContextUtils.getNotNull(
KotlinType initType = context.bindingContext().getType(initializer); context.bindingContext(), BindingContext.VARIABLE, declaration).getType();
jsInitExpression = coerce(context, jsInitExpression, propertyType);
jsInitExpression = boxCastIfNeeded(context, jsInitExpression, initType, propertyType);
} }
return jsInitExpression; return jsInitExpression;
} }
@@ -394,61 +400,139 @@ public final class TranslationUtils {
ModalityKt.isOverridable(descriptor); ModalityKt.isOverridable(descriptor);
} }
private static boolean overridesReturnAny(CallableDescriptor c) { @NotNull
KotlinType returnType = c.getOriginal().getReturnType(); public static KotlinType getReturnTypeForCoercion(@NotNull CallableDescriptor descriptor) {
assert returnType != null; descriptor = descriptor.getOriginal();
if (KotlinBuiltIns.isAnyOrNullableAny(returnType) || TypeUtils.isTypeParameter(returnType)) return true;
for (CallableDescriptor o : c.getOverriddenDescriptors()) { if (FunctionTypesKt.getFunctionalClassKind(descriptor) != null || descriptor instanceof AnonymousFunctionDescriptor) {
if (overridesReturnAny(o)) return true; return DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
} }
return false;
}
Collection<? extends CallableDescriptor> overridden = descriptor.getOverriddenDescriptors();
public static boolean shouldBoxReturnValue(CallableDescriptor c) { if (overridden.isEmpty()) {
return overridesReturnAny(c) || c instanceof CallableMemberDescriptor && ModalityKt.isOverridable((CallableMemberDescriptor)c); return descriptor.getReturnType() != null ?
} descriptor.getReturnType() :
DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
@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);
} }
return e;
Set<KotlinType> typesFromOverriddenCallables = overridden.stream()
.map(TranslationUtils::getReturnTypeForCoercion)
.collect(Collectors.toSet());
return typesFromOverriddenCallables.size() == 1
? typesFromOverriddenCallables.iterator().next()
: DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
} }
@NotNull @NotNull
public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) { public static KotlinType getDispatchReceiverTypeForCoercion(@NotNull CallableDescriptor descriptor) {
JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.TO_BOXED_CHAR, unnestBoxing(expression)); descriptor = descriptor.getOriginal();
invocation.setSource(expression.getSource()); if (descriptor.getDispatchReceiverParameter() == null) {
return withBoxingMetadata(invocation); throw new IllegalArgumentException("This method can only be used for class members; " +
"given descriptor is not a member of a class " + descriptor);
}
Collection<? extends CallableDescriptor> overridden = descriptor.getOverriddenDescriptors();
if (overridden.isEmpty()) {
return descriptor.getDispatchReceiverParameter().getType();
}
Set<KotlinType> typesFromOverriddenCallables = overridden.stream()
.map(TranslationUtils::getDispatchReceiverTypeForCoercion)
.collect(Collectors.toSet());
return typesFromOverriddenCallables.size() == 1
? typesFromOverriddenCallables.iterator().next()
: DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
} }
@NotNull @NotNull
public static JsExpression boxedCharToChar(@NotNull TranslationContext context, @NotNull JsExpression expression) { public static JsExpression coerce(@NotNull TranslationContext context, @NotNull JsExpression value, @NotNull KotlinType to) {
JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.UNBOX_CHAR, unnestBoxing(expression)); if (DynamicTypesKt.isDynamic(to)) return value;
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation); 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 @NotNull
private static JsExpression unnestBoxing(@NotNull JsExpression expression) { private static JsExpression unitToVoid(@NotNull JsExpression expression) {
if (expression instanceof JsInvocation && MetadataProperties.getBoxing((JsInvocation) expression)) { if (expression instanceof JsBinaryOperation) {
return ((JsInvocation) expression).getArguments().get(0); JsBinaryOperation binary = (JsBinaryOperation) expression;
if (binary.getOperator() == JsBinaryOperator.COMMA && MetadataProperties.isUnit(binary.getArg2())) {
return binary.getArg1();
}
} }
return expression; return expression;
} }
@NotNull @NotNull
private static JsInvocation withBoxingMetadata(@NotNull JsInvocation call) { public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
MetadataProperties.setBoxing(call, true); if (expression instanceof JsInvocation) {
return call; 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 @NotNull
@@ -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;
}
}
+10
View File
@@ -9,5 +9,15 @@ fun box(): String {
assertEquals(false, 'A'== 'B') assertEquals(false, 'A'== 'B')
assertEquals(false, ('A' as Any) == (65 as Any)) assertEquals(false, ('A' as Any) == (65 as Any))
assertTrue(bar('Q'))
assertFalse(bar('W'))
assertTrue(baz('Q'))
assertFalse(baz('W'))
return "OK" return "OK"
} }
fun bar(x: Char) = x.equals('Q')
fun baz(x: Any) = x.equals('Q')
+18
View File
@@ -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"
}
@@ -0,0 +1,17 @@
// EXPECTED_REACHABLE_NODES: 1006
abstract class A<out T> {
abstract fun foo(): T
}
class B() : A<Char>() {
override fun foo() = 'Q'
}
private fun typeOf(x: dynamic) = js("typeof x")
fun box(): String {
val a: A<Any> = B()
if (typeOf(a.foo()) != "object") return "fail1"
if (typeOf(B().foo()) != "number") return "fail2"
return "OK"
}
@@ -0,0 +1,20 @@
// EXPECTED_REACHABLE_NODES: 1012
var log = ""
abstract class A<out T> {
abstract fun foo(): T
}
class B() : A<Unit>() {
override fun foo() {
log += "B.foo()"
}
}
fun box(): String {
val a: A<Any> = B()
if (a.foo() != Unit) return "fail1"
if (log != "B.foo()") return "fail2"
return "OK"
}
@@ -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"
}
@@ -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())
+19
View File
@@ -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"
}
@@ -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"
}
@@ -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 <T> 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"
}
+25
View File
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
@@ -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"
}
+14
View File
@@ -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"
}
+14
View File
@@ -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"
}
+22
View File
@@ -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"
}
@@ -3,7 +3,7 @@
// FILE: lib.kt // FILE: lib.kt
package lib package lib
external fun bar() external fun bar(): Int
val bar = 32 val bar = 32
@@ -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<Char>(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"
}
-20
View File
@@ -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
+1 -1
View File
@@ -27,4 +27,4 @@ fun bar(f: (Pair<String, String>) -> 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 // 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
@@ -32,4 +32,4 @@ inline operator fun P.component1() = a
inline operator fun P.component2() = b 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 // 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
+1 -1
View File
@@ -4,4 +4,4 @@ fun foo(x: Int): () -> Unit = {
fun bar() = 23 fun bar() = 23
// LINES: 1 1 1 2 2 1 1 1 5 5 5 // LINES: 1 1 1 2 2 3 3 1 1 1 5 5 5