Compare commits

...

34 Commits

Author SHA1 Message Date
Vadim Brilyantov
d2e62c054c resolve build issues, still problem with pill 2018-11-08 12:35:41 +03:00
Vadim Brilyantov
d76ebaceb8 Migrate daemon usages to new API.
(cherry picked from commit 668ac6a)
2018-11-06 20:50:14 +03:00
Vadim Brilyantov
f83a39af9b Merge with master and resolve version conflicts.
(cherry picked from commit fe77e5a)
2018-11-06 20:50:13 +03:00
Vadim Brilyantov
5e3b00cf0a connection problems resolved 2018-11-06 20:49:44 +03:00
Vadim Brilyantov
56bcd4148c fix gradle cache bug with nativeplatforms (rubbygrapefruit)
(cherry picked from commit ccfe547)
2018-10-26 12:20:00 +03:00
Vadim Brilyantov
2e5b86d86b split Common into new/old modules
(cherry picked from commit ab401c0)
2018-10-26 12:16:54 +03:00
Vadim Brilyantov
de0a5631ae split Client into new/old modules
(cherry picked from commit 7913b84)
2018-10-26 12:02:15 +03:00
Vadim Brilyantov
e4815f82f4 introduce common Daemon Client interface
new common interface for new/old daemon impls

(cherry picked from commit a95e083)
2018-10-25 21:21:04 +03:00
Vadim Brilyantov
25cb96777b fix connection with old daemon version
(cherry picked from commit 7fd4992)
2018-10-25 21:18:20 +03:00
Vadim Brilyantov
e78739ea87 assume arbitrary short messages are Keep Alives
(cherry picked from commit e87cb06)
2018-10-25 21:18:19 +03:00
Vadim Brilyantov
6c657fcb7c fix parallel start test
(cherry picked from commit 30abd78)
2018-10-25 21:18:18 +03:00
Vadim Brilyantov
504730e006 add KeepAlives mechanism to Kotlin Daemon
(cherry picked from commit 7532aae)
2018-10-25 21:18:16 +03:00
Vadim Brilyantov
7970e41ef3 More fixes for daemon concurrent behaviour
fixed lots of bugs faced while testing daemon parallel start and daemon parallel compile

(cherry picked from commit 67a2685)
2018-10-25 21:18:15 +03:00
Vadim Brilyantov
84388f9a43 fix repl with ifAlive reentrant issue
(cherry picked from commit fe90621)
2018-10-25 21:18:13 +03:00
Vadim Brilyantov
1c4e28d97a fix daemon shutdown when unused
(cherry picked from commit 634fa08)
2018-10-25 21:18:12 +03:00
Vadim Brilyantov
9f3717b005 move suspend to public API
(cherry picked from commit 689b071)
2018-10-25 21:18:04 +03:00
Vadim Brilyantov
00a88a5108 fix parallel compile test
(cherry picked from commit 9568003)
2018-10-25 21:17:55 +03:00
Vadim Brilyantov
48995982c1 remove all runBlocking statements possibly leading to a deadlock
(cherry picked from commit 414801b)
2018-10-25 21:17:47 +03:00
Vadim Brilyantov
e73ac646de fix all npe's (possibly in deserialization or repl)
(cherry picked from commit 67301f1)
2018-10-25 21:17:29 +03:00
Vadim Brilyantov
45addd6b79 add kotlin actors for network IO
Actors give an opportunity to deal with some queries to a specific channel sequentually.
That allows to fix bugs with mixing up messages when sending them concurrently

(cherry picked from commit d28da4a)
2018-10-25 21:12:19 +03:00
Vadim Brilyantov
c08e46a69b fix timeout and async bugs in handshakes
(cherry picked from commit ac2ec69)
2018-10-25 21:11:18 +03:00
Vadim Brilyantov
2b68a2ccff add new tests for multiple connections
multiple connections between different types of daemon. New client, new daemon, old client, old daemon --- and their different combinations running simultaneously

(cherry picked from commit 24be522)
2018-10-25 21:11:09 +03:00
Vadim Brilyantov
264ccc944b fix timeout bug after dserialization
(cherry picked from commit df9b066)
2018-10-25 21:10:58 +03:00
Vadim Brilyantov
7b260eb8c1 add serialization tests
(cherry picked from commit b058d43)
2018-10-25 21:10:47 +03:00
Vadim Brilyantov
5add71545f cleenup infrastructural code
(cherry picked from commit 7569e9e)
2018-10-25 21:10:38 +03:00
Vadim Brilyantov
877150fa41 fix new security mechanisms
(cherry picked from commit 19b60ed)
2018-10-25 21:10:37 +03:00
Vadim Brilyantov
825b8a2d96 add handshakes mechanism and connection verification
(cherry picked from commit 8ebe8c2)
2018-10-25 21:10:35 +03:00
Vadim Brilyantov
c19c009597 add timeout for client's authentification
(cherry picked from commit b69b386)
2018-10-25 21:10:34 +03:00
Vadim Brilyantov
06affc61d0 resolve connection problems between all versions of daemon
(cherry picked from commit f273deb)
2018-10-25 21:10:30 +03:00
Vadim Brilyantov
72bc956070 Minor: change all print's to logger usage
(cherry picked from commit 2da6ec4)
2018-10-25 21:10:22 +03:00
Vadim Brilyantov
3ea2d33c88 pass DaemonAPI test
(cherry picked from commit 8508907)
2018-10-25 21:07:52 +03:00
Vadim Brilyantov
2113908fe3 fix all Proguard warnings
(cherry picked from commit 1e16916)
2018-10-25 21:06:17 +03:00
Vadim Brilyantov
258bf4cdc7 fix simple connection fixed, refactor client/server infrastructure
refactor: now autoclosable clients and no delegations in server

(cherry picked from commit 0f29939)
2018-10-25 21:01:31 +03:00
Vadim Brilyantov
501dced308 fix project build, add unit tests and fix small bugs
(cherry picked from commit edd10d3)
2018-10-25 21:01:08 +03:00
242 changed files with 27071 additions and 1709 deletions

19
.idea/libraries/KotlinJavaRuntime.xml generated Normal file
View File

@@ -0,0 +1,19 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime">
<CLASSES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -0,0 +1,15 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime (2)">
<CLASSES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" />
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" />
</SOURCES>
</library>
</component>

7
.idea/misc.xml generated
View File

@@ -45,6 +45,8 @@
</option>
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
<option name="myDefaultNotNull" value="org.jetbrains.annotations.NotNull" />
<option name="myNullables">
<value>
<list size="9">
@@ -82,10 +84,13 @@
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="RustProjectSettings">
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
</component>
<component name="SuppressABINotification">
<option name="modulesWithSuppressedNotConfigured">
<set>
<option value="backend.src" />
<option value="daemon-common" />
<option value="kotlin-stdlib-common" />
<option value="kotlin-stdlib-js" />
<option value="kotlin-test-common" />

0
DaemonLog.txt Normal file
View File

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.incremental.storage.version
/**
* Diff between actual and expected cache attributes.
* [status] are calculated based on this diff (see [CacheStatus]).
* Based on that [status] system may perform required actions (i.e. rebuild something, clearing caches, etc...).
*
* [CacheAttributesDiff] can be used to cache current attribute values and as facade for version operations.
*/
data class CacheAttributesDiff<Attrs: Any>(
val manager: CacheAttributesManager<Attrs>,
val actual: Attrs?,
val expected: Attrs?
) {
val status: CacheStatus
get() =
if (expected != null) {
if (actual != null && manager.isCompatible(actual, expected)) CacheStatus.VALID
else CacheStatus.INVALID
} else {
if (actual != null) CacheStatus.SHOULD_BE_CLEARED
else CacheStatus.CLEARED
}
fun saveExpectedIfNeeded() {
if (expected != actual) manager.writeActualVersion(expected)
}
override fun toString(): String {
return "$status: actual=$actual -> expected=$expected"
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.incremental.storage.version
/**
* Manages cache attributes values.
*
* Attribute values can be loaded by calling [loadActual].
* Based on loaded actual and fixed [expected] values [CacheAttributesDiff] can be constructed which can calculate [CacheStatus].
* Build system may perform required actions based on that (i.e. rebuild something, clearing caches, etc...).
*
* [CacheAttributesDiff] can be used to cache current attribute values and then can be used as facade for cache version operations.
*/
interface CacheAttributesManager<Attrs : Any> {
/**
* Cache attribute values expected by the current version of build system and compiler.
* `null` means that cache is not required (incremental compilation is disabled).
*/
val expected: Attrs?
/**
* Load actual cache attribute values.
* `null` means that cache is not yet created.
*
* This is internal operation that should be implemented by particular implementation of CacheAttributesManager.
* Consider using `loadDiff().actual` for getting actual values.
*/
fun loadActual(): Attrs?
/**
* Write [values] as cache attributes for next build execution.
*
* This is internal operation that should be implemented by particular implementation of CacheAttributesManager.
* Consider using `loadDiff().saveExpectedIfNeeded()` for saving attributes values for next build.
*/
fun writeActualVersion(values: Attrs?)
/**
* Check if cache with [actual] attributes values can be used when [expected] attributes are required.
*/
fun isCompatible(actual: Attrs, expected: Attrs): Boolean = actual == expected
}
fun <Attrs : Any> CacheAttributesManager<Attrs>.loadDiff(
actual: Attrs? = this.loadActual(),
expected: Attrs? = this.expected
) = CacheAttributesDiff(this, actual, expected)
fun <Attrs : Any> CacheAttributesManager<Attrs>.loadAndCheckStatus() =
loadDiff().status
/**
* This method is kept only for compatibility.
* Save [expected] cache attributes values if it is enabled and not equals to [actual].
*/
@Deprecated(
message = "Consider using `this.loadDiff().saveExpectedIfNeeded()` and cache `loadDiff()` result.",
replaceWith = ReplaceWith("loadDiff().saveExpectedIfNeeded()")
)
fun <Attrs : Any> CacheAttributesManager<Attrs>.saveIfNeeded(
actual: Attrs? = this.loadActual(),
expected: Attrs = this.expected
?: error("To save disabled cache status [delete] should be called (this behavior is kept for compatibility)")
) = loadDiff(actual, expected).saveExpectedIfNeeded()
/**
* This method is kept only for compatibility.
* Delete actual cache attributes values if it existed.
*/
@Deprecated(
message = "Consider using `this.loadDiff().saveExpectedIfNeeded()` and cache `loadDiff()` result.",
replaceWith = ReplaceWith("writeActualVersion(null)")
)
fun CacheAttributesManager<*>.clean() {
writeActualVersion(null)
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.incremental.storage.version
/**
* Status that is used by system to perform required actions (i.e. rebuild something, clearing caches, etc...).
*/
enum class CacheStatus {
/**
* Cache is valid and ready to use.
*/
VALID,
/**
* Cache is not exists or have outdated versions and/or other attributes.
*/
INVALID,
/**
* Cache is exists, but not required anymore.
*/
SHOULD_BE_CLEARED,
/**
* Cache is not exists and not required.
*/
CLEARED
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.incremental.storage.version
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import java.io.File
import java.io.IOException
/**
* Manages files with actual version [loadActual] and provides expected version [expected].
* Based on that actual and expected versions [CacheStatus] can be calculated.
* This can be done by constructing [CacheAttributesDiff] and calling [CacheAttributesDiff.status].
* Based on that status system may perform required actions (i.e. rebuild something, clearing caches, etc...).
*/
class CacheVersionManager(
private val versionFile: File,
expectedOwnVersion: Int?
) : CacheAttributesManager<CacheVersion> {
override val expected: CacheVersion? =
if (expectedOwnVersion == null) null
else {
val metadata = JvmMetadataVersion.INSTANCE
val bytecode = JvmBytecodeBinaryVersion.INSTANCE
CacheVersion(
expectedOwnVersion * 1000000 +
bytecode.major * 10000 + bytecode.minor * 100 +
metadata.major * 1000 + metadata.minor
)
}
override fun loadActual(): CacheVersion? =
if (!versionFile.exists()) null
else try {
CacheVersion(versionFile.readText().toInt())
} catch (e: NumberFormatException) {
null
} catch (e: IOException) {
null
}
override fun writeActualVersion(values: CacheVersion?) {
if (values == null) versionFile.delete()
else {
versionFile.parentFile.mkdirs()
versionFile.writeText(values.version.toString())
}
}
@get:TestOnly
val versionFileForTesting: File
get() = versionFile
}
data class CacheVersion(val version: Int)

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.incremental.storage.version
import java.io.File
private val NORMAL_VERSION = 9
private val NORMAL_VERSION_FILE_NAME = "format-version.txt"
fun localCacheVersionManager(dataRoot: File, isCachesEnabled: Boolean) =
CacheVersionManager(
File(dataRoot, NORMAL_VERSION_FILE_NAME),
if (isCachesEnabled) NORMAL_VERSION else null
)

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.incremental.storage.version
import java.io.File
private val DATA_CONTAINER_VERSION_FILE_NAME = "data-container-format-version.txt"
private val DATA_CONTAINER_VERSION = 3
fun lookupsCacheVersionManager(dataRoot: File, isEnabled: Boolean) =
CacheVersionManager(
File(dataRoot, DATA_CONTAINER_VERSION_FILE_NAME),
if (isEnabled) DATA_CONTAINER_VERSION else null
)
fun readLookupsCacheStatus(dataRoot: File, isEnabled: Boolean): CacheStatus =
lookupsCacheVersionManager(dataRoot, isEnabled).loadAndCheckStatus()

View File

@@ -1,4 +1,5 @@
import com.sun.javafx.scene.CameraHelper.project
import jdk.nashorn.internal.objects.NativeArray.forEach
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project
import java.util.*
@@ -8,6 +9,7 @@ import org.gradle.plugins.ide.idea.model.IdeaModel
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import java.nio.file.Files.delete
import proguard.gradle.ProGuardTask
buildscript {
@@ -15,11 +17,12 @@ buildscript {
kotlinBootstrapFrom(BootstrapOption.TeamCity("1.3.20-dev-564", onlySuccessBootstrap = false))
repositories.withRedirector(project) {
repositories {
bootstrapKotlinRepo?.let(::maven)
maven("https://plugins.gradle.org/m2")
}
// a workaround for kotlin compiler classpath in kotlin project: sometimes gradle substitutes
// kotlin-stdlib external dependency with local project :kotlin-stdlib in kotlinCompilerClasspath configuration.
// see also configureCompilerClasspath@
@@ -35,7 +38,7 @@ buildscript {
}
plugins {
`build-scan` version "1.15"
`build-scan`
idea
id("jps-compatible")
}
@@ -55,11 +58,11 @@ buildScan {
}
val configuredJdks: List<JdkId> =
getConfiguredJdks().also {
it.forEach {
logger.info("Using ${it.majorVersion} home: ${it.homeDir}")
}
getConfiguredJdks().also {
it.forEach {
logger.info("Using ${it.majorVersion} home: ${it.homeDir}")
}
}
val defaultSnapshotVersion: String by extra
val buildNumber by extra(findProperty("build.number")?.toString() ?: defaultSnapshotVersion)
@@ -102,31 +105,11 @@ extra["ideaUltimatePluginDir"] = project.file(ideaUltimatePluginDir)
extra["cidrPluginDir"] = project.file(cidrPluginDir)
extra["isSonatypeRelease"] = false
// Work-around necessary to avoid setting null javaHome. Will be removed after support of lazy task configuration
val jdkNotFoundConst = "JDK NOT FOUND"
extra["JDK_16"] = jdkPath("1.6")
extra["JDK_17"] = jdkPath("1.7")
extra["JDK_18"] = jdkPath("1.8")
extra["JDK_9"] = jdkPath("9")
extra["JDK_10"] = jdkPath("10")
extra["JDK_11"] = jdkPath("11")
gradle.taskGraph.beforeTask() {
checkJDK()
}
var jdkChecked: Boolean = false
fun checkJDK() {
if (jdkChecked) {
return
}
var unpresentJdks = JdkMajorVersion.values().filter { it.isMandatory() }.map { it -> it.name }.filter { it == null || extra[it] == jdkNotFoundConst }.toList()
if (!unpresentJdks.isEmpty()) {
throw GradleException("Please set environment variable${if (unpresentJdks.size > 1) "s" else ""}: ${unpresentJdks.joinToString()} to point to corresponding JDK installation.")
}
jdkChecked = true
}
extra["JDK_10"] = jdkPathIfFound("10")
rootProject.apply {
from(rootProject.file("versions.gradle.kts"))
@@ -151,6 +134,12 @@ extra["versions.robolectric"] = "3.1"
extra["versions.org.springframework"] = "4.2.0.RELEASE"
extra["versions.jflex"] = "1.7.0"
extra["versions.markdown"] = "0.1.25"
extra["versions.ktor-network"] = "0.9.5-rc13"
val markdownVer = "4054 - Kotlin 1.0.2-dev-566".replace(" ", "%20") // fixed here, was last with "status:SUCCESS,tag:forKotlin"
extra["markdownParserVersion"] = markdownVer
extra["markdownParserRepo"] =
"https://teamcity.jetbrains.com/guestAuth/repository/download/IntelliJMarkdownParser_Build/$markdownVer/([artifact]_[ext]/)[artifact](.[ext])"
val isTeamcityBuild = project.hasProperty("teamcity") || System.getenv("TEAMCITY_VERSION") != null
val intellijUltimateEnabled = project.getBooleanProperty("intellijUltimateEnabled") ?: isTeamcityBuild
@@ -163,84 +152,87 @@ extra["intellijUltimateEnabled"] = intellijUltimateEnabled
extra["intellijSeparateSdks"] = intellijSeparateSdks
extra["IntellijCoreDependencies"] =
listOf("annotations",
"asm-all",
"guava",
"jdom",
"jna",
"log4j",
"picocontainer",
"snappy-in-java",
"streamex",
"trove4j")
listOf(
"annotations",
"asm-all",
"guava",
"jdom",
"jna",
"log4j",
"picocontainer",
"snappy-in-java",
"streamex",
"trove4j"
)
extra["compilerModules"] = arrayOf(
":compiler:util",
":compiler:container",
":compiler:resolution",
":compiler:serialization",
":compiler:psi",
":compiler:frontend",
":compiler:frontend.java",
":compiler:frontend.script",
":compiler:cli-common",
":compiler:daemon-common",
":compiler:daemon",
":compiler:ir.tree",
":compiler:ir.psi2ir",
":compiler:ir.backend.common",
":compiler:backend.js",
":compiler:backend-common",
":compiler:backend",
":compiler:plugin-api",
":compiler:light-classes",
":compiler:cli",
":compiler:incremental-compilation-impl",
":js:js.ast",
":js:js.serializer",
":js:js.parser",
":js:js.frontend",
":js:js.translator",
":js:js.dce",
":compiler",
":kotlin-build-common",
":core:metadata",
":core:metadata.jvm",
":core:descriptors",
":core:descriptors.jvm",
":core:deserialization",
":core:util.runtime"
":compiler:util",
":compiler:container",
":compiler:resolution",
":compiler:serialization",
":compiler:psi",
":compiler:frontend",
":compiler:frontend.java",
":compiler:frontend.script",
":compiler:cli-common",
":compiler:daemon-common",
":compiler:daemon-common-new",
":compiler:daemon",
":compiler:ir.tree",
":compiler:ir.psi2ir",
":compiler:ir.backend.common",
":compiler:backend.js",
":compiler:backend-common",
":compiler:backend",
":compiler:plugin-api",
":compiler:light-classes",
":compiler:cli",
":compiler:incremental-compilation-impl",
":js:js.ast",
":js:js.serializer",
":js:js.parser",
":js:js.frontend",
":js:js.translator",
":js:js.dce",
":compiler",
":kotlin-build-common",
":core:metadata",
":core:metadata.jvm",
":core:descriptors",
":core:descriptors.jvm",
":core:deserialization",
":core:util.runtime"
)
val coreLibProjects = listOf(
":kotlin-stdlib",
":kotlin-stdlib-common",
":kotlin-stdlib-js",
":kotlin-stdlib-jre7",
":kotlin-stdlib-jre8",
":kotlin-stdlib-jdk7",
":kotlin-stdlib-jdk8",
":kotlin-test:kotlin-test-common",
":kotlin-test:kotlin-test-jvm",
":kotlin-test:kotlin-test-junit",
":kotlin-test:kotlin-test-junit5",
":kotlin-test:kotlin-test-testng",
":kotlin-test:kotlin-test-js",
":kotlin-reflect"
":kotlin-stdlib",
":kotlin-stdlib-common",
":kotlin-stdlib-js",
":kotlin-stdlib-jre7",
":kotlin-stdlib-jre8",
":kotlin-stdlib-jdk7",
":kotlin-stdlib-jdk8",
":kotlin-test:kotlin-test-common",
":kotlin-test:kotlin-test-jvm",
":kotlin-test:kotlin-test-junit",
":kotlin-test:kotlin-test-junit5",
":kotlin-test:kotlin-test-testng",
":kotlin-test:kotlin-test-js",
":kotlin-reflect"
)
val gradlePluginProjects = listOf(
":kotlin-gradle-plugin",
":kotlin-gradle-plugin:plugin-marker",
":kotlin-gradle-plugin-api",
":kotlin-gradle-plugin",
":kotlin-gradle-plugin:plugin-marker",
":kotlin-gradle-plugin-api",
// ":kotlin-gradle-plugin-integration-tests", // TODO: build fails
":kotlin-allopen",
":kotlin-allopen:plugin-marker",
":kotlin-annotation-processing-gradle",
":kotlin-noarg",
":kotlin-noarg:plugin-marker",
":kotlin-sam-with-receiver"
":kotlin-allopen",
":kotlin-allopen:plugin-marker",
":kotlin-annotation-processing-gradle",
":kotlin-noarg",
":kotlin-noarg:plugin-marker",
":kotlin-sam-with-receiver"
)
apply {
@@ -263,21 +255,14 @@ fun Task.listConfigurationContents(configName: String) {
}
}
IdeVersionConfigurator.setCurrentIde(this)
val defaultJvmTarget = "1.8"
val defaultJavaHome = jdkPath(defaultJvmTarget)
val defaultJavaHome = jdkPath(defaultJvmTarget!!)
val ignoreTestFailures by extra(project.findProperty("ignoreTestFailures")?.toString()?.toBoolean() ?: project.hasProperty("teamcity"))
allprojects {
jvmTarget = defaultJvmTarget
if (defaultJavaHome != null) {
javaHome = defaultJavaHome
} else {
logger.error("Could not find default java home. Please set environment variable JDK_${defaultJavaHome} to point to JDK ${defaultJavaHome} installation.")
}
javaHome = defaultJavaHome
// There are problems with common build dir:
// - some tests (in particular js and binary-compatibility-validator depend on the fixed (default) location
@@ -287,12 +272,13 @@ allprojects {
val mirrorRepo: String? = findProperty("maven.repository.mirror")?.toString()
repositories.withRedirector(project) {
repositories {
intellijSdkRepo(project)
androidDxJarRepo(project)
mirrorRepo?.let(::maven)
bootstrapKotlinRepo?.let(::maven)
jcenter()
maven("http://dl.bintray.com/kotlin/ktor")
}
configureJvmProject(javaHome!!, jvmTarget!!)
@@ -300,6 +286,7 @@ allprojects {
val commonCompilerArgs = listOfNotNull(
"-Xallow-kotlin-package",
"-Xread-deserialized-contracts",
"-Xread-deserialized-contracts",
"-Xprogressive".takeIf { hasProperty("test.progressive.mode") } // TODO: change to "-progressive" after bootstrap
)
@@ -340,24 +327,28 @@ allprojects {
task("listDistJar") { listConfigurationContents("distJar") }
afterEvaluate {
logger.info("configuring project $name to compile to the target jvm version $jvmTarget using jdk: $javaHome")
if (javaHome != defaultJavaHome || jvmTarget != defaultJvmTarget) {
logger.info("configuring project $name to compile to the target jvm version $jvmTarget using jdk: $javaHome")
configureJvmProject(javaHome!!, jvmTarget!!)
} // else we will actually fail during the first task execution. We could not fail before configuration is done due to impact on import in IDE
}
fun File.toProjectRootRelativePathOrSelf() = (relativeToOrNull(rootDir)?.takeUnless { it.startsWith("..") } ?: this).path
fun FileCollection.printClassPath(role: String) =
println("${project.path} $role classpath:\n ${joinToString("\n ") { it.toProjectRootRelativePathOrSelf() } }")
println("${project.path} $role classpath:\n ${joinToString("\n ") { it.toProjectRootRelativePathOrSelf() }}")
try { javaPluginConvention() } catch (_: UnknownDomainObjectException) { null }?.let { javaConvention ->
try {
javaPluginConvention()
} catch (_: UnknownDomainObjectException) {
null
}?.let { javaConvention ->
task("printCompileClasspath") { doFirst { javaConvention.sourceSets["main"].compileClasspath.printClassPath("compile") } }
task("printRuntimeClasspath") { doFirst { javaConvention.sourceSets["main"].runtimeClasspath.printClassPath("runtime") } }
task("printTestCompileClasspath") { doFirst { javaConvention.sourceSets["test"].compileClasspath.printClassPath("test compile") } }
task("printTestRuntimeClasspath") { doFirst { javaConvention.sourceSets["test"].runtimeClasspath.printClassPath("test runtime") } }
}
run configureCompilerClasspath@ {
run configureCompilerClasspath@{
val bootstrapCompilerClasspath by rootProject.buildscript.configurations
configurations.findByName("kotlinCompilerClasspath")?.let {
dependencies.add(it.name, files(bootstrapCompilerClasspath))
@@ -379,7 +370,7 @@ gradle.taskGraph.whenReady {
logger.warn("Local build profile is active (IC is on, proguard is off). Use -Pteamcity=true to reproduce TC build")
for (task in allTasks) {
when (task) {
// todo: remove when Gradle 4.10+ is used (Java IC on by default)
// todo: remove when Gradle 4.10+ is used (Java IC on by default)
is JavaCompile -> task.options.isIncremental = true
is org.gradle.jvm.tasks.Jar -> task.entryCompression = ZipEntryCompression.STORED
}
@@ -426,10 +417,10 @@ tasks {
create("coreLibsTest") {
(coreLibProjects + listOf(
":kotlin-stdlib:samples",
":kotlin-test:kotlin-test-js:kotlin-test-js-it",
":kotlinx-metadata-jvm",
":tools:binary-compatibility-validator"
":kotlin-stdlib:samples",
":kotlin-test:kotlin-test-js:kotlin-test-js-it",
":kotlinx-metadata-jvm",
":tools:binary-compatibility-validator"
)).forEach {
dependsOn(it + ":check")
}
@@ -447,10 +438,11 @@ tasks {
create("jvmCompilerTest") {
dependsOn("dist")
dependsOn(":compiler:test",
":compiler:container:test",
":compiler:tests-java8:test",
":compiler:tests-spec:remoteRunTests")
dependsOn(
":compiler:test",
":compiler:container:test",
":compiler:tests-java8:test"
)
}
create("jsCompilerTest") {
@@ -511,45 +503,53 @@ tasks {
create("idea-plugin-additional-tests") {
dependsOn("dist")
dependsOn(":idea:idea-gradle:test",
":idea:idea-maven:test",
":j2k:test",
":eval4j:test")
dependsOn(
":idea:idea-gradle:test",
":idea:idea-maven:test",
":j2k:test",
":eval4j:test"
)
}
create("idea-plugin-tests") {
dependsOn("dist")
dependsOn("idea-plugin-main-tests",
"idea-plugin-additional-tests")
dependsOn(
"idea-plugin-main-tests",
"idea-plugin-additional-tests"
)
}
create("android-ide-tests") {
dependsOn("dist")
dependsOn(":plugins:android-extensions-ide:test",
":idea:idea-android:test",
":kotlin-annotation-processing:test")
dependsOn(
":plugins:android-extensions-ide:test",
":idea:idea-android:test",
":kotlin-annotation-processing:test"
)
}
create("plugins-tests") {
dependsOn("dist")
dependsOn(":kotlin-annotation-processing:test",
":kotlin-source-sections-compiler-plugin:test",
":kotlin-allopen-compiler-plugin:test",
":kotlin-noarg-compiler-plugin:test",
":kotlin-sam-with-receiver-compiler-plugin:test",
":plugins:uast-kotlin:test",
":kotlin-annotation-processing-gradle:test",
":kotlinx-serialization-ide-plugin:test")
dependsOn(
":kotlin-annotation-processing:test",
":kotlin-source-sections-compiler-plugin:test",
":kotlin-allopen-compiler-plugin:test",
":kotlin-noarg-compiler-plugin:test",
":kotlin-sam-with-receiver-compiler-plugin:test",
":plugins:uast-kotlin:test",
":kotlin-annotation-processing-gradle:test",
":kotlinx-serialization-ide-plugin:test"
)
}
create("ideaPluginTest") {
dependsOn(
"idea-plugin-tests",
"jps-tests",
"plugins-tests",
"android-ide-tests",
":generators:test"
"idea-plugin-tests",
"jps-tests",
"plugins-tests",
"android-ide-tests",
":generators:test"
)
}
@@ -661,21 +661,23 @@ val zipCidrPlugin by task<Zip> {
configure<IdeaModel> {
module {
excludeDirs = files(
project.buildDir,
commonLocalDataDir,
".gradle",
"dependencies",
"dist"
project.buildDir,
commonLocalDataDir,
".gradle",
"dependencies",
"dist"
).toSet()
}
}
fun jdkPath(version: String): String {
fun jdkPathIfFound(version: String): String? {
val jdkName = "JDK_${version.replace(".", "")}"
val jdkMajorVersion = JdkMajorVersion.valueOf(jdkName)
return configuredJdks.find { it.majorVersion == jdkMajorVersion }?.homeDir?.canonicalPath?:jdkNotFoundConst
return configuredJdks.find { it.majorVersion == jdkMajorVersion }?.homeDir?.canonicalPath
}
fun jdkPath(version: String): String = jdkPathIfFound(version)
?: throw GradleException("Please set environment variable JDK_${version.replace(".", "")} to point to JDK $version installation")
fun Project.configureJvmProject(javaHome: String, javaVersion: String) {
tasks.withType<JavaCompile> {

6105
build_log.txt Normal file

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.backend.common.isTopLevelInPackage
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME
import org.jetbrains.kotlin.builtins.PrimitiveType

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.codegen.coroutines
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.SUSPENSION_POINT_INSIDE_MONITOR
class GlobalCoroutinesContext(private val diagnostics: DiagnosticSink) {
private var monitorsDepth = 0
private val inlineLambdaInsideMonitorSourceArgumentIndexes = arrayListOf<Set<Int>>()
fun pushArgumentIndexes(indexes: Set<Int>) {
inlineLambdaInsideMonitorSourceArgumentIndexes.add(indexes)
}
fun popArgumentIndexes() {
inlineLambdaInsideMonitorSourceArgumentIndexes.pop()
}
private fun enterMonitor() {
monitorsDepth++
}
fun enterMonitorIfNeeded(index: Int?) {
if (index == null) return
if (inlineLambdaInsideMonitorSourceArgumentIndexes.peek()?.contains(index) != true) return
enterMonitor()
}
private fun exitMonitor() {
assert(monitorsDepth > 0) {
"exitMonitor without corresponding enterMonitor"
}
monitorsDepth--
}
fun exitMonitorIfNeeded(index: Int?) {
if (index == null) return
if (inlineLambdaInsideMonitorSourceArgumentIndexes.peek()?.contains(index) != true) return
exitMonitor()
}
fun checkSuspendCall(call: ResolvedCall<*>) {
if (monitorsDepth != 0) {
diagnostics.report(SUSPENSION_POINT_INSIDE_MONITOR.on(call.call.callElement, call.resultingDescriptor))
}
}
}

View File

@@ -36,6 +36,31 @@ val depDistProjects = listOf(
":kotlin-stdlib",
":kotlin-test:kotlin-test-jvm"
)
// TODO: it seems incomplete, find and add missing dependencies
val testDistProjects = listOf(
"", // for root project
":kotlin-stdlib:jvm-minimal-for-test",
":kotlin-compiler",
":kotlin-script-runtime",
":kotlin-stdlib",
":kotlin-stdlib-jre7",
":kotlin-stdlib-jre8",
":kotlin-stdlib-jdk7",
":kotlin-stdlib-jdk8",
":kotlin-stdlib-js",
":kotlin-reflect",
":kotlin-test:kotlin-test-jvm",
":kotlin-test:kotlin-test-junit",
":kotlin-test:kotlin-test-js",
":kotlin-preloader",
":plugins:android-extensions-compiler",
":kotlin-ant",
":kotlin-annotations-jvm",
":kotlin-annotations-android"
)
val testJvm6ServerRuntime by configurations.creating
val antLauncherJar by configurations.creating
dependencies {
@@ -51,21 +76,48 @@ dependencies {
testCompile(projectTests(":generators:test-generator"))
testCompile(project(":compiler:ir.ir2cfg"))
testCompile(project(":compiler:ir.tree")) // used for deepCopyWithSymbols call that is removed by proguard from the compiler TODO: make it more straightforward
testCompileOnly(project(":kotlin-daemon-client"))
testCompileOnly(project(":kotlin-daemon-client-new"))
testCompile(project(":compiler:daemon")) // +
testCompile(project(":compiler:daemon-common")) // +
testCompile(project(":compiler:daemon-common-new")) // +
testCompile(project(":kotlin-scripting-compiler"))
testCompile(project(":kotlin-script-util"))
testCompileOnly(projectRuntimeJar(":kotlin-daemon-client"))
testCompileOnly(project(":kotlin-reflect-api"))
testCompile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
otherCompilerModules.forEach {
testCompileOnly(project(it))
}
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "idea_rt", "util", "asm-all", rootProject = rootProject) }
testCompileOnly(intellijDep()) { includeJars("openapi", "idea", "idea_rt", "util", "asm-all") }
testRuntime(project(":kotlin-reflect"))
testRuntime(project(":kotlin-daemon-client"))
testRuntime(projectDist(":kotlin-reflect"))
testRuntime(projectDist(":kotlin-daemon-client"))
testRuntime(projectDist(":kotlin-daemon-client-new"))
testRuntime(project(":compiler:daemon")) // +
testRuntime(project(":compiler:daemon-common")) // +
testRuntime(project(":compiler:daemon-common-new")) // +
testRuntime(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
testRuntime(commonDep("io.ktor", "ktor-network")) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-reflect")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-jdk8")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-common")
}
testRuntime(androidDxJar())
testRuntime(files(toolsJar()))
testJvm6ServerRuntime(projectTests(":compiler:tests-common-jvm6"))
antLauncherJar(commonDep("org.apache.ant", "ant"))
antLauncherJar(files(toolsJar()))
}
@@ -92,6 +144,91 @@ projectTest {
}
}
fun Project.codegenTest(target: Int, jvm: Int,
jdk: String = "JDK_${if (jvm <= 8) "1" else ""}$jvm",
body: Test.() -> Unit): Test = projectTest("codegenTarget${target}Jvm${jvm}Test") {
dependsOn(*testDistProjects.map { "$it:dist" }.toTypedArray())
workingDir = rootDir
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.BlackBoxCodegenTestGenerated*")
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.BlackBoxInlineCodegenTestGenerated*")
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.CompileKotlinAgainstInlineKotlinTestGenerated*")
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.CompileKotlinAgainstKotlinTestGenerated*")
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.BlackBoxAgainstJavaCodegenTestGenerated*")
if (jdk == "JDK_9") {
jvmArgs = listOf("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
}
body()
doFirst {
val jdkPath = project.findProperty(jdk) ?: error("$jdk is not optional to run this test")
executable = "$jdkPath/bin/java"
println("Running test with $executable")
}
group = "verification"
}
codegenTest(target = 6, jvm = 6, jdk = "JDK_18") {
dependsOn(testJvm6ServerRuntime)
val port = project.findProperty("kotlin.compiler.codegen.tests.port")?.toString() ?: "5100"
var jdkProcess: Process? = null
doFirst {
logger.info("Configuring JDK 6 server...")
val jdkPath = project.findProperty("JDK_16") ?: error("JDK_16 is not optional to run this test")
val executable = "$jdkPath/bin/java"
val main = "org.jetbrains.kotlin.test.clientserver.TestProcessServer"
val classpath = testJvm6ServerRuntime.asPath
logger.debug("Server classpath: $classpath")
val builder = ProcessBuilder(executable, "-cp", classpath, main, port)
builder.directory(rootDir)
builder.inheritIO()
builder.redirectErrorStream(true)
logger.info("Starting JDK 6 server $executable")
jdkProcess = builder.start()
}
systemProperty("kotlin.test.default.jvm.target", "1.6")
systemProperty("kotlin.test.java.compilation.target", "1.6")
systemProperty("kotlin.test.box.in.separate.process.port", port)
doLast {
logger.info("Stopping JDK 6 server...")
jdkProcess?.destroy()
}
}
codegenTest(target = 6, jvm = 9) {
systemProperty("kotlin.test.default.jvm.target", "1.6")
}
codegenTest(target = 8, jvm = 8) {
systemProperty("kotlin.test.default.jvm.target", "1.8")
}
codegenTest(target = 8, jvm = 9) {
systemProperty("kotlin.test.default.jvm.target", "1.8")
}
codegenTest(target = 9, jvm = 9) {
systemProperty("kotlin.test.default.jvm.target", "1.8")
systemProperty("kotlin.test.substitute.bytecode.1.8.to.1.9", "true")
}
codegenTest(target = 10, jvm = 10) {
systemProperty("kotlin.test.default.jvm.target", "1.8")
systemProperty("kotlin.test.substitute.bytecode.1.8.to.10", "true")
}
codegenTest(target = 8, jvm = 11) {
systemProperty("kotlin.test.default.jvm.target", "1.8")
jvmArgs!!.add( "-XX:-FailOverToOldVerifier")
}
val generateTests by generator("org.jetbrains.kotlin.generators.tests.GenerateCompilerTestsKt")

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.common.repl.experimental
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.repl.*
import java.io.File
import java.io.Serializable
import java.util.*
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.reflect.KClass
data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializable {
override fun equals(other: Any?): Boolean =
(other as? CompiledClassData)?.let { path == it.path && Arrays.equals(bytes, it.bytes) } ?: false
override fun hashCode(): Int = path.hashCode() + Arrays.hashCode(bytes)
companion object {
private val serialVersionUID: Long = 8228357578L
}
}
interface CreateReplStageStateAction {
suspend fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*>
}
// --- check
interface ReplCheckAction {
suspend fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult
}
// --- compile
interface ReplCompileAction {
suspend fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult
}
interface ReplCompiler : ReplCompileAction, ReplCheckAction, CreateReplStageStateAction
// --- eval
data class EvalClassWithInstanceAndLoader(
val klass: KClass<*>,
val instance: Any?,
val classLoader: ClassLoader,
val invokeWrapper: InvokeWrapper?
)
interface ReplEvalAction {
suspend fun eval(
state: IReplStageState<*>,
compileResult: ReplCompileResult.CompiledClasses,
scriptArgs: ScriptArgsWithTypes? = null,
invokeWrapper: InvokeWrapper? = null
): ReplEvalResult
}
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction
// --- compileAdnEval
interface ReplAtomicEvalAction {
suspend fun compileAndEval(
state: IReplStageState<*>,
codeLine: ReplCodeLine,
scriptArgs: ScriptArgsWithTypes? = null,
invokeWrapper: InvokeWrapper? = null
): ReplEvalResult
}
interface ReplAtomicEvaluator : ReplAtomicEvalAction, ReplCheckAction
interface ReplDelayedEvalAction {
suspend fun compileToEvaluable(
state: IReplStageState<*>,
codeLine: ReplCodeLine,
defaultScriptArgs: ScriptArgsWithTypes? = null
): Pair<ReplCompileResult, Evaluable?>
}
// other
interface Evaluable {
val compiledCode: ReplCompileResult.CompiledClasses
suspend fun eval(scriptArgs: ScriptArgsWithTypes? = null, invokeWrapper: InvokeWrapper? = null): ReplEvalResult
}
interface ReplFullEvaluator : ReplEvaluator, ReplAtomicEvaluator, ReplDelayedEvalAction
/**
* Keep args and arg types together, so as a whole they are present or absent
*/

View File

@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.cli.common
import jdk.nashorn.internal.runtime.regexp.joni.Config.log
import org.fusesource.jansi.AnsiConsole
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.cli.common.arguments.ManualLanguageFeatureSetting

View File

@@ -16,10 +16,12 @@ dependencies {
compileOnly(project(":kotlin-preloader"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":compiler:daemon-common"))
compileOnly(project(":compiler:daemon-common-new"))
compile(projectRuntimeJar(":kotlin-daemon-client"))
compileOnly(project(":compiler:util"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
runtimeOnly(projectRuntimeJar(":kotlin-compiler-embeddable"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) { isTransitive = false }
}
sourceSets {

View File

@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.compilerRunner
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -24,8 +25,12 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
import org.jetbrains.kotlin.daemon.client.CompileServiceSession
import org.jetbrains.kotlin.daemon.client.DaemonReportMessage
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient
import org.jetbrains.kotlin.daemon.client.KotlinCompilerDaemonClient
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.impls.DaemonReportCategory
import org.jetbrains.kotlin.daemon.common.DummyProfiler
import org.jetbrains.kotlin.daemon.common.Profiler
import org.jetbrains.kotlin.daemon.common.WallAndThreadAndMemoryTotalProfiler
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File
@@ -52,14 +57,16 @@ abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
protected abstract fun getDaemonConnection(environment: Env): CompileServiceSession?
protected val kotlinDaemonClient = KotlinCompilerDaemonClient.instantiate(Version.RMI)
@Synchronized
protected fun newDaemonConnection(
compilerId: CompilerId,
clientAliveFlagFile: File,
sessionAliveFlagFile: File,
environment: Env,
daemonOptions: DaemonOptions = configureDaemonOptions(),
additionalJvmParams: Array<String> = arrayOf()
compilerId: CompilerId,
clientAliveFlagFile: File,
sessionAliveFlagFile: File,
environment: Env,
daemonOptions: DaemonOptions = configureDaemonOptions(),
additionalJvmParams: Array<String> = arrayOf()
): CompileServiceSession? {
val daemonJVMOptions = configureDaemonJVMOptions(
additionalParams = *additionalJvmParams,
@@ -73,20 +80,24 @@ abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
val profiler = if (daemonOptions.reportPerf) WallAndThreadAndMemoryTotalProfiler(withGC = false) else DummyProfiler()
val connection = profiler.withMeasure(null) {
KotlinCompilerClient.connectAndLease(compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
daemonReportingTargets,
autostart = true,
leaseSession = true,
sessionAliveFlagFile = sessionAliveFlagFile)
val connection = runBlocking {
profiler.withMeasure(null) {
kotlinDaemonClient.connectAndLease(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
daemonReportingTargets,
autostart = true,
leaseSession = true,
sessionAliveFlagFile = sessionAliveFlagFile
)
}
}
if (connection == null || log.isDebugEnabled) {
for (message in daemonReportMessages) {
val severity = when(message.category) {
val severity = when (message.category) {
DaemonReportCategory.DEBUG -> CompilerMessageSeverity.INFO
DaemonReportCategory.INFO -> CompilerMessageSeverity.INFO
DaemonReportCategory.EXCEPTION -> CompilerMessageSeverity.EXCEPTION
@@ -95,11 +106,19 @@ abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
}
}
fun reportTotalAndThreadPerf(message: String, daemonOptions: DaemonOptions, messageCollector: MessageCollector, profiler: Profiler) {
fun reportTotalAndThreadPerf(
message: String,
daemonOptions: DaemonOptions,
messageCollector: MessageCollector,
profiler: Profiler
) {
if (daemonOptions.reportPerf) {
fun Long.ms() = TimeUnit.NANOSECONDS.toMillis(this)
val counters = profiler.getTotalCounters()
messageCollector.report(CompilerMessageSeverity.INFO, "PERF: $message ${counters.time.ms()} ms, thread ${counters.threadTime.ms()}")
messageCollector.report(
CompilerMessageSeverity.INFO,
"PERF: $message ${counters.time.ms()} ms, thread ${counters.threadTime.ms()}"
)
}
}
@@ -108,9 +127,9 @@ abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
}
protected fun processCompilerOutput(
environment: Env,
stream: ByteArrayOutputStream,
exitCode: ExitCode?
environment: Env,
stream: ByteArrayOutputStream,
exitCode: ExitCode?
) {
val reader = BufferedReader(StringReader(stream.toString()))
CompilerOutputParser.parseCompilerMessagesFromReader(environment.messageCollector, reader, environment.outputItemsCollector)
@@ -126,31 +145,31 @@ abstract class KotlinCompilerRunner<in Env : CompilerEnvironment> {
}
protected fun runCompiler(
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env): ExitCode {
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env
): ExitCode {
return try {
compileWithDaemonOrFallback(compilerClassName, compilerArgs, environment)
}
catch (e: Throwable) {
} catch (e: Throwable) {
MessageCollectorUtil.reportException(environment.messageCollector, e)
reportInternalCompilerError(environment.messageCollector)
}
}
protected abstract fun compileWithDaemonOrFallback(
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env
): ExitCode
/**
* Returns null if could not connect to daemon
*/
protected abstract fun compileWithDaemon(
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env
compilerClassName: String,
compilerArgs: CommonCompilerArguments,
environment: Env
): ExitCode?
protected fun exitCodeFromProcessExitCode(code: Int): ExitCode = Companion.exitCodeFromProcessExitCode(log, code)

View File

@@ -67,6 +67,7 @@ messages/**)
-dontwarn com.intellij.util.io.Decompressor*
-dontwarn org.w3c.dom.Location
-dontwarn org.w3c.dom.Window
-dontwarn org.slf4j.**
#-libraryjars '<rtjar>'

View File

@@ -1,3 +1,6 @@
import com.sun.javafx.scene.CameraHelper.project
import org.gradle.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.exclude
import org.jetbrains.kotlin.gradle.dsl.Coroutines
plugins {
kotlin("jvm")
@@ -9,15 +12,34 @@ jvmTarget = "1.6"
dependencies {
compile(project(":compiler:cli"))
compile(project(":compiler:daemon-common"))
compile(project(":compiler:daemon-common-new"))
compile(project(":compiler:incremental-compilation-impl"))
compile(project(":kotlin-build-common"))
compile(commonDep("org.fusesource.jansi", "jansi"))
compile(commonDep("org.jline", "jline"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
compile(projectDist(":kotlin-reflect"))
compile(project(":kotlin-reflect-api"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
compile(commonDep("io.ktor", "ktor-network")) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-reflect")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-jdk8")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-common")
}
}
sourceSets {
"main" { projectDefault() }
"test" {}
}
kotlin {
experimental.coroutines = Coroutines.ENABLE
}

View File

@@ -0,0 +1,77 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
description = "Kotlin Daemon Client New"
plugins {
kotlin("jvm")
id("jps-compatible")
}
jvmTarget = "1.6"
val nativePlatformVariants = listOf(
"windows-amd64",
"windows-i386",
"osx-amd64",
"osx-i386",
"linux-amd64",
"linux-i386",
"freebsd-amd64-libcpp",
"freebsd-amd64-libstdcpp",
"freebsd-i386-libcpp",
"freebsd-i386-libstdcpp"
)
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:cli-common"))
compileOnly(project(":compiler:daemon-common-new"))
compileOnly(project(":kotlin-reflect-api"))
compileOnly(project(":kotlin-daemon-client"))
embeddedComponents(project(":kotlin-daemon-client"))
compileOnly(project(":js:js.frontend"))
compileOnly(commonDep("net.rubygrapefruit", "native-platform"))
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
embeddedComponents(project(":compiler:daemon-common")) { isTransitive = false }
embeddedComponents(commonDep("net.rubygrapefruit", "native-platform"))
nativePlatformVariants.forEach {
embeddedComponents(commonDep("net.rubygrapefruit", "native-platform", "-$it"))
}
compile(projectDist(":kotlin-reflect"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
compile(commonDep("io.ktor", "ktor-network")) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-reflect")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-jdk8")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-common")
}
}
sourceSets {
"main" { projectDefault() }
"test" {}
}
noDefaultJar()
runtimeJar(task<ShadowJar>("shadowJar")) {
from(mainSourceSet.output)
fromEmbeddedComponents()
}
sourcesJar()
javadocJar()
dist()
ideaPlugin()
publish()

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental
import io.ktor.network.sockets.Socket
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.client.reportFromDaemon
import org.jetbrains.kotlin.daemon.common.experimental.*
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ServerSocketWrapper
import java.io.File
import java.io.Serializable
//interface MessageCollectorAsync {
// fun clear()
//
// suspend fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation? = null)
//
// suspend fun hasErrors(): Boolean
//}
//fun MessageCollector.toAsync() = object : MessageCollectorAsync {
//
// override fun clear() = this@toAsync.clear()
//
// override suspend fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) =
// this@toAsync.report(severity, message, location)
//
// override suspend fun hasErrors(): Boolean = this@toAsync.hasErrors()
//
//}
open class BasicCompilerServicesWithResultsFacadeServerServerSide(
val messageCollector: MessageCollector,
val outputsCollector: ((File, List<File>) -> Unit)? = null,
override val serverSocketWithPort: ServerSocketWrapper = findCallbackServerSocket()
) : CompilerServicesFacadeBaseServerSide {
override val clients = hashMapOf<Socket, Server.ClientInfo>()
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
messageCollector.reportFromDaemon(outputsCollector, category, severity, message, attachment)
}
val clientSide: CompilerServicesFacadeBaseClientSide
get() = CompilerServicesFacadeBaseClientSideImpl(serverSocketWithPort.port)
}
//suspend fun MessageCollectorAsync.reportFromDaemon(
// outputsCollector: ((File, List<File>) -> Unit)?,
// category: Int,
// severity: Int,
// message: String?,
// attachment: Serializable?
//) {
// val reportCategory = ReportCategory.fromCode(category)
//
// when (reportCategory) {
// ReportCategory.OUTPUT_MESSAGE -> {
// if (outputsCollector != null) {
// OutputMessageUtil.parseOutputMessage(message.orEmpty())?.let { outs ->
// outs.outputFile?.let {
// outputsCollector.invoke(it, outs.sourceFiles.toList())
// }
// }
// } else {
// report(CompilerMessageSeverity.OUTPUT, message.orEmpty())
// }
// }
// ReportCategory.EXCEPTION -> {
// report(CompilerMessageSeverity.EXCEPTION, message.orEmpty())
// }
// ReportCategory.COMPILER_MESSAGE -> {
// val compilerSeverity = when (ReportSeverity.fromCode(severity)) {
// ReportSeverity.ERROR -> CompilerMessageSeverity.ERROR
// ReportSeverity.WARNING -> CompilerMessageSeverity.WARNING
// ReportSeverity.INFO -> CompilerMessageSeverity.INFO
// ReportSeverity.DEBUG -> CompilerMessageSeverity.LOGGING
// else -> throw IllegalStateException("Unexpected compiler message report severity $severity")
// }
// if (message != null) {
// report(compilerSeverity, message, attachment as? CompilerMessageLocation)
// } else {
// reportUnexpected(category, severity, message, attachment)
// }
// }
// ReportCategory.DAEMON_MESSAGE,
// ReportCategory.IC_MESSAGE -> {
// if (message != null) {
// report(CompilerMessageSeverity.LOGGING, message)
// } else {
// reportUnexpected(category, severity, message, attachment)
// }
// }
// else -> {
// reportUnexpected(category, severity, message, attachment)
// }
// }
//}
//
//private suspend fun MessageCollectorAsync.reportUnexpected(category: Int, severity: Int, message: String?, attachment: Serializable?) {
// val compilerMessageSeverity = when (ReportSeverity.fromCode(severity)) {
// ReportSeverity.ERROR -> CompilerMessageSeverity.ERROR
// ReportSeverity.WARNING -> CompilerMessageSeverity.WARNING
// ReportSeverity.INFO -> CompilerMessageSeverity.INFO
// else -> CompilerMessageSeverity.LOGGING
// }
//
// report(
// compilerMessageSeverity,
// "Unexpected message: category=$category; severity=$severity; message='$message'; attachment=$attachment"
// )
//}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental
import io.ktor.network.sockets.Socket
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.client.reportFromDaemon
import org.jetbrains.kotlin.daemon.common.experimental.*
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ServerSocketWrapper
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.progress.experimental.CompilationCanceledStatus
import org.jetbrains.kotlin.utils.isProcessCanceledException
import java.io.Serializable
open class CompilerCallbackServicesFacadeServerServerSide(
val incrementalCompilationComponents: IncrementalCompilationComponents? = null,
val lookupTracker: LookupTracker? = null,
val compilationCanceledStatus: CompilationCanceledStatus? = null,
val messageCollector: MessageCollector? = null,
override val serverSocketWithPort: ServerSocketWrapper = findCallbackServerSocket()
) : CompilerCallbackServicesFacadeServerSide {
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
messageCollector?.reportFromDaemon(null, category, severity, message, attachment)
}
override val clients = hashMapOf<Socket, Server.ClientInfo>()
val clientSide : CompilerServicesFacadeBaseClientSide
get() = CompilerCallbackServicesFacadeClientSideImpl(serverSocketWithPort.port)
override suspend fun hasIncrementalCaches(): Boolean = incrementalCompilationComponents != null
override suspend fun hasLookupTracker(): Boolean = lookupTracker != null
override suspend fun hasCompilationCanceledStatus(): Boolean = compilationCanceledStatus != null
// TODO: consider replacing NPE with other reporting, although NPE here means most probably incorrect usage
override suspend fun incrementalCache_getObsoletePackageParts(target: TargetId): Collection<String> =
incrementalCompilationComponents!!.getIncrementalCache(target).getObsoletePackageParts()
override suspend fun incrementalCache_getObsoleteMultifileClassFacades(target: TargetId): Collection<String> =
incrementalCompilationComponents!!.getIncrementalCache(target).getObsoleteMultifileClasses()
override suspend fun incrementalCache_getMultifileFacadeParts(target: TargetId, internalName: String): Collection<String>? =
incrementalCompilationComponents!!.getIncrementalCache(target).getStableMultifileFacadeParts(internalName)
override suspend fun incrementalCache_getPackagePartData(target: TargetId, partInternalName: String): JvmPackagePartProto? =
incrementalCompilationComponents!!.getIncrementalCache(target).getPackagePartData(partInternalName)
override suspend fun incrementalCache_getModuleMappingData(target: TargetId): ByteArray? =
incrementalCompilationComponents!!.getIncrementalCache(target).getModuleMappingData()
// todo: remove (the method it called was relevant only for old IC)
override suspend fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String) {
}
override suspend fun incrementalCache_getClassFilePath(target: TargetId, internalClassName: String): String =
incrementalCompilationComponents!!.getIncrementalCache(target).getClassFilePath(internalClassName)
override suspend fun incrementalCache_close(target: TargetId) {
incrementalCompilationComponents!!.getIncrementalCache(target).close()
}
override suspend fun lookupTracker_requiresPosition() = lookupTracker!!.requiresPosition
override suspend fun lookupTracker_record(lookups: Collection<LookupInfo>) {
val lookupTracker = lookupTracker!!
for (it in lookups) {
lookupTracker.record(it.filePath, it.position, it.scopeFqName, it.scopeKind, it.name)
}
}
private val lookupTracker_isDoNothing: Boolean = lookupTracker === LookupTracker.DO_NOTHING
override suspend fun lookupTracker_isDoNothing(): Boolean = lookupTracker_isDoNothing
override suspend fun compilationCanceledStatus_checkCanceled(): Void? {
try {
compilationCanceledStatus?.checkCanceled()
return null
}
catch (e: Exception) {
// avoid passing exceptions that may have different serialVersionUID on across rmi border
// removing dependency from openapi (this is obsolete part anyway, and will be removed soon)
if (e.isProcessCanceledException())
throw Exception("-TODO- RmiFriendlyCompilationCanceledException()") //RmiFriendlyCompilationCanceledException()
else throw e
}
}
}

View File

@@ -0,0 +1,624 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental
import io.ktor.network.sockets.Socket
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Unconfined
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.client.CompileServiceSession
import org.jetbrains.kotlin.daemon.client.KotlinCompilerDaemonClient
import org.jetbrains.kotlin.daemon.client.DaemonReportMessage
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.Profiler
import org.jetbrains.kotlin.daemon.common.experimental.*
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ServerSocketWrapper
import org.jetbrains.kotlin.daemon.common.impls.*
import java.io.File
import java.io.Serializable
import java.net.SocketException
import java.nio.channels.ClosedChannelException
import java.rmi.ConnectException
import java.rmi.ConnectIOException
import java.rmi.UnmarshalException
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.logging.Logger
import kotlin.concurrent.thread
class KotlinCompilerClient : KotlinCompilerDaemonClient {
init {
println("experimental KotlinCompilerClient is being instantiated")
}
val DAEMON_DEFAULT_STARTUP_TIMEOUT_MS = 10000L
val DAEMON_CONNECT_CYCLE_ATTEMPTS = 3
val verboseReporting = System.getProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY) != null
private val log = Logger.getLogger("KotlinCompilerClient")
private fun String.info(msg: String) = {}()//log.info("[$this] : $msg")
fun getOrCreateClientFlagFile(daemonOptions: DaemonOptions): File =
// for jps property is passed from IDEA to JPS in KotlinBuildProcessParametersProvider
System.getProperty(COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY)
?.let(String::trimQuotes)
?.takeUnless(String::isBlank)
?.let(::File)
?.takeIf(File::exists)
?: makeAutodeletingFlagFile(baseDir = File(daemonOptions.runFilesPathOrDefault))
override suspend fun connectToCompileService(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
checkId: Boolean
): CompileServiceAsync? {
log.info("in connectToCompileService")
val flagFile = getOrCreateClientFlagFile(daemonOptions)
return connectToCompileService(
compilerId,
flagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart
)
}
override suspend fun connectToCompileService(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean
): CompileServiceAsync? {
log.info("connectToCompileService")
return connectAndLease(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
leaseSession = false,
sessionAliveFlagFile = null
)?.compileService
}
override suspend fun connectAndLease(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
leaseSession: Boolean,
sessionAliveFlagFile: File?
): CompileServiceSession? {
return connectLoop(
reportingTargets,
autostart
) { isLastAttempt ->
log.info("connectAndLease")
fun CompileServiceAsync.leaseImpl(): Deferred<CompileServiceSession?> =
async {
// the newJVMOptions could be checked here for additional parameters, if needed
log.info("trying registerClient")
println("trying registerClient")
try {
registerClient(clientAliveFlagFile.absolutePath)
} catch (e: Throwable) {
return@async null
}
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
if (!leaseSession)
org.jetbrains.kotlin.daemon.client.CompileServiceSession(this@leaseImpl, CompileService.NO_SESSION)
else
try {
leaseCompileSession(sessionAliveFlagFile?.absolutePath)
} catch (e: Throwable) {
return@async null
}
.takeUnless { it is CompileService.CallResult.Dying }
?.let {
org.jetbrains.kotlin.daemon.client.CompileServiceSession(this@leaseImpl, it.get())
}
}
ensureServerHostnameIsSetUp()
val (service, newJVMOptions) =
tryFindSuitableDaemonOrNewOpts(
File(daemonOptions.runFilesPath),
compilerId,
daemonJVMOptions,
{ cat, msg -> async { reportingTargets.report(cat, msg) } }).await()
if (service != null) {
// service.connectToServer()
service.leaseImpl().await()
} else {
if (!isLastAttempt && autostart) {
log.info("starting daemon...")
if (startDaemon(
compilerId,
newJVMOptions,
daemonOptions,
reportingTargets
)
) {
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
}
}
null
}
}
}
override suspend fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions) {
connectToCompileService(
compilerId,
DaemonJVMOptions(),
daemonOptions,
DaemonReportingTargets(out = System.out),
autostart = false,
checkId = false
)?.shutdown()
}
override suspend fun leaseCompileSession(compilerService: CompileServiceAsync, aliveFlagPath: String?): Int =
compilerService.leaseCompileSession(aliveFlagPath).get()
override suspend fun releaseCompileSession(compilerService: CompileServiceAsync, sessionId: Int) =
compilerService.releaseCompileSession(sessionId)
override suspend fun compile(
compilerService: CompileServiceAsync,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
outputsCollector: ((File, List<File>) -> Unit)?,
compilerMode: CompilerMode,
reportSeverity: ReportSeverity,
profiler: Profiler
): Int = profiler.withMeasure(this) {
val services = BasicCompilerServicesWithResultsFacadeServerServerSide(
messageCollector,
outputsCollector,
findCallbackServerSocket()
)
services.runServer()
compilerService.compile(
sessionId,
args,
CompilationOptions(
compilerMode,
targetPlatform,
arrayOf(
ReportCategory.COMPILER_MESSAGE.code,
ReportCategory.DAEMON_MESSAGE.code,
ReportCategory.EXCEPTION.code,
ReportCategory.OUTPUT_MESSAGE.code
),
reportSeverity.code,
emptyArray()
),
services.clientSide,
createCompResults().clientSide
)
.get()
.also {
log.info("CODE = $it")
}
}
val COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY: String = "kotlin.daemon.client.options"
data class ClientOptions(
var stop: Boolean = false
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf(BoolPropMapper(this, ClientOptions::stop))
}
private fun configureClientOptions(opts: ClientOptions): ClientOptions {
System.getProperty(COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY)?.let {
val unrecognized = it.trimQuotes().split(",").filterExtractProps(opts.mappers, "")
if (unrecognized.any())
throw IllegalArgumentException(
"Unrecognized client options passed via property $COMPILE_DAEMON_OPTIONS_PROPERTY: " + unrecognized.joinToString(" ") +
"\nSupported options: " + opts.mappers.joinToString(", ", transform = { it.names.first() })
)
}
return opts
}
private fun configureClientOptions(): ClientOptions =
configureClientOptions(ClientOptions())
override fun main(vararg args: String) {
runBlocking(Unconfined) {
val compilerId = CompilerId()
val daemonOptions = configureDaemonOptions()
val daemonLaunchingOptions = configureDaemonJVMOptions(
inheritMemoryLimits = true,
inheritOtherJvmOptions = false,
inheritAdditionalProperties = true
)
val clientOptions = configureClientOptions()
val filteredArgs = args.asIterable().filterExtractProps(
compilerId,
daemonOptions,
daemonLaunchingOptions,
clientOptions,
prefix = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX
)
if (!clientOptions.stop) {
if (compilerId.compilerClasspath.none()) {
// attempt to find compiler to use
// attempt to find compiler to use
System.err.println("compiler wasn't explicitly specified, attempt to find appropriate jar")
detectCompilerClasspath()
?.let { compilerId.compilerClasspath = it }
}
if (compilerId.compilerClasspath.none())
throw IllegalArgumentException("Cannot find compiler jar")
else
log.info("desired compiler classpath: " + compilerId.compilerClasspath.joinToString(File.pathSeparator))
}
val daemon = connectToCompileService(
compilerId,
daemonLaunchingOptions,
daemonOptions,
DaemonReportingTargets(out = System.out),
autostart = !clientOptions.stop,
checkId = !clientOptions.stop
)
if (daemon == null) {
if (clientOptions.stop) {
System.err.println("No daemon found to shut down")
} else throw Exception("Unable to connect to daemon")
} else when {
clientOptions.stop -> {
log.info("Shutdown the daemon")
daemon.shutdown()
log.info("Daemon shut down successfully")
}
filteredArgs.none() -> {
// so far used only in tests
log.info(
"Warning: empty arguments list, only daemon check is performed: checkCompilerId() returns ${
daemon.checkCompilerId(
compilerId
)}"
)
}
else -> {
log.info("Executing daemon compilation with args: " + filteredArgs.joinToString(" "))
val servicesFacade =
CompilerCallbackServicesFacadeServerServerSide()
val serverRun = servicesFacade.runServer()
try {
val memBefore = daemon.getUsedMemory().get() / 1024
val startTime = System.nanoTime()
val compResults = createCompResults()
val compResultsServerRun = compResults.runServer()
val res = daemon.compile(
CompileService.NO_SESSION,
filteredArgs.toList().toTypedArray(),
CompilationOptions(
CompilerMode.NON_INCREMENTAL_COMPILER,
CompileService.TargetPlatform.JVM,
arrayOf(), // TODO ???
ReportSeverity.INFO.code, // TODO ???
arrayOf() // TODO ???
),
servicesFacade.clientSide,
compResults.clientSide
)
val endTime = System.nanoTime()
log.info("Compilation ${if (res.isGood) "succeeded" else "failed"}, result code: ${res.get()}")
val memAfter = daemon.getUsedMemory().get() / 1024
log.info("Compilation time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
log.info("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)")
// serverRun.await()
} finally {
// TODO ??
}
}
}
}
}
override fun createCompResults(): CompilationResultsServerSide = object : CompilationResultsServerSide {
override val clients = hashMapOf<Socket, Server.ClientInfo>()
override val serverSocketWithPort: ServerSocketWrapper
get() = resultsPort
private val resultsPort = findPortForSocket(
COMPILE_DAEMON_FIND_PORT_ATTEMPTS,
RESULTS_SERVER_PORTS_RANGE_START,
RESULTS_SERVER_PORTS_RANGE_END
)
private val resultsMap = hashMapOf<Int, MutableList<Serializable>>()
override val clientSide: CompilationResultsClientSide
get() = CompilationResultsClientSideImpl(resultsPort.port)
override suspend fun add(compilationResultCategory: Int, value: Serializable) {
synchronized(this) {
resultsMap.putIfAbsent(compilationResultCategory, mutableListOf())
resultsMap[compilationResultCategory]!!.add(value)
// TODO logger?
}
}
}
private fun detectCompilerClasspath(): List<String>? =
System.getProperty("java.class.path")
?.split(File.pathSeparator)
?.map { File(it).parentFile }
?.distinct()
?.mapNotNull {
it?.walk()
?.firstOrNull { it.name.equals(COMPILER_JAR_NAME, ignoreCase = true) }
}
?.firstOrNull()
?.let { listOf(it.absolutePath) }
// --- Implementation ---------------------------------------
@Synchronized
private inline fun <R> connectLoop(reportingTargets: DaemonReportingTargets, autostart: Boolean, body: (Boolean) -> R?): R? {
try {
var attempts = 1
while (true) {
val (res, err) = try {
body(attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS) to null
} catch (e: SocketException) {
null to e
} catch (e: ConnectException) {
null to e
} catch (e: ConnectIOException) {
null to e
} catch (e: UnmarshalException) {
null to e
} catch (e: RuntimeException) {
null to e
} catch (e: ClosedChannelException) {
null to e
}
if (res != null) return res
if (err != null) {
async {
reportingTargets.report(
DaemonReportCategory.INFO,
(if (attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) "no more retries on: " else "retrying($attempts) on: ")
+ err.toString()
)
}
}
if (attempts++ > DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) {
return null
}
}
} catch (e: Throwable) {
async { reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString()) }
}
return null
}
private fun tryFindSuitableDaemonOrNewOpts(
registryDir: File,
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
report: (DaemonReportCategory, String) -> Unit
): Deferred<Pair<CompileServiceAsync?, DaemonJVMOptions>> = async {
log.info("tryFindSuitableDaemonOrNewOpts")
registryDir.mkdirs()
val timestampMarker = createTempFile("kotlin-daemon-client-tsmarker", directory = registryDir)
val aliveWithMetadata = try {
log.info("walkDaemonsAsync... : ${registryDir.path}")
walkDaemonsAsync(registryDir, compilerId, timestampMarker, report = report).also {
log.info(
"daemons (${it.size}): ${it.map { "daemon(params : " + it.jvmOptions.jvmParams.joinToString(", ") + ")" }.joinToString(
", ", "[", "]"
)}"
)
}
} finally {
timestampMarker.delete()
}
log.info("daemons : ${aliveWithMetadata.map { it.daemon::class.java.name }}")
log.info("aliveWithMetadata: ${aliveWithMetadata.map { it.daemon::class.java.name }}")
val comparator = compareBy<DaemonWithMetadataAsync, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions })
.thenBy {
when (it.daemon) {
is CompileServiceAsyncWrapper -> 0
else -> 1
}
}
.thenBy(FileAgeComparator()) { it.runFile }
val optsCopy = daemonJVMOptions.copy()
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
aliveWithMetadata.maxWith(comparator)
?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }
?.let {
Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions))
}
// else combine all options from running daemon to get fattest option for a new daemon to runServer
?: Pair(null, aliveWithMetadata.fold(optsCopy, { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) }))
}
private suspend fun startDaemon(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets
): Boolean {
log.info("in startDaemon() - 0")
val javaExecutable = File(File(System.getProperty("java.home"), "bin"), "java")
log.info("in startDaemon() - 0.1")
val serverHostname = System.getProperty(JAVA_RMI_SERVER_HOSTNAME) ?: error("$JAVA_RMI_SERVER_HOSTNAME is not set!")
log.info("in startDaemon() - 0.2")
val platformSpecificOptions = listOf(
// hide daemon window
"-Djava.awt.headless=true",
"-D$JAVA_RMI_SERVER_HOSTNAME=$serverHostname"
)
log.info("in startDaemon() - 0.3")
val args = listOf(
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)
) +
platformSpecificOptions +
daemonJVMOptions.mappers.flatMap { it.toArgs("-") } +
COMPILER_DAEMON_CLASS_FQN_EXPERIMENTAL +
daemonOptions.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } +
compilerId.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) }
log.info("in startDaemon() - 1")
reportingTargets.report(DaemonReportCategory.DEBUG, "starting the daemon as: " + args.joinToString(" "))
val processBuilder = ProcessBuilder(args)
log.info("in startDaemon() - 2")
processBuilder.redirectErrorStream(true)
// assuming daemon process is deaf and (mostly) silent, so do not handle streams
log.info("daemon = launchProcessWithFallback")
val daemon =
launchProcessWithFallback(processBuilder, reportingTargets, "daemon client")
val isEchoRead = Semaphore(1)
isEchoRead.acquire()
val stdoutThread =
thread {
try {
daemon.inputStream
.reader()
.forEachLine {
log.info("daemon_process_report : $it")
if (it == COMPILE_DAEMON_IS_READY_MESSAGE) {
async {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"Received the message signalling that the daemon is ready"
)
}
isEchoRead.release()
//TODO return@forEachLine
} else {
async { reportingTargets.report(DaemonReportCategory.INFO, it, "daemon") }
}
}
} finally {
daemon.inputStream.close()
daemon.outputStream.close()
daemon.errorStream.close()
isEchoRead.release()
}
}
try {
// trying to wait for process
val daemonStartupTimeout = System.getProperty(COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY)?.let {
try {
it.toLong()
} catch (e: Exception) {
reportingTargets.report(
DaemonReportCategory.INFO,
"unable to interpret $COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY property ('$it'); using default timeout $DAEMON_DEFAULT_STARTUP_TIMEOUT_MS ms"
)
null
}
} ?: DAEMON_DEFAULT_STARTUP_TIMEOUT_MS
if (daemonOptions.runFilesPath.isNotEmpty()) {
log.info("daemonOptions.runFilesPath.isNotEmpty")
val succeeded = isEchoRead.tryAcquire(daemonStartupTimeout, TimeUnit.MILLISECONDS)
log.info("succeeded : $succeeded")
return when {
!isProcessAlive(daemon) -> {
log.info("!isProcessAlive(daemon)")
reportingTargets.report(
DaemonReportCategory.INFO,
"Daemon terminated unexpectedly with error code: ${daemon.exitValue()}"
)
false
}
!succeeded -> {
log.info("isProcessAlive!")
reportingTargets.report(DaemonReportCategory.INFO, "Unable to get response from daemon in $daemonStartupTimeout ms")
false
}
else -> true
}
} else
log.info("!daemonOptions.runFilesPath.isNotEmpty")
// without startEcho defined waiting for max timeout
Thread.sleep(daemonStartupTimeout)
return true
} finally {
// assuming that all important output is already done, the rest should be routed to the log by the daemon itself
if (stdoutThread.isAlive) {
// TODO: find better method to stop the thread, but seems it will require asynchronous consuming of the stream
stdoutThread.stop()
}
reportingTargets.out?.flush()
}
}
}
internal suspend fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null) {
val sourceMessage: String by lazy { source?.let { "[$it] $message" } ?: message }
out?.println("${category.name}: $sourceMessage")
messages?.add(DaemonReportMessage(category, sourceMessage))
messageCollector?.let {
when (category) {
DaemonReportCategory.DEBUG -> it.report(CompilerMessageSeverity.LOGGING, sourceMessage)
DaemonReportCategory.INFO -> it.report(CompilerMessageSeverity.INFO, sourceMessage)
DaemonReportCategory.EXCEPTION -> it.report(CompilerMessageSeverity.EXCEPTION, sourceMessage)
}
}
compilerServices?.let {
when (category) {
DaemonReportCategory.DEBUG -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.DEBUG, message, source)
DaemonReportCategory.INFO -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.INFO, message, source)
DaemonReportCategory.EXCEPTION -> it.report(ReportCategory.EXCEPTION, ReportSeverity.ERROR, message, source)
}
}
}
internal fun isProcessAlive(process: Process) =
try {
process.exitValue()
false
} catch (e: IllegalThreadStateException) {
true
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.daemon.client.KotlinRemoteReplCompilerClient
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.CompileServiceClientSide
import org.jetbrains.kotlin.daemon.common.experimental.findCallbackServerSocket
import org.jetbrains.kotlin.daemon.common.impls.ReportCategory
import org.jetbrains.kotlin.daemon.common.impls.ReportSeverity
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
// TODO: reduce number of ports used then SOCKET_ANY_FREE_PORT is passed (same problem with other calls)
open class KotlinRemoteReplCompilerClientAsync(
protected val compileService: CompileServiceAsync,
clientAliveFlagFile: File?,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
templateClasspath: List<File>,
templateClassName: String
) : KotlinRemoteReplCompilerClient {
val services = BasicCompilerServicesWithResultsFacadeServerServerSide(
messageCollector,
null,
findCallbackServerSocket()
)
override val sessionId = runBlocking {
compileService.leaseReplSession(
clientAliveFlagFile?.absolutePath,
args,
CompilationOptions(
CompilerMode.NON_INCREMENTAL_COMPILER,
targetPlatform,
arrayOf(
ReportCategory.COMPILER_MESSAGE.code,
ReportCategory.DAEMON_MESSAGE.code,
ReportCategory.EXCEPTION.code,
ReportCategory.OUTPUT_MESSAGE.code
),
ReportSeverity.INFO.code,
emptyArray()
),
services.clientSide,
templateClasspath,
templateClassName
).get()
}
// dispose should be called at the end of the repl lifetime to free daemon repl session and appropriate resources
override suspend fun dispose() {
compileService.releaseReplSession(sessionId)
}
override suspend fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> {
println("creating state...")
val stateRes = compileService.replCreateState(sessionId)
println("stateRes = $stateRes")
val state = stateRes.get()
println("state = $state")
return RemoteReplCompilerStateAsync(state, lock)
}
override suspend fun check(
state: IReplStageState<*>,
codeLine: ReplCodeLine
): ReplCheckResult =
compileService.replCheck(
sessionId,
state.asState(RemoteReplCompilerStateAsync::class.java).replStateFacade.getId(),
codeLine
).get()
override suspend fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult =
compileService.replCompile(
sessionId,
state.asState(RemoteReplCompilerStateAsync::class.java).replStateFacade.getId(),
codeLine
).get()
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.NativePlatformLauncherWrapper
import org.jetbrains.kotlin.daemon.common.impls.DaemonReportCategory
import java.io.IOException
suspend fun launchProcessWithFallback(
processBuilder: ProcessBuilder,
reportingTargets: DaemonReportingTargets,
reportingSource: String = "process launcher"
): Process =
try {
// A separate class to delay classloading until this point, where we can catch class loading errors in case then the native lib is not in the classpath
NativePlatformLauncherWrapper().launch(processBuilder)
} catch (e: UnsatisfiedLinkError) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"Could not start process with native process launcher, falling back to ProcessBuilder#start ($e)",
reportingSource
)
null
} catch (e: IOException) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"Could not start process with native process launcher, falling back to ProcessBuilder#start (${e.cause})",
reportingSource
)
null
} catch (e: NoClassDefFoundError) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start ($e)",
reportingSource
)
null
} catch (e: ClassNotFoundException) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start ($e)",
reportingSource
)
null
} ?: processBuilder.start()

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.daemon.client.RemoteReplCompilerStateHistory
import org.jetbrains.kotlin.daemon.common.ReplStateFacadeAsync
import org.jetbrains.kotlin.daemon.common.experimental.ReplStateFacadeClientSide
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantReadWriteLock
class RemoteReplCompilerStateHistoryAsync(private val state: RemoteReplCompilerStateAsync) : IReplStageHistory<Unit>,
AbstractList<ReplHistoryRecord<Unit>>() {
override val size: Int
get() = runBlocking { state.replStateFacade.getHistorySize() }
override fun get(index: Int): ReplHistoryRecord<Unit> = runBlocking {
ReplHistoryRecord(state.replStateFacade.historyGet(index), Unit)
}
override fun push(id: ILineId, item: Unit) {
throw NotImplementedError("push to remote history is not supported")
}
override fun pop(): ReplHistoryRecord<Unit>? {
throw NotImplementedError("pop from remote history is not supported")
}
override fun reset(): Iterable<ILineId> = runBlocking {
state.replStateFacade.historyReset().apply {
currentGeneration.incrementAndGet()
}
}
override fun resetTo(id: ILineId): Iterable<ILineId> = runBlocking {
state.replStateFacade.historyResetTo(id).apply {
currentGeneration.incrementAndGet()
}
}
val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN)
override val lock: ReentrantReadWriteLock get() = state.lock
}
class RemoteReplCompilerStateAsync(
internal val replStateFacade: ReplStateFacadeAsync,
override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
) : IReplStageState<Unit> {
override val currentGeneration: Int get() = (history as RemoteReplCompilerStateHistory).currentGeneration.get()
override val history: IReplStageHistory<Unit> =
RemoteReplCompilerStateHistoryAsync(this)
}

View File

@@ -36,6 +36,10 @@ dependencies {
nativePlatformVariants.forEach {
embeddedComponents(commonDep("net.rubygrapefruit", "native-platform", "-$it"))
}
compile(projectDist(":kotlin-reflect"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
}
sourceSets {

View File

@@ -20,25 +20,30 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.impls.*
import java.io.File
import java.io.Serializable
import java.rmi.server.UnicastRemoteObject
open class BasicCompilerServicesWithResultsFacadeServer(
val messageCollector: MessageCollector,
val outputsCollector: ((File, List<File>) -> Unit)? = null,
port: Int = SOCKET_ANY_FREE_PORT
val messageCollector: MessageCollector,
val outputsCollector: ((File, List<File>) -> Unit)? = null,
port: Int = SOCKET_ANY_FREE_PORT
) : CompilerServicesFacadeBase,
UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
{
UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) {
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
messageCollector.reportFromDaemon(outputsCollector, category, severity, message, attachment)
}
}
fun MessageCollector.reportFromDaemon(outputsCollector: ((File, List<File>) -> Unit)?, category: Int, severity: Int, message: String?, attachment: Serializable?) {
fun MessageCollector.reportFromDaemon(
outputsCollector: ((File, List<File>) -> Unit)?,
category: Int,
severity: Int,
message: String?,
attachment: Serializable?
) {
val reportCategory = ReportCategory.fromCode(category)
when (reportCategory) {
@@ -49,9 +54,8 @@ fun MessageCollector.reportFromDaemon(outputsCollector: ((File, List<File>) -> U
outputsCollector.invoke(it, outs.sourceFiles.toList())
}
}
}
else {
report(CompilerMessageSeverity.OUTPUT, message!!)
} else {
report(CompilerMessageSeverity.OUTPUT, message.orEmpty())
}
}
ReportCategory.EXCEPTION -> {
@@ -67,8 +71,7 @@ fun MessageCollector.reportFromDaemon(outputsCollector: ((File, List<File>) -> U
}
if (message != null) {
report(compilerSeverity, message, attachment as? CompilerMessageLocation)
}
else {
} else {
reportUnexpected(category, severity, message, attachment)
}
}
@@ -76,8 +79,7 @@ fun MessageCollector.reportFromDaemon(outputsCollector: ((File, List<File>) -> U
ReportCategory.IC_MESSAGE -> {
if (message != null) {
report(CompilerMessageSeverity.LOGGING, message)
}
else {
} else {
reportUnexpected(category, severity, message, attachment)
}
}
@@ -95,5 +97,8 @@ private fun MessageCollector.reportUnexpected(category: Int, severity: Int, mess
else -> CompilerMessageSeverity.LOGGING
}
report(compilerMessageSeverity, "Unexpected message: category=$category; severity=$severity; message='$message'; attachment=$attachment")
report(
compilerMessageSeverity,
"Unexpected message: category=$category; severity=$severity; message='$message'; attachment=$attachment"
)
}

View File

@@ -16,8 +16,12 @@
package org.jetbrains.kotlin.daemon.client
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.impls.*
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.daemon.common.impls.CompilerCallbackServicesFacade
import org.jetbrains.kotlin.daemon.common.impls.LoopbackNetworkInterface
import org.jetbrains.kotlin.daemon.common.impls.RmiFriendlyCompilationCanceledException
import org.jetbrains.kotlin.daemon.common.impls.SOCKET_ANY_FREE_PORT
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider

View File

@@ -1,487 +1,174 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.daemon.common.impls.CompilationResults
import org.jetbrains.kotlin.daemon.common.impls.DaemonReportCategory
import org.jetbrains.kotlin.daemon.common.impls.ReportSeverity
import org.jetbrains.kotlin.daemon.common.impls.SOCKET_ANY_FREE_PORT
import java.io.File
import java.io.OutputStream
import java.io.PrintStream
import java.net.SocketException
import java.rmi.ConnectException
import java.rmi.ConnectIOException
import java.rmi.UnmarshalException
import java.rmi.server.UnicastRemoteObject
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
import java.io.Serializable
class CompilationServices(
val incrementalCompilationComponents: IncrementalCompilationComponents? = null,
val lookupTracker: LookupTracker? = null,
val compilationCanceledStatus: CompilationCanceledStatus? = null
)
data class CompileServiceSession(val compileService: CompileServiceAsync, val sessionId: Int)
data class CompileServiceSession(val compileService: CompileService, val sessionId: Int)
fun org.jetbrains.kotlin.daemon.client.impls.CompileServiceSession.toWrapper() =
CompileServiceSession(this.compileService.toClient(), this.sessionId)
object KotlinCompilerClient {
class KotlinCompilerClient : KotlinCompilerDaemonClient {
val DAEMON_DEFAULT_STARTUP_TIMEOUT_MS = 10000L
val DAEMON_CONNECT_CYCLE_ATTEMPTS = 3
private val oldKotlinCompilerClient = org.jetbrains.kotlin.daemon.client.impls.KotlinCompilerClientImpl
val verboseReporting = System.getProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY) != null
override suspend fun connectToCompileService(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
checkId: Boolean
): CompileServiceAsync? = oldKotlinCompilerClient.connectToCompileService(
compilerId,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
checkId
)?.toClient()
fun getOrCreateClientFlagFile(daemonOptions: DaemonOptions): File =
// for jps property is passed from IDEA to JPS in KotlinBuildProcessParametersProvider
System.getProperty(COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY)
?.let(String::trimQuotes)
?.takeUnless(String::isBlank)
?.let(::File)
?.takeIf(File::exists)
?: makeAutodeletingFlagFile(baseDir = File(daemonOptions.runFilesPathOrDefault))
override suspend fun connectToCompileService(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean
): CompileServiceAsync? = oldKotlinCompilerClient.connectToCompileService(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart
)?.toClient()
fun connectToCompileService(compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean = true,
checkId: Boolean = true
): CompileService? {
val flagFile = getOrCreateClientFlagFile(daemonOptions)
return connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, reportingTargets, autostart)
override suspend fun connectAndLease(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
leaseSession: Boolean,
sessionAliveFlagFile: File?
): CompileServiceSession? = oldKotlinCompilerClient.connectAndLease(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
leaseSession,
sessionAliveFlagFile
)?.toWrapper()
override suspend fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions) =
oldKotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
override suspend fun leaseCompileSession(compilerService: CompileServiceAsync, aliveFlagPath: String?): Int =
oldKotlinCompilerClient.leaseCompileSession(compilerService.toRMI(), aliveFlagPath)
override suspend fun releaseCompileSession(
compilerService: CompileServiceAsync,
sessionId: Int
) = runBlocking {
oldKotlinCompilerClient.releaseCompileSession(compilerService.toRMI(), sessionId)
CompileService.CallResult.Ok() // TODO
}
fun connectToCompileService(compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean = true
): CompileService? =
connectAndLease(compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
leaseSession = false,
sessionAliveFlagFile = null)?.compileService
fun Profiler.toRMI() = object : org.jetbrains.kotlin.daemon.common.impls.Profiler {
override fun getCounters() = this@toRMI.getCounters()
fun connectAndLease(compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
leaseSession: Boolean,
sessionAliveFlagFile: File? = null
): CompileServiceSession? = connectLoop(reportingTargets, autostart) { isLastAttempt ->
override fun getTotalCounters() = this@toRMI.getTotalCounters()
fun CompileService.leaseImpl(): CompileServiceSession? {
// the newJVMOptions could be checked here for additional parameters, if needed
registerClient(clientAliveFlagFile.absolutePath)
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
if (!leaseSession) return CompileServiceSession(this, CompileService.NO_SESSION)
return leaseCompileSession(sessionAliveFlagFile?.absolutePath).takeUnless { it is CompileService.CallResult.Dying }?.let {
CompileServiceSession(this, it.get())
override fun <R> withMeasure(obj: Any?, body: () -> R): R = runBlocking {
this@toRMI.withMeasure(obj) {
body()
}
}
ensureServerHostnameIsSetUp()
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(File(daemonOptions.runFilesPath), compilerId, daemonJVMOptions, { cat, msg -> reportingTargets.report(cat, msg) })
}
if (service != null) {
service.leaseImpl()
override suspend fun compile(
compilerService: CompileServiceAsync,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
outputsCollector: ((File, List<File>) -> Unit)?,
compilerMode: CompilerMode,
reportSeverity: ReportSeverity,
profiler: Profiler
) = runBlocking {
oldKotlinCompilerClient.compile(
compilerService.toRMI(),
sessionId,
targetPlatform,
args,
messageCollector,
outputsCollector,
compilerMode,
reportSeverity,
SOCKET_ANY_FREE_PORT,
profiler.toRMI()
)
}
interface CompilationResultsServSideCompatible : CompilationResults {
}
private fun CompilationResultsServSideCompatible.toServer() =
object : CompilationResultsAsync {
override val clientSide: CompilationResultsAsync
get() = this
override suspend fun add(compilationResultCategory: Int, value: Serializable) =
this@toServer.add(compilationResultCategory, value)
}
else {
if (!isLastAttempt && autostart) {
if (startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)) {
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
override fun createCompResults(): CompilationResultsAsync {
val oldCompResults = object : CompilationResultsServSideCompatible {
private val resultsMap = hashMapOf<Int, MutableList<Serializable>>()
override fun add(compilationResultCategory: Int, value: Serializable) {
synchronized(this) {
resultsMap.putIfAbsent(compilationResultCategory, mutableListOf())
resultsMap[compilationResultCategory]!!.add(value)
// TODO logger?
}
}
null
}
return oldCompResults.toServer()
}
fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions): Unit {
connectToCompileService(compilerId, DaemonJVMOptions(), daemonOptions, DaemonReportingTargets(out = System.out), autostart = false, checkId = false)
?.shutdown()
}
override fun main(vararg args: String) = oldKotlinCompilerClient.main(*args)
fun shutdownCompileService(compilerId: CompilerId): Unit {
shutdownCompileService(compilerId, DaemonOptions())
}
fun leaseCompileSession(compilerService: CompileService, aliveFlagPath: String?): Int =
compilerService.leaseCompileSession(aliveFlagPath).get()
fun releaseCompileSession(compilerService: CompileService, sessionId: Int): Unit {
compilerService.releaseCompileSession(sessionId)
}
@Deprecated("Use other compile method", ReplaceWith("compile"))
fun compile(compilerService: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
out: OutputStream,
port: Int = SOCKET_ANY_FREE_PORT,
operationsTracer: RemoteOperationsTracer? = null
): Int {
val outStrm = RemoteOutputStreamServer(out, port = port)
return compilerService.remoteCompile(sessionId, targetPlatform, args, CompilerCallbackServicesFacadeServer(port = port), outStrm, CompileService.OutputFormat.PLAIN, outStrm, operationsTracer).get()
}
@Deprecated("Use non-deprecated compile method", ReplaceWith("compile"))
fun incrementalCompile(compileService: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
callbackServices: CompilationServices,
compilerOut: OutputStream,
daemonOut: OutputStream,
port: Int = SOCKET_ANY_FREE_PORT,
profiler: Profiler = DummyProfiler(),
operationsTracer: RemoteOperationsTracer? = null
): Int = profiler.withMeasure(this) {
compileService.remoteIncrementalCompile(
sessionId,
targetPlatform,
args,
CompilerCallbackServicesFacadeServer(incrementalCompilationComponents = callbackServices.incrementalCompilationComponents,
lookupTracker = callbackServices.lookupTracker,
compilationCanceledStatus = callbackServices.compilationCanceledStatus,
port = port),
RemoteOutputStreamServer(compilerOut, port),
CompileService.OutputFormat.XML,
RemoteOutputStreamServer(daemonOut, port),
operationsTracer).get()
}
fun compile(compilerService: CompileService,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
outputsCollector: ((File, List<File>) -> Unit)? = null,
compilerMode: CompilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
reportSeverity: ReportSeverity = ReportSeverity.INFO,
port: Int = SOCKET_ANY_FREE_PORT,
profiler: Profiler = DummyProfiler()
): Int = profiler.withMeasure(this) {
val services = BasicCompilerServicesWithResultsFacadeServer(messageCollector, outputsCollector, port)
compilerService.compile(
sessionId,
args,
CompilationOptions(
compilerMode,
targetPlatform,
arrayOf(ReportCategory.COMPILER_MESSAGE.code, ReportCategory.DAEMON_MESSAGE.code, ReportCategory.EXCEPTION.code, ReportCategory.OUTPUT_MESSAGE.code),
reportSeverity.code,
emptyArray()),
services,
null
).get()
}
val COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY: String = "kotlin.daemon.client.options"
data class ClientOptions(
var stop: Boolean = false
) : OptionsGroup {
override val mappers: List<PropMapper<*, *, *>>
get() = listOf(BoolPropMapper(this, ClientOptions::stop))
}
private fun configureClientOptions(opts: ClientOptions): ClientOptions {
System.getProperty(COMPILE_DAEMON_CLIENT_OPTIONS_PROPERTY)?.let {
val unrecognized = it.trimQuotes().split(",").filterExtractProps(opts.mappers, "")
if (unrecognized.any())
throw IllegalArgumentException(
"Unrecognized client options passed via property $COMPILE_DAEMON_OPTIONS_PROPERTY: " + unrecognized.joinToString(" ") +
"\nSupported options: " + opts.mappers.joinToString(", ", transform = { it.names.first() }))
}
return opts
}
private fun configureClientOptions(): ClientOptions = configureClientOptions(ClientOptions())
@JvmStatic
fun main(vararg args: String) {
val compilerId = CompilerId()
val daemonOptions = configureDaemonOptions()
val daemonLaunchingOptions = configureDaemonJVMOptions(inheritMemoryLimits = true, inheritOtherJvmOptions = false, inheritAdditionalProperties = true)
val clientOptions = configureClientOptions()
val filteredArgs = args.asIterable().filterExtractProps(compilerId, daemonOptions, daemonLaunchingOptions, clientOptions, prefix = COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX)
if (!clientOptions.stop) {
if (compilerId.compilerClasspath.none()) {
// attempt to find compiler to use
System.err.println("compiler wasn't explicitly specified, attempt to find appropriate jar")
detectCompilerClasspath()
?.let { compilerId.compilerClasspath = it }
}
if (compilerId.compilerClasspath.none())
throw IllegalArgumentException("Cannot find compiler jar")
else
println("desired compiler classpath: " + compilerId.compilerClasspath.joinToString(File.pathSeparator))
}
val daemon = connectToCompileService(compilerId, daemonLaunchingOptions, daemonOptions, DaemonReportingTargets(out = System.out), autostart = !clientOptions.stop, checkId = !clientOptions.stop)
if (daemon == null) {
if (clientOptions.stop) {
System.err.println("No daemon found to shut down")
}
else throw Exception("Unable to connect to daemon")
}
else when {
clientOptions.stop -> {
println("Shutdown the daemon")
daemon.shutdown()
println("Daemon shut down successfully")
}
filteredArgs.none() -> {
// so far used only in tests
println("Warning: empty arguments list, only daemon check is performed: checkCompilerId() returns ${daemon.checkCompilerId(compilerId)}")
}
else -> {
println("Executing daemon compilation with args: " + filteredArgs.joinToString(" "))
val outStrm = RemoteOutputStreamServer(System.out)
val servicesFacade = CompilerCallbackServicesFacadeServer()
try {
val memBefore = daemon.getUsedMemory().get() / 1024
val startTime = System.nanoTime()
val res = daemon.remoteCompile(CompileService.NO_SESSION, CompileService.TargetPlatform.JVM, filteredArgs.toList().toTypedArray(), servicesFacade, outStrm, CompileService.OutputFormat.PLAIN, outStrm, null)
val endTime = System.nanoTime()
println("Compilation ${if (res.isGood) "succeeded" else "failed"}, result code: ${res.get()}")
val memAfter = daemon.getUsedMemory().get() / 1024
println("Compilation time: " + TimeUnit.NANOSECONDS.toMillis(endTime - startTime) + " ms")
println("Used memory $memAfter (${"%+d".format(memAfter - memBefore)} kb)")
}
finally {
// forcing RMI to unregister all objects and stop
UnicastRemoteObject.unexportObject(servicesFacade, true)
UnicastRemoteObject.unexportObject(outStrm, true)
}
}
}
}
fun detectCompilerClasspath(): List<String>? =
System.getProperty("java.class.path")
?.split(File.pathSeparator)
?.map { File(it).parentFile }
?.distinct()
?.mapNotNull {
it?.walk()
?.firstOrNull { it.name.equals(COMPILER_JAR_NAME, ignoreCase = true) }
}
?.firstOrNull()
?.let { listOf(it.absolutePath) }
// --- Implementation ---------------------------------------
@Synchronized
private inline fun <R> connectLoop(reportingTargets: DaemonReportingTargets, autostart: Boolean, body: (Boolean) -> R?): R? {
try {
var attempts = 1
while (true) {
val (res, err) = try {
body(attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS) to null
}
catch (e: SocketException) { null to e }
catch (e: ConnectException) { null to e }
catch (e: ConnectIOException) { null to e }
catch (e: UnmarshalException) { null to e }
catch (e: RuntimeException) { null to e }
if (res != null) return res
if (err != null) {
reportingTargets.report(DaemonReportCategory.INFO,
(if (attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) "no more retries on: " else "retrying($attempts) on: ")
+ err.toString())
}
if (attempts++ > DAEMON_CONNECT_CYCLE_ATTEMPTS || !autostart) {
return null
}
}
}
catch (e: Throwable) {
reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString())
}
return null
}
private fun tryFindSuitableDaemonOrNewOpts(registryDir: File, compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, report: (DaemonReportCategory, String) -> Unit): Pair<CompileService?, DaemonJVMOptions> {
registryDir.mkdirs()
val timestampMarker = createTempFile("kotlin-daemon-client-tsmarker", directory = registryDir)
val aliveWithMetadata = try {
walkDaemons(registryDir, compilerId, timestampMarker, report = report).toList()
}
finally {
timestampMarker.delete()
}
val comparator = compareBy<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions })
.thenBy(FileAgeComparator()) { it.runFile }
val optsCopy = daemonJVMOptions.copy()
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
return aliveWithMetadata.maxWith(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let {
Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions))
}
// else combine all options from running daemon to get fattest option for a new daemon to run
?: Pair(null, aliveWithMetadata.fold(optsCopy, { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) }))
}
private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, reportingTargets: DaemonReportingTargets): Boolean {
val javaExecutable = File(File(System.getProperty("java.home"), "bin"), "java")
val serverHostname = System.getProperty(JAVA_RMI_SERVER_HOSTNAME) ?: error("$JAVA_RMI_SERVER_HOSTNAME is not set!")
val platformSpecificOptions = listOf(
// hide daemon window
"-Djava.awt.headless=true",
"-D$JAVA_RMI_SERVER_HOSTNAME=$serverHostname")
val args = listOf(
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
platformSpecificOptions +
daemonJVMOptions.mappers.flatMap { it.toArgs("-") } +
COMPILER_DAEMON_CLASS_FQN +
daemonOptions.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) } +
compilerId.mappers.flatMap { it.toArgs(COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX) }
reportingTargets.report(DaemonReportCategory.DEBUG, "starting the daemon as: " + args.joinToString(" "))
val processBuilder = ProcessBuilder(args)
processBuilder.redirectErrorStream(true)
// assuming daemon process is deaf and (mostly) silent, so do not handle streams
val daemon = launchProcessWithFallback(processBuilder, reportingTargets, "daemon client")
val isEchoRead = Semaphore(1)
isEchoRead.acquire()
val stdoutThread =
thread {
try {
daemon.inputStream
.reader()
.forEachLine {
if (it == COMPILE_DAEMON_IS_READY_MESSAGE) {
reportingTargets.report(DaemonReportCategory.DEBUG, "Received the message signalling that the daemon is ready")
isEchoRead.release()
return@forEachLine
}
else {
reportingTargets.report(DaemonReportCategory.INFO, it, "daemon")
}
}
}
finally {
daemon.inputStream.close()
daemon.outputStream.close()
daemon.errorStream.close()
isEchoRead.release()
}
}
try {
// trying to wait for process
val daemonStartupTimeout = System.getProperty(COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY)?.let {
try {
it.toLong()
}
catch (e: Exception) {
reportingTargets.report(DaemonReportCategory.INFO, "unable to interpret $COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY property ('$it'); using default timeout $DAEMON_DEFAULT_STARTUP_TIMEOUT_MS ms")
null
}
} ?: DAEMON_DEFAULT_STARTUP_TIMEOUT_MS
if (daemonOptions.runFilesPath.isNotEmpty()) {
val succeeded = isEchoRead.tryAcquire(daemonStartupTimeout, TimeUnit.MILLISECONDS)
return when {
!isProcessAlive(daemon) -> {
reportingTargets.report(DaemonReportCategory.INFO, "Daemon terminated unexpectedly with error code: ${daemon.exitValue()}")
false
}
!succeeded -> {
reportingTargets.report(DaemonReportCategory.INFO, "Unable to get response from daemon in $daemonStartupTimeout ms")
false
}
else -> true
}
}
else
// without startEcho defined waiting for max timeout
Thread.sleep(daemonStartupTimeout)
return true
}
finally {
// assuming that all important output is already done, the rest should be routed to the log by the daemon itself
if (stdoutThread.isAlive) {
// TODO: find better method to stop the thread, but seems it will require asynchronous consuming of the stream
stdoutThread.stop()
}
reportingTargets.out?.flush()
}
}
}
data class DaemonReportMessage(val category: DaemonReportCategory, val message: String)
class DaemonReportingTargets(val out: PrintStream? = null,
val messages: MutableCollection<DaemonReportMessage>? = null,
val messageCollector: MessageCollector? = null,
val compilerServices: CompilerServicesFacadeBase? = null)
internal fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String? = null) {
val sourceMessage: String by lazy { source?.let { "[$it] $message" } ?: message }
out?.println("${category.name}: $sourceMessage")
messages?.add(DaemonReportMessage(category, sourceMessage))
messageCollector?.let {
when (category) {
DaemonReportCategory.DEBUG -> it.report(CompilerMessageSeverity.LOGGING, sourceMessage)
DaemonReportCategory.INFO -> it.report(CompilerMessageSeverity.INFO, sourceMessage)
DaemonReportCategory.EXCEPTION -> it.report(CompilerMessageSeverity.EXCEPTION, sourceMessage)
}
}
compilerServices?.let {
when (category) {
DaemonReportCategory.DEBUG -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.DEBUG, message, source)
DaemonReportCategory.INFO -> it.report(ReportCategory.DAEMON_MESSAGE, ReportSeverity.INFO, message, source)
DaemonReportCategory.EXCEPTION -> it.report(ReportCategory.EXCEPTION, ReportSeverity.ERROR, message, source)
}
}
}
internal fun isProcessAlive(process: Process) =
try {
process.exitValue()
false
}
catch (e: IllegalThreadStateException) {
true
}
class DaemonReportingTargets(
val out: PrintStream? = null,
val messages: MutableCollection<DaemonReportMessage>? = null,
val messageCollector: MessageCollector? = null,
val compilerServices: CompilerServicesFacadeBaseAsync? = null
)

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.client.KotlinCompilerDaemonClient.Companion.instantiate
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.DummyProfiler
import org.jetbrains.kotlin.daemon.common.Profiler
import org.jetbrains.kotlin.daemon.common.impls.ReportSeverity
import java.io.File
interface KotlinCompilerDaemonClient {
suspend fun connectToCompileService(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean = true,
checkId: Boolean = true
): CompileServiceAsync?
suspend fun connectToCompileService(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean = true
): CompileServiceAsync?
suspend fun connectAndLease(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
leaseSession: Boolean,
sessionAliveFlagFile: File? = null
): CompileServiceSession?
suspend fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions)
suspend fun leaseCompileSession(compilerService: CompileServiceAsync, aliveFlagPath: String?): Int
suspend fun releaseCompileSession(compilerService: CompileServiceAsync, sessionId: Int): CompileService.CallResult<Unit>
suspend fun compile(
compilerService: CompileServiceAsync,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
outputsCollector: ((File, List<File>) -> Unit)? = null,
compilerMode: CompilerMode = CompilerMode.NON_INCREMENTAL_COMPILER,
reportSeverity: ReportSeverity = ReportSeverity.INFO,
profiler: Profiler = DummyProfiler()
): Int
fun createCompResults(): CompilationResultsAsync
fun main(vararg args: String)
companion object {
fun instantiate(version: Version): KotlinCompilerDaemonClient =
when (version) {
Version.RMI ->
ClassLoader
.getSystemClassLoader()
.loadClass("org.jetbrains.kotlin.daemon.client.KotlinCompilerClient")
.newInstance() as KotlinCompilerDaemonClient
Version.SOCKETS ->
ClassLoader
.getSystemClassLoader()
.loadClass("org.jetbrains.kotlin.daemon.client.experimental.KotlinCompilerClient")
.newInstance() as KotlinCompilerDaemonClient
}
}
}
object KotlinCompilerClientInstance {
const val RMI_FLAG = "-old"
const val SOCKETS_FLAG = "-new"
@JvmStatic
fun main(vararg args: String) {
val clientInstance: KotlinCompilerDaemonClient? = when (args.last()) {
SOCKETS_FLAG ->
instantiate(Version.SOCKETS)
else ->
instantiate(Version.RMI)
}
clientInstance?.main(*args.sliceArray(0..args.lastIndex))
}
}

View File

@@ -1,71 +1,87 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.cli.common.repl.IReplStageState
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.cli.common.repl.experimental.ReplCompiler
import org.jetbrains.kotlin.daemon.client.impls.KotlinRemoteReplCompilerClientImpl
import org.jetbrains.kotlin.daemon.common.CompileService
import org.jetbrains.kotlin.daemon.common.CompileServiceAsync
import org.jetbrains.kotlin.daemon.common.impls.SOCKET_ANY_FREE_PORT
import org.jetbrains.kotlin.daemon.common.toRMI
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
// TODO: reduce number of ports used then SOCKET_ANY_FREE_PORT is passed (same problem with other calls)
interface KotlinRemoteReplCompilerClient : ReplCompiler {
open class KotlinRemoteReplCompilerClient(
protected val compileService: CompileService,
clientAliveFlagFile: File?,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
templateClasspath: List<File>,
templateClassName: String,
port: Int = SOCKET_ANY_FREE_PORT
) : ReplCompiler {
val services = BasicCompilerServicesWithResultsFacadeServer(messageCollector, null, port)
val sessionId = compileService.leaseReplSession(
clientAliveFlagFile?.absolutePath,
args,
CompilationOptions(
CompilerMode.NON_INCREMENTAL_COMPILER,
targetPlatform,
arrayOf(ReportCategory.COMPILER_MESSAGE.code, ReportCategory.DAEMON_MESSAGE.code, ReportCategory.EXCEPTION.code, ReportCategory.OUTPUT_MESSAGE.code),
ReportSeverity.INFO.code,
emptyArray()),
services,
templateClasspath,
templateClassName
).get()
val sessionId: Int
// dispose should be called at the end of the repl lifetime to free daemon repl session and appropriate resources
open fun dispose() {
try {
compileService.releaseReplSession(sessionId)
}
catch (ex: java.rmi.RemoteException) {
// assuming that communication failed and daemon most likely is already down
}
suspend fun dispose()
override suspend fun createState(lock: ReentrantReadWriteLock): IReplStageState<*>
override suspend fun check(
state: IReplStageState<*>,
codeLine: ReplCodeLine
): ReplCheckResult
override suspend fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult
companion object {
fun instantiate(
compileService: CompileServiceAsync,
clientAliveFlagFile: File?,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
templateClasspath: List<File>,
templateClassName: String,
port: Int = SOCKET_ANY_FREE_PORT
): KotlinRemoteReplCompilerClient =
when (compileService::class.java.simpleName) {
"CompileServiceClientSideImpl" ->
ClassLoader
.getSystemClassLoader()
.loadClass("org.jetbrains.kotlin.daemon.client.experimental.KotlinRemoteReplCompilerClientAsync")
.getDeclaredConstructor(
CompileServiceAsync::class.java,
File::class.java,
CompileService.TargetPlatform::class.java,
Array<out String>::class.java,
MessageCollector::class.java,
List::class.java,
String::class.java,
Int::class.java
)
.newInstance(
compileService,
clientAliveFlagFile,
targetPlatform,
args,
messageCollector,
templateClasspath,
templateClassName,
port
) as KotlinRemoteReplCompilerClient
else -> KotlinRemoteReplCompilerWrapper(
KotlinRemoteReplCompilerClientImpl(
compileService.toRMI(),
clientAliveFlagFile,
targetPlatform,
args,
messageCollector,
templateClasspath,
templateClassName
)
)
}
}
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
RemoteReplCompilerState(compileService.replCreateState(sessionId).get(), lock)
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult =
compileService.replCheck(sessionId, state.asState(RemoteReplCompilerState::class.java).replStateFacade.getId(), codeLine).get()
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult =
compileService.replCompile(sessionId, state.asState(RemoteReplCompilerState::class.java).replStateFacade.getId(), codeLine).get()
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client
import org.jetbrains.kotlin.cli.common.repl.IReplStageState
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.daemon.client.impls.KotlinRemoteReplCompilerClientImpl
import java.util.concurrent.locks.ReentrantReadWriteLock
class KotlinRemoteReplCompilerWrapper(
val oldReplCompiler: KotlinRemoteReplCompilerClientImpl
) : KotlinRemoteReplCompilerClient {
override suspend fun dispose() = oldReplCompiler.dispose()
override suspend fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
oldReplCompiler.createState(lock)
override suspend fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult =
oldReplCompiler.check(state, codeLine)
override suspend fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult =
oldReplCompiler.compile(state, codeLine)
override val sessionId: Int = oldReplCompiler.sessionId
}

View File

@@ -16,43 +16,59 @@
package org.jetbrains.kotlin.daemon.client
import org.jetbrains.kotlin.daemon.common.DaemonReportCategory
import org.jetbrains.kotlin.daemon.client.impls.report
import org.jetbrains.kotlin.daemon.common.impls.DaemonReportCategory
import java.io.IOException
private class NativePlatformLauncherWrapper {
class NativePlatformLauncherWrapper {
private val nativeLauncher: net.rubygrapefruit.platform.ProcessLauncher by lazy {
net.rubygrapefruit.platform.Native.get(net.rubygrapefruit.platform.ProcessLauncher::class.java)
}
fun launch(processBuilder: ProcessBuilder): Process =
try {
nativeLauncher.start(processBuilder)
}
catch (e: net.rubygrapefruit.platform.NativeException) {
throw IOException(e)
}
try {
nativeLauncher.start(processBuilder)
} catch (e: net.rubygrapefruit.platform.NativeException) {
throw IOException(e)
}
}
fun launchProcessWithFallback(processBuilder: ProcessBuilder, reportingTargets: DaemonReportingTargets, reportingSource: String = "process launcher"): Process =
try {
// A separate class to delay classloading until this point, where we can catch class loading errors in case then the native lib is not in the classpath
NativePlatformLauncherWrapper().launch(processBuilder)
}
catch (e: UnsatisfiedLinkError) {
reportingTargets.report(DaemonReportCategory.DEBUG, "Could not start process with native process launcher, falling back to ProcessBuilder#start ($e)", reportingSource)
null
}
catch (e: IOException) {
reportingTargets.report(DaemonReportCategory.DEBUG, "Could not start process with native process launcher, falling back to ProcessBuilder#start (${e.cause})", reportingSource)
null
}
catch (e: NoClassDefFoundError) {
reportingTargets.report(DaemonReportCategory.DEBUG, "net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start ($e)", reportingSource)
null
}
catch (e: ClassNotFoundException) {
reportingTargets.report(DaemonReportCategory.DEBUG, "net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start ($e)", reportingSource)
null
}
?: processBuilder.start()
fun launchProcessWithFallback(
processBuilder: ProcessBuilder,
reportingTargets: DaemonReportingTargets,
reportingSource: String = "process launcher"
): Process =
try {
// A separate class to delay classloading until this point, where we can catch class loading errors in case then the native lib is not in the classpath
NativePlatformLauncherWrapper().launch(processBuilder)
} catch (e: UnsatisfiedLinkError) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"Could not start process with native process launcher, falling back to ProcessBuilder#start ($e)",
reportingSource
)
null
} catch (e: IOException) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"Could not start process with native process launcher, falling back to ProcessBuilder#start (${e.cause})",
reportingSource
)
null
} catch (e: NoClassDefFoundError) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start ($e)",
reportingSource
)
null
} catch (e: ClassNotFoundException) {
reportingTargets.report(
DaemonReportCategory.DEBUG,
"net.rubygrapefruit.platform library is not in the classpath, falling back to ProcessBuilder#start ($e)",
reportingSource
)
null
}
?: processBuilder.start()

View File

@@ -16,9 +16,9 @@
package org.jetbrains.kotlin.daemon.client
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
import org.jetbrains.kotlin.daemon.common.RemoteInputStream
import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
import org.jetbrains.kotlin.daemon.common.impls.LoopbackNetworkInterface
import org.jetbrains.kotlin.daemon.common.impls.RemoteInputStream
import org.jetbrains.kotlin.daemon.common.impls.SOCKET_ANY_FREE_PORT
import java.io.InputStream
import java.rmi.server.UnicastRemoteObject

View File

@@ -16,10 +16,9 @@
package org.jetbrains.kotlin.daemon.client
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
import org.jetbrains.kotlin.daemon.common.RemoteOutputStream
import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
import org.jetbrains.kotlin.daemon.common.impls.LoopbackNetworkInterface
import org.jetbrains.kotlin.daemon.common.impls.RemoteOutputStream
import org.jetbrains.kotlin.daemon.common.impls.SOCKET_ANY_FREE_PORT
import java.io.OutputStream
import java.rmi.server.UnicastRemoteObject

View File

@@ -17,13 +17,14 @@
package org.jetbrains.kotlin.daemon.client
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.daemon.common.ReplStateFacade
import org.jetbrains.kotlin.daemon.common.impls.ReplStateFacade
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantReadWriteLock
// NOTE: the lock is local
// TODO: verify that locla lock doesn't lead to any synch problems
class RemoteReplCompilerStateHistory(private val state: RemoteReplCompilerState) : IReplStageHistory<Unit>, AbstractList<ReplHistoryRecord<Unit>>() {
class RemoteReplCompilerStateHistory(private val state: RemoteReplCompilerState) : IReplStageHistory<Unit>,
AbstractList<ReplHistoryRecord<Unit>>() {
override val size: Int
get() = state.replStateFacade.getHistorySize()
@@ -50,7 +51,10 @@ class RemoteReplCompilerStateHistory(private val state: RemoteReplCompilerState)
override val lock: ReentrantReadWriteLock get() = state.lock
}
class RemoteReplCompilerState(internal val replStateFacade: ReplStateFacade, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<Unit> {
class RemoteReplCompilerState(
internal val replStateFacade: ReplStateFacade,
override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
) : IReplStageState<Unit> {
override val currentGeneration: Int get() = (history as RemoteReplCompilerStateHistory).currentGeneration.get()

View File

@@ -0,0 +1,181 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.experimental.old
import io.ktor.network.sockets.Socket
import kotlinx.coroutines.experimental.runBlocking
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.experimental.common.KotlinCompilerDaemonClient
import org.jetbrains.kotlin.daemon.client.experimental.new.CompileServiceSession
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.experimental.*
import org.jetbrains.kotlin.daemon.common.experimental.Profiler
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import java.io.File
import java.io.Serializable
object KotlinCompilerClient : KotlinCompilerDaemonClient {
private val oldKotlinCompilerClient = org.jetbrains.kotlin.daemon.client.KotlinCompilerClient
override suspend fun connectToCompileService(
compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
checkId: Boolean
): CompileServiceClientSide? = oldKotlinCompilerClient.connectToCompileService(
compilerId,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
checkId
)?.toClient()
override suspend fun connectToCompileService(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean
): CompileServiceClientSide? = oldKotlinCompilerClient.connectToCompileService(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart
)?.toClient()
private fun org.jetbrains.kotlin.daemon.client.CompileServiceSession.toWrapper() = CompileServiceSession(
this.compileService.toClient(),
this.sessionId
)
private fun CompileServiceSession.unwrap() = org.jetbrains.kotlin.daemon.client.CompileServiceSession(
this.compileService.toRMI(),
this.sessionId
)
override suspend fun connectAndLease(
compilerId: CompilerId,
clientAliveFlagFile: File,
daemonJVMOptions: DaemonJVMOptions,
daemonOptions: DaemonOptions,
reportingTargets: DaemonReportingTargets,
autostart: Boolean,
leaseSession: Boolean,
sessionAliveFlagFile: File?
): CompileServiceSession? = oldKotlinCompilerClient.connectAndLease(
compilerId,
clientAliveFlagFile,
daemonJVMOptions,
daemonOptions,
reportingTargets,
autostart,
leaseSession,
sessionAliveFlagFile
)?.toWrapper()
override suspend fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions) =
oldKotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
override suspend fun leaseCompileSession(compilerService: CompileServiceClientSide, aliveFlagPath: String?): Int =
oldKotlinCompilerClient.leaseCompileSession(compilerService.toRMI(), aliveFlagPath)
override suspend fun releaseCompileSession(
compilerService: CompileServiceClientSide,
sessionId: Int
) = runBlocking {
oldKotlinCompilerClient.releaseCompileSession(compilerService.toRMI(), sessionId)
CompileService.CallResult.Ok() // TODO
}
fun Profiler.toRMI() = object : org.jetbrains.kotlin.daemon.common.Profiler {
override fun getCounters() = this@toRMI.getCounters()
override fun getTotalCounters() = this@toRMI.getTotalCounters()
override fun <R> withMeasure(obj: Any?, body: () -> R): R = runBlocking {
this@toRMI.withMeasure(obj) {
body()
}
}
}
override suspend fun compile(
compilerService: CompileServiceClientSide,
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
outputsCollector: ((File, List<File>) -> Unit)?,
compilerMode: CompilerMode,
reportSeverity: ReportSeverity,
profiler: Profiler
) = runBlocking {
oldKotlinCompilerClient.compile(
compilerService.toRMI(),
sessionId,
targetPlatform,
args,
messageCollector,
outputsCollector,
compilerMode,
reportSeverity,
SOCKET_ANY_FREE_PORT,
profiler.toRMI()
)
}
interface CompilationResultsServSideCompatible : CompilationResults {
val clients: HashMap<Socket, Server.ClientInfo>
}
private fun CompilationResultsServSideCompatible.toServer() = object : CompilationResultsServerSide {
override suspend fun add(compilationResultCategory: Int, value: Serializable) =
this@toServer.add(compilationResultCategory, value)
override val clientSide: CompilationResultsClientSide
get() = this@toServer.toClient()
override val serverSocketWithPort: ServerSocketWrapper
get() = TODO("not implemented")
override val clients: HashMap<Socket, Server.ClientInfo>
get() = this@toServer.clients
}
override fun createCompResults(): CompilationResultsServerSide {
val oldCompResults = object : CompilationResultsServSideCompatible {
override val clients = hashMapOf<Socket, Server.ClientInfo>()
private val resultsPort = findPortForSocket(
COMPILE_DAEMON_FIND_PORT_ATTEMPTS,
RESULTS_SERVER_PORTS_RANGE_START,
RESULTS_SERVER_PORTS_RANGE_END
)
private val resultsMap = hashMapOf<Int, MutableList<Serializable>>()
override fun add(compilationResultCategory: Int, value: Serializable) {
synchronized(this) {
resultsMap.putIfAbsent(compilationResultCategory, mutableListOf())
resultsMap[compilationResultCategory]!!.add(value)
// TODO logger?
}
}
}
return oldCompResults.toServer()
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.client.impls
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.daemon.client.BasicCompilerServicesWithResultsFacadeServer
import org.jetbrains.kotlin.daemon.client.RemoteReplCompilerState
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.impls.ReportCategory
import org.jetbrains.kotlin.daemon.common.impls.ReportSeverity
import org.jetbrains.kotlin.daemon.common.impls.SOCKET_ANY_FREE_PORT
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
// TODO: reduce number of ports used then SOCKET_ANY_FREE_PORT is passed (same problem with other calls)
open class KotlinRemoteReplCompilerClientImpl(
val compileService: CompileService,
clientAliveFlagFile: File?,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
messageCollector: MessageCollector,
templateClasspath: List<File>,
templateClassName: String,
port: Int = SOCKET_ANY_FREE_PORT
) : ReplCompiler {
val services = BasicCompilerServicesWithResultsFacadeServer(messageCollector, null, port)
val sessionId = compileService.leaseReplSession(
clientAliveFlagFile?.absolutePath,
args,
CompilationOptions(
CompilerMode.NON_INCREMENTAL_COMPILER,
targetPlatform,
arrayOf(
ReportCategory.COMPILER_MESSAGE.code,
ReportCategory.DAEMON_MESSAGE.code,
ReportCategory.EXCEPTION.code,
ReportCategory.OUTPUT_MESSAGE.code
),
ReportSeverity.INFO.code,
emptyArray()
),
services,
templateClasspath,
templateClassName
).get()
// dispose should be called at the end of the repl lifetime to free daemon repl session and appropriate resources
open fun dispose() {
try {
compileService.releaseReplSession(sessionId)
} catch (ex: java.rmi.RemoteException) {
// assuming that communication failed and daemon most likely is already down
}
}
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
RemoteReplCompilerState(compileService.replCreateState(sessionId).get(), lock)
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult =
compileService.replCheck(sessionId, state.asState(RemoteReplCompilerState::class.java).replStateFacade.getId(), codeLine).get()
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult =
compileService.replCompile(sessionId, state.asState(RemoteReplCompilerState::class.java).replStateFacade.getId(), codeLine).get()
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
import com.sun.javafx.scene.CameraHelper.project
plugins {
java
kotlin("jvm")
id("jps-compatible")
}
jvmTarget = "1.6"
dependencies {
compile(project(":core:descriptors"))
compile(project(":core:descriptors.jvm"))
compile(project(":compiler:util"))
compile(project(":compiler:cli-common"))
compile(project(":kotlin-stdlib"))
compileOnly(project(":compiler:daemon-common"))
compileOnly(project(":js:js.frontend"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
compile(projectDist(":kotlin-reflect"))
compile(project(":kotlin-reflect-api"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
compile(commonDep("io.ktor", "ktor-network")) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-reflect")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-jdk8")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-common")
}
}
sourceSets {
"main" { projectDefault() }
"test" {}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import java.util.concurrent.TimeUnit
val RMI_WRAPPER_PORTS_RANGE_START: Int = 13001
val RMI_WRAPPER_PORTS_RANGE_END: Int = 14000
val REPL_SERVER_PORTS_RANGE_START: Int = 14001
val REPL_SERVER_PORTS_RANGE_END: Int = 15000
val CALLBACK_SERVER_PORTS_RANGE_START: Int = 15001
val CALLBACK_SERVER_PORTS_RANGE_END: Int = 16000
val RESULTS_SERVER_PORTS_RANGE_START: Int = 16001
val RESULTS_SERVER_PORTS_RANGE_END: Int = 17000
val COMPILER_DAEMON_CLASS_FQN_EXPERIMENTAL: String = "org.jetbrains.kotlin.daemon.experimental.KotlinCompileDaemon"
val FIRST_HANDSHAKE_BYTE_TOKEN = byteArrayOf(1, 2, 3, 4)
val AUTH_TIMEOUT_IN_MILLISECONDS = 200L
val DAEMON_PERIODIC_CHECK_INTERVAL_MS = 1000L
val DAEMON_PERIODIC_SELDOM_CHECK_INTERVAL_MS = 60000L
val KEEPALIVE_PERIOD = 2000L
val KEEPALIVE_PERIOD_SERVER = 4000L

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.runWithTimeout
import org.jetbrains.kotlin.daemon.common.impls.DaemonReportCategory
import org.jetbrains.kotlin.daemon.common.impls.makePortFromRunFilenameExtractor
import java.io.File
import java.rmi.registry.LocateRegistry
import java.util.logging.Logger
/*
1) walkDaemonsAsync = walkDaemons + some async calls inside (also some used classes changed *** -> ***Async)
2) tryConnectToDaemonBySockets / tryConnectToDaemonByRMI
*/
internal val MAX_PORT_NUMBER = 0xffff
private const val ORPHANED_RUN_FILE_AGE_THRESHOLD_MS = 1000000L
data class DaemonWithMetadataAsync(val daemon: CompileServiceAsync, val runFile: File, val jvmOptions: DaemonJVMOptions)
val log = Logger.getLogger("client utils")
internal fun String.info(msg: String) = {}()//log.info("[$this] : $msg")
private suspend fun <T, R : Any> List<T>.mapNotNullAsync(transform: suspend (T) -> R?): List<R> =
this
.map { async { transform(it) } }
.mapNotNull { it.await() } // await for completion of the last action
// TODO: write metadata into discovery file to speed up selection
// TODO: consider using compiler jar signature (checksum) as a CompilerID (plus java version, plus ???) instead of classpath checksum
// would allow to use same compiler from taken different locations
// reqs: check that plugins (or anything els) should not be part of the CP
suspend fun walkDaemonsAsync(
registryDir: File,
compilerId: CompilerId,
fileToCompareTimestamp: File,
filter: (File, Int) -> Boolean = { _, _ -> true },
report: (DaemonReportCategory, String) -> Unit = { _, _ -> },
useRMI: Boolean = true,
useSockets: Boolean = true
): List<DaemonWithMetadataAsync> {//Deferred<List<DaemonWithMetadataAsync>> {
// : Sequence<DaemonWithMetadataAsync>
val classPathDigest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString()
val portExtractor = makePortFromRunFilenameExtractor(classPathDigest)
log.info("\nssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss\n")
return registryDir.walk().toList() // list, since walk returns Sequence and Sequence.map{...} is not inline => coroutines dont work
.map { Pair(it, portExtractor(it.name)) }
.filter { (file, port) -> port != null && filter(file, port) }
.mapNotNull { (file, port) ->
//.mapNotNull { (file, port) ->
// all actions process concurrently
log.info("\n<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>\n")
log.info("(port = $port, file = $file)")
log.info("(port = $port, file = $file)")
log.info("fileToCompareTimestamp = $fileToCompareTimestamp")
log.info("port != null : ${port != null}")
log.info("port in RANGE : ${(port ?: -1) in 1..(MAX_PORT_NUMBER - 1)}")
assert(port!! in 1..(MAX_PORT_NUMBER - 1))
log.info("lastModified()")
log.info("file.lastModified() : ${file.lastModified()}")
log.info("fileToCompareTimestamp.lastModified() : ${fileToCompareTimestamp.lastModified()}")
val relativeAge = fileToCompareTimestamp.lastModified() - file.lastModified()
log.info("after ASSERT - relativeAge : $relativeAge")
// report(
// DaemonReportCategory.DEBUG,
// "found daemon on socketPort $port ($relativeAge ms old), trying to connect"
// )
log.info("found daemon on socketPort $port ($relativeAge ms old), trying to connect")
val daemon = tryConnectToDaemonAsync(port, report, file, useRMI, useSockets)
log.info("daemon = $daemon (port= $port)")
// cleaning orphaned file; note: daemon should shut itself down if it detects that the runServer file is deleted
if (daemon == null) {
if (relativeAge - ORPHANED_RUN_FILE_AGE_THRESHOLD_MS <= 0) {
report(
DaemonReportCategory.DEBUG,
"found fresh runServer file '${file.absolutePath}' ($relativeAge ms old), but no daemon, ignoring it"
)
} else {
report(
DaemonReportCategory.DEBUG,
"found seemingly orphaned runServer file '${file.absolutePath}' ($relativeAge ms old), deleting it"
)
if (!file.delete()) {
report(
DaemonReportCategory.INFO,
"WARNING: unable to delete seemingly orphaned file '${file.absolutePath}', cleanup recommended"
)
}
}
}
try {
log.info("try daemon = ... ($daemon(port=$port)), daemon != null : ${daemon != null}")
daemon
?.let {
DaemonWithMetadataAsync(it, file, it.getDaemonJVMOptions().get())
}
.also {
log.info("($port)DaemonWithMetadataAsync == $it)")
}
} catch (e: Exception) {
log.info("($port)<error_in_client_utils> : " + e.message)
report(
DaemonReportCategory.INFO,
"ERROR: unable to retrieve daemon JVM options, assuming daemon is dead: ${e.message}"
)
null
}.also {
log.info("\n\n_______________________________________________________________________________________________________\n\n")
}
}
}
//}
private inline fun tryConnectToDaemonByRMI(port: Int, report: (DaemonReportCategory, String) -> Unit): CompileServiceAsync? {
try {
log.info("tryConnectToDaemonByRMI(port = $port)")
val daemon = runBlocking {
runWithTimeout(2 * DAEMON_PERIODIC_CHECK_INTERVAL_MS) {
LocateRegistry.getRegistry(
LoopbackNetworkInterface.loopbackInetAddressName,
port,
LoopbackNetworkInterface.clientLoopbackSocketFactoryRMI
)?.lookup(COMPILER_SERVICE_RMI_NAME)
}
}
when (daemon) {
null -> report(DaemonReportCategory.INFO, "daemon not found")
is CompileService -> return daemon.toClient()
else -> report(DaemonReportCategory.INFO, "Unable to cast compiler service, actual class received: ${daemon::class.java.name}")
}
} catch (e: Throwable) {
report(DaemonReportCategory.INFO, "cannot connect to registry: " + (e.cause?.message ?: e.message ?: "unknown error"))
}
return null
}
private suspend fun tryConnectToDaemonBySockets(
port: Int,
file: File,
report: (DaemonReportCategory, String) -> Unit
): CompileServiceClientSide? {
return CompileServiceClientSideImpl(
port,
LoopbackNetworkInterface.loopbackInetAddressName,
file
).let { daemon ->
try {
log.info("tryConnectToDaemonBySockets(port = $port)")
log.info("daemon($port) = $daemon")
log.info("daemon($port) connecting to server...")
daemon.connectToServer()
log.info("OK - daemon($port) connected to server!!!")
daemon
} catch (e: Throwable) {
report(DaemonReportCategory.INFO, "kcannot find or connect to socket")
daemon.close()
null
}
}
}
private suspend fun tryConnectToDaemonAsync(
port: Int,
report: (DaemonReportCategory, String) -> Unit,
file: File,
useRMI: Boolean = true,
useSockets: Boolean = true
): CompileServiceAsync? =
useSockets.takeIf { it }?.let { tryConnectToDaemonBySockets(port, file, report) }
?: (useRMI.takeIf { it }?.let { tryConnectToDaemonByRMI(port, report) })

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.CompilationResultsAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClient
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import java.io.Serializable
interface CompilationResultsServerSide : CompilationResultsAsync, Server<CompilationResultsServerSide> {
class AddMessage(
val compilationResultCategory: Int,
val value: Serializable
) : Server.Message<CompilationResultsServerSide>() {
override suspend fun processImpl(server: CompilationResultsServerSide, printObject: (Any?) -> Unit) {
server.add(compilationResultCategory, value)
}
}
}
interface CompilationResultsClientSide : CompilationResultsAsync, Client<CompilationResultsServerSide>
class CompilationResultsClientSideImpl(val socketPort: Int) : CompilationResultsClientSide,
Client<CompilationResultsServerSide> by DefaultClient(socketPort) {
override val clientSide: CompilationResultsAsync
get() = this
override suspend fun add(compilationResultCategory: Int, value: Serializable) {
sendMessage(CompilationResultsServerSide.AddMessage(compilationResultCategory, value))
}
// init {
// runBlocking {
// connectToServer()
// }
// }
}
enum class CompilationResultCategory(val code: Int) {
IC_COMPILE_ITERATION(0)
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("EXPERIMENTAL_FEATURE_WARNING")
package org.jetbrains.kotlin.daemon.common
import org.jetbrains.kotlin.daemon.common.experimental.CompileServiceServerSide
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.*
interface CompileServiceClientSide : CompileServiceAsync, Client<CompileServiceServerSide> {
override val serverPort: Int
}

View File

@@ -0,0 +1,413 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.newSingleThreadContext
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.*
import org.jetbrains.kotlin.daemon.common.CompileService
import org.jetbrains.kotlin.daemon.common.impls.CompilerServicesFacadeBase
import java.io.File
import java.util.logging.Logger
class CompileServiceClientSideImpl(
override val serverPort: Int,
val serverHost: String,
val serverFile: File
) : CompileServiceClientSide,
Client<CompileServiceServerSide> by object : DefaultAuthorizableClient<CompileServiceServerSide>(
serverPort,
serverHost
) {
private fun nowMillieconds() = System.currentTimeMillis()
@Volatile
private var lastUsedMilliSeconds: Long = nowMillieconds()
private fun deltaTime() = nowMillieconds() - lastUsedMilliSeconds
private fun keepAliveSuccess() = deltaTime() < KEEPALIVE_PERIOD
override suspend fun authorizeOnServer(serverOutputChannel: ByteWriteChannelWrapper): Boolean =
runWithTimeout {
log.info("in authoriseOnServer(serverFile=$serverFile)")
val signature = serverFile.inputStream().use(::readTokenKeyPairAndSign)
sendSignature(serverOutputChannel, signature)
true
} ?: false
override suspend fun clientHandshake(input: ByteReadChannelWrapper, output: ByteWriteChannelWrapper, log: Logger): Boolean {
return trySendHandshakeMessage(output, log) && tryAcquireHandshakeMessage(input, log)
}
override suspend fun startKeepAlives() {
val keepAliveMessage = Server.KeepAliveMessage<CompileServiceServerSide>()
async(newSingleThreadContext("keepAliveThread")) {
delay(KEEPALIVE_PERIOD * 4)
while (true) {
delay(KEEPALIVE_PERIOD)
// println("[$this] KEEPALIVE_PERIOD")
while (keepAliveSuccess()) {
// println("[$this] remained ${KEEPALIVE_PERIOD - deltaTime()}")
delay(KEEPALIVE_PERIOD - deltaTime())
}
runWithTimeout(timeout = KEEPALIVE_PERIOD / 2) {
// println("[$this] sent keepalive")
val id = sendMessage(keepAliveMessage)
readMessage<Server.KeepAliveAcknowledgement<*>>(id)
} ?: if (!keepAliveSuccess()) readActor.send(StopAllRequests()).also {
// println("[$this] got keepalive")
}
}
}
}
override suspend fun delayKeepAlives() {
// println("[$this] delayKeepAlives")
lastUsedMilliSeconds = nowMillieconds()
}
} {
override suspend fun classesFqNamesByFiles(sessionId: Int, sourceFiles: Set<File>): CompileService.CallResult<Set<String>> {
val id = sendMessage(ClassesFqNamesByFilesMessage(sessionId, sourceFiles))
return readMessage(id)
}
val log = Logger.getLogger("CompileServiceClientSideImpl")
override suspend fun compile(
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBaseAsync,
compilationResults: CompilationResultsAsync?
): CompileService.CallResult<Int> {
log.info("override fun compile(")
val id = sendMessage(CompileMessage(
sessionId,
compilerArguments,
compilationOptions,
servicesFacade,
compilationResults
))
log.info("override fun compile(: id = $id")
return readMessage(id)
}
override suspend fun leaseReplSession(
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBaseAsync,
templateClasspath: List<File>,
templateClassName: String
): CompileService.CallResult<Int> {
val id = sendMessage(
LeaseReplSessionMessage(
aliveFlagPath,
compilerArguments,
compilationOptions,
servicesFacade,
templateClasspath,
templateClassName
)
)
return readMessage(id)
}
// CompileService methods:
override suspend fun checkCompilerId(expectedCompilerId: CompilerId): Boolean {
val id = sendMessage(
CheckCompilerIdMessage(
expectedCompilerId
)
)
return readMessage(id)
}
override suspend fun getUsedMemory(): CompileService.CallResult<Long> {
val id = sendMessage(GetUsedMemoryMessage())
return readMessage(id)
}
override suspend fun getDaemonOptions(): CompileService.CallResult<DaemonOptions> {
val id = sendMessage(GetDaemonOptionsMessage())
return readMessage(id)
}
override suspend fun getDaemonInfo(): CompileService.CallResult<String> {
val id = sendMessage(GetDaemonInfoMessage())
return readMessage(id)
}
override suspend fun getDaemonJVMOptions(): CompileService.CallResult<DaemonJVMOptions> {
log.info("sending message (GetDaemonJVMOptionsMessage) ... (deaemon port = $serverPort)")
val id = sendMessage(GetDaemonJVMOptionsMessage())
log.info("message is sent!")
log.info("reading message...")
val res = readMessage<CompileService.CallResult<DaemonJVMOptions>>(id)
log.info("reply : $res")
return res
}
override suspend fun registerClient(aliveFlagPath: String?): CompileService.CallResult<Nothing> {
log.info("registerClient")
// println("client's fun registerClient")
val id = sendMessage(RegisterClientMessage(aliveFlagPath))
return readMessage(id)
}
override suspend fun getClients(): CompileService.CallResult<List<String>> {
val id = sendMessage(GetClientsMessage())
return readMessage(id)
}
override suspend fun leaseCompileSession(aliveFlagPath: String?): CompileService.CallResult<Int> {
val id = sendMessage(
LeaseCompileSessionMessage(
aliveFlagPath
)
)
return readMessage(id)
}
override suspend fun releaseCompileSession(sessionId: Int): CompileService.CallResult<Nothing> {
val id = sendMessage(
ReleaseCompileSessionMessage(
sessionId
)
)
return readMessage(id)
}
override suspend fun shutdown(): CompileService.CallResult<Nothing> {
val id = sendMessage(ShutdownMessage())
log.info("ShutdownMessage_id = $id")
val res = readMessage<CompileService.CallResult<Nothing>>(id)
log.info("ShutdownMessage_res : $res")
return res
}
override suspend fun scheduleShutdown(graceful: Boolean): CompileService.CallResult<Boolean> {
val id = sendMessage(ScheduleShutdownMessage(graceful))
return readMessage(id)
}
override suspend fun clearJarCache() {
val id = sendMessage(ClearJarCacheMessage())
}
override suspend fun releaseReplSession(sessionId: Int): CompileService.CallResult<Nothing> {
val id = sendMessage(ReleaseReplSessionMessage(sessionId))
return readMessage(id)
}
override suspend fun replCreateState(sessionId: Int): CompileService.CallResult<ReplStateFacadeAsync> {
val id = sendMessage(ReplCreateStateMessage(sessionId))
return readMessage(id)
}
override suspend fun replCheck(
sessionId: Int,
replStateId: Int,
codeLine: ReplCodeLine
): CompileService.CallResult<ReplCheckResult> {
val id = sendMessage(
ReplCheckMessage(
sessionId,
replStateId,
codeLine
)
)
return readMessage(id)
}
override suspend fun replCompile(
sessionId: Int,
replStateId: Int,
codeLine: ReplCodeLine
): CompileService.CallResult<ReplCompileResult> {
val id = sendMessage(
ReplCompileMessage(
sessionId,
replStateId,
codeLine
)
)
return readMessage(id)
}
// Query messages:
class CheckCompilerIdMessage(val expectedCompilerId: CompilerId) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.checkCompilerId(expectedCompilerId))
}
class GetUsedMemoryMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.getUsedMemory())
}
class GetDaemonOptionsMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.getDaemonOptions())
}
class GetDaemonJVMOptionsMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.getDaemonJVMOptions())
}
class GetDaemonInfoMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.getDaemonInfo())
}
class RegisterClientMessage(val aliveFlagPath: String?) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.registerClient(aliveFlagPath))
}
class GetClientsMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.getClients())
}
class LeaseCompileSessionMessage(val aliveFlagPath: String?) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.leaseCompileSession(aliveFlagPath))
}
class ReleaseCompileSessionMessage(val sessionId: Int) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.releaseCompileSession(sessionId))
}
class ShutdownMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.shutdown())
}
class ScheduleShutdownMessage(val graceful: Boolean) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.scheduleShutdown(graceful))
}
class CompileMessage(
val sessionId: Int,
val compilerArguments: Array<out String>,
val compilationOptions: CompilationOptions,
val servicesFacade: CompilerServicesFacadeBaseAsync,
val compilationResults: CompilationResultsAsync?
) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(
server.compile(
sessionId,
compilerArguments,
compilationOptions,
servicesFacade,
compilationResults
)
)
}
class ClassesFqNamesByFilesMessage(
val sessionId: Int,
val sourceFiles: Set<File>
) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(
server.classesFqNamesByFiles(sessionId, sourceFiles)
)
}
class ClearJarCacheMessage : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
server.clearJarCache()
}
class LeaseReplSessionMessage(
val aliveFlagPath: String?,
val compilerArguments: Array<out String>,
val compilationOptions: CompilationOptions,
val servicesFacade: CompilerServicesFacadeBaseAsync,
val templateClasspath: List<File>,
val templateClassName: String
) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(
server.leaseReplSession(
aliveFlagPath,
compilerArguments,
compilationOptions,
servicesFacade,
templateClasspath,
templateClassName
)
)
}
class ReleaseReplSessionMessage(val sessionId: Int) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.releaseReplSession(sessionId))
}
class LeaseReplSession_Short_Message(
val aliveFlagPath: String?,
val compilerArguments: Array<out String>,
val compilationOptions: CompilationOptions,
val servicesFacade: CompilerServicesFacadeBase,
val templateClasspath: List<File>,
val templateClassName: String
) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(
server.leaseReplSession(
aliveFlagPath,
compilerArguments,
compilationOptions,
servicesFacade.toClient(),
templateClasspath,
templateClassName
)
)
}
class ReplCreateStateMessage(val sessionId: Int) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.replCreateState(sessionId))
}
class ReplCheckMessage(
val sessionId: Int,
val replStateId: Int,
val codeLine: ReplCodeLine
) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.replCheck(sessionId, replStateId, codeLine))
}
class ReplCompileMessage(
val sessionId: Int,
val replStateId: Int,
val codeLine: ReplCodeLine
) : Server.Message<CompileServiceServerSide>() {
override suspend fun processImpl(server: CompileServiceServerSide, sendReply: (Any?) -> Unit) =
sendReply(server.replCompile(sessionId, replStateId, codeLine))
}
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.impls.LoopbackNetworkInterface
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClientRMIWrapper
import org.jetbrains.kotlin.daemon.common.impls.*
import java.io.File
import java.io.Serializable
import java.rmi.NoSuchObjectException
import java.rmi.server.UnicastRemoteObject
import java.util.*
import java.util.logging.Logger
class CompileServiceRMIWrapper(val server: CompileServiceServerSide, daemonOptions: DaemonOptions, compilerId: CompilerId) :
CompileService {
override fun classesFqNamesByFiles(sessionId: Int, sourceFiles: Set<File>) = runBlocking {
server.classesFqNamesByFiles(sessionId, sourceFiles)
}
val log = Logger.getLogger("CompileServiceRMIWrapper")
private fun deprecated(): Nothing = TODO("NEVER USE DEPRECATED METHODS, PLEASE!") // prints this todo message
override fun checkCompilerId(expectedCompilerId: CompilerId) = runBlocking {
server.checkCompilerId(expectedCompilerId)
}
override fun getUsedMemory() = runBlocking {
server.getUsedMemory()
}
override fun getDaemonOptions() = runBlocking {
server.getDaemonOptions()
}
override fun getDaemonInfo() = runBlocking {
server.getDaemonInfo()
}
override fun getDaemonJVMOptions() = runBlocking {
log.info("in wrapper's getDaemonJVMOptions")
server.getDaemonJVMOptions().also {
log.info("server returned ${if (it.isGood) it.get() else it}")
}
}
override fun registerClient(aliveFlagPath: String?) = runBlocking {
server.registerClient(aliveFlagPath)
}
override fun getClients() = runBlocking {
server.getClients()
}
override fun leaseCompileSession(aliveFlagPath: String?) = runBlocking {
server.leaseCompileSession(aliveFlagPath)
}
override fun releaseCompileSession(sessionId: Int) = runBlocking {
server.releaseCompileSession(sessionId)
}
override fun shutdown() = runBlocking {
server.shutdown()
}
override fun scheduleShutdown(graceful: Boolean) = runBlocking {
server.scheduleShutdown(graceful)
}
override fun remoteCompile(
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
outputFormat: CompileService.OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
) = deprecated()
override fun remoteIncrementalCompile(
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
compilerOutputFormat: CompileService.OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
) = deprecated()
override fun compile(
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
compilationResults: CompilationResults?
) = runBlocking {
server.compile(
sessionId,
compilerArguments,
compilationOptions,
servicesFacade.toClient(),
compilationResults?.toClient() ?: object : CompilationResultsClientSide,
Client<CompilationResultsServerSide> by DefaultClientRMIWrapper() {
override val clientSide: CompilationResultsAsync
get() = this
override suspend fun add(compilationResultCategory: Int, value: Serializable) {}
}
)
}
override fun clearJarCache() = runBlocking {
server.clearJarCache()
}
override fun leaseReplSession(
aliveFlagPath: String?,
targetPlatform: CompileService.TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade,
templateClasspath: List<File>,
templateClassName: String,
scriptArgs: Array<out Any?>?,
scriptArgsTypes: Array<out Class<out Any>>?,
compilerMessagesOutputStream: RemoteOutputStream,
evalOutputStream: RemoteOutputStream?,
evalErrorStream: RemoteOutputStream?,
evalInputStream: RemoteInputStream?,
operationsTracer: RemoteOperationsTracer?
) = deprecated()
override fun releaseReplSession(sessionId: Int) = runBlocking {
server.releaseReplSession(sessionId)
}
override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine) = deprecated()
override fun remoteReplLineCompile(
sessionId: Int,
codeLine: ReplCodeLine,
history: List<ReplCodeLine>?
) = deprecated()
override fun remoteReplLineEval(
sessionId: Int,
codeLine: ReplCodeLine,
history: List<ReplCodeLine>?
) = deprecated()
override fun leaseReplSession(
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
templateClasspath: List<File>,
templateClassName: String
) = runBlocking {
server.leaseReplSession(
aliveFlagPath,
compilerArguments,
compilationOptions,
servicesFacade.toClient(),
templateClasspath,
templateClassName
)
}
override fun replCreateState(sessionId: Int) = runBlocking {
server.replCreateState(sessionId).toRMI()
}
override fun replCheck(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine) = runBlocking {
server.replCheck(sessionId, replStateId, codeLine)
}
override fun replCompile(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine) = runBlocking {
server.replCompile(sessionId, replStateId, codeLine)
}
init {
// assuming logically synchronized
log.info("<init>")
try {
// cleanup for the case of incorrect restart and many other situations
UnicastRemoteObject.unexportObject(this, false)
log.info("unexportObject_________________________________")
} catch (e: NoSuchObjectException) {
// ignoring if object already exported
log.info("// ignoring if object already exported_________________________________")
}
val (registry, port) = findPortAndCreateRegistry(
COMPILE_DAEMON_FIND_PORT_ATTEMPTS,
RMI_WRAPPER_PORTS_RANGE_START,
RMI_WRAPPER_PORTS_RANGE_END
)
log.info("port = $port , registry = $registry")
val stub = UnicastRemoteObject.exportObject(
this,
port,
LoopbackNetworkInterface.clientLoopbackSocketFactory,
LoopbackNetworkInterface.serverLoopbackSocketFactory
) as CompileService
log.info("stub = $stub")
registry.rebind(COMPILER_SERVICE_RMI_NAME, stub)
log.info("rebinded!")
// create file :
val runFileDir = File(daemonOptions.runFilesPathOrDefault)
runFileDir.mkdirs()
val runFile = File(
runFileDir,
makeRunFilenameString(
timestamp = "%tFT%<tH-%<tM-%<tS.%<tLZ".format(Calendar.getInstance(TimeZone.getTimeZone("Z"))),
digest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString(),
port = port.toString()
)
)
try {
if (!runFile.createNewFile()) throw Exception("createNewFile returned false")
} catch (e: Throwable) {
throw IllegalStateException("Unable to create runServer file '${runFile.absolutePath}'", e)
}
runFile.deleteOnExit()
}
}
fun CompileServiceServerSide.toRMIServer(daemonOptions: DaemonOptions, compilerId: CompilerId) =
CompileServiceRMIWrapper(this, daemonOptions, compilerId)

View File

@@ -0,0 +1,14 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.CompileServiceAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
interface CompileServiceServerSide : CompileServiceAsync, Server<CompileServiceServerSide> {
override val serverPort: Int
get
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacadeAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClient
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
import org.jetbrains.kotlin.modules.TargetId
import java.io.Serializable
interface CompilerCallbackServicesFacadeClientSide : CompilerCallbackServicesFacadeAsync, Client<CompilerServicesFacadeBaseServerSide>, CompilerServicesFacadeBaseClientSide
@Suppress("UNCHECKED_CAST")
class CompilerCallbackServicesFacadeClientSideImpl(serverPort: Int) : CompilerCallbackServicesFacadeClientSide,
Client<CompilerServicesFacadeBaseServerSide> by DefaultClient(serverPort) {
override suspend fun hasIncrementalCaches(): Boolean {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.HasIncrementalCachesMessage())
return readMessage(id)
}
override suspend fun hasLookupTracker(): Boolean {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.HasLookupTrackerMessage())
return readMessage(id)
}
override suspend fun hasCompilationCanceledStatus(): Boolean {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.HasCompilationCanceledStatusMessage())
return readMessage(id)
}
override suspend fun incrementalCache_getObsoletePackageParts(target: TargetId): Collection<String> {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_getObsoletePackagePartsMessage(target))
return readMessage(id)
}
override suspend fun incrementalCache_getObsoleteMultifileClassFacades(target: TargetId): Collection<String> {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_getObsoleteMultifileClassFacadesMessage(target))
return readMessage(id)
}
override suspend fun incrementalCache_getPackagePartData(target: TargetId, partInternalName: String): JvmPackagePartProto? {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_getPackagePartDataMessage(target, partInternalName))
return readMessage(id)
}
override suspend fun incrementalCache_getModuleMappingData(target: TargetId): ByteArray? {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_getModuleMappingDataMessage(target))
return readMessage(id)
}
override suspend fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String) {
sendNoReplyMessage(
CompilerCallbackServicesFacadeServerSide.IncrementalCache_registerInlineMessage(
target,
fromPath,
jvmSignature,
toPath
)
)
}
override suspend fun incrementalCache_getClassFilePath(target: TargetId, internalClassName: String): String {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_getClassFilePathMessage(target, internalClassName))
return readMessage(id)
}
override suspend fun incrementalCache_close(target: TargetId) =
sendNoReplyMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_closeMessage(target))
override suspend fun incrementalCache_getMultifileFacadeParts(target: TargetId, internalName: String): Collection<String>? {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.IncrementalCache_getMultifileFacadePartsMessage(target, internalName))
return readMessage(id)
}
override suspend fun lookupTracker_requiresPosition(): Boolean {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.LookupTracker_requiresPositionMessage())
return readMessage(id)
}
override suspend fun lookupTracker_record(lookups: Collection<LookupInfo>) =
sendNoReplyMessage(CompilerCallbackServicesFacadeServerSide.LookupTracker_recordMessage(lookups))
override suspend fun lookupTracker_isDoNothing(): Boolean {
val id = sendMessage(CompilerCallbackServicesFacadeServerSide.LookupTracker_isDoNothingMessage())
return readMessage(id)
}
override suspend fun compilationCanceledStatus_checkCanceled(): Void? {
sendNoReplyMessage(CompilerCallbackServicesFacadeServerSide.CompilationCanceledStatus_checkCanceledMessage())
return null
}
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
sendNoReplyMessage(CompilerServicesFacadeBaseServerSide.ReportMessage(category, severity, message, attachment))
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacadeAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server.Message
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.modules.TargetId
interface CompilerCallbackServicesFacadeServerSide : CompilerCallbackServicesFacadeAsync, CompilerServicesFacadeBaseServerSide {
class HasIncrementalCachesMessage : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.hasIncrementalCaches())
}
class HasLookupTrackerMessage : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.hasLookupTracker())
}
class HasCompilationCanceledStatusMessage : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.hasCompilationCanceledStatus())
}
// ----------------------------------------------------
// IncrementalCache
class IncrementalCache_getObsoletePackagePartsMessage(val target: TargetId) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.incrementalCache_getObsoletePackageParts(target))
}
class IncrementalCache_getObsoleteMultifileClassFacadesMessage(val target: TargetId) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.incrementalCache_getObsoleteMultifileClassFacades(target))
}
class IncrementalCache_getPackagePartDataMessage(val target: TargetId, val partInternalName: String) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.incrementalCache_getPackagePartData(target, partInternalName))
}
class IncrementalCache_getModuleMappingDataMessage(val target: TargetId) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.incrementalCache_getModuleMappingData(target))
}
class IncrementalCache_registerInlineMessage(
val target: TargetId,
val fromPath: String,
val jvmSignature: String,
val toPath: String
) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
server.incrementalCache_registerInline(target, fromPath, jvmSignature, toPath)
}
class IncrementalCache_getClassFilePathMessage(val target: TargetId, val internalClassName: String) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.incrementalCache_getClassFilePath(target, internalClassName))
}
class IncrementalCache_closeMessage(val target: TargetId) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
server.incrementalCache_close(target)
}
class IncrementalCache_getMultifileFacadePartsMessage(val target: TargetId, val internalName: String) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.incrementalCache_getMultifileFacadeParts(target, internalName))
}
// ----------------------------------------------------
// LookupTracker
class LookupTracker_requiresPositionMessage : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) {
server.lookupTracker_requiresPosition()
}
}
class LookupTracker_recordMessage(val lookups: Collection<LookupInfo>) : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.lookupTracker_record(lookups))
}
class LookupTracker_isDoNothingMessage : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.lookupTracker_isDoNothing())
}
// ----------------------------------------------------
// CompilationCanceledStatus
class CompilationCanceledStatus_checkCanceledMessage : Message<CompilerCallbackServicesFacadeServerSide>() {
override suspend fun processImpl(server: CompilerCallbackServicesFacadeServerSide, printObject: (Any?) -> Unit) {
server.compilationCanceledStatus_checkCanceled()
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.CompilerServicesFacadeBaseAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClient
import java.io.Serializable
interface CompilerServicesFacadeBaseClientSide : CompilerServicesFacadeBaseAsync, Client<CompilerServicesFacadeBaseServerSide>
class CompilerServicesFacadeBaseClientSideImpl(val serverPort: Int) :
CompilerServicesFacadeBaseClientSide,
Client<CompilerServicesFacadeBaseServerSide> by DefaultClient(serverPort) {
init {
// runBlocking { connectToServer() }
log.info("CompilerServicesFacadeBaseClientSideImpl on $serverPort - inited")
}
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
log.info("client $serverPort - fun report")
sendNoReplyMessage(
CompilerServicesFacadeBaseServerSide.ReportMessage(
category, severity, message, attachment
)
)
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.CompilerServicesFacadeBaseAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import java.io.Serializable
interface CompilerServicesFacadeBaseServerSide : CompilerServicesFacadeBaseAsync, Server<CompilerServicesFacadeBaseServerSide> {
class ReportMessage(
val category: Int,
val severity: Int,
val message: String?,
val attachment: Serializable?
) : Server.Message<CompilerServicesFacadeBaseServerSide>() {
override suspend fun processImpl(server: CompilerServicesFacadeBaseServerSide, sendReply: (Any?) -> Unit) {
log.info("reporting_-_-_-_-")
server.report(category, severity, message, attachment)
log.info("reported-_-_-_-")
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.daemon.common.experimental
import java.io.File
enum class OSKind {
Windows,
OSX,
Unix,
Unknown;
companion object {
val current: OSKind = System.getProperty("os.name").toLowerCase().let {
when {
// partly taken from http://www.code4copy.com/java/post/detecting-os-type-in-java
it.startsWith("windows") -> Windows
it.startsWith("mac os") -> OSX
it.contains("unix") -> Unix
it.startsWith("linux") -> Unix
it.contains("bsd") -> Unix
it.startsWith("irix") -> Unix
it.startsWith("mpe/ix") -> Unix
it.startsWith("aix") -> Unix
it.startsWith("hp-ux") -> Unix
it.startsWith("sunos") -> Unix
it.startsWith("sun os") -> Unix
it.startsWith("solaris") -> Unix
else -> Unknown
}
}
}
}
private fun String?.orDefault(v: String): String =
if (this == null || this.isBlank()) v else this
// Note links to OS recommendations for storing various kinds of files
// Windows: http://www.microsoft.com/security/portal/mmpc/shared/variables.aspx
// unix (freedesktop): http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
// OS X: https://developer.apple.com/library/mac/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html
object FileSystem {
val userHomePath: String get() = System.getProperty("user.home")
val tempPath: String get() = System.getProperty("java.io.tmpdir")
val logFilesPath: String get() = tempPath
val runtimeStateFilesBasePath: String get() = when (OSKind.current) {
OSKind.Windows -> System.getenv("LOCALAPPDATA").orDefault(tempPath)
OSKind.OSX -> userHomePath + "/Library/Application Support"
OSKind.Unix -> System.getenv("XDG_DATA_HOME").orDefault(userHomePath + "/.local/share")
OSKind.Unknown -> tempPath
}
fun getRuntimeStateFilesPath(vararg names: String): String {
assert(names.any())
val base = File(runtimeStateFilesBasePath)
// if base is not suitable, take home dir as a base and ensure the first name is prefixed with "." -
// this will work ok as a fallback solution on most systems
val dir = if (base.exists() && base.isDirectory) names.fold(base, ::File)
else names.drop(1)
.fold(File(userHomePath, names.first().let { if (it.startsWith(".")) it else ".$it" }), ::File)
return if ((dir.exists() && dir.isDirectory) || dir.mkdirs()) dir.absolutePath
else tempPath
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.impls.IncrementalCompilerServicesFacade
import org.jetbrains.kotlin.daemon.common.impls.SimpleDirtyData
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClientRMIWrapper
import java.io.File
import java.io.Serializable
class IncrementalCompilerServicesFacadeAsyncWrapper(
val rmiImpl: IncrementalCompilerServicesFacade
) : IncrementalCompilerServicesFacadeClientSide, Client<CompilerServicesFacadeBaseServerSide> by DefaultClientRMIWrapper() {
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) =
rmiImpl.report(category, severity, message, attachment)
}
fun IncrementalCompilerServicesFacade.toClient() = IncrementalCompilerServicesFacadeAsyncWrapper(this)

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.IncrementalCompilerServicesFacadeAsync
import org.jetbrains.kotlin.daemon.common.impls.SimpleDirtyData
import org.jetbrains.kotlin.daemon.common.experimental.IncrementalCompilerServicesFacadeServerSide.*
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClient
import java.io.File
import java.io.Serializable
interface IncrementalCompilerServicesFacadeClientSide : IncrementalCompilerServicesFacadeAsync, CompilerServicesFacadeBaseClientSide
class IncrementalCompilerServicesFacadeClientSideImpl(val serverPort: Int) :
IncrementalCompilerServicesFacadeClientSide,
Client<CompilerServicesFacadeBaseServerSide> by DefaultClient(serverPort) {
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) {
sendNoReplyMessage(CompilerServicesFacadeBaseServerSide.ReportMessage(category, severity, message, attachment))
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.impls.IncrementalCompilerServicesFacade
import org.jetbrains.kotlin.daemon.common.impls.SimpleDirtyData
import java.io.File
import java.io.Serializable
class IncrementalCompilerServicesFacadeRMIWrapper(val clientSide: IncrementalCompilerServicesFacadeClientSide) :
IncrementalCompilerServicesFacade, Serializable {
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) = runBlocking {
clientSide.report(category, severity, message, attachment)
}
init {
// runBlocking {
// clientSide.connectToServer()
// }
}
}
fun IncrementalCompilerServicesFacadeClientSide.toRMI() =
if (this is IncrementalCompilerServicesFacadeAsyncWrapper) this.rmiImpl
else IncrementalCompilerServicesFacadeRMIWrapper(this)

View File

@@ -0,0 +1,14 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.IncrementalCompilerServicesFacadeAsync
import org.jetbrains.kotlin.daemon.common.impls.SimpleDirtyData
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
import java.io.File
interface IncrementalCompilerServicesFacadeServerSide : IncrementalCompilerServicesFacadeAsync, CompilerServicesFacadeBaseServerSide

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import io.ktor.network.selector.ActorSelectorManager
import io.ktor.network.sockets.aSocket
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ServerSocketWrapper
import org.jetbrains.kotlin.daemon.common.impls.*
import java.io.IOException
import java.io.Serializable
import java.net.*
import java.rmi.server.RMIClientSocketFactory
import java.rmi.server.RMIServerSocketFactory
import java.util.*
// copyed from original(org.jetbrains.kotlin.daemon.common.NetworkUtils) TODO
// unique part :
// - AbstractClientLoopbackSocketFactory / ServerLoopbackSocketFactoryRMI / ServerLoopbackSocketFactoryKtor - Ktor-Sockets instead of java sockets
// - findPortAndCreateSocket
// TODO: get rid of copy-paste here
object LoopbackNetworkInterface {
const val IPV4_LOOPBACK_INET_ADDRESS = "127.0.0.1"
const val IPV6_LOOPBACK_INET_ADDRESS = "::1"
// size of the requests queue for daemon services, so far seems that we don't need any big numbers here
// but if we'll start getting "connection refused" errors, that could be the first place to try to fix it
val SERVER_SOCKET_BACKLOG_SIZE by lazy {
System.getProperty(DAEMON_RMI_SOCKET_BACKLOG_SIZE_PROPERTY)?.toIntOrNull() ?: DEFAULT_SERVER_SOCKET_BACKLOG_SIZE
}
val SOCKET_CONNECT_ATTEMPTS by lazy {
System.getProperty(DAEMON_RMI_SOCKET_CONNECT_ATTEMPTS_PROPERTY)?.toIntOrNull() ?: DEFAULT_SOCKET_CONNECT_ATTEMPTS
}
val SOCKET_CONNECT_INTERVAL_MS by lazy {
System.getProperty(DAEMON_RMI_SOCKET_CONNECT_INTERVAL_PROPERTY)?.toLongOrNull() ?: DEFAULT_SOCKET_CONNECT_INTERVAL_MS
}
val serverLoopbackSocketFactoryRMI by lazy { ServerLoopbackSocketFactoryRMI() }
val clientLoopbackSocketFactoryRMI by lazy { ClientLoopbackSocketFactoryRMI() }
val serverLoopbackSocketFactoryKtor by lazy { ServerLoopbackSocketFactoryKtor() }
val clientLoopbackSocketFactoryKtor by lazy { ClientLoopbackSocketFactoryKtor() }
// TODO switch to InetAddress.getLoopbackAddress on java 7+
val loopbackInetAddressName by lazy {
try {
if (InetAddress.getByName(null) is Inet6Address) IPV6_LOOPBACK_INET_ADDRESS else IPV4_LOOPBACK_INET_ADDRESS
} catch (e: IOException) {
// getLocalHost may fail for unknown reasons in some situations, the fallback is to assume IPv4 for now
// TODO consider some other ways to detect default to IPv6 addresses in this case
IPV4_LOOPBACK_INET_ADDRESS
}
}
// base socket factories by default don't implement equals properly (see e.g. http://stackoverflow.com/questions/21555710/rmi-and-jmx-socket-factories)
// so implementing it in derived classes using the fact that they are singletons
class ServerLoopbackSocketFactoryRMI : RMIServerSocketFactory, Serializable {
override fun equals(other: Any?): Boolean = other === this || super.equals(other)
override fun hashCode(): Int = super.hashCode()
@Throws(IOException::class)
override fun createServerSocket(port: Int): java.net.ServerSocket =
ServerSocket(port, SERVER_SOCKET_BACKLOG_SIZE, InetAddress.getByName(null))
}
val selectorMgr = ActorSelectorManager(Dispatchers.IO)
class ServerLoopbackSocketFactoryKtor : Serializable {
override fun equals(other: Any?): Boolean = other === this || super.equals(other)
override fun hashCode(): Int = super.hashCode()
@Throws(IOException::class)
fun createServerSocket(port: Int) =
aSocket(selectorMgr)
.tcp()
.bind(InetSocketAddress(InetAddress.getByName(null), port)) // TODO : NO BACKLOG SIZE CHANGE =(
}
abstract class AbstractClientLoopbackSocketFactory<SocketType> : Serializable {
override fun equals(other: Any?): Boolean = other === this || super.equals(other)
override fun hashCode(): Int = super.hashCode()
abstract protected fun socketCreate(host: String, port: Int): SocketType
@Throws(IOException::class)
fun createSocket(host: String, port: Int): SocketType {
var attemptsLeft = SOCKET_CONNECT_ATTEMPTS
while (true) {
try {
return socketCreate(host, port)
} catch (e: ConnectException) {
if (--attemptsLeft <= 0) throw e
}
Thread.sleep(SOCKET_CONNECT_INTERVAL_MS)
}
}
}
class ClientLoopbackSocketFactoryRMI : AbstractClientLoopbackSocketFactory<java.net.Socket>(), RMIClientSocketFactory {
override fun socketCreate(host: String, port: Int): Socket = Socket(InetAddress.getByName(null), port)
}
class ClientLoopbackSocketFactoryKtor : AbstractClientLoopbackSocketFactory<io.ktor.network.sockets.Socket>() {
override fun socketCreate(host: String, port: Int): io.ktor.network.sockets.Socket =
runBlocking { aSocket(selectorMgr).tcp().connect(InetSocketAddress(host, port)) }
}
}
private val portSelectionRng = Random()
fun findPortForSocket(attempts: Int, portRangeStart: Int, portRangeEnd: Int): ServerSocketWrapper {
var i = 0
var lastException: Exception? = null
while (i++ < attempts) {
val port = portSelectionRng.nextInt(portRangeEnd - portRangeStart) + portRangeStart
try {
return ServerSocketWrapper(
port,
LoopbackNetworkInterface.serverLoopbackSocketFactoryKtor.createServerSocket(port)
)
} catch (e: Exception) {
// assuming that the socketPort is already taken
lastException = e
}
}
throw IllegalStateException("Cannot find free socketPort in $attempts attempts", lastException)
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
interface RemoteOutputStreamAsync {
/** closeStream() name is chosen since Clients are AutoClosable now
* and Client-implementations of RemoteOutputStreamAsync have conflict of 'close' name **/
suspend fun closeStream()
suspend fun write(data: ByteArray, offset: Int, length: Int)
suspend fun write(dataByte: Int)
}
interface RemoteInputStreamAsync {
/** closeStream() name is chosen since Clients are AutoClosable now
* and Client-implementations of RemoteInputStreamAsync have conflict of 'close' name **/
suspend fun closeStream()
suspend fun read(length: Int): ByteArray
suspend fun read(): Int
}

View File

@@ -0,0 +1,12 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
interface RemoteOutputStreamAsyncClientSide : RemoteOutputStreamAsync, Client<RemoteOutputStreamAsyncServerSide>
interface RemoteInputStreamClientSide : RemoteInputStreamAsync, Client<RemoteInputStreamServerSide>

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ByteWriteChannelWrapper
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
interface RemoteOutputStreamAsyncServerSide : RemoteOutputStreamAsync, Server<RemoteOutputStreamAsyncServerSide> {
// Query messages:
class CloseMessage : Server.Message<RemoteOutputStreamAsyncServerSide>() {
override suspend fun processImpl(server: RemoteOutputStreamAsyncServerSide, printObject: (Any?) -> Unit) =
server.closeStream()
}
class WriteMessage(val data: ByteArray, val offset: Int = -1, val length: Int = -1) :
Server.Message<RemoteOutputStreamAsyncServerSide>() {
override suspend fun processImpl(server: RemoteOutputStreamAsyncServerSide, printObject: (Any?) -> Unit) =
server.write(data, offset, length)
}
class WriteIntMessage(val dataByte: Int) : Server.Message<RemoteOutputStreamAsyncServerSide>() {
override suspend fun processImpl(server: RemoteOutputStreamAsyncServerSide, printObject: (Any?) -> Unit) =
server.write(dataByte)
}
}
interface RemoteInputStreamServerSide : RemoteInputStreamAsync, Server<RemoteInputStreamServerSide> {
// Query messages:
class CloseMessage : Server.Message<RemoteInputStreamServerSide>() {
override suspend fun processImpl(server: RemoteInputStreamServerSide, printObject: (Any?) -> Unit) =
server.closeStream()
}
class ReadMessage(val length: Int = -1) : Server.Message<RemoteInputStreamServerSide>() {
override suspend fun processImpl(server: RemoteInputStreamServerSide, printObject: (Any?) -> Unit) =
printObject(if (length == -1) server.read() else server.read(length))
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.impls.RemoteInputStream
import org.jetbrains.kotlin.daemon.common.impls.RemoteOutputStream
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.DefaultClientRMIWrapper
class RemoteOutputStreamAsyncWrapper(val rmiOutput: RemoteOutputStream) : RemoteOutputStreamAsyncClientSide,
Client<RemoteOutputStreamAsyncServerSide> by DefaultClientRMIWrapper() {
override suspend fun closeStream() =
rmiOutput.close()
override suspend fun write(data: ByteArray, offset: Int, length: Int) =
rmiOutput.write(data, offset, length)
override suspend fun write(dataByte: Int) =
rmiOutput.write(dataByte)
}
class RemoteInputStreamAsyncWrapper(private val rmiInput: RemoteInputStream) : RemoteInputStreamClientSide,
Client<RemoteInputStreamServerSide> by DefaultClientRMIWrapper() {
override suspend fun closeStream() =
rmiInput.close()
override suspend fun read() =
rmiInput.read()
override suspend fun read(length: Int) =
rmiInput.read(length)
}
fun RemoteOutputStream.toClient() = RemoteOutputStreamAsyncWrapper(this)
fun RemoteInputStream.toClient() = RemoteInputStreamAsyncWrapper(this)

View File

@@ -0,0 +1,11 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.ReplStateFacadeAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Client
interface ReplStateFacadeClientSide: ReplStateFacadeAsync, Client<ReplStateFacadeServerSide>

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.cli.common.repl.ILineId
import org.jetbrains.kotlin.daemon.common.ReplStateFacadeAsync
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ByteWriteChannelWrapper
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.Server
interface ReplStateFacadeServerSide: ReplStateFacadeAsync, Server<ReplStateFacadeServerSide> {
// Query messages:
class GetIdMessage : Server.Message<ReplStateFacadeServerSide>() {
override suspend fun processImpl(server: ReplStateFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.getId())
}
class GetHistorySizeMessage : Server.Message<ReplStateFacadeServerSide>() {
override suspend fun processImpl(server: ReplStateFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.getHistorySize())
}
class HistoryGetMessage(val index: Int) : Server.Message<ReplStateFacadeServerSide>() {
override suspend fun processImpl(server: ReplStateFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.historyGet(index))
}
class HistoryResetMessage : Server.Message<ReplStateFacadeServerSide>() {
override suspend fun processImpl(server: ReplStateFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.historyReset())
}
class HistoryResetToMessage(val id: ILineId) : Server.Message<ReplStateFacadeServerSide>() {
override suspend fun processImpl(server: ReplStateFacadeServerSide, printObject: (Any?) -> Unit) =
printObject(server.historyResetTo(id))
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("CAST_NEVER_SUCCEEDS")
package org.jetbrains.kotlin.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ByteReadChannelWrapper
import org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure.ByteWriteChannelWrapper
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.X509EncodedKeySpec
const val SECURITY_TOKEN_SIZE = 128
private val secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
private val pairGenerator = KeyPairGenerator.getInstance("DSA", "SUN")
private val keyFactory = KeyFactory.getInstance("DSA", "SUN")
private fun generateSecurityToken(): ByteArray {
val tokenBuffer = ByteArray(SECURITY_TOKEN_SIZE)
secureRandom.nextBytes(tokenBuffer)
return tokenBuffer
}
data class SecurityData(val privateKey: PrivateKey, val publicKey: PublicKey, val token: ByteArray)
fun generateKeysAndToken() = pairGenerator.generateKeyPair().let {
SecurityData(it.private, it.public, generateSecurityToken())
}
private fun FileInputStream.readAllBytes(): ByteArray {
val bytes = arrayListOf<Byte>()
val buffer = ByteArray(1024)
var bytesRead = 0
while (bytesRead != -1) {
bytesRead = this.read(buffer, 0, 1024)
bytes.addAll(buffer.toList())
}
return ByteArray(bytes.size, bytes::get)
}
private fun FileInputStream.readBytesFixedLength(n: Int): ByteArray {
val buffer = ByteArray(n)
var bytesRead = 0
while (bytesRead != n) {
bytesRead += this.read(buffer, bytesRead, n - bytesRead)
}
return buffer
}
// server part :
fun sendTokenKeyPair(output: FileOutputStream, token: ByteArray, privateKey: PrivateKey) {
output.write(token)
ObjectOutputStream(output).use {
it.writeObject(privateKey)
}
}
suspend fun getSignatureAndVerify(input: ByteReadChannelWrapper, expectedToken: ByteArray, publicKey: PublicKey): Boolean {
val signature = input.nextBytes()
val dsa = Signature.getInstance("SHA1withDSA", "SUN")
dsa.initVerify(publicKey)
dsa.update(expectedToken, 0, SECURITY_TOKEN_SIZE)
val verified = dsa.verify(signature)
log.info("verified : $verified")
return verified
}
// client part :
fun readTokenKeyPairAndSign(input: FileInputStream): ByteArray {
val token = input.readBytesFixedLength(SECURITY_TOKEN_SIZE)
val privateKey = ObjectInputStream(input).use(ObjectInputStream::readObject) as PrivateKey
val dsa = Signature.getInstance("SHA1withDSA", "SUN")
dsa.initSign(privateKey)
dsa.update(token, 0, SECURITY_TOKEN_SIZE)
return dsa.sign()
}
suspend fun sendSignature(output: ByteWriteChannelWrapper, signature: ByteArray) = output.printBytesAndLength(signature.size, signature)

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import org.jetbrains.kotlin.daemon.common.COMPILE_DAEMON_FIND_PORT_ATTEMPTS
import org.jetbrains.kotlin.daemon.common.experimental.*
fun findCallbackServerSocket() = findPortForSocket(
COMPILE_DAEMON_FIND_PORT_ATTEMPTS,
CALLBACK_SERVER_PORTS_RANGE_START,
CALLBACK_SERVER_PORTS_RANGE_END
)
fun findReplServerSocket() = findPortForSocket(
COMPILE_DAEMON_FIND_PORT_ATTEMPTS,
REPL_SERVER_PORTS_RANGE_START,
REPL_SERVER_PORTS_RANGE_END
)

View File

@@ -0,0 +1,9 @@
package org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure
import java.util.concurrent.ConcurrentHashMap
typealias ConcurrentHashSet<T> = ConcurrentHashMap<T, Boolean>
fun <T> ConcurrentHashSet<T>.add(t: T) = this.put(t, true)
fun <T> ConcurrentHashSet<T>.asList() = this.keys().toList()

View File

@@ -0,0 +1,275 @@
package org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure
import io.ktor.network.sockets.Socket
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.experimental.LoopbackNetworkInterface
import sun.net.ConnectionResetException
import java.beans.Transient
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
import java.util.logging.Logger
interface Client<ServerType : ServerBase> : Serializable, AutoCloseable {
@Throws(Exception::class)
suspend fun connectToServer()
suspend fun sendMessage(msg: Server.AnyMessage<out ServerType>): Int // returns message unique id
suspend fun sendNoReplyMessage(msg: Server.AnyMessage<out ServerType>)
suspend fun <T> readMessage(id: Int): T
}
internal fun Logger.info_and_print(msg: String?) {
this.info(msg)
// println(msg)
}
@Suppress("UNCHECKED_CAST")
abstract class DefaultAuthorizableClient<ServerType : ServerBase>(
val serverPort: Int,
val serverHost: String = LoopbackNetworkInterface.loopbackInetAddressName
) : Client<ServerType> {
val log: Logger
@Transient get() = Logger.getLogger("default client($serverPort)")//.also { it.setUseParentHandlers(false); }
@kotlin.jvm.Transient
lateinit var input: ByteReadChannelWrapper
@kotlin.jvm.Transient
lateinit var output: ByteWriteChannelWrapper
@kotlin.jvm.Transient
private var socket: Socket? = null
abstract suspend fun authorizeOnServer(serverOutputChannel: ByteWriteChannelWrapper): Boolean
abstract suspend fun clientHandshake(input: ByteReadChannelWrapper, output: ByteWriteChannelWrapper, log: Logger): Boolean
abstract suspend fun startKeepAlives()
abstract suspend fun delayKeepAlives()
override fun close() {
socket?.close()
}
class MessageReply<T : Any>(val messageId: Int, val reply: T?) : Serializable
protected interface ReadActorQuery
protected data class ExpectReplyQuery(val messageId: Int, val result: CompletableDeferred<MessageReply<*>>) : ReadActorQuery
protected class ReceiveReplyQuery(val reply: MessageReply<*>) : ReadActorQuery
protected interface WriteActorQuery
protected data class SendNoreplyMessageQuery(val message: Server.AnyMessage<*>) : WriteActorQuery
protected data class SendMessageQuery(val message: Server.AnyMessage<*>, val messageId: CompletableDeferred<Any>) : WriteActorQuery
protected class StopAllRequests : ReadActorQuery, WriteActorQuery
@kotlin.jvm.Transient
protected lateinit var readActor: SendChannel<ReadActorQuery>
@kotlin.jvm.Transient
private lateinit var writeActor: SendChannel<WriteActorQuery>
override suspend fun sendMessage(msg: Server.AnyMessage<out ServerType>): Int {
log.info_and_print("send message : $msg")
val id = CompletableDeferred<Any>()
writeActor.send(SendMessageQuery(msg, id))
val idVal = id.await()
if (idVal is IOException) {
log.info_and_print("write exception : ${idVal.message}")
throw idVal
}
log.info_and_print("idVal = $idVal")
return idVal as Int
}
override suspend fun sendNoReplyMessage(msg: Server.AnyMessage<out ServerType>) {
log.info_and_print("sendNoReplyMessage $msg")
log.info_and_print("readActor: $readActor")
log.info_and_print("closed 4 send : ${readActor.isClosedForSend}")
writeActor.send(SendNoreplyMessageQuery(msg))
}
override suspend fun <T> readMessage(id: Int): T {
log.info("readMessage with_id$id")
val result = CompletableDeferred<MessageReply<*>>()
log.info("result : $result with_id$id")
try {
readActor.send(ExpectReplyQuery(id, result))
} catch (e: ClosedSendChannelException) {
throw IOException("failed to read message (channel was closed)")
}
log.info("sent with_id$id")
val actualResult = result.await().reply
log.info("actualResult : $actualResult with_id$id")
if (actualResult is IOException) {
throw actualResult
}
return actualResult as T
}
override suspend fun connectToServer() {
writeActor = actor(capacity = Channel.UNLIMITED) {
var firstFreeMessageId = 0
consumeEach { query ->
when (query) {
is SendMessageQuery -> {
val id = firstFreeMessageId++
log.info_and_print("[${log.name}, ${this@DefaultAuthorizableClient}] : sending message : ${query.message} (predicted id = ${id})")
try {
output.writeObject(query.message.withId(id))
query.messageId.complete(id)
} catch (e: IOException) {
query.messageId.complete(e)
}
}
is SendNoreplyMessageQuery -> {
log.info_and_print("[${log.name}] : sending noreply : ${query.message}")
output.writeObject(query.message.withId(-1))
}
is StopAllRequests -> {
channel.close()
}
}
}
}
class NextObjectQuery
val nextObjectQuery = NextObjectQuery()
val objectReaderActor = actor<NextObjectQuery>(capacity = Channel.UNLIMITED) {
consumeEach {
try {
val reply = input.nextObject()
if (reply is Server.ServerDownMessage<*>) {
throw IOException("connection closed by server")
} else if (reply !is MessageReply<*>) {
log.info_and_print("replyAny as MessageReply<*> - failed!")
throw IOException("contrafact message (expected MessageReply<*>)")
} else {
log.info_and_print("[${log.name}] : received reply ${reply.reply} (id = ${reply.messageId})}")
readActor.send(ReceiveReplyQuery(reply))
}
} catch (e: IOException) {
readActor.send(StopAllRequests())
}
}
}
readActor = actor(capacity = Channel.UNLIMITED) {
val receivedMessages = hashMapOf<Int, MessageReply<*>>()
val expectedMessages = hashMapOf<Int, ExpectReplyQuery>()
fun broadcastIOException(e: IOException) {
channel.close()
expectedMessages.forEach { id, deferred ->
deferred.result.complete(MessageReply(id, e))
}
expectedMessages.clear()
receivedMessages.clear()
}
consumeEach { query ->
when (query) {
is ExpectReplyQuery -> {
log.info_and_print("[${log.name}] : expect message with id = ${query.messageId}")
receivedMessages[query.messageId]?.also { reply ->
query.result.complete(reply)
} ?: expectedMessages.put(query.messageId, query).also {
log.info_and_print("[${log.name}] : intermediateActor.send(ReceiveReplyQuery())")
objectReaderActor.send(nextObjectQuery)
}
}
is ReceiveReplyQuery -> {
val reply = query.reply
log.info_and_print("[${log.name}] : got ReceiveReplyQuery")
expectedMessages[reply.messageId]?.also { expectedMsg ->
expectedMsg.result.complete(reply)
} ?: receivedMessages.put(reply.messageId, reply).also {
log.info_and_print("[${log.name}] : intermediateActor.send(ReceiveReplyQuery())")
objectReaderActor.send(nextObjectQuery)
}
delayKeepAlives()
}
is StopAllRequests -> {
broadcastIOException(IOException("KeepAlive failed"))
writeActor.send(StopAllRequests())
}
}
}
}
log.info_and_print("connectToServer (port = $serverPort | host = $serverHost)")
try {
socket = LoopbackNetworkInterface.clientLoopbackSocketFactoryKtor.createSocket(
serverHost,
serverPort
)
} catch (e: Throwable) {
log.info_and_print("EXCEPTION while connecting to server ($e)")
close()
throw e
}
log.info_and_print("connected (port = $serverPort, serv =$serverPort)")
socket?.openIO(log)?.also {
log.info_and_print("OK serv.openIO() |port=$serverPort|")
input = it.input
output = it.output
if (!clientHandshake(input, output, log)) {
log.info_and_print("failed handshake($serverPort)")
throw ConnectionResetException("failed to establish connection with server (handshake failed)")
}
if (!authorizeOnServer(output)) {
log.info_and_print("failed authorization($serverPort)")
throw ConnectionResetException("failed to establish connection with server (authorization failed)")
}
}
startKeepAlives()
}
@Throws(ClassNotFoundException::class, IOException::class)
private fun readObject(aInputStream: ObjectInputStream) {
aInputStream.defaultReadObject()
println("connecting...")
runBlocking { connectToServer() }
println("connectED")
}
@Throws(IOException::class)
private fun writeObject(aOutputStream: ObjectOutputStream) {
aOutputStream.defaultWriteObject()
}
}
class DefaultClient<ServerType : ServerBase>(
serverPort: Int,
serverHost: String = LoopbackNetworkInterface.loopbackInetAddressName
) : DefaultAuthorizableClient<ServerType>(serverPort, serverHost) {
override suspend fun clientHandshake(input: ByteReadChannelWrapper, output: ByteWriteChannelWrapper, log: Logger) = true
override suspend fun authorizeOnServer(output: ByteWriteChannelWrapper): Boolean = true
override suspend fun startKeepAlives() {}
override suspend fun delayKeepAlives() {}
}
class DefaultClientRMIWrapper<ServerType : ServerBase> : Client<ServerType> {
override suspend fun connectToServer() {}
override suspend fun sendMessage(msg: Server.AnyMessage<out ServerType>) =
throw UnsupportedOperationException("sendMessage is not supported for RMI wrappers")
override suspend fun sendNoReplyMessage(msg: Server.AnyMessage<out ServerType>) =
throw UnsupportedOperationException("sendMessage is not supported for RMI wrappers")
override suspend fun <T> readMessage(id: Int) = throw UnsupportedOperationException("readMessage is not supported for RMI wrappers")
override fun close() {}
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental.socketInfrastructure
import java.util.logging.Logger
class NullLogger {
fun info(msg: String) {}
fun error(msg: String) {}
fun fine(msg: String) {}
fun debug(msg: String) {}
}

View File

@@ -0,0 +1,231 @@
package org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure
import io.ktor.network.sockets.ServerSocket
import io.ktor.network.sockets.Socket
import kotlinx.coroutines.*
import org.jetbrains.kotlin.daemon.common.experimental.*
import java.io.Serializable
import java.util.concurrent.TimeUnit
import java.util.logging.Logger
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
private fun Logger.info_and_print(msg: String) {
this.info(msg)
// println(msg)
}
data class ServerSocketWrapper(val port: Int, val socket: ServerSocket)
interface ServerBase
@Suppress("UNCHECKED_CAST")
interface Server<out T : ServerBase> : ServerBase {
val serverSocketWithPort: ServerSocketWrapper
val serverPort: Int
get() = serverSocketWithPort.port
private val log: Logger
get() = Logger.getLogger("default server($serverPort)")
enum class State {
WORKING, CLOSED, ERROR, DOWNING, UNVERIFIED
}
fun processMessage(msg: AnyMessage<in T>, output: ByteWriteChannelWrapper): State =
when (msg) {
is Server.Message<in T> -> Server.State.WORKING.also {
msg.process(this as T, output)
}
is Server.EndConnectionMessage<in T> -> {
log.info_and_print("!EndConnectionMessage!")
Server.State.CLOSED
}
is Server.ServerDownMessage<in T> -> Server.State.CLOSED
else -> Server.State.ERROR
}
fun attachClient(client: Socket): Deferred<State> = async {
val (input, output) = client.openIO(log)
if (!serverHandshake(input, output, log)) {
log.info_and_print("failed to establish connection with client (handshake failed)")
return@async Server.State.UNVERIFIED
}
if (!securityCheck(input)) {
log.info_and_print("failed to check securitay")
return@async Server.State.UNVERIFIED
}
log.info_and_print(" client verified ($client)")
clients[client] = ClientInfo(client, input, output)
log.info_and_print(" ($client)client in clients($clients)")
var finalState = Server.State.WORKING
val keepAliveAcknowledgement = KeepAliveAcknowledgement<T>()
loop@
while (true) {
log.info_and_print(" reading message from ($client)")
val message = input.nextObject()
when (message) {
is Server.ServerDownMessage<*> -> {
downClient(client)
break@loop
}
is Server.KeepAliveMessage<*> -> Server.State.WORKING.also {
output.writeObject(
DefaultAuthorizableClient.MessageReply(
message.messageId!!,
keepAliveAcknowledgement
)
)
}
!is Server.AnyMessage<*> -> {
log.info_and_print("contrafact message")
finalState = Server.State.ERROR
break@loop
}
else -> {
log.info_and_print("message ($client): $message")
val state = processMessage(message as Server.AnyMessage<T>, output)
when (state) {
Server.State.WORKING -> continue@loop
Server.State.ERROR -> {
log.info_and_print("ERROR after processing message")
finalState = Server.State.ERROR
break@loop
}
else -> {
finalState = state
break@loop
}
}
}
}
}
finalState
}
abstract class AnyMessage<ServerType : ServerBase> : Serializable {
var messageId: Int? = null
fun withId(id: Int): AnyMessage<ServerType> {
messageId = id
return this
}
}
abstract class Message<ServerType : ServerBase> : AnyMessage<ServerType>() {
fun process(server: ServerType, output: ByteWriteChannelWrapper) = async {
log.info("$server starts processing ${this@Message}")
processImpl(server, {
log.info("$server finished processing ${this@Message}, sending output")
async {
log.info("$server starts sending ${this@Message} to output")
output.writeObject(DefaultAuthorizableClient.MessageReply(messageId ?: -1, it))
log.info("$server finished sending ${this@Message} to output")
}
})
}
abstract suspend fun processImpl(server: ServerType, sendReply: (Any?) -> Unit)
}
class EndConnectionMessage<ServerType : ServerBase> : AnyMessage<ServerType>()
class KeepAliveAcknowledgement<ServerType : ServerBase> : AnyMessage<ServerType>()
class KeepAliveMessage<ServerType : ServerBase> : AnyMessage<ServerType>()
class ServerDownMessage<ServerType : ServerBase> : AnyMessage<ServerType>()
data class ClientInfo(val socket: Socket, val input: ByteReadChannelWrapper, val output: ByteWriteChannelWrapper)
val clients: HashMap<Socket, ClientInfo>
fun runServer(): Deferred<Unit> {
log.info_and_print("binding to address(${serverSocketWithPort.port})")
val serverSocket = serverSocketWithPort.socket
return async {
serverSocket.use {
log.info_and_print("accepting clientSocket...")
while (true) {
val client = serverSocket.accept()
log.info_and_print("client accepted! (${client.remoteAddress})")
async {
val state = attachClient(client).await()
log.info_and_print("finished ($client) with state : $state")
when (state) {
Server.State.CLOSED, State.UNVERIFIED -> {
downClient(client)
}
Server.State.DOWNING -> {
downServer()
}
else -> {
downClient(client)
}
}
}
}
}
}
}
fun downServer() {
clients.forEach { socket, info ->
runBlockingWithTimeout {
info.output.writeObject(ServerDownMessage<T>())
info.output.close()
}
socket.close()
}
clients.clear()
serverSocketWithPort.socket.close()
}
private fun downClient(client: Socket) {
clients.remove(client)
client.close()
}
suspend fun securityCheck(clientInputChannel: ByteReadChannelWrapper): Boolean = true
suspend fun serverHandshake(input: ByteReadChannelWrapper, output: ByteWriteChannelWrapper, log: Logger) = true
}
fun <T> runBlockingWithTimeout(timeout: Long = AUTH_TIMEOUT_IN_MILLISECONDS, block: suspend () -> T) =
runBlocking { runWithTimeout(timeout = timeout) { block() } }
//@Throws(TimeoutException::class)
suspend fun <T> runWithTimeout(
timeout: Long = AUTH_TIMEOUT_IN_MILLISECONDS,
unit: TimeUnit = TimeUnit.MILLISECONDS,
block: suspend () -> T
): T? = withTimeoutOrNull(timeout, unit) { block() }
//@Throws(ConnectionResetException::class)
suspend fun tryAcquireHandshakeMessage(input: ByteReadChannelWrapper, log: Logger): Boolean {
log.info_and_print("tryAcquireHandshakeMessage")
val bytes = runWithTimeout {
input.nextBytes()
} ?: return false.also { log.info_and_print("tryAcquireHandshakeMessage - FAIL") }
log.info_and_print("bytes : ${bytes.toList()}")
if (bytes.zip(FIRST_HANDSHAKE_BYTE_TOKEN).any { it.first != it.second }) {
log.info_and_print("invalid token received")
return false
}
log.info_and_print("tryAcquireHandshakeMessage - SUCCESS")
return true
}
//@Throws(ConnectionResetException::class)
suspend fun trySendHandshakeMessage(output: ByteWriteChannelWrapper, log: Logger): Boolean {
log.info_and_print("trySendHandshakeMessage")
runWithTimeout {
output.printBytesAndLength(FIRST_HANDSHAKE_BYTE_TOKEN.size, FIRST_HANDSHAKE_BYTE_TOKEN)
} ?: return false.also { log.info_and_print("trySendHandshakeMessage - FAIL") }
log.info_and_print("trySendHandshakeMessage - SUCCESS")
return true
}

View File

@@ -0,0 +1,233 @@
package org.jetbrains.kotlin.daemon.common.experimental.socketInfrastructure
import io.ktor.network.sockets.Socket
import io.ktor.network.sockets.openReadChannel
import io.ktor.network.sockets.openWriteChannel
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.io.*
import kotlinx.io.core.readBytes
import java.io.*
import java.nio.ByteBuffer
import java.util.logging.Logger
private val DEFAULT_BYTE_ARRAY = byteArrayOf(0, 0, 0, 0)
class ByteReadChannelWrapper(readChannel: ByteReadChannel, private val log: Logger) {
private interface ReadQuery
private open class BytesQuery(val bytes: CompletableDeferred<ByteArray>) : ReadQuery
private class SerObjectQuery(val obj: CompletableDeferred<Any?>) : ReadQuery
suspend fun readLength(readChannel: ByteReadChannel) =
if (readChannel.isClosedForRead)
null
else
try {
readChannel.readPacket(4).readBytes()
} catch (e: Exception) {
log.info("failed to read message length, ${e.message}")
null
}
suspend fun readPacket(length: Int, readChannel: ByteReadChannel) =
try {
readChannel.readPacket(
length
).readBytes()
} catch (e: Exception) {
log.info("failed to read packet (${e.message})")
null
}
private val readActor = actor<ReadQuery>(capacity = Channel.UNLIMITED) {
consumeEach { message ->
if (!readChannel.isClosedForRead) {
readLength(readChannel)?.let { messageLength ->
when (message) {
is BytesQuery -> message.bytes.complete(
readChannel.readPacket(
getLength(messageLength)
).readBytes()
)
is SerObjectQuery -> message.obj.complete(
getObject(
getLength(messageLength),
{ len -> readPacket(len, readChannel) }
)
)
else -> {
}
}
}
} else {
log.info("read chanel closed " + log.name)
}
}
}
private fun getLength(packet: ByteArray): Int {
val (b1, b2, b3, b4) = packet.map(Byte::toInt)
return (0xFF and b1 shl 24 or (0xFF and b2 shl 16) or
(0xFF and b3 shl 8) or (0xFF and b4)).also { log.info(" $it") }
}
/** reads exactly <tt>length</tt> bytes.
* after deafault timeout returns <tt>DEFAULT_BYTE_ARRAY</tt> */
suspend fun readBytes(length: Int): ByteArray = runWithTimeout {
val expectedBytes = CompletableDeferred<ByteArray>()
// readActor.send(GivenLengthBytesQuery(length, expectedBytes))
expectedBytes.await()
} ?: DEFAULT_BYTE_ARRAY
/** first reads <t>length</t> token (4 bytes) and then -- reads <t>length</t> bytes.
* after deafault timeout returns <tt>DEFAULT_BYTE_ARRAY</tt> */
suspend fun nextBytes(): ByteArray = runWithTimeout {
val expectedBytes = CompletableDeferred<ByteArray>()
readActor.send(BytesQuery(expectedBytes))
expectedBytes.await()
} ?: DEFAULT_BYTE_ARRAY
private suspend fun getObject(length: Int, readPacket: suspend (Int) -> ByteArray?): Any? =
if (length >= 0) {
readPacket(length)?.let { bytes ->
ObjectInputStream(
ByteArrayInputStream(bytes)
).use {
it.readObject()
}
}
} else { // optimize for long strings!
readPacket(-length)?.let { bytes ->
String(
ByteArrayInputStream(
bytes
).readBytes()
)
}
}
/** first reads <t>length</t> token (4 bytes), then reads <t>length</t> bytes and returns deserialized object */
suspend fun nextObject(): Any? {
val obj = CompletableDeferred<Any?>()
readActor.send(SerObjectQuery(obj))
val result = obj.await()
if (result is Server.ServerDownMessage<*>) {
throw IOException("connection closed by server")
}
return result
}
}
class ByteWriteChannelWrapper(writeChannel: ByteWriteChannel, private val log: Logger) {
private interface WriteActorQuery
private open class ByteData(val bytes: ByteArray) : WriteActorQuery {
open fun toByteArray(): ByteArray = bytes
}
private class ObjectWithLength(val lengthBytes: ByteArray, bytes: ByteArray) : ByteData(bytes) {
override fun toByteArray() = lengthBytes + bytes
}
private class CloseMessage : WriteActorQuery
private suspend fun tryPrint(b: ByteArray, writeChannel: ByteWriteChannel) {
if (!writeChannel.isClosedForWrite) {
try {
writeChannel.writeFully(b)
} catch (e: Exception) {
log.info("failed to print message, ${e.message}")
}
} else {
log.info("closed chanel (write)")
}
}
private val writeActor = actor<WriteActorQuery>(capacity = Channel.UNLIMITED) {
consumeEach { message ->
if (!writeChannel.isClosedForWrite) {
when (message) {
is CloseMessage -> {
log.info("${log.name} closing chanel...")
writeChannel.close()
}
is ByteData -> {
tryPrint(message.toByteArray(), writeChannel)
if (!writeChannel.isClosedForWrite) {
try {
writeChannel.flush()
} catch (e: Exception) {
log.info("failed to flush byte write chanel")
}
}
}
}
} else {
log.info("${log.name} write chanel closed")
}
}
}
suspend fun printBytesAndLength(length: Int, bytes: ByteArray) {
writeActor.send(
ObjectWithLength(
getLengthBytes(length),
bytes
)
)
}
private suspend fun printObjectImpl(obj: Any?) =
ByteArrayOutputStream().use { bos ->
ObjectOutputStream(bos).use { objOut ->
objOut.writeObject(obj)
objOut.flush()
val bytes = bos.toByteArray()
printBytesAndLength(bytes.size, bytes)
}
}
.also {
log.info("sent object : $obj")
}
private suspend fun printString(s: String) = printBytesAndLength(-s.length, s.toByteArray())
fun getLengthBytes(length: Int) =
ByteBuffer
.allocate(4)
.putInt(length)
.array()
.also {
log.info("printLength $length")
}
suspend fun writeObject(obj: Any?) {
// println("write object : $obj")
if (obj is String) printString(obj)
else printObjectImpl(obj)
}
suspend fun close() = writeActor.send(CloseMessage())
}
fun ByteReadChannel.toWrapper(log: Logger) = ByteReadChannelWrapper(this, log)
fun ByteWriteChannel.toWrapper(log: Logger) = ByteWriteChannelWrapper(this, log)
fun Socket.openAndWrapReadChannel(log: Logger) = this.openReadChannel().toWrapper(log)
fun Socket.openAndWrapWriteChannel(log: Logger) = this.openWriteChannel().toWrapper(log)
data class IOPair(val input: ByteReadChannelWrapper, val output: ByteWriteChannelWrapper)
fun Socket.openIO(log: Logger) = IOPair(this.openAndWrapReadChannel(log), this.openAndWrapWriteChannel(log))

View File

@@ -15,6 +15,11 @@ dependencies {
compileOnly(project(":js:js.frontend"))
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
compileOnly(intellijDep()) { includeIntellijCoreJarDependencies(project) }
compile(projectDist(":kotlin-reflect"))
compile(project(":kotlin-reflect-api"))
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) {
isTransitive = false
}
}
sourceSets {

View File

@@ -1,128 +0,0 @@
/*
* 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.daemon.common
import java.io.File
import java.rmi.registry.LocateRegistry
internal val MAX_PORT_NUMBER = 0xffff
enum class DaemonReportCategory {
DEBUG, INFO, EXCEPTION;
}
fun makeRunFilenameString(timestamp: String, digest: String, port: String, escapeSequence: String = ""): String =
"$COMPILE_DAEMON_DEFAULT_FILES_PREFIX$escapeSequence.$timestamp$escapeSequence.$digest$escapeSequence.$port$escapeSequence.run"
fun makePortFromRunFilenameExtractor(digest: String): (String) -> Int? {
val regex = makeRunFilenameString(timestamp = "[0-9TZ:\\.\\+-]+", digest = digest, port = "(\\d+)", escapeSequence = "\\").toRegex()
return { regex.find(it)
?.groups?.get(1)
?.value?.toInt()
}
}
private const val ORPHANED_RUN_FILE_AGE_THRESHOLD_MS = 1000000L
data class DaemonWithMetadata(val daemon: CompileService, val runFile: File, val jvmOptions: DaemonJVMOptions)
// TODO: write metadata into discovery file to speed up selection
// TODO: consider using compiler jar signature (checksum) as a CompilerID (plus java version, plus ???) instead of classpath checksum
// would allow to use same compiler from taken different locations
// reqs: check that plugins (or anything els) should not be part of the CP
fun walkDaemons(registryDir: File,
compilerId: CompilerId,
fileToCompareTimestamp: File,
filter: (File, Int) -> Boolean = { _, _ -> true },
report: (DaemonReportCategory, String) -> Unit = { _, _ -> }
): Sequence<DaemonWithMetadata> {
val classPathDigest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString()
val portExtractor = makePortFromRunFilenameExtractor(classPathDigest)
return registryDir.walk()
.map { Pair(it, portExtractor(it.name)) }
.filter { (file, port) -> port != null && filter(file, port) }
.mapNotNull { (file, port) ->
assert(port!! in 1..(MAX_PORT_NUMBER - 1))
val relativeAge = fileToCompareTimestamp.lastModified() - file.lastModified()
report(DaemonReportCategory.DEBUG, "found daemon on port $port ($relativeAge ms old), trying to connect")
val daemon = tryConnectToDaemon(port, report)
// cleaning orphaned file; note: daemon should shut itself down if it detects that the run file is deleted
if (daemon == null) {
if (relativeAge - ORPHANED_RUN_FILE_AGE_THRESHOLD_MS <= 0) {
report(DaemonReportCategory.DEBUG, "found fresh run file '${file.absolutePath}' ($relativeAge ms old), but no daemon, ignoring it")
}
else {
report(DaemonReportCategory.DEBUG, "found seemingly orphaned run file '${file.absolutePath}' ($relativeAge ms old), deleting it")
if (!file.delete()) {
report(DaemonReportCategory.INFO, "WARNING: unable to delete seemingly orphaned file '${file.absolutePath}', cleanup recommended")
}
}
}
try {
daemon?.let { DaemonWithMetadata(it, file, it.getDaemonJVMOptions().get()) }
}
catch (e: Exception) {
report(DaemonReportCategory.INFO, "ERROR: unable to retrieve daemon JVM options, assuming daemon is dead: ${e.message}")
null
}
}
}
private inline fun tryConnectToDaemon(port: Int, report: (DaemonReportCategory, String) -> Unit): CompileService? {
try {
val daemon = LocateRegistry.getRegistry(LoopbackNetworkInterface.loopbackInetAddressName, port, LoopbackNetworkInterface.clientLoopbackSocketFactory)
?.lookup(COMPILER_SERVICE_RMI_NAME)
when (daemon) {
null -> report(DaemonReportCategory.INFO, "daemon not found")
is CompileService -> return daemon
else -> report(DaemonReportCategory.INFO, "Unable to cast compiler service, actual class received: ${daemon::class.java.name}")
}
}
catch (e: Throwable) {
report(DaemonReportCategory.INFO, "cannot connect to registry: " + (e.cause?.message ?: e.message ?: "unknown error"))
}
return null
}
private const val validFlagFileKeywordChars = "abcdefghijklmnopqrstuvwxyz0123456789-_"
fun makeAutodeletingFlagFile(keyword: String = "compiler-client", baseDir: File? = null): File {
val flagFile = File.createTempFile("kotlin-${keyword.filter { validFlagFileKeywordChars.contains(it.toLowerCase()) }}-", "-is-running", baseDir?.takeIf { it.isDirectory && it.exists() })
flagFile.deleteOnExit()
return flagFile
}
// Comparator for reliable choice between daemons
class FileAgeComparator : Comparator<File> {
override fun compare(left: File, right: File): Int {
val leftTS = left.lastModified()
val rightTS = right.lastModified()
return when {
leftTS == 0L || rightTS == 0L -> 0 // cannot read any file timestamp, => undecidable
leftTS > rightTS -> -1
leftTS < rightTS -> 1
else -> compareValues(left.canonicalPath, right.canonicalPath)
}
}
}
const val LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE = "Assuming other daemons have"

View File

@@ -21,13 +21,13 @@ import java.io.Serializable
import java.util.*
open class CompilationOptions(
val compilerMode: CompilerMode,
val targetPlatform: CompileService.TargetPlatform,
/** @See [ReportCategory] */
val compilerMode: CompilerMode,
val targetPlatform: CompileService.TargetPlatform,
/** @See [ReportCategory] */
val reportCategories: Array<Int>,
/** @See [ReportSeverity] */
/** @See [ReportSeverity] */
val reportSeverity: Int,
/** @See [CompilationResultCategory]] */
/** @See [CompilationResultCategory]] */
val requestedCompilationResults: Array<Int>
) : Serializable {
companion object {
@@ -46,18 +46,18 @@ open class CompilationOptions(
}
class IncrementalCompilationOptions(
val areFileChangesKnown: Boolean,
val modifiedFiles: List<File>?,
val deletedFiles: List<File>?,
val workingDir: File,
compilerMode: CompilerMode,
targetPlatform: CompileService.TargetPlatform,
/** @See [ReportCategory] */
reportCategories: Array<Int>,
/** @See [ReportSeverity] */
reportSeverity: Int,
/** @See [CompilationResultCategory]] */
requestedCompilationResults: Array<Int>,
val areFileChangesKnown: Boolean,
val modifiedFiles: List<File>?,
val deletedFiles: List<File>?,
val workingDir: File,
compilerMode: CompilerMode,
targetPlatform: CompileService.TargetPlatform,
/** @See [ReportCategory] */
reportCategories: Array<Int>,
/** @See [ReportSeverity] */
reportSeverity: Int,
/** @See [CompilationResultCategory]] */
requestedCompilationResults: Array<Int>,
val usePreciseJavaTracking: Boolean,
/**
* Directories that should be cleared when IC decides to rebuild

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import java.io.Serializable
interface CompilationResultsAsync {
suspend fun add(compilationResultCategory: Int, value: Serializable)
val clientSide: CompilationResultsAsync
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.impls.CompilationResults
import java.io.Serializable
class CompilationResultsAsyncWrapper(val rmiImpl: CompilationResults) : CompilationResultsAsync {
override val clientSide: CompilationResultsAsync
get() = this
override suspend fun add(compilationResultCategory: Int, value: Serializable) {
rmiImpl.add(compilationResultCategory, value)
}
}
class CompilationResultsRMIWrapper(val clientSide: CompilationResultsAsync) : CompilationResults, Serializable {
override fun add(compilationResultCategory: Int, value: Serializable) = runBlocking {
clientSide.add(compilationResultCategory, value)
}
// init {
// runBlocking {
// clientSide.connectToServer()
// }
// }
}
fun CompilationResults.toClient() =
if (this is CompilationResultsRMIWrapper) this.clientSide
else CompilationResultsAsyncWrapper(this)
fun CompilationResultsAsync.toRMI() =
if (this is CompilationResultsAsyncWrapper) this.rmiImpl
else CompilationResultsRMIWrapper(this)

View File

@@ -1,22 +1,12 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.daemon.common.impls.*
import java.io.File
import java.io.Serializable
import java.rmi.Remote
@@ -108,36 +98,36 @@ interface CompileService : Remote {
@Deprecated("The usages should be replaced with `compile` method", ReplaceWith("compile"))
@Throws(RemoteException::class)
fun remoteCompile(
sessionId: Int,
targetPlatform: TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
outputFormat: OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
sessionId: Int,
targetPlatform: TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
outputFormat: OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
): CallResult<Int>
@Deprecated("The usages should be replaced with `compile` method", ReplaceWith("compile"))
@Throws(RemoteException::class)
fun remoteIncrementalCompile(
sessionId: Int,
targetPlatform: TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
compilerOutputFormat: OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
sessionId: Int,
targetPlatform: TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
compilerOutputFormat: OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
): CallResult<Int>
@Throws(RemoteException::class)
fun compile(
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
compilationResults: CompilationResults?
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
compilationResults: CompilationResults?
): CallResult<Int>
@Throws(RemoteException::class)
@@ -152,18 +142,18 @@ interface CompileService : Remote {
@Deprecated("The usages should be replaced with other `leaseReplSession` method", ReplaceWith("leaseReplSession"))
@Throws(RemoteException::class)
fun leaseReplSession(
aliveFlagPath: String?,
targetPlatform: CompileService.TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade,
templateClasspath: List<File>,
templateClassName: String,
scriptArgs: Array<out Any?>?,
scriptArgsTypes: Array<out Class<out Any>>?,
compilerMessagesOutputStream: RemoteOutputStream,
evalOutputStream: RemoteOutputStream?,
evalErrorStream: RemoteOutputStream?,
evalInputStream: RemoteInputStream?,
operationsTracer: RemoteOperationsTracer?
aliveFlagPath: String?,
targetPlatform: TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade,
templateClasspath: List<File>,
templateClassName: String,
scriptArgs: Array<out Any?>?,
scriptArgsTypes: Array<out Class<out Any>>?,
compilerMessagesOutputStream: RemoteOutputStream,
evalOutputStream: RemoteOutputStream?,
evalErrorStream: RemoteOutputStream?,
evalInputStream: RemoteInputStream?,
operationsTracer: RemoteOperationsTracer?
): CallResult<Int>
@Throws(RemoteException::class)
@@ -194,12 +184,12 @@ interface CompileService : Remote {
@Throws(RemoteException::class)
fun leaseReplSession(
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
templateClasspath: List<File>,
templateClassName: String
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
templateClasspath: List<File>,
templateClassName: String
): CallResult<Int>
@Throws(RemoteException::class)

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import java.io.File
interface CompileServiceAsync {
suspend fun checkCompilerId(expectedCompilerId: CompilerId): Boolean
suspend fun getUsedMemory(): CompileService.CallResult<Long>
suspend fun getDaemonOptions(): CompileService.CallResult<DaemonOptions>
suspend fun getDaemonInfo(): CompileService.CallResult<String>
suspend fun getDaemonJVMOptions(): CompileService.CallResult<DaemonJVMOptions>
suspend fun registerClient(aliveFlagPath: String?): CompileService.CallResult<Nothing>
// TODO: (-old-) consider adding another client alive checking mechanism, e.g. socket/socketPort
suspend fun getClients(): CompileService.CallResult<List<String>>
suspend fun leaseCompileSession(aliveFlagPath: String?): CompileService.CallResult<Int>
suspend fun releaseCompileSession(sessionId: Int): CompileService.CallResult<Nothing>
suspend fun shutdown(): CompileService.CallResult<Nothing>
suspend fun scheduleShutdown(graceful: Boolean): CompileService.CallResult<Boolean>
suspend fun compile(
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBaseAsync,
compilationResults: CompilationResultsAsync?
): CompileService.CallResult<Int>
suspend fun clearJarCache()
suspend fun releaseReplSession(sessionId: Int): CompileService.CallResult<Nothing>
suspend fun leaseReplSession(
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBaseAsync,
templateClasspath: List<java.io.File>,
templateClassName: String
): CompileService.CallResult<Int>
suspend fun replCreateState(sessionId: Int): CompileService.CallResult<ReplStateFacadeAsync>
suspend fun replCheck(
sessionId: Int,
replStateId: Int,
codeLine: ReplCodeLine
): CompileService.CallResult<ReplCheckResult>
suspend fun replCompile(
sessionId: Int,
replStateId: Int,
codeLine: ReplCodeLine
): CompileService.CallResult<ReplCompileResult>
suspend fun classesFqNamesByFiles(sessionId: Int, sourceFiles: Set<File>): CompileService.CallResult<Set<String>>
val serverPort: Int
get() = 0
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import java.io.File
class CompileServiceAsyncWrapper(
val rmiCompileService: CompileService
) : CompileServiceAsync {
override suspend fun classesFqNamesByFiles(sessionId: Int, sourceFiles: Set<File>) =
rmiCompileService.classesFqNamesByFiles(sessionId, sourceFiles)
override suspend fun compile(
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBaseAsync,
compilationResults: CompilationResultsAsync?
) = rmiCompileService.compile(
sessionId,
compilerArguments,
compilationOptions,
servicesFacade.toRMI(),
compilationResults?.toRMI()
)
override suspend fun leaseReplSession(
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBaseAsync,
templateClasspath: List<File>,
templateClassName: String
) = rmiCompileService.leaseReplSession(
aliveFlagPath,
compilerArguments,
compilationOptions,
servicesFacade.toRMI(),
templateClasspath,
templateClassName
)
override suspend fun replCreateState(sessionId: Int) =
rmiCompileService.replCreateState(sessionId).toClient()
override suspend fun getUsedMemory() =
rmiCompileService.getUsedMemory()
override suspend fun getDaemonOptions() =
rmiCompileService.getDaemonOptions()
override suspend fun getDaemonInfo() =
rmiCompileService.getDaemonInfo()
override suspend fun getDaemonJVMOptions() =
rmiCompileService.getDaemonJVMOptions()
override suspend fun registerClient(aliveFlagPath: String?) =
rmiCompileService.registerClient(aliveFlagPath)
override suspend fun getClients() =
rmiCompileService.getClients()
override suspend fun leaseCompileSession(aliveFlagPath: String?) =
rmiCompileService.leaseCompileSession(aliveFlagPath)
override suspend fun releaseCompileSession(sessionId: Int) =
rmiCompileService.releaseCompileSession(sessionId)
override suspend fun shutdown() =
rmiCompileService.shutdown()
override suspend fun scheduleShutdown(graceful: Boolean) =
rmiCompileService.scheduleShutdown(graceful)
override suspend fun clearJarCache() =
rmiCompileService.clearJarCache()
override suspend fun releaseReplSession(sessionId: Int) =
rmiCompileService.releaseReplSession(sessionId)
override suspend fun replCheck(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine) =
rmiCompileService.replCheck(sessionId, replStateId, codeLine)
override suspend fun replCompile(
sessionId: Int,
replStateId: Int,
codeLine: ReplCodeLine
) = rmiCompileService.replCompile(sessionId, replStateId, codeLine)
override suspend fun checkCompilerId(expectedCompilerId: CompilerId) =
rmiCompileService.checkCompilerId(expectedCompilerId)
}
fun CompileService.toClient() = CompileServiceAsyncWrapper(this)

View File

@@ -0,0 +1,200 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.daemon.common.impls.*
import java.io.File
class CompileServiceClientRMIWrapper(
val asyncCompileService: CompileServiceAsync
) : CompileService {
override fun classesFqNamesByFiles(sessionId: Int, sourceFiles: Set<File>) = runBlocking {
asyncCompileService.classesFqNamesByFiles(sessionId, sourceFiles)
}
// deprecated methods :
override fun remoteCompile(
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
outputFormat: CompileService.OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
): CompileService.CallResult<Int> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun remoteIncrementalCompile(
sessionId: Int,
targetPlatform: CompileService.TargetPlatform,
args: Array<out String>,
servicesFacade: CompilerCallbackServicesFacade,
compilerOutputStream: RemoteOutputStream,
compilerOutputFormat: CompileService.OutputFormat,
serviceOutputStream: RemoteOutputStream,
operationsTracer: RemoteOperationsTracer?
): CompileService.CallResult<Int> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun leaseReplSession(
aliveFlagPath: String?,
targetPlatform: CompileService.TargetPlatform,
servicesFacade: CompilerCallbackServicesFacade,
templateClasspath: List<File>,
templateClassName: String,
scriptArgs: Array<out Any?>?,
scriptArgsTypes: Array<out Class<out Any>>?,
compilerMessagesOutputStream: RemoteOutputStream,
evalOutputStream: RemoteOutputStream?,
evalErrorStream: RemoteOutputStream?,
evalInputStream: RemoteInputStream?,
operationsTracer: RemoteOperationsTracer?
): CompileService.CallResult<Int> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun remoteReplLineCompile(
sessionId: Int,
codeLine: ReplCodeLine,
history: List<ReplCodeLine>?
): CompileService.CallResult<ReplCompileResult> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun remoteReplLineEval(
sessionId: Int,
codeLine: ReplCodeLine,
history: List<ReplCodeLine>?
): CompileService.CallResult<ReplEvalResult> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
// normal methods:
override fun compile(
sessionId: Int,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
compilationResults: CompilationResults?
) = runBlocking {
asyncCompileService.compile(
sessionId,
compilerArguments,
compilationOptions,
servicesFacade.toClient(),
compilationResults?.toClient() // TODO
)
}
override fun leaseReplSession(
aliveFlagPath: String?,
compilerArguments: Array<out String>,
compilationOptions: CompilationOptions,
servicesFacade: CompilerServicesFacadeBase,
templateClasspath: List<File>,
templateClassName: String
) = runBlocking {
asyncCompileService.leaseReplSession(
aliveFlagPath,
compilerArguments,
compilationOptions,
servicesFacade.toClient(),
templateClasspath,
templateClassName
)
}
override fun replCreateState(sessionId: Int) = runBlocking {
asyncCompileService.replCreateState(sessionId)
}.toRMI()
override fun getUsedMemory() = runBlocking {
asyncCompileService.getUsedMemory()
}
override fun getDaemonOptions() = runBlocking {
asyncCompileService.getDaemonOptions()
}
override fun getDaemonInfo() = runBlocking {
asyncCompileService.getDaemonInfo()
}
override fun getDaemonJVMOptions() = runBlocking {
asyncCompileService.getDaemonJVMOptions()
}
override fun registerClient(aliveFlagPath: String?) = runBlocking {
asyncCompileService.registerClient(aliveFlagPath)
}
override fun getClients() = runBlocking {
asyncCompileService.getClients()
}
override fun leaseCompileSession(aliveFlagPath: String?) = runBlocking {
asyncCompileService.leaseCompileSession(aliveFlagPath)
}
override fun releaseCompileSession(sessionId: Int) = runBlocking {
asyncCompileService.releaseCompileSession(sessionId)
}
override fun shutdown() = runBlocking {
asyncCompileService.shutdown()
}
override fun scheduleShutdown(graceful: Boolean) = runBlocking {
asyncCompileService.scheduleShutdown(graceful)
}
override fun clearJarCache() = runBlocking {
asyncCompileService.clearJarCache()
}
override fun releaseReplSession(sessionId: Int) = runBlocking {
asyncCompileService.releaseReplSession(sessionId)
}
override fun replCheck(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine) = runBlocking {
asyncCompileService.replCheck(sessionId, replStateId, codeLine)
}
override fun replCompile(
sessionId: Int,
replStateId: Int,
codeLine: ReplCodeLine
) = runBlocking {
asyncCompileService.replCompile(sessionId, replStateId, codeLine)
}
override fun checkCompilerId(expectedCompilerId: CompilerId) = runBlocking {
asyncCompileService.checkCompilerId(expectedCompilerId)
}
}
fun CompileServiceAsync.toRMI() = CompileServiceClientRMIWrapper(this)

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
import org.jetbrains.kotlin.modules.TargetId
interface CompilerCallbackServicesFacadeAsync : CompilerServicesFacadeBaseAsync {
suspend fun hasIncrementalCaches(): Boolean
suspend fun hasLookupTracker(): Boolean
suspend fun hasCompilationCanceledStatus(): Boolean
// ----------------------------------------------------
// IncrementalCache
suspend fun incrementalCache_getObsoletePackageParts(target: TargetId): Collection<String>
suspend fun incrementalCache_getObsoleteMultifileClassFacades(target: TargetId): Collection<String>
suspend fun incrementalCache_getPackagePartData(target: TargetId, partInternalName: String): JvmPackagePartProto?
suspend fun incrementalCache_getModuleMappingData(target: TargetId): ByteArray?
suspend fun incrementalCache_registerInline(target: TargetId, fromPath: String, jvmSignature: String, toPath: String)
suspend fun incrementalCache_getClassFilePath(target: TargetId, internalClassName: String): String
suspend fun incrementalCache_close(target: TargetId)
suspend fun incrementalCache_getMultifileFacadeParts(target: TargetId, internalName: String): Collection<String>?
// ----------------------------------------------------
// LookupTracker
suspend fun lookupTracker_requiresPosition(): Boolean
suspend fun lookupTracker_record(lookups: Collection<LookupInfo>)
suspend fun lookupTracker_isDoNothing(): Boolean
// ----------------------------------------------------
// CompilationCanceledStatus
suspend fun compilationCanceledStatus_checkCanceled(): Void?
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.daemon.common.impls.ReportCategory
import org.jetbrains.kotlin.daemon.common.impls.ReportSeverity
import java.io.Serializable
interface CompilerServicesFacadeBaseAsync {
/**
* Reports different kind of diagnostic messages from compile daemon to compile daemon clients (jps, gradle, ...)
*/
suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?)
}
suspend fun CompilerServicesFacadeBaseAsync.report(
category: ReportCategory,
severity: ReportSeverity,
message: String? = null,
attachment: Serializable? = null
) {
report(category.code, severity.code, message, attachment)
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.daemon.common.impls.CompilerServicesFacadeBase
import java.io.Serializable
class CompilerServicesFacadeBaseAsyncWrapper(
val rmiImpl: CompilerServicesFacadeBase
) : CompilerServicesFacadeBaseAsync {
override suspend fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) =
rmiImpl.report(category, severity, message, attachment)
}
fun CompilerServicesFacadeBase.toClient() =
if (this is CompilerServicesFacadeBaseRMIWrapper) this.clientSide
else CompilerServicesFacadeBaseAsyncWrapper(this)

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.impls.CompilerServicesFacadeBase
import java.io.Serializable
class CompilerServicesFacadeBaseRMIWrapper(val clientSide: CompilerServicesFacadeBaseAsync) : CompilerServicesFacadeBase, Serializable {
override fun report(category: Int, severity: Int, message: String?, attachment: Serializable?) = runBlocking {
clientSide.report(category, severity, message, attachment)
}
}
fun CompilerServicesFacadeBaseAsync.toRMI() =
if (this is CompilerServicesFacadeBaseAsyncWrapper) this.rmiImpl
else CompilerServicesFacadeBaseRMIWrapper(this)

View File

@@ -40,7 +40,7 @@ val COMPILE_DAEMON_REPORT_PERF_PROPERTY: String = "kotlin.daemon.perf"
val COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY: String = "kotlin.daemon.verbose"
val COMPILE_DAEMON_CMDLINE_OPTIONS_PREFIX: String = "--daemon-"
val COMPILE_DAEMON_STARTUP_TIMEOUT_PROPERTY: String = "kotlin.daemon.startup.timeout"
val COMPILE_DAEMON_DEFAULT_FILES_PREFIX: String = "kotlin-daemon"
val COMPILE_DAEMON_DEFAULT_FILES_PREFIX: String = "kotlin-daemon-EXP"
val COMPILE_DAEMON_TIMEOUT_INFINITE_S: Int = 0
val COMPILE_DAEMON_DEFAULT_IDLE_TIMEOUT_S: Int = 7200 // 2 hours
val COMPILE_DAEMON_DEFAULT_UNUSED_TIMEOUT_S: Int = 60

View File

@@ -1,19 +0,0 @@
/*
* Copyright 2010-2017 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.daemon.common
interface IncrementalCompilerServicesFacade : CompilerServicesFacadeBase

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.daemon.common.CompilerServicesFacadeBaseAsync
import org.jetbrains.kotlin.daemon.common.impls.SimpleDirtyData
import java.io.File
interface IncrementalCompilerServicesFacadeAsync : CompilerServicesFacadeBaseAsync

View File

@@ -1,19 +0,0 @@
/*
* Copyright 2010-2017 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.daemon.common
interface JpsCompilerServicesFacade : CompilerServicesFacadeBase, CompilerCallbackServicesFacade

View File

@@ -1,143 +1,30 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.daemon.common.impls.*
import java.lang.management.ManagementFactory
import java.lang.management.ThreadMXBean
import java.util.concurrent.atomic.AtomicLong
interface PerfCounters {
val count: Long
val time: Long
val threadTime: Long
val threadUserTime: Long
val memory: Long
fun addMeasurement(time: Long = 0, thread: Long = 0, threadUser: Long = 0, memory: Long = 0)
}
interface Profiler {
fun getCounters(): Map<Any?, PerfCounters>
fun getTotalCounters(): PerfCounters
fun<R> withMeasure(obj: Any?, body: () -> R): R
suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R
}
open class SimplePerfCounters : PerfCounters {
private val _count: AtomicLong = AtomicLong(0L)
private val _time: AtomicLong = AtomicLong(0L)
private val _threadTime: AtomicLong = AtomicLong(0L)
private val _threadUserTime: AtomicLong = AtomicLong(0L)
private val _memory: AtomicLong = AtomicLong(0L)
override val count: Long get() = _count.get()
override val time: Long get() = _time.get()
override val threadTime: Long get() = _threadTime.get()
override val threadUserTime: Long get() = _threadUserTime.get()
override val memory: Long get() = _memory.get()
override fun addMeasurement(time: Long, thread: Long, threadUser: Long, memory: Long) {
_count.incrementAndGet()
_time.addAndGet(time)
_threadTime.addAndGet(thread)
_threadUserTime.addAndGet(threadUser)
_memory.addAndGet(memory)
}
}
class SimplePerfCountersWithTotal(val totalRef: PerfCounters) : SimplePerfCounters() {
override fun addMeasurement(time: Long, thread: Long, threadUser: Long, memory: Long) {
super.addMeasurement(time, thread, threadUser, memory)
totalRef.addMeasurement(time, thread, threadUser, memory)
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun ThreadMXBean.threadCpuTime() = if (isCurrentThreadCpuTimeSupported) currentThreadCpuTime else 0L
@Suppress("NOTHING_TO_INLINE")
inline fun ThreadMXBean.threadUserTime() = if (isCurrentThreadCpuTimeSupported) currentThreadUserTime else 0L
@Suppress("NOTHING_TO_INLINE")
inline fun usedMemory(withGC: Boolean): Long {
if (withGC) {
System.gc()
}
val rt = Runtime.getRuntime()
return (rt.totalMemory() - rt.freeMemory())
}
inline fun<R> withMeasureWallTime(perfCounters: PerfCounters, body: () -> R): R {
val startTime = System.nanoTime()
val res = body()
perfCounters.addMeasurement(time = System.nanoTime() - startTime) // TODO: add support for time wrapping
return res
}
inline fun<R> withMeasureWallAndThreadTimes(perfCounters: PerfCounters, threadMXBean: ThreadMXBean, body: () -> R): R {
val startTime = System.nanoTime()
val startThreadTime = threadMXBean.threadCpuTime()
val startThreadUserTime = threadMXBean.threadUserTime()
val res = body()
// TODO: add support for time wrapping
perfCounters.addMeasurement(time = System.nanoTime() - startTime,
thread = threadMXBean.threadCpuTime() - startThreadTime,
threadUser = threadMXBean.threadUserTime() - startThreadUserTime)
return res
}
inline fun<R> withMeasureWallAndThreadTimes(perfCounters: PerfCounters, body: () -> R): R = withMeasureWallAndThreadTimes(perfCounters, ManagementFactory.getThreadMXBean(), body)
inline fun<R> withMeasureWallAndThreadTimesAndMemory(perfCounters: PerfCounters, withGC: Boolean = false, threadMXBean: ThreadMXBean, body: () -> R): R {
val startMem = usedMemory(withGC)
val startTime = System.nanoTime()
val startThreadTime = threadMXBean.threadCpuTime()
val startThreadUserTime = threadMXBean.threadUserTime()
val res = body()
// TODO: add support for time wrapping
perfCounters.addMeasurement(time = System.nanoTime() - startTime,
thread = threadMXBean.threadCpuTime() - startThreadTime,
threadUser = threadMXBean.threadUserTime() - startThreadUserTime,
memory = usedMemory(withGC) - startMem)
return res
}
inline fun<R> withMeasureWallAndThreadTimesAndMemory(perfCounters: PerfCounters, withGC: Boolean, body: () -> R): R =
withMeasureWallAndThreadTimesAndMemory(perfCounters, withGC, ManagementFactory.getThreadMXBean(), body)
class DummyProfiler : Profiler {
override fun getCounters(): Map<Any?, PerfCounters> = mapOf(null to SimplePerfCounters())
override fun getTotalCounters(): PerfCounters = SimplePerfCounters()
override fun getTotalCounters(): PerfCounters =
SimplePerfCounters()
override final inline fun <R> withMeasure(obj: Any?, body: () -> R): R = body()
override suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R = body()
}
abstract class TotalProfiler : Profiler {
val total = SimplePerfCounters()
@@ -147,28 +34,65 @@ abstract class TotalProfiler : Profiler {
override fun getTotalCounters(): PerfCounters = total
}
suspend fun <R> withMeasureWallAndThreadTimesAndMemory(
perfCounters: PerfCounters,
withGC: Boolean = false,
threadMXBean: ThreadMXBean,
body: suspend () -> R
): R {
val startMem = usedMemory(withGC)
val startTime = System.nanoTime()
val startThreadTime = threadMXBean.threadCpuTime()
val startThreadUserTime = threadMXBean.threadUserTime()
class WallTotalProfiler : TotalProfiler() {
override final inline fun <R> withMeasure(obj: Any?, body: () -> R): R = withMeasureWallTime(total, body)
val res = body()
// TODO: add support for time wrapping
perfCounters.addMeasurement(
time = System.nanoTime() - startTime,
thread = threadMXBean.threadCpuTime() - startThreadTime,
threadUser = threadMXBean.threadUserTime() - startThreadUserTime,
memory = usedMemory(withGC) - startMem
)
return res
}
suspend fun <R> withMeasureWallAndThreadTimes(
perfCounters: PerfCounters,
threadMXBean: ThreadMXBean,
body: suspend () -> R
): R {
val startTime = System.nanoTime()
val startThreadTime = threadMXBean.threadCpuTime()
val startThreadUserTime = threadMXBean.threadUserTime()
val res = body()
// TODO: add support for time wrapping
perfCounters.addMeasurement(
time = System.nanoTime() - startTime,
thread = threadMXBean.threadCpuTime() - startThreadTime,
threadUser = threadMXBean.threadUserTime() - startThreadUserTime
)
return res
}
class WallAndThreadTotalProfiler : TotalProfiler() {
override final inline fun <R> withMeasure(obj: Any?, body: () -> R): R = withMeasureWallAndThreadTimes(total, threadMXBean, body)
override suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R =
withMeasureWallAndThreadTimes(
total,
threadMXBean,
body
)
}
class WallAndThreadAndMemoryTotalProfiler(val withGC: Boolean) : TotalProfiler() {
override final inline fun <R> withMeasure(obj: Any?, body: () -> R): R = withMeasureWallAndThreadTimesAndMemory(total, withGC, threadMXBean, body)
override suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R =
withMeasureWallAndThreadTimesAndMemory(total, withGC, threadMXBean, body)
}
class WallAndThreadByClassProfiler() : TotalProfiler() {
val counters = hashMapOf<Any?, SimplePerfCountersWithTotal>()
override fun getCounters(): Map<Any?, PerfCounters> = counters
override final inline fun <R> withMeasure(obj: Any?, body: () -> R): R =
withMeasureWallAndThreadTimes(counters.getOrPut(obj?.javaClass?.name, { SimplePerfCountersWithTotal(total) }), threadMXBean, body)
}
fun <R> Profiler.withMeasureBlocking(obj: Any?, body: suspend () -> R): R = runBlocking {
this@withMeasureBlocking.withMeasure<R>(obj, body)
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.cli.common.repl.ILineId
interface ReplStateFacadeAsync {
suspend fun getId(): Int
suspend fun getHistorySize(): Int
suspend fun historyGet(index: Int): ILineId
suspend fun historyReset(): List<ILineId>
suspend fun historyResetTo(id: ILineId): List<ILineId>
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import org.jetbrains.kotlin.cli.common.repl.ILineId
import org.jetbrains.kotlin.daemon.common.impls.ReplStateFacade
class ReplStateFacadeAsyncWrapper(val rmiReplStateFacade: ReplStateFacade) : ReplStateFacadeAsync {
override suspend fun getId() = rmiReplStateFacade.getId()
override suspend fun getHistorySize() = rmiReplStateFacade.getHistorySize()
override suspend fun historyGet(index: Int) = rmiReplStateFacade.historyGet(index)
override suspend fun historyReset() = rmiReplStateFacade.historyReset()
override suspend fun historyResetTo(id: ILineId) = rmiReplStateFacade.historyResetTo(id)
}
fun ReplStateFacade.toClient() = ReplStateFacadeAsyncWrapper(this)
fun CompileService.CallResult<ReplStateFacade>.toClient() = when (this) {
is CompileService.CallResult.Good -> CompileService.CallResult.Good(this.result.toClient())
is CompileService.CallResult.Dying -> this
is CompileService.CallResult.Error -> this
is CompileService.CallResult.Ok -> this
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.repl.ILineId
import org.jetbrains.kotlin.daemon.common.CompileService
import org.jetbrains.kotlin.daemon.common.impls.ReplStateFacade
import java.io.Serializable
class ReplStateFacadeRMIWrapper(val clientSide: ReplStateFacadeAsync) : ReplStateFacade, Serializable {
override fun getId() = runBlocking { clientSide.getId() }
override fun getHistorySize() = runBlocking { clientSide.getHistorySize() }
override fun historyGet(index: Int) = runBlocking { clientSide.historyGet(index) }
override fun historyReset() = runBlocking { clientSide.historyReset() }
override fun historyResetTo(id: ILineId) = runBlocking { clientSide.historyResetTo(id) }
// init {
// runBlocking {
// clientSide.connectToServer()
// }
// }
}
fun ReplStateFacadeAsync.toRMI() = ReplStateFacadeRMIWrapper(this)
fun CompileService.CallResult<ReplStateFacadeAsync>.toRMI() = when (this) {
is CompileService.CallResult.Good -> CompileService.CallResult.Good(this.result.toRMI())
is CompileService.CallResult.Dying -> this
is CompileService.CallResult.Error -> this
is CompileService.CallResult.Ok -> this
}

View File

@@ -0,0 +1,10 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
enum class Version {
RMI, SOCKETS
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.experimental
import kotlinx.coroutines.experimental.runBlocking
import org.jetbrains.kotlin.daemon.common.*
import java.lang.management.ManagementFactory
import java.lang.management.ThreadMXBean
interface Profiler {
fun getCounters(): Map<Any?, PerfCounters>
fun getTotalCounters(): PerfCounters
suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R
}
class DummyProfiler : Profiler {
override fun getCounters(): Map<Any?, PerfCounters> = mapOf(null to SimplePerfCounters())
override fun getTotalCounters(): PerfCounters = SimplePerfCounters()
override suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R = body()
}
abstract class TotalProfiler : Profiler {
val total = SimplePerfCounters()
val threadMXBean = ManagementFactory.getThreadMXBean()
override fun getCounters(): Map<Any?, PerfCounters> = mapOf()
override fun getTotalCounters(): PerfCounters = total
}
suspend fun <R> withMeasureWallAndThreadTimesAndMemory(
perfCounters: PerfCounters,
withGC: Boolean = false,
threadMXBean: ThreadMXBean,
body: suspend () -> R
): R {
val startMem = usedMemory(withGC)
val startTime = System.nanoTime()
val startThreadTime = threadMXBean.threadCpuTime()
val startThreadUserTime = threadMXBean.threadUserTime()
val res = body()
// TODO: add support for time wrapping
perfCounters.addMeasurement(
time = System.nanoTime() - startTime,
thread = threadMXBean.threadCpuTime() - startThreadTime,
threadUser = threadMXBean.threadUserTime() - startThreadUserTime,
memory = usedMemory(withGC) - startMem
)
return res
}
suspend fun <R> withMeasureWallAndThreadTimes(
perfCounters: PerfCounters,
threadMXBean: ThreadMXBean,
body: suspend () -> R
): R {
val startTime = System.nanoTime()
val startThreadTime = threadMXBean.threadCpuTime()
val startThreadUserTime = threadMXBean.threadUserTime()
val res = body()
// TODO: add support for time wrapping
perfCounters.addMeasurement(
time = System.nanoTime() - startTime,
thread = threadMXBean.threadCpuTime() - startThreadTime,
threadUser = threadMXBean.threadUserTime() - startThreadUserTime
)
return res
}
class WallAndThreadTotalProfiler : TotalProfiler() {
override suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R = withMeasureWallAndThreadTimes(
total,
threadMXBean,
body
)
}
class WallAndThreadAndMemoryTotalProfiler(val withGC: Boolean) : TotalProfiler() {
override suspend fun <R> withMeasure(obj: Any?, body: suspend () -> R): R =
withMeasureWallAndThreadTimesAndMemory(total, withGC, threadMXBean, body)
}
fun <R> Profiler.withMeasureBlocking(obj: Any?, body: suspend () -> R): R = runBlocking {
this@withMeasureBlocking.withMeasure<R>(obj, body)
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common.impls
import org.jetbrains.kotlin.daemon.common.*
import java.io.File
import java.rmi.registry.LocateRegistry
import java.util.*
import java.util.logging.Logger
internal val MAX_PORT_NUMBER = 0xffff
enum class DaemonReportCategory {
DEBUG, INFO, EXCEPTION;
}
fun makeRunFilenameString(timestamp: String, digest: String, port: String, escapeSequence: String = ""): String =
"$COMPILE_DAEMON_DEFAULT_FILES_PREFIX$escapeSequence.$timestamp$escapeSequence.$digest$escapeSequence.$port$escapeSequence.runServer"
fun makePortFromRunFilenameExtractor(digest: String): (String) -> Int? {
val regex = makeRunFilenameString(
timestamp = "[0-9TZ:\\.\\+-]+",
digest = digest,
port = "(\\d+)",
escapeSequence = "\\"
).toRegex()
return {
regex.find(it)
?.groups?.get(1)
?.value?.toInt()
}
}
private const val ORPHANED_RUN_FILE_AGE_THRESHOLD_MS = 1000000L
private val log = Logger.getLogger("ClientUtils(old)")
data class DaemonWithMetadata(val daemon: CompileService, val runFile: File, val jvmOptions: DaemonJVMOptions)
// TODO: write metadata into discovery file to speed up selection
// TODO: consider using compiler jar signature (checksum) as a CompilerID (plus java version, plus ???) instead of classpath checksum
// would allow to use same compiler from taken different locations
// reqs: check that plugins (or anything els) should not be part of the CP
fun walkDaemons(
registryDir: File,
compilerId: CompilerId,
fileToCompareTimestamp: File,
filter: (File, Int) -> Boolean = { _, _ -> true },
report: (DaemonReportCategory, String) -> Unit = { _, _ -> }
): Sequence<DaemonWithMetadata> {
val classPathDigest = compilerId.compilerClasspath.map { File(it).absolutePath }.distinctStringsDigest().toHexString()
val portExtractor = makePortFromRunFilenameExtractor(classPathDigest)
return registryDir.walk()
.map { Pair(it, portExtractor(it.name)) }
.filter { (file, port) -> port != null && filter(file, port) }
.mapNotNull { (file, port) ->
assert(port!! in 1..(MAX_PORT_NUMBER - 1))
val relativeAge = fileToCompareTimestamp.lastModified() - file.lastModified()
report(DaemonReportCategory.DEBUG, "found daemon on socketPort $port ($relativeAge ms old), trying to connect")
val daemon = tryConnectToDaemon(port, report)
log.info("discovered daemon = $daemon")
// cleaning orphaned file; note: daemon should shut itself down if it detects that the runServer file is deleted
if (daemon == null) {
if (relativeAge - ORPHANED_RUN_FILE_AGE_THRESHOLD_MS <= 0) {
report(
DaemonReportCategory.DEBUG,
"found fresh runServer file '${file.absolutePath}' ($relativeAge ms old), but no daemon, ignoring it"
)
} else {
report(
DaemonReportCategory.DEBUG,
"found seemingly orphaned runServer file '${file.absolutePath}' ($relativeAge ms old), deleting it"
)
if (!file.delete()) {
report(
DaemonReportCategory.INFO,
"WARNING: unable to delete seemingly orphaned file '${file.absolutePath}', cleanup recommended"
)
}
}
}
try {
log.info("it.getDaemonJVMOptions()")
daemon?.let { DaemonWithMetadata(it, file, it.getDaemonJVMOptions().get()) }
} catch (e: Exception) {
log.info(e.message)
report(DaemonReportCategory.INFO, "ERROR: unable to retrieve daemon JVM options, assuming daemon is dead: ${e.message}")
null
}
}
}
private inline fun tryConnectToDaemon(port: Int, report: (DaemonReportCategory, String) -> Unit): CompileService? {
log.info("trying to connect to daemon (using port $port)")
try {
log.info("acquiring registry")
val registry = LocateRegistry.getRegistry(
LoopbackNetworkInterface.loopbackInetAddressName,
port,
LoopbackNetworkInterface.clientLoopbackSocketFactory
)
log.info("registry = $registry")
log.info("looking up for daemon...")
val daemon = registry?.lookup(COMPILER_SERVICE_RMI_NAME)
log.info("connection result daemon = $daemon")
when (daemon) {
null -> report(DaemonReportCategory.INFO, "daemon not found")
is CompileService -> return daemon
else -> report(DaemonReportCategory.INFO, "Unable to cast compiler service, actual class received: ${daemon::class.java.name}")
}
} catch (e: Throwable) {
report(DaemonReportCategory.INFO, "cannot connect to registry: " + (e.cause?.message ?: e.message ?: "unknown error"))
}
log.info("connection result daemon = NULL")
return null
}
private const val validFlagFileKeywordChars = "abcdefghijklmnopqrstuvwxyz0123456789-_"
fun makeAutodeletingFlagFile(keyword: String = "compiler-client", baseDir: File? = null): File {
val flagFile = File.createTempFile("kotlin-${keyword.filter { validFlagFileKeywordChars.contains(it.toLowerCase()) }}-",
"-is-running",
baseDir?.takeIf { it.isDirectory && it.exists() })
flagFile.deleteOnExit()
return flagFile
}
// Comparator for reliable choice between daemons
class FileAgeComparator : Comparator<File> {
override fun compare(left: File, right: File): Int {
val leftTS = left.lastModified()
val rightTS = right.lastModified()
return when {
leftTS == 0L || rightTS == 0L -> 0 // cannot read any file timestamp, => undecidable
leftTS > rightTS -> -1
leftTS < rightTS -> 1
else -> compareValues(left.canonicalPath, right.canonicalPath)
}
}
}
const val LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE = "Assuming other daemons have"

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.daemon.common
package org.jetbrains.kotlin.daemon.common.impls
import java.io.Serializable
import java.rmi.Remote

View File

@@ -1,20 +1,9 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
package org.jetbrains.kotlin.daemon.common.impls
import org.jetbrains.kotlin.incremental.components.LookupInfo
import org.jetbrains.kotlin.incremental.js.JsInlineFunctionHash

View File

@@ -1,20 +1,9 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
package org.jetbrains.kotlin.daemon.common.impls
import java.io.Serializable
import java.rmi.Remote

View File

@@ -1,20 +1,9 @@
/*
* 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.
* Copyright 2000-2018 JetBrains s.r.o. 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.daemon.common
package org.jetbrains.kotlin.daemon.common.impls
import java.io.File
import java.io.Serializable

Some files were not shown because too many files have changed in this diff Show More