All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.lang.kotlin.KotlinCompilerHelper.kt Maven / Gradle / Ivy

There is a newer version: 4.0.0-milestone4
Show newest version
/*
 * Copyright 2019 Red Hat, Inc.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * The Apache License v2.0 is available at
 * http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 */
package io.vertx.lang.kotlin

import org.jetbrains.kotlin.cli.common.*
import org.jetbrains.kotlin.cli.common.config.*
import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.*
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.*
import org.jetbrains.kotlin.com.intellij.openapi.*
import org.jetbrains.kotlin.com.intellij.openapi.util.*
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
import java.io.*
import java.net.*
import java.nio.file.*
import java.util.*
import java.util.jar.*

/**
 * Author: Sergey Mashkov
 */
object KotlinCompilerHelper {
  fun compileKotlinScript(classLoader: ClassLoader,
                          scriptMode: Boolean,
                          url: URL,
                          predicate: (GenerationState, ClassDescriptor) -> Boolean
  ): Map, ClassDescriptor> {

    setIdeaIoUseFallback()

    val configuration = CompilerConfiguration()
    val printingMessageCollector = PrintingMessageCollector(System.err, MessageRenderer.WITHOUT_PATHS, false)
    configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, printingMessageCollector)
    configuration.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, false)

    configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT)

    configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
    configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
    configuration.put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, true)
    configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME)
    configuration.add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar())

    if (scriptMode) {
      configuration.put(CommonConfigurationKeys.MODULE_NAME, "dynamic")
      configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, VerticleScriptDefinition)
    }

    val classPath = (
        classLoader.classPath()
            + ClassLoader.getSystemClassLoader().classPath()
            + (Thread.currentThread().contextClassLoader?.classPath() ?: emptyList())
            + propertyClassPath("java.class.path")
            + propertyClassPath("sun.boot.class.path")
        ).distinct().filter { Files.exists(it) }

    for (item in classPath) {
      configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(item.toFile()))
    }
    configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, KotlinSourceRoot(Paths.get(url.toURI()).toString(), false))

    val collected = HashMap()

    val environment = KotlinCoreEnvironment.createForProduction(Disposable { }, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
    val finalState = KotlinToJVMBytecodeCompiler.analyzeAndGenerate(environment)

    if (printingMessageCollector.hasErrors()) {
      throw CompilationException("Compilation failed", null, null)
    }
    if (finalState == null) {
      return emptyMap()
    }

    val compilerClassLoader = GeneratedClassLoader(finalState.factory, classLoader)

    return finalState.factory.getClassFiles().toList()
        .map { it.relativePath.removeSuffix(".class").replace("/", ".") }
        .filter { it !in collected }
        .mapNotNull { finalState.bindingContext.get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, FqNameUnsafe(it.replace("$", "."))) }
        .filter { predicate(finalState, it) }
        .associateBy { finalState.typeMapper.mapClass(it).className }
        .mapKeys { compilerClassLoader.loadClass(it.key) }
  }

  private fun ClassLoader.classPath() = (classPathImpl() + manifestClassPath()).distinct()

  private fun ClassLoader.classPathImpl(): List {
    val parentUrls = parent?.classPathImpl() ?: emptyList()

    return when {
      this is URLClassLoader -> urLs.filterNotNull().map(URL::toURI).mapNotNull { ifFailed(null) { Paths.get(it) } } + parentUrls
      else -> parentUrls
    }
  }

  private fun ClassLoader.manifestClassPath() =
      getResources("META-INF/MANIFEST.MF")
          .asSequence()
          .mapNotNull { ifFailed(null) { it.openStream().use { Manifest().apply { read(it) } } } }
          .flatMap { it.mainAttributes?.getValue("Class-Path")?.splitToSequence(" ")?.filter(String::isNotBlank) ?: emptySequence() }
          .mapNotNull { ifFailed(null) { Paths.get(URI.create(it)) } }
          .toList()

  private fun propertyClassPath(key: String) = System.getProperty(key)
      ?.split(File.pathSeparator)
      ?.filter(String::isNotEmpty)
      ?.map { Paths.get(it) }
      ?: emptyList()

  private inline fun  ifFailed(default: R, block: () -> R) = try {
    block()
  } catch (t: Throwable) {
    default
  }

  private fun setIdeaIoUseFallback() {
    if (SystemInfo.isWindows) {
      val properties = System.getProperties()

      properties.setProperty("idea.io.use.nio2", java.lang.Boolean.TRUE.toString())

      if (!(SystemInfo.isJavaVersionAtLeast("1.7") && "1.7.0-ea" != SystemInfo.JAVA_VERSION)) {
        properties.setProperty("idea.io.use.fallback", java.lang.Boolean.TRUE.toString())
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy