[JS] Don't fail on recursive upper bounds while resolving JsExports
Fixes https://youtrack.jetbrains.com/issue/KT-42112
This commit is contained in:
+45
-33
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty
|
||||||
import org.jetbrains.kotlin.resolve.inline.isInlineWithReified
|
import org.jetbrains.kotlin.resolve.inline.isInlineWithReified
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeProjection
|
|
||||||
import org.jetbrains.kotlin.types.isDynamic
|
import org.jetbrains.kotlin.types.isDynamic
|
||||||
import org.jetbrains.kotlin.types.typeUtil.*
|
import org.jetbrains.kotlin.types.typeUtil.*
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
|||||||
|
|
||||||
fun checkTypeParameter(descriptor: TypeParameterDescriptor) {
|
fun checkTypeParameter(descriptor: TypeParameterDescriptor) {
|
||||||
for (upperBound in descriptor.upperBounds) {
|
for (upperBound in descriptor.upperBounds) {
|
||||||
if (!isTypeExportable(upperBound, bindingContext)) {
|
if (!upperBound.isExportable(bindingContext)) {
|
||||||
val typeParameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)!!
|
val typeParameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)!!
|
||||||
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(typeParameterDeclaration, "upper bound", upperBound))
|
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(typeParameterDeclaration, "upper bound", upperBound))
|
||||||
}
|
}
|
||||||
@@ -38,7 +37,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkValueParameter(descriptor: ValueParameterDescriptor) {
|
fun checkValueParameter(descriptor: ValueParameterDescriptor) {
|
||||||
if (!isTypeExportable(descriptor.type, bindingContext)) {
|
if (!descriptor.type.isExportable(bindingContext)) {
|
||||||
val valueParameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)!!
|
val valueParameterDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor)!!
|
||||||
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(valueParameterDeclaration, "parameter", descriptor.type))
|
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(valueParameterDeclaration, "parameter", descriptor.type))
|
||||||
}
|
}
|
||||||
@@ -86,7 +85,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
descriptor.returnType?.let { returnType ->
|
descriptor.returnType?.let { returnType ->
|
||||||
if (!isTypeExportable(returnType, bindingContext, true)) {
|
if (!returnType.isExportableReturn(bindingContext)) {
|
||||||
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "return", returnType))
|
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "return", returnType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +97,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
|||||||
reportWrongExportedDeclaration("extension property")
|
reportWrongExportedDeclaration("extension property")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!isTypeExportable(descriptor.type, bindingContext)) {
|
if (!descriptor.type.isExportable(bindingContext)) {
|
||||||
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "property", descriptor.type))
|
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "property", descriptor.type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +121,7 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (superType in descriptor.defaultType.supertypes()) {
|
for (superType in descriptor.defaultType.supertypes()) {
|
||||||
if (!isTypeExportable(superType, bindingContext)) {
|
if (!superType.isExportable(bindingContext)) {
|
||||||
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "super", superType))
|
trace.report(ErrorsJs.NON_EXPORTABLE_TYPE.on(declaration, "super", superType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,45 +129,58 @@ object JsExportDeclarationChecker : DeclarationChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KotlinType.isExportableReturn(bindingContext: BindingContext, currentlyProcessed: MutableSet<KotlinType> = mutableSetOf()) =
|
||||||
|
isUnit() || isExportable(bindingContext, currentlyProcessed)
|
||||||
|
|
||||||
private fun isTypeExportable(type: KotlinType, bindingContext: BindingContext, isReturnType: Boolean = false): Boolean {
|
private fun KotlinType.isExportable(
|
||||||
if (isReturnType && type.isUnit())
|
bindingContext: BindingContext,
|
||||||
|
currentlyProcessed: MutableSet<KotlinType> = mutableSetOf()
|
||||||
|
): Boolean {
|
||||||
|
if (!currentlyProcessed.add(this)) {
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if (type.isFunctionType) {
|
currentlyProcessed.add(this)
|
||||||
val arguments = type.arguments
|
|
||||||
val argumentsSize = type.arguments.size - 1
|
if (isFunctionType) {
|
||||||
for (i in 0 until argumentsSize) {
|
for (i in 0 until arguments.lastIndex) {
|
||||||
if (!isTypeExportable(arguments[i].type, bindingContext))
|
if (!arguments[i].type.isExportable(bindingContext, currentlyProcessed)) {
|
||||||
|
currentlyProcessed.remove(this)
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!isTypeExportable(arguments.last().type, bindingContext, isReturnType = true))
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
currentlyProcessed.remove(this)
|
||||||
|
return arguments.last().type.isExportableReturn(bindingContext, currentlyProcessed)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (argument: TypeProjection in type.arguments) {
|
for (argument in arguments) {
|
||||||
if (!isTypeExportable(argument.type, bindingContext))
|
if (!argument.type.isExportable(bindingContext, currentlyProcessed)) {
|
||||||
|
currentlyProcessed.remove(this)
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val nonNullable = type.makeNotNullable()
|
currentlyProcessed.remove(this)
|
||||||
|
|
||||||
// Is primitive exportable type
|
val nonNullable = makeNotNullable()
|
||||||
if (nonNullable.isAnyOrNullableAny() ||
|
|
||||||
nonNullable.isDynamic() ||
|
|
||||||
nonNullable.isBoolean() ||
|
|
||||||
KotlinBuiltIns.isThrowableOrNullableThrowable(nonNullable) ||
|
|
||||||
KotlinBuiltIns.isString(nonNullable) ||
|
|
||||||
(nonNullable.isPrimitiveNumberOrNullableType() && !nonNullable.isLong()) ||
|
|
||||||
nonNullable.isNothingOrNullableNothing() ||
|
|
||||||
(KotlinBuiltIns.isArray(type)) ||
|
|
||||||
KotlinBuiltIns.isPrimitiveArray(type)
|
|
||||||
) return true
|
|
||||||
|
|
||||||
val descriptor = type.constructor.declarationDescriptor ?: return false
|
val isPrimitiveExportableType = nonNullable.isAnyOrNullableAny() ||
|
||||||
return descriptor is MemberDescriptor && descriptor.isEffectivelyExternal() ||
|
nonNullable.isDynamic() ||
|
||||||
AnnotationsUtils.isExportedObject(descriptor, bindingContext)
|
nonNullable.isBoolean() ||
|
||||||
|
KotlinBuiltIns.isThrowableOrNullableThrowable(nonNullable) ||
|
||||||
|
KotlinBuiltIns.isString(nonNullable) ||
|
||||||
|
(nonNullable.isPrimitiveNumberOrNullableType() && !nonNullable.isLong()) ||
|
||||||
|
nonNullable.isNothingOrNullableNothing() ||
|
||||||
|
KotlinBuiltIns.isArray(this) ||
|
||||||
|
KotlinBuiltIns.isPrimitiveArray(this)
|
||||||
|
|
||||||
|
if (isPrimitiveExportableType) return true
|
||||||
|
|
||||||
|
val descriptor = constructor.declarationDescriptor
|
||||||
|
|
||||||
|
if (descriptor !is MemberDescriptor) return false
|
||||||
|
|
||||||
|
return descriptor.isEffectivelyExternal() || AnnotationsUtils.isExportedObject(descriptor, bindingContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
@@ -5155,6 +5155,24 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("js/js.translator/testData/box/jsExport")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class JsExport extends AbstractIrBoxJsES6Test {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInJsExport() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/jsExport"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recursiveExport.kt")
|
||||||
|
public void testRecursiveExport() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/jsExport/recursiveExport.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("js/js.translator/testData/box/jsModule")
|
@TestMetadata("js/js.translator/testData/box/jsModule")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+18
@@ -5155,6 +5155,24 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("js/js.translator/testData/box/jsExport")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class JsExport extends AbstractIrBoxJsTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInJsExport() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/jsExport"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recursiveExport.kt")
|
||||||
|
public void testRecursiveExport() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/jsExport/recursiveExport.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("js/js.translator/testData/box/jsModule")
|
@TestMetadata("js/js.translator/testData/box/jsModule")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+18
@@ -5170,6 +5170,24 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("js/js.translator/testData/box/jsExport")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class JsExport extends AbstractBoxJsTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInJsExport() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/jsExport"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recursiveExport.kt")
|
||||||
|
public void testRecursiveExport() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/jsExport/recursiveExport.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("js/js.translator/testData/box/jsModule")
|
@TestMetadata("js/js.translator/testData/box/jsModule")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
$kotlin_test_internal$.beginModule();
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
var ping = require("JS_TESTS").api.ping;
|
||||||
|
var Something = require("JS_TESTS").api.Something;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"pingCall": function() {
|
||||||
|
return ping(new Something())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$kotlin_test_internal$.endModule("lib");
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// MODULE_KIND: COMMON_JS
|
||||||
|
// SKIP_MINIFICATION
|
||||||
|
|
||||||
|
// FILE: api.kt
|
||||||
|
package api
|
||||||
|
@JsExport
|
||||||
|
class Something<T: Something<T>> {
|
||||||
|
fun ping(): String {
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsExport
|
||||||
|
fun ping(s: Something<*>): String {
|
||||||
|
return s.ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
external interface JsResult {
|
||||||
|
val pingCall: () -> String
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsModule("lib")
|
||||||
|
external fun jsBox(): JsResult
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return jsBox().pingCall()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user