mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-18 15:51:22 +00:00
Compare commits
2 Commits
abannykh/v
...
stdlib/sco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5a1d628ea | ||
|
|
83aec23052 |
4
.idea/kotlinc.xml
generated
4
.idea/kotlinc.xml
generated
@@ -1,9 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinCommonCompilerArguments">
|
||||
<option name="languageVersion" value="1.1" />
|
||||
<option name="apiVersion" value="1.1" />
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
|
||||
</component>
|
||||
|
||||
122
ChangeLog.md
122
ChangeLog.md
@@ -3,128 +3,6 @@
|
||||
<!-- Find: ([^\`/\[])(KT-\d+) -->
|
||||
<!-- Replace: $1[`$2`](https://youtrack.jetbrains.com/issue/$2) -->
|
||||
|
||||
## 1.1
|
||||
|
||||
### Compiler exceptions
|
||||
- [`KT-16411`](https://youtrack.jetbrains.com/issue/KT-16411) Exception from compiler when try to inline callable reference to class constructor inside object
|
||||
- [`KT-16412`](https://youtrack.jetbrains.com/issue/KT-16412) Exception from compiler when try call SAM constructor where argument is callable reference to nested class inside object
|
||||
- [`KT-16413`](https://youtrack.jetbrains.com/issue/KT-16413) When we create sam adapter for java.util.function.Function we add incorrect null-check for argument
|
||||
|
||||
### Standard library
|
||||
- [`KT-6561`](https://youtrack.jetbrains.com/issue/KT-6561) Drop java.util.Collections package from js stdlib
|
||||
|
||||
### IDE
|
||||
- [`KT-16329`](https://youtrack.jetbrains.com/issue/KT-16329) Inspection "Calls to staic methods in Java interfaces..." always reports warning undependent of jvm-target
|
||||
|
||||
|
||||
## 1.1-RC
|
||||
|
||||
### Reflection
|
||||
- [`KT-16358`](https://youtrack.jetbrains.com/issue/KT-16358) Incompatibility between kotlin-reflect 1.0 and kotlin-stdlib 1.1 fixed
|
||||
|
||||
### Compiler
|
||||
|
||||
#### Coroutine support
|
||||
- [`KT-15938`](https://youtrack.jetbrains.com/issue/KT-15938) Changed error message for calling suspend function outside of suspendable context
|
||||
- [`KT-16092`](https://youtrack.jetbrains.com/issue/KT-16092) Backend crash fixed: "Don't know how to generate outer expression" for destructuring suspend lambda
|
||||
- [`KT-16093`](https://youtrack.jetbrains.com/issue/KT-16093) Annotations are retained during reading the binary representation of suspend functions
|
||||
- [`KT-16122`](https://youtrack.jetbrains.com/issue/KT-16122) java.lang.VerifyError fixed in couroutines: (String, null, suspend () -> String)
|
||||
- [`KT-16124`](https://youtrack.jetbrains.com/issue/KT-16124) Marked as UNSUPPORTED: suspension points in default parameters
|
||||
- [`KT-16219`](https://youtrack.jetbrains.com/issue/KT-16219) Marked as UNSUPPORTED: suspend get/set, in/!in operators for
|
||||
- [`KT-16145`](https://youtrack.jetbrains.com/issue/KT-16145) Beta-2 coroutine regression fixed (wrong code generation)
|
||||
|
||||
#### Kapt3
|
||||
- [`KT-15524`](https://youtrack.jetbrains.com/issue/KT-15524) Fix javac error reporting in Kotlin daemon
|
||||
- [`KT-15721`](https://youtrack.jetbrains.com/issue/KT-15721) JetBrains nullability annotations are now returned from Element.getAnnotationMirrors()
|
||||
- [`KT-16146`](https://youtrack.jetbrains.com/issue/KT-16146) Fixed work in verbose mode
|
||||
- [`KT-16153`](https://youtrack.jetbrains.com/issue/KT-16153) Ignore declarations with illegal Java identifiers
|
||||
- [`KT-16167`](https://youtrack.jetbrains.com/issue/KT-16167) Fixed compilation error with kapt arguments in build.gradle
|
||||
- [`KT-16170`](https://youtrack.jetbrains.com/issue/KT-16170) Stub generator now adds imports for corrected error types to stubs
|
||||
- [`KT-16176`](https://youtrack.jetbrains.com/issue/KT-16176) javac's finalCompiler log is now used to determine annotation processing errors
|
||||
|
||||
#### Backward compatibility
|
||||
- [`KT-16017`](https://youtrack.jetbrains.com/issue/KT-16017) More graceful error message for disabled features
|
||||
- [`KT-16073`](https://youtrack.jetbrains.com/issue/KT-16073) Improved backward compatibility mode with version 1.0 on JDK dependent built-ins
|
||||
- [`KT-16094`](https://youtrack.jetbrains.com/issue/KT-16094) Compiler considers API availability when compiling language features requiring runtime support
|
||||
- [`KT-16171`](https://youtrack.jetbrains.com/issue/KT-16171) Fixed regression "Unexpected container error on Kotlin 1.0 project"
|
||||
- [`KT-16199`](https://youtrack.jetbrains.com/issue/KT-16199) Do not import "kotlin.comparisons.*" by default in language version 1.0 mode
|
||||
|
||||
#### Various issues
|
||||
- [`KT-16225`](https://youtrack.jetbrains.com/issue/KT-16225) enumValues non-reified stub implementation references nonexistent method no more
|
||||
- [`KT-16291`](https://youtrack.jetbrains.com/issue/KT-16291) Smart cast works now when getting class of instance
|
||||
- [`KT-16380`](https://youtrack.jetbrains.com/issue/KT-16380) Show warning when running the compiler under Java 6 or 7
|
||||
|
||||
### JavaScript backend
|
||||
- [`KT-16144`](https://youtrack.jetbrains.com/issue/KT-16144) Fixed inlining of functions called through inheritor ("fake" override) from another module
|
||||
- [`KT-16158`](https://youtrack.jetbrains.com/issue/KT-16158) Error is not reported now when library path contains JAR file without JS metadata, report warning instead
|
||||
- [`KT-16160`](https://youtrack.jetbrains.com/issue/KT-16160) Companion object dispatch receiver translation fixed
|
||||
|
||||
### Standard library
|
||||
- [`KT-7858`](https://youtrack.jetbrains.com/issue/KT-7858) Add extension function `takeUnless`
|
||||
- `javaClass` extension property is deprecated, use `instance::class.java` instead
|
||||
- Massive deprecations are coming in JS standard library in `kotlin.dom` and `kotlin.dom.build` packages
|
||||
|
||||
### IDE
|
||||
|
||||
#### Configuration issues
|
||||
- [`KT-15899`](https://youtrack.jetbrains.com/issue/KT-15899) Kotlin facet: language and api version for submodule setup for 1.0 are filled now as 1.0 too
|
||||
- [`KT-15914`](https://youtrack.jetbrains.com/issue/KT-15914) Kotlin facet works now with multi-selected modules in Project Settings too
|
||||
- [`KT-15954`](https://youtrack.jetbrains.com/issue/KT-15954) Does not suggest to configure kotlin for the module after each new kt-file creation
|
||||
- [`KT-16157`](https://youtrack.jetbrains.com/issue/KT-16157) freeCompilerArgs are now imported from Gradle into IDEA
|
||||
- [`KT-16206`](https://youtrack.jetbrains.com/issue/KT-16206) Idea no more refuses to compile a kotlin project defined as a maven project
|
||||
- [`KT-16312`](https://youtrack.jetbrains.com/issue/KT-16312) Kotlin facet: import from gradle: don't import options which are set implicitly already
|
||||
- [`KT-16325`](https://youtrack.jetbrains.com/issue/KT-16325) Kotlin facet: correct configuration after upgrading the IDE plugin
|
||||
- [`KT-16345`](https://youtrack.jetbrains.com/issue/KT-16345) Kotlin facet: detect JavaScript if the module has language 1.0 `kotlin-js-library` dependency
|
||||
|
||||
#### Coroutine support
|
||||
- [`KT-16109`](https://youtrack.jetbrains.com/issue/KT-16109) Error fixed: The -Xcoroutines can only have one value
|
||||
- [`KT-16251`](https://youtrack.jetbrains.com/issue/KT-16251) Fix detection of suspend calls containing extracted parameters
|
||||
|
||||
#### Intention actions, inspections and quick-fixes
|
||||
|
||||
##### 2017.1 compatibility
|
||||
- [`KT-15870`](https://youtrack.jetbrains.com/issue/KT-15870) "Package name does not match containing directory" inspection: fixed throwable "AWT events are not allowed inside write action"
|
||||
- [`KT-15924`](https://youtrack.jetbrains.com/issue/KT-15924) Create Test action: fixed throwable "AWT events are not allowed inside write action"
|
||||
|
||||
##### Bug fixes
|
||||
- [`KT-14831`](https://youtrack.jetbrains.com/issue/KT-14831) Import statement and FQN are not added on converting lambda to reference for typealias
|
||||
- [`KT-15545`](https://youtrack.jetbrains.com/issue/KT-15545) Inspection "join with assignment" does not change now execution order for properties
|
||||
- [`KT-15744`](https://youtrack.jetbrains.com/issue/KT-15744) Fix: intention to import `sleep` wrongly suggests `Thread.sleep`
|
||||
- [`KT-16000`](https://youtrack.jetbrains.com/issue/KT-16000) Inspection "join with assignment" handles initialization with 'this' correctly
|
||||
- [`KT-16009`](https://youtrack.jetbrains.com/issue/KT-16009) Auto-import for JDK classes in .kts files
|
||||
- [`KT-16104`](https://youtrack.jetbrains.com/issue/KT-16104) Don't insert modifiers (e.g. suspend) before visibility
|
||||
|
||||
#### Completion
|
||||
- [`KT-16076`](https://youtrack.jetbrains.com/issue/KT-16076) Completion does not insert more FQN kotlin.text.String
|
||||
- [`KT-16088`](https://youtrack.jetbrains.com/issue/KT-16088) Completion does not insert more FQN for `kotlin` package
|
||||
- [`KT-16110`](https://youtrack.jetbrains.com/issue/KT-16110) Keyword 'suspend' completion inside generic arguments
|
||||
- [`KT-16243`](https://youtrack.jetbrains.com/issue/KT-16243) Performance enhanced after variable of type `ArrayList`
|
||||
|
||||
#### Various issues
|
||||
- [`KT-15291`](https://youtrack.jetbrains.com/issue/KT-15291) 'Find usages' now does not report property access as usage of getter method in Java class with parameter
|
||||
- [`KT-15647`](https://youtrack.jetbrains.com/issue/KT-15647) Exception fixed: KDoc link to member of class from different package and module
|
||||
- [`KT-16071`](https://youtrack.jetbrains.com/issue/KT-16071) IDEA deadlock fixed: when typing "parse()" in .kt file
|
||||
- [`KT-16149`](https://youtrack.jetbrains.com/issue/KT-16149) Intellij Idea 2017.1/Android Studio 2.3 beta3 and Kotlin plugin 1.1-beta2 deadlock fixed
|
||||
|
||||
### Coroutine libraries
|
||||
- [`KT-15716`](https://youtrack.jetbrains.com/issue/KT-15716) Introduced startCoroutineUninterceptedOrReturn coroutine intrinsic
|
||||
- [`KT-15718`](https://youtrack.jetbrains.com/issue/KT-15718) createCoroutine now returns safe continuation
|
||||
- [`KT-16155`](https://youtrack.jetbrains.com/issue/KT-16155) Introduced createCoroutineUnchecked intrinsic
|
||||
|
||||
|
||||
### Gradle support
|
||||
- [`KT-15829`](https://youtrack.jetbrains.com/issue/KT-15829) Gradle Kotlin JS plugin: removed false "Duplicate source root:" warning for kotlin files
|
||||
- [`KT-15902`](https://youtrack.jetbrains.com/issue/KT-15902) JS: gradle task output is now considered as source set output
|
||||
- [`KT-16174`](https://youtrack.jetbrains.com/issue/KT-16174) Error fixed during IDEA-Gradle synchronization for Kotlin JS
|
||||
- [`KT-16267`](https://youtrack.jetbrains.com/issue/KT-16267) JS: fixed regression in 1.1-beta2 for multi-module gradle project
|
||||
- [`KT-16274`](https://youtrack.jetbrains.com/issue/KT-16274) Kotlin JS Gradle unexpected compiler error / absolute path to output file
|
||||
- [`KT-16322`](https://youtrack.jetbrains.com/issue/KT-16322) Circlet project Gradle import issue fixed
|
||||
|
||||
### REPL
|
||||
- [`KT-15861`](https://youtrack.jetbrains.com/issue/KT-15861) Use windows line separator in kotlin's JSR implementation
|
||||
- [`KT-16126`](https://youtrack.jetbrains.com/issue/KT-16126) Proper `jvmTarget` for REPL compilation
|
||||
|
||||
|
||||
## 1.1-Beta2
|
||||
|
||||
### Language related changes
|
||||
|
||||
@@ -21,7 +21,10 @@ import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.build.deserializeFromPlainText
|
||||
import org.jetbrains.kotlin.build.serializeToPlainText
|
||||
|
||||
/**
|
||||
* If you want to add a new field, check its type is supported by [serializeToPlainText], [deserializeFromPlainText]
|
||||
@@ -59,7 +62,7 @@ data class JvmBuildMetaInfo(
|
||||
}
|
||||
|
||||
fun JvmBuildMetaInfo(args: CommonCompilerArguments): JvmBuildMetaInfo =
|
||||
JvmBuildMetaInfo(isEAP = KotlinCompilerVersion.isPreRelease(),
|
||||
JvmBuildMetaInfo(isEAP = DeserializedDescriptorResolver.IS_PRE_RELEASE,
|
||||
compilerBuildVersion = KotlinCompilerVersion.VERSION,
|
||||
languageVersionString = args.languageVersion ?: LanguageVersion.LATEST.versionString,
|
||||
apiVersionString = args.apiVersion ?: ApiVersion.LATEST.versionString,
|
||||
@@ -72,4 +75,4 @@ fun JvmBuildMetaInfo(args: CommonCompilerArguments): JvmBuildMetaInfo =
|
||||
metadataVersionPatch = JvmMetadataVersion.INSTANCE.patch,
|
||||
bytecodeVersionMajor = JvmBytecodeBinaryVersion.INSTANCE.major,
|
||||
bytecodeVersionMinor = JvmBytecodeBinaryVersion.INSTANCE.minor,
|
||||
bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch)
|
||||
bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch)
|
||||
42
build.xml
42
build.xml
@@ -617,6 +617,13 @@
|
||||
</target>
|
||||
|
||||
<target name="compiler">
|
||||
<taskdef resource="proguard/ant/task.properties">
|
||||
<classpath>
|
||||
<pathelement path="${dependencies}/proguard.jar"/>
|
||||
<pathelement path="${dependencies}/proguard-anttask.jar"/>
|
||||
</classpath>
|
||||
</taskdef>
|
||||
|
||||
<cleandir dir="${output}/classes/compiler"/>
|
||||
|
||||
<javac2 destdir="${output}/classes/compiler" debug="true" debuglevel="lines,vars,source" includeAntRuntime="false"
|
||||
@@ -638,32 +645,17 @@
|
||||
unless:true="${shrink}" />
|
||||
|
||||
<sequential if:true="${shrink}">
|
||||
<shrink configuration="${basedir}/compiler/compiler.pro"/>
|
||||
</sequential>
|
||||
|
||||
<pack-compiler-for-maven/>
|
||||
</target>
|
||||
|
||||
<macrodef name="shrink">
|
||||
<attribute name="configuration"/>
|
||||
|
||||
<sequential>
|
||||
<taskdef resource="proguard/ant/task.properties">
|
||||
<classpath>
|
||||
<pathelement path="${dependencies}/proguard.jar"/>
|
||||
<pathelement path="${dependencies}/proguard-anttask.jar"/>
|
||||
</classpath>
|
||||
</taskdef>
|
||||
|
||||
<available property="rtjar" value="${java.home}/lib/rt.jar" file="${java.home}/lib/rt.jar"/>
|
||||
<available property="rtjar" value="${java.home}/../Classes/classes.jar" file="${java.home}/../Classes/classes.jar"/>
|
||||
|
||||
<available property="jssejar" value="${java.home}/lib/jsse.jar" file="${java.home}/lib/jsse.jar"/>
|
||||
<available property="jssejar" value="${java.home}/../Classes/jsse.jar" file="${java.home}/../Classes/jsse.jar"/>
|
||||
|
||||
<proguard configuration="@{configuration}"/>
|
||||
<proguard configuration="${basedir}/compiler/compiler.pro"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<pack-compiler-for-maven/>
|
||||
</target>
|
||||
|
||||
<macrodef name="pack-compiler-for-maven">
|
||||
<sequential>
|
||||
@@ -1232,10 +1224,11 @@
|
||||
</manifest>
|
||||
</jar>
|
||||
|
||||
<delete file="${output}/kotlin-reflect-jarjar.jar" failonerror="false"/>
|
||||
|
||||
<sequential if:true="${obfuscate.reflect}">
|
||||
<taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpath="dependencies/jarjar.jar"/>
|
||||
<delete file="${output}/kotlin-reflect-after-jarjar.jar" failonerror="false"/>
|
||||
<jarjar jarfile="${output}/kotlin-reflect-after-jarjar.jar" filesonly="true" filesetmanifest="merge">
|
||||
<jarjar jarfile="${output}/kotlin-reflect-jarjar.jar" filesonly="true" filesetmanifest="merge">
|
||||
<zipfileset src="${output}/kotlin-reflect-before-jarjar.jar"/>
|
||||
<rule pattern="org.jetbrains.kotlin.**" result="kotlin.reflect.jvm.internal.impl.@1"/>
|
||||
<rule pattern="javax.inject.**" result="kotlin.reflect.jvm.internal.impl.javax.inject.@1"/>
|
||||
@@ -1246,18 +1239,17 @@
|
||||
<compilerarg value="-script"/>
|
||||
<compilerarg value="kotlin/Metadata"/> <!-- Annotation to strip -->
|
||||
<compilerarg value="kotlin/reflect/jvm/internal/impl/.*"/> <!-- Classes to strip from -->
|
||||
<compilerarg value="${output}/kotlin-reflect-after-jarjar.jar"/>
|
||||
<compilerarg value="${output}/kotlin-reflect-before-proguard.jar"/>
|
||||
<compilerarg value="${output}/kotlin-reflect-jarjar.jar"/>
|
||||
<compilerarg value="${kotlin-home}/lib/kotlin-reflect.jar"/>
|
||||
<classpath>
|
||||
<pathelement location="${idea.sdk}/lib/asm-all.jar"/>
|
||||
</classpath>
|
||||
</kotlinc>
|
||||
|
||||
<shrink configuration="${basedir}/core/reflection.jvm/reflection.pro"/>
|
||||
</sequential>
|
||||
|
||||
<sequential unless:true="${obfuscate.reflect}">
|
||||
<echo message="Obfuscation of kotlin-reflect is disabled"/>
|
||||
<copy file="${output}/kotlin-reflect-before-jarjar.jar" tofile="${output}/kotlin-reflect-jarjar.jar" overwrite="true"/>
|
||||
<copy file="${output}/kotlin-reflect-before-jarjar.jar" tofile="${kotlin-home}/lib/kotlin-reflect.jar" overwrite="true"/>
|
||||
</sequential>
|
||||
</target>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
@@ -32,12 +31,7 @@ abstract class ArgumentGenerator {
|
||||
*
|
||||
* @see kotlin.reflect.jvm.internal.KCallableImpl.callBy
|
||||
*/
|
||||
open fun generate(
|
||||
valueArgumentsByIndex: List<ResolvedValueArgument>,
|
||||
actualArgs: List<ResolvedValueArgument>,
|
||||
// may be null for a constructor of an object literal
|
||||
calleeDescriptor: CallableDescriptor?
|
||||
): DefaultCallArgs {
|
||||
open fun generate(valueArgumentsByIndex: List<ResolvedValueArgument>, actualArgs: List<ResolvedValueArgument>): DefaultCallArgs {
|
||||
assert(valueArgumentsByIndex.size == actualArgs.size) {
|
||||
"Value arguments collection should have same size, but ${valueArgumentsByIndex.size} != ${actualArgs.size}"
|
||||
}
|
||||
@@ -54,8 +48,7 @@ abstract class ArgumentGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
// Use unwrapped version, because additional synthetic parameters can't have default values
|
||||
val defaultArgs = DefaultCallArgs(calleeDescriptor?.unwrapFrontendVersion()?.valueParameters?.size ?: 0)
|
||||
val defaultArgs = DefaultCallArgs(valueArgumentsByIndex.size)
|
||||
|
||||
for (argumentWithDeclIndex in actualArgsWithDeclIndex) {
|
||||
val argument = argumentWithDeclIndex.arg
|
||||
|
||||
@@ -770,7 +770,6 @@ public class AsmUtil {
|
||||
if (left == Type.DOUBLE_TYPE || right == Type.DOUBLE_TYPE) return Type.DOUBLE_TYPE;
|
||||
if (left == Type.FLOAT_TYPE || right == Type.FLOAT_TYPE) return Type.FLOAT_TYPE;
|
||||
if (left == Type.LONG_TYPE || right == Type.LONG_TYPE) return Type.LONG_TYPE;
|
||||
if (left == Type.CHAR_TYPE || right == Type.CHAR_TYPE) return Type.CHAR_TYPE;
|
||||
return Type.INT_TYPE;
|
||||
}
|
||||
|
||||
|
||||
@@ -210,12 +210,7 @@ class NumberCompare(
|
||||
) : BranchedValue(left, right, operandType, NumberCompare.getNumberCompareOpcode(opToken)) {
|
||||
|
||||
override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int {
|
||||
// Opcode takes one int operand from the stack
|
||||
assert(opcode in IFEQ..IFLE) {
|
||||
"Opcode for comparing must be in range ${IFEQ..IFLE}, but $opcode was found"
|
||||
}
|
||||
|
||||
return when (operandType) {
|
||||
when (operandType) {
|
||||
Type.FLOAT_TYPE, Type.DOUBLE_TYPE -> {
|
||||
if (opToken == KtTokens.GT || opToken == KtTokens.GTEQ) {
|
||||
v.cmpl(operandType)
|
||||
@@ -223,19 +218,17 @@ class NumberCompare(
|
||||
else {
|
||||
v.cmpg(operandType)
|
||||
}
|
||||
|
||||
opcode
|
||||
}
|
||||
Type.LONG_TYPE -> {
|
||||
v.lcmp()
|
||||
|
||||
opcode
|
||||
}
|
||||
else -> {
|
||||
opcode + (IF_ICMPEQ - IFEQ)
|
||||
return opcode + (IF_ICMPEQ - IFEQ)
|
||||
}
|
||||
}
|
||||
return opcode
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getNumberCompareOpcode(opToken: IElementType): Int {
|
||||
return when (opToken) {
|
||||
@@ -269,4 +262,4 @@ class ObjectCompare(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,10 @@
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.ValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||
import org.jetbrains.kotlin.types.FlexibleTypesKt;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.List;
|
||||
@@ -56,12 +53,11 @@ public class CallBasedArgumentGenerator extends ArgumentGenerator {
|
||||
@Override
|
||||
public DefaultCallArgs generate(
|
||||
@NotNull List<? extends ResolvedValueArgument> valueArgumentsByIndex,
|
||||
@NotNull List<? extends ResolvedValueArgument> valueArgs,
|
||||
@Nullable CallableDescriptor calleeDescriptor
|
||||
@NotNull List<? extends ResolvedValueArgument> valueArgs
|
||||
) {
|
||||
boolean shouldMarkLineNumbers = this.codegen.isShouldMarkLineNumbers();
|
||||
this.codegen.setShouldMarkLineNumbers(false);
|
||||
DefaultCallArgs defaultArgs = super.generate(valueArgumentsByIndex, valueArgs, calleeDescriptor);
|
||||
DefaultCallArgs defaultArgs = super.generate(valueArgumentsByIndex, valueArgs);
|
||||
this.codegen.setShouldMarkLineNumbers(shouldMarkLineNumbers);
|
||||
return defaultArgs;
|
||||
}
|
||||
@@ -88,9 +84,7 @@ public class CallBasedArgumentGenerator extends ArgumentGenerator {
|
||||
protected void generateVararg(int i, @NotNull VarargValueArgument argument) {
|
||||
ValueParameterDescriptor parameter = valueParameters.get(i);
|
||||
Type type = valueParameterTypes.get(i);
|
||||
// Upper bound for type of vararg parameter should always have a form of 'Array<out T>',
|
||||
// while its lower bound may be Nothing-typed after approximation
|
||||
codegen.genVarargs(argument, FlexibleTypesKt.upperIfFlexible(parameter.getType()));
|
||||
codegen.genVarargs(argument, parameter.getType());
|
||||
callGenerator.afterParameterPut(type, null, i);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -128,13 +129,11 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
) {
|
||||
val typeMapper = state.typeMapper
|
||||
val isStatic = AsmUtil.isStaticMethod(contextKind, functionDescriptor)
|
||||
val baseMethodFlags = AsmUtil.getCommonCallableFlags(functionDescriptor) and Opcodes.ACC_VARARGS.inv()
|
||||
val remainingParameters = getRemainingParameters(functionDescriptor.original, substituteCount)
|
||||
val flags =
|
||||
baseMethodFlags or
|
||||
val flags = AsmUtil.getCommonCallableFlags(functionDescriptor) or
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (functionDescriptor.modality == Modality.FINAL && functionDescriptor !is ConstructorDescriptor) Opcodes.ACC_FINAL else 0) or
|
||||
(if (remainingParameters.lastOrNull()?.varargElementType != null) Opcodes.ACC_VARARGS else 0)
|
||||
(if (functionDescriptor.modality == Modality.FINAL && functionDescriptor !is ConstructorDescriptor) Opcodes.ACC_FINAL else 0)
|
||||
|
||||
val remainingParameters = getRemainingParameters(functionDescriptor.original, substituteCount)
|
||||
val signature = typeMapper.mapSignature(functionDescriptor, contextKind, remainingParameters, false)
|
||||
val mv = classBuilder.newMethod(OtherOrigin(methodElement, functionDescriptor), flags,
|
||||
signature.asmMethod.name,
|
||||
|
||||
@@ -1709,7 +1709,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
ArgumentGenerator argumentGenerator =
|
||||
new CallBasedArgumentGenerator(ExpressionCodegen.this, defaultCallGenerator, valueParameters, mappedTypes);
|
||||
|
||||
argumentGenerator.generate(valueArguments, valueArguments, null);
|
||||
argumentGenerator.generate(valueArguments, valueArguments);
|
||||
}
|
||||
|
||||
Collection<ClassConstructorDescriptor> constructors = classDescriptor.getConstructors();
|
||||
@@ -2867,11 +2867,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
assert valueArguments != null : "Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor();
|
||||
|
||||
DefaultCallArgs defaultArgs =
|
||||
argumentGenerator.generate(
|
||||
valueArguments,
|
||||
new ArrayList<ResolvedValueArgument>(resolvedCall.getValueArguments().values()),
|
||||
resolvedCall.getResultingDescriptor()
|
||||
);
|
||||
argumentGenerator.generate(valueArguments, new ArrayList<ResolvedValueArgument>(resolvedCall.getValueArguments().values()));
|
||||
|
||||
if (tailRecursionCodegen.isTailRecursion(resolvedCall)) {
|
||||
tailRecursionCodegen.generateTailRecursion(resolvedCall);
|
||||
@@ -3196,7 +3192,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
boolean isSingleton = calleeContainingClass.getKind().isSingleton();
|
||||
if (isSingleton) {
|
||||
if (calleeContainingClass.equals(context.getThisDescriptor()) &&
|
||||
!CodegenUtilKt.isJvmStaticInObjectOrClass(context.getFunctionDescriptor())) {
|
||||
!CodegenUtilKt.isJvmStaticInObjectOrClass(context.getContextDescriptor())) {
|
||||
return StackValue.local(0, typeMapper.mapType(calleeContainingClass));
|
||||
}
|
||||
else if (isEnumEntry(calleeContainingClass)) {
|
||||
@@ -4911,8 +4907,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
public NameGenerator getInlineNameGenerator() {
|
||||
NameGenerator nameGenerator = getParentCodegen().getInlineNameGenerator();
|
||||
Name name = context.getContextDescriptor().getName();
|
||||
String inlinedName = name.isSpecial() ? InlineCodegenUtil.SPECIAL_TRANSFORMATION_NAME : name.asString();
|
||||
return nameGenerator.subGenerator(inlinedName + InlineCodegenUtil.INLINE_CALL_TRANSFORMATION_SUFFIX);
|
||||
return nameGenerator.subGenerator((name.isSpecial() ? "$special" : name.asString()) + "$$inlined" );
|
||||
}
|
||||
|
||||
public Type getReturnType() {
|
||||
|
||||
@@ -231,7 +231,7 @@ public class FunctionCodegen {
|
||||
boolean staticInCompanionObject = CodegenUtilKt.isJvmStaticInCompanionObject(functionDescriptor);
|
||||
if (staticInCompanionObject) {
|
||||
ImplementationBodyCodegen parentBodyCodegen = (ImplementationBodyCodegen) memberCodegen.getParentCodegen();
|
||||
parentBodyCodegen.addAdditionalTask(new JvmStaticInCompanionObjectGenerator(functionDescriptor, origin, state, parentBodyCodegen));
|
||||
parentBodyCodegen.addAdditionalTask(new JvmStaticGenerator(functionDescriptor, origin, state, parentBodyCodegen));
|
||||
}
|
||||
|
||||
if (!state.getClassBuilderMode().generateBodies || isAbstractMethod(functionDescriptor, contextKind, state)) {
|
||||
@@ -256,8 +256,7 @@ public class FunctionCodegen {
|
||||
else if (staticInCompanionObject) {
|
||||
// native @JvmStatic foo() in companion object should delegate to the static native function moved to the outer class
|
||||
mv.visitCode();
|
||||
FunctionDescriptor staticFunctionDescriptor = JvmStaticInCompanionObjectGenerator
|
||||
.createStaticFunctionDescriptor(functionDescriptor);
|
||||
FunctionDescriptor staticFunctionDescriptor = JvmStaticGenerator.createStaticFunctionDescriptor(functionDescriptor);
|
||||
Method accessorMethod =
|
||||
typeMapper.mapAsmMethod(memberCodegen.getContext().accessibleDescriptor(staticFunctionDescriptor, null));
|
||||
Type owningType = typeMapper.mapClass((ClassifierDescriptor) staticFunctionDescriptor.getContainingDeclaration());
|
||||
|
||||
@@ -37,8 +37,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject;
|
||||
|
||||
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased {
|
||||
private final ResolvedCall<?> resolvedCall;
|
||||
private final FunctionDescriptor referencedFunction;
|
||||
@@ -148,12 +146,6 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
|
||||
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) -
|
||||
(receiverType != null ? 1 : 0);
|
||||
|
||||
if (receivers < 0 && referencedFunction instanceof ConstructorDescriptor && isObject(referencedFunction.getContainingDeclaration().getContainingDeclaration())) {
|
||||
//reference to object nested class
|
||||
//TODO: seems problem should be fixed on frontend side (note that object instance are captured by generated class)
|
||||
receivers = 0;
|
||||
}
|
||||
|
||||
List<ValueParameterDescriptor> parameters = CollectionsKt.drop(functionDescriptor.getValueParameters(), receivers);
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
ValueParameterDescriptor parameter = parameters.get(i);
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
class JvmStaticInCompanionObjectGenerator(
|
||||
class JvmStaticGenerator(
|
||||
val descriptor: FunctionDescriptor,
|
||||
val declarationOrigin: JvmDeclarationOrigin,
|
||||
val state: GenerationState,
|
||||
@@ -499,11 +499,10 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
int indexOfDelegatedProperty = PropertyCodegen.indexOfDelegatedProperty(property);
|
||||
|
||||
StackValue delegateValue = PropertyCodegen.invokeDelegatedPropertyConventionMethodWithReceiver(
|
||||
codegen, typeMapper, provideDelegateResolvedCall, indexOfDelegatedProperty, 1,
|
||||
provideDelegateReceiver, propertyDescriptor
|
||||
);
|
||||
codegen, typeMapper, provideDelegateResolvedCall, indexOfDelegatedProperty, 1, provideDelegateReceiver);
|
||||
|
||||
propValue.store(delegateValue, codegen.v);
|
||||
|
||||
}
|
||||
|
||||
protected boolean shouldInitializeProperty(@NotNull KtProperty property) {
|
||||
@@ -591,7 +590,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
}
|
||||
if (delegatedProperties.isEmpty()) return;
|
||||
|
||||
v.newField(NO_ORIGIN, ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME,
|
||||
v.newField(NO_ORIGIN, ACC_PRIVATE | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC, JvmAbi.DELEGATED_PROPERTIES_ARRAY_NAME,
|
||||
"[" + K_PROPERTY_TYPE, null, null);
|
||||
|
||||
if (!state.getClassBuilderMode().generateBodies) return;
|
||||
|
||||
@@ -57,7 +57,6 @@ import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getDeprecatedAccessFlag;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getVisibilityForBackingField;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPropertyWithBackingFieldCopyInOuterClass;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConstOrHasJvmFieldAnnotation;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
|
||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.FIELD_FOR_PROPERTY;
|
||||
@@ -535,7 +534,7 @@ public class PropertyCodegen {
|
||||
StackValue.Property receiver = codegen.intermediateValueForProperty(propertyDescriptor, true, null, StackValue.LOCAL_0);
|
||||
return invokeDelegatedPropertyConventionMethodWithReceiver(
|
||||
codegen, typeMapper, resolvedCall, indexInPropertyMetadataArray, propertyMetadataArgumentIndex,
|
||||
receiver, propertyDescriptor
|
||||
receiver
|
||||
);
|
||||
}
|
||||
|
||||
@@ -545,12 +544,9 @@ public class PropertyCodegen {
|
||||
@NotNull ResolvedCall<FunctionDescriptor> resolvedCall,
|
||||
final int indexInPropertyMetadataArray,
|
||||
int propertyMetadataArgumentIndex,
|
||||
@Nullable StackValue receiver,
|
||||
@NotNull PropertyDescriptor propertyDescriptor
|
||||
@Nullable StackValue receiver
|
||||
) {
|
||||
final Type owner = JvmAbi.isPropertyWithBackingFieldInOuterClass(propertyDescriptor) ?
|
||||
codegen.getState().getTypeMapper().mapOwner(propertyDescriptor) :
|
||||
getDelegatedPropertyMetadataOwner(codegen, typeMapper);
|
||||
final Type owner = getDelegatedPropertyMetadataOwner(codegen, typeMapper);
|
||||
|
||||
codegen.tempVariables.put(
|
||||
resolvedCall.getCall().getValueArguments().get(propertyMetadataArgumentIndex).asElement(),
|
||||
|
||||
@@ -883,7 +883,7 @@ public abstract class StackValue {
|
||||
newReceiver.put(newReceiver.type, v);
|
||||
callGenerator.processAndPutHiddenParameters(false);
|
||||
|
||||
defaultArgs = generator.generate(valueArguments, valueArguments, call.getResultingDescriptor());
|
||||
defaultArgs = generator.generate(valueArguments, valueArguments);
|
||||
}
|
||||
|
||||
private ArgumentGenerator createArgumentGenerator() {
|
||||
|
||||
@@ -40,7 +40,7 @@ private fun Iterable<PackageParts>.addCompiledParts(state: GenerationState): Lis
|
||||
val incrementalCache = state.incrementalCacheForThisTarget ?: return this.toList()
|
||||
val moduleMappingData = incrementalCache.getModuleMappingData() ?: return this.toList()
|
||||
|
||||
val mapping = ModuleMapping.create(moduleMappingData, "<incremental>", state.deserializationConfiguration)
|
||||
val mapping = ModuleMapping.create(moduleMappingData, "<incremental>")
|
||||
|
||||
incrementalCache.getObsoletePackageParts().forEach { internalName ->
|
||||
val qualifier = internalName.substringBeforeLast('/', "").replace('/', '.')
|
||||
|
||||
@@ -72,7 +72,7 @@ class CoroutineCodegen private constructor(
|
||||
// protected fun doResume(result, throwable)
|
||||
private val doResumeDescriptor =
|
||||
SimpleFunctionDescriptorImpl.create(
|
||||
classDescriptor, Annotations.EMPTY, Name.identifier(DO_RESUME_METHOD_NAME), CallableMemberDescriptor.Kind.DECLARATION,
|
||||
classDescriptor, Annotations.EMPTY, Name.identifier("doResume"), CallableMemberDescriptor.Kind.DECLARATION,
|
||||
funDescriptor.source
|
||||
).apply doResume@{
|
||||
initialize(
|
||||
@@ -328,11 +328,6 @@ class CoroutineCodegen private constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun shouldCreateByLambda(
|
||||
originalSuspendLambdaDescriptor: CallableDescriptor,
|
||||
declaration: KtElement): Boolean {
|
||||
return (declaration is KtFunctionLiteral && originalSuspendLambdaDescriptor.isSuspendLambda)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createByLambda(
|
||||
@@ -341,7 +336,8 @@ class CoroutineCodegen private constructor(
|
||||
declaration: KtElement,
|
||||
classBuilder: ClassBuilder
|
||||
): ClosureCodegen? {
|
||||
if (!shouldCreateByLambda(originalSuspendLambdaDescriptor, declaration)) return null
|
||||
if (declaration !is KtFunctionLiteral) return null
|
||||
if (!originalSuspendLambdaDescriptor.isSuspendLambda) return null
|
||||
|
||||
return CoroutineCodegen(
|
||||
expressionCodegen,
|
||||
|
||||
@@ -141,12 +141,12 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
||||
List<CapturedParamInfo> additionalFakeParams =
|
||||
extractParametersMappingAndPatchConstructor(constructor, allCapturedParamBuilder, constructorParamBuilder,
|
||||
transformationInfo, parentRemapper);
|
||||
List<DeferredMethodVisitor> deferringMethods = new ArrayList<DeferredMethodVisitor>();
|
||||
List<MethodVisitor> deferringMethods = new ArrayList<MethodVisitor>();
|
||||
|
||||
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams);
|
||||
|
||||
for (MethodNode next : methodsToTransform) {
|
||||
DeferredMethodVisitor deferringVisitor = newMethod(classBuilder, next);
|
||||
MethodVisitor deferringVisitor = newMethod(classBuilder, next);
|
||||
InlineResult funResult =
|
||||
inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false);
|
||||
|
||||
@@ -161,8 +161,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
||||
deferringMethods.add(deferringVisitor);
|
||||
}
|
||||
|
||||
for (DeferredMethodVisitor method : deferringMethods) {
|
||||
InlineCodegenUtil.removeFinallyMarkers(method.getIntermediate());
|
||||
for (MethodVisitor method : deferringMethods) {
|
||||
method.visitEnd();
|
||||
}
|
||||
|
||||
@@ -314,7 +313,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
||||
MethodNode intermediateMethodNode =
|
||||
new MethodNode(AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor, constructorInlineBuilder, true);
|
||||
InlineCodegenUtil.removeFinallyMarkers(intermediateMethodNode);
|
||||
|
||||
AbstractInsnNode first = intermediateMethodNode.instructions.getFirst();
|
||||
final Label oldStartLabel = first instanceof LabelNode ? ((LabelNode) first).getLabel() : null;
|
||||
|
||||
@@ -466,9 +466,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
adapter, infos, ((StackValue.Local) remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index
|
||||
);
|
||||
removeStaticInitializationTrigger(adapter);
|
||||
if (!InlineCodegenUtil.isFinallyMarkerRequired(codegen.getContext())) {
|
||||
InlineCodegenUtil.removeFinallyMarkers(adapter);
|
||||
}
|
||||
removeFinallyMarkers(adapter);
|
||||
|
||||
adapter.accept(new MethodBodyVisitor(codegen.v));
|
||||
|
||||
@@ -1026,6 +1024,25 @@ public class InlineCodegen extends CallGenerator {
|
||||
//processor.substituteLocalVarTable(intoNode);
|
||||
}
|
||||
|
||||
private void removeFinallyMarkers(@NotNull MethodNode intoNode) {
|
||||
if (InlineCodegenUtil.isFinallyMarkerRequired(codegen.getContext())) return;
|
||||
|
||||
InsnList instructions = intoNode.instructions;
|
||||
AbstractInsnNode curInstr = instructions.getFirst();
|
||||
while (curInstr != null) {
|
||||
if (InlineCodegenUtil.isFinallyMarker(curInstr)) {
|
||||
AbstractInsnNode marker = curInstr;
|
||||
//just to assert
|
||||
getConstant(marker.getPrevious());
|
||||
curInstr = curInstr.getNext();
|
||||
instructions.remove(marker.getPrevious());
|
||||
instructions.remove(marker);
|
||||
continue;
|
||||
}
|
||||
curInstr = curInstr.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap, @NotNull SourceMapper parent) {
|
||||
return new NestedSourceMapper(parent, nodeAndSmap.getSortedRanges(), nodeAndSmap.getClassSMAP().getSourceInfo());
|
||||
|
||||
@@ -81,9 +81,7 @@ public class InlineCodegenUtil {
|
||||
private static final String INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall";
|
||||
private static final String INLINE_MARKER_FINALLY_START = "finallyStart";
|
||||
private static final String INLINE_MARKER_FINALLY_END = "finallyEnd";
|
||||
public static final String SPECIAL_TRANSFORMATION_NAME = "$special";
|
||||
public static final String INLINE_TRANSFORMATION_SUFFIX = "$inlined";
|
||||
public static final String INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX;
|
||||
public static final String INLINE_FUN_THIS_0_SUFFIX = "$inline_fun";
|
||||
public static final String INLINE_FUN_VAR_SUFFIX = "$iv";
|
||||
|
||||
@@ -466,23 +464,6 @@ public class InlineCodegenUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeFinallyMarkers(@NotNull MethodNode intoNode) {
|
||||
InsnList instructions = intoNode.instructions;
|
||||
AbstractInsnNode curInstr = instructions.getFirst();
|
||||
while (curInstr != null) {
|
||||
if (isFinallyMarker(curInstr)) {
|
||||
AbstractInsnNode marker = curInstr;
|
||||
//just to assert
|
||||
getConstant(marker.getPrevious());
|
||||
curInstr = curInstr.getNext();
|
||||
instructions.remove(marker.getPrevious());
|
||||
instructions.remove(marker);
|
||||
continue;
|
||||
}
|
||||
curInstr = curInstr.getNext();
|
||||
}
|
||||
}
|
||||
|
||||
public static void addInlineMarker(@NotNull InstructionAdapter v, boolean isStartNotEnd) {
|
||||
v.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
|
||||
|
||||
@@ -463,7 +463,6 @@ public class MethodInliner {
|
||||
Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
|
||||
|
||||
int offset = 0;
|
||||
boolean capturesAnonymousObjectThatMustBeRegenerated = false;
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
SourceValue sourceValue = frame.getStack(firstParameterIndex + i);
|
||||
LambdaInfo lambdaInfo = MethodInlinerUtilKt.getLambdaIfExistsAndMarkInstructions(
|
||||
@@ -472,17 +471,12 @@ public class MethodInliner {
|
||||
if (lambdaInfo != null) {
|
||||
lambdaMapping.put(offset, lambdaInfo);
|
||||
}
|
||||
else if (i < argTypes.length && isAnonymousClassThatMustBeRegenerated(argTypes[i])) {
|
||||
capturesAnonymousObjectThatMustBeRegenerated = true;
|
||||
}
|
||||
|
||||
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
|
||||
}
|
||||
|
||||
transformations.add(
|
||||
buildConstructorInvocation(
|
||||
owner, desc, lambdaMapping, awaitClassReification, capturesAnonymousObjectThatMustBeRegenerated
|
||||
)
|
||||
buildConstructorInvocation(owner, desc, lambdaMapping, awaitClassReification)
|
||||
);
|
||||
awaitClassReification = false;
|
||||
}
|
||||
@@ -544,12 +538,6 @@ public class MethodInliner {
|
||||
return node;
|
||||
}
|
||||
|
||||
private boolean isAnonymousClassThatMustBeRegenerated(@Nullable Type type) {
|
||||
if (type == null || type.getSort() != Type.OBJECT) return false;
|
||||
AnonymousObjectTransformationInfo info = inliningContext.findAnonymousObjectTransformationInfo(type.getInternalName());
|
||||
return info != null && info.shouldRegenerate(true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Frame<SourceValue>[] analyzeMethodNodeBeforeInline(@NotNull MethodNode node) {
|
||||
try {
|
||||
@@ -596,8 +584,7 @@ public class MethodInliner {
|
||||
@NotNull String anonymousType,
|
||||
@NotNull String desc,
|
||||
@NotNull Map<Integer, LambdaInfo> lambdaMapping,
|
||||
boolean needReification,
|
||||
boolean capturesAnonymousObjectThatMustBeRegenerated
|
||||
boolean needReification
|
||||
) {
|
||||
boolean memoizeAnonymousObject = inliningContext.findAnonymousObjectTransformationInfo(anonymousType) == null;
|
||||
|
||||
@@ -607,8 +594,7 @@ public class MethodInliner {
|
||||
isAlreadyRegenerated(anonymousType),
|
||||
desc,
|
||||
false,
|
||||
inliningContext.nameGenerator,
|
||||
capturesAnonymousObjectThatMustBeRegenerated
|
||||
inliningContext.nameGenerator
|
||||
);
|
||||
|
||||
if (memoizeAnonymousObject) {
|
||||
|
||||
@@ -68,8 +68,7 @@ class AnonymousObjectTransformationInfo internal constructor(
|
||||
private val alreadyRegenerated: Boolean,
|
||||
val constructorDesc: String?,
|
||||
private val isStaticOrigin: Boolean,
|
||||
parentNameGenerator: NameGenerator,
|
||||
private val capturesAnonymousObjectThatMustBeRegenerated: Boolean = false
|
||||
parentNameGenerator: NameGenerator
|
||||
) : TransformationInfo {
|
||||
|
||||
override val nameGenerator by lazy {
|
||||
@@ -94,8 +93,7 @@ class AnonymousObjectTransformationInfo internal constructor(
|
||||
) : this(ownerInternalName, needReification, hashMapOf(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator)
|
||||
|
||||
override fun shouldRegenerate(sameModule: Boolean): Boolean =
|
||||
!alreadyRegenerated &&
|
||||
(!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification || capturesAnonymousObjectThatMustBeRegenerated)
|
||||
!alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
|
||||
|
||||
override fun canRemoveAfterTransformation(): Boolean {
|
||||
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
class CompareTo : IntrinsicMethod() {
|
||||
private fun genInvoke(type: Type?, v: InstructionAdapter) {
|
||||
when (type) {
|
||||
Type.INT_TYPE, Type.CHAR_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(II)I", false)
|
||||
Type.INT_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(II)I", false)
|
||||
Type.LONG_TYPE -> v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "compare", "(JJ)I", false)
|
||||
Type.FLOAT_TYPE -> v.invokestatic("java/lang/Float", "compare", "(FF)I", false)
|
||||
Type.DOUBLE_TYPE -> v.invokestatic("java/lang/Double", "compare", "(DD)I", false)
|
||||
|
||||
@@ -47,12 +47,12 @@ class Concat : IntrinsicMethod() {
|
||||
codegen.invokeAppend(element.right)
|
||||
}
|
||||
else {
|
||||
// Explicit plus call LHS?.plus(RHS) or LHS.plus(RHS)
|
||||
receiver.put(AsmTypes.JAVA_STRING_TYPE, v)
|
||||
// LHS?.plus(RHS)
|
||||
receiver.put(AsmTypes.OBJECT_TYPE, v)
|
||||
genStringBuilderConstructor(v)
|
||||
v.swap()
|
||||
genInvokeAppendMethod(v, returnType)
|
||||
codegen.invokeAppend(arguments[0])
|
||||
codegen.invokeAppend(arguments.get(0))
|
||||
}
|
||||
|
||||
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen.signature;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmDescriptorTypeWriter;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
||||
@@ -58,7 +57,7 @@ public class JvmSignatureWriter extends JvmDescriptorTypeWriter<Type> {
|
||||
return;
|
||||
case Type.ARRAY:
|
||||
writeArrayType();
|
||||
writeAsmType(AsmUtil.correctElementType(asmType));
|
||||
writeAsmType(asmType.getElementType());
|
||||
writeArrayEnd();
|
||||
return;
|
||||
default:
|
||||
|
||||
@@ -41,10 +41,12 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.BindingTraceFilter
|
||||
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import java.io.File
|
||||
|
||||
@@ -92,7 +94,6 @@ class GenerationState @JvmOverloads constructor(
|
||||
val incrementalCacheForThisTarget: IncrementalCache?
|
||||
val packagesWithObsoleteParts: Set<FqName>
|
||||
val obsoleteMultifileClasses: List<FqName>
|
||||
val deserializationConfiguration: DeserializationConfiguration = CompilerDeserializationConfiguration(configuration)
|
||||
|
||||
init {
|
||||
val icComponents = configuration.get(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS)
|
||||
@@ -166,7 +167,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
|
||||
val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA)
|
||||
|
||||
val languageVersionSettings = configuration.languageVersionSettings
|
||||
val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT)
|
||||
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
||||
|
||||
init {
|
||||
|
||||
@@ -1154,13 +1154,13 @@ public class KotlinTypeMapper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getDefaultDescriptor(
|
||||
@NotNull Method method,
|
||||
@Nullable String dispatchReceiverDescriptor,
|
||||
@NotNull CallableDescriptor callableDescriptor
|
||||
) {
|
||||
private static String getDefaultDescriptor(@NotNull Method method, @Nullable String dispatchReceiverDescriptor, boolean isExtension) {
|
||||
String descriptor = method.getDescriptor();
|
||||
int maskArgumentsCount = (callableDescriptor.getValueParameters().size() + Integer.SIZE - 1) / Integer.SIZE;
|
||||
int argumentsCount = Type.getArgumentTypes(descriptor).length;
|
||||
if (isExtension) {
|
||||
argumentsCount--;
|
||||
}
|
||||
int maskArgumentsCount = (argumentsCount + Integer.SIZE - 1) / Integer.SIZE;
|
||||
String additionalArgs = StringUtil.repeat(Type.INT_TYPE.getDescriptor(), maskArgumentsCount);
|
||||
additionalArgs += (isConstructor(method) ? DEFAULT_CONSTRUCTOR_MARKER : OBJECT_TYPE).getDescriptor();
|
||||
String result = descriptor.replace(")", additionalArgs + ")");
|
||||
@@ -1186,7 +1186,7 @@ public class KotlinTypeMapper {
|
||||
String descriptor = getDefaultDescriptor(
|
||||
jvmSignature,
|
||||
isStaticMethod(kind, functionDescriptor) || isConstructor ? null : ownerType.getDescriptor(),
|
||||
CodegenUtilKt.unwrapFrontendVersion(functionDescriptor)
|
||||
functionDescriptor.getExtensionReceiverParameter() != null
|
||||
);
|
||||
|
||||
return new Method(isConstructor ? "<init>" : jvmSignature.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, descriptor);
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.DeserializedDescriptorResolver
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
||||
@@ -37,7 +37,7 @@ fun writeKotlinMetadata(
|
||||
av.visit(JvmAnnotationNames.BYTECODE_VERSION_FIELD_NAME, JvmBytecodeBinaryVersion.INSTANCE.toArray())
|
||||
av.visit(JvmAnnotationNames.KIND_FIELD_NAME, kind.id)
|
||||
var flags = extraFlags
|
||||
if (KotlinCompilerVersion.isPreRelease() && state.languageVersionSettings.languageVersion == LanguageVersion.LATEST) {
|
||||
if (DeserializedDescriptorResolver.IS_PRE_RELEASE && state.languageVersionSettings.languageVersion == LanguageVersion.LATEST) {
|
||||
flags = flags or JvmAnnotationNames.METADATA_PRE_RELEASE_FLAG
|
||||
}
|
||||
if (flags != 0) {
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.jetbrains.kotlin.serialization.builtins
|
||||
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.builtins.JvmBuiltInClassDescriptorFactory
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.*
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
@@ -28,16 +26,12 @@ import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.addKotlinSourceRoots
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.serialization.MetadataSerializer
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import java.io.File
|
||||
|
||||
class BuiltInsSerializer(dependOnOldBuiltIns: Boolean) : MetadataSerializer(dependOnOldBuiltIns) {
|
||||
@@ -94,21 +88,11 @@ class BuiltInsSerializer(dependOnOldBuiltIns: Boolean) : MetadataSerializer(depe
|
||||
fqName ->
|
||||
val packageView = module.getPackage(fqName)
|
||||
PackageSerializer(
|
||||
packageView.memberScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) + createCloneable(module),
|
||||
packageView.memberScope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS),
|
||||
packageView.fragments.flatMap { fragment -> DescriptorUtils.getAllDescriptors(fragment.getMemberScope()) },
|
||||
packageView.fqName,
|
||||
File(destDir, BuiltInSerializerProtocol.getBuiltInsFilePath(packageView.fqName))
|
||||
).run()
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize metadata for kotlin.Cloneable manually for compatibility with kotlin-reflect 1.0 which expects this metadata to be there.
|
||||
// Since Kotlin 1.1, we always discard this class during deserialization (see ClassDeserializer.kt).
|
||||
private fun createCloneable(module: ModuleDescriptor): ClassDescriptor {
|
||||
val factory = JvmBuiltInClassDescriptorFactory(LockBasedStorageManager.NO_LOCKS, module) {
|
||||
EmptyPackageFragmentDescriptor(module, KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)
|
||||
}
|
||||
return factory.createClass(ClassId.topLevel(KotlinBuiltIns.FQ_NAMES.cloneable.toSafe()))
|
||||
?: error("Could not create kotlin.Cloneable in $module")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,9 +63,6 @@ public abstract class CommonCompilerArguments implements Serializable {
|
||||
@ValueDescription("<count>")
|
||||
public String repeat;
|
||||
|
||||
@Argument(value = "Xskip-metadata-version-check", description = "Load classes with bad metadata version anyway (incl. pre-release classes)")
|
||||
public boolean skipMetadataVersionCheck;
|
||||
|
||||
@Argument(value = "Xallow-kotlin-package", description = "Allow compiling code in package 'kotlin'")
|
||||
public boolean allowKotlinPackage;
|
||||
|
||||
|
||||
@@ -94,6 +94,9 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
|
||||
@Argument(value = "Xmultifile-parts-inherit", description = "Compile multifile classes as a hierarchy of parts and facade")
|
||||
public boolean inheritMultifileParts;
|
||||
|
||||
@Argument(value = "Xskip-metadata-version-check", description = "Load classes with bad metadata version anyway (incl. pre-release classes)")
|
||||
public boolean skipMetadataVersionCheck;
|
||||
|
||||
@Argument(value = "Xskip-runtime-version-check", description = "Allow Kotlin runtime libraries of incompatible versions in the classpath")
|
||||
public boolean skipRuntimeVersionCheck;
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ import java.util.EnumSet;
|
||||
public enum CompilerMessageSeverity {
|
||||
EXCEPTION,
|
||||
ERROR,
|
||||
// Unlike a normal warning, a strong warning is not discarded when there are compilation errors.
|
||||
// Use it for problems related to configuration, not the diagnostics
|
||||
STRONG_WARNING,
|
||||
WARNING,
|
||||
INFO,
|
||||
|
||||
@@ -1,88 +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.cli.common.repl
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class AggregatedReplStateHistory<T1, T2>(val history1: IReplStageHistory<T1>, val history2: IReplStageHistory<T2>, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
|
||||
: IReplStageHistory<Pair<T1, T2>>, AbstractList<ReplHistoryRecord<Pair<T1, T2>>>()
|
||||
{
|
||||
override val size: Int
|
||||
get() = minOf(history1.size, history2.size)
|
||||
|
||||
override fun push(id: ILineId, item: Pair<T1, T2>) {
|
||||
lock.write {
|
||||
assertSameSize()
|
||||
history1.push(id, item.first)
|
||||
history2.push(id, item.second)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(index: Int): ReplHistoryRecord<Pair<T1, T2>> = lock.read {
|
||||
assertSameSize()
|
||||
val r1 = history1[index]
|
||||
val r2 = history2[index]
|
||||
assertSameId(r1, r2)
|
||||
ReplHistoryRecord(r1.id, r1.item to r2.item)
|
||||
}
|
||||
|
||||
override fun pop(): ReplHistoryRecord<Pair<T1, T2>>? = lock.write {
|
||||
assertSameSize()
|
||||
val r1 = history1.pop()
|
||||
val r2 = history2.pop()
|
||||
if (r1 == null && r2 == null) return null
|
||||
if (r1 == null || r2 == null) throw IllegalStateException("Aggregated history mismatch: $r1 vs $r2")
|
||||
assertSameId(r1, r2)
|
||||
ReplHistoryRecord(r1.id, r1.item to r2.item)
|
||||
}
|
||||
|
||||
override fun resetTo(id: ILineId): Iterable<ILineId> = lock.write {
|
||||
assertSameSize()
|
||||
val i1 = history1.resetTo(id).toList()
|
||||
val i2 = history2.resetTo(id).toList()
|
||||
if (i1 != i2) throw IllegalStateException("Aggregated history resetted lines mismatch: $i1 != $i2")
|
||||
i1
|
||||
}
|
||||
|
||||
private fun assertSameSize() {
|
||||
if (history1.size != history2.size) throw IllegalStateException("Aggregated history sizes mismatch: ${history1.size} != ${history2.size}")
|
||||
}
|
||||
|
||||
private fun assertSameId(r1: ReplHistoryRecord<T1>, r2: ReplHistoryRecord<T2>) {
|
||||
if (r1.id != r2.id) throw IllegalStateException("Aggregated history mismatch: ${r1.id} != ${r2.id}")
|
||||
}
|
||||
}
|
||||
|
||||
open class AggregatedReplStageState<T1, T2>(val state1: IReplStageState<T1>, val state2: IReplStageState<T2>, final override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
|
||||
: IReplStageState<Pair<T1, T2>>
|
||||
{
|
||||
override val history: IReplStageHistory<Pair<T1, T2>> = AggregatedReplStateHistory(state1.history, state2.history, lock)
|
||||
|
||||
override fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
|
||||
when {
|
||||
target.isAssignableFrom(state1::class.java) -> state1 as StateT
|
||||
target.isAssignableFrom(state2::class.java) -> state2 as StateT
|
||||
else -> super.asState(target)
|
||||
}
|
||||
|
||||
override fun getNextLineNo() = state1.getNextLineNo()
|
||||
|
||||
override val currentGeneration: Int get() = state1.currentGeneration
|
||||
}
|
||||
|
||||
@@ -1,72 +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.cli.common.repl
|
||||
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
|
||||
data class LineId(override val no: Int, override val generation: Int, private val codeHash: Int) : ILineId, Serializable {
|
||||
|
||||
constructor(codeLine: ReplCodeLine): this(codeLine.no, codeLine.generation, codeLine.code.hashCode())
|
||||
|
||||
override fun compareTo(other: ILineId): Int = (other as? LineId)?.let {
|
||||
no.compareTo(it.no).takeIf { it != 0 }
|
||||
?: generation.compareTo(it.generation).takeIf { it != 0 }
|
||||
?: codeHash.compareTo(it.codeHash)
|
||||
} ?: -1 // TODO: check if it doesn't break something
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8328353000L
|
||||
}
|
||||
}
|
||||
|
||||
open class BasicReplStageHistory<T>(override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageHistory<T>, ArrayList<ReplHistoryRecord<T>>() {
|
||||
|
||||
val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN)
|
||||
|
||||
override fun push(id: ILineId, item: T) {
|
||||
lock.write {
|
||||
add(ReplHistoryRecord(id, item))
|
||||
}
|
||||
}
|
||||
|
||||
override fun pop(): ReplHistoryRecord<T>? = lock.write { if (isEmpty()) null else removeAt(lastIndex) }
|
||||
|
||||
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||
lock.write {
|
||||
val idx = indexOfFirst { it.id == id }
|
||||
if (idx < 0) throw java.util.NoSuchElementException("Cannot rest to inexistent line ${id.no}")
|
||||
if (idx < lastIndex) {
|
||||
val removed = asSequence().drop(idx + 1).map { it.id }.toList()
|
||||
removeRange(idx + 1, size)
|
||||
currentGeneration.incrementAndGet()
|
||||
return removed
|
||||
}
|
||||
else return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class BasicReplStageState<HistoryItemT>(override final val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<HistoryItemT> {
|
||||
|
||||
override val currentGeneration: Int get() = history.currentGeneration.get()
|
||||
|
||||
override val history: BasicReplStageHistory<HistoryItemT> = BasicReplStageHistory(lock)
|
||||
}
|
||||
@@ -1,40 +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.cli.common.repl
|
||||
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
|
||||
open class GenericReplEvaluatorState(baseClasspath: Iterable<File>, baseClassloader: ClassLoader?, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock())
|
||||
: IReplStageState<EvalClassWithInstanceAndLoader>
|
||||
{
|
||||
override val history: IReplStageHistory<EvalClassWithInstanceAndLoader> = BasicReplStageHistory(lock)
|
||||
|
||||
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||
|
||||
val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||
|
||||
val currentClasspath: List<File> get() = lock.read {
|
||||
history.peek()?.item?.classLoader?.listAllUrlsAsFiles()
|
||||
?: topClassLoader.listAllUrlsAsFiles()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
||||
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
||||
@@ -22,37 +22,27 @@ import kotlin.concurrent.write
|
||||
|
||||
class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
||||
baseClasspath: Iterable<File>,
|
||||
baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
|
||||
private val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
|
||||
baseClassloader: ClassLoader?,
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||
repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
||||
) : ReplFullEvaluator {
|
||||
val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode)
|
||||
val evaluator = GenericReplEvaluator(baseClasspath, baseClassloader, fallbackScriptArgs, repeatingMode, stateLock)
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = AggregatedReplStageState(compiler.createState(lock), evaluator.createState(lock), lock)
|
||||
|
||||
override fun compileAndEval(state: IReplStageState<*>, codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return state.lock.write {
|
||||
val aggregatedState = state.asState(AggregatedReplStageState::class.java)
|
||||
val compiled = compiler.compile(state, codeLine)
|
||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return stateLock.write {
|
||||
val compiled = compiler.compile(codeLine, verifyHistory)
|
||||
when (compiled) {
|
||||
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.message, compiled.location)
|
||||
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete()
|
||||
is ReplCompileResult.Error -> ReplEvalResult.Error.CompileTime(compiled.compiledHistory, compiled.message, compiled.location)
|
||||
is ReplCompileResult.HistoryMismatch -> ReplEvalResult.HistoryMismatch(compiled.compiledHistory, compiled.lineNo)
|
||||
is ReplCompileResult.Incomplete -> ReplEvalResult.Incomplete(compiled.compiledHistory)
|
||||
is ReplCompileResult.CompiledClasses -> {
|
||||
val result = eval(state, compiled, scriptArgs, invokeWrapper)
|
||||
val result = eval(compiled, scriptArgs, invokeWrapper)
|
||||
when (result) {
|
||||
is ReplEvalResult.Error,
|
||||
is ReplEvalResult.HistoryMismatch,
|
||||
is ReplEvalResult.Incomplete -> {
|
||||
aggregatedState.apply {
|
||||
lock.write {
|
||||
if (state1.history.size > state2.history.size) {
|
||||
state2.history.peek()?.let {
|
||||
state1.history.resetTo(it.id)
|
||||
}
|
||||
assert(state1.history.size == state2.history.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
result.completedEvalHistory.lastOrNull()?.let { compiler.resetToLine(it) }
|
||||
result
|
||||
}
|
||||
is ReplEvalResult.ValueResult,
|
||||
@@ -66,25 +56,49 @@ class GenericReplCompilingEvaluator(val compiler: ReplCompiler,
|
||||
}
|
||||
}
|
||||
|
||||
override fun eval(state: IReplStageState<*>, compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
evaluator.eval(state, compileResult, scriptArgs, invokeWrapper)
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
stateLock.write {
|
||||
val removedCompiledLines = compiler.resetToLine(lineNumber)
|
||||
val removedEvaluatorLines = evaluator.resetToLine(lineNumber)
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = compiler.check(state, codeLine)
|
||||
removedCompiledLines.zip(removedEvaluatorLines).forEach {
|
||||
if (it.first != it.second) {
|
||||
throw IllegalStateException("History mismatch when resetting lines")
|
||||
}
|
||||
}
|
||||
|
||||
override fun compileToEvaluable(state: IReplStageState<*>, codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes?): Pair<ReplCompileResult, Evaluable?> {
|
||||
val compiled = compiler.compile(state, codeLine)
|
||||
return removedCompiledLines
|
||||
}
|
||||
}
|
||||
|
||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() = evaluator.lastEvaluatedScripts
|
||||
override val history: List<ReplCodeLine> get() = evaluator.history
|
||||
override val currentClasspath: List<File> get() = evaluator.currentClasspath
|
||||
|
||||
override val compiledHistory: List<ReplCodeLine> get() = compiler.history
|
||||
override val evaluatedHistory: List<ReplCodeLine> get() = evaluator.history
|
||||
|
||||
override fun eval(compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
evaluator.eval(compileResult, scriptArgs, invokeWrapper)
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult = compiler.check(codeLine)
|
||||
|
||||
override fun compileToEvaluable(codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?): Pair<ReplCompileResult, Evaluable?> {
|
||||
val compiled = compiler.compile(codeLine, verifyHistory)
|
||||
return when (compiled) {
|
||||
// TODO: seems usafe when delayed evaluation may happen after some more compileAndEval calls on the same state; check and fix or protect
|
||||
is ReplCompileResult.CompiledClasses -> Pair(compiled, DelayedEvaluation(state, compiled, evaluator, defaultScriptArgs ?: fallbackScriptArgs))
|
||||
is ReplCompileResult.CompiledClasses -> Pair(compiled, DelayedEvaluation(compiled, stateLock, evaluator, defaultScriptArgs ?: fallbackScriptArgs))
|
||||
else -> Pair(compiled, null)
|
||||
}
|
||||
}
|
||||
|
||||
class DelayedEvaluation(private val state: IReplStageState<*>,
|
||||
override val compiledCode: ReplCompileResult.CompiledClasses,
|
||||
class DelayedEvaluation(override val compiledCode: ReplCompileResult.CompiledClasses,
|
||||
private val stateLock: ReentrantReadWriteLock,
|
||||
private val evaluator: ReplEvaluator,
|
||||
private val defaultScriptArgs: ScriptArgsWithTypes?) : Evaluable {
|
||||
override fun eval(scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
evaluator.eval(state, compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper)
|
||||
override fun eval(scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return stateLock.write { evaluator.eval(compiledCode, scriptArgs ?: defaultScriptArgs, invokeWrapper) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,55 +18,144 @@ package org.jetbrains.kotlin.cli.common.repl
|
||||
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class GenericReplEvaluator(val baseClasspath: Iterable<File>,
|
||||
val baseClassloader: ClassLoader? = Thread.currentThread().contextClassLoader,
|
||||
open class GenericReplEvaluator(baseClasspath: Iterable<File>,
|
||||
baseClassloader: ClassLoader?,
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT
|
||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
||||
) : ReplEvaluator {
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = GenericReplEvaluatorState(baseClasspath, baseClassloader, lock)
|
||||
private val topClassLoader: ReplClassLoader = makeReplClassLoader(baseClassloader, baseClasspath)
|
||||
|
||||
override fun eval(state: IReplStageState<*>,
|
||||
compileResult: ReplCompileResult.CompiledClasses,
|
||||
private val evaluatedHistory = ReplHistory<EvalClassWithInstanceAndLoader>()
|
||||
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return stateLock.write {
|
||||
evaluatedHistory.resetToLine(lineNumber)
|
||||
}.map { it.first }
|
||||
}
|
||||
|
||||
override val history: List<ReplCodeLine> get() = stateLock.read { evaluatedHistory.copySources() }
|
||||
|
||||
override val currentClasspath: List<File> get() = stateLock.read {
|
||||
evaluatedHistory.copyValues().lastOrNull()?.classLoader?.listAllUrlsAsFiles()
|
||||
?: topClassLoader.listAllUrlsAsFiles()
|
||||
}
|
||||
|
||||
private class HistoryActions(val effectiveHistory: List<EvalClassWithInstanceAndLoader>,
|
||||
val verify: (compareHistory: SourceList?) -> Int?,
|
||||
val addPlaceholder: (line: CompiledReplCodeLine, value: EvalClassWithInstanceAndLoader) -> Unit,
|
||||
val removePlaceholder: (line: CompiledReplCodeLine) -> Boolean,
|
||||
val addFinal: (line: CompiledReplCodeLine, value: EvalClassWithInstanceAndLoader) -> Unit,
|
||||
val processClasses: (compileResult: ReplCompileResult.CompiledClasses) -> Pair<ClassLoader, Class<out Any>>)
|
||||
|
||||
private fun prependClassLoaderWithNewClasses(effectiveHistory: List<EvalClassWithInstanceAndLoader>, compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> {
|
||||
return stateLock.write {
|
||||
var mainLineClassName: String? = null
|
||||
val classLoader = makeReplClassLoader(effectiveHistory.lastOrNull()?.classLoader ?: topClassLoader, compileResult.classpathAddendum)
|
||||
fun classNameFromPath(path: String) = JvmClassName.byInternalName(path.removeSuffix(".class"))
|
||||
fun compiledClassesNames() = compileResult.classes.map { classNameFromPath(it.path).internalName.replace('/', '.') }
|
||||
val expectedClassName = compileResult.generatedClassname
|
||||
compileResult.classes.filter { it.path.endsWith(".class") }
|
||||
.forEach {
|
||||
val className = classNameFromPath(it.path)
|
||||
if (className.internalName == expectedClassName || className.internalName.endsWith("/$expectedClassName")) {
|
||||
mainLineClassName = className.internalName.replace('/', '.')
|
||||
}
|
||||
classLoader.addClass(className, it.bytes)
|
||||
}
|
||||
|
||||
val scriptClass = try {
|
||||
classLoader.loadClass(mainLineClassName!!)
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
throw Exception("Error loading class $mainLineClassName: known classes: ${compiledClassesNames()}", t)
|
||||
}
|
||||
Pair(classLoader, scriptClass)
|
||||
}
|
||||
}
|
||||
|
||||
override fun eval(compileResult: ReplCompileResult.CompiledClasses,
|
||||
scriptArgs: ScriptArgsWithTypes?,
|
||||
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
state.lock.write {
|
||||
val evalState = state.asState(GenericReplEvaluatorState::class.java)
|
||||
val historyActor = when (repeatingMode) {
|
||||
ReplRepeatingMode.NONE -> HistoryActionsForNoRepeat(evalState)
|
||||
stateLock.write {
|
||||
val verifyHistory = compileResult.compiledHistory.dropLast(1)
|
||||
val defaultHistoryActor = HistoryActions(
|
||||
effectiveHistory = evaluatedHistory.copyValues(),
|
||||
verify = { line -> evaluatedHistory.firstMismatchingHistory(line) },
|
||||
addPlaceholder = { line, value -> evaluatedHistory.add(line, value) },
|
||||
removePlaceholder = { line -> evaluatedHistory.removeLast(line) },
|
||||
addFinal = { line, value -> evaluatedHistory.add(line, value) },
|
||||
processClasses = { compiled ->
|
||||
prependClassLoaderWithNewClasses(evaluatedHistory.copyValues(), compiled)
|
||||
})
|
||||
|
||||
val historyActor: HistoryActions = when (repeatingMode) {
|
||||
ReplRepeatingMode.NONE -> defaultHistoryActor
|
||||
ReplRepeatingMode.REPEAT_ONLY_MOST_RECENT -> {
|
||||
val lastItem = evalState.history.peek()
|
||||
if (lastItem == null || lastItem.id != compileResult.lineId) {
|
||||
HistoryActionsForNoRepeat(evalState)
|
||||
val lastItem = evaluatedHistory.lastItem()
|
||||
if (lastItem == null || lastItem.first.source != compileResult.compiledCodeLine.source) {
|
||||
defaultHistoryActor
|
||||
}
|
||||
else {
|
||||
HistoryActionsForRepeatRecentOnly(evalState)
|
||||
val trimmedHistory = ReplHistory(evaluatedHistory.copyAll().dropLast(1))
|
||||
HistoryActions(
|
||||
effectiveHistory = trimmedHistory.copyValues(),
|
||||
verify = { trimmedHistory.firstMismatchingHistory(it) },
|
||||
addPlaceholder = { _, _ -> NO_ACTION() },
|
||||
removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
||||
addFinal = { line, value ->
|
||||
evaluatedHistory.removeLast(line)
|
||||
evaluatedHistory.add(line, value)
|
||||
},
|
||||
processClasses = { _ ->
|
||||
Pair(lastItem.second.classLoader, lastItem.second.klass.java)
|
||||
})
|
||||
}
|
||||
}
|
||||
ReplRepeatingMode.REPEAT_ANY_PREVIOUS -> {
|
||||
val matchingItem = evalState.history.firstOrNull { it.id == compileResult.lineId }
|
||||
if (matchingItem == null) {
|
||||
HistoryActionsForNoRepeat(evalState)
|
||||
if (evaluatedHistory.isEmpty() || !evaluatedHistory.contains(compileResult.compiledCodeLine.source)) {
|
||||
defaultHistoryActor
|
||||
}
|
||||
else {
|
||||
HistoryActionsForRepeatAny(evalState, matchingItem)
|
||||
val historyCopy = evaluatedHistory.copyAll()
|
||||
val matchingItem = historyCopy.first { it.first.source == compileResult.compiledCodeLine.source }
|
||||
val trimmedHistory = ReplHistory(evaluatedHistory.copyAll().takeWhile { it != matchingItem })
|
||||
HistoryActions(
|
||||
effectiveHistory = trimmedHistory.copyValues(),
|
||||
verify = { trimmedHistory.firstMismatchingHistory(it) },
|
||||
addPlaceholder = { _, _ -> NO_ACTION() },
|
||||
removePlaceholder = { NO_ACTION_THAT_RETURNS(true) },
|
||||
addFinal = { line, value ->
|
||||
val extraLines = evaluatedHistory.resetToLine(line)
|
||||
evaluatedHistory.removeLast(line)
|
||||
evaluatedHistory.add(line, value)
|
||||
extraLines.forEach {
|
||||
evaluatedHistory.add(it.first, it.second)
|
||||
}
|
||||
},
|
||||
processClasses = { _ ->
|
||||
Pair(matchingItem.second.classLoader, matchingItem.second.klass.java)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val firstMismatch = historyActor.firstMismatch(compileResult.previousLines.asSequence())
|
||||
val firstMismatch = historyActor.verify(verifyHistory)
|
||||
if (firstMismatch != null) {
|
||||
return@eval ReplEvalResult.HistoryMismatch(firstMismatch.first?.id?.no ?: firstMismatch.second?.no ?: -1 /* means error? */)
|
||||
return@eval ReplEvalResult.HistoryMismatch(evaluatedHistory.copySources(), firstMismatch)
|
||||
}
|
||||
|
||||
val (classLoader, scriptClass) = try {
|
||||
historyActor.processClasses(compileResult)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
return@eval ReplEvalResult.Error.Runtime(e.message ?: "unknown", e)
|
||||
return@eval ReplEvalResult.Error.Runtime(evaluatedHistory.copySources(), e.message ?: "unknown", e)
|
||||
}
|
||||
|
||||
val currentScriptArgs = scriptArgs ?: fallbackScriptArgs
|
||||
@@ -81,7 +170,7 @@ open class GenericReplEvaluator(val baseClasspath: Iterable<File>,
|
||||
// TODO: try/catch ?
|
||||
val scriptInstanceConstructor = scriptClass.getConstructor(*constructorParams)
|
||||
|
||||
historyActor.addPlaceholder(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, classLoader, invokeWrapper))
|
||||
historyActor.addPlaceholder(compileResult.compiledCodeLine, EvalClassWithInstanceAndLoader(scriptClass.kotlin, null, classLoader, invokeWrapper))
|
||||
|
||||
val scriptInstance =
|
||||
try {
|
||||
@@ -89,113 +178,33 @@ open class GenericReplEvaluator(val baseClasspath: Iterable<File>,
|
||||
else scriptInstanceConstructor.newInstance(*constructorArgs)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
historyActor.removePlaceholder(compileResult.lineId)
|
||||
historyActor.removePlaceholder(compileResult.compiledCodeLine)
|
||||
|
||||
// ignore everything in the stack trace until this constructor call
|
||||
return@eval ReplEvalResult.Error.Runtime(renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
||||
return@eval ReplEvalResult.Error.Runtime(evaluatedHistory.copySources(),
|
||||
renderReplStackTrace(e.cause!!, startFromMethodName = "${scriptClass.name}.<init>"), e as? Exception)
|
||||
}
|
||||
|
||||
historyActor.removePlaceholder(compileResult.lineId)
|
||||
historyActor.addFinal(compileResult.lineId, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
|
||||
historyActor.removePlaceholder(compileResult.compiledCodeLine)
|
||||
historyActor.addFinal(compileResult.compiledCodeLine, EvalClassWithInstanceAndLoader(scriptClass.kotlin, scriptInstance, classLoader, invokeWrapper))
|
||||
|
||||
val resultField = scriptClass.getDeclaredField(SCRIPT_RESULT_FIELD_NAME).apply { isAccessible = true }
|
||||
val resultValue: Any? = resultField.get(scriptInstance)
|
||||
|
||||
return if (compileResult.hasResult) ReplEvalResult.ValueResult(resultValue)
|
||||
else ReplEvalResult.UnitResult()
|
||||
return if (compileResult.hasResult) ReplEvalResult.ValueResult(evaluatedHistory.copySources(), resultValue)
|
||||
else ReplEvalResult.UnitResult(evaluatedHistory.copySources())
|
||||
}
|
||||
}
|
||||
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() {
|
||||
return stateLock.read { evaluatedHistory.copyAll() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
|
||||
}
|
||||
|
||||
private fun makeReplClassLoader(baseClassloader: ClassLoader?, baseClasspath: Iterable<File>) =
|
||||
ReplClassLoader(URLClassLoader(baseClasspath.map { it.toURI().toURL() }.toTypedArray(), baseClassloader))
|
||||
}
|
||||
|
||||
private open class HistoryActionsForNoRepeat(val state: GenericReplEvaluatorState) {
|
||||
|
||||
open val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.map { it.item }
|
||||
|
||||
open fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? = state.history.firstMismatch(other)
|
||||
|
||||
open fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
|
||||
|
||||
open fun removePlaceholder(lineId: ILineId): Boolean = state.history.verifiedPop(lineId) != null
|
||||
|
||||
open fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) { state.history.push(lineId, value) }
|
||||
|
||||
open fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> = prependClassLoaderWithNewClasses(effectiveHistory, compileResult)
|
||||
|
||||
private fun prependClassLoaderWithNewClasses(effectiveHistory: List<EvalClassWithInstanceAndLoader>,
|
||||
compileResult: ReplCompileResult.CompiledClasses
|
||||
): Pair<ClassLoader, Class<out Any>> {
|
||||
var mainLineClassName: String? = null
|
||||
val classLoader = makeReplClassLoader(effectiveHistory.lastOrNull()?.classLoader ?: state.topClassLoader, compileResult.classpathAddendum)
|
||||
fun classNameFromPath(path: String) = JvmClassName.byInternalName(path.removeSuffix(".class"))
|
||||
fun compiledClassesNames() = compileResult.classes.map { classNameFromPath(it.path).internalName.replace('/', '.') }
|
||||
val expectedClassName = compileResult.mainClassName
|
||||
compileResult.classes.filter { it.path.endsWith(".class") }
|
||||
.forEach {
|
||||
val className = classNameFromPath(it.path)
|
||||
if (className.internalName == expectedClassName || className.internalName.endsWith("/$expectedClassName")) {
|
||||
mainLineClassName = className.internalName.replace('/', '.')
|
||||
}
|
||||
classLoader.addClass(className, it.bytes)
|
||||
}
|
||||
|
||||
val scriptClass = try {
|
||||
classLoader.loadClass(mainLineClassName!!)
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
throw Exception("Error loading class $mainLineClassName: known classes: ${compiledClassesNames()}", t)
|
||||
}
|
||||
return Pair(classLoader, scriptClass)
|
||||
}
|
||||
}
|
||||
|
||||
private open class HistoryActionsForRepeatRecentOnly(state: GenericReplEvaluatorState) : HistoryActionsForNoRepeat(state) {
|
||||
|
||||
val currentLast = state.history.peek()!!
|
||||
|
||||
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = super.effectiveHistory.dropLast(1)
|
||||
|
||||
override fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? =
|
||||
state.history.firstMismatchFiltered(other) { it.id != currentLast.id }
|
||||
|
||||
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
||||
|
||||
override fun removePlaceholder(lineId: ILineId): Boolean = true
|
||||
|
||||
override fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {
|
||||
state.history.pop()
|
||||
state.history.push(lineId, value)
|
||||
}
|
||||
|
||||
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> =
|
||||
currentLast.item.classLoader to currentLast.item.klass.java
|
||||
}
|
||||
|
||||
private open class HistoryActionsForRepeatAny(state: GenericReplEvaluatorState, val matchingLine: ReplHistoryRecord<EvalClassWithInstanceAndLoader>): HistoryActionsForNoRepeat(state) {
|
||||
|
||||
override val effectiveHistory: List<EvalClassWithInstanceAndLoader> get() = state.history.takeWhile { it.id != matchingLine.id }.map { it.item }
|
||||
|
||||
override fun firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<EvalClassWithInstanceAndLoader>?, ILineId?>? =
|
||||
state.history.firstMismatchWhile(other) { it.id != matchingLine.id }
|
||||
|
||||
override fun addPlaceholder(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {}
|
||||
|
||||
override fun removePlaceholder(lineId: ILineId): Boolean = true
|
||||
|
||||
override fun addFinal(lineId: ILineId, value: EvalClassWithInstanceAndLoader) {
|
||||
val extraLines = state.history.takeLastWhile { it.id == matchingLine.id }
|
||||
state.history.resetTo(lineId)
|
||||
state.history.pop()
|
||||
state.history.push(lineId, value)
|
||||
extraLines.forEach {
|
||||
state.history.push(it.id, it.item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processClasses(compileResult: ReplCompileResult.CompiledClasses): Pair<ClassLoader, Class<out Any>> =
|
||||
matchingLine.item.classLoader to matchingLine.item.klass.java
|
||||
}
|
||||
|
||||
@@ -29,11 +29,10 @@ import kotlin.reflect.full.safeCast
|
||||
@Suppress("unused") // used externally (kotlin.script.utils)
|
||||
interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
|
||||
val state: IReplStageState<*> // The Invokable interface do not allow Context/Bindings substitution, so state is supplied via property
|
||||
val replScriptEvaluator: ReplEvaluatorExposedInternalHistory
|
||||
|
||||
private fun prioritizedHistory(receiverClass: KClass<*>?, receiverInstance: Any?): List<EvalClassWithInstanceAndLoader> {
|
||||
val evalState = state.asState(GenericReplEvaluatorState::class.java)
|
||||
return evalState.history.map { it.item }.filter { it.instance != null }.reversed().ensureNotEmpty("no script ").let { history ->
|
||||
return replScriptEvaluator.lastEvaluatedScripts.map { it.second }.filter { it.instance != null }.reversed().ensureNotEmpty("no script ").let { history ->
|
||||
if (receiverInstance != null) {
|
||||
val receiverKlass = receiverClass ?: receiverInstance.javaClass.kotlin
|
||||
val receiverInHistory = history.find { it.instance == receiverInstance } ?:
|
||||
@@ -60,10 +59,10 @@ interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
private fun invokeImpl(prioritizedCallOrder: List<EvalClassWithInstanceAndLoader>, name: String, args: Array<out Any?>): Any? {
|
||||
// TODO: cache the method lookups?
|
||||
|
||||
val (fn, mapping, invokeWrapper) = prioritizedCallOrder.asSequence().map { (klass, instance, _, invokeWrapper) ->
|
||||
val candidates = klass.functions.filter { it.name == name }
|
||||
candidates.findMapping(listOf(instance) + args)?.let {
|
||||
Triple(it.first, it.second, invokeWrapper)
|
||||
val (fn, mapping, invokeWrapper) = prioritizedCallOrder.asSequence().map { attempt ->
|
||||
val candidates = attempt.klass.functions.filter { it.name == name }
|
||||
candidates.findMapping(listOf<Any?>(attempt.instance) + args)?.let {
|
||||
Triple(it.first, it.second, attempt.invokeWrapper)
|
||||
}
|
||||
}.filterNotNull().firstOrNull() ?: throw NoSuchMethodException("no suitable function '$name' found")
|
||||
|
||||
@@ -95,7 +94,7 @@ interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
}
|
||||
|
||||
private fun <T : Any> proxyInterface(thiz: Any?, clasz: Class<T>?): T? {
|
||||
if (state.history.size == 0) throw IllegalStateException("no script")
|
||||
replScriptEvaluator.lastEvaluatedScripts.ensureNotEmpty("no script")
|
||||
val priority = prioritizedHistory(thiz?.javaClass?.kotlin, thiz)
|
||||
|
||||
if (clasz == null) throw IllegalArgumentException("class object cannot be null")
|
||||
@@ -110,6 +109,33 @@ interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun invokeImpl(prioritizedCallOrder: List<EvalClassWithInstanceAndLoader>, name: String, args: Array<out Any?>): Any? {
|
||||
// TODO: cache the method lookups?
|
||||
|
||||
val (fn, mapping, invokeWrapper) = prioritizedCallOrder.asSequence().map { attempt ->
|
||||
val candidates = attempt.klass.functions.filter { it.name == name }
|
||||
candidates.findMapping(listOf<Any?>(attempt.instance) + args)?.let {
|
||||
Triple(it.first, it.second, attempt.invokeWrapper)
|
||||
}
|
||||
}.filterNotNull().firstOrNull() ?: throw NoSuchMethodException("no suitable function '$name' found")
|
||||
|
||||
val res = try {
|
||||
if (invokeWrapper != null) {
|
||||
invokeWrapper.invoke {
|
||||
fn.callBy(mapping)
|
||||
}
|
||||
}
|
||||
else {
|
||||
fn.callBy(mapping)
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
// ignore everything in the stack trace until this constructor call
|
||||
throw ScriptException(renderReplStackTrace(e.cause!!, startFromMethodName = fn.name))
|
||||
}
|
||||
return if (fn.returnType.classifier == Unit::class) Unit else res
|
||||
}
|
||||
|
||||
private fun Iterable<KFunction<*>>.findMapping(args: List<Any?>): Pair<KFunction<*>, Map<KParameter, Any?>>? {
|
||||
for (fn in this) {
|
||||
val mapping = tryCreateCallableMapping(fn, args)
|
||||
|
||||
@@ -18,16 +18,26 @@ package org.jetbrains.kotlin.cli.common.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import java.io.Reader
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import javax.script.*
|
||||
|
||||
const val KOTLIN_SCRIPT_STATE_BINDINGS_KEY = "kotlin.script.state"
|
||||
const val KOTLIN_SCRIPT_ENGINE_BINDINGS_KEY = "kotlin.script.engine"
|
||||
val KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY = "kotlin.script.history"
|
||||
val KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY = "kotlin.script.line.number"
|
||||
|
||||
// TODO consider additional error handling
|
||||
var Bindings.kotlinScriptHistory: MutableList<ReplCodeLine>
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
get() = getOrPut(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, { arrayListOf<ReplCodeLine>() }) as MutableList<ReplCodeLine>
|
||||
set(v) { put(KOTLIN_SCRIPT_HISTORY_BINDINGS_KEY, v) }
|
||||
|
||||
val Bindings.kotlinScriptLineNumber: AtomicInteger
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
get() = getOrPut(KOTLIN_SCRIPT_LINE_NUMBER_BINDINGS_KEY, { AtomicInteger(0) }) as AtomicInteger
|
||||
|
||||
abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEngineFactory) : AbstractScriptEngine(), ScriptEngine, Compilable {
|
||||
|
||||
protected abstract val replCompiler: ReplCompiler
|
||||
protected abstract val replEvaluator: ReplFullEvaluator
|
||||
protected abstract val replCompiler: ReplCompileAction
|
||||
protected abstract val replScriptEvaluator: ReplFullEvaluator
|
||||
|
||||
override fun eval(script: String, context: ScriptContext): Any? = compileAndEval(script, context)
|
||||
|
||||
@@ -37,29 +47,25 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
||||
|
||||
override fun compile(script: Reader): CompiledScript = compile(script.readText(), getContext())
|
||||
|
||||
override fun createBindings(): Bindings = SimpleBindings().apply { put(KOTLIN_SCRIPT_ENGINE_BINDINGS_KEY, this) }
|
||||
override fun createBindings(): Bindings = SimpleBindings()
|
||||
|
||||
override fun getFactory(): ScriptEngineFactory = myFactory
|
||||
|
||||
// the parameter could be used in the future when we decide to keep state completely in the context and solve appropriate problems (now e.g. replCompiler keeps separate state)
|
||||
fun nextCodeLine(context: ScriptContext, code: String) = getCurrentState(context).let { ReplCodeLine(it.getNextLineNo(), it.currentGeneration, code) }
|
||||
fun nextCodeLine(@Suppress("UNUSED_PARAMETER") context: ScriptContext, code: String) = ReplCodeLine(this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptLineNumber.incrementAndGet(), code)
|
||||
|
||||
protected abstract fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*>
|
||||
private fun getCurrentHistory(@Suppress("UNUSED_PARAMETER") context: ScriptContext) = this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory
|
||||
|
||||
protected fun getCurrentState(context: ScriptContext) =
|
||||
context.getBindings(ScriptContext.ENGINE_SCOPE)
|
||||
.getOrPut(KOTLIN_SCRIPT_STATE_BINDINGS_KEY, {
|
||||
// TODO: check why createBinding is not called on creating default context, so the engine is not set
|
||||
context.getBindings(ScriptContext.ENGINE_SCOPE).put(KOTLIN_SCRIPT_ENGINE_BINDINGS_KEY, this@KotlinJsr223JvmScriptEngineBase)
|
||||
createState()
|
||||
}) as IReplStageState<*>
|
||||
private fun setContextHistory(@Suppress("UNUSED_PARAMETER") context: ScriptContext, history: ArrayList<ReplCodeLine>) {
|
||||
this.context.getBindings(ScriptContext.ENGINE_SCOPE).kotlinScriptHistory = history
|
||||
}
|
||||
|
||||
open fun overrideScriptArgs(context: ScriptContext): ScriptArgsWithTypes? = null
|
||||
|
||||
open fun compileAndEval(script: String, context: ScriptContext): Any? {
|
||||
val codeLine = nextCodeLine(context, script)
|
||||
val state = getCurrentState(context)
|
||||
val result = replEvaluator.compileAndEval(state, codeLine, scriptArgs = overrideScriptArgs(context))
|
||||
val history = getCurrentHistory(context)
|
||||
val result = replScriptEvaluator.compileAndEval(codeLine, scriptArgs = overrideScriptArgs(context), verifyHistory = history)
|
||||
val ret = when (result) {
|
||||
is ReplEvalResult.ValueResult -> result.value
|
||||
is ReplEvalResult.UnitResult -> null
|
||||
@@ -67,26 +73,29 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
||||
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||
}
|
||||
setContextHistory(context, ArrayList(result.completedEvalHistory))
|
||||
return ret
|
||||
}
|
||||
|
||||
open fun compile(script: String, context: ScriptContext): CompiledScript {
|
||||
val codeLine = nextCodeLine(context, script)
|
||||
val state = getCurrentState(context)
|
||||
val history = getCurrentHistory(context)
|
||||
|
||||
val result = replCompiler.compile(state, codeLine)
|
||||
val result = replCompiler.compile(codeLine, history)
|
||||
val compiled = when (result) {
|
||||
is ReplCompileResult.Error -> throw ScriptException("Error${result.locationString()}: ${result.message}")
|
||||
is ReplCompileResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||
is ReplCompileResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||
is ReplCompileResult.CompiledClasses -> result
|
||||
}
|
||||
// TODO: check if it is ok to keep compiled history in the same place as compiledEval one
|
||||
setContextHistory(context, ArrayList(result.compiledHistory))
|
||||
return CompiledKotlinScript(this, codeLine, compiled)
|
||||
}
|
||||
|
||||
open fun eval(compiledScript: CompiledKotlinScript, context: ScriptContext): Any? {
|
||||
val state = getCurrentState(context)
|
||||
val result = try {
|
||||
replEvaluator.eval(state, compiledScript.compiledData, scriptArgs = overrideScriptArgs(context))
|
||||
replScriptEvaluator.eval(compiledScript.compiledData, scriptArgs = overrideScriptArgs(context))
|
||||
}
|
||||
catch (e: Exception) {
|
||||
throw ScriptException(e)
|
||||
@@ -99,6 +108,7 @@ abstract class KotlinJsr223JvmScriptEngineBase(protected val myFactory: ScriptEn
|
||||
is ReplEvalResult.Incomplete -> throw ScriptException("error: incomplete code")
|
||||
is ReplEvalResult.HistoryMismatch -> throw ScriptException("Repl history mismatch at line: ${result.lineNo}")
|
||||
}
|
||||
setContextHistory(context, ArrayList(result.completedEvalHistory))
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,9 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
const val REPL_CODE_LINE_FIRST_NO = 1
|
||||
const val REPL_CODE_LINE_FIRST_GEN = 1
|
||||
|
||||
data class ReplCodeLine(val no: Int, val generation: Int, val code: String) : Serializable {
|
||||
data class ReplCodeLine(val no: Int, val code: String) : Serializable {
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8228357578L
|
||||
}
|
||||
@@ -47,14 +43,9 @@ data class CompiledClassData(val path: String, val bytes: ByteArray) : Serializa
|
||||
}
|
||||
}
|
||||
|
||||
interface CreateReplStageStateAction {
|
||||
fun createState(lock: ReentrantReadWriteLock = ReentrantReadWriteLock()): IReplStageState<*>
|
||||
}
|
||||
|
||||
// --- check
|
||||
|
||||
interface ReplCheckAction {
|
||||
fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult
|
||||
fun check(codeLine: ReplCodeLine): ReplCheckResult
|
||||
}
|
||||
|
||||
sealed class ReplCheckResult : Serializable {
|
||||
@@ -72,24 +63,40 @@ sealed class ReplCheckResult : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
// --- compile
|
||||
interface ReplResettableCodeLine {
|
||||
fun resetToLine(lineNumber: Int): List<ReplCodeLine>
|
||||
|
||||
interface ReplCompileAction {
|
||||
fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult
|
||||
fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
}
|
||||
|
||||
sealed class ReplCompileResult : Serializable {
|
||||
class CompiledClasses(val lineId: LineId,
|
||||
val previousLines: List<ILineId>,
|
||||
val mainClassName: String,
|
||||
interface ReplCodeLineHistory {
|
||||
val history: List<ReplCodeLine>
|
||||
}
|
||||
|
||||
interface ReplCombinedHistory {
|
||||
val compiledHistory: List<ReplCodeLine>
|
||||
val evaluatedHistory: List<ReplCodeLine>
|
||||
}
|
||||
|
||||
interface ReplCompileAction {
|
||||
fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>? = null): ReplCompileResult
|
||||
}
|
||||
|
||||
sealed class ReplCompileResult(val compiledHistory: List<ReplCodeLine>) : Serializable {
|
||||
class CompiledClasses(compiledHistory: List<ReplCodeLine>,
|
||||
val compiledCodeLine: CompiledReplCodeLine,
|
||||
val generatedClassname: String,
|
||||
val classes: List<CompiledClassData>,
|
||||
val hasResult: Boolean,
|
||||
val classpathAddendum: List<File>) : ReplCompileResult()
|
||||
val classpathAddendum: List<File>) : ReplCompileResult(compiledHistory)
|
||||
|
||||
class Incomplete : ReplCompileResult()
|
||||
class Incomplete(compiledHistory: List<ReplCodeLine>) : ReplCompileResult(compiledHistory)
|
||||
|
||||
class Error(val message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult() {
|
||||
class HistoryMismatch(compiledHistory: List<ReplCodeLine>, val lineNo: Int) : ReplCompileResult(compiledHistory)
|
||||
|
||||
class Error(compiledHistory: List<ReplCodeLine>,
|
||||
val message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : ReplCompileResult(compiledHistory) {
|
||||
override fun toString(): String = "Error(message = \"$message\""
|
||||
}
|
||||
|
||||
@@ -98,35 +105,43 @@ sealed class ReplCompileResult : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
interface ReplCompiler : ReplCompileAction, ReplCheckAction, CreateReplStageStateAction
|
||||
interface ReplCompiler : ReplResettableCodeLine, ReplCodeLineHistory, ReplCompileAction, ReplCheckAction
|
||||
|
||||
// --- eval
|
||||
typealias EvalHistoryType = Pair<CompiledReplCodeLine, EvalClassWithInstanceAndLoader>
|
||||
|
||||
interface ReplEvaluatorExposedInternalHistory {
|
||||
val lastEvaluatedScripts: List<EvalHistoryType>
|
||||
}
|
||||
|
||||
interface ReplClasspath {
|
||||
val currentClasspath: List<File>
|
||||
}
|
||||
|
||||
data class EvalClassWithInstanceAndLoader(val klass: KClass<*>, val instance: Any?, val classLoader: ClassLoader, val invokeWrapper: InvokeWrapper?)
|
||||
|
||||
interface ReplEvalAction {
|
||||
fun eval(state: IReplStageState<*>,
|
||||
compileResult: ReplCompileResult.CompiledClasses,
|
||||
fun eval(compileResult: ReplCompileResult.CompiledClasses,
|
||||
scriptArgs: ScriptArgsWithTypes? = null,
|
||||
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||
}
|
||||
|
||||
sealed class ReplEvalResult : Serializable {
|
||||
class ValueResult(val value: Any?) : ReplEvalResult() {
|
||||
sealed class ReplEvalResult(val completedEvalHistory: List<ReplCodeLine>) : Serializable {
|
||||
class ValueResult(completedEvalHistory: List<ReplCodeLine>, val value: Any?) : ReplEvalResult(completedEvalHistory) {
|
||||
override fun toString(): String = "Result: $value"
|
||||
}
|
||||
|
||||
class UnitResult : ReplEvalResult()
|
||||
class UnitResult(completedEvalHistory: List<ReplCodeLine>) : ReplEvalResult(completedEvalHistory)
|
||||
|
||||
class Incomplete : ReplEvalResult()
|
||||
class Incomplete(completedEvalHistory: List<ReplCodeLine>) : ReplEvalResult(completedEvalHistory)
|
||||
|
||||
class HistoryMismatch(val lineNo: Int) : ReplEvalResult()
|
||||
class HistoryMismatch(completedEvalHistory: List<ReplCodeLine>, val lineNo: Int) : ReplEvalResult(completedEvalHistory)
|
||||
|
||||
sealed class Error(val message: String) : ReplEvalResult() {
|
||||
class Runtime(message: String, val cause: Exception? = null) : Error(message)
|
||||
sealed class Error(completedEvalHistory: List<ReplCodeLine>, val message: String) : ReplEvalResult(completedEvalHistory) {
|
||||
class Runtime(completedEvalHistory: List<ReplCodeLine>, message: String, val cause: Exception? = null) : Error(completedEvalHistory, message)
|
||||
|
||||
class CompileTime(message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(message)
|
||||
class CompileTime(completedEvalHistory: List<ReplCodeLine>,
|
||||
message: String,
|
||||
val location: CompilerMessageLocation = CompilerMessageLocation.NO_LOCATION) : Error(completedEvalHistory, message)
|
||||
|
||||
override fun toString(): String = "${this::class.simpleName}Error(message = \"$message\""
|
||||
}
|
||||
@@ -136,44 +151,43 @@ sealed class ReplEvalResult : Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
interface ReplEvaluator : ReplEvalAction, CreateReplStageStateAction
|
||||
|
||||
// --- compileAdnEval
|
||||
interface ReplEvaluator : ReplResettableCodeLine, ReplCodeLineHistory, ReplEvaluatorExposedInternalHistory, ReplEvalAction, ReplClasspath
|
||||
|
||||
interface ReplAtomicEvalAction {
|
||||
fun compileAndEval(state: IReplStageState<*>,
|
||||
codeLine: ReplCodeLine,
|
||||
fun compileAndEval(codeLine: ReplCodeLine,
|
||||
scriptArgs: ScriptArgsWithTypes? = null,
|
||||
verifyHistory: List<ReplCodeLine>? = null,
|
||||
invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||
}
|
||||
|
||||
interface ReplAtomicEvaluator : ReplAtomicEvalAction, ReplCheckAction
|
||||
interface ReplAtomicEvaluator : ReplResettableCodeLine, ReplCombinedHistory, ReplEvaluatorExposedInternalHistory, ReplAtomicEvalAction, ReplCheckAction, ReplClasspath
|
||||
|
||||
interface ReplDelayedEvalAction {
|
||||
fun compileToEvaluable(state: IReplStageState<*>,
|
||||
codeLine: ReplCodeLine,
|
||||
defaultScriptArgs: ScriptArgsWithTypes? = null): Pair<ReplCompileResult, Evaluable?>
|
||||
fun compileToEvaluable(codeLine: ReplCodeLine, defaultScriptArgs: ScriptArgsWithTypes? = null, verifyHistory: List<ReplCodeLine>?): Pair<ReplCompileResult, Evaluable?>
|
||||
}
|
||||
|
||||
// other
|
||||
|
||||
interface Evaluable {
|
||||
val compiledCode: ReplCompileResult.CompiledClasses
|
||||
fun eval(scriptArgs: ScriptArgsWithTypes? = null, invokeWrapper: InvokeWrapper? = null): ReplEvalResult
|
||||
}
|
||||
|
||||
interface ReplFullEvaluator : ReplEvaluator, ReplAtomicEvaluator, ReplDelayedEvalAction
|
||||
interface ReplFullEvaluator : ReplEvaluator, ReplAtomicEvaluator, ReplDelayedEvalAction, ReplCombinedHistory
|
||||
|
||||
/**
|
||||
* Keep args and arg types together, so as a whole they are present or absent
|
||||
*/
|
||||
class ScriptArgsWithTypes(val scriptArgs: Array<out Any?>, val scriptArgsTypes: Array<out KClass<out Any>>) : Serializable {
|
||||
init { assert(scriptArgs.size == scriptArgsTypes.size) }
|
||||
companion object {
|
||||
private val serialVersionUID: Long = 8529357500L
|
||||
}
|
||||
}
|
||||
|
||||
interface ScriptTemplateEmptyArgsProvider {
|
||||
val defaultEmptyArgs: ScriptArgsWithTypes?
|
||||
}
|
||||
|
||||
class SimpleScriptTemplateEmptyArgsProvider(override val defaultEmptyArgs: ScriptArgsWithTypes? = null) : ScriptTemplateEmptyArgsProvider
|
||||
|
||||
enum class ReplRepeatingMode {
|
||||
NONE,
|
||||
REPEAT_ONLY_MOST_RECENT,
|
||||
|
||||
@@ -1,78 +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.cli.common.repl
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
|
||||
interface ILineId : Comparable<ILineId> {
|
||||
val no: Int
|
||||
val generation: Int
|
||||
}
|
||||
|
||||
data class ReplHistoryRecord<out T> (val id: ILineId, val item: T)
|
||||
|
||||
interface IReplStageHistory<T> : List<ReplHistoryRecord<T>> {
|
||||
|
||||
fun peek(): ReplHistoryRecord<T>? = lock.read { lastOrNull() }
|
||||
|
||||
fun push(id: ILineId, item: T)
|
||||
|
||||
fun pop(): ReplHistoryRecord<T>?
|
||||
|
||||
fun verifiedPop(id: ILineId): ReplHistoryRecord<T>? = lock.write {
|
||||
if (lastOrNull()?.id == id) pop()
|
||||
else null
|
||||
}
|
||||
|
||||
fun resetTo(id: ILineId): Iterable<ILineId>
|
||||
|
||||
val lock: ReentrantReadWriteLock
|
||||
}
|
||||
|
||||
interface IReplStageState<T> {
|
||||
val history: IReplStageHistory<T>
|
||||
|
||||
val lock: ReentrantReadWriteLock
|
||||
|
||||
val currentGeneration: Int
|
||||
|
||||
fun getNextLineNo(): Int = history.peek()?.id?.no?.let { it + 1 } ?: REPL_CODE_LINE_FIRST_NO // TODO: it should be more robust downstream (e.g. use atomic)
|
||||
|
||||
fun <StateT : IReplStageState<*>> asState(target: Class<out StateT>): StateT =
|
||||
if (target.isAssignableFrom(this::class.java)) this as StateT
|
||||
else throw IllegalArgumentException("$this is not an expected instance of IReplStageState")
|
||||
}
|
||||
|
||||
|
||||
fun <T> IReplStageHistory<T>.firstMismatch(other: Sequence<ILineId>): Pair<ReplHistoryRecord<T>?, ILineId?>? =
|
||||
lock.read {
|
||||
iterator().asSequence().zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
|
||||
}
|
||||
|
||||
fun<T> IReplStageHistory<T>.firstMismatchFiltered(other: Sequence<ILineId>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ILineId?>? =
|
||||
lock.read {
|
||||
iterator().asSequence().filter(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
|
||||
}
|
||||
|
||||
fun<T> IReplStageHistory<T>.firstMismatchWhile(other: Sequence<ILineId>, predicate: (ReplHistoryRecord<T>) -> Boolean): Pair<ReplHistoryRecord<T>?, ILineId?>? =
|
||||
lock.read {
|
||||
iterator().asSequence().takeWhile(predicate).zip(other.asSequence()).firstOrNull { it.first.id != it.second }?.let { it.first to it.second }
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ import com.google.common.base.Throwables
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
|
||||
fun makeScriptBaseName(codeLine: ReplCodeLine) =
|
||||
"Line_${codeLine.no}${if (codeLine.generation > REPL_CODE_LINE_FIRST_GEN) "_gen_${codeLine.generation}" else ""}"
|
||||
fun makeScriptBaseName(codeLine: ReplCodeLine, generation: Long) =
|
||||
"Line_${codeLine.no}${if (generation > 1) "_gen_$generation" else ""}"
|
||||
|
||||
fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String {
|
||||
val newTrace = arrayListOf<StackTraceElement>()
|
||||
var skip = true
|
||||
for ((_, element) in cause.stackTrace.withIndex().reversed()) {
|
||||
for ((i, element) in cause.stackTrace.withIndex().reversed()) {
|
||||
if ("${element.className}.${element.methodName}" == startFromMethodName) {
|
||||
skip = false
|
||||
}
|
||||
@@ -43,6 +43,10 @@ fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String
|
||||
return Throwables.getStackTraceAsString(cause)
|
||||
}
|
||||
|
||||
fun NO_ACTION(): Unit = Unit
|
||||
fun <T> NO_ACTION_THAT_RETURNS(v: T): T = v
|
||||
|
||||
|
||||
internal fun ClassLoader.listAllUrlsAsFiles(): List<File> {
|
||||
val parents = generateSequence(this) { loader -> loader.parent }.filterIsInstance(URLClassLoader::class.java)
|
||||
return parents.fold(emptyList<File>()) { accum, loader ->
|
||||
|
||||
@@ -22,7 +22,10 @@ import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.intellij.util.concurrency.AppScheduledExecutorService;
|
||||
import com.sampullara.cli.Args;
|
||||
import kotlin.Pair;
|
||||
import kotlin.collections.ArraysKt;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -232,8 +235,6 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
configuration.put(CLIConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
|
||||
}
|
||||
|
||||
configuration.put(CommonConfigurationKeys.SKIP_METADATA_VERSION_CHECK, arguments.skipMetadataVersionCheck);
|
||||
|
||||
setupLanguageVersionSettings(configuration, arguments);
|
||||
}
|
||||
|
||||
@@ -275,32 +276,27 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
extraLanguageFeatures.add(coroutinesApplicabilityLevel);
|
||||
}
|
||||
|
||||
CommonConfigurationKeysKt.setLanguageVersionSettings(
|
||||
configuration,
|
||||
new LanguageVersionSettingsImpl(
|
||||
languageVersion,
|
||||
ApiVersion.createByLanguageVersion(apiVersion),
|
||||
extraLanguageFeatures,
|
||||
arguments.apiVersion != null
|
||||
)
|
||||
configuration.put(
|
||||
CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
|
||||
new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion), extraLanguageFeatures)
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LanguageFeature chooseCoroutinesApplicabilityLevel(
|
||||
@NotNull CompilerConfiguration configuration,
|
||||
@NotNull CommonCompilerArguments arguments
|
||||
) {
|
||||
if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) {
|
||||
@NotNull CompilerConfiguration configuration, @NotNull CommonCompilerArguments arguments) {
|
||||
if (!arguments.coroutinesEnable && !arguments.coroutinesError && !arguments.coroutinesWarn) {
|
||||
return LanguageFeature.WarnOnCoroutines;
|
||||
}
|
||||
else if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) {
|
||||
return LanguageFeature.ErrorOnCoroutines;
|
||||
}
|
||||
else if (arguments.coroutinesWarn && !arguments.coroutinesError && !arguments.coroutinesEnable) {
|
||||
return LanguageFeature.WarnOnCoroutines;
|
||||
}
|
||||
else if (arguments.coroutinesEnable && !arguments.coroutinesWarn && !arguments.coroutinesError) {
|
||||
return LanguageFeature.DoNotWarnOnCoroutines;
|
||||
}
|
||||
else if (!arguments.coroutinesEnable && !arguments.coroutinesError) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
String message = "The -Xcoroutines can only have one value";
|
||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
||||
CompilerMessageSeverity.ERROR, message, CompilerMessageLocation.NO_LOCATION
|
||||
|
||||
@@ -39,8 +39,10 @@ class Usage {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
String usage = fieldUsage(field, extraHelp);
|
||||
if (usage != null) {
|
||||
if (usage.contains("Xcoroutines")) {
|
||||
if (coroutinesUsagePrinted) continue;
|
||||
boolean coroutinesUsage = usage.contains("Xcoroutines");
|
||||
if (coroutinesUsage && coroutinesUsagePrinted) {
|
||||
continue;
|
||||
} else if (coroutinesUsage) {
|
||||
coroutinesUsagePrinted = true;
|
||||
}
|
||||
target.println(usage);
|
||||
@@ -87,7 +89,6 @@ class Usage {
|
||||
if (isXCoroutinesKey) {
|
||||
sb.append(" ");
|
||||
sb.append(coroutinesKeyDescription);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
int width = OPTION_NAME_PADDING_WIDTH - 1;
|
||||
|
||||
@@ -22,7 +22,9 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
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.config.*
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.MavenComparableVersion
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.jar.Attributes
|
||||
@@ -31,16 +33,21 @@ import java.util.jar.Manifest
|
||||
object JvmRuntimeVersionsConsistencyChecker {
|
||||
private val LOG = Logger.getInstance(JvmRuntimeVersionsConsistencyChecker::class.java)
|
||||
|
||||
private fun <T> T?.assertNotNull(lazyMessage: () -> String): T =
|
||||
this ?: lazyMessage().let { message ->
|
||||
LOG.error(message)
|
||||
throw AssertionError(message)
|
||||
}
|
||||
private fun fatal(message: String): Nothing {
|
||||
LOG.error(message)
|
||||
throw AssertionError(message)
|
||||
}
|
||||
|
||||
private fun <T> T?.assertNotNull(message: () -> String): T =
|
||||
if (this == null) fatal(message()) else this
|
||||
|
||||
private val VERSION_ISSUE_SEVERITY = CompilerMessageSeverity.ERROR
|
||||
|
||||
private const val META_INF = "META-INF"
|
||||
private const val MANIFEST_MF = "$META_INF/MANIFEST.MF"
|
||||
|
||||
private const val MANIFEST_KOTLIN_VERSION_ATTRIBUTE = "manifest.impl.attribute.kotlin.version"
|
||||
private const val MANIFEST_KOTLIN_VERSION_VALUE = "manifest.impl.value.kotlin.version"
|
||||
private const val MANIFEST_KOTLIN_RUNTIME_COMPONENT = "manifest.impl.attribute.kotlin.runtime.component"
|
||||
private const val MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE = "manifest.impl.value.kotlin.runtime.component.core"
|
||||
private const val MANIFEST_KOTLIN_RUNTIME_COMPONENT_MAIN = "manifest.impl.value.kotlin.runtime.component.main"
|
||||
@@ -53,6 +60,8 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
)
|
||||
|
||||
private val KOTLIN_VERSION_ATTRIBUTE: String
|
||||
private val CURRENT_COMPILER_VERSION: MavenComparableVersion
|
||||
|
||||
private val KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE: String
|
||||
private val KOTLIN_RUNTIME_COMPONENT_CORE: String
|
||||
private val KOTLIN_RUNTIME_COMPONENT_MAIN: String
|
||||
@@ -70,6 +79,18 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
|
||||
KOTLIN_VERSION_ATTRIBUTE = manifestProperties.getProperty(MANIFEST_KOTLIN_VERSION_ATTRIBUTE)
|
||||
.assertNotNull { "$MANIFEST_KOTLIN_VERSION_ATTRIBUTE not found in kotlinManifest.properties" }
|
||||
|
||||
CURRENT_COMPILER_VERSION = run {
|
||||
val kotlinVersionString = manifestProperties.getProperty(MANIFEST_KOTLIN_VERSION_VALUE)
|
||||
.assertNotNull { "$MANIFEST_KOTLIN_VERSION_VALUE not found in kotlinManifest.properties" }
|
||||
|
||||
MavenComparableVersion(kotlinVersionString)
|
||||
}
|
||||
|
||||
if (CURRENT_COMPILER_VERSION != ApiVersion.LATEST.version) {
|
||||
fatal("Kotlin compiler version $CURRENT_COMPILER_VERSION in kotlinManifest.properties doesn't match ${ApiVersion.LATEST}")
|
||||
}
|
||||
|
||||
KOTLIN_RUNTIME_COMPONENT_ATTRIBUTE = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT)
|
||||
.assertNotNull { "$MANIFEST_KOTLIN_RUNTIME_COMPONENT not found in kotlinManifest.properties" }
|
||||
KOTLIN_RUNTIME_COMPONENT_CORE = manifestProperties.getProperty(MANIFEST_KOTLIN_RUNTIME_COMPONENT_CORE)
|
||||
@@ -94,62 +115,29 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
|
||||
fun checkCompilerClasspathConsistency(
|
||||
messageCollector: MessageCollector,
|
||||
configuration: CompilerConfiguration,
|
||||
languageVersionSettings: LanguageVersionSettings?,
|
||||
classpathJarRoots: List<VirtualFile>
|
||||
) {
|
||||
val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots)
|
||||
if (runtimeJarsInfo.jars.isEmpty()) return
|
||||
|
||||
val languageVersionSettings = configuration.languageVersionSettings
|
||||
val apiVersion = languageVersionSettings.apiVersion.version
|
||||
val apiVersion = languageVersionSettings?.apiVersion?.version ?: CURRENT_COMPILER_VERSION
|
||||
|
||||
val consistency = checkCompilerClasspathConsistency(messageCollector, apiVersion, runtimeJarsInfo)
|
||||
if (consistency is ClasspathConsistency.InconsistentWithApiVersion) {
|
||||
val actualRuntimeVersion = consistency.actualRuntimeVersion
|
||||
messageCollector.issue(
|
||||
null,
|
||||
"Runtime JAR files in the classpath have the version $actualRuntimeVersion, " +
|
||||
if (consistency != ClasspathConsistency.Consistent) {
|
||||
val message = when (consistency) {
|
||||
is ClasspathConsistency.InconsistentWithApiVersion ->
|
||||
"Runtime JAR files in the classpath have the version ${consistency.actualRuntimeVersion}, " +
|
||||
"which is older than the API version $apiVersion. " +
|
||||
"Consider using the runtime of version $apiVersion, or pass '-api-version $actualRuntimeVersion' explicitly to " +
|
||||
"restrict the available APIs to the runtime of version $actualRuntimeVersion. " +
|
||||
"You can also pass '-language-version $actualRuntimeVersion' instead, which will restrict " +
|
||||
"Remove them from the classpath or pass '-api-version ${consistency.actualRuntimeVersion}' explicitly. " +
|
||||
"You can also pass '-language-version ${consistency.actualRuntimeVersion}' instead, which will restrict " +
|
||||
"not only the APIs to the specified version, but also the language features. " +
|
||||
"Alternatively, you can use '-Xskip-runtime-version-check' to suppress this warning"
|
||||
)
|
||||
|
||||
val actualApi = ApiVersion.parse(actualRuntimeVersion.toString())
|
||||
if (actualApi != null) {
|
||||
val inferredApiVersion =
|
||||
if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit)
|
||||
languageVersionSettings.apiVersion
|
||||
else
|
||||
minOf(languageVersionSettings.apiVersion, actualApi)
|
||||
|
||||
// "minOf" is needed in case when API version was inferred from language version and it's older than actualApi.
|
||||
// For example, in "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0
|
||||
val newSettings = LanguageVersionSettingsImpl(
|
||||
languageVersionSettings.languageVersion,
|
||||
inferredApiVersion,
|
||||
languageVersionSettings.additionalFeatures,
|
||||
isApiVersionExplicit = false
|
||||
)
|
||||
|
||||
messageCollector.issue(null, "Old runtime has been found in the classpath. " +
|
||||
"Initial language version settings: $languageVersionSettings. " +
|
||||
"Updated language version settings: $newSettings", CompilerMessageSeverity.LOGGING)
|
||||
|
||||
configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, newSettings)
|
||||
}
|
||||
else {
|
||||
messageCollector.issue(null, "Could not parse runtime JAR version: $actualRuntimeVersion")
|
||||
}
|
||||
}
|
||||
else if (consistency != ClasspathConsistency.Consistent) {
|
||||
messageCollector.issue(
|
||||
null,
|
||||
"Alternatively, you can use '-Xskip-runtime-version-check' to suppress this error"
|
||||
else ->
|
||||
"Some runtime JAR files in the classpath have an incompatible version. " +
|
||||
"Consider removing them from the classpath or use '-Xskip-runtime-version-check' to suppress this warning"
|
||||
)
|
||||
"Remove them from the classpath or use '-Xskip-runtime-version-check' to suppress errors"
|
||||
}
|
||||
messageCollector.issue(null, message)
|
||||
}
|
||||
|
||||
val librariesWithBundled = runtimeJarsInfo.otherLibrariesWithBundledRuntime
|
||||
@@ -158,11 +146,16 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
null,
|
||||
"Some JAR files in the classpath have the Kotlin Runtime library bundled into them. " +
|
||||
"This may cause difficult to debug problems if there's a different version of the Kotlin Runtime library in the classpath. " +
|
||||
"Consider removing these libraries from the classpath or use '-Xskip-runtime-version-check' to suppress this warning"
|
||||
"Consider removing these libraries from the classpath or use '-Xskip-runtime-version-check' to suppress this warning",
|
||||
CompilerMessageSeverity.STRONG_WARNING
|
||||
)
|
||||
|
||||
for (library in librariesWithBundled) {
|
||||
messageCollector.issue(library, "Library has Kotlin runtime bundled into it")
|
||||
messageCollector.issue(
|
||||
library,
|
||||
"Library has Kotlin runtime bundled into it",
|
||||
CompilerMessageSeverity.STRONG_WARNING
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,12 +194,8 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
}
|
||||
|
||||
private fun checkNotNewerThanCompiler(messageCollector: MessageCollector, jar: KotlinLibraryFile): Boolean {
|
||||
if (jar.version > ApiVersion.LATEST.version) {
|
||||
messageCollector.issue(
|
||||
jar.file,
|
||||
"Runtime JAR file has version ${jar.version} which is newer than compiler version ${ApiVersion.LATEST.version}",
|
||||
CompilerMessageSeverity.ERROR
|
||||
)
|
||||
if (jar.version > CURRENT_COMPILER_VERSION) {
|
||||
messageCollector.issue(jar.file, "Runtime JAR file has version ${jar.version} which is newer than compiler version $CURRENT_COMPILER_VERSION")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -216,10 +205,7 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
messageCollector: MessageCollector, jar: KotlinLibraryFile, apiVersion: MavenComparableVersion
|
||||
): Boolean {
|
||||
if (jar.version < apiVersion) {
|
||||
messageCollector.issue(
|
||||
jar.file,
|
||||
"Runtime JAR file has version ${jar.version} which is older than required for API version $apiVersion"
|
||||
)
|
||||
messageCollector.issue(jar.file, "Runtime JAR file has version ${jar.version} which is older than required for API version $apiVersion")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -238,34 +224,16 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
if (oldestVersion == newestVersion) return oldestVersion
|
||||
|
||||
messageCollector.issue(null, buildString {
|
||||
appendln("Runtime JAR files in the classpath should have the same version. These files were found in the classpath:")
|
||||
appendln("Runtime JAR files in the classpath must have the same version. These files were found in the classpath:")
|
||||
for (jar in jars) {
|
||||
appendln(" ${jar.file.path} (version ${jar.version})")
|
||||
}
|
||||
}.trimEnd())
|
||||
|
||||
// If there's kotlin-stdlib of version X in the classpath and kotlin-reflect of version Y < X,
|
||||
// we suggest to provide an explicit dependency on version X.
|
||||
// TODO: report this depending on the content of the jars instead
|
||||
val minReflectJar =
|
||||
jars.filter { it.file.name.startsWith("kotlin-reflect") }.minBy { it.version }
|
||||
val maxStdlibJar =
|
||||
jars.filter { it.file.name.startsWith("kotlin-runtime") || it.file.name.startsWith("kotlin-stdlib") }.maxBy { it.version }
|
||||
if (minReflectJar != null && maxStdlibJar != null && minReflectJar.version < maxStdlibJar.version) {
|
||||
messageCollector.issue(
|
||||
null,
|
||||
"Consider providing an explicit dependency on kotlin-reflect ${maxStdlibJar.version} to prevent strange errors"
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun MessageCollector.issue(
|
||||
file: VirtualFile?,
|
||||
message: String,
|
||||
severity: CompilerMessageSeverity = CompilerMessageSeverity.STRONG_WARNING
|
||||
) {
|
||||
private fun MessageCollector.issue(file: VirtualFile?, message: String, severity: CompilerMessageSeverity = VERSION_ISSUE_SEVERITY) {
|
||||
report(severity, message, CompilerMessageLocation.create(file?.let(VfsUtilCore::virtualToIoFile)?.path))
|
||||
}
|
||||
|
||||
@@ -274,15 +242,11 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
val coreJars = ArrayList<KotlinLibraryFile>(2)
|
||||
val otherLibrariesWithBundledRuntime = ArrayList<VirtualFile>(0)
|
||||
|
||||
val visitedPaths = hashSetOf<String>()
|
||||
|
||||
for (jarRoot in classpathJarRoots) {
|
||||
val fileKind = determineFileKind(jarRoot)
|
||||
if (fileKind is FileKind.Irrelevant) continue
|
||||
|
||||
val jarFile = VfsUtilCore.getVirtualFileForJar(jarRoot) ?: continue
|
||||
if (!visitedPaths.add(jarFile.path)) continue
|
||||
|
||||
when (fileKind) {
|
||||
is FileKind.Runtime -> {
|
||||
val file = KotlinLibraryFile(jarFile, fileKind.version)
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.compiler.plugin.PluginCliOptionProcessingException
|
||||
import org.jetbrains.kotlin.compiler.plugin.cliPluginUsageString
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
||||
@@ -113,6 +114,10 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
return ExitCode.OK
|
||||
}
|
||||
|
||||
if (arguments.skipMetadataVersionCheck) {
|
||||
JvmMetadataVersion.skipCheck = true
|
||||
}
|
||||
|
||||
if (arguments.includeRuntime) {
|
||||
configuration.put(JVMConfigurationKeys.INCLUDE_RUNTIME, true)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
||||
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
|
||||
import java.io.EOFException
|
||||
|
||||
class JvmPackagePartProvider(
|
||||
@@ -33,8 +32,6 @@ class JvmPackagePartProvider(
|
||||
) : PackagePartProvider {
|
||||
private data class ModuleMappingInfo(val root: VirtualFile, val mapping: ModuleMapping)
|
||||
|
||||
private val deserializationConfiguration = CompilerDeserializationConfiguration(env.configuration)
|
||||
|
||||
private val notLoadedRoots by lazy(LazyThreadSafetyMode.NONE) {
|
||||
env.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS)
|
||||
.filterIsInstance<JvmClasspathRoot>()
|
||||
@@ -99,7 +96,7 @@ class JvmPackagePartProvider(
|
||||
val moduleFiles = metaInf.children.filter { it.name.endsWith(ModuleMapping.MAPPING_FILE_EXT) }
|
||||
for (moduleFile in moduleFiles) {
|
||||
val mapping = try {
|
||||
ModuleMapping.create(moduleFile.contentsToByteArray(), moduleFile.toString(), deserializationConfiguration)
|
||||
ModuleMapping.create(moduleFile.contentsToByteArray(), moduleFile.toString())
|
||||
}
|
||||
catch (e: EOFException) {
|
||||
throw RuntimeException("Error on reading package parts for '$packageFqName' package in '$moduleFile', " +
|
||||
|
||||
@@ -67,8 +67,7 @@ import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
|
||||
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot
|
||||
@@ -160,7 +159,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
if (messageCollector != null) {
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS),
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,62 +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.cli.jvm.repl
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
|
||||
class ReplCompilerStageHistory(private val state: GenericReplCompilerState) : BasicReplStageHistory<ScriptDescriptor>(state.lock) {
|
||||
|
||||
override fun resetTo(id: ILineId): Iterable<ILineId> {
|
||||
val removedCompiledLines = super.resetTo(id)
|
||||
val removedAnalyzedLines = state.analyzerEngine.resetToLine(id.no)
|
||||
|
||||
removedCompiledLines.zip(removedAnalyzedLines).forEach {
|
||||
if (it.first != LineId(it.second)) {
|
||||
throw IllegalStateException("History mismatch when resetting lines: ${it.first.no} != ${it.second}")
|
||||
}
|
||||
}
|
||||
return removedCompiledLines
|
||||
}
|
||||
}
|
||||
|
||||
abstract class GenericReplCheckerState: IReplStageState<ScriptDescriptor> {
|
||||
|
||||
// "line" - is the unit of evaluation here, could in fact consists of several character lines
|
||||
class LineState(
|
||||
val codeLine: ReplCodeLine,
|
||||
val psiFile: KtFile,
|
||||
val errorHolder: DiagnosticMessageHolder)
|
||||
|
||||
var lastLineState: LineState? = null // for transferring state to the compiler in most typical case
|
||||
}
|
||||
|
||||
class GenericReplCompilerState(environment: KotlinCoreEnvironment, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<ScriptDescriptor>, GenericReplCheckerState() {
|
||||
|
||||
override val history = ReplCompilerStageHistory(this)
|
||||
|
||||
override val currentGeneration: Int get() = (history as BasicReplStageHistory<*>).currentGeneration.get()
|
||||
|
||||
val analyzerEngine = ReplCodeAnalyzer(environment)
|
||||
|
||||
var lastDependencies: KotlinScriptExternalDependencies? = null
|
||||
}
|
||||
@@ -22,31 +22,66 @@ import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import java.io.File
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
|
||||
// TODO: remove if unused after switching old repl to the new infrastruct
|
||||
open class GenericRepl protected constructor(
|
||||
disposable: Disposable,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector,
|
||||
baseClassloader: ClassLoader?,
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes? = null,
|
||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.NONE
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes?,
|
||||
protected val repeatingMode: ReplRepeatingMode = ReplRepeatingMode.NONE,
|
||||
protected val stateLock: ReentrantReadWriteLock
|
||||
) : ReplCompiler, ReplEvaluator, ReplAtomicEvaluator {
|
||||
constructor (
|
||||
disposable: Disposable,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector,
|
||||
baseClassloader: ClassLoader?,
|
||||
fallbackScriptArgs: ScriptArgsWithTypes? = null
|
||||
) : this(disposable, scriptDefinition, compilerConfiguration, messageCollector, baseClassloader, fallbackScriptArgs, stateLock = ReentrantReadWriteLock())
|
||||
|
||||
protected val compiler: ReplCompiler by lazy { GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector) }
|
||||
protected val evaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(compiler, compilerConfiguration.jvmClasspathRoots, baseClassloader, fallbackScriptArgs, repeatingMode) }
|
||||
protected val compiler: ReplCompiler by lazy { GenericReplCompiler(disposable, scriptDefinition, compilerConfiguration, messageCollector, stateLock) }
|
||||
protected val evaluator: ReplFullEvaluator by lazy { GenericReplCompilingEvaluator(compiler, compilerConfiguration.jvmClasspathRoots, baseClassloader, fallbackScriptArgs, repeatingMode, stateLock) }
|
||||
protected var codeLineNumber = AtomicInteger(0)
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = AggregatedReplStageState(compiler.createState(lock), evaluator.createState(lock), lock)
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return evaluator.resetToLine(lineNumber)
|
||||
}
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = compiler.check(state, codeLine)
|
||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> {
|
||||
return evaluator.resetToLine(line)
|
||||
}
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult = compiler.compile(state, codeLine)
|
||||
override val history: List<ReplCodeLine> get() = evaluator.history
|
||||
override val compiledHistory: List<ReplCodeLine> get() = compiler.history
|
||||
override val evaluatedHistory: List<ReplCodeLine> get() = evaluator.history
|
||||
|
||||
override fun eval(state: IReplStageState<*>, compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
evaluator.eval(state, compileResult, scriptArgs, invokeWrapper)
|
||||
override val currentClasspath: List<File>
|
||||
get() = evaluator.currentClasspath
|
||||
|
||||
override fun compileAndEval(state: IReplStageState<*>, codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult =
|
||||
evaluator.compileAndEval(state, codeLine, scriptArgs, invokeWrapper)
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType>
|
||||
get() = evaluator.lastEvaluatedScripts
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compiler.check(codeLine)
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
return compiler.compile(codeLine, verifyHistory)
|
||||
}
|
||||
|
||||
override fun eval(compileResult: ReplCompileResult.CompiledClasses, scriptArgs: ScriptArgsWithTypes?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return evaluator.eval(compileResult, scriptArgs, invokeWrapper)
|
||||
}
|
||||
|
||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return evaluator.compileAndEval(codeLine, scriptArgs, verifyHistory, invokeWrapper)
|
||||
}
|
||||
|
||||
fun nextCodeLine(code: String) = ReplCodeLine(codeLineNumber.incrementAndGet(), code)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package org.jetbrains.kotlin.cli.jvm.repl
|
||||
import org.jetbrains.kotlin.cli.common.repl.CompiledReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplHistory
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplResettableCodeLine
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
@@ -42,18 +43,23 @@ import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.replaceImportingScopes
|
||||
import org.jetbrains.kotlin.script.ScriptPriorities
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
|
||||
class GenericReplAnalyzer(environment: KotlinCoreEnvironment,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplResettableCodeLine {
|
||||
private val topDownAnalysisContext: TopDownAnalysisContext
|
||||
private val topDownAnalyzer: LazyTopDownAnalyzer
|
||||
private val resolveSession: ResolveSession
|
||||
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
|
||||
private val replState = ResettableAnalyzerState()
|
||||
private val replState = ResettableReplState()
|
||||
|
||||
val module: ModuleDescriptorImpl
|
||||
get() = stateLock.read { field }
|
||||
|
||||
val trace: BindingTraceContext = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
|
||||
get() = stateLock.read { field }
|
||||
|
||||
init {
|
||||
// Module source scope is empty because all binary classes are in the dependency module, and all source classes are guaranteed
|
||||
@@ -88,15 +94,21 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
}
|
||||
|
||||
fun resetToLine(lineNumber: Int): List<ReplCodeLine> = replState.resetToLine(lineNumber)
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return stateLock.write { replState.resetToLine(lineNumber) }
|
||||
}
|
||||
|
||||
override fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
|
||||
fun analyzeReplLine(psiFile: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
return stateLock.write {
|
||||
topDownAnalysisContext.scripts.clear()
|
||||
trace.clearDiagnostics()
|
||||
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, codeLine.no)
|
||||
|
||||
return doAnalyze(psiFile, codeLine)
|
||||
doAnalyze(psiFile, codeLine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doAnalyze(linePsi: KtFile, codeLine: ReplCodeLine): ReplLineAnalysisResult {
|
||||
@@ -167,8 +179,7 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
}
|
||||
|
||||
// TODO: merge with org.jetbrains.kotlin.resolve.repl.ReplState when switching to new REPL infrastruct everywhere
|
||||
// TODO: review it's place in the extracted state infrastruct (now the analyzer itself is a part of the state
|
||||
class ResettableAnalyzerState {
|
||||
class ResettableReplState {
|
||||
private val successfulLines = ReplHistory<LineInfo.SuccessfulLine>()
|
||||
private val submittedLines = hashMapOf<KtFile, LineInfo>()
|
||||
|
||||
@@ -178,6 +189,8 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
return removed.map { it.first }
|
||||
}
|
||||
|
||||
fun resetToLine(line: ReplCodeLine): List<ReplCodeLine> = resetToLine(line.no)
|
||||
|
||||
fun submitLine(ktFile: KtFile, codeLine: ReplCodeLine) {
|
||||
val line = LineInfo.SubmittedLine(ktFile, successfulLines.lastValue())
|
||||
submittedLines[ktFile] = line
|
||||
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.cli.jvm.repl
|
||||
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||
@@ -26,9 +25,12 @@ import com.intellij.testFramework.LightVirtualFile
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCheckResult
|
||||
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
|
||||
import org.jetbrains.kotlin.cli.common.repl.makeScriptBaseName
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplTerminalDiagnosticMessageHolder
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
@@ -37,6 +39,8 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
const val KOTLIN_REPL_JVM_TARGET_PROPERTY = "kotlin.repl.jvm.target"
|
||||
@@ -45,9 +49,9 @@ open class GenericReplChecker(
|
||||
disposable: Disposable,
|
||||
val scriptDefinition: KotlinScriptDefinition,
|
||||
val compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : ReplCheckAction {
|
||||
|
||||
messageCollector: MessageCollector,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()
|
||||
) {
|
||||
internal val environment = run {
|
||||
compilerConfiguration.apply {
|
||||
add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||
@@ -65,14 +69,23 @@ open class GenericReplChecker(
|
||||
|
||||
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
|
||||
internal fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
|
||||
// "line" - is the unit of evaluation here, could in fact consists of several character lines
|
||||
internal class LineState(
|
||||
val codeLine: ReplCodeLine,
|
||||
val psiFile: KtFile,
|
||||
val errorHolder: DiagnosticMessageHolder)
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
||||
state.lock.write {
|
||||
val checkerState = state.asState(GenericReplCheckerState::class.java)
|
||||
val scriptFileName = makeScriptBaseName(codeLine)
|
||||
private var _lineState: LineState? = null
|
||||
|
||||
internal val lineState: LineState? get() = stateLock.read { _lineState }
|
||||
|
||||
fun createDiagnosticHolder() = ReplTerminalDiagnosticMessageHolder()
|
||||
|
||||
fun check(codeLine: ReplCodeLine, generation: Long): ReplCheckResult {
|
||||
stateLock.write {
|
||||
val scriptFileName = makeScriptBaseName(codeLine, generation)
|
||||
val virtualFile =
|
||||
LightVirtualFile("$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, StringUtil.convertLineSeparators(codeLine.code)).apply {
|
||||
LightVirtualFile("$scriptFileName${KotlinParserDefinition.STD_SCRIPT_EXT}", KotlinLanguage.INSTANCE, codeLine.code).apply {
|
||||
charset = CharsetToolkit.UTF8_CHARSET
|
||||
}
|
||||
val psiFile: KtFile = psiFileFactory.trySetupPsiForFile(virtualFile, KotlinLanguage.INSTANCE, true, false) as KtFile?
|
||||
@@ -83,7 +96,7 @@ open class GenericReplChecker(
|
||||
val syntaxErrorReport = AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorHolder)
|
||||
|
||||
if (!syntaxErrorReport.isHasErrors) {
|
||||
checkerState.lastLineState = GenericReplCheckerState.LineState(codeLine, psiFile, errorHolder)
|
||||
_lineState = LineState(codeLine, psiFile, errorHolder)
|
||||
}
|
||||
|
||||
return when {
|
||||
|
||||
@@ -26,82 +26,115 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.KotlinScriptExternalDependencies
|
||||
import java.io.File
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
// WARNING: not thread safe, assuming external synchronization
|
||||
|
||||
open class GenericReplCompiler(disposable: Disposable,
|
||||
protected val scriptDefinition: KotlinScriptDefinition,
|
||||
protected val compilerConfiguration: CompilerConfiguration,
|
||||
messageCollector: MessageCollector
|
||||
) : ReplCompiler {
|
||||
messageCollector: MessageCollector,
|
||||
protected val stateLock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : ReplCompiler {
|
||||
private val checker = GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector, stateLock)
|
||||
|
||||
private val checker = GenericReplChecker(disposable, scriptDefinition, compilerConfiguration, messageCollector)
|
||||
private val analyzerEngine = GenericReplAnalyzer(checker.environment, stateLock)
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> = GenericReplCompilerState(checker.environment, lock)
|
||||
private var lastDependencies: KotlinScriptExternalDependencies? = null
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult = checker.check(state, codeLine)
|
||||
private val descriptorsHistory = ReplHistory<ScriptDescriptor>()
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
|
||||
state.lock.write {
|
||||
val compilerState = state.asState(GenericReplCompilerState::class.java)
|
||||
private val generation = AtomicLong(1)
|
||||
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return stateLock.write {
|
||||
generation.incrementAndGet()
|
||||
val removedCompiledLines = descriptorsHistory.resetToLine(lineNumber)
|
||||
val removedAnalyzedLines = analyzerEngine.resetToLine(lineNumber)
|
||||
|
||||
removedCompiledLines.zip(removedAnalyzedLines).forEach {
|
||||
if (it.first.first != it.second) {
|
||||
throw IllegalStateException("History mismatch when resetting lines")
|
||||
}
|
||||
}
|
||||
|
||||
removedCompiledLines
|
||||
}.map { it.first }
|
||||
}
|
||||
|
||||
override val history: List<ReplCodeLine> get() = stateLock.read { descriptorsHistory.copySources() }
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult = stateLock.read {
|
||||
return checker.check(codeLine, generation.get())
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
stateLock.write {
|
||||
val firstMismatch = descriptorsHistory.firstMismatchingHistory(verifyHistory)
|
||||
if (firstMismatch != null) {
|
||||
return@compile ReplCompileResult.HistoryMismatch(descriptorsHistory.copySources(), firstMismatch)
|
||||
}
|
||||
|
||||
val currentGeneration = generation.get()
|
||||
|
||||
val (psiFile, errorHolder) = run {
|
||||
if (compilerState.lastLineState == null || compilerState.lastLineState!!.codeLine != codeLine) {
|
||||
val res = checker.check(state, codeLine)
|
||||
if (checker.lineState == null || checker.lineState!!.codeLine != codeLine) {
|
||||
val res = checker.check(codeLine, currentGeneration)
|
||||
when (res) {
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete()
|
||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(res.message, res.location)
|
||||
is ReplCheckResult.Incomplete -> return@compile ReplCompileResult.Incomplete(descriptorsHistory.copySources())
|
||||
is ReplCheckResult.Error -> return@compile ReplCompileResult.Error(descriptorsHistory.copySources(), res.message, res.location)
|
||||
is ReplCheckResult.Ok -> {} // continue
|
||||
}
|
||||
}
|
||||
Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder)
|
||||
Pair(checker.lineState!!.psiFile, checker.lineState!!.errorHolder)
|
||||
}
|
||||
|
||||
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, compilerState.lastDependencies)
|
||||
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, lastDependencies)
|
||||
var classpathAddendum: List<File>? = null
|
||||
if (compilerState.lastDependencies != newDependencies) {
|
||||
compilerState.lastDependencies = newDependencies
|
||||
if (lastDependencies != newDependencies) {
|
||||
lastDependencies = newDependencies
|
||||
classpathAddendum = newDependencies?.let { checker.environment.updateClasspath(it.classpath.map(::JvmClasspathRoot)) }
|
||||
}
|
||||
|
||||
val analysisResult = compilerState.analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, codeLine)
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(errorHolder.renderedDiagnostics)
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.WithErrors -> return ReplCompileResult.Error(descriptorsHistory.copySources(), errorHolder.renderedDiagnostics)
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||
}
|
||||
|
||||
val generationState = GenerationState(
|
||||
val state = GenerationState(
|
||||
psiFile.project,
|
||||
ClassBuilderFactories.binaries(false),
|
||||
compilerState.analyzerEngine.module,
|
||||
compilerState.analyzerEngine.trace.bindingContext,
|
||||
analyzerEngine.module,
|
||||
analyzerEngine.trace.bindingContext,
|
||||
listOf(psiFile),
|
||||
compilerConfiguration
|
||||
)
|
||||
generationState.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
|
||||
generationState.replSpecific.earlierScriptsForReplInterpreter = compilerState.history.map { it.item }
|
||||
generationState.beforeCompile()
|
||||
state.replSpecific.scriptResultFieldName = SCRIPT_RESULT_FIELD_NAME
|
||||
state.replSpecific.earlierScriptsForReplInterpreter = descriptorsHistory.copyValues()
|
||||
state.beforeCompile()
|
||||
KotlinCodegenFacade.generatePackage(
|
||||
generationState,
|
||||
psiFile.script!!.containingKtFile.packageFqName,
|
||||
setOf(psiFile.script!!.containingKtFile),
|
||||
state,
|
||||
psiFile.script!!.getContainingKtFile().packageFqName,
|
||||
setOf(psiFile.script!!.getContainingKtFile()),
|
||||
org.jetbrains.kotlin.codegen.CompilationErrorHandler.THROW_EXCEPTION)
|
||||
|
||||
val generatedClassname = makeScriptBaseName(codeLine)
|
||||
compilerState.history.push(LineId(codeLine), scriptDescriptor)
|
||||
val generatedClassname = makeScriptBaseName(codeLine, currentGeneration)
|
||||
val compiledCodeLine = CompiledReplCodeLine(generatedClassname, codeLine)
|
||||
descriptorsHistory.add(compiledCodeLine, scriptDescriptor)
|
||||
|
||||
return ReplCompileResult.CompiledClasses(LineId(codeLine),
|
||||
compilerState.history.map { it.id },
|
||||
return ReplCompileResult.CompiledClasses(descriptorsHistory.copySources(),
|
||||
compiledCodeLine,
|
||||
generatedClassname,
|
||||
generationState.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
||||
generationState.replSpecific.hasResult,
|
||||
classpathAddendum ?: emptyList())
|
||||
state.factory.asList().map { CompiledClassData(it.relativePath, it.asByteArray()) },
|
||||
state.replSpecific.hasResult,
|
||||
classpathAddendum ?: emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class ReplInterpreter(
|
||||
}
|
||||
|
||||
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
|
||||
private val analyzerEngine = ReplCodeAnalyzer(environment)
|
||||
private val analyzerEngine = GenericReplAnalyzer(environment)
|
||||
|
||||
fun eval(line: String): LineResult {
|
||||
++lineNumber
|
||||
@@ -98,11 +98,11 @@ class ReplInterpreter(
|
||||
return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||
}
|
||||
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, 0, "fake line"))
|
||||
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, ReplCodeLine(lineNumber, "fake line"))
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder)
|
||||
val scriptDescriptor = when (analysisResult) {
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||
is ReplCodeAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.WithErrors -> return LineResult.Error.CompileTime(errorHolder.renderedDiagnostics)
|
||||
is GenericReplAnalyzer.ReplLineAnalysisResult.Successful -> analysisResult.scriptDescriptor
|
||||
else -> error("Unexpected result ${analysisResult.javaClass}")
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ class ReplInterpreter(
|
||||
private fun renderStackTrace(cause: Throwable, startFromMethodName: String): String {
|
||||
val newTrace = arrayListOf<StackTraceElement>()
|
||||
var skip = true
|
||||
for ((_, element) in cause.stackTrace.withIndex().reversed()) {
|
||||
for ((i, element) in cause.stackTrace.withIndex().reversed()) {
|
||||
if ("${element.className}.${element.methodName}" == startFromMethodName) {
|
||||
skip = false
|
||||
}
|
||||
@@ -214,8 +214,8 @@ class ReplInterpreter(
|
||||
state.beforeCompile()
|
||||
KotlinCodegenFacade.generatePackage(
|
||||
state,
|
||||
script.containingKtFile.packageFqName,
|
||||
setOf(script.containingKtFile),
|
||||
script.getContainingKtFile().packageFqName,
|
||||
setOf(script.getContainingKtFile()),
|
||||
errorHandler
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="daemon-common" scope="PROVIDED" />
|
||||
<orderEntry type="module" module-name="util" scope="PROVIDED" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="native-platform-uberjar" level="project" />
|
||||
<orderEntry type="module" module-name="cli-common" scope="PROVIDED" />
|
||||
<orderEntry type="module" module-name="descriptors" scope="PROVIDED" />
|
||||
<orderEntry type="module" module-name="daemon-common" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="library" name="native-platform-uberjar" level="project" />
|
||||
<orderEntry type="module" module-name="cli-common" />
|
||||
<orderEntry type="module" module-name="descriptors" />
|
||||
<orderEntry type="library" name="intellij-core" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.client
|
||||
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacade
|
||||
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
|
||||
import org.jetbrains.kotlin.daemon.common.RmiFriendlyCompilationCanceledException
|
||||
@@ -26,9 +27,7 @@ import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompil
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.utils.rethrow
|
||||
import java.rmi.server.UnicastRemoteObject
|
||||
import kotlin.reflect.full.allSuperclasses
|
||||
|
||||
|
||||
open class CompilerCallbackServicesFacadeServer(
|
||||
@@ -80,17 +79,14 @@ open class CompilerCallbackServicesFacadeServer(
|
||||
|
||||
override fun lookupTracker_isDoNothing(): Boolean = lookupTracker_isDoNothing
|
||||
|
||||
override fun compilationCanceledStatus_checkCanceled(): Void? {
|
||||
override fun compilationCanceledStatus_checkCanceled() {
|
||||
try {
|
||||
compilationCanceledStatus!!.checkCanceled()
|
||||
return null
|
||||
}
|
||||
catch (e: Exception) {
|
||||
catch (e: ProcessCanceledException) {
|
||||
// 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::class.allSuperclasses + e::class).any { it.qualifiedName == "com.intellij.openapi.progress.ProcessCanceledException" })
|
||||
throw RmiFriendlyCompilationCanceledException()
|
||||
else throw e
|
||||
// TODO: doublecheck whether we need to distinguish different cancellation exceptions
|
||||
throw RmiFriendlyCompilationCanceledException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
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
|
||||
@@ -61,9 +57,9 @@ object KotlinCompilerClient {
|
||||
): CompileService? {
|
||||
val flagFile = System.getProperty(COMPILE_DAEMON_CLIENT_ALIVE_PATH_PROPERTY)
|
||||
?.let(String::trimQuotes)
|
||||
?.check { !it.isBlank() }
|
||||
?.takeIfNot(String::isBlank)
|
||||
?.let(::File)
|
||||
?.check(File::exists)
|
||||
?.takeIf(File::exists)
|
||||
?: makeAutodeletingFlagFile(baseDir = File(daemonOptions.runFilesPathOrDefault))
|
||||
return connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, reportingTargets, autostart)
|
||||
}
|
||||
@@ -74,49 +70,32 @@ object KotlinCompilerClient {
|
||||
daemonOptions: DaemonOptions,
|
||||
reportingTargets: DaemonReportingTargets,
|
||||
autostart: Boolean = true
|
||||
): CompileService? =
|
||||
connectAndLease(compilerId,
|
||||
clientAliveFlagFile,
|
||||
daemonJVMOptions,
|
||||
daemonOptions,
|
||||
reportingTargets,
|
||||
autostart,
|
||||
leaseSession = false,
|
||||
sessionAliveFlagFile = null)?.first
|
||||
): CompileService? {
|
||||
|
||||
|
||||
fun connectAndLease(compilerId: CompilerId,
|
||||
clientAliveFlagFile: File,
|
||||
daemonJVMOptions: DaemonJVMOptions,
|
||||
daemonOptions: DaemonOptions,
|
||||
reportingTargets: DaemonReportingTargets,
|
||||
autostart: Boolean,
|
||||
leaseSession: Boolean,
|
||||
sessionAliveFlagFile: File? = null
|
||||
): Pair<CompileService, Int>? = connectLoop(reportingTargets) {
|
||||
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(File(daemonOptions.runFilesPath), compilerId, daemonJVMOptions, { cat, msg -> reportingTargets.report(cat, msg) })
|
||||
if (service != null) {
|
||||
// the newJVMOptions could be checked here for additional parameters, if needed
|
||||
service.registerClient(clientAliveFlagFile.absolutePath)
|
||||
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
|
||||
if (!leaseSession) service to CompileService.NO_SESSION
|
||||
else {
|
||||
val sessionId = service.leaseCompileSession(sessionAliveFlagFile?.absolutePath)
|
||||
if (sessionId is CompileService.CallResult.Dying)
|
||||
null
|
||||
else
|
||||
service to sessionId.get()
|
||||
var attempts = 0
|
||||
try {
|
||||
while (attempts++ < DAEMON_CONNECT_CYCLE_ATTEMPTS) {
|
||||
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts(File(daemonOptions.runFilesPath), compilerId, daemonJVMOptions, { cat, msg -> reportingTargets.report(cat, msg) })
|
||||
if (service != null) {
|
||||
// the newJVMOptions could be checked here for additional parameters, if needed
|
||||
service.registerClient(clientAliveFlagFile.absolutePath)
|
||||
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
|
||||
return service
|
||||
}
|
||||
reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found")
|
||||
if (autostart) {
|
||||
startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)
|
||||
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reportingTargets.report(DaemonReportCategory.DEBUG, "no suitable daemon found")
|
||||
if (autostart) {
|
||||
startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)
|
||||
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
|
||||
}
|
||||
null
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString())
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
fun shutdownCompileService(compilerId: CompilerId, daemonOptions: DaemonOptions): Unit {
|
||||
connectToCompileService(compilerId, DaemonJVMOptions(), daemonOptions, DaemonReportingTargets(out = System.out), autostart = false, checkId = false)
|
||||
?.shutdown()
|
||||
@@ -128,13 +107,6 @@ object KotlinCompilerClient {
|
||||
}
|
||||
|
||||
|
||||
fun leaseCompileSession(compilerService: CompileService, aliveFlagPath: String?): Int =
|
||||
compilerService.leaseCompileSession(aliveFlagPath).get()
|
||||
|
||||
fun releaseCompileSession(compilerService: CompileService, sessionId: Int): Unit {
|
||||
compilerService.releaseCompileSession(sessionId)
|
||||
}
|
||||
|
||||
fun compile(compilerService: CompileService,
|
||||
sessionId: Int,
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
@@ -298,33 +270,6 @@ object KotlinCompilerClient {
|
||||
|
||||
// --- Implementation ---------------------------------------
|
||||
|
||||
private inline fun <R> connectLoop(reportingTargets: DaemonReportingTargets, body: () -> R?): R? {
|
||||
try {
|
||||
var attempts = 0
|
||||
while (attempts < DAEMON_CONNECT_CYCLE_ATTEMPTS) {
|
||||
attempts += 1
|
||||
val (res, err) = try {
|
||||
body() 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 }
|
||||
|
||||
when {
|
||||
res != null -> return res
|
||||
err != null && attempts >= DAEMON_CONNECT_CYCLE_ATTEMPTS -> throw err
|
||||
err != null ->
|
||||
reportingTargets.report(DaemonReportCategory.INFO, "retrying($attempts) on: " + err.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
reportingTargets.report(DaemonReportCategory.EXCEPTION, e.toString())
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun DaemonReportingTargets.report(category: DaemonReportCategory, message: String, source: String = "daemon client") {
|
||||
if (category == DaemonReportCategory.DEBUG && !verboseReporting) return
|
||||
out?.println("[$source] ${category.name}: $message")
|
||||
@@ -346,33 +291,23 @@ object KotlinCompilerClient {
|
||||
}
|
||||
|
||||
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 = compareByDescending<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions })
|
||||
.thenBy(FileAgeComparator()) { it.runFile }
|
||||
val aliveWithOpts = walkDaemons(registryDir, compilerId, report = report)
|
||||
.map { Pair(it, it.getDaemonJVMOptions()) }
|
||||
.filter { it.second.isGood }
|
||||
.sortedWith(compareByDescending(DaemonJVMOptionsMemoryComparator(), { it.second.get() }))
|
||||
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))
|
||||
return aliveWithOpts.firstOrNull()?.check { daemonJVMOptions memorywiseFitsInto it.second.get() }?.let {
|
||||
Pair(it.first, optsCopy.updateMemoryUpperBounds(it.second.get()))
|
||||
}
|
||||
// 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) }))
|
||||
?: Pair(null, aliveWithOpts.fold(optsCopy, { opts, d -> opts.updateMemoryUpperBounds(d.second.get()) }))
|
||||
}
|
||||
|
||||
|
||||
private fun startDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, reportingTargets: DaemonReportingTargets) {
|
||||
val javaExecutable = File(File(System.getProperty("java.home"), "bin"), "java")
|
||||
val platformSpecificOptions = listOf(
|
||||
// hide daemon window
|
||||
"-Djava.awt.headless=true",
|
||||
// prevent host name resolution
|
||||
"-Djava.rmi.server.hostname=${LoopbackNetworkInterface.loopbackInetAddressName}")
|
||||
val platformSpecificOptions = listOf("-Djava.awt.headless=true") // hide daemon window
|
||||
val args = listOf(
|
||||
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
|
||||
platformSpecificOptions +
|
||||
@@ -396,7 +331,7 @@ object KotlinCompilerClient {
|
||||
daemon.inputStream
|
||||
.reader()
|
||||
.forEachLine {
|
||||
if (it == COMPILE_DAEMON_IS_READY_MESSAGE) {
|
||||
if (daemonOptions.runFilesPath.isNotEmpty() && it.contains(daemonOptions.runFilesPath)) {
|
||||
isEchoRead.release()
|
||||
return@forEachLine
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.daemon.client
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.daemon.common.CompileService
|
||||
import org.jetbrains.kotlin.daemon.common.RemoteOperationsTracer
|
||||
import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
// TODO: reduce number of ports used then SOCKET_ANY_FREE_PORT is passed (same problem with other calls)
|
||||
|
||||
open class KotlinRemoteReplClientBase(
|
||||
disposable: Disposable,
|
||||
protected val compileService: CompileService,
|
||||
clientAliveFlagFile: File?,
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<out Any?>? = null,
|
||||
scriptArgsTypes: Array<out Class<out Any>>? = null,
|
||||
compilerMessagesOutputStream: OutputStream = System.err,
|
||||
evalOutputStream: OutputStream? = null,
|
||||
evalErrorStream: OutputStream? = null,
|
||||
evalInputStream: InputStream? = null,
|
||||
port: Int = SOCKET_ANY_FREE_PORT,
|
||||
operationsTracer: RemoteOperationsTracer? = null
|
||||
) {
|
||||
|
||||
val sessionId = compileService.leaseReplSession(
|
||||
clientAliveFlagFile?.absolutePath,
|
||||
targetPlatform,
|
||||
CompilerCallbackServicesFacadeServer(port = port),
|
||||
templateClasspath,
|
||||
templateClassName,
|
||||
scriptArgs,
|
||||
scriptArgsTypes,
|
||||
RemoteOutputStreamServer(compilerMessagesOutputStream, port),
|
||||
evalOutputStream?.let { RemoteOutputStreamServer(it, port) },
|
||||
evalErrorStream?.let { RemoteOutputStreamServer(it, port) },
|
||||
evalInputStream?.let { RemoteInputStreamServer(it, port) },
|
||||
operationsTracer
|
||||
).get()
|
||||
|
||||
init {
|
||||
Disposer.register(disposable, Disposable {
|
||||
try {
|
||||
compileService.releaseReplSession(sessionId)
|
||||
}
|
||||
catch (ex: java.rmi.RemoteException) {
|
||||
// assuming that communication failed and daemon most likely is already down
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinRemoteReplCompiler(
|
||||
disposable: Disposable,
|
||||
compileService: CompileService,
|
||||
clientAliveFlagFile: File?,
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
compilerMessagesOutputStream: OutputStream,
|
||||
port: Int = SOCKET_ANY_FREE_PORT,
|
||||
operationsTracer: RemoteOperationsTracer? = null
|
||||
) : KotlinRemoteReplClientBase(
|
||||
disposable = disposable,
|
||||
compileService = compileService,
|
||||
clientAliveFlagFile = clientAliveFlagFile,
|
||||
targetPlatform = targetPlatform,
|
||||
templateClasspath = templateClasspath,
|
||||
templateClassName = templateClassName,
|
||||
compilerMessagesOutputStream = compilerMessagesOutputStream,
|
||||
evalOutputStream = null,
|
||||
evalErrorStream = null,
|
||||
evalInputStream = null,
|
||||
port = port,
|
||||
operationsTracer = operationsTracer
|
||||
), ReplCompiler, ReplCheckAction {
|
||||
override fun resetToLine(lineNumber: Int): List<ReplCodeLine> {
|
||||
return emptyList() // TODO: not implemented, no current need
|
||||
}
|
||||
|
||||
override val history: List<ReplCodeLine>
|
||||
get() = emptyList() // TODO: not implemented, no current need
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compileService.remoteReplLineCheck(sessionId, codeLine).get()
|
||||
}
|
||||
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
return compileService.remoteReplLineCompile(sessionId, codeLine, verifyHistory).get()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider removing daemon eval completely - it is not required now and has questionable security. This will simplify daemon interface as well
|
||||
class KotlinRemoteReplEvaluator(
|
||||
disposable: Disposable,
|
||||
compileService: CompileService,
|
||||
clientAliveFlagFile: File?,
|
||||
targetPlatform: CompileService.TargetPlatform,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
scriptArgs: Array<out Any?>? = null,
|
||||
scriptArgsTypes: Array<out Class<out Any>>? = null,
|
||||
compilerMessagesOutputStream: OutputStream,
|
||||
evalOutputStream: OutputStream?,
|
||||
evalErrorStream: OutputStream?,
|
||||
evalInputStream: InputStream?,
|
||||
port: Int = SOCKET_ANY_FREE_PORT,
|
||||
operationsTracer: RemoteOperationsTracer? = null
|
||||
) : KotlinRemoteReplClientBase(
|
||||
disposable = disposable,
|
||||
compileService = compileService,
|
||||
clientAliveFlagFile = clientAliveFlagFile,
|
||||
targetPlatform = targetPlatform,
|
||||
templateClasspath = templateClasspath,
|
||||
templateClassName = templateClassName,
|
||||
scriptArgs = scriptArgs,
|
||||
scriptArgsTypes = scriptArgsTypes,
|
||||
compilerMessagesOutputStream = compilerMessagesOutputStream,
|
||||
evalOutputStream = evalOutputStream,
|
||||
evalErrorStream = evalErrorStream,
|
||||
evalInputStream = evalInputStream,
|
||||
port = port,
|
||||
operationsTracer = operationsTracer
|
||||
), ReplAtomicEvalAction, ReplEvaluatorExposedInternalHistory, ReplCheckAction {
|
||||
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> = emptyList() // not implemented, no need so far
|
||||
|
||||
// TODO: invokeWrapper is ignored here, and in the daemon the session wrapper is used instead; So consider to make it per call (avoid performance penalties though)
|
||||
// TODO: scriptArgs are ignored here, they should be passed through
|
||||
override fun compileAndEval(codeLine: ReplCodeLine,
|
||||
scriptArgs: ScriptArgsWithTypes?,
|
||||
verifyHistory: List<ReplCodeLine>?,
|
||||
invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
return compileService.remoteReplLineEval(sessionId, codeLine, verifyHistory).get()
|
||||
}
|
||||
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
return compileService.remoteReplLineCheck(sessionId, codeLine).get()
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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 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 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()
|
||||
|
||||
// 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()
|
||||
}
|
||||
@@ -1,56 +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.client
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.daemon.common.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>>() {
|
||||
override val size: Int
|
||||
get() = state.replStateFacade.getHistorySize()
|
||||
|
||||
override fun get(index: Int): ReplHistoryRecord<Unit> = 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 resetTo(id: ILineId): Iterable<ILineId> = state.replStateFacade.historyResetTo(id).apply {
|
||||
if (isNotEmpty()) {
|
||||
currentGeneration.incrementAndGet()
|
||||
}
|
||||
}
|
||||
|
||||
val currentGeneration = AtomicInteger(REPL_CODE_LINE_FIRST_GEN)
|
||||
|
||||
override val lock: ReentrantReadWriteLock get() = state.lock
|
||||
}
|
||||
|
||||
class RemoteReplCompilerState(internal val replStateFacade: ReplStateFacade, override val lock: ReentrantReadWriteLock = ReentrantReadWriteLock()) : IReplStageState<Unit> {
|
||||
|
||||
override val currentGeneration: Int get() = (history as RemoteReplCompilerStateHistory).currentGeneration.get()
|
||||
|
||||
override val history: IReplStageHistory<Unit> = RemoteReplCompilerStateHistory(this)
|
||||
}
|
||||
@@ -40,48 +40,30 @@ fun makePortFromRunFilenameExtractor(digest: String): (String) -> Int? {
|
||||
}
|
||||
}
|
||||
|
||||
private const val ORPHANED_RUN_FILE_AGE_THRESHOLD_MS = 1000000L
|
||||
|
||||
data class DaemonWithMetadata(val daemon: CompileService, val runFile: File, val jvmOptions: DaemonJVMOptions)
|
||||
|
||||
fun walkDaemons(registryDir: File,
|
||||
compilerId: CompilerId,
|
||||
fileToCompareTimestamp: File,
|
||||
filter: (File, Int) -> Boolean = { _, _ -> true },
|
||||
report: (DaemonReportCategory, String) -> Unit = { _, _ -> }
|
||||
): Sequence<DaemonWithMetadata> {
|
||||
filter: (File, Int) -> Boolean = { f, p -> true },
|
||||
report: (DaemonReportCategory, String) -> Unit = { cat, msg -> }
|
||||
): Sequence<CompileService> {
|
||||
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)
|
||||
.filter { it.second != null && filter(it.first, it.second!!) }
|
||||
.mapNotNull {
|
||||
assert(it.second!! > 0 && it.second!! < MAX_PORT_NUMBER)
|
||||
report(DaemonReportCategory.DEBUG, "found daemon on port ${it.second}, trying to connect")
|
||||
val daemon = tryConnectToDaemon(it.second!!, 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
|
||||
if (daemon == null && !it.first.delete()) {
|
||||
report(DaemonReportCategory.INFO, "WARNING: unable to delete seemingly orphaned file '${it.first.absolutePath}', cleanup recommended")
|
||||
}
|
||||
daemon
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private inline fun tryConnectToDaemon(port: Int, report: (DaemonReportCategory, String) -> Unit): CompileService? {
|
||||
|
||||
try {
|
||||
@@ -106,19 +88,3 @@ fun makeAutodeletingFlagFile(keyword: String = "compiler-client", baseDir: File?
|
||||
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"
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.common
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
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 java.io.File
|
||||
import java.io.Serializable
|
||||
import java.rmi.Remote
|
||||
@@ -35,6 +38,8 @@ interface CompileService : Remote {
|
||||
METADATA
|
||||
}
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
val NO_SESSION: Int = 0
|
||||
}
|
||||
@@ -102,7 +107,6 @@ interface CompileService : Remote {
|
||||
|
||||
// TODO: consider adding async version of shutdown and release
|
||||
|
||||
@Deprecated("The usages should be replaced with `compile` method", ReplaceWith("compile"))
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteCompile(
|
||||
sessionId: Int,
|
||||
@@ -115,7 +119,6 @@ interface CompileService : Remote {
|
||||
operationsTracer: RemoteOperationsTracer?
|
||||
): CallResult<Int>
|
||||
|
||||
@Deprecated("The usages should be replaced with `compile` method", ReplaceWith("compile"))
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteIncrementalCompile(
|
||||
sessionId: Int,
|
||||
@@ -140,7 +143,6 @@ interface CompileService : Remote {
|
||||
@Throws(RemoteException::class)
|
||||
fun clearJarCache()
|
||||
|
||||
@Deprecated("The usages should be replaced with other `leaseReplSession` method", ReplaceWith("leaseReplSession"))
|
||||
@Throws(RemoteException::class)
|
||||
fun leaseReplSession(
|
||||
aliveFlagPath: String?,
|
||||
@@ -160,14 +162,12 @@ interface CompileService : Remote {
|
||||
@Throws(RemoteException::class)
|
||||
fun releaseReplSession(sessionId: Int): CallResult<Nothing>
|
||||
|
||||
@Deprecated("The usages should be replaced with `replCheck` method", ReplaceWith("replCheck"))
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteReplLineCheck(
|
||||
sessionId: Int,
|
||||
codeLine: ReplCodeLine
|
||||
): CallResult<ReplCheckResult>
|
||||
|
||||
@Deprecated("The usages should be replaced with `replCompile` method", ReplaceWith("replCompile"))
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteReplLineCompile(
|
||||
sessionId: Int,
|
||||
@@ -175,38 +175,10 @@ interface CompileService : Remote {
|
||||
history: List<ReplCodeLine>?
|
||||
): CallResult<ReplCompileResult>
|
||||
|
||||
@Deprecated("Evaluation on daemon is not supported")
|
||||
@Throws(RemoteException::class)
|
||||
fun remoteReplLineEval(
|
||||
sessionId: Int,
|
||||
codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>?
|
||||
): CallResult<ReplEvalResult>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun leaseReplSession(
|
||||
aliveFlagPath: String?,
|
||||
compilerArguments: Array<out String>,
|
||||
compilationOptions: CompilationOptions,
|
||||
servicesFacade: CompilerServicesFacadeBase,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String
|
||||
): CallResult<Int>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun replCreateState(sessionId: Int): CallResult<ReplStateFacade>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun replCheck(
|
||||
sessionId: Int,
|
||||
replStateId: Int,
|
||||
codeLine: ReplCodeLine
|
||||
): CallResult<ReplCheckResult>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun replCompile(
|
||||
sessionId: Int,
|
||||
replStateId: Int,
|
||||
codeLine: ReplCodeLine
|
||||
): CallResult<ReplCompileResult>
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import java.rmi.RemoteException
|
||||
* the reason for having common facade is attempt to reduce number of connections between client and daemon
|
||||
* Note: non-standard naming convention used to denote combining several entities in one facade - prefix <entityName>_ is used for every function belonging to the entity
|
||||
*/
|
||||
@Deprecated("The usages should be replaced with `compile` method and `CompilerServicesFacadeBase` implementations", ReplaceWith("CompilerServicesFacadeBase"))
|
||||
interface CompilerCallbackServicesFacade : Remote {
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
@@ -81,7 +80,7 @@ interface CompilerCallbackServicesFacade : Remote {
|
||||
// ----------------------------------------------------
|
||||
// CompilationCanceledStatus
|
||||
@Throws(RemoteException::class, RmiFriendlyCompilationCanceledException::class)
|
||||
fun compilationCanceledStatus_checkCanceled(): Void?
|
||||
fun compilationCanceledStatus_checkCanceled(): Unit
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ val COMPILE_DAEMON_DEFAULT_SHUTDOWN_DELAY_MS: Long = 1000L // 1 sec
|
||||
val COMPILE_DAEMON_MEMORY_THRESHOLD_INFINITE: Long = 0L
|
||||
val COMPILE_DAEMON_FORCE_SHUTDOWN_DEFAULT_TIMEOUT_MS: Long = 10000L // 10 secs
|
||||
val COMPILE_DAEMON_TIMEOUT_INFINITE_MS: Long = 0L
|
||||
val COMPILE_DAEMON_IS_READY_MESSAGE = "Kotlin compile daemon is ready"
|
||||
|
||||
val COMPILE_DAEMON_DEFAULT_RUN_DIR_PATH: String get() =
|
||||
FileSystem.getRuntimeStateFilesPath("kotlin", "daemon")
|
||||
|
||||
@@ -1,36 +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
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.ILineId
|
||||
import java.rmi.Remote
|
||||
import java.rmi.RemoteException
|
||||
|
||||
interface ReplStateFacade : Remote {
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun getId(): Int
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun getHistorySize(): Int
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun historyGet(index: Int): ILineId
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun historyResetTo(id: ILineId): List<ILineId>
|
||||
}
|
||||
@@ -63,6 +63,7 @@ import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.Logger
|
||||
import kotlin.comparisons.compareByDescending
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.schedule
|
||||
import kotlin.concurrent.write
|
||||
@@ -101,7 +102,6 @@ class CompileServiceImpl(
|
||||
val timer: Timer,
|
||||
val onShutdown: () -> Unit
|
||||
) : CompileService {
|
||||
|
||||
init {
|
||||
System.setProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY, "true")
|
||||
}
|
||||
@@ -133,13 +133,24 @@ class CompileServiceImpl(
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
private val sessions: MutableMap<Int, ClientOrSessionProxy<Any>> = hashMapOf()
|
||||
private val sessionsIdCounter = AtomicInteger(0)
|
||||
private val internalRng = Random()
|
||||
|
||||
fun<T: Any> leaseSession(session: ClientOrSessionProxy<T>): Int = lock.write {
|
||||
val newId = getValidId(sessionsIdCounter) {
|
||||
it != CompileService.NO_SESSION && !sessions.containsKey(it)
|
||||
fun<T: Any> leaseSession(session: ClientOrSessionProxy<T>): Int {
|
||||
// fighting hypothetical integer wrapping
|
||||
var newId = sessionsIdCounter.incrementAndGet()
|
||||
for (attempt in 1..100) {
|
||||
if (newId != CompileService.NO_SESSION) {
|
||||
lock.write {
|
||||
if (!sessions.containsKey(newId)) {
|
||||
sessions.put(newId, session)
|
||||
return newId
|
||||
}
|
||||
}
|
||||
}
|
||||
// assuming wrap, jumping to random number to reduce probability of further clashes
|
||||
newId = sessionsIdCounter.addAndGet(internalRng.nextInt())
|
||||
}
|
||||
sessions.put(newId, session)
|
||||
newId
|
||||
throw IllegalStateException("Invalid state or algorithm error")
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean = lock.read { sessions.isEmpty() }
|
||||
@@ -256,35 +267,27 @@ class CompileServiceImpl(
|
||||
ifAlive { CompileService.CallResult.Good(usedMemory(withGC = true)) }
|
||||
|
||||
override fun shutdown(): CompileService.CallResult<Nothing> = ifAliveExclusive(minAliveness = Aliveness.LastSession, ignoreCompilerChanged = true) {
|
||||
shutdownWithDelay()
|
||||
shutdownImpl()
|
||||
CompileService.CallResult.Ok()
|
||||
}
|
||||
|
||||
override fun scheduleShutdown(graceful: Boolean): CompileService.CallResult<Boolean> = ifAlive(minAliveness = Aliveness.Alive) {
|
||||
when {
|
||||
!graceful -> {
|
||||
shutdownWithDelay()
|
||||
CompileService.CallResult.Good(true)
|
||||
}
|
||||
state.alive.compareAndSet(Aliveness.Alive.ordinal, Aliveness.LastSession.ordinal) -> {
|
||||
timer.schedule(1) {
|
||||
ifAliveExclusive(minAliveness = Aliveness.LastSession, ignoreCompilerChanged = true) {
|
||||
when {
|
||||
state.sessions.isEmpty() -> shutdownWithDelay()
|
||||
else -> {
|
||||
daemonOptions.autoshutdownIdleSeconds = TimeUnit.MILLISECONDS.toSeconds(daemonOptions.forceShutdownTimeoutMilliseconds).toInt()
|
||||
daemonOptions.autoshutdownUnusedSeconds = daemonOptions.autoshutdownIdleSeconds
|
||||
log.info("Graceful shutdown signalled; unused/idle timeouts are set to ${daemonOptions.autoshutdownUnusedSeconds}/${daemonOptions.autoshutdownIdleSeconds}s")
|
||||
CompileService.CallResult.Good(
|
||||
if (!graceful || state.alive.compareAndSet(Aliveness.Alive.ordinal, Aliveness.LastSession.ordinal)) {
|
||||
timer.schedule(0) {
|
||||
ifAliveExclusive(minAliveness = Aliveness.LastSession, ignoreCompilerChanged = true) {
|
||||
if (!graceful || state.sessions.isEmpty()) {
|
||||
shutdownImpl()
|
||||
}
|
||||
else {
|
||||
log.info("Some sessions are active, waiting for them to finish")
|
||||
}
|
||||
CompileService.CallResult.Ok()
|
||||
}
|
||||
CompileService.CallResult.Ok()
|
||||
}
|
||||
true
|
||||
}
|
||||
CompileService.CallResult.Good(true)
|
||||
}
|
||||
else -> CompileService.CallResult.Good(false)
|
||||
}
|
||||
else false)
|
||||
}
|
||||
|
||||
override fun remoteCompile(sessionId: Int,
|
||||
@@ -461,10 +464,9 @@ class CompileServiceImpl(
|
||||
CompileService.CallResult.Error("Sorry, only JVM target platform is supported now")
|
||||
else {
|
||||
val disposable = Disposer.newDisposable()
|
||||
val compilerMessagesStream = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(compilerMessagesOutputStream, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE))
|
||||
val messageCollector = KeepFirstErrorMessageCollector(compilerMessagesStream)
|
||||
val repl = KotlinJvmReplService(disposable, port, templateClasspath, templateClassName,
|
||||
messageCollector, operationsTracer)
|
||||
val repl = KotlinJvmReplService(disposable, templateClasspath, templateClassName,
|
||||
scriptArgs?.let { ScriptArgsWithTypes(it, scriptArgsTypes?.map { it.kotlin }?.toTypedArray() ?: emptyArray()) },
|
||||
compilerMessagesOutputStream, operationsTracer)
|
||||
val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable))
|
||||
|
||||
CompileService.CallResult.Good(sessionId)
|
||||
@@ -477,14 +479,14 @@ class CompileServiceImpl(
|
||||
override fun remoteReplLineCheck(sessionId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
CompileService.CallResult.Good(check(codeLine))
|
||||
check(codeLine)
|
||||
}
|
||||
}
|
||||
|
||||
override fun remoteReplLineCompile(sessionId: Int, codeLine: ReplCodeLine, history: List<ReplCodeLine>?): CompileService.CallResult<ReplCompileResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
CompileService.CallResult.Good(compile(codeLine, history))
|
||||
compile(codeLine, history)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,56 +495,12 @@ class CompileServiceImpl(
|
||||
codeLine: ReplCodeLine,
|
||||
history: List<ReplCodeLine>?
|
||||
): CompileService.CallResult<ReplEvalResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
CompileService.CallResult.Error("Eval on daemon is not supported")
|
||||
}
|
||||
|
||||
override fun leaseReplSession(aliveFlagPath: String?,
|
||||
compilerArguments: Array<out String>,
|
||||
compilationOptions: CompilationOptions,
|
||||
servicesFacade: CompilerServicesFacadeBase,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String
|
||||
): CompileService.CallResult<Int> = ifAlive(minAliveness = Aliveness.Alive) {
|
||||
if (compilationOptions.targetPlatform != CompileService.TargetPlatform.JVM)
|
||||
CompileService.CallResult.Error("Sorry, only JVM target platform is supported now")
|
||||
else {
|
||||
val disposable = Disposer.newDisposable()
|
||||
val messageCollector = CompileServicesFacadeMessageCollector(servicesFacade, compilationOptions)
|
||||
val repl = KotlinJvmReplService(disposable, port, templateClasspath, templateClassName,
|
||||
messageCollector, null)
|
||||
val sessionId = state.sessions.leaseSession(ClientOrSessionProxy(aliveFlagPath, repl, disposable))
|
||||
|
||||
CompileService.CallResult.Good(sessionId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun replCreateState(sessionId: Int): CompileService.CallResult<ReplStateFacade> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
CompileService.CallResult.Good(createRemoteState(port))
|
||||
compileAndEval(codeLine, verifyHistory = history)
|
||||
}
|
||||
}
|
||||
|
||||
override fun replCheck(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCheckResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
withValidReplState(replStateId) { state ->
|
||||
check(state, codeLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun replCompile(sessionId: Int, replStateId: Int, codeLine: ReplCodeLine): CompileService.CallResult<ReplCompileResult> =
|
||||
ifAlive(minAliveness = Aliveness.Alive) {
|
||||
withValidRepl(sessionId) {
|
||||
withValidReplState(replStateId) { state ->
|
||||
compile(state, codeLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// internal implementation stuff
|
||||
|
||||
// TODO: consider matching compilerId coming from outside with actual one
|
||||
@@ -571,7 +529,7 @@ class CompileServiceImpl(
|
||||
val stub = UnicastRemoteObject.exportObject(this, port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory) as CompileService
|
||||
registry.rebind (COMPILER_SERVICE_RMI_NAME, stub)
|
||||
|
||||
timer.schedule(10) {
|
||||
timer.schedule(0) {
|
||||
initiateElections()
|
||||
}
|
||||
timer.schedule(delay = DAEMON_PERIODIC_CHECK_INTERVAL_MS, period = DAEMON_PERIODIC_CHECK_INTERVAL_MS) {
|
||||
@@ -594,7 +552,7 @@ class CompileServiceImpl(
|
||||
// 1. check if unused for a timeout - shutdown
|
||||
if (shutdownCondition({ daemonOptions.autoshutdownUnusedSeconds != COMPILE_DAEMON_TIMEOUT_INFINITE_S && compilationsCounter.get() == 0 && nowSeconds() - lastUsedSeconds > daemonOptions.autoshutdownUnusedSeconds },
|
||||
"Unused timeout exceeded ${daemonOptions.autoshutdownUnusedSeconds}s, shutting down")) {
|
||||
scheduleShutdown(true)
|
||||
shutdown()
|
||||
}
|
||||
else {
|
||||
var anyDead = state.sessions.cleanDead()
|
||||
@@ -603,7 +561,7 @@ class CompileServiceImpl(
|
||||
|
||||
// 3. check if in graceful shutdown state and all sessions are closed
|
||||
if (shutdownCondition({ state.alive.get() == Aliveness.LastSession.ordinal && state.sessions.isEmpty() }, "All sessions finished, shutting down")) {
|
||||
shutdownWithDelay()
|
||||
shutdown()
|
||||
shuttingDown = true
|
||||
}
|
||||
|
||||
@@ -629,7 +587,7 @@ class CompileServiceImpl(
|
||||
// TODO: could be too expensive anyway, consider removing this check
|
||||
shutdownCondition({ classpathWatcher.isChanged }, "Compiler changed"))
|
||||
{
|
||||
scheduleShutdown(true)
|
||||
shutdown()
|
||||
shuttingDown = true
|
||||
}
|
||||
|
||||
@@ -642,61 +600,39 @@ class CompileServiceImpl(
|
||||
}
|
||||
|
||||
|
||||
// TODO: handover should include mechanism for client to switch to a new daemon then previous "handed over responsibilities" and shot down
|
||||
private fun initiateElections() {
|
||||
|
||||
ifAlive {
|
||||
|
||||
val aliveWithOpts = walkDaemons(File(daemonOptions.runFilesPathOrDefault), compilerId, runFile, filter = { f, p -> p != port }, report = { _, msg -> log.info(msg) }).toList()
|
||||
val comparator = compareByDescending<DaemonWithMetadata, DaemonJVMOptions>(DaemonJVMOptionsMemoryComparator(), { it.jvmOptions })
|
||||
.thenBy(FileAgeComparator()) { it.runFile }
|
||||
aliveWithOpts.maxWith(comparator)?.let { bestDaemonWithMetadata ->
|
||||
val fattestOpts = bestDaemonWithMetadata.jvmOptions
|
||||
if (fattestOpts memorywiseFitsInto daemonJVMOptions && FileAgeComparator().compare(bestDaemonWithMetadata.runFile, runFile) < 0 ) {
|
||||
val aliveWithOpts = walkDaemons(File(daemonOptions.runFilesPathOrDefault), compilerId, filter = { f, p -> p != port }, report = { lvl, msg -> log.info(msg) })
|
||||
.map { Pair(it, it.getDaemonJVMOptions()) }
|
||||
.filter { it.second.isGood }
|
||||
.sortedWith(compareByDescending(DaemonJVMOptionsMemoryComparator(), { it.second.get() }))
|
||||
if (aliveWithOpts.any()) {
|
||||
val fattestOpts = aliveWithOpts.first().second.get()
|
||||
// second part of the condition means that we prefer other daemon if is "equal" to the current one
|
||||
if (fattestOpts memorywiseFitsInto daemonJVMOptions && !(daemonJVMOptions memorywiseFitsInto fattestOpts)) {
|
||||
// all others are smaller that me, take overs' clients and shut them down
|
||||
log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE lower prio, taking clients from them and schedule them to shutdown: my runfile: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})")
|
||||
aliveWithOpts.forEach { (daemon, runFile, _) ->
|
||||
try {
|
||||
daemon.getClients().takeIf { it.isGood }?.let {
|
||||
it.get().forEach { clientAliveFile -> registerClient(clientAliveFile) }
|
||||
}
|
||||
daemon.scheduleShutdown(true)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
log.info("Cannot connect to a daemon, assuming dying ('${runFile.canonicalPath}'): ${e.message}")
|
||||
aliveWithOpts.forEach {
|
||||
it.first.getClients().check { it.isGood }?.let {
|
||||
it.get().forEach { registerClient(it) }
|
||||
}
|
||||
it.first.scheduleShutdown(true)
|
||||
}
|
||||
}
|
||||
// TODO: seems that the second part of condition is incorrect, reconsider:
|
||||
// the comment by @tsvtkv from review:
|
||||
// Algorithm in plain english:
|
||||
// (1) If the best daemon fits into me and the best daemon is younger than me, then I take over all other daemons clients.
|
||||
// (2) If I fit into the best daemon and the best daemon is older than me, then I give my clients to that daemon.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// daemon A starts with params: maxMem=100, codeCache=50
|
||||
// daemon B starts with params: maxMem=200, codeCache=50
|
||||
// daemon C starts with params: maxMem=150, codeCache=100
|
||||
// A performs election: (1) is false because neither B nor C does not fit into A, (2) is false because both B and C are younger than A.
|
||||
// B performs election: (1) is false because neither A nor C does not fit into B, (2) is false because B does not fit into neither A nor C.
|
||||
// C performs election: (1) is false because B is better than A and B does not fit into C, (2) is false C does not fit into neither A nor B.
|
||||
// Result: all daemons are alive and well.
|
||||
else if (daemonJVMOptions memorywiseFitsInto fattestOpts && FileAgeComparator().compare(bestDaemonWithMetadata.runFile, runFile) > 0) {
|
||||
else if (daemonJVMOptions memorywiseFitsInto fattestOpts) {
|
||||
// there is at least one bigger, handover my clients to it and shutdown
|
||||
log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE higher prio, handover clients to it and schedule shutdown: my runfile: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})")
|
||||
getClients().takeIf { it.isGood }?.let {
|
||||
it.get().forEach { bestDaemonWithMetadata.daemon.registerClient(it) }
|
||||
}
|
||||
scheduleShutdown(true)
|
||||
aliveWithOpts.first().first.let { fattest ->
|
||||
getClients().check { it.isGood }?.let {
|
||||
it.get().forEach { fattest.registerClient(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// undecided, do nothing
|
||||
log.info("$LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE equal prio, continue: ${runFile.name} (${runFile.lastModified()}) vs best other runfile: ${bestDaemonWithMetadata.runFile.name} (${bestDaemonWithMetadata.runFile.lastModified()})")
|
||||
// TODO: implement some behaviour here, e.g.:
|
||||
// - shutdown/takeover smaller daemon
|
||||
// - run (or better persuade client to run) a bigger daemon (in fact may be even simple shutdown will do, because of client's daemon choosing logic)
|
||||
}
|
||||
// else - do nothing, all daemons are staying
|
||||
// TODO: implement some behaviour here, e.g.:
|
||||
// - shutdown/takeover smaller daemon
|
||||
// - run (or better persuade client to run) a bigger daemon (in fact may be even simple shutdown will do, because of client's daemon choosing logic)
|
||||
}
|
||||
CompileService.CallResult.Ok()
|
||||
}
|
||||
@@ -717,11 +653,8 @@ class CompileServiceImpl(
|
||||
timer.schedule(daemonOptions.shutdownDelayMilliseconds) {
|
||||
state.delayedShutdownQueued.set(false)
|
||||
if (currentCompilationsCount == compilationsCounter.get()) {
|
||||
ifAliveExclusive(minAliveness = Aliveness.LastSession, ignoreCompilerChanged = true) {
|
||||
log.fine("Execute delayed shutdown")
|
||||
shutdownImpl()
|
||||
CompileService.CallResult.Ok()
|
||||
}
|
||||
log.fine("Execute delayed shutdown")
|
||||
shutdown()
|
||||
}
|
||||
else {
|
||||
log.info("Cancel delayed shutdown due to new client")
|
||||
@@ -745,7 +678,7 @@ class CompileServiceImpl(
|
||||
operationsTracer: RemoteOperationsTracer?,
|
||||
body: (PrintStream, EventManager, Profiler) -> ExitCode): CompileService.CallResult<Int> =
|
||||
ifAlive {
|
||||
withValidClientOrSessionProxy(sessionId) { _ ->
|
||||
withValidClientOrSessionProxy(sessionId) { session ->
|
||||
operationsTracer?.before("compile")
|
||||
val rpcProfiler = if (daemonOptions.reportPerf) WallAndThreadTotalProfiler() else DummyProfiler()
|
||||
val eventManger = EventManagerImpl()
|
||||
@@ -775,7 +708,7 @@ class CompileServiceImpl(
|
||||
tracer: RemoteOperationsTracer?,
|
||||
body: (EventManager, Profiler) -> ExitCode): CompileService.CallResult<Int> =
|
||||
ifAlive {
|
||||
withValidClientOrSessionProxy(sessionId) { _ ->
|
||||
withValidClientOrSessionProxy(sessionId) { session ->
|
||||
tracer?.before("compile")
|
||||
val rpcProfiler = if (daemonOptions.reportPerf) WallAndThreadTotalProfiler() else DummyProfiler()
|
||||
val eventManger = EventManagerImpl()
|
||||
@@ -868,7 +801,7 @@ class CompileServiceImpl(
|
||||
state.alive.get() < minAliveness.ordinal -> CompileService.CallResult.Dying()
|
||||
!ignoreCompilerChanged && classpathWatcher.isChanged -> {
|
||||
log.info("Compiler changed, scheduling shutdown")
|
||||
shutdownWithDelay()
|
||||
timer.schedule(0) { shutdown() }
|
||||
CompileService.CallResult.Dying()
|
||||
}
|
||||
else -> {
|
||||
@@ -895,6 +828,7 @@ class CompileServiceImpl(
|
||||
finally {
|
||||
_lastUsedSeconds = nowSeconds()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private inline fun<R> withValidRepl(sessionId: Int, body: KotlinJvmReplService.() -> R): CompileService.CallResult<R> =
|
||||
@@ -903,12 +837,4 @@ class CompileServiceImpl(
|
||||
CompileService.CallResult.Good(it.body())
|
||||
} ?: CompileService.CallResult.Error("Not a REPL session $sessionId")
|
||||
}
|
||||
|
||||
@JvmName("withValidRepl1")
|
||||
private inline fun<R> withValidRepl(sessionId: Int, body: KotlinJvmReplService.() -> CompileService.CallResult<R>): CompileService.CallResult<R> =
|
||||
withValidClientOrSessionProxy(sessionId) { session ->
|
||||
(session?.data as? KotlinJvmReplService?)?.let {
|
||||
it.body()
|
||||
} ?: CompileService.CallResult.Error("Not a REPL session $sessionId")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,14 @@ import java.net.URLClassLoader
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.jar.Manifest
|
||||
import java.util.logging.*
|
||||
import java.util.logging.Level
|
||||
import java.util.logging.LogManager
|
||||
import java.util.logging.Logger
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
val DAEMON_PERIODIC_CHECK_INTERVAL_MS = 1000L
|
||||
|
||||
|
||||
class LogStream(name: String) : OutputStream() {
|
||||
|
||||
val log by lazy { Logger.getLogger(name) }
|
||||
@@ -52,6 +55,7 @@ class LogStream(name: String) : OutputStream() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object KotlinCompileDaemon {
|
||||
|
||||
init {
|
||||
@@ -67,7 +71,7 @@ object KotlinCompileDaemon {
|
||||
"java.util.logging.FileHandler.count = ${if (fileIsGiven) 1 else 3}\n" +
|
||||
"java.util.logging.FileHandler.append = $fileIsGiven\n" +
|
||||
"java.util.logging.FileHandler.pattern = ${if (fileIsGiven) logPath else (logPath + File.separator + "${COMPILE_DAEMON_DEFAULT_FILES_PREFIX}.$logTime.%u%g.log")}\n" +
|
||||
"java.util.logging.SimpleFormatter.format = %1\$tF %1\$tT.%1\$tL [%3\$s] %4\$s: %5\$s%n\n"
|
||||
"java.util.logging.SimpleFormatter.format = %1\$tF %1\$tT.%1\$tL [%3\$s] %4\$s: %5\$s\\n\n"
|
||||
|
||||
LogManager.getLogManager().readConfiguration(cfg.byteInputStream())
|
||||
}
|
||||
@@ -92,7 +96,6 @@ object KotlinCompileDaemon {
|
||||
log.info("Kotlin compiler daemon version " + (loadVersionFromResource() ?: "<unknown>"))
|
||||
log.info("daemon JVM args: " + ManagementFactory.getRuntimeMXBean().inputArguments.joinToString(" "))
|
||||
log.info("daemon args: " + args.joinToString(" "))
|
||||
log.info("daemon process name: " + ManagementFactory.getRuntimeMXBean().name)
|
||||
|
||||
val compilerId = CompilerId()
|
||||
val daemonOptions = DaemonOptions()
|
||||
@@ -153,8 +156,8 @@ object KotlinCompileDaemon {
|
||||
}
|
||||
})
|
||||
|
||||
println(COMPILE_DAEMON_IS_READY_MESSAGE)
|
||||
log.info("daemon is listening on port: $port")
|
||||
if (daemonOptions.runFilesPath.isNotEmpty())
|
||||
println(daemonOptions.runFilesPath)
|
||||
|
||||
// this supposed to stop redirected streams reader(s) on the client side and prevent some situations with hanging threads, but doesn't work reliably
|
||||
// TODO: implement more reliable scheme
|
||||
|
||||
@@ -20,40 +20,58 @@ import com.intellij.openapi.Disposable
|
||||
import org.jetbrains.kotlin.cli.common.messages.*
|
||||
import org.jetbrains.kotlin.cli.common.repl.*
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.GenericReplCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.GenericReplCompilerState
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.daemon.common.CompileService
|
||||
import org.jetbrains.kotlin.daemon.common.DummyProfiler
|
||||
import org.jetbrains.kotlin.daemon.common.RemoteOperationsTracer
|
||||
import org.jetbrains.kotlin.daemon.common.RemoteOutputStream
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.net.URLClassLoader
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
open class KotlinJvmReplService(
|
||||
disposable: Disposable,
|
||||
val portForServers: Int,
|
||||
templateClasspath: List<File>,
|
||||
templateClassName: String,
|
||||
protected val messageCollector: MessageCollector,
|
||||
@Deprecated("drop it")
|
||||
protected val fallbackScriptArgs: ScriptArgsWithTypes?,
|
||||
compilerOutputStreamProxy: RemoteOutputStream,
|
||||
protected val operationsTracer: RemoteOperationsTracer?
|
||||
) : ReplCompileAction, ReplCheckAction, CreateReplStageStateAction {
|
||||
) : ReplCompileAction, ReplAtomicEvalAction, ReplCheckAction, ReplEvaluatorExposedInternalHistory {
|
||||
protected val compilerMessagesStream = PrintStream(BufferedOutputStream(RemoteOutputStreamClient(compilerOutputStreamProxy, DummyProfiler()), REMOTE_STREAM_BUFFER_SIZE))
|
||||
|
||||
protected class KeepFirstErrorMessageCollector(compilerMessagesStream: PrintStream) : MessageCollector {
|
||||
|
||||
private val innerCollector = PrintingMessageCollector(compilerMessagesStream, MessageRenderer.WITHOUT_PATHS, false)
|
||||
|
||||
internal var firstErrorMessage: String? = null
|
||||
internal var firstErrorLocation: CompilerMessageLocation? = null
|
||||
|
||||
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) {
|
||||
if (firstErrorMessage == null && severity.isError) {
|
||||
firstErrorMessage = message
|
||||
firstErrorLocation = location
|
||||
}
|
||||
innerCollector.report(severity, message, location)
|
||||
}
|
||||
|
||||
override fun hasErrors(): Boolean = innerCollector.hasErrors()
|
||||
override fun clear() {
|
||||
innerCollector.clear()
|
||||
}
|
||||
}
|
||||
protected val messageCollector = KeepFirstErrorMessageCollector(compilerMessagesStream)
|
||||
|
||||
protected val configuration = CompilerConfiguration().apply {
|
||||
addJvmClasspathRoots(PathUtil.getJdkClassesRoots())
|
||||
addJvmClasspathRoots(PathUtil.getKotlinPathsForCompiler().let { listOf(it.runtimePath, it.reflectPath, it.scriptRuntimePath) })
|
||||
addJvmClasspathRoots(templateClasspath)
|
||||
put(CommonConfigurationKeys.MODULE_NAME, "kotlin-script")
|
||||
put(CommonConfigurationKeys.SKIP_METADATA_VERSION_CHECK, true)
|
||||
}
|
||||
|
||||
protected fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition? {
|
||||
@@ -89,92 +107,49 @@ open class KotlinJvmReplService(
|
||||
else GenericReplCompiler(disposable, scriptDef, configuration, messageCollector)
|
||||
}
|
||||
|
||||
protected val statesLock = ReentrantReadWriteLock()
|
||||
// TODO: consider using values here for session cleanup
|
||||
protected val states = WeakHashMap<RemoteReplStateFacadeServer, Boolean>() // used as (missing) WeakHashSet
|
||||
protected val stateIdCounter = AtomicInteger()
|
||||
@Deprecated("remove after removal state-less check/compile/eval methods")
|
||||
protected val defaultStateFacade: RemoteReplStateFacadeServer by lazy { createRemoteState() }
|
||||
private val replEvaluator: ReplFullEvaluator? by lazy {
|
||||
replCompiler?.let { compiler ->
|
||||
GenericReplCompilingEvaluator(compiler, configuration.jvmClasspathRoots, null, fallbackScriptArgs, ReplRepeatingMode.NONE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createState(lock: ReentrantReadWriteLock): IReplStageState<*> =
|
||||
replCompiler?.createState(lock) ?: throw IllegalStateException("repl compiler is not initialized properly")
|
||||
override val lastEvaluatedScripts: List<EvalHistoryType> get() = replEvaluator?.lastEvaluatedScripts ?: emptyList()
|
||||
|
||||
override fun check(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCheckResult {
|
||||
override fun check(codeLine: ReplCodeLine): ReplCheckResult {
|
||||
operationsTracer?.before("check")
|
||||
try {
|
||||
return replCompiler?.check(state, codeLine)
|
||||
?: ReplCheckResult.Error("Initialization error", CompilerMessageLocation.NO_LOCATION)
|
||||
return replCompiler?.check(codeLine)
|
||||
?: ReplCheckResult.Error(messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
finally {
|
||||
operationsTracer?.after("check")
|
||||
}
|
||||
}
|
||||
|
||||
override fun compile(state: IReplStageState<*>, codeLine: ReplCodeLine): ReplCompileResult {
|
||||
override fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult {
|
||||
operationsTracer?.before("compile")
|
||||
try {
|
||||
return replCompiler?.compile(state, codeLine)
|
||||
?: ReplCompileResult.Error("Initialization error", CompilerMessageLocation.NO_LOCATION)
|
||||
return replCompiler?.compile(codeLine, verifyHistory)
|
||||
?: ReplCompileResult.Error(verifyHistory ?: emptyList(),
|
||||
messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
finally {
|
||||
operationsTracer?.after("compile")
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use check(state, line) instead")
|
||||
fun check(codeLine: ReplCodeLine): ReplCheckResult = check(defaultStateFacade.state, codeLine)
|
||||
|
||||
@Deprecated("Use compile(state, line) instead")
|
||||
fun compile(codeLine: ReplCodeLine, verifyHistory: List<ReplCodeLine>?): ReplCompileResult = compile(defaultStateFacade.state, codeLine)
|
||||
|
||||
fun createRemoteState(port: Int = portForServers): RemoteReplStateFacadeServer = statesLock.write {
|
||||
val id = getValidId(stateIdCounter) { id -> states.none { it.key.getId() == id} }
|
||||
val stateFacade = RemoteReplStateFacadeServer(id, createState().asState(GenericReplCompilerState::class.java), port)
|
||||
states.put(stateFacade, true)
|
||||
stateFacade
|
||||
}
|
||||
|
||||
fun<R> withValidReplState(stateId: Int, body: (IReplStageState<*>) -> R): CompileService.CallResult<R> = statesLock.read {
|
||||
states.keys.firstOrNull { it.getId() == stateId }?.let {
|
||||
CompileService.CallResult.Good(body(it.state))
|
||||
override fun compileAndEval(codeLine: ReplCodeLine, scriptArgs: ScriptArgsWithTypes?, verifyHistory: List<ReplCodeLine>?, invokeWrapper: InvokeWrapper?): ReplEvalResult {
|
||||
operationsTracer?.before("eval")
|
||||
try {
|
||||
return replEvaluator?.compileAndEval(codeLine, scriptArgs ?: fallbackScriptArgs, verifyHistory, invokeWrapper)
|
||||
?: ReplEvalResult.Error.CompileTime(verifyHistory ?: replEvaluator?.history ?: emptyList(),
|
||||
messageCollector.firstErrorMessage ?: "Unknown error",
|
||||
messageCollector.firstErrorLocation ?: CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
?: CompileService.CallResult.Error("No REPL state with id $stateId found")
|
||||
}
|
||||
}
|
||||
|
||||
internal class KeepFirstErrorMessageCollector(compilerMessagesStream: PrintStream) : MessageCollector {
|
||||
|
||||
private val innerCollector = PrintingMessageCollector(compilerMessagesStream, MessageRenderer.WITHOUT_PATHS, false)
|
||||
|
||||
internal var firstErrorMessage: String? = null
|
||||
internal var firstErrorLocation: CompilerMessageLocation? = null
|
||||
|
||||
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation) {
|
||||
if (firstErrorMessage == null && severity.isError) {
|
||||
firstErrorMessage = message
|
||||
firstErrorLocation = location
|
||||
finally {
|
||||
operationsTracer?.after("eval")
|
||||
}
|
||||
innerCollector.report(severity, message, location)
|
||||
}
|
||||
|
||||
override fun hasErrors(): Boolean = innerCollector.hasErrors()
|
||||
override fun clear() {
|
||||
innerCollector.clear()
|
||||
}
|
||||
}
|
||||
|
||||
internal val internalRng = Random()
|
||||
|
||||
inline internal fun getValidId(counter: AtomicInteger, check: (Int) -> Boolean): Int {
|
||||
// fighting hypothetical integer wrapping
|
||||
var newId = counter.incrementAndGet()
|
||||
var attemptsLeft = 100
|
||||
while (!check(newId)) {
|
||||
attemptsLeft -= 1
|
||||
if (attemptsLeft <= 0)
|
||||
throw IllegalStateException("Invalid state or algorithm error")
|
||||
// assuming wrap, jumping to random number to reduce probability of further clashes
|
||||
newId = counter.addAndGet(internalRng.nextInt())
|
||||
}
|
||||
return newId
|
||||
}
|
||||
|
||||
@@ -1,39 +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
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.repl.ILineId
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.GenericReplCompilerState
|
||||
import org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface
|
||||
import org.jetbrains.kotlin.daemon.common.ReplStateFacade
|
||||
import org.jetbrains.kotlin.daemon.common.SOCKET_ANY_FREE_PORT
|
||||
import java.rmi.server.UnicastRemoteObject
|
||||
|
||||
class RemoteReplStateFacadeServer(val _id: Int,
|
||||
val state: GenericReplCompilerState,
|
||||
port: Int = SOCKET_ANY_FREE_PORT
|
||||
) : ReplStateFacade,
|
||||
UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
|
||||
{
|
||||
override fun getId(): Int = _id
|
||||
|
||||
override fun getHistorySize(): Int = state.history.size
|
||||
|
||||
override fun historyGet(index: Int): ILineId = state.history[index].id
|
||||
|
||||
override fun historyResetTo(id: ILineId): List<ILineId> = state.history.resetTo(id).toList()
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.context.LazyResolveToken
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.frontend.di.configureCommon
|
||||
import org.jetbrains.kotlin.frontend.di.configureModule
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.load.java.InternalFlexibleTypeTransformer
|
||||
@@ -47,10 +46,11 @@ import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
|
||||
private fun StorageComponentContainer.configureJavaTopDownAnalysis(
|
||||
fun StorageComponentContainer.configureJavaTopDownAnalysis(
|
||||
moduleContentScope: GlobalSearchScope,
|
||||
project: Project,
|
||||
lookupTracker: LookupTracker
|
||||
lookupTracker: LookupTracker,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
useInstance(moduleContentScope)
|
||||
useInstance(lookupTracker)
|
||||
@@ -75,6 +75,7 @@ private fun StorageComponentContainer.configureJavaTopDownAnalysis(
|
||||
useImpl<JavaSourceElementFactoryImpl>()
|
||||
useInstance(InternalFlexibleTypeTransformer)
|
||||
|
||||
useInstance(languageVersionSettings)
|
||||
useImpl<CompilerDeserializationConfiguration>()
|
||||
}
|
||||
|
||||
@@ -87,19 +88,19 @@ fun createContainerForLazyResolveWithJava(
|
||||
targetEnvironment: TargetEnvironment,
|
||||
lookupTracker: LookupTracker,
|
||||
packagePartProvider: PackagePartProvider,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
useBuiltInsProvider: Boolean,
|
||||
useLazyResolve: Boolean
|
||||
useLazyResolve: Boolean,
|
||||
compilerConfiguration: CompilerConfiguration
|
||||
): StorageComponentContainer = createContainer("LazyResolveWithJava", JvmPlatform) {
|
||||
configureModule(moduleContext, JvmPlatform, bindingTrace)
|
||||
configureJavaTopDownAnalysis(moduleContentScope, moduleContext.project, lookupTracker)
|
||||
configureJavaTopDownAnalysis(moduleContentScope, moduleContext.project, lookupTracker, languageVersionSettings)
|
||||
|
||||
useInstance(compilerConfiguration)
|
||||
useInstance(packagePartProvider)
|
||||
useInstance(moduleClassResolver)
|
||||
useInstance(declarationProviderFactory)
|
||||
|
||||
configureCommon(compilerConfiguration)
|
||||
|
||||
if (useBuiltInsProvider) {
|
||||
useInstance((moduleContext.module.builtIns as JvmBuiltIns).settings)
|
||||
useImpl<JvmBuiltInsPackageFragmentProvider>()
|
||||
@@ -122,17 +123,18 @@ fun createContainerForTopDownAnalyzerForJvm(
|
||||
moduleContentScope: GlobalSearchScope,
|
||||
lookupTracker: LookupTracker,
|
||||
packagePartProvider: PackagePartProvider,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
moduleClassResolver: ModuleClassResolver,
|
||||
compilerConfiguration: CompilerConfiguration
|
||||
): ComponentProvider = createContainerForLazyResolveWithJava(
|
||||
moduleContext, bindingTrace, declarationProviderFactory, moduleContentScope, moduleClassResolver,
|
||||
CompilerEnvironment, lookupTracker, packagePartProvider, compilerConfiguration,
|
||||
useBuiltInsProvider = true, useLazyResolve = false
|
||||
CompilerEnvironment, lookupTracker, packagePartProvider, languageVersionSettings,
|
||||
useBuiltInsProvider = true, useLazyResolve = false, compilerConfiguration = compilerConfiguration
|
||||
)
|
||||
|
||||
|
||||
fun ComponentProvider.initJvmBuiltInsForTopDownAnalysis() {
|
||||
get<JvmBuiltIns>().initialize(get<ModuleDescriptor>(), get<LanguageVersionSettings>())
|
||||
fun ComponentProvider.initJvmBuiltInsForTopDownAnalysis(module: ModuleDescriptor, languageVersionSettings: LanguageVersionSettings) {
|
||||
get<JvmBuiltIns>().initialize(module, languageVersionSettings)
|
||||
}
|
||||
|
||||
internal fun JvmBuiltIns.initialize(module: ModuleDescriptor, languageVersionSettings: LanguageVersionSettings) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.java.sam;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
|
||||
@@ -24,18 +25,18 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.*;
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.name.SpecialNames;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JavaResolverUtils;
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE;
|
||||
|
||||
@@ -133,10 +134,6 @@ public class SingleAbstractMethodUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DescriptorUtilsKt.getFqNameSafe(klass).asString().equals("android.databinding.DataBindingComponent")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType());
|
||||
if (abstractMembers.size() == 1) {
|
||||
CallableMemberDescriptor member = abstractMembers.get(0);
|
||||
|
||||
@@ -19,19 +19,18 @@ package org.jetbrains.kotlin.load.kotlin.incremental
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
|
||||
internal class IncrementalPackagePartProvider(
|
||||
internal class IncrementalPackagePartProvider private constructor(
|
||||
private val parent: PackagePartProvider,
|
||||
incrementalCaches: List<IncrementalCache>,
|
||||
storageManager: StorageManager
|
||||
) : PackagePartProvider {
|
||||
lateinit var deserializationConfiguration: DeserializationConfiguration
|
||||
|
||||
private val moduleMappings = storageManager.createLazyValue {
|
||||
incrementalCaches.map { cache ->
|
||||
ModuleMapping.create(cache.getModuleMappingData(), "<incremental>", deserializationConfiguration)
|
||||
ModuleMapping.create(cache.getModuleMappingData(), "<incremental>")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,4 +41,19 @@ internal class IncrementalPackagePartProvider(
|
||||
|
||||
// TODO
|
||||
override fun findMetadataPackageParts(packageFqName: String): List<String> = TODO()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun create(
|
||||
parent: PackagePartProvider,
|
||||
targets: List<TargetId>?,
|
||||
incrementalCompilationComponents: IncrementalCompilationComponents?,
|
||||
storageManager: StorageManager
|
||||
): PackagePartProvider {
|
||||
if (targets == null || incrementalCompilationComponents == null) return parent
|
||||
|
||||
val incrementalCaches = targets.map { incrementalCompilationComponents.getIncrementalCache(it) }
|
||||
return IncrementalPackagePartProvider(parent, incrementalCaches, storageManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.analyzer.*
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
@@ -82,20 +81,15 @@ object JvmAnalyzerFacade : AnalyzerFacade<JvmPlatformParameters>() {
|
||||
resolverForModule.componentProvider.get<JavaDescriptorResolver>()
|
||||
}
|
||||
|
||||
val configuration = CompilerConfiguration().apply {
|
||||
val languageSettingsProvider = LanguageSettingsProvider.getInstance(project)
|
||||
languageVersionSettings = languageSettingsProvider.getLanguageVersionSettings(moduleInfo, project)
|
||||
|
||||
val platform = languageSettingsProvider.getTargetPlatform(moduleInfo)
|
||||
if (platform is JvmTarget) {
|
||||
put(JVMConfigurationKeys.JVM_TARGET, platform)
|
||||
}
|
||||
|
||||
isReadOnly = true
|
||||
}
|
||||
|
||||
val languageSettingsProvider = LanguageSettingsProvider.getInstance(project)
|
||||
val trace = CodeAnalyzerInitializer.getInstance(project).createTrace()
|
||||
|
||||
//TODO: need to propagate full CompilerConfiguration to frontend
|
||||
val compilerConfiguration = CompilerConfiguration()
|
||||
val platform = languageSettingsProvider.getTargetPlatform(moduleInfo)
|
||||
if (platform is JvmTarget) {
|
||||
compilerConfiguration.put(JVMConfigurationKeys.JVM_TARGET, platform)
|
||||
}
|
||||
compilerConfiguration.isReadOnly = true
|
||||
val container = createContainerForLazyResolveWithJava(
|
||||
moduleContext,
|
||||
trace,
|
||||
@@ -105,9 +99,10 @@ object JvmAnalyzerFacade : AnalyzerFacade<JvmPlatformParameters>() {
|
||||
targetEnvironment,
|
||||
LookupTracker.DO_NOTHING,
|
||||
packagePartProvider,
|
||||
configuration,
|
||||
languageSettingsProvider.getLanguageVersionSettings(moduleInfo, project),
|
||||
useBuiltInsProvider = false, // TODO: load built-ins from module dependencies in IDE
|
||||
useLazyResolve = true
|
||||
useLazyResolve = true,
|
||||
compilerConfiguration = compilerConfiguration
|
||||
)
|
||||
|
||||
StorageComponentContainerContributor.getInstances(project).forEach { it.onContainerComposed(container, moduleInfo) }
|
||||
|
||||
@@ -24,9 +24,10 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.builtins.JvmBuiltInsPackageFragmentProvider
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.container.ComponentProvider
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.context.ContextForNewModule
|
||||
@@ -62,7 +63,6 @@ import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtens
|
||||
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import java.util.*
|
||||
|
||||
@@ -135,10 +135,13 @@ object TopDownAnalyzerFacadeForJVM {
|
||||
val sourceScope = if (separateModules) sourceModuleSearchScope else GlobalSearchScope.allScope(project)
|
||||
val moduleClassResolver = SourceOrBinaryModuleClassResolver(sourceScope)
|
||||
|
||||
val languageVersionSettings =
|
||||
configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT)
|
||||
|
||||
val optionalBuiltInsModule =
|
||||
if (configuration.getBoolean(JVMConfigurationKeys.ADD_BUILT_INS_FROM_COMPILER_TO_DEPENDENCIES)) {
|
||||
if (createBuiltInsFromModule)
|
||||
JvmBuiltIns(storageManager).apply { initialize(module, configuration.languageVersionSettings) }.builtInsModule
|
||||
JvmBuiltIns(storageManager).apply { initialize(module, languageVersionSettings) }.builtInsModule
|
||||
else module.builtIns.builtInsModule
|
||||
}
|
||||
else null
|
||||
@@ -154,7 +157,7 @@ object TopDownAnalyzerFacadeForJVM {
|
||||
|
||||
val dependenciesContainer = createContainerForTopDownAnalyzerForJvm(
|
||||
dependenciesContext, trace, DeclarationProviderFactory.EMPTY, dependencyScope, lookupTracker,
|
||||
packagePartProvider(dependencyScope), moduleClassResolver, configuration
|
||||
packagePartProvider(dependencyScope), languageVersionSettings, moduleClassResolver, configuration
|
||||
)
|
||||
|
||||
StorageComponentContainerContributor.getInstances(project).forEach { it.onContainerComposed(dependenciesContainer, null) }
|
||||
@@ -170,22 +173,18 @@ object TopDownAnalyzerFacadeForJVM {
|
||||
}
|
||||
else null
|
||||
|
||||
val partProvider = packagePartProvider(sourceScope).let { fragment ->
|
||||
if (targetIds == null || incrementalComponents == null) fragment
|
||||
else IncrementalPackagePartProvider(fragment, targetIds.map(incrementalComponents::getIncrementalCache), storageManager)
|
||||
}
|
||||
|
||||
// Note that it's necessary to create container for sources _after_ creation of container for dependencies because
|
||||
// CliLightClassGenerationSupport#initialize is invoked when container is created, so only the last module descriptor is going
|
||||
// to be stored in CliLightClassGenerationSupport, and it better be the source one (otherwise light classes would not be found)
|
||||
// TODO: get rid of duplicate invocation of CodeAnalyzerInitializer#initialize, or refactor CliLightClassGenerationSupport
|
||||
val container = createContainerForTopDownAnalyzerForJvm(
|
||||
moduleContext, trace, declarationProviderFactory(storageManager, files), sourceScope, lookupTracker,
|
||||
partProvider, moduleClassResolver, configuration
|
||||
IncrementalPackagePartProvider.create(
|
||||
packagePartProvider(sourceScope), targetIds, incrementalComponents, storageManager
|
||||
),
|
||||
languageVersionSettings, moduleClassResolver, configuration
|
||||
).apply {
|
||||
initJvmBuiltInsForTopDownAnalysis()
|
||||
(partProvider as? IncrementalPackagePartProvider)?.deserializationConfiguration = get<DeserializationConfiguration>()
|
||||
|
||||
initJvmBuiltInsForTopDownAnalysis(module, languageVersionSettings)
|
||||
StorageComponentContainerContributor.getInstances(project).forEach { it.onContainerComposed(this, null) }
|
||||
}
|
||||
|
||||
@@ -254,7 +253,7 @@ object TopDownAnalyzerFacadeForJVM {
|
||||
fun createContextWithSealedModule(project: Project, configuration: CompilerConfiguration): MutableModuleContext =
|
||||
createModuleContext(project, configuration, false).apply {
|
||||
setDependencies(module, module.builtIns.builtInsModule)
|
||||
(module.builtIns as JvmBuiltIns).initialize(module, configuration.languageVersionSettings)
|
||||
(module.builtIns as JvmBuiltIns).initialize(module, configuration.get(LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT))
|
||||
}
|
||||
|
||||
private fun createModuleContext(
|
||||
|
||||
@@ -19,7 +19,10 @@ package org.jetbrains.kotlin.analyzer.common
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.analyzer.*
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.container.useImpl
|
||||
@@ -31,7 +34,6 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.frontend.di.configureCommon
|
||||
import org.jetbrains.kotlin.frontend.di.configureModule
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory
|
||||
@@ -49,11 +51,9 @@ import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmen
|
||||
* See [TargetPlatform.Default]
|
||||
*/
|
||||
object DefaultAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||
private val compilerConfiguration = CompilerConfiguration().apply {
|
||||
languageVersionSettings = LanguageVersionSettingsImpl(
|
||||
LanguageVersion.LATEST, ApiVersion.LATEST, setOf(LanguageFeature.MultiPlatformProjects)
|
||||
)
|
||||
}
|
||||
private val languageVersionSettings = LanguageVersionSettingsImpl(
|
||||
LanguageVersion.LATEST, ApiVersion.LATEST, setOf(LanguageFeature.MultiPlatformProjects)
|
||||
)
|
||||
|
||||
private class SourceModuleInfo(
|
||||
override val name: Name,
|
||||
@@ -135,7 +135,7 @@ object DefaultAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||
useImpl<ResolveSession>()
|
||||
useImpl<LazyTopDownAnalyzer>()
|
||||
useImpl<FileScopeProviderImpl>()
|
||||
configureCommon(compilerConfiguration)
|
||||
useInstance(languageVersionSettings)
|
||||
useImpl<CompilerDeserializationConfiguration>()
|
||||
useInstance(packagePartProvider)
|
||||
useInstance(declarationProviderFactory)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
public class CommonConfigurationKeys {
|
||||
private CommonConfigurationKeys() {
|
||||
}
|
||||
|
||||
public static final CompilerConfigurationKey<LanguageVersionSettings> LANGUAGE_VERSION_SETTINGS =
|
||||
CompilerConfigurationKey.create("language version settings");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> DISABLE_INLINE =
|
||||
CompilerConfigurationKey.create("disable inline");
|
||||
|
||||
public static final CompilerConfigurationKey<String> MODULE_NAME =
|
||||
CompilerConfigurationKey.create("module name");
|
||||
}
|
||||
@@ -1,35 +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.config
|
||||
|
||||
object CommonConfigurationKeys {
|
||||
@JvmField
|
||||
val LANGUAGE_VERSION_SETTINGS = CompilerConfigurationKey<LanguageVersionSettings>("language version settings")
|
||||
|
||||
@JvmField
|
||||
val SKIP_METADATA_VERSION_CHECK = CompilerConfigurationKey<Boolean>("skip metadata version check")
|
||||
|
||||
@JvmField
|
||||
val DISABLE_INLINE = CompilerConfigurationKey<Boolean>("disable inline")
|
||||
|
||||
@JvmField
|
||||
val MODULE_NAME = CompilerConfigurationKey<String>("module name")
|
||||
}
|
||||
|
||||
var CompilerConfiguration.languageVersionSettings: LanguageVersionSettings
|
||||
get() = get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, LanguageVersionSettingsImpl.DEFAULT)
|
||||
set(value) = put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, value)
|
||||
@@ -23,11 +23,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class CompilerConfigurationKey<T> {
|
||||
Key<T> ideaKey;
|
||||
|
||||
public CompilerConfigurationKey(@NotNull @NonNls String name) {
|
||||
private CompilerConfigurationKey(@NotNull @NonNls String name) {
|
||||
ideaKey = Key.create(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> CompilerConfigurationKey<T> create(@NotNull @NonNls String name) {
|
||||
return new CompilerConfigurationKey<T>(name);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cfg.WhenMissingCase;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.config.LanguageVersion;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken;
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken;
|
||||
@@ -64,11 +63,11 @@ public interface Errors {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> UNSUPPORTED = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Pair<LanguageFeature, LanguageVersionSettings>> UNSUPPORTED_FEATURE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, LanguageFeature> UNSUPPORTED_FEATURE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Throwable> EXCEPTION_FROM_ANALYZER = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, Pair<LanguageFeature, LanguageVersionSettings>> EXPERIMENTAL_FEATURE_WARNING = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, Pair<LanguageFeature, LanguageVersionSettings>> EXPERIMENTAL_FEATURE_ERROR = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, LanguageFeature> EXPERIMENTAL_FEATURE_WARNING = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, LanguageFeature> EXPERIMENTAL_FEATURE_ERROR = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -454,6 +453,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtPropertyDelegate> ABSTRACT_DELEGATED_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtPropertyAccessor> ACCESSOR_FOR_DELEGATED_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtPropertyDelegate> DELEGATED_PROPERTY_IN_INTERFACE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtPropertyDelegate> LOCAL_VARIABLE_WITH_DELEGATE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtProperty> PROPERTY_WITH_NO_TYPE_NO_INITIALIZER = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
@@ -702,7 +702,7 @@ public interface Errors {
|
||||
DiagnosticFactory2<KtExpression, String, Collection<? extends ResolvedCall<?>>> DELEGATE_PD_METHOD_NONE_APPLICABLE = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<KtSimpleNameExpression, KotlinType> COMPARE_TO_TYPE_MISMATCH = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, String> YIELD_IS_RESERVED = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> UNDERSCORE_IS_RESERVED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, String> INVALID_CHARACTERS = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
@@ -892,7 +892,6 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<KtClass> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
||||
DiagnosticFactory0<KtObjectDeclaration> NESTED_OBJECT_NOT_ALLOWED = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
||||
|
||||
//Inline and inlinable parameters
|
||||
DiagnosticFactory2<KtElement, DeclarationDescriptor, DeclarationDescriptor> NON_PUBLIC_CALL_FROM_PUBLIC_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT);
|
||||
|
||||
@@ -237,6 +237,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(ABSTRACT_DELEGATED_PROPERTY, "Delegated property cannot be abstract");
|
||||
MAP.put(ACCESSOR_FOR_DELEGATED_PROPERTY, "Delegated property cannot have accessors with non-default implementations");
|
||||
MAP.put(DELEGATED_PROPERTY_IN_INTERFACE, "Delegated properties are not allowed in interfaces");
|
||||
MAP.put(LOCAL_VARIABLE_WITH_DELEGATE, "Local variables are not allowed to have delegates");
|
||||
|
||||
MAP.put(INAPPLICABLE_LATEINIT_MODIFIER, "''lateinit'' modifier {0}", STRING);
|
||||
|
||||
@@ -437,7 +438,6 @@ public class DefaultErrorMessages {
|
||||
|
||||
MAP.put(INACCESSIBLE_OUTER_CLASS_EXPRESSION, "Expression is inaccessible from a nested class ''{0}'', use ''inner'' keyword to make the class inner", NAME);
|
||||
MAP.put(NESTED_CLASS_NOT_ALLOWED, "Nested class is not allowed here, use 'inner' keyword to make the class inner");
|
||||
MAP.put(NESTED_OBJECT_NOT_ALLOWED, "Objects inside inner classes are prohibited");
|
||||
|
||||
MAP.put(HAS_NEXT_MISSING, "hasNext() cannot be called on iterator() of type ''{0}''", RENDER_TYPE);
|
||||
MAP.put(HAS_NEXT_FUNCTION_AMBIGUITY, "hasNext() is ambiguous for iterator() of type ''{0}''", RENDER_TYPE);
|
||||
@@ -463,7 +463,6 @@ public class DefaultErrorMessages {
|
||||
MAP.put(COMPARE_TO_TYPE_MISMATCH, "''compareTo()'' must return Int, but returns {0}", RENDER_TYPE);
|
||||
|
||||
MAP.put(UNDERSCORE_IS_RESERVED, "Names _, __, ___, ..., are reserved in Kotlin");
|
||||
MAP.put(YIELD_IS_RESERVED, "{0}", STRING);
|
||||
MAP.put(INVALID_CHARACTERS, "Name {0}", STRING);
|
||||
|
||||
MAP.put(INAPPLICABLE_OPERATOR_MODIFIER, "''operator'' modifier is inapplicable on this function: {0}", STRING);
|
||||
@@ -600,11 +599,30 @@ public class DefaultErrorMessages {
|
||||
MAP.put(UNSAFE_IMPLICIT_INVOKE_CALL, "Reference has a nullable type ''{0}'', use explicit ''?.invoke()'' to make a function-like call instead", RENDER_TYPE);
|
||||
MAP.put(AMBIGUOUS_LABEL, "Ambiguous label");
|
||||
MAP.put(UNSUPPORTED, "Unsupported [{0}]", STRING);
|
||||
|
||||
MAP.put(UNSUPPORTED_FEATURE, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.UNSUPPORTED));
|
||||
MAP.put(EXPERIMENTAL_FEATURE_WARNING, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.WARNING));
|
||||
MAP.put(EXPERIMENTAL_FEATURE_ERROR, "{0}", new LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.ERROR));
|
||||
|
||||
MAP.put(UNSUPPORTED_FEATURE, "The feature is {0}", new DiagnosticParameterRenderer<LanguageFeature>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(LanguageFeature feature, @NotNull RenderingContext renderingContext) {
|
||||
LanguageVersion version = feature.getSinceVersion();
|
||||
return version != null
|
||||
? "only available since Kotlin " + version.getVersionString() + ": " + feature.getPresentableText()
|
||||
: "experimental and should be turned on explicitly via a command line option or in IDE settings: " + feature.getPresentableText();
|
||||
}
|
||||
});
|
||||
MAP.put(EXPERIMENTAL_FEATURE_WARNING, "The feature is experimental: {0}", new DiagnosticParameterRenderer<LanguageFeature>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(LanguageFeature feature, @NotNull RenderingContext renderingContext) {
|
||||
return feature.getPresentableText();
|
||||
}
|
||||
});
|
||||
MAP.put(EXPERIMENTAL_FEATURE_ERROR, "The experimental feature is disabled: {0}", new DiagnosticParameterRenderer<LanguageFeature>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(LanguageFeature feature, @NotNull RenderingContext renderingContext) {
|
||||
return feature.getPresentableText();
|
||||
}
|
||||
});
|
||||
MAP.put(EXCEPTION_FROM_ANALYZER, "Internal Error occurred while analyzing this expression:\n{0}", THROWABLE);
|
||||
MAP.put(UNNECESSARY_SAFE_CALL, "Unnecessary safe call on a non-null receiver of type {0}", RENDER_TYPE);
|
||||
MAP.put(UNEXPECTED_SAFE_CALL, "Safe-call is not allowed here");
|
||||
|
||||
@@ -1,69 +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.diagnostics.rendering
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
|
||||
class LanguageFeatureMessageRenderer @JvmOverloads constructor(
|
||||
private val type: Type,
|
||||
private val useHtml: Boolean = false
|
||||
): DiagnosticParameterRenderer<Pair<LanguageFeature, LanguageVersionSettings>> {
|
||||
|
||||
enum class Type {
|
||||
UNSUPPORTED,
|
||||
WARNING,
|
||||
ERROR
|
||||
}
|
||||
|
||||
override fun render(obj: Pair<LanguageFeature, LanguageVersionSettings>, renderingContext: RenderingContext): String {
|
||||
val (feature, settings) = obj
|
||||
val since = feature.sinceVersion
|
||||
|
||||
val sb = StringBuilder()
|
||||
sb.append("The feature \"").append(feature.presentableName).append("\" is ")
|
||||
|
||||
when (type) {
|
||||
Type.UNSUPPORTED ->
|
||||
when {
|
||||
since == null ->
|
||||
sb.append("experimental and should be enabled explicitly")
|
||||
since > settings.languageVersion ->
|
||||
sb.append("only available since language version ").append(since.versionString)
|
||||
feature.sinceApiVersion > settings.apiVersion ->
|
||||
sb.append("only available since API version ").append(feature.sinceApiVersion.versionString)
|
||||
else ->
|
||||
sb.append("disabled")
|
||||
}
|
||||
|
||||
Type.WARNING -> sb.append("experimental")
|
||||
Type.ERROR -> sb.append("experimental and disabled")
|
||||
}
|
||||
|
||||
val hintUrl = feature.hintUrl
|
||||
if (hintUrl != null) {
|
||||
if (useHtml) {
|
||||
sb.append(" (").append("see more <a href=\"").append(hintUrl).append("\">here</a>)")
|
||||
}
|
||||
else {
|
||||
sb.append(" (see: ").append(hintUrl).append(")")
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.kotlin.frontend.di
|
||||
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.container.useImpl
|
||||
@@ -26,11 +26,9 @@ import org.jetbrains.kotlin.context.LazyResolveToken
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.types.expressions.DeclarationScopeProviderForLocalClassifierAnalyzer
|
||||
import org.jetbrains.kotlin.types.expressions.LocalClassDescriptorHolder
|
||||
import org.jetbrains.kotlin.types.expressions.LocalLazyDeclarationResolver
|
||||
@@ -66,17 +64,12 @@ fun StorageComponentContainer.configureModule(
|
||||
useInstance(trace)
|
||||
}
|
||||
|
||||
fun StorageComponentContainer.configureCommon(configuration: CompilerConfiguration) {
|
||||
useInstance(configuration)
|
||||
useInstance(configuration.languageVersionSettings)
|
||||
}
|
||||
|
||||
fun createContainerForBodyResolve(
|
||||
moduleContext: ModuleContext,
|
||||
bindingTrace: BindingTrace,
|
||||
platform: TargetPlatform,
|
||||
statementFilter: StatementFilter,
|
||||
compilerConfiguration: CompilerConfiguration
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): StorageComponentContainer = createContainer("BodyResolve", platform) {
|
||||
configureModule(moduleContext, platform, bindingTrace)
|
||||
|
||||
@@ -84,7 +77,7 @@ fun createContainerForBodyResolve(
|
||||
|
||||
useInstance(LookupTracker.DO_NOTHING)
|
||||
useInstance(BodyResolveCache.ThrowException)
|
||||
configureCommon(compilerConfiguration)
|
||||
useInstance(languageVersionSettings)
|
||||
|
||||
useImpl<BodyResolver>()
|
||||
}
|
||||
@@ -95,7 +88,7 @@ fun createContainerForLazyBodyResolve(
|
||||
bindingTrace: BindingTrace,
|
||||
platform: TargetPlatform,
|
||||
bodyResolveCache: BodyResolveCache,
|
||||
compilerConfiguration: CompilerConfiguration
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): StorageComponentContainer = createContainer("LazyBodyResolve", platform) {
|
||||
configureModule(moduleContext, platform, bindingTrace)
|
||||
|
||||
@@ -103,7 +96,7 @@ fun createContainerForLazyBodyResolve(
|
||||
useInstance(kotlinCodeAnalyzer)
|
||||
useInstance(kotlinCodeAnalyzer.fileScopeProvider)
|
||||
useInstance(bodyResolveCache)
|
||||
configureCommon(compilerConfiguration)
|
||||
useInstance(languageVersionSettings)
|
||||
useImpl<LazyTopDownAnalyzer>()
|
||||
useImpl<BasicAbsentDescriptorHandler>()
|
||||
}
|
||||
@@ -113,7 +106,7 @@ fun createContainerForLazyLocalClassifierAnalyzer(
|
||||
bindingTrace: BindingTrace,
|
||||
platform: TargetPlatform,
|
||||
lookupTracker: LookupTracker,
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
statementFilter: StatementFilter,
|
||||
localClassDescriptorHolder: LocalClassDescriptorHolder
|
||||
): StorageComponentContainer = createContainer("LocalClassifierAnalyzer", platform) {
|
||||
@@ -133,7 +126,7 @@ fun createContainerForLazyLocalClassifierAnalyzer(
|
||||
useImpl<DeclarationScopeProviderForLocalClassifierAnalyzer>()
|
||||
useImpl<LocalLazyDeclarationResolver>()
|
||||
|
||||
configureCommon(compilerConfiguration)
|
||||
useInstance(languageVersionSettings)
|
||||
useInstance(statementFilter)
|
||||
}
|
||||
|
||||
@@ -143,14 +136,15 @@ fun createContainerForLazyResolve(
|
||||
bindingTrace: BindingTrace,
|
||||
platform: TargetPlatform,
|
||||
targetEnvironment: TargetEnvironment,
|
||||
compilerConfiguration: CompilerConfiguration
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): StorageComponentContainer = createContainer("LazyResolve", platform) {
|
||||
configureModule(moduleContext, platform, bindingTrace)
|
||||
|
||||
useInstance(declarationProviderFactory)
|
||||
useInstance(LookupTracker.DO_NOTHING)
|
||||
|
||||
configureCommon(compilerConfiguration)
|
||||
useInstance(languageVersionSettings)
|
||||
//TODO: need to propagate full CompilerConfiguration to frontend
|
||||
useInstance(CompilerConfiguration.EMPTY)
|
||||
|
||||
useImpl<FileScopeProviderImpl>()
|
||||
useImpl<CompilerDeserializationConfiguration>()
|
||||
@@ -160,12 +154,14 @@ fun createContainerForLazyResolve(
|
||||
useImpl<ResolveSession>()
|
||||
}
|
||||
|
||||
fun createLazyResolveSession(moduleContext: ModuleContext, files: Collection<KtFile>): ResolveSession =
|
||||
createContainerForLazyResolve(
|
||||
moduleContext,
|
||||
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
|
||||
BindingTraceContext(),
|
||||
TargetPlatform.Default,
|
||||
CompilerEnvironment,
|
||||
CompilerConfiguration.EMPTY
|
||||
).get<ResolveSession>()
|
||||
@JvmOverloads
|
||||
fun createLazyResolveSession(
|
||||
moduleContext: ModuleContext,
|
||||
declarationProviderFactory: DeclarationProviderFactory,
|
||||
bindingTrace: BindingTrace,
|
||||
platform: TargetPlatform,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
targetEnvironment: TargetEnvironment = CompilerEnvironment
|
||||
): ResolveSession = createContainerForLazyResolve(
|
||||
moduleContext, declarationProviderFactory, bindingTrace, platform, targetEnvironment, languageVersionSettings
|
||||
).get<ResolveSession>()
|
||||
|
||||
@@ -461,25 +461,6 @@ fun checkReservedPrefixWord(sink: DiagnosticSink, element: PsiElement, word: Str
|
||||
}
|
||||
}
|
||||
|
||||
fun checkReservedYield(expression: KtSimpleNameExpression?, sink: DiagnosticSink) {
|
||||
// do not force identifier calculation for elements from stubs.
|
||||
if (expression?.getReferencedName() != "yield") return
|
||||
|
||||
val identifier = expression.getIdentifier() ?: return
|
||||
|
||||
if (identifier.node.elementType == KtTokens.IDENTIFIER && "yield" == identifier.text) {
|
||||
sink.report(Errors.YIELD_IS_RESERVED.on(identifier, "Identifier 'yield' is reserved. Use backticks to call it: `yield`"))
|
||||
}
|
||||
}
|
||||
|
||||
val MESSAGE_FOR_YIELD_BEFORE_LAMBDA = "Reserved yield block/lambda. Use 'yield() { ... }' or 'yield(fun...)'"
|
||||
|
||||
fun checkReservedYieldBeforeLambda(element: PsiElement, sink: DiagnosticSink) {
|
||||
KtPsiUtil.getPreviousWord(element, "yield")?.let {
|
||||
sink.report(Errors.YIELD_IS_RESERVED.on(it, MESSAGE_FOR_YIELD_BEFORE_LAMBDA))
|
||||
}
|
||||
}
|
||||
|
||||
fun KtElement.nonStaticOuterClasses(): Sequence<KtClass> {
|
||||
return generateSequence(containingClass()) { if (it.isInner()) it.containingClass() else null }
|
||||
}
|
||||
|
||||
@@ -311,12 +311,6 @@ private fun findFirstLeafWhollyInRange(file: PsiFile, range: TextRange): PsiElem
|
||||
return if (elementRange.endOffset <= range.endOffset) element else null
|
||||
}
|
||||
|
||||
val PsiElement.textRangeWithoutComments: TextRange
|
||||
get() {
|
||||
val firstNonCommentChild = children.firstOrNull { it !is PsiWhiteSpace && it !is PsiComment } ?: return textRange
|
||||
return TextRange(firstNonCommentChild.startOffset, endOffset)
|
||||
}
|
||||
|
||||
// ---------------------------------- Debug/logging ----------------------------------------------------------------------------------------
|
||||
|
||||
fun PsiElement.getElementTextWithContext(): String {
|
||||
|
||||
@@ -16,14 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
|
||||
class CompilerDeserializationConfiguration(configuration: CompilerConfiguration) : DeserializationConfiguration {
|
||||
override val typeAliasesAllowed = configuration.languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)
|
||||
|
||||
override val skipMetadataVersionCheck = configuration.getBoolean(CommonConfigurationKeys.SKIP_METADATA_VERSION_CHECK)
|
||||
class CompilerDeserializationConfiguration(private val languageVersionSettings: LanguageVersionSettings) : DeserializationConfiguration {
|
||||
override val typeAliasesAllowed: Boolean
|
||||
get() = languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)
|
||||
}
|
||||
|
||||
@@ -711,7 +711,7 @@ class DeclarationsChecker(
|
||||
}
|
||||
}
|
||||
else if (property.typeReference == null && !languageVersionSettings.supportsFeature(LanguageFeature.ShortSyntaxForPropertyGetters)) {
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(property, LanguageFeature.ShortSyntaxForPropertyGetters to languageVersionSettings))
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(property, LanguageFeature.ShortSyntaxForPropertyGetters))
|
||||
}
|
||||
else if (noExplicitTypeOrGetterType(property)) {
|
||||
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import kotlin.Pair;
|
||||
import kotlin.TuplesKt;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.collections.SetsKt;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
@@ -325,8 +324,7 @@ public class DescriptorResolver {
|
||||
Function0<List<VariableDescriptor>> destructuringVariables;
|
||||
if (destructuringDeclaration != null) {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.DestructuringLambdaParameters)) {
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(valueParameter,
|
||||
TuplesKt.to(LanguageFeature.DestructuringLambdaParameters, languageVersionSettings)));
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(valueParameter, LanguageFeature.DestructuringLambdaParameters));
|
||||
}
|
||||
|
||||
destructuringVariables = new Function0<List<VariableDescriptor>>() {
|
||||
@@ -744,8 +742,7 @@ public class DescriptorResolver {
|
||||
else if (!languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)) {
|
||||
typeResolver.resolveAbbreviatedType(scopeWithTypeParameters, typeReference, trace);
|
||||
PsiElement typeAliasKeyword = typeAlias.getTypeAliasKeyword();
|
||||
trace.report(UNSUPPORTED_FEATURE.on(typeAliasKeyword != null ? typeAliasKeyword : typeAlias,
|
||||
TuplesKt.to(LanguageFeature.TypeAliases, languageVersionSettings)));
|
||||
trace.report(UNSUPPORTED_FEATURE.on(typeAliasKeyword != null ? typeAliasKeyword : typeAlias, LanguageFeature.TypeAliases));
|
||||
typeAliasDescriptor.initialize(
|
||||
typeParameterDescriptors,
|
||||
ErrorUtils.createErrorType(name.asString()),
|
||||
|
||||
@@ -77,7 +77,7 @@ class LocalVariableResolver(
|
||||
val delegateExpression = property.delegateExpression
|
||||
if (delegateExpression != null) {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.LocalDelegatedProperties)) {
|
||||
context.trace.report(UNSUPPORTED_FEATURE.on(property.delegate!!, LanguageFeature.LocalDelegatedProperties to languageVersionSettings))
|
||||
context.trace.report(LOCAL_VARIABLE_WITH_DELEGATE.on(property.delegate!!))
|
||||
}
|
||||
|
||||
if (propertyDescriptor is VariableDescriptorWithAccessors) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.resolve.checkers.UnderscoreChecker;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_CLASS_NOT_ALLOWED;
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_OBJECT_NOT_ALLOWED;
|
||||
import static org.jetbrains.kotlin.lexer.KtTokens.*;
|
||||
import static org.jetbrains.kotlin.psi.KtStubbedPsiUtil.getContainingDeclaration;
|
||||
|
||||
@@ -220,20 +219,10 @@ public class ModifiersChecker {
|
||||
|
||||
public void checkModifiersForDeclaration(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
|
||||
checkNestedClassAllowed(modifierListOwner, descriptor);
|
||||
checkObjectInsideInnerClass(modifierListOwner, descriptor);
|
||||
checkTypeParametersModifiers(modifierListOwner);
|
||||
checkModifierListCommon(modifierListOwner, descriptor);
|
||||
}
|
||||
|
||||
private void checkObjectInsideInnerClass(@NotNull KtDeclaration modifierListOwner, @NotNull MemberDescriptor descriptor) {
|
||||
if (modifierListOwner instanceof KtObjectDeclaration) {
|
||||
KtObjectDeclaration ktObject = (KtObjectDeclaration) modifierListOwner;
|
||||
if (!ktObject.isLocal() && isIllegalNestedClass(descriptor)) {
|
||||
trace.report(NESTED_OBJECT_NOT_ALLOWED.on(ktObject));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkModifierListCommon(@NotNull KtDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor) {
|
||||
AnnotationUseSiteTargetChecker.INSTANCE.check(modifierListOwner, descriptor, trace);
|
||||
runDeclarationCheckers(modifierListOwner, descriptor);
|
||||
|
||||
@@ -54,22 +54,23 @@ object ModifierCheckerCore {
|
||||
COMPATIBLE_FOR_CLASSES_ONLY
|
||||
}
|
||||
|
||||
private val defaultVisibilityTargets = EnumSet.of(CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS,
|
||||
private val defaultVisibilityTargets = EnumSet.of(CLASS_ONLY, OBJECT, INTERFACE, INNER_CLASS, ENUM_CLASS, ANNOTATION_CLASS,
|
||||
MEMBER_FUNCTION, TOP_LEVEL_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER,
|
||||
MEMBER_PROPERTY, TOP_LEVEL_PROPERTY, CONSTRUCTOR, TYPEALIAS)
|
||||
|
||||
val possibleTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>(
|
||||
ENUM_KEYWORD to EnumSet.of(ENUM_CLASS),
|
||||
ABSTRACT_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
OPEN_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, OBJECT, MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
SEALED_KEYWORD to EnumSet.of(CLASS_ONLY),
|
||||
INNER_KEYWORD to EnumSet.of(CLASS_ONLY),
|
||||
ABSTRACT_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INNER_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
OPEN_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INNER_CLASS, INTERFACE, MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, INNER_CLASS, ENUM_CLASS, OBJECT, MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
SEALED_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS),
|
||||
// We should have also CLASS_ONLY here because INNER_CLASS is not always perfectly identified
|
||||
INNER_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS),
|
||||
OVERRIDE_KEYWORD to EnumSet.of(MEMBER_PROPERTY, MEMBER_FUNCTION),
|
||||
PRIVATE_KEYWORD to defaultVisibilityTargets,
|
||||
PUBLIC_KEYWORD to defaultVisibilityTargets,
|
||||
INTERNAL_KEYWORD to defaultVisibilityTargets,
|
||||
PROTECTED_KEYWORD to EnumSet.of(CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS,
|
||||
PROTECTED_KEYWORD to EnumSet.of(CLASS_ONLY, OBJECT, INTERFACE, INNER_CLASS, ENUM_CLASS, ANNOTATION_CLASS,
|
||||
MEMBER_FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, MEMBER_PROPERTY, CONSTRUCTOR, TYPEALIAS),
|
||||
IN_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION),
|
||||
OUT_KEYWORD to EnumSet.of(TYPE_PARAMETER, TYPE_PROJECTION),
|
||||
@@ -77,7 +78,7 @@ object ModifierCheckerCore {
|
||||
VARARG_KEYWORD to EnumSet.of(VALUE_PARAMETER, PROPERTY_PARAMETER),
|
||||
COMPANION_KEYWORD to EnumSet.of(OBJECT),
|
||||
LATEINIT_KEYWORD to EnumSet.of(MEMBER_PROPERTY),
|
||||
DATA_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS),
|
||||
DATA_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS),
|
||||
INLINE_KEYWORD to EnumSet.of(FUNCTION, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER),
|
||||
NOINLINE_KEYWORD to EnumSet.of(VALUE_PARAMETER),
|
||||
TAILREC_KEYWORD to EnumSet.of(FUNCTION),
|
||||
@@ -88,8 +89,8 @@ object ModifierCheckerCore {
|
||||
CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY),
|
||||
OPERATOR_KEYWORD to EnumSet.of(FUNCTION),
|
||||
INFIX_KEYWORD to EnumSet.of(FUNCTION),
|
||||
HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS),
|
||||
IMPL_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, MEMBER_FUNCTION, TOP_LEVEL_PROPERTY, MEMBER_PROPERTY, CONSTRUCTOR, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, TYPEALIAS)
|
||||
HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE, CLASS_ONLY, OBJECT, INTERFACE, INNER_CLASS, ENUM_CLASS, ANNOTATION_CLASS),
|
||||
IMPL_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, MEMBER_FUNCTION, TOP_LEVEL_PROPERTY, MEMBER_PROPERTY, CONSTRUCTOR, CLASS_ONLY, OBJECT, INTERFACE, INNER_CLASS, ENUM_CLASS, ANNOTATION_CLASS, TYPEALIAS)
|
||||
)
|
||||
|
||||
val featureDependencies = mapOf(
|
||||
@@ -103,8 +104,8 @@ object ModifierCheckerCore {
|
||||
LanguageFeature.Coroutines to LanguageFeature.ErrorOnCoroutines
|
||||
)
|
||||
|
||||
val noWarningOnFeature = mapOf(
|
||||
LanguageFeature.Coroutines to LanguageFeature.DoNotWarnOnCoroutines
|
||||
val warningOnFeature = mapOf(
|
||||
LanguageFeature.Coroutines to LanguageFeature.WarnOnCoroutines
|
||||
)
|
||||
|
||||
val featureDependenciesTargets = mapOf(
|
||||
@@ -120,16 +121,16 @@ object ModifierCheckerCore {
|
||||
)
|
||||
|
||||
val possibleParentTargetMap = mapOf<KtModifierKeywordToken, Set<KotlinTarget>>(
|
||||
INNER_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS),
|
||||
OVERRIDE_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
INNER_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS, ENUM_CLASS),
|
||||
OVERRIDE_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
INTERFACE, ENUM_CLASS, ENUM_ENTRY),
|
||||
PROTECTED_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT),
|
||||
INTERNAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
PROTECTED_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS, ENUM_CLASS, COMPANION_OBJECT),
|
||||
INTERNAL_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
ENUM_CLASS, ENUM_ENTRY, FILE),
|
||||
PRIVATE_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
PRIVATE_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
INTERFACE, ENUM_CLASS, ENUM_ENTRY, FILE),
|
||||
COMPANION_KEYWORD to EnumSet.of(CLASS_ONLY, ENUM_CLASS, INTERFACE),
|
||||
FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
FINAL_KEYWORD to EnumSet.of(CLASS_ONLY, INNER_CLASS, LOCAL_CLASS, OBJECT, OBJECT_LITERAL,
|
||||
ENUM_CLASS, ENUM_ENTRY, ANNOTATION_CLASS, FILE)
|
||||
)
|
||||
|
||||
@@ -168,13 +169,6 @@ object ModifierCheckerCore {
|
||||
result += compatibilityForClassesRegister(PRIVATE_KEYWORD, ABSTRACT_KEYWORD)
|
||||
|
||||
result += incompatibilityRegister(CROSSINLINE_KEYWORD, NOINLINE_KEYWORD)
|
||||
|
||||
// 1. subclasses contained inside a sealed class can not be instantiated, because their constructors needs
|
||||
// an instance of an outer sealed (effectively abstract) class
|
||||
// 2. subclasses of a non-top-level sealed class must be declared inside the class
|
||||
// (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md)
|
||||
result += incompatibilityRegister(SEALED_KEYWORD, INNER_KEYWORD)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -283,7 +277,6 @@ object ModifierCheckerCore {
|
||||
val errorOnDependencyFeature = errorOnFeature[dependency]?.let { languageVersionSettings.supportsFeature(it) } ?: false
|
||||
val supportsFeature = languageVersionSettings.supportsFeature(dependency)
|
||||
|
||||
val diagnosticData = dependency to languageVersionSettings
|
||||
if (!supportsFeature || errorOnDependencyFeature) {
|
||||
val restrictedTargets = featureDependenciesTargets[dependency]
|
||||
if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) {
|
||||
@@ -291,17 +284,17 @@ object ModifierCheckerCore {
|
||||
}
|
||||
|
||||
if (!supportsFeature) {
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData))
|
||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, dependency))
|
||||
}
|
||||
else if (errorOnDependencyFeature) {
|
||||
trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData))
|
||||
trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, dependency))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
val pairedNoWarningFeature = noWarningOnFeature[dependency]
|
||||
if (pairedNoWarningFeature != null && !languageVersionSettings.supportsFeature(pairedNoWarningFeature)) {
|
||||
trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, diagnosticData))
|
||||
val pairedWarningFeature = warningOnFeature[dependency]
|
||||
if (pairedWarningFeature != null && languageVersionSettings.supportsFeature(pairedWarningFeature)) {
|
||||
trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, dependency))
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -66,7 +66,7 @@ object OperatorModifierChecker {
|
||||
|
||||
private fun checkSupportsFeature(feature: LanguageFeature, languageVersionSettings: LanguageVersionSettings, diagnosticHolder: DiagnosticSink, modifier: PsiElement) {
|
||||
if (!languageVersionSettings.supportsFeature(feature)) {
|
||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, feature to languageVersionSettings))
|
||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, feature))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,13 +214,11 @@ class QualifiedExpressionResolver {
|
||||
return null
|
||||
}
|
||||
|
||||
val resolvedDescriptor = resolveToPackageOrClass(
|
||||
val packageOrClassDescriptor = resolveToPackageOrClass(
|
||||
path.subList(0, path.size - 1), moduleDescriptor, trace,
|
||||
packageFragmentForVisibilityCheck, scopeForFirstPart = null, position = QualifierPosition.IMPORT
|
||||
) ?: return null
|
||||
|
||||
val packageOrClassDescriptor = (resolvedDescriptor as? TypeAliasDescriptor)?.let { it.classDescriptor ?: return null } ?: resolvedDescriptor
|
||||
|
||||
return LazyExplicitImportScope(packageOrClassDescriptor, packageFragmentForVisibilityCheck, lastPart.name, aliasName) {
|
||||
candidates ->
|
||||
|
||||
@@ -414,12 +412,12 @@ class QualifiedExpressionResolver {
|
||||
val qualifierDescriptor = when (receiver) {
|
||||
is PackageQualifier -> {
|
||||
val childPackageFQN = receiver.descriptor.fqName.child(name)
|
||||
receiver.descriptor.module.getPackage(childPackageFQN).check { !it.isEmpty() } ?:
|
||||
receiver.descriptor.module.getPackage(childPackageFQN).takeIfNot { it.isEmpty() } ?:
|
||||
receiver.descriptor.memberScope.getContributedClassifier(name, location)
|
||||
}
|
||||
is ClassQualifier -> receiver.staticScope.getContributedClassifier(name, location)
|
||||
null -> context.scope.findClassifier(name, location) ?:
|
||||
context.scope.ownerDescriptor.module.getPackage(FqName.ROOT.child(name)).check { !it.isEmpty() }
|
||||
context.scope.ownerDescriptor.module.getPackage(FqName.ROOT.child(name)).takeIfNot { it.isEmpty() }
|
||||
is ReceiverValue -> receiver.type.memberScope.memberScopeAsImportingScope().findClassifier(name, location)
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import org.jetbrains.kotlin.psi.debugText.getDebugText
|
||||
import org.jetbrains.kotlin.psi.psiUtil.checkReservedYield
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
|
||||
import org.jetbrains.kotlin.resolve.PossiblyBareType.bare
|
||||
import org.jetbrains.kotlin.resolve.PossiblyBareType.type
|
||||
@@ -228,8 +227,6 @@ class TypeResolver(
|
||||
}
|
||||
|
||||
val referenceExpression = type.referenceExpression ?: return
|
||||
|
||||
checkReservedYield(referenceExpression, c.trace)
|
||||
c.trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, classifier)
|
||||
|
||||
result = resolveTypeForClassifier(c, classifier, qualifierResolutionResult, type, annotations)
|
||||
@@ -512,7 +509,7 @@ class TypeResolver(
|
||||
return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor)
|
||||
}
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.TypeAliases)) {
|
||||
c.trace.report(UNSUPPORTED_FEATURE.on(type, LanguageFeature.TypeAliases to languageVersionSettings))
|
||||
c.trace.report(UNSUPPORTED_FEATURE.on(type, LanguageFeature.TypeAliases))
|
||||
return createErrorTypeForTypeConstructor(c, projectionFromAllQualifierParts, typeConstructor)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer
|
||||
import org.jetbrains.kotlin.types.expressions.FakeCallResolver
|
||||
import java.util.*
|
||||
|
||||
class CallCompleter(
|
||||
@@ -61,6 +62,7 @@ class CallCompleter(
|
||||
private val dataFlowAnalyzer: DataFlowAnalyzer,
|
||||
private val callCheckers: Iterable<CallChecker>,
|
||||
private val builtIns: KotlinBuiltIns,
|
||||
private val fakeCallResolver: FakeCallResolver,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val compilerConfiguration: CompilerConfiguration
|
||||
) {
|
||||
@@ -84,7 +86,7 @@ class CallCompleter(
|
||||
temporaryTrace.commit()
|
||||
}
|
||||
|
||||
if (resolvedCall != null && context.trace.wantsDiagnostics()) {
|
||||
if (resolvedCall != null) {
|
||||
val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall)
|
||||
resolvedCall.variableCall.call.calleeExpression
|
||||
else
|
||||
@@ -93,12 +95,10 @@ class CallCompleter(
|
||||
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
|
||||
else resolvedCall.call.callElement
|
||||
|
||||
val callCheckerContext = CallCheckerContext(context, languageVersionSettings, compilerConfiguration)
|
||||
for (callChecker in callCheckers) {
|
||||
callChecker.check(resolvedCall, reportOn, callCheckerContext)
|
||||
|
||||
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||
callChecker.check(resolvedCall.variableCall, reportOn, callCheckerContext)
|
||||
if (context.trace.wantsDiagnostics()) {
|
||||
val callCheckerContext = CallCheckerContext(context, languageVersionSettings, compilerConfiguration)
|
||||
for (callChecker in callCheckers) {
|
||||
callChecker.check(resolvedCall, reportOn, callCheckerContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.OverrideResolver;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*;
|
||||
@@ -186,9 +185,7 @@ public class ValueArgumentsToParametersMapper {
|
||||
ValueArgumentName argumentName = argument.getArgumentName();
|
||||
assert argumentName != null;
|
||||
ValueParameterDescriptor valueParameterDescriptor = parameterByName.get(argumentName.getAsName());
|
||||
KtSimpleNameExpression nameReference = argumentName.getReferenceExpression();
|
||||
|
||||
KtPsiUtilKt.checkReservedYield(nameReference, candidateCall.getTrace());
|
||||
KtReferenceExpression nameReference = argumentName.getReferenceExpression();
|
||||
if (!candidate.hasStableParameterNames() && nameReference != null) {
|
||||
report(NAMED_ARGUMENTS_NOT_ALLOWED.on(
|
||||
nameReference,
|
||||
|
||||
@@ -35,8 +35,7 @@ class CallableReferenceCompatibilityChecker : CallChecker {
|
||||
val callableReferenceResolvedCall = argumentExpression.callableReference.getResolvedCall(context.trace.bindingContext) ?: continue@inner
|
||||
if (callableReferenceResolvedCall.call.isCallableReference() &&
|
||||
callableReferenceResolvedCall.candidateDescriptor.typeParameters.isNotEmpty()) {
|
||||
context.trace.report(Errors.UNSUPPORTED_FEATURE.on(argumentExpression,
|
||||
typeInferenceForCallableReferencesFeature to context.languageVersionSettings))
|
||||
context.trace.report(Errors.UNSUPPORTED_FEATURE.on(argumentExpression, typeInferenceForCallableReferencesFeature))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,15 +89,14 @@ object BuilderFunctionsCallChecker : CallChecker {
|
||||
}
|
||||
|
||||
fun checkCoroutinesFeature(languageVersionSettings: LanguageVersionSettings, diagnosticHolder: DiagnosticSink, reportOn: PsiElement) {
|
||||
val diagnosticData = LanguageFeature.Coroutines to languageVersionSettings
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) {
|
||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, diagnosticData))
|
||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, LanguageFeature.Coroutines))
|
||||
}
|
||||
else if (languageVersionSettings.supportsFeature(LanguageFeature.ErrorOnCoroutines)) {
|
||||
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(reportOn, diagnosticData))
|
||||
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(reportOn, LanguageFeature.Coroutines))
|
||||
}
|
||||
else if (!languageVersionSettings.supportsFeature(LanguageFeature.DoNotWarnOnCoroutines)) {
|
||||
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(reportOn, diagnosticData))
|
||||
else if (languageVersionSettings.supportsFeature(LanguageFeature.WarnOnCoroutines)) {
|
||||
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(reportOn, LanguageFeature.Coroutines))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,13 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.resolvedCallUtil
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtCallElement
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.KtThisExpression
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.getReceiverValueWithSmartCast
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
@@ -76,16 +74,29 @@ fun ResolvedCall<*>.getExplicitReceiverValue(): ReceiverValue? {
|
||||
}
|
||||
}
|
||||
|
||||
fun ResolvedCall<*>.getImplicitReceiverValue(): ImplicitReceiver? =
|
||||
getImplicitReceivers().firstOrNull() as? ImplicitReceiver
|
||||
fun ResolvedCall<*>.getImplicitReceiverValue(): ImplicitReceiver? {
|
||||
return when (explicitReceiverKind) {
|
||||
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> extensionReceiver ?: dispatchReceiver
|
||||
ExplicitReceiverKind.DISPATCH_RECEIVER -> extensionReceiver
|
||||
ExplicitReceiverKind.EXTENSION_RECEIVER -> dispatchReceiver
|
||||
else -> null
|
||||
} as? ImplicitReceiver
|
||||
}
|
||||
|
||||
fun ResolvedCall<*>.getImplicitReceivers(): Collection<ReceiverValue> =
|
||||
when (explicitReceiverKind) {
|
||||
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> listOfNotNull(extensionReceiver, dispatchReceiver)
|
||||
ExplicitReceiverKind.DISPATCH_RECEIVER -> listOfNotNull(extensionReceiver)
|
||||
ExplicitReceiverKind.EXTENSION_RECEIVER -> listOfNotNull(dispatchReceiver)
|
||||
ExplicitReceiverKind.BOTH_RECEIVERS -> emptyList()
|
||||
}
|
||||
fun ResolvedCall<*>.getImplicitReceivers(): Collection<ReceiverValue> {
|
||||
if (this is VariableAsFunctionResolvedCall) {
|
||||
val receivers = variableCall.getImplicitReceivers() + functionCall.getImplicitReceivers()
|
||||
assert(receivers.size <= 3) { "There are ${receivers.size} for $this call" }
|
||||
return receivers
|
||||
}
|
||||
|
||||
return when (explicitReceiverKind) {
|
||||
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> listOfNotNull(dispatchReceiver, extensionReceiver)
|
||||
ExplicitReceiverKind.DISPATCH_RECEIVER -> listOfNotNull(extensionReceiver)
|
||||
ExplicitReceiverKind.EXTENSION_RECEIVER -> listOfNotNull(dispatchReceiver)
|
||||
ExplicitReceiverKind.BOTH_RECEIVERS -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.hasSafeNullableReceiver(context: CallResolutionContext<*>): Boolean {
|
||||
if (!call.isSafeCall()) return false
|
||||
|
||||
@@ -42,7 +42,7 @@ object UnderscoreChecker : DeclarationChecker {
|
||||
diagnosticHolder.report(Errors.UNDERSCORE_IS_RESERVED.on(identifier))
|
||||
}
|
||||
else if (isValidSingleUnderscore && !languageVersionSettings.supportsFeature(LanguageFeature.SingleUnderscoreForParameterName)) {
|
||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(identifier, LanguageFeature.SingleUnderscoreForParameterName to languageVersionSettings))
|
||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(identifier, LanguageFeature.SingleUnderscoreForParameterName))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user