[K/JS] Prepare JS Plain Objects plugin to publication
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
description = "Kotlin JavaScript Plain Objects Compiler Plugin (K2)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:fir:cones"))
|
||||
compileOnly(project(":compiler:fir:tree"))
|
||||
compileOnly(project(":compiler:fir:resolve"))
|
||||
compileOnly(project(":compiler:fir:plugin-utils"))
|
||||
compileOnly(project(":compiler:fir:entrypoint"))
|
||||
compileOnly(project(":compiler:cli-common"))
|
||||
|
||||
implementation(project(":plugins:js-plain-objects:compiler-plugin:js-plain-objects.common"))
|
||||
|
||||
compileOnly(intellijCore())
|
||||
|
||||
testApi(project(":compiler:fir:plugin-utils"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
compilerOptions.freeCompilerArgs.add("-Xcontext-receivers")
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
|
||||
import org.jetbrains.kotlinx.jspo.compiler.fir.checkers.FirJsPlainObjectsCheckersComponent
|
||||
import org.jetbrains.kotlinx.jspo.compiler.fir.services.JsPlainObjectsPropertiesProvider
|
||||
|
||||
class JsPlainObjectsExtensionRegistrar : FirExtensionRegistrar() {
|
||||
override fun ExtensionRegistrarContext.configurePlugin() {
|
||||
+::FirJsPlainObjectsCheckersComponent
|
||||
+::JsPlainObjectsFunctionsGenerator
|
||||
// services
|
||||
+::JsPlainObjectsPropertiesProvider
|
||||
}
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirFunctionTarget
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.origin
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
|
||||
import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext
|
||||
import org.jetbrains.kotlin.fir.extensions.NestedClassGenerationContext
|
||||
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
|
||||
import org.jetbrains.kotlin.fir.moduleData
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildImplicitThisReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.scopes.kotlinScopeProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.toEffectiveVisibility
|
||||
import org.jetbrains.kotlin.fir.types.isNullable
|
||||
import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlinx.jspo.compiler.fir.services.jsPlainObjectPropertiesProvider
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsPluginKey
|
||||
|
||||
/**
|
||||
* The extension generate a synthetic factory and copy-method for an `external interface` annotated with @JsPlainObjects
|
||||
* Imagine the next interfaces:
|
||||
* ```
|
||||
* external interface User {
|
||||
* val name: String
|
||||
* }
|
||||
* @JsPlainObjects
|
||||
* external interface Admin {
|
||||
* val chat: Chat
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For the interface `Admin` this function should generate the companion inline function:
|
||||
* ```
|
||||
* external interface Admin {
|
||||
* val chat: Chat
|
||||
*
|
||||
* inline fun copy(chat: Chat = this.chat, name: String = this.name): Admin =
|
||||
* Admin.Companion.invoke(chat, name)
|
||||
*
|
||||
* companion object {
|
||||
* inline operator fun invoke(chat: Chat, name: String): Admin =
|
||||
* js("{ chat: chat, name: name }")
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class JsPlainObjectsFunctionsGenerator(session: FirSession) : FirDeclarationGenerationExtension(session) {
|
||||
private val predicateBasedProvider = session.predicateBasedProvider
|
||||
|
||||
private val matchedInterfaces by lazy {
|
||||
predicateBasedProvider.getSymbolsByPredicate(JsPlainObjectsPredicates.AnnotatedWithJsPlainObject.LOOKUP)
|
||||
.filterIsInstance<FirRegularClassSymbol>()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private val factoryFqNamesToJsPlainObjectsInterface by lazy {
|
||||
matchedInterfaces.associateBy { it.classId.asSingleFqName() }
|
||||
}
|
||||
|
||||
private val FirClassLikeSymbol<*>.isJsPlainObject: Boolean
|
||||
get() = this is FirRegularClassSymbol && this in matchedInterfaces
|
||||
|
||||
override fun getNestedClassifiersNames(classSymbol: FirClassSymbol<*>, context: NestedClassGenerationContext): Set<Name> {
|
||||
return if (classSymbol.isJsPlainObject) setOf(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) else emptySet()
|
||||
}
|
||||
|
||||
override fun generateNestedClassLikeDeclaration(
|
||||
owner: FirClassSymbol<*>,
|
||||
name: Name,
|
||||
context: NestedClassGenerationContext
|
||||
): FirClassLikeSymbol<*>? {
|
||||
return if (
|
||||
owner is FirRegularClassSymbol &&
|
||||
owner.isJsPlainObject &&
|
||||
name == org.jetbrains.kotlin.name.SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
) generateCompanionDeclaration(owner)
|
||||
else null
|
||||
}
|
||||
|
||||
private fun generateCompanionDeclaration(owner: FirRegularClassSymbol): FirRegularClassSymbol? {
|
||||
if (owner.companionObjectSymbol != null) return null
|
||||
val classId = owner.classId.createNestedClassId(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT)
|
||||
return buildRegularClass {
|
||||
resolvePhase = FirResolvePhase.BODY_RESOLVE
|
||||
moduleData = session.moduleData
|
||||
origin = JsPlainObjectsPluginKey.origin
|
||||
classKind = ClassKind.OBJECT
|
||||
scopeProvider = session.kotlinScopeProvider
|
||||
status = FirResolvedDeclarationStatusImpl(
|
||||
Visibilities.Public,
|
||||
Modality.FINAL,
|
||||
Visibilities.Public.toEffectiveVisibility(owner, forClass = true)
|
||||
).apply {
|
||||
isExternal = true
|
||||
isCompanion = true
|
||||
}
|
||||
name = classId.shortClassName
|
||||
symbol = FirRegularClassSymbol(classId)
|
||||
}.symbol
|
||||
}
|
||||
|
||||
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>, context: MemberGenerationContext): Set<Name> {
|
||||
val outerClass = classSymbol.getContainingClassSymbol(session)
|
||||
return when {
|
||||
classSymbol.isCompanion && outerClass?.isJsPlainObject == true -> setOf(OperatorNameConventions.INVOKE)
|
||||
classSymbol.isJsPlainObject -> setOf(StandardNames.DATA_CLASS_COPY)
|
||||
else -> emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
|
||||
if (context == null) return emptyList()
|
||||
|
||||
val containingClass = callableId.classId
|
||||
val possibleInterface = containingClass?.outerClassId
|
||||
|
||||
return when (callableId.callableName) {
|
||||
StandardNames.DATA_CLASS_COPY -> {
|
||||
containingClass
|
||||
?.let { factoryFqNamesToJsPlainObjectsInterface[it.asSingleFqName()] }
|
||||
?.let { listOf(createJsPlainObjectCopyFunction(callableId, context.owner, it).symbol) } ?: emptyList()
|
||||
}
|
||||
OperatorNameConventions.INVOKE -> {
|
||||
possibleInterface
|
||||
?.takeIf { context.owner.isCompanion }
|
||||
?.let { factoryFqNamesToJsPlainObjectsInterface[it.asSingleFqName()] }
|
||||
?.let { listOf(createJsPlainObjectFactoryFunction(callableId, context.owner, it).symbol) } ?: emptyList()
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createJsPlainObjectFactoryFunction(
|
||||
callableId: CallableId,
|
||||
parent: FirClassSymbol<*>,
|
||||
jsPlainObjectInterface: FirRegularClassSymbol,
|
||||
): FirSimpleFunction {
|
||||
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
||||
runIf(resolvedReturnTypeRef.type.isNullable) {
|
||||
buildConstExpression(
|
||||
source = null,
|
||||
value = null,
|
||||
kind = ConstantValueKind.Null,
|
||||
setType = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createJsPlainObjectCopyFunction(
|
||||
callableId: CallableId,
|
||||
parent: FirClassSymbol<*>,
|
||||
jsPlainObjectInterface: FirRegularClassSymbol,
|
||||
): FirSimpleFunction {
|
||||
val interfaceType = jsPlainObjectInterface.defaultType()
|
||||
return createJsPlainObjectsFunction(callableId, parent, jsPlainObjectInterface) {
|
||||
buildPropertyAccessExpression {
|
||||
dispatchReceiver = buildThisReceiverExpression {
|
||||
calleeReference = buildImplicitThisReference { boundSymbol = jsPlainObjectInterface }
|
||||
coneTypeOrNull = interfaceType
|
||||
}
|
||||
calleeReference = buildResolvedNamedReference {
|
||||
name = this@createJsPlainObjectsFunction.name
|
||||
resolvedSymbol = this@createJsPlainObjectsFunction
|
||||
}
|
||||
coneTypeOrNull = resolvedReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun createJsPlainObjectsFunction(
|
||||
callableId: CallableId,
|
||||
parent: FirClassSymbol<*>,
|
||||
jsPlainObjectInterface: FirRegularClassSymbol,
|
||||
getParameterDefaultValueFromProperty: FirPropertySymbol.() -> FirExpression?
|
||||
): FirSimpleFunction {
|
||||
val jsPlainObjectProperties = session.jsPlainObjectPropertiesProvider.getJsPlainObjectsPropertiesForClass(jsPlainObjectInterface)
|
||||
val functionTarget = FirFunctionTarget(null, isLambda = false)
|
||||
val jsPlainObjectInterfaceDefaultType = jsPlainObjectInterface.defaultType()
|
||||
|
||||
return buildSimpleFunction {
|
||||
moduleData = jsPlainObjectInterface.moduleData
|
||||
resolvePhase = FirResolvePhase.BODY_RESOLVE
|
||||
origin = JsPlainObjectsPluginKey.origin
|
||||
symbol = FirNamedFunctionSymbol(callableId)
|
||||
name = callableId.callableName
|
||||
returnTypeRef = jsPlainObjectInterfaceDefaultType.toFirResolvedTypeRef()
|
||||
|
||||
status = FirResolvedDeclarationStatusImpl(
|
||||
jsPlainObjectInterface.visibility,
|
||||
Modality.FINAL,
|
||||
jsPlainObjectInterface.visibility.toEffectiveVisibility(parent, forClass = true)
|
||||
).apply {
|
||||
isInline = true
|
||||
isOperator = true
|
||||
}
|
||||
|
||||
dispatchReceiverType = parent.defaultType()
|
||||
jsPlainObjectInterface.typeParameterSymbols.mapTo(typeParameters) { it.fir }
|
||||
jsPlainObjectProperties.mapTo(valueParameters) {
|
||||
val typeRef = it.resolvedReturnTypeRef
|
||||
buildValueParameter {
|
||||
moduleData = session.moduleData
|
||||
origin = JsPlainObjectsPluginKey.origin
|
||||
returnTypeRef = typeRef
|
||||
name = it.name
|
||||
symbol = FirValueParameterSymbol(it.name)
|
||||
isCrossinline = false
|
||||
isNoinline = true
|
||||
isVararg = false
|
||||
resolvePhase = FirResolvePhase.BODY_RESOLVE
|
||||
containingFunctionSymbol = this@buildSimpleFunction.symbol
|
||||
defaultValue = it.getParameterDefaultValueFromProperty()
|
||||
}
|
||||
}
|
||||
}.also(functionTarget::bind)
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
|
||||
import org.jetbrains.kotlin.fir.extensions.predicate.LookupPredicate
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsAnnotations
|
||||
|
||||
object JsPlainObjectsPredicates {
|
||||
internal object AnnotatedWithJsPlainObject {
|
||||
private val jsPlainObjectAnnotation = setOf(JsPlainObjectsAnnotations.jsPlainObjectAnnotationFqName)
|
||||
internal val LOOKUP = LookupPredicate.create { annotated(jsPlainObjectAnnotation) }
|
||||
internal val DECLARATION = DeclarationPredicate.create { annotated(jsPlainObjectAnnotation) }
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
|
||||
|
||||
class FirJsPlainObjectsCheckersComponent(session: FirSession) : FirAdditionalCheckersExtension(session) {
|
||||
override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() {
|
||||
override val classCheckers: Set<FirClassChecker> = setOf(FirJsPlainObjectsPluginClassChecker)
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.*
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
|
||||
|
||||
object FirJsPlainObjectsErrors {
|
||||
val NON_EXTERNAL_DECLARATIONS_NOT_SUPPORTED by error1<PsiElement, String>()
|
||||
val ONLY_INTERFACES_ARE_SUPPORTED by error1<PsiElement, String>()
|
||||
val IMPLEMENTING_OF_JS_PLAIN_OBJECT_IS_NOT_SUPPORTED by error1<PsiElement, String>()
|
||||
val METHODS_ARE_NOT_ALLOWED_INSIDE_JS_PLAIN_OBJECT by error0<PsiElement>()
|
||||
val JS_PLAIN_OBJECT_CAN_EXTEND_ONLY_OTHER_JS_PLAIN_OBJECTS by error1<PsiElement, String>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(KtDefaultErrorMessagesJsPlainObjects)
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInline
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isMethodOfAny
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.isAny
|
||||
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsAnnotations
|
||||
|
||||
object FirJsPlainObjectsPluginClassChecker : FirClassChecker() {
|
||||
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
with(context) {
|
||||
val classSymbol = declaration.symbol as? FirRegularClassSymbol ?: return
|
||||
|
||||
if (classSymbol.hasAnnotation(JsPlainObjectsAnnotations.jsPlainObjectAnnotationClassId, session)) {
|
||||
checkJsPlainObjectAnnotationTargets(classSymbol, reporter)
|
||||
checkJsPlainObjectSuperTypes(classSymbol, reporter)
|
||||
checkJsPlainObjectMembers(classSymbol, reporter)
|
||||
} else {
|
||||
checkJsPlainObjectAsSuperInterface(classSymbol, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
private fun checkJsPlainObjectAnnotationTargets(classSymbol: FirClassSymbol<out FirClass>, reporter: DiagnosticReporter) {
|
||||
val classKind = classSymbol.classKind.codeRepresentation ?: error("Unexpected enum entry")
|
||||
|
||||
if (!classSymbol.isEffectivelyExternal(session)) {
|
||||
reporter.reportOn(classSymbol.source, FirJsPlainObjectsErrors.NON_EXTERNAL_DECLARATIONS_NOT_SUPPORTED, classKind)
|
||||
return
|
||||
}
|
||||
if (!classSymbol.isInterface) {
|
||||
reporter.reportOn(classSymbol.source, FirJsPlainObjectsErrors.ONLY_INTERFACES_ARE_SUPPORTED, classKind)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
private fun checkJsPlainObjectMembers(classSymbol: FirClassSymbol<out FirClass>, reporter: DiagnosticReporter) {
|
||||
if (!classSymbol.isEffectivelyExternal(session) || !classSymbol.isInterface) return
|
||||
classSymbol
|
||||
.declaredMemberScope(session, null)
|
||||
.processAllFunctions {
|
||||
if (!it.isMethodOfAny && !it.isInline) {
|
||||
reporter.reportOn(it.source, FirJsPlainObjectsErrors.METHODS_ARE_NOT_ALLOWED_INSIDE_JS_PLAIN_OBJECT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
private fun checkJsPlainObjectSuperTypes(classSymbol: FirClassSymbol<out FirClass>, reporter: DiagnosticReporter) {
|
||||
if (!classSymbol.isEffectivelyExternal(session) || !classSymbol.isInterface) return
|
||||
classSymbol.resolvedSuperTypeRefs.forEach { superType ->
|
||||
val superInterface = superType.coneType
|
||||
.takeIf { !it.isAny }
|
||||
?.fullyExpandedType(session)
|
||||
?.toRegularClassSymbol(session) ?: return@forEach
|
||||
|
||||
if (!superInterface.hasAnnotation(JsPlainObjectsAnnotations.jsPlainObjectAnnotationClassId, session)) {
|
||||
reporter.reportOn(
|
||||
superType.source,
|
||||
FirJsPlainObjectsErrors.JS_PLAIN_OBJECT_CAN_EXTEND_ONLY_OTHER_JS_PLAIN_OBJECTS,
|
||||
classSymbol.classId.asFqNameString()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
private fun checkJsPlainObjectAsSuperInterface(classSymbol: FirClassSymbol<out FirClass>, reporter: DiagnosticReporter) {
|
||||
classSymbol.resolvedSuperTypeRefs.forEach {
|
||||
val superInterface = it.coneType.fullyExpandedType(session)
|
||||
.toRegularClassSymbol(session)
|
||||
?.takeIf { it.classKind == ClassKind.INTERFACE } ?: return@forEach
|
||||
|
||||
if (superInterface.hasAnnotation(JsPlainObjectsAnnotations.jsPlainObjectAnnotationClassId, session)) {
|
||||
reporter.reportOn(
|
||||
it.source,
|
||||
FirJsPlainObjectsErrors.IMPLEMENTING_OF_JS_PLAIN_OBJECT_IS_NOT_SUPPORTED,
|
||||
classSymbol.classId.asFqNameString()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
|
||||
|
||||
object KtDefaultErrorMessagesJsPlainObjects : BaseDiagnosticRendererFactory() {
|
||||
override val MAP = KtDiagnosticFactoryToRendererMap("JsPlainObjects").apply {
|
||||
put(
|
||||
FirJsPlainObjectsErrors.NON_EXTERNAL_DECLARATIONS_NOT_SUPPORTED,
|
||||
"Non-external {0} can not be annotated with JsPlainObjects. Only external interfaces are supported.",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
put(
|
||||
FirJsPlainObjectsErrors.ONLY_INTERFACES_ARE_SUPPORTED,
|
||||
"External {0} can not be annotated with JsPlainObjects. Only external interfaces are supported.",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
put(
|
||||
FirJsPlainObjectsErrors.IMPLEMENTING_OF_JS_PLAIN_OBJECT_IS_NOT_SUPPORTED,
|
||||
"[{0}] is marked as JsPlainObjects, so, it can not be used as a super-type for non-JsPlainObjects declarations",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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 org.jetbrains.kotlinx.jspo.compiler.fir.services
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.processAllProperties
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlinx.jspo.compiler.fir.JsPlainObjectsPredicates
|
||||
import org.jetbrains.kotlinx.jspo.compiler.resolve.JsPlainObjectsAnnotations
|
||||
|
||||
class JsPlainObjectsPropertiesProvider(session: FirSession) : FirExtensionSessionComponent(session) {
|
||||
private val cache: FirCache<FirClassSymbol<*>, List<FirPropertySymbol>, Nothing?> =
|
||||
session.firCachesFactory.createCache(this::createJsPlainObjectProperties)
|
||||
|
||||
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
|
||||
register(JsPlainObjectsPredicates.AnnotatedWithJsPlainObject.DECLARATION)
|
||||
}
|
||||
|
||||
fun getJsPlainObjectsPropertiesForClass(classSymbol: FirClassSymbol<*>): List<FirPropertySymbol> {
|
||||
return cache.getValue(classSymbol)
|
||||
}
|
||||
|
||||
private fun createJsPlainObjectProperties(classSymbol: FirClassSymbol<*>): List<FirPropertySymbol> =
|
||||
if (!classSymbol.hasAnnotation(JsPlainObjectsAnnotations.jsPlainObjectAnnotationClassId, session)) {
|
||||
emptyList()
|
||||
} else {
|
||||
buildList {
|
||||
classSymbol.resolvedSuperTypes.forEach {
|
||||
val superInterface = it.fullyExpandedType(session)
|
||||
.toRegularClassSymbol(session)
|
||||
?.takeIf { it.classKind == ClassKind.INTERFACE } ?: return@forEach
|
||||
|
||||
val superInterfaceSimpleObjectProperties = createJsPlainObjectProperties(superInterface)
|
||||
superInterfaceSimpleObjectProperties.forEach(::addIfNotNull)
|
||||
}
|
||||
|
||||
classSymbol
|
||||
.declaredMemberScope(session, null)
|
||||
.processAllProperties {
|
||||
addIfNotNull(it.takeIf { it.visibility == Visibilities.Public } as? FirPropertySymbol)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.jsPlainObjectPropertiesProvider: JsPlainObjectsPropertiesProvider by FirSession.sessionComponentAccessor()
|
||||
Reference in New Issue
Block a user