diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 19a17e6c5c4..f293a95a62c 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -12,77 +12,85 @@ import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.JVMConfigurationKeys.ADD_BUILT_INS_FROM_COMPILER_TO_DEPENDENCIES -import org.jetbrains.kotlin.config.JVMConfigurationKeys.CREATE_BUILT_INS_FROM_MODULE_DEPENDENCIES import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.addKotlinSourceRoots import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor -import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator -import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM +import org.jetbrains.kotlin.psi.KtFile import java.lang.System.out import java.util.* -class NativeAnalyzer(val environment: KotlinCoreEnvironment) : - AnalyzerWithCompilerReport.Analyzer { - override fun analyze(): AnalysisResult { - val sharedTrace = - CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace() +class NativeAnalyzer( + val environment: KotlinCoreEnvironment, + val sources: Collection, + val config: KonanConfig) : AnalyzerWithCompilerReport.Analyzer { - return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration( - environment.project, - environment.getSourceFiles(), - sharedTrace, - environment.configuration.apply { - put(ADD_BUILT_INS_FROM_COMPILER_TO_DEPENDENCIES, true) - put(CREATE_BUILT_INS_FROM_MODULE_DEPENDENCIES, true) - }, - { scope -> JvmPackagePartProvider(environment, scope) } - ) - } + override fun analyze(): AnalysisResult { + return TopDownAnalyzerFacadeForKonan.analyzeFiles(sources, config); + } - override fun reportEnvironmentErrors() { - } + override fun reportEnvironmentErrors() { + } } -class K2Native : CLICompiler() { - override fun doExecute(arguments : K2NativeCompilerArguments, - configuration : CompilerConfiguration, - rootDisposable: Disposable): ExitCode { - configuration.put(CommonConfigurationKeys.MODULE_NAME, - JvmAbi.DEFAULT_MODULE_NAME) - configuration.addKotlinSourceRoots(arguments.freeArgs) - // TODO: add to source set, once we know how to not compile them. - configuration.addKotlinSourceRoots(arguments.headers.asList()) - val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, - configuration, Arrays.asList("extensions/common.xml")) +class K2Native : CLICompiler() { - val collector = configuration.getNotNull( - CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) - val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector) + val defaultModuleName = "main"; + + override fun doExecute(arguments : K2NativeCompilerArguments, + configuration : CompilerConfiguration, + rootDisposable: Disposable + ): ExitCode { - // Build AST and binding info. - analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles(), - NativeAnalyzer(environment)) + configuration.put(CommonConfigurationKeys.MODULE_NAME, defaultModuleName ) - // Translate AST to high level IR. - val translator = Psi2IrTranslator(Psi2IrConfiguration(false)) - val module = translator.generateModule( - analyzerWithCompilerReport.analysisResult.moduleDescriptor, - environment.getSourceFiles(), - analyzerWithCompilerReport.analysisResult.bindingContext) + configuration.addKotlinSourceRoots(arguments.freeArgs) - // Emit LLVM code. - module.accept(DumpIrTreeVisitor(out), "") - emitLLVM(module, arguments.runtimeFile, arguments.outputFile) - return ExitCode.OK - } + + // TODO: remove, once linker is available. + if (arguments.headers != null) { + configuration.addKotlinSourceRoots(arguments.headers.asList()) + } + + // As of now we don't have any library mechnanism + var libraryFiles = listOf() + configuration.put(KonanConfigurationKeys.LIBRARY_FILES, libraryFiles) + + val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, + configuration, Arrays.asList("extensions/common.xml")) + + val collector = configuration.getNotNull( + CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) + + val analyzerWithCompilerReport = AnalyzerWithCompilerReport(collector) + + val project = environment.project + val config = KonanConfig(project, configuration) + + + // Build AST and binding info. + analyzerWithCompilerReport.analyzeAndReport(environment.getSourceFiles(), + NativeAnalyzer(environment, environment.getSourceFiles(), config)) + + // Translate AST to high level IR. + val translator = Psi2IrTranslator(Psi2IrConfiguration(false)) + val module = translator.generateModule( + analyzerWithCompilerReport.analysisResult.moduleDescriptor, + environment.getSourceFiles(), + analyzerWithCompilerReport.analysisResult.bindingContext) + + // Emit LLVM code. + module.accept(DumpIrTreeVisitor(out), "") + emitLLVM(module, arguments.runtimeFile, arguments.outputFile) + + return ExitCode.OK + } override fun setupPlatformSpecificArgumentsAndServices( - configuration: CompilerConfiguration, - arguments : K2NativeCompilerArguments, - services : Services) {} + configuration: CompilerConfiguration, + arguments : K2NativeCompilerArguments, + services : Services) {} override fun createArguments(): K2NativeCompilerArguments { return K2NativeCompilerArguments() @@ -95,3 +103,4 @@ class K2Native : CLICompiler() { } } fun main(args: Array) = K2Native.main(args) + diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanConfig.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanConfig.kt new file mode 100644 index 00000000000..21e6e73c5ec --- /dev/null +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanConfig.kt @@ -0,0 +1,90 @@ +package org.jetbrains.kotlin.cli.bc + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.descriptors.PackageFragmentProvider +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration +import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor +import org.jetbrains.kotlin.serialization.js.KotlinJavascriptSerializationUtil +import org.jetbrains.kotlin.serialization.js.ModuleKind +import org.jetbrains.kotlin.storage.LockBasedStorageManager + +import org.jetbrains.kotlin.backend.konan.llvm.KotlinKonanMetadata +import org.jetbrains.kotlin.backend.konan.llvm.KotlinKonanMetadataUtils + +/** + * Base class representing a configuration of translator. + */ +class KonanConfig(val project: Project, val configuration: CompilerConfiguration) { + private val storageManager = LockBasedStorageManager() + + private val sourceFilesFromLibraries = listOf() + private var metadata = listOf() + internal var moduleDescriptors: MutableList>? = null + + init { + val libraries = configuration.getList(KonanConfigurationKeys.LIBRARY_FILES) + metadata = KotlinKonanMetadataUtils.loadLibMetadata(libraries) + } + + val moduleId: String + get() = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME) + + val moduleKind: ModuleKind + get() = configuration.get(KonanConfigurationKeys.MODULE_KIND)!! + + fun getModuleDescriptors(): MutableList> { + if (moduleDescriptors != null) return moduleDescriptors!! + + moduleDescriptors = mutableListOf>() + val kotlinModuleDescriptors = mutableListOf() + for (metadataEntry in metadata) { + val descriptor = createModuleDescriptor(metadataEntry) + moduleDescriptors!!.add(descriptor) + kotlinModuleDescriptors.add(descriptor.data) + } + + for (module in moduleDescriptors!!) { + setDependencies(module.data, kotlinModuleDescriptors) + } + + return moduleDescriptors!! + } + + + // We reuse JsModuleDescriptor for serialization for now, as we haven't got one for Konan yet. + private fun createModuleDescriptor(metadata: KotlinKonanMetadata): JsModuleDescriptor { + + val moduleDescriptor = ModuleDescriptorImpl( + Name.special("<" + metadata.moduleName + ">"), storageManager, KonanPlatform.builtIns) + + val rawDescriptor = KotlinJavascriptSerializationUtil.readModule( + metadata.body, storageManager, moduleDescriptor, CompilerDeserializationConfiguration( + configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT))) + + val provider = rawDescriptor.data + moduleDescriptor.initialize(provider ?: PackageFragmentProvider.Empty) + + return rawDescriptor.copy(moduleDescriptor) + } + + companion object { + private fun setDependencies(module: ModuleDescriptorImpl, modules: List) { + module.setDependencies(modules.plus(KonanPlatform.builtIns.builtInsModule)) + } + + fun withJsLibAdded(files: Collection, config: KonanConfig): Collection { + val allFiles = mutableListOf() + allFiles.addAll(files) + // We don't store source files in the bitcode for Konan, dont't we? + //allFiles.addAll(config.getSourceFilesFromLibraries()); + return allFiles + } + } + +} diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanConfigurationKeys.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanConfigurationKeys.kt new file mode 100644 index 00000000000..6bf708bada3 --- /dev/null +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanConfigurationKeys.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlin.cli.bc; + +import org.jetbrains.kotlin.config.CompilerConfigurationKey; +import org.jetbrains.kotlin.serialization.js.ModuleKind; + +class KonanConfigurationKeys { + companion object { + val LIBRARY_FILES: CompilerConfigurationKey> + = CompilerConfigurationKey.create("library file paths"); + val SOURCE_MAP: CompilerConfigurationKey> + = CompilerConfigurationKey.create("generate source map"); + val META_INFO: CompilerConfigurationKey> + = CompilerConfigurationKey.create("generate metadata"); + val MODULE_KIND: CompilerConfigurationKey + = CompilerConfigurationKey.create("module kind"); + } +} diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanPlatform.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanPlatform.kt new file mode 100644 index 00000000000..df3808b5413 --- /dev/null +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanPlatform.kt @@ -0,0 +1,44 @@ +/* + * 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.cli.bc + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.resolve.PlatformConfigurator +import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.storage.StorageManager +import org.jetbrains.kotlin.storage.LockBasedStorageManager + +class KonanBuiltIns: KotlinBuiltIns { + constructor(storageManager: StorageManager) : super(storageManager) + + constructor(storageManager: StorageManager, withModule: Boolean) : this(storageManager) { + if (withModule) createBuiltInsModule() + } + +} + +object KonanPlatform : TargetPlatform("Konan") { + override val defaultImports: List = Default.defaultImports + listOf( + ImportPath("kotlin.*"), + ImportPath("kotlin.io.*") + ) + + override val platformConfigurator: PlatformConfigurator = KonanPlatformConfigurator + + val builtIns: KonanBuiltIns = KonanBuiltIns(LockBasedStorageManager.NO_LOCKS, false) +} diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanPlatformConfigurator.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanPlatformConfigurator.kt new file mode 100644 index 00000000000..d458a3ccf93 --- /dev/null +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/KonanPlatformConfigurator.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2016 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. + */ + +// Adapted from JS compiler, but everyhing has been switched off for now. + +package org.jetbrains.kotlin.cli.bc + +import org.jetbrains.kotlin.container.StorageComponentContainer +import org.jetbrains.kotlin.container.useImpl +import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.platform.PlatformToKotlinClassMap +import org.jetbrains.kotlin.resolve.IdentifierChecker +import org.jetbrains.kotlin.resolve.OverloadFilter +import org.jetbrains.kotlin.resolve.PlatformConfigurator +import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator +import org.jetbrains.kotlin.resolve.scopes.SyntheticConstructorsProvider +import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes +import org.jetbrains.kotlin.types.DynamicTypesAllowed + + +object KonanPlatformConfigurator : PlatformConfigurator( + DynamicTypesAllowed(), + additionalDeclarationCheckers = listOf(), + additionalCallCheckers = listOf(), + additionalTypeCheckers = listOf(), + additionalClassifierUsageCheckers = listOf(), + additionalAnnotationCheckers = listOf(), + identifierChecker = IdentifierChecker.DEFAULT, + overloadFilter = OverloadFilter.DEFAULT, + platformToKotlinClassMap = PlatformToKotlinClassMap.EMPTY +) { + override fun configureModuleComponents(container: StorageComponentContainer) { + container.useInstance(SyntheticScopes.Empty) + container.useInstance(SyntheticConstructorsProvider.Empty) + container.useInstance(TypeSpecificityComparator.NONE) + + } +} diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/TopDownAnalyzerFacadeForKonan.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/TopDownAnalyzerFacadeForKonan.kt new file mode 100644 index 00000000000..060024fe198 --- /dev/null +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/TopDownAnalyzerFacadeForKonan.kt @@ -0,0 +1,75 @@ +/* + * 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.cli.bc + +import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl +import org.jetbrains.kotlin.context.ContextForNewModule +import org.jetbrains.kotlin.context.ModuleContext +import org.jetbrains.kotlin.context.ProjectContext +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.* +import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory + +object TopDownAnalyzerFacadeForKonan { + fun analyzeFiles(files: Collection, config: KonanConfig): AnalysisResult { + val context = ContextForNewModule(ProjectContext(config.project), Name.special("<${config.moduleId}>"), KonanPlatform.builtIns) + + val builtinsForCompilerModule = KonanBuiltIns(context.storageManager, true) + val compilerBuiltInsModule = builtinsForCompilerModule.builtInsModule + KonanPlatform.builtIns.builtInsModule = context.module + + // Make sure the compiler produced BuiltIns module comes in last + context.setDependencies( + listOf(context.module) + + config.getModuleDescriptors().map { it.data } + + listOf(compilerBuiltInsModule) + ) + return analyzeFilesWithGivenTrace(files, BindingTraceContext(), context, config) + } + + fun analyzeFilesWithGivenTrace( + files: Collection, + trace: BindingTrace, + moduleContext: ModuleContext, + config: KonanConfig + ): AnalysisResult { + + val allFiles = KonanConfig.withJsLibAdded(files, config) + + // we print out each file we compile for now + allFiles.forEach{println(it)} + + val analyzerForKonan = createTopDownAnalyzerForKonan( + moduleContext, trace, + FileBasedDeclarationProviderFactory(moduleContext.storageManager, allFiles), + config.configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT) + ) + + analyzerForKonan.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files) + return AnalysisResult.success(trace.bindingContext, moduleContext.module) + } + + fun checkForErrors(allFiles: Collection, bindingContext: BindingContext) { + AnalyzingUtils.throwExceptionOnErrors(bindingContext) + for (file in allFiles) { + AnalyzingUtils.checkForSyntacticErrors(file) + } + } +} diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/injection.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/injection.kt new file mode 100644 index 00000000000..acf5fe114bb --- /dev/null +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/injection.kt @@ -0,0 +1,57 @@ +/* + * 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.cli.bc + +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.container.get +import org.jetbrains.kotlin.container.useImpl +import org.jetbrains.kotlin.container.useInstance +import org.jetbrains.kotlin.context.ModuleContext +import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl +import org.jetbrains.kotlin.frontend.di.configureModule +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer +import org.jetbrains.kotlin.resolve.createContainer +import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl +import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer +import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory + +fun createTopDownAnalyzerForKonan( + moduleContext: ModuleContext, + bindingTrace: BindingTrace, + declarationProviderFactory: DeclarationProviderFactory, + languageVersionSettings: LanguageVersionSettings +): LazyTopDownAnalyzer { + val storageComponentContainer = createContainer("TopDownAnalyzerForKonan", KonanPlatform) { + configureModule(moduleContext, KonanPlatform, bindingTrace) + + useInstance(declarationProviderFactory) + useImpl() + + CompilerEnvironment.configure(this) + + useInstance(LookupTracker.DO_NOTHING) + useInstance(languageVersionSettings) + useImpl() + useImpl() + }.apply { + get().initialize(get().packageFragmentProvider) + } + return storageComponentContainer.get() +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinKonanMetadataUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinKonanMetadataUtils.kt new file mode 100644 index 00000000000..b3129d9e2ba --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinKonanMetadataUtils.kt @@ -0,0 +1,117 @@ +/* + * 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. + */ + + // this file has been adapted from the corresponding piece of Javascript compiler + +package org.jetbrains.kotlin.backend.konan.llvm + +import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion +import java.io.File +import javax.xml.bind.DatatypeConverter.parseBase64Binary +import javax.xml.bind.DatatypeConverter.printBase64Binary + + +class KotlinKonanMetadata(val abiVersion: Int, val moduleName: String, val body: ByteArray) { + val isAbiVersionCompatible: Boolean = KotlinKonanMetadataUtils.isAbiVersionCompatible(abiVersion) +} + +class JsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) { + override fun isCompatible() = this.isCompatibleTo(INSTANCE) + + companion object { + val INSTANCE = JsBinaryVersion(0, 5, 0) + + val INVALID_VERSION = JsBinaryVersion() + } +} + +object KotlinKonanMetadataUtils { + private val KOTLIN_JAVASCRIPT_METHOD_NAME = "kotlin_module_metadata" + private val KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN = "\\.kotlin_module_metadata\\(".toPattern() + + /** + * Matches string like .kotlin_module_metadata(, , ) + */ + private val METADATA_PATTERN = "(?m)\\w+\\.$KOTLIN_JAVASCRIPT_METHOD_NAME\\((\\d+),\\s*(['\"])([^'\"]*)\\2,\\s*(['\"])([^'\"]*)\\4\\)".toPattern() + + val ABI_VERSION: Int = JsBinaryVersion.INSTANCE.minor + + fun isAbiVersionCompatible(abiVersion: Int): Boolean = abiVersion == ABI_VERSION + + fun hasMetadata(text: String): Boolean = + KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find() && METADATA_PATTERN.matcher(text).find() + + fun formatMetadataAsString(moduleName: String, content: ByteArray): String = + "// Kotlin.$KOTLIN_JAVASCRIPT_METHOD_NAME($ABI_VERSION, \"$moduleName\", \"${printBase64Binary(content)}\");\n" + + fun loadMetadata(file: File): List { + assert(file.exists()) { "Library $file not found" } + val metadataList = arrayListOf() + + // For the time being we don't have a reader, so just produce nothing + //val content = readModuleMetadata(file) + //parseMetadata(content, metadataList) + + return metadataList + } + + fun loadMetadata(path: String): List = loadMetadata(File(path)) + + fun parseMetadata(text: String, metadataList: MutableList) { + // Check for literal pattern first in order to reduce time for large files without metadata + if (!KOTLIN_JAVASCRIPT_METHOD_NAME_PATTERN.matcher(text).find()) return + + val matcher = METADATA_PATTERN.matcher(text) + while (matcher.find()) { + val abiVersion = matcher.group(1).toInt() + val moduleName = matcher.group(3) + val data = matcher.group(5) + metadataList.add(KotlinKonanMetadata(abiVersion, moduleName, parseBase64Binary(data))) + } + } + + fun loadLibMetadata(libraries: List): List { + + val allMetadata = mutableListOf() + + for (path in libraries) { + val filePath = File(path) + if (!filePath.exists()) { + // TODO: should we throw here? + println("Path '" + path + "' does not exist"); + } + + val metadataList = loadMetadata(filePath); + + if (metadataList.isEmpty()) { + // TODO: should we throw here? + println("'" + path + "' is not a valid Kotlin Native library"); + } + + for (metadata in metadataList) { + if (!metadata.isAbiVersionCompatible) { + // TODO: should we throw here? + println("File '" + path + "' was compiled with an incompatible version of Kotlin. " + + "Its ABI version is " + metadata.abiVersion + + ", expected version is " + ABI_VERSION); + } + } + + allMetadata.addAll(metadataList); + } + return allMetadata; + } +}