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

View File

@@ -1,6 +1,3 @@
// IGNORE_BACKEND: JS
// JS backend does not support Unit well. See KT-13932
val foo: () -> Unit = {}
fun box(): String {

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) {
}

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) {
}

View File

@@ -1,6 +1,5 @@
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND: JS
import helpers.*
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*

View File

@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS
// Auto-generated by GeneratePrimitiveVsObjectEqualityTestData. Do not edit!
val nx: Any? = '0'

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
class D {

View File

@@ -1,6 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
fun foo() {}
fun box(): String {

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 bar() {}

View File

@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
import org.jetbrains.kotlin.types.KotlinType
var JsName.staticRef: JsNode? by MetadataProperty(default = null)
@@ -56,12 +57,16 @@ var JsParameter.hasDefaultValue: Boolean by MetadataProperty(default = false)
var JsInvocation.typeCheck: TypeCheck? by MetadataProperty(default = null)
var JsInvocation.boxing: Boolean by MetadataProperty(default = false)
var JsInvocation.boxing: BoxingKind by MetadataProperty(default = BoxingKind.NONE)
var JsVars.exportedPackage: String? by MetadataProperty(default = null)
var JsExpressionStatement.exportedTag: String? by MetadataProperty(default = null)
var JsExpression.type: KotlinType? by MetadataProperty(default = null)
var JsExpression.isUnit: Boolean by MetadataProperty(default = false)
/**
* For function and lambda bodies indicates what declaration corresponds to.
* When absent (`null`) on body of a named function, this function is from external JS module.
@@ -142,4 +147,10 @@ enum class SpecialFunction(val suggestedName: String) {
WRAP_FUNCTION("wrapFunction"),
TO_BOXED_CHAR("toBoxedChar"),
UNBOX_CHAR("unboxChar"),
}
enum class BoxingKind {
NONE,
BOXING,
UNBOXING
}

View File

@@ -59,7 +59,24 @@ fun charArray(size: Int, init: dynamic): Array<Char> {
}
@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")
fun longArray(size: Int, init: dynamic): Array<Long> {

View File

@@ -24,4 +24,4 @@ internal external annotation class JsName(val name: String)
internal external annotation class native
internal external fun js(code: String): dynamic
external fun js(code: String): dynamic

View File

@@ -60,12 +60,6 @@ public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest {
doTest(fileName);
}
@TestMetadata("charBoxing.kt")
public void testCharBoxing() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/charBoxing.kt");
doTest(fileName);
}
@TestMetadata("classCapturingLocals.kt")
public void testClassCapturingLocals() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/classCapturingLocals.kt");

View File

@@ -795,6 +795,117 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
}
}
@TestMetadata("js/js.translator/testData/box/coercion")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Coercion extends AbstractBoxJsTest {
public void testAllFilesPresentInCoercion() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/coercion"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("classProperty.kt")
public void testClassProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/classProperty.kt");
doTest(fileName);
}
@TestMetadata("derivedFunctionReturningChar.kt")
public void testDerivedFunctionReturningChar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/derivedFunctionReturningChar.kt");
doTest(fileName);
}
@TestMetadata("derivedFunctionReturningUnit.kt")
public void testDerivedFunctionReturningUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/derivedFunctionReturningUnit.kt");
doTest(fileName);
}
@TestMetadata("destructuringToUnit.kt")
public void testDestructuringToUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/destructuringToUnit.kt");
doTest(fileName);
}
@TestMetadata("extensionReceiver.kt")
public void testExtensionReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/extensionReceiver.kt");
doTest(fileName);
}
@TestMetadata("ifWithUnit.kt")
public void testIfWithUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/ifWithUnit.kt");
doTest(fileName);
}
@TestMetadata("inlineFunReturningUnit.kt")
public void testInlineFunReturningUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/inlineFunReturningUnit.kt");
doTest(fileName);
}
@TestMetadata("lambdaParameters.kt")
public void testLambdaParameters() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/lambdaParameters.kt");
doTest(fileName);
}
@TestMetadata("loopOverUnits.kt")
public void testLoopOverUnits() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/loopOverUnits.kt");
doTest(fileName);
}
@TestMetadata("receiverSmartCast.kt")
public void testReceiverSmartCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/receiverSmartCast.kt");
doTest(fileName);
}
@TestMetadata("safeCallLetReturningUnit.kt")
public void testSafeCallLetReturningUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/safeCallLetReturningUnit.kt");
doTest(fileName);
}
@TestMetadata("topLevelProperty.kt")
public void testTopLevelProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/topLevelProperty.kt");
doTest(fileName);
}
@TestMetadata("tryWithEmptyCatch.kt")
public void testTryWithEmptyCatch() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/tryWithEmptyCatch.kt");
doTest(fileName);
}
@TestMetadata("unitAsExtensionReceiver.kt")
public void testUnitAsExtensionReceiver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/unitAsExtensionReceiver.kt");
doTest(fileName);
}
@TestMetadata("unitIsAs.kt")
public void testUnitIsAs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/unitIsAs.kt");
doTest(fileName);
}
@TestMetadata("unitSafeCall.kt")
public void testUnitSafeCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/unitSafeCall.kt");
doTest(fileName);
}
@TestMetadata("whenWithUnit.kt")
public void testWhenWithUnit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/coercion/whenWithUnit.kt");
doTest(fileName);
}
}
@TestMetadata("js/js.translator/testData/box/crossModuleRef")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -7802,6 +7913,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName);
}
@TestMetadata("charArrayGetSet.kt")
public void testCharArrayGetSet() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/charArrayGetSet.kt");
doTest(fileName);
}
@TestMetadata("hashMapTypeOfElement.kt")
public void testHashMapTypeOfElement() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/standardClasses/hashMapTypeOfElement.kt");

View File

@@ -2966,13 +2966,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("lambdaToUnitCast.kt")
public void testLambdaToUnitCast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/lambdaToUnitCast.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("notIs.kt")
@@ -2990,13 +2984,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("unitAsAny.kt")
public void testUnitAsAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsAny.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("unitAsInt.kt")
@@ -3014,13 +3002,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("unitAsSafeAny.kt")
public void testUnitAsSafeAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/casts/unitAsSafeAny.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("unitNullableCast.kt")
@@ -6694,13 +6676,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("suspendNonLocalReturn.kt")
public void testSuspendNonLocalReturn() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn/suspendNonLocalReturn.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("suspendReturn.kt")
@@ -14247,13 +14223,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("primitiveEqObjectChar.kt")
public void testPrimitiveEqObjectChar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/primitiveTypes/equalityWithObject/generated/primitiveEqObjectChar.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("primitiveEqObjectInt.kt")
@@ -14792,13 +14762,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("kt4383.kt")
public void testKt4383() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/kt4383.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("kt613.kt")
@@ -23603,13 +23567,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("kt4212.kt")
public void testKt4212() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/kt4212.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
@TestMetadata("kt4265.kt")
@@ -23651,13 +23609,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
@TestMetadata("UnitValue.kt")
public void testUnitValue() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/unit/UnitValue.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
doTest(fileName);
}
}

View File

@@ -17,10 +17,14 @@
package org.jetbrains.kotlin.js.translate.callTranslator
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsBlock
import org.jetbrains.kotlin.js.backend.ast.JsConditional
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral
import org.jetbrains.kotlin.js.backend.ast.metadata.type
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
@@ -31,6 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.typeUtil.makeNullable
interface CallInfo {
val context: TranslationContext
@@ -99,10 +104,6 @@ fun TranslationContext.getCallInfo(
return FunctionCallInfo(callInfo, argumentsInfo)
}
private fun TranslationContext.boxIfNeedeed(v: ReceiverValue?, d: ReceiverParameterDescriptor?, r: JsExpression?): JsExpression? {
return r?.let { receiver -> TranslationUtils.boxCastIfNeeded(this, receiver, v?.type, d?.type) }
}
private fun TranslationContext.getDispatchReceiver(receiverValue: ReceiverValue): JsExpression {
return getDispatchReceiver(getReceiverParameterForReceiver(receiverValue))
}
@@ -134,6 +135,10 @@ private fun TranslationContext.createCallInfo(
}
var dispatchReceiver = getDispatchReceiver()
var dispatchReceiverType = resolvedCall.smartCastDispatchReceiverType ?: resolvedCall.dispatchReceiver?.type
if (dispatchReceiverType != null && (resolvedCall.resultingDescriptor as? FunctionDescriptor)?.kind?.isReal == false) {
dispatchReceiverType = TranslationUtils.getDispatchReceiverTypeForCoercion(resolvedCall.resultingDescriptor)
}
var extensionReceiver = getExtensionReceiver()
var notNullConditional: JsConditional? = null
@@ -154,16 +159,19 @@ private fun TranslationContext.createCallInfo(
val container = resolvedCall.resultingDescriptor.containingDeclaration
if (DescriptorUtils.isObject(container)) {
dispatchReceiver = ReferenceTranslator.translateAsValueReference(container, this)
dispatchReceiverType = (container as ClassDescriptor).defaultType
}
}
dispatchReceiver = boxIfNeedeed(resolvedCall.dispatchReceiver,
resolvedCall.candidateDescriptor.dispatchReceiverParameter,
dispatchReceiver)
if (dispatchReceiverType != null) {
dispatchReceiver = dispatchReceiver?.let {
TranslationUtils.coerce(this, it, dispatchReceiverType!!)
}
}
extensionReceiver = boxIfNeedeed(resolvedCall.extensionReceiver,
resolvedCall.candidateDescriptor.extensionReceiverParameter,
extensionReceiver)
extensionReceiver = extensionReceiver?.let {
TranslationUtils.coerce(this, it, resolvedCall.candidateDescriptor.extensionReceiverParameter!!.type)
}
return object : AbstractCallInfo(), CallInfo {
@@ -179,7 +187,9 @@ private fun TranslationContext.createCallInfo(
result
}
else {
notNullConditionalForSafeCall.thenExpression = result
val type = resolvedCall.getReturnType()
result.type = type
notNullConditionalForSafeCall.thenExpression = TranslationUtils.coerce(context, result, type.makeNullable())
notNullConditionalForSafeCall
}
}

View File

@@ -31,11 +31,14 @@ import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.psi.Call.CallType
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariable
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.makeNullable
object CallTranslator {
@JvmOverloads
@@ -52,7 +55,9 @@ object CallTranslator {
extensionOrDispatchReceiver: JsExpression? = null
): JsExpression {
val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), null)
return variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement)
val result = variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement)
result.type = TranslationUtils.getReturnTypeForCoercion(resolvedCall.resultingDescriptor.original)
return result
}
fun translateSet(context: TranslationContext,
@@ -61,7 +66,9 @@ object CallTranslator {
extensionOrDispatchReceiver: JsExpression? = null
): JsExpression {
val variableAccessInfo = VariableAccessInfo(context.getCallInfo(resolvedCall, extensionOrDispatchReceiver), value)
return variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement)
val result = variableAccessInfo.translateVariableAccess().source(resolvedCall.call.callElement)
result.type = context.currentModule.builtIns.unitType
return result
}
fun buildCall(context: TranslationContext,
@@ -95,7 +102,7 @@ private fun translateCall(
assert(explicitReceivers.extensionReceiver == null) { "VariableAsFunctionResolvedCall must have one receiver" }
val variableCall = resolvedCall.variableCall
return if (variableCall.expectedReceivers()) {
val result = if (variableCall.expectedReceivers()) {
val newReceiver = CallTranslator.translateGet(context, variableCall, explicitReceivers.extensionOrDispatchReceiver)
translateFunctionCall(context, resolvedCall.functionCall, resolvedCall.variableCall, ExplicitReceivers(newReceiver))
} else {
@@ -110,6 +117,8 @@ private fun translateCall(
ExplicitReceivers(dispatchReceiver, explicitReceivers.extensionOrDispatchReceiver))
}
}
return result
}
val call = resolvedCall.call
@@ -151,9 +160,13 @@ private fun translateFunctionCall(
callExpression.isTailCallSuspend = true
}
}
callExpression.type = resolvedCall.getReturnType().let { if (resolvedCall.call.isSafeCall()) it.makeNullable() else it }
return callExpression
}
fun ResolvedCall<out CallableDescriptor>.getReturnType(): KotlinType = TranslationUtils.getReturnTypeForCoercion(resultingDescriptor)
private val TranslationContext.isInStateMachine
get() = (declarationDescriptor as? FunctionDescriptor)?.requiresStateMachineTransformation(this) == true

View File

@@ -24,7 +24,7 @@ public final class TemporaryConstVariable extends TemporaryVariable{
private boolean initialized = false;
public TemporaryConstVariable(@NotNull JsName variableName, @NotNull JsExpression assignmentExpression) {
super(variableName, assignmentExpression);
super(variableName, assignmentExpression, null);
}
@NotNull

View File

@@ -21,33 +21,40 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.types.KotlinType;
public class TemporaryVariable {
/*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) {
JsBinaryOperation rhs = null;
KotlinType type = null;
if (initExpression != null) {
rhs = JsAstUtils.assignment(temporaryName.makeRef(), initExpression);
rhs.source(initExpression.getSource());
MetadataProperties.setSynthetic(rhs, true);
type = MetadataProperties.getType(initExpression);
}
return new TemporaryVariable(temporaryName, rhs);
return new TemporaryVariable(temporaryName, rhs, type);
}
@Nullable
private final JsExpression assignmentExpression;
@NotNull
private final JsName variableName;
@Nullable
private final KotlinType type;
protected TemporaryVariable(@NotNull JsName temporaryName, @Nullable JsExpression assignmentExpression) {
protected TemporaryVariable(@NotNull JsName temporaryName, @Nullable JsExpression assignmentExpression, @Nullable KotlinType type) {
this.variableName = temporaryName;
this.assignmentExpression = assignmentExpression;
this.type = type;
}
@NotNull
public JsNameRef reference() {
JsNameRef result = variableName.makeRef();
MetadataProperties.setSynthetic(result, true);
MetadataProperties.setType(result, type);
return result;
}

View File

@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.*
import org.jetbrains.kotlin.js.translate.utils.finalElement
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.addParameter
@@ -172,7 +173,7 @@ fun TranslationContext.translateDelegateOrInitializerExpression(expression: KtPr
CallTranslator.translate(innerContext, provideDelegateCall, initializer)
}
else {
initializer
TranslationUtils.coerce(this, initializer, propertyDescriptor.type)
}
}

View File

@@ -84,8 +84,7 @@ public class DestructuringDeclarationTranslator extends AbstractTranslator {
setInlineCallMetadata(entryInitializer, entry, entryInitCall, context());
}
entryInitializer = TranslationUtils.boxCastIfNeeded(context(), entryInitializer, candidateDescriptor.getReturnType(),
descriptor.getType());
entryInitializer = TranslationUtils.coerce(context(), entryInitializer, descriptor.getType());
JsName name = context().getNameForDescriptor(descriptor);
if (isVarCapturedInClosure(context().bindingContext(), descriptor)) {

View File

@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
@@ -39,6 +38,8 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator;
import org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
@@ -107,6 +108,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
jsBlock.getStatements().add(jsStatement);
}
}
if (statements.isEmpty()) {
ClassDescriptor unitClass = context.getCurrentModule().getBuiltIns().getUnit();
jsBlock.getStatements().add(JsAstUtils.asSyntheticStatement(
ReferenceTranslator.translateAsValueReference(unitClass, context)));
}
return jsBlock;
}
@@ -135,6 +141,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return new JsReturn(ref.source(jetReturnExpression));
}
FunctionDescriptor returnTarget = getNonLocalReturnTarget(jetReturnExpression, context);
JsReturn jsReturn;
if (returned == null) {
jsReturn = new JsReturn(null);
@@ -146,15 +154,19 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
assert returnedType != null : "Resolved return expression is expected to have type: " +
PsiUtilsKt.getTextWithLocation(jetReturnExpression);
if (KotlinBuiltIns.isCharOrNullableChar(returnedType) &&
TranslationUtils.shouldBoxReturnValue((CallableDescriptor)context.getDeclarationDescriptor())) {
jsReturnExpression = TranslationUtils.charToBoxedChar(context, jsReturnExpression);
CallableDescriptor returnTargetOrCurrentFunction = returnTarget;
if (returnTargetOrCurrentFunction == null) {
returnTargetOrCurrentFunction = (CallableDescriptor) context.getDeclarationDescriptor();
}
if (returnTargetOrCurrentFunction != null) {
jsReturnExpression = TranslationUtils.coerce(context, jsReturnExpression,
TranslationUtils.getReturnTypeForCoercion(returnTargetOrCurrentFunction));
}
jsReturn = new JsReturn(jsReturnExpression);
}
MetadataProperties.setReturnTarget(jsReturn, getNonLocalReturnTarget(jetReturnExpression, context));
MetadataProperties.setReturnTarget(jsReturn, returnTarget);
return jsReturn.source(jetReturnExpression);
}
@@ -245,10 +257,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObjectQualifier()) {
JsExpression receiver = translateAsExpression(receiverExpression, context);
KotlinType type = context.bindingContext().getType(receiverExpression);
if (type != null && KotlinBuiltIns.isChar(type)) {
receiver = TranslationUtils.charToBoxedChar(context, receiver);
}
receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getAnyType());
return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver);
}
@@ -270,6 +279,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) {
assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
KotlinType type = context.bindingContext().getType(expression);
boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext());
@@ -281,6 +291,15 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
JsStatement elseStatement =
elseExpression != null ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression, context) : null;
if (type != null) {
if (thenStatement != null) {
thenStatement = LastExpressionMutator.mutateLastExpression(thenStatement, new CoercionMutator(type, context));
}
if (elseStatement != null) {
elseStatement = LastExpressionMutator.mutateLastExpression(elseStatement, new CoercionMutator(type, context));
}
}
if (isKotlinExpression) {
JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement);
JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement);

View File

@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.functionDescriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.hasDefaultValue
import org.jetbrains.kotlin.js.backend.ast.metadata.type
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.descriptorUtils.shouldBeExported
import org.jetbrains.kotlin.js.inline.util.FunctionWithWrapper
@@ -58,7 +59,9 @@ fun TranslationContext.translateAndAliasParameters(
if (descriptor.requiresExtensionReceiverParameter) {
val receiverParameterName = JsScope.declareTemporaryName(Namer.getReceiverParameterName())
aliases[descriptor.extensionReceiverParameter!!] = receiverParameterName.makeRef()
val receiverRef = receiverParameterName.makeRef()
receiverRef.type = descriptor.extensionReceiverParameter!!.type
aliases[descriptor.extensionReceiverParameter!!] = receiverRef
targetList += JsParameter(receiverParameterName)
}

View File

@@ -19,6 +19,7 @@
package org.jetbrains.kotlin.js.translate.expression
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils.*
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -142,7 +144,10 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val currentVarInit =
if (destructuringParameter == null) {
newVar(parameterName, itemValue).apply { source = expression.loopRange }
val loopParameterDescriptor = (getDescriptorForElement(context.bindingContext(), loopParameter) as CallableDescriptor)
val loopParameterType = loopParameterDescriptor.returnType ?: context.currentModule.builtIns.anyType
val coercedItemValue = itemValue?.let { TranslationUtils.coerce(context, it, loopParameterType) }
newVar(parameterName, coercedItemValue).apply { source = expression.loopRange }
}
else {
val innerBlockContext = context.innerBlock(block)
@@ -224,7 +229,7 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
fun translateForOverArray(): JsStatement {
val rangeExpression = context.defineTemporary(Translation.translateAsExpression(loopRange, context))
val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf<JsExpression>(), context)
val length = ArrayFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression, listOf(), context)
val end = context.defineTemporary(length)
val index = context.declareTemporary(JsIntLiteral(0), expression)

View File

@@ -81,10 +81,8 @@ public final class PatternTranslator extends AbstractTranslator {
KtTypeReference typeReference = expression.getRight();
assert typeReference != null: "Cast expression must have type reference";
KotlinType leftType = context().bindingContext().getType(left);
if (leftType != null && KotlinBuiltIns.isChar(leftType)) {
expressionToCast = TranslationUtils.charToBoxedChar(context(), expressionToCast);
}
KotlinType anyType = context().getCurrentModule().getBuiltIns().getAnyType();
expressionToCast = TranslationUtils.coerce(context(), expressionToCast, anyType);
TemporaryVariable temporary = context().declareTemporary(expressionToCast, expression);
JsExpression isCheck = translateIsCheck(temporary.assignmentExpression(), typeReference);
@@ -102,12 +100,11 @@ public final class PatternTranslator extends AbstractTranslator {
JsExpression result = new JsConditional(isCheck, temporary.reference(), onFail);
KotlinType expressionType = context().bindingContext().getType(expression);
if (expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType)) {
result = TranslationUtils.boxedCharToChar(context(), result);
KotlinType targetType = getTypeByReference(bindingContext(), typeReference);
if (isSafeCast(expression)) {
targetType = targetType.unwrap().makeNullableAsSpecified(true);
}
return result;
return TranslationUtils.coerce(context(), result, targetType);
}
@NotNull
@@ -115,10 +112,8 @@ public final class PatternTranslator extends AbstractTranslator {
KtExpression left = expression.getLeftHandSide();
JsExpression expressionToCheck = Translation.translateAsExpression(left, context());
KotlinType leftType = context().bindingContext().getType(left);
if (leftType != null && KotlinBuiltIns.isChar(leftType)) {
expressionToCheck = TranslationUtils.charToBoxedChar(context(), expressionToCheck);
}
KotlinType anyType = context().getCurrentModule().getBuiltIns().getAnyType();
expressionToCheck = TranslationUtils.coerce(context(), expressionToCheck, anyType);
KtTypeReference typeReference = expression.getTypeReference();
assert typeReference != null;

View File

@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.operation.InOperationTranslator;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.mutator.CoercionMutator;
import org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
@@ -50,6 +52,9 @@ public final class WhenTranslator extends AbstractTranslator {
@Nullable
private final JsExpression expressionToMatch;
@Nullable
private final KotlinType type;
private WhenTranslator(@NotNull KtWhenExpression expression, @NotNull TranslationContext context) {
super(context);
@@ -57,6 +62,8 @@ public final class WhenTranslator extends AbstractTranslator {
KtExpression subject = expression.getSubjectExpression();
expressionToMatch = subject != null ? context.defineTemporary(Translation.translateAsExpression(subject, context)) : null;
type = context().bindingContext().getType(expression);
}
private JsNode translate() {
@@ -106,13 +113,18 @@ public final class WhenTranslator extends AbstractTranslator {
}
@NotNull
private static JsStatement translateEntryExpression(
private JsStatement translateEntryExpression(
@NotNull KtWhenEntry entry,
@NotNull TranslationContext context,
@NotNull JsBlock block) {
KtExpression expressionToExecute = entry.getExpression();
assert expressionToExecute != null : "WhenEntry should have whenExpression to execute.";
return Translation.translateAsStatement(expressionToExecute, context, block);
JsStatement result = Translation.translateAsStatement(expressionToExecute, context, block);
if (type != null) {
return LastExpressionMutator.mutateLastExpression(result, new CoercionMutator(type, context));
}
return result;
}
@NotNull

View File

@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.idea.MainFunctionDetector;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.facade.MainCallParameters;
import org.jetbrains.kotlin.js.facade.TranslationUnit;
@@ -123,6 +124,15 @@ public final class Translation {
) {
KotlinType expectedType = context.bindingContext().getType(expression);
ConstantValue<?> constant = compileTimeValue.toConstantValue(expectedType != null ? expectedType : TypeUtils.NO_EXPECTED_TYPE);
JsExpression result = translateConstantWithoutType(constant);
if (result != null) {
MetadataProperties.setType(result, expectedType);
}
return result;
}
@Nullable
private static JsExpression translateConstantWithoutType(@NotNull ConstantValue<?> constant) {
if (constant instanceof NullValue) {
return new JsNullLiteral();
}
@@ -187,10 +197,17 @@ public final class Translation {
@NotNull JsBlock block
) {
JsNode jsNode = translateExpression(expression, context, block);
if (jsNode instanceof JsExpression) {
KotlinType expressionType = context.bindingContext().getType(expression);
return unboxIfNeeded(context, (JsExpression) jsNode,
expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType));
JsExpression jsExpression = (JsExpression) jsNode;
KotlinType type = context.bindingContext().getType(expression);
if (MetadataProperties.getType(jsExpression) == null) {
MetadataProperties.setType(jsExpression, type);
}
else if (type != null) {
jsExpression = TranslationUtils.coerce(context, jsExpression, type);
}
return jsExpression;
}
assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
@@ -198,27 +215,15 @@ public final class Translation {
TemporaryVariable result = context.declareTemporary(null, expression);
AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
return result.reference();
JsExpression tmpVar = result.reference();
MetadataProperties.setType(tmpVar, context.bindingContext().getType(expression));
return tmpVar;
}
block.getStatements().add(convertToStatement(jsNode));
return new JsNullLiteral().source(expression);
}
@NotNull
public static JsExpression unboxIfNeeded(
@NotNull TranslationContext context,
@NotNull JsExpression expression,
boolean charOrNullableChar
) {
if (charOrNullableChar &&
(expression instanceof JsInvocation || expression instanceof JsNameRef || expression instanceof JsArrayAccess)
) {
expression = TranslationUtils.boxedCharToChar(context, expression);
}
return expression;
}
@NotNull
public static JsStatement translateAsStatement(@NotNull KtExpression expression, @NotNull TranslationContext context) {
return translateAsStatement(expression, context, context.dynamicContext().jsBlock());

View File

@@ -50,6 +50,7 @@ public final class FunctionIntrinsics {
register(NumberAndCharConversionFIF.INSTANCE);
register(ThrowableConstructorIntrinsicFactory.INSTANCE);
register(ExceptionPropertyIntrinsicFactory.INSTANCE);
register(AsDynamicFIF.INSTANCE);
}
private void register(@NotNull FunctionIntrinsicFactory instance) {

View File

@@ -43,7 +43,7 @@ import java.util.*
object ArrayFIF : CompositeFIF() {
@JvmField
val GET_INTRINSIC = intrinsify { callInfo, arguments, _ ->
val GET_INTRINSIC = intrinsify { callInfo, arguments, context ->
assert(arguments.size == 1) { "Array get expression must have one argument." }
val (indexExpression) = arguments
JsArrayAccess(callInfo.dispatchReceiver, indexExpression)
@@ -108,7 +108,12 @@ object ArrayFIF : CompositeFIF() {
}
}
else {
"kotlin.newArrayF"
if (primitiveType == CHAR) {
"kotlin.untypedCharArrayF"
}
else {
"kotlin.newArrayF"
}
}
}
@@ -183,7 +188,7 @@ object ArrayFIF : CompositeFIF() {
}
}
else {
JsAstUtils.invokeKotlinFunction("newArrayF", size, fn)
JsAstUtils.invokeKotlinFunction(if (type == CHAR) "untypedCharArrayF" else "newArrayF", size, fn)
}
invocation.inlineStrategy = InlineStrategy.IN_PLACE
val descriptor = callInfo.resolvedCall.resultingDescriptor.original

View File

@@ -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)
})
}
}

View File

@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.resolve.DescriptorFactory;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
@@ -166,13 +167,14 @@ public final class TopLevelFIF extends CompositeFIF {
public static final KotlinFunctionIntrinsic TO_STRING = new KotlinFunctionIntrinsic("toString");
@NotNull
public static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() {
private static final FunctionIntrinsic CHAR_TO_STRING = new FunctionIntrinsicWithReceiverComputed() {
@NotNull
@Override
public JsExpression apply(
@Nullable JsExpression receiver, @NotNull List<? extends JsExpression> arguments, @NotNull TranslationContext context
) {
assert receiver != null;
receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getCharType());
return JsAstUtils.charToString(receiver);
}
};

View File

@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator
import org.jetbrains.kotlin.js.backend.ast.JsExpression
import org.jetbrains.kotlin.js.backend.ast.JsNullLiteral
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
@@ -57,13 +56,15 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
val ktLeft = checkNotNull(expression.left) { "No left-hand side: " + expression.text }
val ktRight = checkNotNull(expression.right) { "No right-hand side: " + expression.text }
val leftType = getRefinedType(ktLeft, context)?.let { KotlinBuiltIns.getPrimitiveType(it) }
val rightType = getRefinedType(ktRight, context)?.let { KotlinBuiltIns.getPrimitiveType(it) }
val leftKotlinType = getRefinedType(ktLeft, context)
val rightKotlinType = getRefinedType(ktRight, context)
val leftType = leftKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
val rightType = rightKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
if (leftType != null && (leftType in SIMPLE_PRIMITIVES || leftType == rightType && leftType != PrimitiveType.LONG)) {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ,
Translation.unboxIfNeeded(context, left, leftType == PrimitiveType.CHAR),
Translation.unboxIfNeeded(context, right, rightType == PrimitiveType.CHAR))
val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType)
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType!!)
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, coercedLeft, coercedRight)
}
val resolvedCall = expression.getResolvedCall(context.bindingContext())
@@ -77,10 +78,10 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
}
val maybeBoxedLeft = if (leftType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, left) else left
val maybeBoxedRight = if (rightType == PrimitiveType.CHAR) TranslationUtils.charToBoxedChar(context, right) else right
val result = TopLevelFIF.KOTLIN_EQUALS.apply(maybeBoxedLeft, Arrays.asList<JsExpression>(maybeBoxedRight), context)
val anyType = context.currentModule.builtIns.anyType
val coercedLeft = TranslationUtils.coerce(context, left, anyType)
val coercedRight = TranslationUtils.coerce(context, right, anyType)
val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context)
return if (isNegated) JsAstUtils.not(result) else result
}

View File

@@ -126,15 +126,14 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression translateElvis() {
KotlinType expressionType = context().bindingContext().getType(expression);
assert expressionType != null;
JsExpression leftExpression = TranslationUtils.boxCastIfNeeded(
context(), Translation.translateAsExpression(leftKtExpression, context()),
context().bindingContext().getType(leftKtExpression), expressionType);
JsExpression leftExpression = TranslationUtils.coerce(
context(), Translation.translateAsExpression(leftKtExpression, context()), expressionType);
JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = TranslationUtils.boxCastIfNeeded(
context(),Translation.translateAsExpression(rightKtExpression, context(), rightBlock),
context().bindingContext().getType(rightKtExpression), expressionType);
JsExpression rightExpression = TranslationUtils.coerce(
context(), Translation.translateAsExpression(rightKtExpression, context(), rightBlock), expressionType);
if (rightBlock.isEmpty()) {
return TranslationUtils.notNullConditional(leftExpression, rightExpression, context());

View File

@@ -66,7 +66,7 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator {
if (leftType != null && KotlinBuiltIns.isStringOrNullableString(leftType)) {
result = JsAstUtils.charToString(result);
}
else if (leftType == null || !KotlinBuiltIns.isCharOrNullableChar(leftType)) {
else if (leftType != null && !KotlinBuiltIns.isCharOrNullableChar(leftType)) {
result = TranslationUtils.charToBoxedChar(context, result);
}
}

View File

@@ -17,9 +17,10 @@
package org.jetbrains.kotlin.js.translate.reference
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineOrReturn
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
@@ -36,9 +37,9 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
@@ -191,18 +192,15 @@ class CallArgumentTranslator private constructor(
val parenthisedArgumentExpression = arg.getArgumentExpression()
val param = argsToParameters[arg]!!.original
val parameterType = if (resolvedCall.call.callType == Call.CallType.INVOKE) {
DefaultBuiltIns.Instance.anyType
}
else {
param.varargElementType ?: param.type
val isLambda = resolvedCall.resultingDescriptor.let { it.getFunctionalClassKind() != null || it is FunctionInvokeDescriptor }
val parameterType = if (!isLambda) param.varargElementType ?: param.type else context.currentModule.builtIns.anyType
var argJs = Translation.translateAsExpression(parenthisedArgumentExpression!!, argumentContext)
if (!param.isVararg || arg.getSpreadElement() == null) {
argJs = TranslationUtils.coerce(context, argJs, parameterType)
}
val argType = context.bindingContext().getType(parenthisedArgumentExpression!!)
val argJs = Translation.translateAsExpression(parenthisedArgumentExpression, argumentContext)
arg to TranslationUtils.boxCastIfNeeded(context, argJs, argType, parameterType)
arg to argJs
}
val resolvedOrder = resolvedCall.valueArgumentsByIndex.orEmpty()

View File

@@ -22,11 +22,13 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind
import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference
import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects
import org.jetbrains.kotlin.js.backend.ast.metadata.type
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.finalElement
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression
@@ -107,15 +109,19 @@ object CallableReferenceTranslator {
null
}
val functionDescriptor = realResolvedCall.resultingDescriptor
val aliases = mutableMapOf<KtExpression, JsExpression>()
for ((index, valueArg) in fakeCall.valueArguments.withIndex()) {
val paramName = JsScope.declareTemporaryName(descriptor.valueParameters[index].name.asString())
function.parameters += JsParameter(paramName)
aliases[valueArg.getArgumentExpression()!!] = paramName.makeRef()
val paramRef = paramName.makeRef()
paramRef.type = context.currentModule.builtIns.anyType
val type = functionDescriptor.valueParameters[index].type
aliases[valueArg.getArgumentExpression()!!] = TranslationUtils.coerce(context, paramRef, type)
}
val functionContext = context.innerBlock(function.body).innerContextWithAliasesForExpressions(aliases)
val invocation = CallTranslator.translate(functionContext, fakeResolvedCall, receiverParam)
function.body.statements += JsReturn(invocation)
function.body.statements += JsReturn(TranslationUtils.coerce(context, invocation, context.currentModule.builtIns.anyType))
val rawCallableRef = bindIfNecessary(function, receiver)
return wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef)

View File

@@ -17,7 +17,10 @@
package org.jetbrains.kotlin.js.translate.reference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.js.backend.ast.JsExpression;
import org.jetbrains.kotlin.js.backend.ast.JsInvocation;
import org.jetbrains.kotlin.js.backend.ast.JsName;
@@ -32,6 +35,7 @@ import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtQualifiedExpression;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.types.KotlinType;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelectorAsSimpleName;
@@ -49,6 +53,41 @@ public final class ReferenceTranslator {
@NotNull
public static JsExpression translateAsValueReference(@NotNull DeclarationDescriptor descriptor, @NotNull TranslationContext context) {
JsExpression result = translateAsValueReferenceWithoutType(descriptor, context);
MetadataProperties.setType(result, getType(descriptor));
if (descriptor instanceof ClassDescriptor) {
if (KotlinBuiltIns.isUnit(((ClassDescriptor) descriptor).getDefaultType())) {
MetadataProperties.setUnit(result, true);
MetadataProperties.setSideEffects(result, SideEffectKind.PURE);
MetadataProperties.setSynthetic(result, true);
}
}
return result;
}
@Nullable
private static KotlinType getType(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ClassDescriptor) {
return ((ClassDescriptor) descriptor).getDefaultType();
}
else if (descriptor instanceof CallableDescriptor) {
if (descriptor instanceof ValueParameterDescriptor) {
ValueParameterDescriptor parameter = (ValueParameterDescriptor) descriptor;
if (parameter.getContainingDeclaration() instanceof AnonymousFunctionDescriptor) {
return DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
}
}
return ((CallableDescriptor) descriptor).getReturnType();
}
return null;
}
@NotNull
private static JsExpression translateAsValueReferenceWithoutType(
@NotNull DeclarationDescriptor descriptor,
@NotNull TranslationContext context
) {
if (AnnotationsUtils.isNativeObject(descriptor) || AnnotationsUtils.isLibraryObject(descriptor)) {
return context.getInnerReference(descriptor);
}

View File

@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
@@ -31,6 +32,7 @@ import org.jetbrains.kotlin.js.translate.expression.LocalFunctionCollector;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
import org.jetbrains.kotlin.psi.KtBlockExpression;
import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
@@ -120,6 +122,16 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements());
if (jetBodyExpression instanceof KtBlockExpression &&
descriptor.getReturnType() != null && KotlinBuiltIns.isUnit(descriptor.getReturnType()) &&
!KotlinBuiltIns.isUnit(TranslationUtils.getReturnTypeForCoercion(descriptor))) {
ClassDescriptor unit = context().getCurrentModule().getBuiltIns().getUnit();
JsReturn jsReturn = new JsReturn(ReferenceTranslator.translateAsValueReference(unit, context()));
jsReturn.setSource(UtilsKt.getFinalElement(declaration));
jsBlock.getStatements().add(jsReturn);
}
return jsBlock;
}
@@ -145,12 +157,8 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
}
assert declaration.getBodyExpression() != null;
assert descriptor.getReturnType() != null;
KotlinType bodyType = context().bindingContext().getType(declaration.getBodyExpression());
if (bodyType == null && KotlinBuiltIns.isCharOrNullableChar(descriptor.getReturnType()) ||
bodyType != null && KotlinBuiltIns.isCharOrNullableChar(bodyType) && TranslationUtils.shouldBoxReturnValue(descriptor)) {
node = TranslationUtils.charToBoxedChar(context(), (JsExpression) node);
}
KotlinType returnType = TranslationUtils.getReturnTypeForCoercion(descriptor);
node = TranslationUtils.coerce(context(), (JsExpression) node, returnType);
JsReturn jsReturn = new JsReturn((JsExpression) node);
jsReturn.setSource(declaration.getBodyExpression());

View File

@@ -19,12 +19,15 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.BoxingKind;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction;
import org.jetbrains.kotlin.js.translate.context.Namer;
@@ -43,11 +46,11 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
import org.jetbrains.kotlin.types.DynamicTypesKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
@@ -186,7 +189,11 @@ public final class TranslationUtils {
else {
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
}
return new JsNameRef(backingFieldName, receiver);
JsNameRef result = new JsNameRef(backingFieldName, receiver);
MetadataProperties.setType(result, getReturnTypeForCoercion(descriptor));
return result;
}
@NotNull
@@ -205,10 +212,9 @@ public final class TranslationUtils {
if (initializer != null) {
jsInitExpression = Translation.translateAsExpression(initializer, context);
KotlinType propertyType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, declaration).getType();
KotlinType initType = context.bindingContext().getType(initializer);
jsInitExpression = boxCastIfNeeded(context, jsInitExpression, initType, propertyType);
KotlinType propertyType = BindingContextUtils.getNotNull(
context.bindingContext(), BindingContext.VARIABLE, declaration).getType();
jsInitExpression = coerce(context, jsInitExpression, propertyType);
}
return jsInitExpression;
}
@@ -394,61 +400,139 @@ public final class TranslationUtils {
ModalityKt.isOverridable(descriptor);
}
private static boolean overridesReturnAny(CallableDescriptor c) {
KotlinType returnType = c.getOriginal().getReturnType();
assert returnType != null;
if (KotlinBuiltIns.isAnyOrNullableAny(returnType) || TypeUtils.isTypeParameter(returnType)) return true;
for (CallableDescriptor o : c.getOverriddenDescriptors()) {
if (overridesReturnAny(o)) return true;
@NotNull
public static KotlinType getReturnTypeForCoercion(@NotNull CallableDescriptor descriptor) {
descriptor = descriptor.getOriginal();
if (FunctionTypesKt.getFunctionalClassKind(descriptor) != null || descriptor instanceof AnonymousFunctionDescriptor) {
return DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
}
return false;
}
public static boolean shouldBoxReturnValue(CallableDescriptor c) {
return overridesReturnAny(c) || c instanceof CallableMemberDescriptor && ModalityKt.isOverridable((CallableMemberDescriptor)c);
}
@NotNull
public static JsExpression boxCastIfNeeded(
@NotNull TranslationContext context,
@NotNull JsExpression e,
@Nullable KotlinType castFrom, @Nullable KotlinType castTo
) {
if (castFrom != null && KotlinBuiltIns.isCharOrNullableChar(castFrom) &&
castTo != null && !KotlinBuiltIns.isCharOrNullableChar(castTo)
) {
return charToBoxedChar(context, e);
Collection<? extends CallableDescriptor> overridden = descriptor.getOverriddenDescriptors();
if (overridden.isEmpty()) {
return descriptor.getReturnType() != null ?
descriptor.getReturnType() :
DescriptorUtils.getContainingModule(descriptor).getBuiltIns().getAnyType();
}
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
public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.TO_BOXED_CHAR, unnestBoxing(expression));
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation);
public static KotlinType getDispatchReceiverTypeForCoercion(@NotNull CallableDescriptor descriptor) {
descriptor = descriptor.getOriginal();
if (descriptor.getDispatchReceiverParameter() == null) {
throw new IllegalArgumentException("This method can only be used for class members; " +
"given descriptor is not a member of a class " + descriptor);
}
Collection<? 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
public static JsExpression boxedCharToChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
JsInvocation invocation = invokeSpecialFunction(context, SpecialFunction.UNBOX_CHAR, unnestBoxing(expression));
invocation.setSource(expression.getSource());
return withBoxingMetadata(invocation);
public static JsExpression coerce(@NotNull TranslationContext context, @NotNull JsExpression value, @NotNull KotlinType to) {
if (DynamicTypesKt.isDynamic(to)) return value;
KotlinType from = MetadataProperties.getType(value);
if (from == null) {
from = context.getCurrentModule().getBuiltIns().getAnyType();
}
if (from.equals(to)) return value;
if (KotlinBuiltIns.isCharOrNullableChar(to)) {
if (!KotlinBuiltIns.isCharOrNullableChar(from) && !(value instanceof JsNullLiteral)) {
value = boxedCharToChar(context, value);
}
}
else if (KotlinBuiltIns.isUnit(to)) {
if (!KotlinBuiltIns.isUnit(from)) {
value = unitToVoid(value);
}
}
else if (KotlinBuiltIns.isCharOrNullableChar(from)) {
if (!KotlinBuiltIns.isCharOrNullableChar(to) && !(value instanceof JsNullLiteral)) {
value = charToBoxedChar(context, value);
}
}
else if (KotlinBuiltIns.isUnit(from)) {
if (!KotlinBuiltIns.isUnit(to) && !MetadataProperties.isUnit(value)) {
ClassDescriptor unit = context.getCurrentModule().getBuiltIns().getUnit();
JsExpression unitRef = ReferenceTranslator.translateAsValueReference(unit, context);
value = JsAstUtils.newSequence(Arrays.asList(value, unitRef));
}
}
MetadataProperties.setType(value, to);
return value;
}
@NotNull
private static JsExpression unnestBoxing(@NotNull JsExpression expression) {
if (expression instanceof JsInvocation && MetadataProperties.getBoxing((JsInvocation) expression)) {
return ((JsInvocation) expression).getArguments().get(0);
private static JsExpression unitToVoid(@NotNull JsExpression expression) {
if (expression instanceof JsBinaryOperation) {
JsBinaryOperation binary = (JsBinaryOperation) expression;
if (binary.getOperator() == JsBinaryOperator.COMMA && MetadataProperties.isUnit(binary.getArg2())) {
return binary.getArg1();
}
}
return expression;
}
@NotNull
private static JsInvocation withBoxingMetadata(@NotNull JsInvocation call) {
MetadataProperties.setBoxing(call, true);
return call;
public static JsExpression charToBoxedChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
if (expression instanceof JsInvocation) {
JsInvocation invocation = (JsInvocation) expression;
BoxingKind existingKind = MetadataProperties.getBoxing(invocation);
switch (existingKind) {
case UNBOXING:
return invocation.getArguments().get(0);
case BOXING:
return expression;
case NONE:
break;
}
}
JsInvocation result = invokeSpecialFunction(context, SpecialFunction.TO_BOXED_CHAR, expression);
result.setSource(expression.getSource());
MetadataProperties.setBoxing(result, BoxingKind.BOXING);
return result;
}
@NotNull
private static JsExpression boxedCharToChar(@NotNull TranslationContext context, @NotNull JsExpression expression) {
if (expression instanceof JsInvocation) {
JsInvocation invocation = (JsInvocation) expression;
BoxingKind existingKind = MetadataProperties.getBoxing(invocation);
switch (existingKind) {
case BOXING:
return invocation.getArguments().get(0);
case UNBOXING:
return expression;
case NONE:
break;
}
}
JsInvocation result = invokeSpecialFunction(context, SpecialFunction.UNBOX_CHAR, expression);
result.setSource(expression.getSource());
MetadataProperties.setBoxing(result, BoxingKind.UNBOXING);
return result;
}
@NotNull

View File

@@ -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;
}
}

View File

@@ -9,5 +9,15 @@ fun box(): String {
assertEquals(false, 'A'== 'B')
assertEquals(false, ('A' as Any) == (65 as Any))
assertTrue(bar('Q'))
assertFalse(bar('W'))
assertTrue(baz('Q'))
assertFalse(baz('W'))
return "OK"
}
}
fun bar(x: Char) = x.equals('Q')
fun baz(x: Any) = x.equals('Q')

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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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())

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"
}

View File

@@ -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"
}

View File

@@ -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"
}

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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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"
}

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"
}

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"
}

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"
}

View File

@@ -3,7 +3,7 @@
// FILE: lib.kt
package lib
external fun bar()
external fun bar(): Int
val bar = 32

View File

@@ -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"
}

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

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

View File

@@ -32,4 +32,4 @@ inline operator fun P.component1() = a
inline operator fun P.component2() = b
// LINES: 22 17 17 31 18 18 33 20 20 21 21 23 9 9 9 9 4 9 9 9 6 6 31 7 7 33 11 11 12 12 15 15 27 26 26 29 29 29 * 31 31 31 33 33 33 * 1 * 1
// 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

View File

@@ -4,4 +4,4 @@ fun foo(x: Int): () -> Unit = {
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