diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index c8222e35368..fba07727ac5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -8,12 +8,10 @@ package org.jetbrains.kotlin.ir.backend.js import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI -import org.jetbrains.kotlin.ir.backend.js.utils.getJsName import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.isLong import org.jetbrains.kotlin.ir.util.constructors @@ -109,6 +107,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC // RTTI: + val implementSymbol = getInternalFunction("implement") + val setMetadataForSymbol = getInternalFunction("setMetadataFor") + val isInterfaceSymbol = getInternalFunction("isInterface") val isArraySymbol = getInternalFunction("isArray") // val isCharSymbol = getInternalFunction("isChar") diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 9cb95504339..0b7a384e001 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -155,6 +155,7 @@ class JsIrBackendContext( val dynamicType: IrDynamicType = IrDynamicTypeImpl(null, emptyList(), Variance.INVARIANT) val intrinsics: JsIntrinsics = JsIntrinsics(irBuiltIns, this) + override val reflectionSymbols: ReflectionSymbols get() = intrinsics.reflectionSymbols override val propertyLazyInitialization: PropertyLazyInitialization = PropertyLazyInitialization( diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 7cd1de294db..2b22aa49d89 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -526,7 +526,7 @@ private val privateMemberUsagesLoweringPhase = makeBodyLoweringPhase( private val propertyReferenceLoweringPhase = makeBodyLoweringPhase( ::PropertyReferenceLowering, name = "PropertyReferenceLowering", - description = "Transform property references" + description = "Transform property references", ) private val interopCallableReferenceLoweringPhase = makeBodyLoweringPhase( @@ -669,7 +669,7 @@ private val typeOperatorLoweringPhase = makeBodyLoweringPhase( bridgesConstructionPhase, removeInlineDeclarationsWithReifiedTypeParametersLoweringPhase, singleAbstractMethodPhase, errorExpressionLoweringPhase, - interopCallableReferenceLoweringPhase + interopCallableReferenceLoweringPhase, ) ) @@ -705,7 +705,6 @@ private val constLoweringPhase = makeBodyLoweringPhase( name = "ConstLowering", description = "Wrap Long and Char constants into constructor invocation" ) - private val inlineClassDeclarationLoweringPhase = makeDeclarationTransformerPhase( { InlineClassLowering(it).inlineClassDeclarationLowering }, name = "InlineClassDeclarationLowering", @@ -806,6 +805,7 @@ private val implicitlyExportedDeclarationsMarkingLowering = makeDeclarationTrans description = "Add @JsImplicitExport annotation to declarations which are not exported but are used inside other exported declarations as a type" ) + private val cleanupLoweringPhase = makeBodyLoweringPhase( { CleanupLowering() }, name = "CleanupLowering", diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt index 387d6d2808c..73c15b0fe9d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt @@ -5,22 +5,17 @@ package org.jetbrains.kotlin.ir.backend.js.dce +import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.collectOverridenSymbols import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass -import org.jetbrains.kotlin.ir.backend.js.utils.associatedObject -import org.jetbrains.kotlin.ir.backend.js.utils.getJsName -import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName -import org.jetbrains.kotlin.ir.backend.js.utils.invokeFunForLambda +import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.classifierOrFail -import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.types.getClass +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* internal class JsUsefulDeclarationProcessor( @@ -33,6 +28,13 @@ internal class JsUsefulDeclarationProcessor( private val hashCodeMethod = getMethodOfAny("hashCode") override val bodyVisitor: BodyVisitorBase = object : BodyVisitorBase() { + override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: IrDeclaration) { + if (expression.symbol != context.intrinsics.implementSymbol) { + // Just ignore implement to not include large chunk of code inside small applications if it's not needed + super.visitFunctionAccess(expression, data) + } + } + override fun visitCall(expression: IrCall, data: IrDeclaration) { super.visitCall(expression, data) when (expression.symbol) { @@ -113,20 +115,41 @@ internal class JsUsefulDeclarationProcessor( } + override fun processSuperTypes(irClass: IrClass) { + irClass.superTypes.forEach { + if (!it.isInterface()) { + (it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue(irClass, "superTypes") + } + } + } + override fun processClass(irClass: IrClass) { super.processClass(irClass) if (irClass.containsMetadata()) { when { - irClass.isInterface -> context.intrinsics.metadataInterfaceConstructorSymbol.owner.enqueue(irClass, "interface metadata") irClass.isObject -> context.intrinsics.metadataObjectConstructorSymbol.owner.enqueue(irClass, "object metadata") + irClass.isInterface -> { + context.intrinsics.implementSymbol.owner.enqueue(irClass, "interface metadata") + context.intrinsics.metadataInterfaceConstructorSymbol.owner.enqueue(irClass, "interface metadata") + } else -> context.intrinsics.metadataClassConstructorSymbol.owner.enqueue(irClass, "class metadata") } + + context.intrinsics.setMetadataForSymbol.owner.enqueue(irClass, "metadata") + } + } + + override fun processSimpleFunction(irFunction: IrSimpleFunction) { + super.processSimpleFunction(irFunction) + + if (irFunction.isReal && irFunction.body != null) { + irFunction.parentClassOrNull?.takeIf { it.isInterface }?.enqueue(irFunction, "interface default method is used") } } private fun IrClass.containsMetadata(): Boolean = - !isExternal && !isExpect && !isBuiltInClass(this) + !isExternal && !isExpect && !isBuiltInClass(this) override fun processConstructedClassDeclaration(declaration: IrDeclaration) { if (declaration in result) return diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt index 11f9f1856b5..3636b7be166 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.isAssociatedObjectAnnotatedAnnot import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor @@ -138,14 +139,12 @@ abstract class UsefulDeclarationProcessor( protected val result = hashSetOf() protected val classesWithObjectAssociations = hashSetOf() - public val usefulPolyfilledDeclarations = hashSetOf() + val usefulPolyfilledDeclarations = hashSetOf() protected open fun processField(irField: IrField): Unit = Unit protected open fun processClass(irClass: IrClass) { - irClass.superTypes.forEach { - (it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue(irClass, "superTypes") - } + processSuperTypes(irClass) if (irClass.isObject && isExported(irClass)) { context.mapping.objectToGetInstanceFunction[irClass] @@ -161,6 +160,12 @@ abstract class UsefulDeclarationProcessor( } } + protected open fun processSuperTypes(irClass: IrClass) { + irClass.superTypes.forEach { + (it.classifierOrNull as? IrClassSymbol)?.owner?.enqueue(irClass, "superTypes") + } + } + protected open fun processSimpleFunction(irFunction: IrSimpleFunction) { if (irFunction.isFakeOverride) { irFunction.resolveFakeOverride()?.enqueue(irFunction, "real overridden fun", isContagious = false) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UselessDeclarationsRemover.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UselessDeclarationsRemover.kt index 0dc878f4d44..a6335593944 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UselessDeclarationsRemover.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UselessDeclarationsRemover.kt @@ -13,6 +13,11 @@ import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.types.isAny import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid @@ -24,6 +29,8 @@ class UselessDeclarationsRemover( private val context: JsIrBackendContext, private val dceRuntimeDiagnostic: RuntimeDiagnostic?, ) : IrElementVisitorVoid { + private val savedTypesCache = hashMapOf>() + override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } @@ -47,6 +54,23 @@ class UselessDeclarationsRemover( if (removeUnusedAssociatedObjects && declaration.annotations.any { !it.shouldKeepAnnotation() }) { declaration.annotations = declaration.annotations.filter { it.shouldKeepAnnotation() } } + + declaration.superTypes = declaration.superTypes + .flatMap { it.classOrNull?.collectUsedSuperTypes() ?: emptyList() } + .distinct() + .map { it.defaultType } + } + + private fun IrClassSymbol.collectUsedSuperTypes(): Set { + return savedTypesCache.getOrPut(this) { + if (owner in usefulDeclarations) { + setOf(this) + } else { + owner.superTypes + .flatMap { it.takeIf { !it.isAny() }?.classOrNull?.collectUsedSuperTypes() ?: emptyList() } + .toSet() + } + } } // TODO bring back the primary constructor fix diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index bc03b5295b3..1eef2058bb6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -128,8 +128,8 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer result.classes[nameGenerator.getNameForClass(symbol.owner)] = - JsIrIcClassModel(model.klass.superTypes.map { staticContext.getNameForClass((it.classifierOrFail as IrClassSymbol).owner) }).also { + JsIrIcClassModel(model.superClasses.map { staticContext.getNameForClass(it.owner) }).also { it.preDeclarationBlock.statements += model.preDeclarationBlock.statements it.postDeclarationBlock.statements += model.postDeclarationBlock.statements } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index f86a925f660..89f9908d609 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -6,27 +6,31 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import org.jetbrains.kotlin.backend.common.compilationException +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.export.isAllowedFakeOverriddenDeclaration import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isOverriddenExported import org.jetbrains.kotlin.ir.backend.js.utils.* +import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrClassReference +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classifierOrFail -import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.types.isAny +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.common.isValidES5Identifier import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.addToStdlib.runIf class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) { - private val className = context.getNameForClass(irClass) private val classNameRef = className.makeRef() private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface } @@ -44,6 +48,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo assert(!irClass.isExpect) if (!es6mode) maybeGeneratePrimaryConstructor() + val transformer = IrDeclarationToJsTransformer() // Properties might be lowered out of classes @@ -74,7 +79,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo } } else { classBlock.statements += declaration.accept(transformer, context) - classModel.preDeclarationBlock.statements += generateInheritanceCode() } } is IrSimpleFunction -> { @@ -104,8 +108,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo } } - classBlock.statements += generateClassMetadata() - if (!irClass.isInterface) { for (property in properties) { if (property.getter?.extensionReceiverParameter != null || property.setter?.extensionReceiverParameter != null) @@ -206,7 +208,11 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo } } } + + classModel.preDeclarationBlock.statements += generateSetMetadataCall() + context.staticContext.classModels[irClass.symbol] = classModel + return classBlock } @@ -307,24 +313,49 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}") func.name = className classBlock.statements += func.makeStmt() - classModel.preDeclarationBlock.statements += generateInheritanceCode() } } - private fun generateInheritanceCode(): List { - val baseClassPrototype = baseClassRef ?: return emptyList() + private fun generateSetMetadataCall(): JsStatement { + val setMetadataFor = context.staticContext.backendContext.intrinsics.setMetadataForSymbol.owner - val createCall = jsAssignment( - classPrototypeRef, JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototypeOf(baseClassPrototype)) + val ctor = classNameRef + val parent = baseClassRef + val name = generateSimpleName() + val interfaces = generateInterfacesList() + val metadataConstructor = getMetadataConstructor() + val associatedObjectKey = generateAssociatedObjectKey() + val associatedObjects = generateAssociatedObjects() + val suspendArity = generateSuspendArity() + + return JsInvocation( + JsNameRef(context.getNameForStaticFunction(setMetadataFor)), + listOf(ctor, name, metadataConstructor, parent, interfaces, associatedObjectKey, associatedObjects, suspendArity) + .dropLastWhile { it == null } + .map { it ?: Namer.JS_UNDEFINED } ).makeStmt() - val ctorAssign = jsAssignment(JsNameRef(Namer.CONSTRUCTOR_NAME, classPrototypeRef), classNameRef).makeStmt() - - return listOf(createCall, ctorAssign) } - private fun generateClassMetadata(): JsStatement { - val metadataConstructor = with(context.staticContext.backendContext.intrinsics) { + + private fun IrType.asConstructorRef(): JsNameRef? { + val ownerSymbol = classOrNull?.takeIf { + !isAny() && !isFunctionType() && !it.owner.isEffectivelyExternal() + } ?: return null + + return JsNameRef(context.getNameForClass(ownerSymbol.owner)) + } + + private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction() + + private fun isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() } + + private fun generateSimpleName(): JsStringLiteral? { + return irClass.name.takeIf { !it.isSpecial }?.let { JsStringLiteral(it.identifier) } + } + + private fun getMetadataConstructor(): JsNameRef { + val metadataConstructorSymbol = with(context.staticContext.backendContext.intrinsics) { when { irClass.isInterface -> metadataInterfaceConstructorSymbol irClass.isObject -> metadataObjectConstructorSymbol @@ -332,32 +363,21 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo } } - val simpleName = irClass.name - .takeIf { !it.isSpecial } - ?.let { JsStringLiteral(it.identifier) } - - val interfaces = generateSuperClasses() - val associatedObjectKey = generateAssociatedObjectKey() - val associatedObjects = generateAssociatedObjects() - val fastPrototype = generateFastPrototype() - val suspendArity = generateSuspendArity() - - val constructorCall = JsInvocation( - JsNameRef(context.getNameForStaticFunction(metadataConstructor.owner)), - listOf(simpleName, interfaces, associatedObjectKey, associatedObjects, suspendArity, fastPrototype) - .dropLastWhile { it == null } - .map { it ?: Namer.JS_UNDEFINED } - ) - - return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), constructorCall).makeStmt() + return JsNameRef(context.getNameForStaticFunction(metadataConstructorSymbol.owner)) } - private fun isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() } + private fun generateInterfacesList(): JsArrayLiteral? { + val listRef = irClass.superTypes + .filter { it.classOrNull?.owner?.isExternal != true } + .takeIf { it.size > 1 || it.singleOrNull() != baseClass } + ?.mapNotNull { it.asConstructorRef() } + ?.takeIf { it.isNotEmpty() } ?: return null + return JsArrayLiteral(listRef) + } private fun generateSuspendArity(): JsArrayLiteral? { - if (!isCoroutineClass()) return null - - val arity = context.staticContext.backendContext.mapping.suspendArityStore[irClass]!! + val invokeFunctions = context.staticContext.backendContext.mapping.suspendArityStore[irClass] ?: return null + val arity = invokeFunctions .map { it.valueParameters.size } .distinct() .map { JsIntLiteral(it) } @@ -365,30 +385,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo return JsArrayLiteral(arity) } - private fun generateSuperClasses(): JsArrayLiteral? { - val parentSymbols = irClass.superTypes.mapNotNull { - val symbol = it.classifierOrFail as IrClassSymbol - val isFunctionType = it.isFunctionType() - // TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal - val requireInMetadata = if (context.staticContext.backendContext.baseClassIntoMetadata) - !it.isAny() - else - symbol.isInterface - - if (requireInMetadata && !isFunctionType && !symbol.isEffectivelyExternal) { - symbol - } else null - } - - return parentSymbols - .takeIf { it.isNotEmpty() } - ?.run { JsArrayLiteral(map { JsNameRef(context.getNameForClass(it.owner)) }) } - } - - private fun generateFastPrototype() = baseClassRef?.let { prototypeOf(it) } - - private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction() - private fun generateAssociatedObjectKey(): JsIntLiteral? { return context.getAssociatedObjectKey(irClass)?.let { JsIntLiteral(it) } } @@ -440,10 +436,9 @@ private fun IrOverridableDeclaration<*>.overridesExternal(): Boolean { } private val IrClassifierSymbol.isInterface get() = (owner as? IrClass)?.isInterface == true -private val IrClassifierSymbol.isEffectivelyExternal get() = (owner as? IrDeclaration)?.isEffectivelyExternal() == true class JsIrClassModel(val klass: IrClass) { - val superClasses = klass.superTypes.map { it.classifierOrFail as IrClassSymbol } + val superClasses = klass.superTypes.map { it.classifierOrNull as IrClassSymbol } val preDeclarationBlock = JsCompositeBlock() val postDeclarationBlock = JsCompositeBlock() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt index bd6e437fca6..e894b092b34 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/Merger.kt @@ -272,7 +272,7 @@ class Merger( DFS.dfs( classModelMap.keys, - { klass -> classModelMap[klass]?.superClasses ?: emptyList() }, + { classModelMap[it]?.superClasses ?: emptyList() }, declarationHandler ) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt index 8c216d807f0..f0aa7585b7f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.IrOverridableDeclaration import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrVararg import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName @@ -36,8 +37,8 @@ fun IrConstructorCall.getSingleConstStringArgument() = (getValueArgument(0) as IrConst).value @Suppress("UNCHECKED_CAST") -fun IrConstructorCall.getSingleConstBooleanArgument() = - (getValueArgument(0) as IrConst).value +fun IrConstructorCall.getClassReferencVarargArguments() = + (getValueArgument(0) as? IrVararg)?.elements as? List fun IrAnnotationContainer.getJsModule(): String? = getAnnotation(JsAnnotations.jsModuleFqn)?.getSingleConstStringArgument() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index 45deddf8111..ab5e228e1e9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -32,7 +32,7 @@ object Namer { val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT) val METADATA = "\$metadata\$" - val METADATA_INTERFACE_ID = "interfaceId" + val INTERFACES_MASK = "\$imask\$" val KCALLABLE_GET_NAME = "" val KCALLABLE_NAME = "callableName" diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index dbf6778ff9c..dec9edc11fd 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -3811,6 +3811,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/expression/typeCheck"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true); } + @Test + @TestMetadata("complexIsInterface.kt") + public void testComplexIsInterface() throws Exception { + runTest("js/js.translator/testData/box/expression/typeCheck/complexIsInterface.kt"); + } + @Test @TestMetadata("simpleAsClass.kt") public void testSimpleAsClass() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java index 7527a27af55..7402db3f8c3 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java @@ -4265,6 +4265,12 @@ public class FirJsTestGenerated extends AbstractFirJsTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/expression/typeCheck"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true); } + @Test + @TestMetadata("complexIsInterface.kt") + public void testComplexIsInterface() throws Exception { + runTest("js/js.translator/testData/box/expression/typeCheck/complexIsInterface.kt"); + } + @Test @TestMetadata("simpleAsClass.kt") public void testSimpleAsClass() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index bcc53878e13..b0081e47b14 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -4265,6 +4265,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/expression/typeCheck"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true); } + @Test + @TestMetadata("complexIsInterface.kt") + public void testComplexIsInterface() throws Exception { + runTest("js/js.translator/testData/box/expression/typeCheck/complexIsInterface.kt"); + } + @Test @TestMetadata("simpleAsClass.kt") public void testSimpleAsClass() throws Exception { diff --git a/js/js.translator/testData/box/expression/typeCheck/complexIsInterface.kt b/js/js.translator/testData/box/expression/typeCheck/complexIsInterface.kt new file mode 100644 index 00000000000..6987ba7f9b7 --- /dev/null +++ b/js/js.translator/testData/box/expression/typeCheck/complexIsInterface.kt @@ -0,0 +1,219 @@ +// WITH_STDLIB + +package foo + +interface I1: I61 +interface I2: I61, I62 +interface I3: I61, I62, I63 +interface I4: I61, I62, I63, I64 +interface I5: I61, I62, I63, I64, I65 +interface I6: I61, I62, I63, I64, I65, I66 +interface I7: I61, I62, I63, I64, I65, I66, I67 +interface I8: I61, I62, I63, I64, I65, I66, I67, I68 +interface I9: I61, I62, I63, I64, I65, I66, I67, I68, I69 +interface I10: I61, I62, I63, I64, I65, I66, I67, I68, I69, I70 + +interface I11: I51, I52, I53, I54, I55, I56, I57, I58, I59, I60 +interface I12: I51, I52, I53, I54, I55, I56, I57, I58, I59 +interface I13: I51, I52, I53, I54, I55, I56, I57, I58 +interface I14: I51, I52, I53, I54, I55, I56, I57 +interface I15: I51, I52, I53, I54, I55, I56 +interface I16: I51, I52, I53, I54, I55 +interface I17: I51, I52, I53, I54 +interface I18: I51, I52, I53 +interface I19: I51, I52 +interface I20: I51 + +interface I21 +interface I22: I21 +interface I23: I22 +interface I24: I23 +interface I25: I24 +interface I26: I25 +interface I27: I26 +interface I28: I27 +interface I29: I28 +interface I30: I29 + +interface I31: I40 +interface I32: I37 +interface I33: I36 +interface I34: I35 +interface I35 +interface I36: I34 +interface I37: I33 +interface I38: I32 +interface I39: I31 +interface I40 + +interface I41: I1 +interface I42: I1, I2 +interface I43: I1, I2, I3 +interface I44: I1, I2, I3, I4 +interface I45: I1, I2, I3, I4, I5 +interface I46: I1, I2, I3, I4, I5, I6 +interface I47: I1, I2, I3, I4, I5, I6, I7 +interface I48: I1, I2, I3, I4, I5, I6, I7, I8 +interface I49: I1, I2, I3, I4, I5, I6, I7, I8, I9 +interface I50: I1, I2, I3, I4, I5, I6, I7, I8, I9, I10 + +interface I51: I10 +interface I52: I20 +interface I53: I30 +interface I54: I40 +interface I55: I50 +interface I56: I60 +interface I57: I70 +interface I58 +interface I59 +interface I60 + +interface I61 +interface I62 +interface I63 +interface I64 +interface I65 +interface I66 +interface I67 +interface I68 +interface I69 +interface I70 + +open class C1: I51 +open class C2: C1(), I52 +open class C3: C2(), I53 +open class C4: C3(), I54 +open class C5: C4(), I55 +open class C6: C5(), I56 +open class C7: C6(), I57 +open class C8: C7() // SKIP I58 +open class C9: C8() // SKIP I59 +class C10: C9(), I60 + +class C11: I1, I11 +class C12: I2, I12 +class C13: I3, I13 +class C14: I4, I14 +class C15: I5, I15 +class C16: I6, I16 +class C17: I7, I17 +class C18: I8, I18 +class C19: I9, I19 +class C20: I10, I20 + +open class C21: I31, I32, I33 +class C22: C21(), I34, I35, I36 +open class C23: I37, I38, I39 +class C24: C23(), I40, I50, I60 +class C25 + +fun process(instance: Any, label: String, interfaces: Set) { + assertEquals("I1" in interfaces, instance is I1, "$label is I1") + assertEquals("I2" in interfaces, instance is I2, "$label is I2") + assertEquals("I3" in interfaces, instance is I3, "$label is I3") + assertEquals("I4" in interfaces, instance is I4, "$label is I4") + assertEquals("I5" in interfaces, instance is I5, "$label is I5") + assertEquals("I6" in interfaces, instance is I6, "$label is I6") + assertEquals("I7" in interfaces, instance is I7, "$label is I7") + assertEquals("I8" in interfaces, instance is I8, "$label is I8") + assertEquals("I9" in interfaces, instance is I9, "$label is I9") + assertEquals("I10" in interfaces, instance is I10, "$label is I10") + + assertEquals("I11" in interfaces, instance is I11, "$label is I11") + assertEquals("I12" in interfaces, instance is I12, "$label is I12") + assertEquals("I13" in interfaces, instance is I13, "$label is I13") + assertEquals("I14" in interfaces, instance is I14, "$label is I14") + assertEquals("I15" in interfaces, instance is I15, "$label is I15") + assertEquals("I16" in interfaces, instance is I16, "$label is I16") + assertEquals("I17" in interfaces, instance is I17, "$label is I17") + assertEquals("I18" in interfaces, instance is I18, "$label is I18") + assertEquals("I19" in interfaces, instance is I19, "$label is I19") + assertEquals("I20" in interfaces, instance is I20, "$label is I20") + + assertEquals("I21" in interfaces, instance is I21, "$label is I21") + assertEquals("I22" in interfaces, instance is I22, "$label is I22") + assertEquals("I23" in interfaces, instance is I23, "$label is I23") + assertEquals("I24" in interfaces, instance is I24, "$label is I24") + assertEquals("I25" in interfaces, instance is I25, "$label is I25") + assertEquals("I26" in interfaces, instance is I26, "$label is I26") + assertEquals("I27" in interfaces, instance is I27, "$label is I27") + assertEquals("I28" in interfaces, instance is I28, "$label is I28") + assertEquals("I29" in interfaces, instance is I29, "$label is I29") + assertEquals("I30" in interfaces, instance is I30, "$label is I30") + + assertEquals("I31" in interfaces, instance is I31, "$label is I31") + assertEquals("I32" in interfaces, instance is I32, "$label is I32") + assertEquals("I33" in interfaces, instance is I33, "$label is I33") + assertEquals("I34" in interfaces, instance is I34, "$label is I34") + assertEquals("I35" in interfaces, instance is I35, "$label is I35") + assertEquals("I36" in interfaces, instance is I36, "$label is I36") + assertEquals("I37" in interfaces, instance is I37, "$label is I37") + assertEquals("I38" in interfaces, instance is I38, "$label is I38") + assertEquals("I39" in interfaces, instance is I39, "$label is I39") + assertEquals("I40" in interfaces, instance is I40, "$label is I40") + + assertEquals("I41" in interfaces, instance is I41, "$label is I41") + assertEquals("I42" in interfaces, instance is I42, "$label is I42") + assertEquals("I43" in interfaces, instance is I43, "$label is I43") + assertEquals("I44" in interfaces, instance is I44, "$label is I44") + assertEquals("I45" in interfaces, instance is I45, "$label is I45") + assertEquals("I46" in interfaces, instance is I46, "$label is I46") + assertEquals("I47" in interfaces, instance is I47, "$label is I47") + assertEquals("I48" in interfaces, instance is I48, "$label is I48") + assertEquals("I49" in interfaces, instance is I49, "$label is I49") + assertEquals("I50" in interfaces, instance is I50, "$label is I50") + + assertEquals("I51" in interfaces, instance is I51, "$label is I51") + assertEquals("I52" in interfaces, instance is I52, "$label is I52") + assertEquals("I53" in interfaces, instance is I53, "$label is I53") + assertEquals("I54" in interfaces, instance is I54, "$label is I54") + assertEquals("I55" in interfaces, instance is I55, "$label is I55") + assertEquals("I56" in interfaces, instance is I56, "$label is I56") + assertEquals("I57" in interfaces, instance is I57, "$label is I57") + assertEquals("I58" in interfaces, instance is I58, "$label is I58") + assertEquals("I59" in interfaces, instance is I59, "$label is I59") + assertEquals("I60" in interfaces, instance is I60, "$label is I60") + + assertEquals("I61" in interfaces, instance is I61, "$label is I61") + assertEquals("I62" in interfaces, instance is I62, "$label is I62") + assertEquals("I63" in interfaces, instance is I63, "$label is I63") + assertEquals("I64" in interfaces, instance is I64, "$label is I64") + assertEquals("I65" in interfaces, instance is I65, "$label is I65") + assertEquals("I66" in interfaces, instance is I66, "$label is I66") + assertEquals("I67" in interfaces, instance is I67, "$label is I67") + assertEquals("I68" in interfaces, instance is I68, "$label is I68") + assertEquals("I69" in interfaces, instance is I69, "$label is I69") + assertEquals("I70" in interfaces, instance is I70, "$label is I70") +} + +fun box(): String { + process(C1(), "C1", setOf("I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C2(), "C2", setOf("I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C3(), "C3", setOf("I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C4(), "C4", setOf("I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C5(), "C5", setOf("I55", "I50", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C6(), "C6", setOf("I56", "I60", "I55", "I50", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C7(), "C7", setOf("I57", "I56", "I60", "I55", "I50", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C8(), "C8", setOf("I57", "I56", "I60", "I55", "I50", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C9(), "C9", setOf("I57", "I56", "I60", "I55", "I50", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C10(), "C10", setOf("I57", "I56", "I60", "I55", "I50", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I54", "I40", "I53", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I52", "I20", "I51", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + + process(C11(), "C11", setOf("I1", "I61", "I11", "I51", "I52", "I53", "I54", "I55", "I56", "I57", "I58", "I59", "I60", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70", "I10", "I20", "I30", "I40", "I50", "I60", "I70", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10")) + process(C12(), "C12", setOf("I2", "I61", "I62", "I12", "I51", "I52", "I53", "I54", "I55", "I56", "I57", "I58", "I59", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70", "I10", "I20", "I30", "I40", "I50", "I70", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I60")) + process(C13(), "C13", setOf("I3", "I13", "I61", "I62", "I63", "I51", "I52", "I53", "I54", "I55", "I56", "I57", "I58", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70", "I10", "I20", "I30", "I40", "I50", "I70", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I60")) + process(C14(), "C14", setOf("I4", "I14", "I61", "I62", "I63", "I64", "I51", "I52", "I53", "I54", "I55", "I56", "I57", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70", "I10", "I20", "I30", "I40", "I50", "I70", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I60")) + process(C15(), "C15", setOf("I5", "I61", "I62", "I63", "I64", "I65", "I15", "I51", "I52", "I53", "I54", "I55", "I56", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70", "I10", "I20", "I30", "I40", "I50", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I60")) + process(C16(), "C16", setOf("I6", "I61", "I62", "I63", "I64", "I65", "I66", "I16", "I51", "I52", "I53", "I54", "I55", "I10", "I20", "I30", "I40", "I50", "I67", "I68", "I69", "I70", "I51", "I29", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "I40", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I67", "I68", "I69", "I70")) + process(C17(), "C17", setOf("I7", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I17", "I51", "I52", "I53", "I54", "I10", "I68", "I69", "I70", "I20", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21", "I40")) + process(C18(), "C18", setOf("I8", "I18", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I51", "I52", "I53", "I10", "I69", "I70", "I20", "I30", "I29", "I28", "I27", "I26", "I25", "I24", "I23", "I22", "I21")) + process(C19(), "C19", setOf("I9", "I19", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I51", "I52", "I10", "I70", "I20")) + process(C20(), "C20", setOf("I10", "I20", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70", "I51")) + + process(C21(), "C21", setOf("I31", "I32", "I33", "I40", "I37", "I33", "I36", "I34", "I35")) + process(C22(), "C22", setOf("I31", "I32", "I33", "I40", "I37", "I33", "I36", "I34", "I35")) + process(C23(), "C23", setOf("I37", "I38", "I39", "I33", "I36", "I34", "I35", "I32", "I31", "I40")) + process(C24(), "C24", setOf("I37", "I38", "I39", "I33", "I36", "I34", "I35", "I32", "I31", "I40", "I50", "I60", "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8", "I9", "I10", "I61", "I62", "I63", "I64", "I65", "I66", "I67", "I68", "I69", "I70")) + process(C25(), "C25", emptySet()) + + return "OK" +} diff --git a/js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt b/js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt index 04ef8fe9680..fd19bd1da92 100644 --- a/js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt +++ b/js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt @@ -1,4 +1,5 @@ // EXPECTED_REACHABLE_NODES: 1403 + package foo diff --git a/libraries/stdlib/js-ir/runtime/BitMask.kt b/libraries/stdlib/js-ir/runtime/BitMask.kt new file mode 100644 index 00000000000..43faba11b48 --- /dev/null +++ b/libraries/stdlib/js-ir/runtime/BitMask.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.js + +internal fun implement(vararg interfaces: dynamic): BitMask { + var maxSize = 1 + val masks = js("[]") + + for (i in interfaces) { + var currentSize = maxSize + val imask: BitMask? = i.prototype.`$imask$` ?: i.`$imask$` + + if (imask != null) { + masks.push(imask) + currentSize = imask.intArray.size + } + + val iid: Int? = i.`$metadata$`.iid + val iidImask: BitMask? = iid?.let { BitMask(arrayOf(it)) } + + if (iidImask != null) { + masks.push(iidImask) + currentSize = JsMath.max(currentSize, iidImask.intArray.size) + } + + if (currentSize > maxSize) { + maxSize = currentSize + } + } + + val resultIntArray = IntArray(maxSize) { i -> + masks.reduce({ acc: Int, it: BitMask -> + if (i >= it.intArray.size) + acc + else + acc or it.intArray[i] + }, 0) + } + + val result = BitMask(emptyArray()) + result.intArray = resultIntArray + return result +} + +internal class BitMask(activeBits: Array) { + var intArray: IntArray = run { + if (activeBits.size == 0) { + IntArray(0) + } else { + val max: Int = JsMath.asDynamic().max.apply(null, activeBits) + val intArray = IntArray((max shr 5) + 1) + for (activeBit in activeBits) { + val numberIndex = activeBit shr 5 + val positionInNumber = activeBit and 31 + val numberWithSettledBit = 1 shl positionInNumber + intArray[numberIndex] = intArray[numberIndex] or numberWithSettledBit + } + intArray + } + } + + fun isBitSet(possibleActiveBit: Int): Boolean { + val numberIndex = possibleActiveBit shr 5 + if (numberIndex > intArray.size) return false + val positionInNumber = possibleActiveBit and 31 + val numberWithSettledBit = 1 shl positionInNumber + return intArray[numberIndex] and numberWithSettledBit != 0 + } +} \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt b/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt index de82749f926..ac62314b184 100644 --- a/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt +++ b/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt @@ -5,6 +5,8 @@ package kotlin.js +import kotlin.reflect.KClass + @PublishedApi internal fun > enumValuesIntrinsic(): Array = throw IllegalStateException("Should be replaced by compiler") @@ -51,3 +53,6 @@ internal annotation class JsFun(val code: String) */ @Target(AnnotationTarget.CLASS) internal annotation class JsImplicitExport() + +@Target(AnnotationTarget.CLASS) +internal annotation class JsSubtypeCheckable(vararg val implements: KClass<*>) \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/reflectRuntime.kt b/libraries/stdlib/js-ir/runtime/reflectRuntime.kt index 7d03a8904d4..a74d3b8b1cc 100644 --- a/libraries/stdlib/js-ir/runtime/reflectRuntime.kt +++ b/libraries/stdlib/js-ir/runtime/reflectRuntime.kt @@ -7,44 +7,45 @@ package kotlin.js import kotlin.reflect.KProperty -internal fun getPropertyCallableRef(name: String, paramCount: Int, type: dynamic, getter: dynamic, setter: dynamic): KProperty<*> { +internal fun getPropertyCallableRef( + name: String, + paramCount: Int, + superType: dynamic, + getter: dynamic, + setter: dynamic +): KProperty<*> { getter.get = getter getter.set = setter getter.callableName = name - return getPropertyRefClass(getter, getKPropMetadata(paramCount, setter, type)).unsafeCast>() + return getPropertyRefClass( + getter, + getKPropMetadata(paramCount, setter), + getInterfaceMaskFor(getter, superType) + ).unsafeCast>() } -internal fun getLocalDelegateReference(name: String, type: dynamic, mutable: Boolean, lambda: dynamic): KProperty<*> { - return getPropertyCallableRef(name, 0, type, lambda, if (mutable) lambda else null) +internal fun getLocalDelegateReference(name: String, superType: dynamic, mutable: Boolean, lambda: dynamic): KProperty<*> { + return getPropertyCallableRef(name, 0, superType, lambda, if (mutable) lambda else null) } -private fun getPropertyRefClass(obj: Ctor, metadata: Metadata): dynamic { - obj.`$metadata$` = metadata; - obj.constructor = obj; +private fun getPropertyRefClass(obj: Ctor, metadata: Metadata, imask: BitMask): dynamic { + obj.`$metadata$` = metadata + obj.constructor = obj + obj.`$imask$` = imask return obj; } -private fun getKPropMetadata(paramCount: Int, setter: Any?, type: dynamic): dynamic { - val mdata: Metadata = propertyRefClassMetadataCache[paramCount][if (setter == null) 0 else 1] +private fun getInterfaceMaskFor(obj: Ctor, superType: dynamic): BitMask = + obj.`$imask$` ?: implement(superType) - if (mdata.interfaces.size == 0) { - mdata.interfaces.asDynamic().push(type) - - if (mdata.interfacesCache == null) { - mdata.interfacesCache = generateInterfaceCache() - } else { - mdata.interfacesCache!!.isComplete = false - } - - mdata.interfacesCache!!.extendCacheWithSingle(type) - } - - return mdata +@Suppress("UNUSED_PARAMETER") +private fun getKPropMetadata(paramCount: Int, setter: Any?): dynamic { + return propertyRefClassMetadataCache[paramCount][if (setter == null) 0 else 1] } private fun metadataObject(): Metadata { val undef = js("undefined") - return classMeta(undef, undef, undef, undef, undef, undef) + return classMeta(undef, undef, undef, undef) } private val propertyRefClassMetadataCache: Array> = arrayOf>( diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index c15e4aaa23d..01695a315e1 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -5,36 +5,42 @@ package kotlin.js -internal fun interfaceMeta( +internal fun setMetadataFor( + ctor: Ctor, name: String?, - interfaces: Array?, + metadataConstructor: (name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array?) -> Metadata, + parent: Ctor?, + interfaces: Array?, associatedObjectKey: Number?, associatedObjects: dynamic, - suspendArity: Array?, -): Metadata { - return createMetadata("interface", name, interfaces, associatedObjectKey, associatedObjects, suspendArity, js("undefined")) + suspendArity: Array? +) { + if (parent != null) { + js(""" + ctor.prototype = Object.create(parent.prototype) + ctor.prototype.constructor = ctor; + """) + } + + val metadata = metadataConstructor(name, associatedObjectKey, associatedObjects, suspendArity) + ctor.`$metadata$` = metadata + + if (interfaces != null) { + val receiver = if (metadata.iid != null) ctor else ctor.prototype + receiver.`$imask$` = implement(*interfaces) + } } -internal fun objectMeta( - name: String?, - interfaces: Array?, - associatedObjectKey: Number?, - associatedObjects: dynamic, - suspendArity: Array?, - fastPrototype: Prototype?, -): Metadata { - return createMetadata("object", name, interfaces, associatedObjectKey, associatedObjects, suspendArity, fastPrototype) +internal fun interfaceMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array?): Metadata { + return createMetadata("interface", name, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId()) } -internal fun classMeta( - name: String?, - interfaces: Array?, - associatedObjectKey: Number?, - associatedObjects: dynamic, - suspendArity: Array?, - fastPrototype: Prototype?, -): Metadata { - return createMetadata("class", name, interfaces, associatedObjectKey, associatedObjects, suspendArity, fastPrototype) +internal fun objectMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array?): Metadata { + return createMetadata("object", name, associatedObjectKey, associatedObjects, suspendArity, null) +} + +internal fun classMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array?): Metadata { + return createMetadata("class", name, associatedObjectKey, associatedObjects, suspendArity, null) } // Seems like we need to disable this check if variables are used inside js annotation @@ -42,192 +48,103 @@ internal fun classMeta( private fun createMetadata( kind: String, name: String?, - interfaces: Array?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array?, - fastPrototype: Prototype? + iid: Int? ): Metadata { return js("""({ kind: kind, simpleName: name, - interfaceId: kind === "interface" ? -1 : undefined, - interfaces: interfaces || [], associatedObjectKey: associatedObjectKey, associatedObjects: associatedObjects, suspendArity: suspendArity, - fastPrototype: fastPrototype, ${'$'}kClass$: undefined, - interfacesCache: { - isComplete: fastPrototype === undefined && (interfaces === undefined || interfaces.length === 0), - implementInterfaceMemo: {} - } + iid: iid })""") } internal external interface Metadata { val kind: String - val interfaces: Array // This field gives fast access to the prototype of metadata owner (Object.getPrototypeOf()) // Can be pre-initialized or lazy initialized and then should be immutable val simpleName: String? val associatedObjectKey: Number? val associatedObjects: dynamic - var interfaceId: Number? - var fastPrototype: Prototype? val suspendArity: Array? + val iid: Int? var `$kClass$`: dynamic - // This is an object for memoization of a isInterfaceImpl function - // Can be mutated quite often - var interfacesCache: IsImplementsCache? -} - -// This is a flag for memoization of a isInterfaceImpl function -internal external interface IsImplementsCache { - var isComplete: Boolean - val implementInterfaceMemo: dynamic } internal external interface Ctor { - var `$metadata$`: Metadata? + var `$imask$`: BitMask? + var `$metadata$`: Metadata var constructor: Ctor? - val prototype: Prototype? + val prototype: dynamic } -internal external interface Prototype { - val constructor: Ctor? -} +private var iid: Int? = null -private var interfacesCounter = 0 - -private fun Ctor.getOrDefineInterfaceId(): Number? { - val metadata = `$metadata$`.unsafeCast() - val interfaceId = metadata.interfaceId ?: -1 - return if (interfaceId != -1) { - interfaceId +@Suppress("SMARTCAST_IMPOSSIBLE") +internal fun generateInterfaceId(): Int { + if (iid == null) { + iid = 1 } else { - val result = interfacesCounter++ - metadata.interfaceId = result - result - } -} - -private fun Ctor.getPrototype() = prototype?.let { js("Object").getPrototypeOf(it).unsafeCast() } - -internal fun IsImplementsCache.extendCacheWith(cache: IsImplementsCache?) { - val anotherInterfaceMemo = cache?.implementInterfaceMemo ?: return - js("Object").assign(implementInterfaceMemo, anotherInterfaceMemo) -} - -internal fun IsImplementsCache.extendCacheWithSingle(intr: Ctor) { - implementInterfaceMemo[intr.getOrDefineInterfaceId()] = true -} - -private fun fastGetPrototype(ctor: Ctor): Prototype? { - return ctor.`$metadata$`?.run { - if (fastPrototype == null) { - fastPrototype = ctor.getPrototype() - } - fastPrototype - } ?: ctor.getPrototype() -} - -private fun completeInterfaceCache(ctor: Ctor): IsImplementsCache? { - val metadata = ctor.`$metadata$` - - if (metadata != null && metadata.interfacesCache == null) { - metadata.interfacesCache = generateInterfaceCache() + iid += 1 } - val interfacesCache = metadata?.interfacesCache - - if (interfacesCache != null) { - if (interfacesCache.isComplete == true) { - return interfacesCache - } - - for (i in metadata.interfaces) { - interfacesCache.extendCacheWithSingle(i) - interfacesCache.extendCacheWith(completeInterfaceCache(i)) - } - } - - val parentInterfacesCache = fastGetPrototype(ctor)?.constructor?.let(::completeInterfaceCache) - - return interfacesCache?.apply { - extendCacheWith(parentInterfacesCache) - isComplete = true - } ?: parentInterfacesCache + return iid } -// Old JS Backend -internal fun generateInterfaceCache(): IsImplementsCache { - return js("{ isComplete: false, implementInterfaceMemo: {} }") +@Suppress("UNUSED_PARAMETER") +private fun getPrototypeOf(obj: dynamic) = + js("Object.getPrototypeOf(obj)") + +private fun searchForMetadata(obj: dynamic): Metadata? { + if (obj == null) { + return null + } + var metadata: Metadata? = obj.`$metadata$` + var currentObject = getPrototypeOf(obj) + + while (metadata == null && currentObject != null) { + val currentConstructor = currentObject.constructor + metadata = currentConstructor.`$metadata$` + currentObject = getPrototypeOf(currentObject) + } + + return metadata } -private fun isInterfaceImpl(ctor: Ctor, iface: Ctor): Boolean { - if (ctor === iface) { - return true +// TODO: Remove after 1.8 bootstrapping +// Needed to pass all nodejs tests, because of stdlib compilation via bootstrapped compiler with old metadata format +private fun verySlowIsInterfaceImpl(obj: dynamic, iface: dynamic): Boolean { + val metadata = searchForMetadata(obj) ?: return false + val interfaces = metadata.asDynamic().associatedObjectKey + + if ( + interfaces != null && + (interfaces.indexOf(iface) != -1 || interfaces.some { x -> verySlowIsInterfaceImpl(x, iface) }) + ) { + return true } - val metadata = ctor.`$metadata$` + return verySlowIsInterfaceImpl(getPrototypeOf(obj), iface) +} - if (metadata != null && metadata.interfacesCache == null) { - metadata.interfacesCache = generateInterfaceCache() - } - - val interfacesCache = metadata?.interfacesCache - - return if (interfacesCache != null) { - if (!interfacesCache.isComplete) completeInterfaceCache(ctor) - val interfaceId = iface.`$metadata$`?.interfaceId ?: return false - !!interfacesCache.implementInterfaceMemo[interfaceId] - } else { - val constructor = fastGetPrototype(ctor)?.constructor ?: return false - isInterfaceImpl(constructor, iface) - } +private fun isInterfaceImpl(obj: dynamic, iface: Int): Boolean { + val mask: BitMask = obj.`$imask$`.unsafeCast() ?: return false + return mask.isBitSet(iface) } internal fun isInterface(obj: dynamic, iface: dynamic): Boolean { - val ctor = obj.constructor ?: return false - return isInterfaceImpl(ctor, iface) -} - -/* - -internal interface ClassMetadata { - val simpleName: String - val interfaces: Array -} - -// TODO: replace `isInterface` with the following -public fun isInterface(ctor: dynamic, IType: dynamic): Boolean { - if (ctor === IType) return true - - val metadata = ctor.`$metadata$`.unsafeCast() - - if (metadata !== null) { - val interfaces = metadata.interfaces - for (i in interfaces) { - if (isInterface(i, IType)) { - return true - } - } + return if (obj.`$imask$` != null) { + isInterfaceImpl(obj, iface.`$metadata$`.iid) + } else { + verySlowIsInterfaceImpl(obj, iface) } - - var superPrototype = ctor.prototype - if (superPrototype !== null) { - superPrototype = js("Object.getPrototypeOf(superPrototype)") - } - - val superConstructor = if (superPrototype !== null) { - superPrototype.constructor - } else null - - return superConstructor != null && isInterface(superConstructor, IType) } -*/ internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean { if (jsTypeOf(obj) == "function") { @@ -237,7 +154,7 @@ internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean { if (jsTypeOf(obj) == "object" && jsIn("${'$'}metadata${'$'}", obj.constructor)) { @Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS") - return obj.constructor.unsafeCast().`$metadata$`?.suspendArity?.let { + return obj.constructor.unsafeCast().`$metadata$`.suspendArity?.let { var result = false for (item in it) { if (arity == item) { @@ -288,7 +205,6 @@ internal fun isFloatArray(a: dynamic): Boolean = jsInstanceOf(a, js("Float32Arra internal fun isDoubleArray(a: dynamic): Boolean = jsInstanceOf(a, js("Float64Array")) internal fun isLongArray(a: dynamic): Boolean = isJsArray(a) && a.`$type$` === "LongArray" - internal fun jsGetPrototypeOf(jsClass: dynamic) = js("Object").getPrototypeOf(jsClass) internal fun jsIsType(obj: dynamic, jsClass: dynamic): Boolean { @@ -320,8 +236,9 @@ internal fun jsIsType(obj: dynamic, jsClass: dynamic): Boolean { return jsInstanceOf(obj, jsClass) } - if (klassMetadata.kind === "interface" && obj.constructor != null) { - return isInterfaceImpl(obj.constructor, jsClass) + if (klassMetadata.kind === "interface") { + val iid = klassMetadata.iid.unsafeCast() + return iid?.let { isInterfaceImpl(obj, it) } ?: verySlowIsInterfaceImpl(obj, constructor) } return false @@ -329,14 +246,16 @@ internal fun jsIsType(obj: dynamic, jsClass: dynamic): Boolean { internal fun isNumber(a: dynamic) = jsTypeOf(a) == "number" || a is Long +@OptIn(JsIntrinsic::class) internal fun isComparable(value: dynamic): Boolean { var type = jsTypeOf(value) return type == "string" || type == "boolean" || isNumber(value) || - isInterface(value, Comparable::class.js) + isInterface(value, jsClassIntrinsic>()) } +@OptIn(JsIntrinsic::class) internal fun isCharSequence(value: dynamic): Boolean = - jsTypeOf(value) == "string" || isInterface(value, CharSequence::class.js) + jsTypeOf(value) == "string" || isInterface(value, jsClassIntrinsic()) diff --git a/libraries/stdlib/js-ir/src/kotlin/reflection_js-ir.kt b/libraries/stdlib/js-ir/src/kotlin/reflection_js-ir.kt index 0a489de3f41..755133ea89c 100644 --- a/libraries/stdlib/js-ir/src/kotlin/reflection_js-ir.kt +++ b/libraries/stdlib/js-ir/src/kotlin/reflection_js-ir.kt @@ -9,8 +9,8 @@ import kotlin.reflect.js.internal.* @PublishedApi internal fun KClass<*>.findAssociatedObject(annotationClass: KClass): Any? { return if (this is KClassImpl<*> && annotationClass is KClassImpl) { - val key = annotationClass.jClass.asDynamic().unsafeCast().`$metadata$`?.associatedObjectKey?.unsafeCast() ?: return null - val map = jClass.asDynamic().unsafeCast().`$metadata$`?.associatedObjects ?: return null + val key = annotationClass.jClass.asDynamic().`$metadata$`?.associatedObjectKey?.unsafeCast() ?: return null + val map = jClass.asDynamic().`$metadata$`?.associatedObjects ?: return null val factory = map[key] ?: return null return factory() } else {