From 5ba70b2cbbfb2e168d3f23b6f64288adc7a16c25 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 31 Aug 2020 20:15:40 +0300 Subject: [PATCH] as42: Additional fixes for AS42 and 202 platform --- ...KotlinCoreApplicationEnvironment.java.as42 | 55 ++++ .../idea/actions/NewKotlinFileAction.kt.as42 | 259 ++++++++++++++++++ ...nNonJvmSourceRootConverterProvider.kt.as42 | 227 +++++++++++++++ 3 files changed, 541 insertions(+) create mode 100644 compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreApplicationEnvironment.java.as42 create mode 100644 idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt.as42 create mode 100644 idea/src/org/jetbrains/kotlin/idea/roots/KotlinNonJvmSourceRootConverterProvider.kt.as42 diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreApplicationEnvironment.java.as42 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreApplicationEnvironment.java.as42 new file mode 100644 index 00000000000..4394e43dfbf --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreApplicationEnvironment.java.as42 @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2019 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.kotlin.cli.jvm.compiler; + +import com.intellij.DynamicBundle; +import com.intellij.codeInsight.ContainerProvider; +import com.intellij.codeInsight.runner.JavaMainMethodProvider; +import com.intellij.core.JavaCoreApplicationEnvironment; +import com.intellij.lang.MetaLanguage; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.vfs.VirtualFileSystem; +import com.intellij.psi.FileContextProvider; +import com.intellij.psi.augment.PsiAugmentProvider; +import com.intellij.psi.compiled.ClassFileDecompilers; +import com.intellij.psi.meta.MetaDataContributor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem; + +public class KotlinCoreApplicationEnvironment extends JavaCoreApplicationEnvironment { + public static KotlinCoreApplicationEnvironment create(@NotNull Disposable parentDisposable, boolean unitTestMode) { + KotlinCoreApplicationEnvironment environment = new KotlinCoreApplicationEnvironment(parentDisposable, unitTestMode); + registerExtensionPoints(); + return environment; + } + + private KotlinCoreApplicationEnvironment(@NotNull Disposable parentDisposable, boolean unitTestMode) { + super(parentDisposable, unitTestMode); + } + + private static void registerExtensionPoints() { + registerApplicationExtensionPoint(DynamicBundle.LanguageBundleEP.EP_NAME, DynamicBundle.LanguageBundleEP.class); + registerApplicationExtensionPoint(FileContextProvider.EP_NAME, FileContextProvider.class); + + registerApplicationExtensionPoint(MetaDataContributor.EP_NAME, MetaDataContributor.class); + registerApplicationExtensionPoint(PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class); + registerApplicationExtensionPoint(JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class); + + registerApplicationExtensionPoint(ContainerProvider.EP_NAME, ContainerProvider.class); + registerApplicationExtensionPoint(ClassFileDecompilers.getInstance().EP_NAME, ClassFileDecompilers.Decompiler.class); + + registerApplicationExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class); + + IdeaExtensionPoints.INSTANCE.registerVersionSpecificAppExtensionPoints(Extensions.getRootArea()); + } + + @Nullable + @Override + protected VirtualFileSystem createJrtFileSystem() { + return new CoreJrtFileSystem(); + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt.as42 b/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt.as42 new file mode 100644 index 00000000000..5d79bca2808 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt.as42 @@ -0,0 +1,259 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.actions + +import com.intellij.ide.actions.CreateFileFromTemplateAction +import com.intellij.ide.actions.CreateFileFromTemplateDialog +import com.intellij.ide.actions.CreateFromTemplateAction +import com.intellij.ide.fileTemplates.FileTemplate +import com.intellij.ide.fileTemplates.FileTemplateManager +import com.intellij.ide.fileTemplates.actions.AttributesDefaults +import com.intellij.ide.fileTemplates.ui.CreateFromTemplateDialog +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.actionSystem.LangDataKeys +import com.intellij.openapi.actionSystem.PlatformDataKeys +import com.intellij.openapi.editor.LogicalPosition +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.ui.InputValidatorEx +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiFile +import com.intellij.util.IncorrectOperationException +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.KotlinIcons +import org.jetbrains.kotlin.idea.statistics.FUSEventGroups +import org.jetbrains.kotlin.idea.statistics.KotlinFUSLogger +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.parsing.KotlinParserDefinition.Companion.STD_SCRIPT_SUFFIX +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import java.util.* + +class NewKotlinFileAction : CreateFileFromTemplateAction( + KotlinBundle.message("action.new.file.text"), + KotlinBundle.message("action.new.file.description"), + KotlinFileType.INSTANCE.icon +), DumbAware { + override fun postProcess(createdElement: PsiFile, templateName: String?, customProperties: Map?) { + super.postProcess(createdElement, templateName, customProperties) + + val module = ModuleUtilCore.findModuleForPsiElement(createdElement!!) + + if (createdElement is KtFile) { + if (module != null) { + for (hook in NewKotlinFileHook.EP_NAME.extensions) { + hook.postProcess(createdElement, module) + } + } + + val ktClass = createdElement.declarations.singleOrNull() as? KtNamedDeclaration + if (ktClass != null) { + CreateFromTemplateAction.moveCaretAfterNameIdentifier(ktClass) + } else { + val editor = FileEditorManager.getInstance(createdElement.project).selectedTextEditor ?: return + if (editor.document == createdElement.viewProvider.document) { + val lineCount = editor.document.lineCount + if (lineCount > 0) { + editor.caretModel.moveToLogicalPosition(LogicalPosition(lineCount - 1, 0)) + } + } + } + } + } + + override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) { + builder.setTitle(KotlinBundle.message("action.new.file.dialog.title")) + .addKind( + KotlinBundle.message("action.new.file.dialog.file.title"), + KotlinFileType.INSTANCE.icon, + "Kotlin File" + ) + .addKind( + KotlinBundle.message("action.new.file.dialog.class.title"), + KotlinIcons.CLASS, + "Kotlin Class" + ) + .addKind( + KotlinBundle.message("action.new.file.dialog.interface.title"), + KotlinIcons.INTERFACE, + "Kotlin Interface" + ) + .addKind( + KotlinBundle.message("action.new.file.dialog.enum.title"), + KotlinIcons.ENUM, + "Kotlin Enum" + ) + .addKind( + KotlinBundle.message("action.new.file.dialog.object.title"), + KotlinIcons.OBJECT, + "Kotlin Object" + ) + + builder.setValidator(NameValidator) + } + + override fun getActionName(directory: PsiDirectory, newName: String, templateName: String): String = + KotlinBundle.message("action.new.file.text") + + override fun isAvailable(dataContext: DataContext): Boolean { + if (super.isAvailable(dataContext)) { + val ideView = LangDataKeys.IDE_VIEW.getData(dataContext)!! + val project = PlatformDataKeys.PROJECT.getData(dataContext)!! + val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex + return ideView.directories.any { projectFileIndex.isInSourceContent(it.virtualFile) } + } + + return false + } + + override fun hashCode(): Int = 0 + + override fun equals(other: Any?): Boolean = other is NewKotlinFileAction + + override fun startInWriteAction() = false + + override fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory) = + createFileFromTemplateWithStat(name, template, dir) + + companion object { + private object NameValidator : InputValidatorEx { + override fun getErrorText(inputString: String): String? { + if (inputString.trim().isEmpty()) { + return KotlinBundle.message("action.new.file.error.empty.name") + } + + val parts: List = inputString.split(*FQNAME_SEPARATORS) + if (parts.any { it.trim().isEmpty() }) { + return KotlinBundle.message("action.new.file.error.empty.name.part") + } + + return null + } + + override fun checkInput(inputString: String): Boolean = true + + override fun canClose(inputString: String): Boolean = getErrorText(inputString) == null + } + + @get:TestOnly + val nameValidator: InputValidatorEx + get() = NameValidator + + private fun findOrCreateTarget(dir: PsiDirectory, name: String, directorySeparators: CharArray): Pair { + var className = removeKotlinExtensionIfPresent(name) + var targetDir = dir + + for (splitChar in directorySeparators) { + if (splitChar in className) { + val names = className.trim().split(splitChar) + + for (dirName in names.dropLast(1)) { + targetDir = targetDir.findSubdirectory(dirName) ?: runWriteAction { + targetDir.createSubdirectory(dirName) + } + } + + className = names.last() + break + } + } + return Pair(className, targetDir) + } + + private fun removeKotlinExtensionIfPresent(name: String): String = when { + name.endsWith(".$KOTLIN_WORKSHEET_EXTENSION") -> name.removeSuffix(".$KOTLIN_WORKSHEET_EXTENSION") + name.endsWith(".$STD_SCRIPT_SUFFIX") -> name.removeSuffix(".$STD_SCRIPT_SUFFIX") + name.endsWith(".${KotlinFileType.EXTENSION}") -> name.removeSuffix(".${KotlinFileType.EXTENSION}") + else -> name + } + + private fun createFromTemplate(dir: PsiDirectory, className: String, template: FileTemplate): PsiFile? { + val project = dir.project + val defaultProperties = FileTemplateManager.getInstance(project).defaultProperties + + val properties = Properties(defaultProperties) + + val element = try { + CreateFromTemplateDialog( + project, dir, template, + AttributesDefaults(className).withFixedName(true), + properties + ).create() + } catch (e: IncorrectOperationException) { + throw e + } catch (e: Exception) { + LOG.error(e) + return null + } + + return element?.containingFile + } + + private val FILE_SEPARATORS = charArrayOf('/', '\\') + private val FQNAME_SEPARATORS = charArrayOf('/', '\\', '.') + + fun createFileFromTemplateWithStat(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? { + KotlinFUSLogger.log(FUSEventGroups.NewFileTemplate, template.name) + return createFileFromTemplate(name, template, dir) + } + + + fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? { + val directorySeparators = when (template.name) { + "Kotlin File" -> FILE_SEPARATORS + else -> FQNAME_SEPARATORS + } + val (className, targetDir) = findOrCreateTarget(dir, name, directorySeparators) + + val service = DumbService.getInstance(dir.project) + service.isAlternativeResolveEnabled = true + try { + val psiFile = createFromTemplate(targetDir, className, template) + if (psiFile is KtFile) { + val singleClass = psiFile.declarations.singleOrNull() as? KtClass + if (singleClass != null && !singleClass.isEnum() && !singleClass.isInterface() && name.contains("Abstract")) { + runWriteAction { + singleClass.addModifier(KtTokens.ABSTRACT_KEYWORD) + } + } + } + return psiFile + } finally { + service.isAlternativeResolveEnabled = false + } + } + } +} + +abstract class NewKotlinFileHook { + companion object { + val EP_NAME: ExtensionPointName = + ExtensionPointName.create("org.jetbrains.kotlin.newFileHook") + } + + abstract fun postProcess(createdElement: KtFile, module: Module) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/roots/KotlinNonJvmSourceRootConverterProvider.kt.as42 b/idea/src/org/jetbrains/kotlin/idea/roots/KotlinNonJvmSourceRootConverterProvider.kt.as42 new file mode 100644 index 00000000000..221eb010401 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/roots/KotlinNonJvmSourceRootConverterProvider.kt.as42 @@ -0,0 +1,227 @@ +/* + * Copyright 2010-2019 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.kotlin.idea.roots + +import com.intellij.conversion.* +import com.intellij.conversion.impl.ConversionContextImpl +import com.intellij.conversion.impl.ModuleSettingsImpl +import com.intellij.openapi.roots.ExternalProjectSystemRegistry +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.impl.ContentEntryImpl +import com.intellij.openapi.roots.impl.SourceFolderImpl +import com.intellij.openapi.roots.impl.libraries.ApplicationLibraryTable +import com.intellij.openapi.roots.impl.libraries.LibraryEx +import com.intellij.openapi.roots.impl.libraries.LibraryImpl +import com.intellij.openapi.roots.libraries.Library +import com.intellij.openapi.roots.libraries.LibraryKind +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar +import com.intellij.openapi.roots.libraries.PersistentLibraryKind +import com.intellij.openapi.vfs.JarFileSystem +import com.intellij.openapi.vfs.VirtualFile +import org.jdom.Element +import org.jetbrains.jps.model.JpsElement +import org.jetbrains.jps.model.JpsElementFactory +import org.jetbrains.jps.model.java.JavaResourceRootType +import org.jetbrains.jps.model.java.JavaSourceRootType +import org.jetbrains.jps.model.module.JpsModuleSourceRootType +import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot +import org.jetbrains.jps.model.serialization.facet.JpsFacetSerializer +import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer +import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer.* +import org.jetbrains.kotlin.config.getFacetPlatformByConfigurationElement +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.core.util.toVirtualFile +import org.jetbrains.kotlin.idea.facet.KotlinFacetType +import org.jetbrains.kotlin.idea.framework.JavaRuntimeDetectionUtil +import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil +import org.jetbrains.kotlin.idea.framework.getLibraryJar +import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.isCommon +import org.jetbrains.kotlin.platform.js.JsPlatforms +import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.platform.jvm.isJvm +import org.jetbrains.kotlin.utils.PathUtil + +class KotlinNonJvmSourceRootConverterProvider : ConverterProvider("kotlin-non-jvm-source-roots") { + companion object { + private val rootTypesToMigrate: List> = listOf( + JavaSourceRootType.SOURCE, + JavaSourceRootType.TEST_SOURCE, + JavaResourceRootType.RESOURCE, + JavaResourceRootType.TEST_RESOURCE + ) + + // TODO(dsavvinov): review how it behaves in HMPP environment + private val PLATFORM_TO_STDLIB_DETECTORS: Map) -> Boolean> = mapOf( + JvmPlatforms.unspecifiedJvmPlatform to { roots: Array -> JavaRuntimeDetectionUtil.getRuntimeJar(roots.toList()) != null }, + JsPlatforms.defaultJsPlatform to { roots: Array -> JsLibraryStdDetectionUtil.getJsStdLibJar(roots.toList()) != null }, + CommonPlatforms.defaultCommonPlatform to { roots: Array -> + getLibraryJar(roots, PathUtil.KOTLIN_STDLIB_COMMON_JAR_PATTERN) != null + } + ) + } + + sealed class LibInfo { + class ByXml( + private val element: Element, + private val conversionContext: ConversionContext, + private val moduleSettings: ModuleSettings + ) : LibInfo() { + override val explicitKind: PersistentLibraryKind<*>? + get() = LibraryKind.findById(element.getAttributeValue("type")) as? PersistentLibraryKind<*> + + override fun getRoots(): Array { + val contextImpl = conversionContext as? ConversionContextImpl ?: return VirtualFile.EMPTY_ARRAY + val moduleSettingsImpl = moduleSettings as? ModuleSettingsImpl ?: return VirtualFile.EMPTY_ARRAY + return contextImpl + .getClassRoots(element, moduleSettingsImpl) + .mapNotNull { it.toVirtualFile()?.let { file -> JarFileSystem.getInstance().getJarRootForLocalFile(file) } } + .toTypedArray() + } + } + + class ByLibrary(private val library: Library) : LibInfo() { + override val explicitKind: PersistentLibraryKind<*>? + get() = (library as? LibraryEx)?.kind + + override fun getRoots(): Array = library.getFiles(OrderRootType.CLASSES) + } + + abstract val explicitKind: PersistentLibraryKind<*>? + abstract fun getRoots(): Array + + val stdlibPlatform: TargetPlatform? by lazy { + val roots = getRoots() + for ((platform, detector) in PLATFORM_TO_STDLIB_DETECTORS) { + if (detector.invoke(roots)) { + return@lazy platform + } + } + + return@lazy null + } + } + + class ConverterImpl(private val context: ConversionContext) : ProjectConverter() { + private val projectLibrariesByName by lazy { + context.projectLibrariesSettings.projectLibraries.groupBy { it.getAttributeValue(JpsLibraryTableSerializer.NAME_ATTRIBUTE) } + } + + private fun findGlobalLibrary(name: String) = ApplicationLibraryTable.getApplicationTable().getLibraryByName(name) + + private fun findProjectLibrary(name: String) = projectLibrariesByName[name]?.firstOrNull() + + private fun createLibInfo(orderEntryElement: Element, moduleSettings: ModuleSettings): LibInfo? { + return when (orderEntryElement.getAttributeValue("type")) { + MODULE_LIBRARY_TYPE -> { + orderEntryElement.getChild(LIBRARY_TAG)?.let { LibInfo.ByXml(it, context, moduleSettings) } + } + + LIBRARY_TYPE -> { + val libraryName = orderEntryElement.getAttributeValue(NAME_ATTRIBUTE) ?: return null + when (orderEntryElement.getAttributeValue(LEVEL_ATTRIBUTE)) { + LibraryTablesRegistrar.PROJECT_LEVEL -> + findProjectLibrary(libraryName)?.let { LibInfo.ByXml(it, context, moduleSettings) } + LibraryTablesRegistrar.APPLICATION_LEVEL -> + findGlobalLibrary(libraryName)?.let { LibInfo.ByLibrary(it) } + else -> + null + } + } + + else -> null + } + } + + override fun createModuleFileConverter(): ConversionProcessor { + return object : ConversionProcessor() { + private fun ModuleSettings.detectPlatformByFacet() = + getFacetElement(KotlinFacetType.ID) + ?.getChild(JpsFacetSerializer.CONFIGURATION_TAG) + ?.getFacetPlatformByConfigurationElement() + + + private fun ModuleSettings.detectPlatformByDependencies(): TargetPlatform? { + var hasCommonStdlib = false + + orderEntries + .asSequence() + .mapNotNull { createLibInfo(it, this) } + .forEach { + val stdlibPlatform = it.stdlibPlatform + if (stdlibPlatform != null) { + if (stdlibPlatform.isCommon()) { + hasCommonStdlib = true + } else { + return stdlibPlatform + } + } + } + + return if (hasCommonStdlib) CommonPlatforms.defaultCommonPlatform else null + } + + private fun ModuleSettings.detectPlatform(): TargetPlatform { + return detectPlatformByFacet() + ?: detectPlatformByDependencies() + ?: JvmPlatforms.unspecifiedJvmPlatform + } + + private fun ModuleSettings.getSourceFolderElements(): List { + val rootManagerElement = getComponentElement(ModuleSettings.MODULE_ROOT_MANAGER_COMPONENT) ?: return emptyList() + return rootManagerElement + .getChildren(ContentEntryImpl.ELEMENT_NAME) + .flatMap { it.getChildren(SourceFolderImpl.ELEMENT_NAME) } + } + + @Suppress("UnstableApiUsage") + private fun ModuleSettings.isExternalModule(): Boolean { + return when { + rootElement.getAttributeValue(ExternalProjectSystemRegistry.EXTERNAL_SYSTEM_ID_KEY) != null -> true + rootElement.getAttributeValue(ExternalProjectSystemRegistry.IS_MAVEN_MODULE_KEY)?.toBoolean() ?: false -> true + else -> false + } + } + + override fun isConversionNeeded(settings: ModuleSettings): Boolean { + if (settings.isExternalModule()) return false + + val hasMigrationRoots = settings.getSourceFolderElements().any { + loadSourceRoot(it).rootType in rootTypesToMigrate + } + if (!hasMigrationRoots) { + return false + } + + val targetPlatform = settings.detectPlatform() + return (!targetPlatform.isJvm()) + } + + override fun process(settings: ModuleSettings) { + for (sourceFolder in settings.getSourceFolderElements()) { + val contentRoot = sourceFolder.parent as? Element ?: continue + val oldSourceRoot = loadSourceRoot(sourceFolder) + val url = sourceFolder.getAttributeValue(URL_ATTRIBUTE) + + val (newRootType, data) = oldSourceRoot.getMigratedSourceRootTypeWithProperties() ?: continue + @Suppress("UNCHECKED_CAST") + val newSourceRoot = JpsElementFactory.getInstance().createModuleSourceRoot(url, newRootType, data) + as? JpsTypedModuleSourceRoot ?: continue + + contentRoot.removeContent(sourceFolder) + saveSourceRoot(contentRoot, url, newSourceRoot) + } + } + } + } + } + + override fun getConversionDescription() = + KotlinBundle.message("roots.description.text.update.source.roots.for.non.jvm.modules.in.kotlin.project") + + override fun createConverter(context: ConversionContext) = ConverterImpl(context) +} \ No newline at end of file