Merge branch 'master' into KT-18765
This commit is contained in:
Generated
+7
-1
@@ -52,7 +52,13 @@
|
|||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
<component name="SuppressABINotification">
|
<component name="SuppressABINotification">
|
||||||
<option name="isSuppressed" value="true" />
|
<option name="modulesWithSuppressedNotConfigured">
|
||||||
|
<set>
|
||||||
|
<option value="kotlin-stdlib-common" />
|
||||||
|
<option value="kotlin-stdlib-js" />
|
||||||
|
<option value="kotlin-test-common" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="WebServicesPlugin" addRequiredLibraries="true" />
|
<component name="WebServicesPlugin" addRequiredLibraries="true" />
|
||||||
<component name="com.sixrr.metrics.MetricsReloaded">
|
<component name="com.sixrr.metrics.MetricsReloaded">
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Generate Tests" type="GradleRunConfiguration" factoryName="Gradle">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$/generators" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" value="" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="generateTests" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" value="" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProtobufSettings">
|
||||||
|
<option name="includePaths">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||||
<a href="http://slack.kotlinlang.org/"><img src="http://slack.kotlinlang.org/badge.svg" height="20"></a>
|
<a href="http://slack.kotlinlang.org/"><img src="http://slack.kotlinlang.org/badge.svg" height="20"></a>
|
||||||
[](https://teamcity.jetbrains.com/viewType.html?buildTypeId=bt345&branch_Kotlin=%3Cdefault%3E&tab=buildTypeStatusDiv)
|
[](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_dev_Compiler&branch_Kotlin_dev=%3Cdefault%3E&tab=buildTypeStatusDiv)
|
||||||
[](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jetbrains.kotlin%22)
|
[](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jetbrains.kotlin%22)
|
||||||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
@@ -40,6 +40,8 @@ In order to build Kotlin distribution you need to have:
|
|||||||
|
|
||||||
For local development, if you're not working on bytecode generation or the standard library, it's OK to have only JDK 8 installed, and to point all of the environment variables mentioned above to your JDK 8 installation.
|
For local development, if you're not working on bytecode generation or the standard library, it's OK to have only JDK 8 installed, and to point all of the environment variables mentioned above to your JDK 8 installation.
|
||||||
|
|
||||||
|
You also can use [Gradle properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_properties_and_system_properties) to setup JDK_* variables.
|
||||||
|
|
||||||
> Note: The JDK 6 for MacOS is not available on Oracle's site. You can [download it here](https://support.apple.com/kb/DL1572).
|
> Note: The JDK 6 for MacOS is not available on Oracle's site. You can [download it here](https://support.apple.com/kb/DL1572).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
@@ -85,8 +87,6 @@ Refer to [libraries/ReadMe.md](libraries/ReadMe.md) for details.
|
|||||||
|
|
||||||
Working with the Kotlin project requires IntelliJ IDEA 2017.3. You can download an Early Access Preview version of IntelliJ IDEA 2017.3 [here](https://www.jetbrains.com/idea/nextversion/).
|
Working with the Kotlin project requires IntelliJ IDEA 2017.3. You can download an Early Access Preview version of IntelliJ IDEA 2017.3 [here](https://www.jetbrains.com/idea/nextversion/).
|
||||||
|
|
||||||
The [root Kotlin project](https://github.com/JetBrains/kotlin) should be imported into IDEA as gradle project.
|
|
||||||
|
|
||||||
To import the project in Intellij choose project directory in Open project dialog. Then, after project opened, Select
|
To import the project in Intellij choose project directory in Open project dialog. Then, after project opened, Select
|
||||||
`File` -> `New...` -> `Module from Existing Sources` in the menu, and select `build.gradle.kts` file in the project's root folder.
|
`File` -> `New...` -> `Module from Existing Sources` in the menu, and select `build.gradle.kts` file in the project's root folder.
|
||||||
|
|
||||||
@@ -127,6 +127,11 @@ Also the [JavaScript translation](https://github.com/JetBrains/kotlin/blob/maste
|
|||||||
|
|
||||||
Some of the code in the standard library is created by generating code from templates. See the [README](libraries/stdlib/ReadMe.md) in the stdlib section for how run the code generator. The existing templates can be used as examples for creating new ones.
|
Some of the code in the standard library is created by generating code from templates. See the [README](libraries/stdlib/ReadMe.md) in the stdlib section for how run the code generator. The existing templates can be used as examples for creating new ones.
|
||||||
|
|
||||||
|
### Running specific generated tests
|
||||||
|
|
||||||
|
If you need to debug a specific generated test, ensure that you have the `Working directory` in your IntelliJ run configuration set
|
||||||
|
to the root directory of this project. If you don't, every test you try to run will fail with a `No such file or directory` exception.
|
||||||
|
|
||||||
## Submitting patches
|
## Submitting patches
|
||||||
|
|
||||||
The best way to submit a patch is to [fork the project on github](https://help.github.com/articles/fork-a-repo/) then send us a
|
The best way to submit a patch is to [fork the project on github](https://help.github.com/articles/fork-a-repo/) then send us a
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runtimeJar()
|
runtimeJar()
|
||||||
|
sourcesJar()
|
||||||
|
javadocJar()
|
||||||
|
|
||||||
testsJar()
|
testsJar()
|
||||||
|
|
||||||
|
|||||||
+42
-4
@@ -211,6 +211,22 @@ public final class DebugJsAstProtoBuf {
|
|||||||
* <code>UNBOX_CHAR = 4;</code>
|
* <code>UNBOX_CHAR = 4;</code>
|
||||||
*/
|
*/
|
||||||
UNBOX_CHAR(3, 4),
|
UNBOX_CHAR(3, 4),
|
||||||
|
/**
|
||||||
|
* <code>SUSPEND_CALL = 5;</code>
|
||||||
|
*/
|
||||||
|
SUSPEND_CALL(4, 5),
|
||||||
|
/**
|
||||||
|
* <code>COROUTINE_RESULT = 6;</code>
|
||||||
|
*/
|
||||||
|
COROUTINE_RESULT(5, 6),
|
||||||
|
/**
|
||||||
|
* <code>COROUTINE_CONTROLLER = 7;</code>
|
||||||
|
*/
|
||||||
|
COROUTINE_CONTROLLER(6, 7),
|
||||||
|
/**
|
||||||
|
* <code>COROUTINE_RECEIVER = 8;</code>
|
||||||
|
*/
|
||||||
|
COROUTINE_RECEIVER(7, 8),
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,6 +245,22 @@ public final class DebugJsAstProtoBuf {
|
|||||||
* <code>UNBOX_CHAR = 4;</code>
|
* <code>UNBOX_CHAR = 4;</code>
|
||||||
*/
|
*/
|
||||||
public static final int UNBOX_CHAR_VALUE = 4;
|
public static final int UNBOX_CHAR_VALUE = 4;
|
||||||
|
/**
|
||||||
|
* <code>SUSPEND_CALL = 5;</code>
|
||||||
|
*/
|
||||||
|
public static final int SUSPEND_CALL_VALUE = 5;
|
||||||
|
/**
|
||||||
|
* <code>COROUTINE_RESULT = 6;</code>
|
||||||
|
*/
|
||||||
|
public static final int COROUTINE_RESULT_VALUE = 6;
|
||||||
|
/**
|
||||||
|
* <code>COROUTINE_CONTROLLER = 7;</code>
|
||||||
|
*/
|
||||||
|
public static final int COROUTINE_CONTROLLER_VALUE = 7;
|
||||||
|
/**
|
||||||
|
* <code>COROUTINE_RECEIVER = 8;</code>
|
||||||
|
*/
|
||||||
|
public static final int COROUTINE_RECEIVER_VALUE = 8;
|
||||||
|
|
||||||
|
|
||||||
public final int getNumber() { return value; }
|
public final int getNumber() { return value; }
|
||||||
@@ -239,6 +271,10 @@ public final class DebugJsAstProtoBuf {
|
|||||||
case 2: return WRAP_FUNCTION;
|
case 2: return WRAP_FUNCTION;
|
||||||
case 3: return TO_BOXED_CHAR;
|
case 3: return TO_BOXED_CHAR;
|
||||||
case 4: return UNBOX_CHAR;
|
case 4: return UNBOX_CHAR;
|
||||||
|
case 5: return SUSPEND_CALL;
|
||||||
|
case 6: return COROUTINE_RESULT;
|
||||||
|
case 7: return COROUTINE_CONTROLLER;
|
||||||
|
case 8: return COROUTINE_RECEIVER;
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49361,10 +49397,12 @@ public final class DebugJsAstProtoBuf {
|
|||||||
"t.Fragment*@\n\013SideEffects\022\021\n\rAFFECTS_STA" +
|
"t.Fragment*@\n\013SideEffects\022\021\n\rAFFECTS_STA" +
|
||||||
"TE\020\001\022\024\n\020DEPENDS_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016" +
|
"TE\020\001\022\024\n\020DEPENDS_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016" +
|
||||||
"InlineStrategy\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PL" +
|
"InlineStrategy\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PL" +
|
||||||
"ACE\020\001\022\016\n\nNOT_INLINE\020\002*c\n\017SpecialFunction" +
|
"ACE\020\001\022\016\n\nNOT_INLINE\020\002*\275\001\n\017SpecialFunctio" +
|
||||||
"\022\032\n\026DEFINE_INLINE_FUNCTION\020\001\022\021\n\rWRAP_FUN" +
|
"n\022\032\n\026DEFINE_INLINE_FUNCTION\020\001\022\021\n\rWRAP_FU" +
|
||||||
"CTION\020\002\022\021\n\rTO_BOXED_CHAR\020\003\022\016\n\nUNBOX_CHAR" +
|
"NCTION\020\002\022\021\n\rTO_BOXED_CHAR\020\003\022\016\n\nUNBOX_CHA" +
|
||||||
"\020\004B\024B\022DebugJsAstProtoBuf"
|
"R\020\004\022\020\n\014SUSPEND_CALL\020\005\022\024\n\020COROUTINE_RESUL" +
|
||||||
|
"T\020\006\022\030\n\024COROUTINE_CONTROLLER\020\007\022\026\n\022COROUTI" +
|
||||||
|
"NE_RECEIVER\020\010B\024B\022DebugJsAstProtoBuf"
|
||||||
};
|
};
|
||||||
org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||||
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
|
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
|
||||||
|
|||||||
+2
-3
@@ -6,11 +6,11 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
val repos = listOf(
|
val repos = listOfNotNull(
|
||||||
System.getProperty("bootstrap.kotlin.repo"),
|
System.getProperty("bootstrap.kotlin.repo"),
|
||||||
"https://repo.gradle.org/gradle/repo",
|
"https://repo.gradle.org/gradle/repo",
|
||||||
"https://plugins.gradle.org/m2",
|
"https://plugins.gradle.org/m2",
|
||||||
"http://repository.jetbrains.com/utils/").filterNotNull()
|
"http://repository.jetbrains.com/utils/")
|
||||||
|
|
||||||
extra["bootstrapKotlinVersion"] = bootstrapKotlinVersion
|
extra["bootstrapKotlinVersion"] = bootstrapKotlinVersion
|
||||||
|
|
||||||
@@ -105,7 +105,6 @@ extra["JDK_9"] = jdkPathIfFound("9")
|
|||||||
extra["versions.protobuf-java"] = "2.6.1"
|
extra["versions.protobuf-java"] = "2.6.1"
|
||||||
extra["versions.javax.inject"] = "1"
|
extra["versions.javax.inject"] = "1"
|
||||||
extra["versions.jsr305"] = "1.3.9"
|
extra["versions.jsr305"] = "1.3.9"
|
||||||
extra["versions.cli-parser"] = "1.1.2"
|
|
||||||
extra["versions.jansi"] = "1.16"
|
extra["versions.jansi"] = "1.16"
|
||||||
extra["versions.jline"] = "3.3.1"
|
extra["versions.jline"] = "3.3.1"
|
||||||
extra["versions.junit"] = "4.12"
|
extra["versions.junit"] = "4.12"
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.artifacts.ProjectDependency
|
||||||
|
import org.gradle.api.file.ConfigurableFileCollection
|
||||||
|
import org.gradle.api.file.FileCollection
|
||||||
|
import org.gradle.api.internal.ConventionTask
|
||||||
|
import org.gradle.api.plugins.ExtensionAware
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
import org.gradle.api.tasks.compile.AbstractCompile
|
||||||
|
import org.gradle.kotlin.dsl.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun Project.configureInstrumentation() {
|
||||||
|
plugins.matching { it::class.java.canonicalName.startsWith("org.jetbrains.kotlin.gradle.plugin") }.all {
|
||||||
|
// When we change the output classes directory, Gradle will automatically configure
|
||||||
|
// the test compile tasks to use the instrumented classes. Normally this is fine,
|
||||||
|
// however, it causes problems for Kotlin projects:
|
||||||
|
|
||||||
|
// The "internal" modifier can be used to restrict access to the same module.
|
||||||
|
// To make it possible to use internal methods from the main source set in test classes,
|
||||||
|
// the Kotlin Gradle plugin adds the original output directory of the Java task
|
||||||
|
// as "friendly directory" which makes it possible to access internal members
|
||||||
|
// of the main module. Also this directory should be available on classpath during compilation
|
||||||
|
|
||||||
|
// This fails when we change the classes dir. The easiest fix is to prepend the
|
||||||
|
// classes from the "friendly directory" to the compile classpath.
|
||||||
|
val testCompile = tasks.findByName("compileTestKotlin") as AbstractCompile?
|
||||||
|
testCompile?.doFirst {
|
||||||
|
val mainSourceSet = the<JavaPluginConvention>().sourceSets.getByName("main")
|
||||||
|
testCompile.classpath = (testCompile.classpath
|
||||||
|
- mainSourceSet.output.classesDirs
|
||||||
|
+ files((mainSourceSet as ExtensionAware).extra.get("classesDirsCopy")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
the<JavaPluginConvention>().sourceSets.all { sourceSetParam ->
|
||||||
|
// This copy will ignore filters, but they are unlikely to be used.
|
||||||
|
val classesDirs = (sourceSetParam.output.classesDirs as ConfigurableFileCollection).from as Collection<Any>
|
||||||
|
|
||||||
|
val classesDirsCopy = project.files(classesDirs.toTypedArray()).filter { it.exists() }
|
||||||
|
(sourceSetParam as ExtensionAware).extra.set("classesDirsCopy", classesDirsCopy)
|
||||||
|
|
||||||
|
logger.info("Saving old sources dir for project ${project.name}")
|
||||||
|
val instrumentedClassesDir = File(project.buildDir, "classes/${sourceSetParam.name}-instrumented")
|
||||||
|
(sourceSetParam.output.classesDirs as ConfigurableFileCollection).setFrom(instrumentedClassesDir)
|
||||||
|
val instrumentTask = project.tasks.create(sourceSetParam.getTaskName("instrument", "classes"), IntelliJInstrumentCodeTask::class.java)
|
||||||
|
instrumentTask.apply {
|
||||||
|
dependsOn(sourceSetParam.classesTaskName).onlyIf { !classesDirsCopy.isEmpty }
|
||||||
|
sourceSet = sourceSetParam
|
||||||
|
originalClassesDirs = classesDirsCopy
|
||||||
|
output = instrumentedClassesDir
|
||||||
|
}
|
||||||
|
|
||||||
|
instrumentTask.outputs.dir(instrumentedClassesDir)
|
||||||
|
// Ensure that our task is invoked when the source set is built
|
||||||
|
sourceSetParam.compiledBy(instrumentTask)
|
||||||
|
@Suppress("UNUSED_EXPRESSION")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheableTask
|
||||||
|
open class IntelliJInstrumentCodeTask : ConventionTask() {
|
||||||
|
companion object {
|
||||||
|
private const val FILTER_ANNOTATION_REGEXP_CLASS = "com.intellij.ant.ClassFilterAnnotationRegexp"
|
||||||
|
private const val LOADER_REF = "java2.loader"
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceSet: SourceSet? = null
|
||||||
|
|
||||||
|
@Input
|
||||||
|
var originalClassesDirs: FileCollection? = null
|
||||||
|
|
||||||
|
@get:InputFiles
|
||||||
|
val sourceDirs: FileCollection
|
||||||
|
get() = project.files(sourceSet!!.allSource.srcDirs.filter { !sourceSet!!.resources.contains(it) && it.exists() })
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
var output: File? = null
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun instrumentClasses() {
|
||||||
|
logger.info("input files are: ${originalClassesDirs?.joinToString("; ", transform = { "'${it.name}'${if (it.exists()) "" else " (does not exists)" }"})}")
|
||||||
|
copyOriginalClasses()
|
||||||
|
|
||||||
|
val classpath = project.ideaSdkDeps("javac2.jar", "jdom.jar", "asm-all.jar", "jgoodies-forms.jar")
|
||||||
|
|
||||||
|
ant.withGroovyBuilder {
|
||||||
|
"taskdef"("name" to "instrumentIdeaExtensions",
|
||||||
|
"classpath" to classpath.asPath,
|
||||||
|
"loaderref" to LOADER_REF,
|
||||||
|
"classname" to "com.intellij.ant.InstrumentIdeaExtensions")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Compiling forms and instrumenting code with nullability preconditions")
|
||||||
|
val instrumentNotNull = prepareNotNullInstrumenting(classpath)
|
||||||
|
instrumentCode(sourceDirs, instrumentNotNull)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyOriginalClasses() {
|
||||||
|
project.copy {
|
||||||
|
from(originalClassesDirs)
|
||||||
|
into(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareNotNullInstrumenting(classpath: ConfigurableFileCollection): Boolean {
|
||||||
|
ant.withGroovyBuilder {
|
||||||
|
"typedef"("name" to "skip",
|
||||||
|
"classpath" to classpath.asPath,
|
||||||
|
"loaderref" to LOADER_REF,
|
||||||
|
"classname" to FILTER_ANNOTATION_REGEXP_CLASS)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun instrumentCode(srcDirs: FileCollection, instrumentNotNull: Boolean) {
|
||||||
|
val headlessOldValue = System.setProperty("java.awt.headless", "true")
|
||||||
|
|
||||||
|
// Instrumentation needs to have access to sources of forms for inclusion
|
||||||
|
val depSourceDirectorySets = project.configurations["compile"].dependencies.withType(ProjectDependency::class.java)
|
||||||
|
.map { p -> p.dependencyProject.the<JavaPluginConvention>().sourceSets.getByName("main").allSource.sourceDirectories }
|
||||||
|
val instrumentationClasspath =
|
||||||
|
depSourceDirectorySets.fold(sourceSet!!.compileClasspath) { acc, v -> acc + v }.asPath.also {
|
||||||
|
logger.info("Using following dependency source dirs: $it")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Running instrumentIdeaExtensions with srcdir=${srcDirs.asPath}}, destdir=$output and classpath=$instrumentationClasspath")
|
||||||
|
|
||||||
|
ant.withGroovyBuilder {
|
||||||
|
"instrumentIdeaExtensions"("srcdir" to srcDirs.asPath,
|
||||||
|
"destdir" to output,
|
||||||
|
"classpath" to instrumentationClasspath,
|
||||||
|
"includeantruntime" to false,
|
||||||
|
"instrumentNotNull" to instrumentNotNull) {
|
||||||
|
if (instrumentNotNull) {
|
||||||
|
ant.withGroovyBuilder {
|
||||||
|
"skip"("pattern" to "kotlin/Metadata")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headlessOldValue != null) {
|
||||||
|
System.setProperty("java.awt.headless", headlessOldValue)
|
||||||
|
} else {
|
||||||
|
System.clearProperty("java.awt.headless")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,8 @@ data class JdkId(val explicit: Boolean, val majorVersion: JdkMajorVersion, var v
|
|||||||
fun Project.getConfiguredJdks(): List<JdkId> {
|
fun Project.getConfiguredJdks(): List<JdkId> {
|
||||||
val res = arrayListOf<JdkId>()
|
val res = arrayListOf<JdkId>()
|
||||||
for (jdkMajorVersion in JdkMajorVersion.values()) {
|
for (jdkMajorVersion in JdkMajorVersion.values()) {
|
||||||
val explicitJdkEnvVal = System.getenv(jdkMajorVersion.name)
|
val explicitJdkEnvVal = findProperty(jdkMajorVersion.name)?.toString()
|
||||||
|
?: System.getenv(jdkMajorVersion.name)
|
||||||
?: jdkAlternativeVarNames[jdkMajorVersion]?.mapNotNull { System.getenv(it) }?.firstOrNull()
|
?: jdkAlternativeVarNames[jdkMajorVersion]?.mapNotNull { System.getenv(it) }?.firstOrNull()
|
||||||
?: continue
|
?: continue
|
||||||
val explicitJdk = File(explicitJdkEnvVal)
|
val explicitJdk = File(explicitJdkEnvVal)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
@@ -161,4 +162,8 @@ object CodegenUtil {
|
|||||||
(1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" }
|
(1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" }
|
||||||
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression
|
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getActualDeclarations(file: KtFile): List<KtDeclaration> =
|
||||||
|
file.declarations.filterNot(KtDeclaration::hasExpectModifier)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,9 +103,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
|
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.extractReificationArgument;
|
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.*;
|
||||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isPossiblyUninitializedSingleton;
|
|
||||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.unwrapInitialSignatureDescriptor;
|
|
||||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*;
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*;
|
||||||
@@ -1611,7 +1609,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void returnExpression(KtExpression expr) {
|
public void returnExpression(@NotNull KtExpression expr) {
|
||||||
boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction;
|
boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction;
|
||||||
|
|
||||||
FunctionDescriptor originalSuspendLambdaDescriptor = getOriginalSuspendLambdaDescriptorFromContext(context);
|
FunctionDescriptor originalSuspendLambdaDescriptor = getOriginalSuspendLambdaDescriptorFromContext(context);
|
||||||
@@ -1644,7 +1642,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean endsWithReturn(KtElement bodyExpression) {
|
private static boolean endsWithReturn(@NotNull KtElement bodyExpression) {
|
||||||
if (bodyExpression instanceof KtBlockExpression) {
|
if (bodyExpression instanceof KtBlockExpression) {
|
||||||
List<KtExpression> statements = ((KtBlockExpression) bodyExpression).getStatements();
|
List<KtExpression> statements = ((KtBlockExpression) bodyExpression).getStatements();
|
||||||
return statements.size() > 0 && statements.get(statements.size() - 1) instanceof KtReturnExpression;
|
return statements.size() > 0 && statements.get(statements.size() - 1) instanceof KtReturnExpression;
|
||||||
@@ -1653,7 +1651,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
return bodyExpression instanceof KtReturnExpression;
|
return bodyExpression instanceof KtReturnExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isLambdaVoidBody(KtElement bodyExpression, Type returnType) {
|
private static boolean isLambdaVoidBody(@NotNull KtElement bodyExpression, @NotNull Type returnType) {
|
||||||
if (bodyExpression instanceof KtBlockExpression) {
|
if (bodyExpression instanceof KtBlockExpression) {
|
||||||
PsiElement parent = bodyExpression.getParent();
|
PsiElement parent = bodyExpression.getParent();
|
||||||
if (parent instanceof KtFunctionLiteral) {
|
if (parent instanceof KtFunctionLiteral) {
|
||||||
@@ -2658,6 +2656,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canSkipArrayCopyForSpreadArgument(KtExpression spreadArgument) {
|
||||||
|
ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt.getResolvedCall(spreadArgument, bindingContext);
|
||||||
|
if (resolvedCall == null) return false;
|
||||||
|
|
||||||
|
CallableDescriptor calleeDescriptor = resolvedCall.getResultingDescriptor();
|
||||||
|
return (calleeDescriptor instanceof ConstructorDescriptor) ||
|
||||||
|
CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) ||
|
||||||
|
(DescriptorUtils.getFqName(calleeDescriptor).asString().equals("kotlin.arrayOfNulls"));
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public StackValue genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) {
|
public StackValue genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) {
|
||||||
@@ -2680,10 +2687,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
Type arrayType = getArrayType(arrayOfReferences ? AsmTypes.OBJECT_TYPE : elementType);
|
Type arrayType = getArrayType(arrayOfReferences ? AsmTypes.OBJECT_TYPE : elementType);
|
||||||
return StackValue.operation(type, adapter -> {
|
return StackValue.operation(type, adapter -> {
|
||||||
gen(arguments.get(0).getArgumentExpression(), type);
|
KtExpression spreadArgument = arguments.get(0).getArgumentExpression();
|
||||||
v.dup();
|
gen(spreadArgument, type);
|
||||||
v.arraylength();
|
if (!canSkipArrayCopyForSpreadArgument(spreadArgument)) {
|
||||||
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
|
v.dup();
|
||||||
|
v.arraylength();
|
||||||
|
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
|
||||||
|
}
|
||||||
if (arrayOfReferences) {
|
if (arrayOfReferences) {
|
||||||
v.checkcast(type);
|
v.checkcast(type);
|
||||||
}
|
}
|
||||||
@@ -3904,7 +3914,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
|||||||
|
|
||||||
boolean isGetter = OperatorNameConventions.GET.equals(operationDescriptor.getName());
|
boolean isGetter = OperatorNameConventions.GET.equals(operationDescriptor.getName());
|
||||||
|
|
||||||
Callable callable = resolveToCallable(operationDescriptor, false, isGetter ? resolvedGetCall : resolvedSetCall);
|
ResolvedCall<FunctionDescriptor> resolvedCall = isGetter ? resolvedGetCall : resolvedSetCall;
|
||||||
|
assert resolvedCall != null : "No resolved call for " + operationDescriptor;
|
||||||
|
Callable callable = resolveToCallable(accessibleFunctionDescriptor(resolvedCall), false, resolvedCall);
|
||||||
Callable callableMethod = resolveToCallableMethod(operationDescriptor, false);
|
Callable callableMethod = resolveToCallableMethod(operationDescriptor, false);
|
||||||
Type[] argumentTypes = callableMethod.getParameterTypes();
|
Type[] argumentTypes = callableMethod.getParameterTypes();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression;
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
@@ -49,7 +51,9 @@ public abstract class FunctionGenerationStrategy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||||
codegen.returnExpression(declaration.getBodyExpression());
|
KtExpression bodyExpression = declaration.getBodyExpression();
|
||||||
|
assert bodyExpression != null : "Function has no body: " + PsiUtilsKt.getElementTextWithContext(declaration);
|
||||||
|
codegen.returnExpression(bodyExpression);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -813,27 +813,37 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
|||||||
);
|
);
|
||||||
|
|
||||||
boolean hasDispatchReceiver = !isStaticDeclaration(functionDescriptor) && !isNonDefaultInterfaceMember(functionDescriptor, state);
|
boolean hasDispatchReceiver = !isStaticDeclaration(functionDescriptor) && !isNonDefaultInterfaceMember(functionDescriptor, state);
|
||||||
int reg = hasDispatchReceiver ? 1 : 0;
|
|
||||||
boolean accessorIsConstructor = accessorDescriptor instanceof AccessorForConstructorDescriptor;
|
boolean accessorIsConstructor = accessorDescriptor instanceof AccessorForConstructorDescriptor;
|
||||||
|
|
||||||
|
int accessorParam = (hasDispatchReceiver && !accessorIsConstructor) ? 1 : 0;
|
||||||
|
int reg = hasDispatchReceiver ? 1 : 0;
|
||||||
if (!accessorIsConstructor && functionDescriptor instanceof ConstructorDescriptor) {
|
if (!accessorIsConstructor && functionDescriptor instanceof ConstructorDescriptor) {
|
||||||
iv.anew(callableMethod.getOwner());
|
iv.anew(callableMethod.getOwner());
|
||||||
iv.dup();
|
iv.dup();
|
||||||
reg = 0;
|
reg = 0;
|
||||||
|
accessorParam = 0;
|
||||||
}
|
}
|
||||||
else if (accessorIsConstructor || (accessorDescriptor != null && KotlinTypeMapper.isAccessor(accessorDescriptor) && hasDispatchReceiver)) {
|
else if (KotlinTypeMapper.isAccessor(accessorDescriptor) && (hasDispatchReceiver || accessorIsConstructor)) {
|
||||||
if (!CodegenUtilKt.isJvmStaticInObjectOrClass(functionDescriptor)) {
|
if (!CodegenUtilKt.isJvmStaticInObjectOrClass(functionDescriptor)) {
|
||||||
iv.load(0, OBJECT_TYPE);
|
iv.load(0, OBJECT_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Type argType : callableMethod.getParameterTypes()) {
|
Type[] calleeParameterTypes = callableMethod.getParameterTypes();
|
||||||
if (AsmTypes.DEFAULT_CONSTRUCTOR_MARKER.equals(argType)) {
|
Type[] accessorParameterTypes = accessorDescriptor != null
|
||||||
|
? typeMapper.mapToCallableMethod(accessorDescriptor, false).getParameterTypes()
|
||||||
|
: calleeParameterTypes;
|
||||||
|
for (Type calleeArgType: calleeParameterTypes) {
|
||||||
|
if (AsmTypes.DEFAULT_CONSTRUCTOR_MARKER.equals(calleeArgType)) {
|
||||||
iv.aconst(null);
|
iv.aconst(null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
iv.load(reg, argType);
|
Type accessorParameterType = accessorParameterTypes[accessorParam];
|
||||||
reg += argType.getSize();
|
iv.load(reg, accessorParameterType);
|
||||||
|
StackValue.coerce(accessorParameterType, calleeArgType, iv);
|
||||||
|
reg += accessorParameterType.getSize();
|
||||||
}
|
}
|
||||||
|
accessorParam++;
|
||||||
}
|
}
|
||||||
|
|
||||||
callableMethod.genInvokeInstruction(iv);
|
callableMethod.genInvokeInstruction(iv);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager
|
|||||||
import com.intellij.openapi.progress.ProcessCanceledException
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
import com.intellij.util.ArrayUtil
|
import com.intellij.util.ArrayUtil
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
|
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
||||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
@@ -241,7 +242,7 @@ class MultifileClassCodegenImpl(
|
|||||||
private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) {
|
private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) {
|
||||||
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
|
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
|
||||||
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
|
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
|
||||||
for (declaration in file.declarations) {
|
for (declaration in CodegenUtil.getActualDeclarations(file)) {
|
||||||
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
|
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
|
||||||
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
|
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
|
||||||
if (descriptor !is MemberDescriptor) {
|
if (descriptor !is MemberDescriptor) {
|
||||||
@@ -367,7 +368,7 @@ class MultifileClassCodegenImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun KtFile.hasDeclarationsForPartClass() =
|
private fun KtFile.hasDeclarationsForPartClass() =
|
||||||
declarations.any { it is KtProperty || it is KtFunction }
|
CodegenUtil.getActualDeclarations(this).any { it is KtProperty || it is KtFunction || it is KtTypeAlias }
|
||||||
|
|
||||||
private fun getCompiledPackageFragment(
|
private fun getCompiledPackageFragment(
|
||||||
facadeFqName: FqName, state: GenerationState
|
facadeFqName: FqName, state: GenerationState
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
|
|
||||||
import com.intellij.util.ArrayUtil
|
import com.intellij.util.ArrayUtil
|
||||||
|
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||||
import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext
|
import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext
|
||||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
|
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||||
@@ -28,16 +28,13 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
import org.jetbrains.kotlin.psi.KtTypeAlias
|
import org.jetbrains.kotlin.psi.KtTypeAlias
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.MultifileClass
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class MultifileClassPartCodegen(
|
class MultifileClassPartCodegen(
|
||||||
v: ClassBuilder,
|
v: ClassBuilder,
|
||||||
@@ -135,7 +132,7 @@ class MultifileClassPartCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun generateBody() {
|
override fun generateBody() {
|
||||||
for (declaration in element.declarations) {
|
for (declaration in CodegenUtil.getActualDeclarations(element)) {
|
||||||
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
|
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
|
||||||
genSimpleMember(declaration)
|
genSimpleMember(declaration)
|
||||||
}
|
}
|
||||||
@@ -161,25 +158,7 @@ class MultifileClassPartCodegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun generateKotlinMetadataAnnotation() {
|
override fun generateKotlinMetadataAnnotation() {
|
||||||
val members = ArrayList<DeclarationDescriptor>()
|
val (serializer, packageProto) = PackagePartCodegen.serializePackagePartMembers(this, partType)
|
||||||
for (declaration in element.declarations) {
|
|
||||||
when (declaration) {
|
|
||||||
is KtNamedFunction -> {
|
|
||||||
val functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration)
|
|
||||||
members.add(functionDescriptor ?: throw AssertionError("Function ${declaration.name} is not bound in ${element.name}"))
|
|
||||||
}
|
|
||||||
is KtProperty -> {
|
|
||||||
val property = bindingContext.get(BindingContext.VARIABLE, declaration)
|
|
||||||
members.add(property ?: throw AssertionError("Property ${declaration.name} is not bound in ${element.name}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val extension = JvmSerializerExtension(v.serializationBindings, state)
|
|
||||||
val serializer = DescriptorSerializer.createTopLevel(extension)
|
|
||||||
val builder = serializer.packagePartProto(packageFragment.fqName, members)
|
|
||||||
extension.serializeJvmPackage(builder, partType)
|
|
||||||
val packageProto = builder.build()
|
|
||||||
|
|
||||||
val extraFlags = if (shouldGeneratePartHierarchy) JvmAnnotationNames.METADATA_MULTIFILE_PARTS_INHERIT_FLAG else 0
|
val extraFlags = if (shouldGeneratePartHierarchy) JvmAnnotationNames.METADATA_MULTIFILE_PARTS_INHERIT_FLAG else 0
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
|||||||
import com.intellij.util.SmartList;
|
import com.intellij.util.SmartList;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
|
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
|
||||||
@@ -31,7 +32,6 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
|||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
@@ -98,9 +98,7 @@ public class PackageCodegenImpl implements PackageCodegen {
|
|||||||
|
|
||||||
List<KtClassOrObject> classOrObjects = new ArrayList<>();
|
List<KtClassOrObject> classOrObjects = new ArrayList<>();
|
||||||
|
|
||||||
for (KtDeclaration declaration : file.getDeclarations()) {
|
for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(file)) {
|
||||||
if (PsiUtilsKt.hasExpectModifier(declaration)) continue;
|
|
||||||
|
|
||||||
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
|
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
|
||||||
generatePackagePart = true;
|
generatePackagePart = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,22 +17,21 @@
|
|||||||
package org.jetbrains.kotlin.codegen;
|
package org.jetbrains.kotlin.codegen;
|
||||||
|
|
||||||
import com.intellij.util.ArrayUtil;
|
import com.intellij.util.ArrayUtil;
|
||||||
|
import kotlin.Pair;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
|
import kotlin.collections.CollectionsKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||||
import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
|
import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
|
||||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
|
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
|
||||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
|
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
|
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
|
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
|
||||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||||
import org.jetbrains.kotlin.psi.*;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
|
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
||||||
@@ -88,9 +87,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateBody() {
|
protected void generateBody() {
|
||||||
for (KtDeclaration declaration : element.getDeclarations()) {
|
for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(element)) {
|
||||||
if (PsiUtilsKt.hasExpectModifier(declaration)) continue;
|
|
||||||
|
|
||||||
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) {
|
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) {
|
||||||
genSimpleMember(declaration);
|
genSimpleMember(declaration);
|
||||||
}
|
}
|
||||||
@@ -103,34 +100,42 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateKotlinMetadataAnnotation() {
|
protected void generateKotlinMetadataAnnotation() {
|
||||||
List<DeclarationDescriptor> members = new ArrayList<>();
|
Pair<DescriptorSerializer, ProtoBuf.Package> serializedPart = serializePackagePartMembers(this, packagePartType);
|
||||||
for (KtDeclaration declaration : element.getDeclarations()) {
|
|
||||||
if (declaration instanceof KtNamedFunction) {
|
|
||||||
SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration);
|
|
||||||
members.add(functionDescriptor);
|
|
||||||
}
|
|
||||||
else if (declaration instanceof KtProperty) {
|
|
||||||
VariableDescriptor property = bindingContext.get(BindingContext.VARIABLE, declaration);
|
|
||||||
members.add(property);
|
|
||||||
}
|
|
||||||
else if (declaration instanceof KtTypeAlias) {
|
|
||||||
TypeAliasDescriptor typeAlias = bindingContext.get(BindingContext.TYPE_ALIAS, declaration);
|
|
||||||
members.add(typeAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JvmSerializerExtension extension = new JvmSerializerExtension(v.getSerializationBindings(), state);
|
|
||||||
DescriptorSerializer serializer = DescriptorSerializer.createTopLevel(extension);
|
|
||||||
ProtoBuf.Package.Builder builder = serializer.packagePartProto(element.getPackageFqName(), members);
|
|
||||||
extension.serializeJvmPackage(builder, packagePartType);
|
|
||||||
ProtoBuf.Package packageProto = builder.build();
|
|
||||||
|
|
||||||
WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.FILE_FACADE, 0, av -> {
|
WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.FILE_FACADE, 0, av -> {
|
||||||
writeAnnotationData(av, serializer, packageProto);
|
writeAnnotationData(av, serializedPart.getFirst(), serializedPart.getSecond());
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected static Pair<DescriptorSerializer, ProtoBuf.Package> serializePackagePartMembers(
|
||||||
|
@NotNull MemberCodegen<? extends KtFile> codegen,
|
||||||
|
@NotNull Type packagePartType
|
||||||
|
) {
|
||||||
|
BindingContext bindingContext = codegen.bindingContext;
|
||||||
|
List<DeclarationDescriptor> members = CollectionsKt.mapNotNull(CodegenUtil.getActualDeclarations(codegen.element), declaration -> {
|
||||||
|
if (declaration instanceof KtNamedFunction) {
|
||||||
|
return bindingContext.get(BindingContext.FUNCTION, declaration);
|
||||||
|
}
|
||||||
|
else if (declaration instanceof KtProperty) {
|
||||||
|
return bindingContext.get(BindingContext.VARIABLE, declaration);
|
||||||
|
}
|
||||||
|
else if (declaration instanceof KtTypeAlias) {
|
||||||
|
return bindingContext.get(BindingContext.TYPE_ALIAS, declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
JvmSerializerExtension extension = new JvmSerializerExtension(codegen.v.getSerializationBindings(), codegen.state);
|
||||||
|
DescriptorSerializer serializer = DescriptorSerializer.createTopLevel(extension);
|
||||||
|
ProtoBuf.Package.Builder builder = serializer.packagePartProto(codegen.element.getPackageFqName(), members);
|
||||||
|
extension.serializeJvmPackage(builder, packagePartType);
|
||||||
|
|
||||||
|
return new Pair<>(serializer, builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateSyntheticPartsAfterBody() {
|
protected void generateSyntheticPartsAfterBody() {
|
||||||
generateSyntheticAccessors();
|
generateSyntheticAccessors();
|
||||||
|
|||||||
@@ -179,6 +179,19 @@ public abstract class StackValue {
|
|||||||
return type == Type.VOID_TYPE ? none() : new OnStack(type);
|
return type == Type.VOID_TYPE ? none() : new OnStack(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static StackValue integerConstant(int value, @NotNull Type type) {
|
||||||
|
if (type == Type.LONG_TYPE) {
|
||||||
|
return constant(Long.valueOf(value), type);
|
||||||
|
}
|
||||||
|
else if (type == Type.BYTE_TYPE || type == Type.SHORT_TYPE || type == Type.INT_TYPE) {
|
||||||
|
return constant(Integer.valueOf(value), type);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new AssertionError("Unexpected integer type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static StackValue constant(@Nullable Object value, @NotNull Type type) {
|
public static StackValue constant(@Nullable Object value, @NotNull Type type) {
|
||||||
if (type == Type.BOOLEAN_TYPE) {
|
if (type == Type.BOOLEAN_TYPE) {
|
||||||
|
|||||||
+2
-1
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
@@ -70,6 +71,6 @@ class SuspendFunctionGenerationStrategy(
|
|||||||
|
|
||||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
this.codegen = codegen
|
this.codegen = codegen
|
||||||
codegen.returnExpression(declaration.bodyExpression)
|
codegen.returnExpression(declaration.bodyExpression ?: error("Function has no body: " + declaration.getElementTextWithContext()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+132
-97
@@ -16,10 +16,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.coroutines
|
package org.jetbrains.kotlin.codegen.coroutines
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.CustomFramesMethodAnalyzer
|
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.insnListOf
|
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
@@ -71,124 +69,161 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
|||||||
* - restore constructor arguments
|
* - restore constructor arguments
|
||||||
*/
|
*/
|
||||||
internal fun processUninitializedStores(methodNode: MethodNode) {
|
internal fun processUninitializedStores(methodNode: MethodNode) {
|
||||||
val interpreter = UninitializedNewValueMarkerInterpreter()
|
UninitializedStoresProcessor(methodNode, forCoroutines = true).run()
|
||||||
val frames = CustomFramesMethodAnalyzer("fake", methodNode, interpreter, ::UninitializedNewValueFrame).analyze()
|
|
||||||
|
|
||||||
for ((index, insn) in methodNode.instructions.toArray().withIndex()) {
|
|
||||||
val frame = frames[index] ?: continue
|
|
||||||
val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue
|
|
||||||
|
|
||||||
val copyUsages: Set<AbstractInsnNode> = interpreter.uninitializedValuesToCopyUsages[uninitializedValue.newInsn]!!
|
|
||||||
assert(copyUsages.size > 0) { "At least DUP copy operation expected" }
|
|
||||||
|
|
||||||
// Value generated by NEW wasn't store to local/field (only DUPed)
|
|
||||||
if (copyUsages.size == 1) continue
|
|
||||||
|
|
||||||
(copyUsages + uninitializedValue.newInsn).forEach {
|
|
||||||
methodNode.instructions.remove(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size
|
|
||||||
val storedTypes = arrayListOf<Type>()
|
|
||||||
var nextVarIndex = methodNode.maxLocals
|
|
||||||
|
|
||||||
for (i in 0 until indexOfConstructorArgumentFromTopOfStack) {
|
|
||||||
val value = frame.getStack(frame.stackSize - 1 - i)
|
|
||||||
val type = value.type
|
|
||||||
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), nextVarIndex))
|
|
||||||
nextVarIndex += type.size
|
|
||||||
storedTypes.add(type)
|
|
||||||
}
|
|
||||||
methodNode.maxLocals = Math.max(methodNode.maxLocals, nextVarIndex)
|
|
||||||
|
|
||||||
methodNode.instructions.insertBefore(insn, insnListOf(
|
|
||||||
TypeInsnNode(Opcodes.NEW, uninitializedValue.newInsn.desc),
|
|
||||||
InsnNode(Opcodes.DUP)
|
|
||||||
))
|
|
||||||
|
|
||||||
for (type in storedTypes.reversed()) {
|
|
||||||
nextVarIndex -= type.size
|
|
||||||
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), nextVarIndex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UninitializedNewValue(
|
class UninitializedStoresProcessor(private val methodNode: MethodNode, private val forCoroutines: Boolean) {
|
||||||
val newInsn: TypeInsnNode, val internalName: String
|
private val isInSpecialMethod = methodNode.name == "<init>" || methodNode.name == "<clinit>"
|
||||||
) : StrictBasicValue(Type.getObjectType(internalName)) {
|
|
||||||
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
|
|
||||||
}
|
|
||||||
|
|
||||||
private class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame<BasicValue>(nLocals, nStack) {
|
fun run() {
|
||||||
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<BasicValue>?) {
|
val interpreter = UninitializedNewValueMarkerInterpreter()
|
||||||
val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null
|
|
||||||
|
|
||||||
super.execute(insn, interpreter)
|
val frames = CustomFramesMethodAnalyzer(
|
||||||
|
"fake", methodNode, interpreter,
|
||||||
|
this::UninitializedNewValueFrame
|
||||||
|
).analyze()
|
||||||
|
|
||||||
if (replaceTopValueWithInitialized) {
|
for ((index, insn) in methodNode.instructions.toArray().withIndex()) {
|
||||||
// Drop top value
|
val frame = frames[index] ?: continue
|
||||||
val value = pop() as UninitializedNewValue
|
val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue
|
||||||
|
|
||||||
// uninitialized value become initialized after <init> call
|
val newInsn = uninitializedValue.newInsn
|
||||||
push(StrictBasicValue(value.type))
|
val copyUsages: Set<AbstractInsnNode> = interpreter.uninitializedValuesToCopyUsages[newInsn]!!
|
||||||
|
assert(copyUsages.isNotEmpty()) { "At least DUP copy operation expected" }
|
||||||
|
|
||||||
|
// Value generated by NEW wasn't store to local/field (only DUPed)
|
||||||
|
if (copyUsages.size == 1) continue
|
||||||
|
|
||||||
|
methodNode.instructions.run {
|
||||||
|
removeAll(copyUsages)
|
||||||
|
if (forCoroutines) {
|
||||||
|
remove(newInsn)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Replace 'NEW C' instruction with "manual" initialization of class 'C':
|
||||||
|
// LDC [typeName for C]
|
||||||
|
// INVOKESTATIC java/lang/Class.forName (Ljava/lang/String;)Ljava/lang/Class;
|
||||||
|
// POP
|
||||||
|
val typeNameForClass = newInsn.desc.replace('/', '.')
|
||||||
|
insertBefore(newInsn, LdcInsnNode(typeNameForClass))
|
||||||
|
insertBefore(
|
||||||
|
newInsn,
|
||||||
|
MethodInsnNode(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
"java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
set(newInsn, InsnNode(Opcodes.POP))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val indexOfConstructorArgumentFromTopOfStack = Type.getArgumentTypes((insn as MethodInsnNode).desc).size
|
||||||
|
val storedTypes = arrayListOf<Type>()
|
||||||
|
var nextVarIndex = methodNode.maxLocals
|
||||||
|
|
||||||
|
for (i in 0 until indexOfConstructorArgumentFromTopOfStack) {
|
||||||
|
val value = frame.getStack(frame.stackSize - 1 - i)
|
||||||
|
val type = value.type
|
||||||
|
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ISTORE), nextVarIndex))
|
||||||
|
nextVarIndex += type.size
|
||||||
|
storedTypes.add(type)
|
||||||
|
}
|
||||||
|
methodNode.maxLocals = Math.max(methodNode.maxLocals, nextVarIndex)
|
||||||
|
|
||||||
|
methodNode.instructions.insertBefore(insn, insnListOf(
|
||||||
|
TypeInsnNode(Opcodes.NEW, newInsn.desc),
|
||||||
|
InsnNode(Opcodes.DUP)
|
||||||
|
))
|
||||||
|
|
||||||
|
for (type in storedTypes.reversed()) {
|
||||||
|
nextVarIndex -= type.size
|
||||||
|
methodNode.instructions.insertBefore(insn, VarInsnNode(type.getOpcode(Opcodes.ILOAD), nextVarIndex))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private inner class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame<BasicValue>(nLocals, nStack) {
|
||||||
* @return value generated by NEW that used as 0-th argument of constructor call or null if current instruction is not constructor call
|
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<BasicValue>?) {
|
||||||
*/
|
val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null
|
||||||
private fun Frame<BasicValue>.getUninitializedValueForConstructorCall(
|
|
||||||
insn: AbstractInsnNode
|
|
||||||
): UninitializedNewValue? {
|
|
||||||
if (!insn.isConstructorCall()) return null
|
|
||||||
|
|
||||||
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
|
super.execute(insn, interpreter)
|
||||||
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
|
|
||||||
val newValue = getStack(stackSize - (paramsCountIncludingReceiver + 1)) as? UninitializedNewValue ?: error("Expected value generated with NEW")
|
|
||||||
|
|
||||||
assert(getStack(stackSize - paramsCountIncludingReceiver) is UninitializedNewValue) {
|
if (replaceTopValueWithInitialized) {
|
||||||
"Next value after NEW should be one generated by DUP"
|
// Drop top value
|
||||||
|
val value = pop() as UninitializedNewValue
|
||||||
|
|
||||||
|
// uninitialized value become initialized after <init> call
|
||||||
|
push(StrictBasicValue(value.type))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newValue
|
/**
|
||||||
}
|
* @return value generated by NEW that used as 0-th argument of constructor call or null if current instruction is not constructor call
|
||||||
|
*/
|
||||||
|
private fun Frame<BasicValue>.getUninitializedValueForConstructorCall(insn: AbstractInsnNode): UninitializedNewValue? {
|
||||||
|
if (!insn.isConstructorCall()) return null
|
||||||
|
|
||||||
private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "<init>"
|
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
|
||||||
|
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
|
||||||
|
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue ?:
|
||||||
|
if (isInSpecialMethod)
|
||||||
|
return null
|
||||||
|
else
|
||||||
|
error("Expected value generated with NEW")
|
||||||
|
|
||||||
private class UninitializedNewValueMarkerInterpreter : OptimizationBasicInterpreter() {
|
assert(peek(paramsCountIncludingReceiver - 1) is UninitializedNewValue) {
|
||||||
val uninitializedValuesToCopyUsages = hashMapOf<AbstractInsnNode, MutableSet<AbstractInsnNode>>()
|
"Next value after NEW should be one generated by DUP"
|
||||||
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
|
||||||
if (insn.opcode == Opcodes.NEW) {
|
|
||||||
uninitializedValuesToCopyUsages.getOrPut(insn) { mutableSetOf() }
|
|
||||||
return UninitializedNewValue(insn as TypeInsnNode, insn.desc)
|
|
||||||
}
|
}
|
||||||
return super.newOperation(insn)
|
|
||||||
|
return newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
private class UninitializedNewValue(
|
||||||
if (value is UninitializedNewValue) {
|
val newInsn: TypeInsnNode,
|
||||||
uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn)
|
val internalName: String
|
||||||
return value
|
) : StrictBasicValue(Type.getObjectType(internalName)) {
|
||||||
}
|
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
|
||||||
return super.copyOperation(insn, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue {
|
|
||||||
if (v === w) return v
|
private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "<init>"
|
||||||
if (v === StrictBasicValue.UNINITIALIZED_VALUE || w === StrictBasicValue.UNINITIALIZED_VALUE) {
|
|
||||||
return StrictBasicValue.UNINITIALIZED_VALUE
|
private class UninitializedNewValueMarkerInterpreter : OptimizationBasicInterpreter() {
|
||||||
|
val uninitializedValuesToCopyUsages = hashMapOf<AbstractInsnNode, MutableSet<AbstractInsnNode>>()
|
||||||
|
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
||||||
|
if (insn.opcode == Opcodes.NEW) {
|
||||||
|
uninitializedValuesToCopyUsages.getOrPut(insn) { mutableSetOf() }
|
||||||
|
return UninitializedNewValue(insn as TypeInsnNode, insn.desc)
|
||||||
|
}
|
||||||
|
return super.newOperation(insn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v is UninitializedNewValue || w is UninitializedNewValue) {
|
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||||
if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) {
|
if (value is UninitializedNewValue) {
|
||||||
// Merge of two different ANEW result is possible, but such values should not be used further
|
uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return super.copyOperation(insn, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun merge(v: BasicValue, w: BasicValue): BasicValue {
|
||||||
|
if (v === w) return v
|
||||||
|
if (v === StrictBasicValue.UNINITIALIZED_VALUE || w === StrictBasicValue.UNINITIALIZED_VALUE) {
|
||||||
return StrictBasicValue.UNINITIALIZED_VALUE
|
return StrictBasicValue.UNINITIALIZED_VALUE
|
||||||
}
|
}
|
||||||
|
|
||||||
return v
|
if (v is UninitializedNewValue || w is UninitializedNewValue) {
|
||||||
}
|
if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) {
|
||||||
|
// Merge of two different ANEW result is possible, but such values should not be used further
|
||||||
|
return StrictBasicValue.UNINITIALIZED_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
return super.merge(v, w)
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.merge(v, w)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -17,9 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization;
|
package org.jetbrains.kotlin.codegen.optimization;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilder;
|
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory;
|
import org.jetbrains.kotlin.codegen.ClassBuilderFactory;
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode;
|
|
||||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory;
|
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -17,9 +17,9 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization
|
package org.jetbrains.kotlin.codegen.optimization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||||
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
|
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
|
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
|
||||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
||||||
@@ -35,11 +35,10 @@ class OptimizationMethodVisitor(
|
|||||||
signature: String?,
|
signature: String?,
|
||||||
exceptions: Array<String>?
|
exceptions: Array<String>?
|
||||||
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
||||||
|
|
||||||
override fun performTransformations(methodNode: MethodNode) {
|
override fun performTransformations(methodNode: MethodNode) {
|
||||||
MANDATORY_METHOD_TRANSFORMER.transform("fake", methodNode)
|
normalizationMethodTransformer.transform("fake", methodNode)
|
||||||
if (canBeOptimized(methodNode) && !disableOptimization) {
|
if (canBeOptimized(methodNode) && !disableOptimization) {
|
||||||
OPTIMIZATION_TRANSFORMER.transform("fake", methodNode)
|
optimizationTransformer.transform("fake", methodNode)
|
||||||
}
|
}
|
||||||
methodNode.prepareForEmitting()
|
methodNode.prepareForEmitting()
|
||||||
}
|
}
|
||||||
@@ -47,12 +46,13 @@ class OptimizationMethodVisitor(
|
|||||||
companion object {
|
companion object {
|
||||||
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
||||||
|
|
||||||
private val MANDATORY_METHOD_TRANSFORMER = CompositeMethodTransformer(
|
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||||
FixStackWithLabelNormalizationMethodTransformer(),
|
FixStackWithLabelNormalizationMethodTransformer(),
|
||||||
|
UninitializedStoresMethodTransformer(),
|
||||||
MethodVerifier("AFTER mandatory stack transformations")
|
MethodVerifier("AFTER mandatory stack transformations")
|
||||||
)
|
)
|
||||||
|
|
||||||
private val OPTIMIZATION_TRANSFORMER = CompositeMethodTransformer(
|
val optimizationTransformer = CompositeMethodTransformer(
|
||||||
CapturedVarsOptimizationMethodTransformer(),
|
CapturedVarsOptimizationMethodTransformer(),
|
||||||
RedundantNullCheckMethodTransformer(),
|
RedundantNullCheckMethodTransformer(),
|
||||||
RedundantCheckCastEliminationMethodTransformer(),
|
RedundantCheckCastEliminationMethodTransformer(),
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.codegen.optimization
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
|
class UninitializedStoresMethodTransformer : MethodTransformer() {
|
||||||
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
|
UninitializedStoresProcessor(methodNode, forCoroutines = false).run()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -214,3 +214,6 @@ internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcod
|
|||||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
||||||
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
||||||
|
|
||||||
|
fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) {
|
||||||
|
for (node in nodes) remove(node)
|
||||||
|
}
|
||||||
+8
-1
@@ -18,8 +18,15 @@ package org.jetbrains.kotlin.codegen.optimization.transformer
|
|||||||
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
open class CompositeMethodTransformer(private vararg val transformers: MethodTransformer) : MethodTransformer() {
|
open class CompositeMethodTransformer(private val transformers: List<MethodTransformer>) : MethodTransformer() {
|
||||||
|
constructor(vararg transformers: MethodTransformer?) : this(transformers.filterNotNull())
|
||||||
|
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
transformers.forEach { it.transform(internalClassName, methodNode) }
|
transformers.forEach { it.transform(internalClassName, methodNode) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) =
|
||||||
|
CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-3
@@ -18,10 +18,8 @@ package org.jetbrains.kotlin.codegen.range.forLoop
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtForExpression
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.constants.*
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
|
||||||
class ForInUntilConstantRangeLoopGenerator(
|
class ForInUntilConstantRangeLoopGenerator(
|
||||||
@@ -36,5 +34,5 @@ class ForInUntilConstantRangeLoopGenerator(
|
|||||||
codegen.generateReceiverValue(from, false)
|
codegen.generateReceiverValue(from, false)
|
||||||
|
|
||||||
override fun generateTo(): StackValue =
|
override fun generateTo(): StackValue =
|
||||||
StackValue.constant(untilValue, asmElementType)
|
StackValue.integerConstant(untilValue, asmElementType)
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,6 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
|
|
||||||
val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA)
|
val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA)
|
||||||
|
|
||||||
|
|
||||||
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
@@ -831,11 +831,11 @@ public class KotlinTypeMapper {
|
|||||||
JvmCodegenUtil.isJvm8InterfaceWithDefaults(ownerForDefault, isJvm8Target, isJvm8TargetWithDefaults);
|
JvmCodegenUtil.isJvm8InterfaceWithDefaults(ownerForDefault, isJvm8Target, isJvm8TargetWithDefaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAccessor(@NotNull CallableMemberDescriptor descriptor) {
|
public static boolean isAccessor(@Nullable CallableMemberDescriptor descriptor) {
|
||||||
return descriptor instanceof AccessorForCallableDescriptor<?>;
|
return descriptor instanceof AccessorForCallableDescriptor<?>;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isStaticAccessor(@NotNull CallableMemberDescriptor descriptor) {
|
public static boolean isStaticAccessor(@Nullable CallableMemberDescriptor descriptor) {
|
||||||
if (descriptor instanceof AccessorForConstructorDescriptor) return false;
|
if (descriptor instanceof AccessorForConstructorDescriptor) return false;
|
||||||
return isAccessor(descriptor);
|
return isAccessor(descriptor);
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-13
@@ -32,7 +32,8 @@ val testDistProjects = listOf(
|
|||||||
":kotlin-daemon-client",
|
":kotlin-daemon-client",
|
||||||
":kotlin-preloader",
|
":kotlin-preloader",
|
||||||
":plugins:android-extensions-compiler",
|
":plugins:android-extensions-compiler",
|
||||||
":kotlin-ant")
|
":kotlin-ant",
|
||||||
|
":kotlin-annotations-jvm")
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
depDistProjects.forEach {
|
depDistProjects.forEach {
|
||||||
@@ -82,36 +83,85 @@ projectTest {
|
|||||||
|
|
||||||
evaluationDependsOn(":compiler:tests-common-jvm6")
|
evaluationDependsOn(":compiler:tests-common-jvm6")
|
||||||
|
|
||||||
fun Project.codegenTest(taskName: String, body: Test.() -> Unit): Test = projectTest(taskName) {
|
fun Project.codegenTest(taskName: String, jdk: String, body: Test.() -> Unit): Test = projectTest(taskName) {
|
||||||
dependsOn(*testDistProjects.map { "$it:dist" }.toTypedArray())
|
dependsOn(*testDistProjects.map { "$it:dist" }.toTypedArray())
|
||||||
workingDir = rootDir
|
workingDir = rootDir
|
||||||
environment("TEST_SERVER_CLASSES_DIRS", project(":compiler:tests-common-jvm6").the<JavaPluginConvention>().sourceSets.getByName("main").output.classesDirs.asPath)
|
|
||||||
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.CodegenJdkCommonTestSuite*")
|
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.BlackBoxCodegenTestGenerated*")
|
||||||
|
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.BlackBoxInlineCodegenTestGenerated*")
|
||||||
|
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.CompileKotlinAgainstInlineKotlinTestGenerated*")
|
||||||
|
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.CompileKotlinAgainstKotlinTestGenerated*")
|
||||||
|
filter.includeTestsMatching("org.jetbrains.kotlin.codegen.BlackBoxAgainstJavaCodegenTestGenerated*")
|
||||||
|
|
||||||
|
if (jdk == "JDK_9") {
|
||||||
|
jvmArgs = listOf("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
|
||||||
|
}
|
||||||
body()
|
body()
|
||||||
|
doFirst {
|
||||||
|
val jdkPath = project.property(jdk) ?: error("$jdk is not optional to run this test")
|
||||||
|
executable = "$jdkPath/bin/java"
|
||||||
|
println("Running test with $executable")
|
||||||
|
}
|
||||||
|
}.also {
|
||||||
|
task(taskName.replace(Regex("-[a-z]"), { it.value.takeLast(1).toUpperCase() })) {
|
||||||
|
dependsOn(it)
|
||||||
|
group = "verification"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
codegenTest("codegen-target6-jvm6-test") {
|
codegenTest("codegen-target6-jvm6-test", "JDK_18") {
|
||||||
|
dependsOn(":compiler:tests-common-jvm6:build")
|
||||||
|
|
||||||
|
//TODO make port flexible
|
||||||
|
val port = "5100"
|
||||||
|
var jdkProcess: Process? = null
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
logger.info("Configuring JDK 6 server...")
|
||||||
|
val jdkPath = project.property("JDK_16") ?: error("JDK_16 is not optional to run this test")
|
||||||
|
val executable = "$jdkPath/bin/java"
|
||||||
|
val main = "org.jetbrains.kotlin.test.clientserver.TestProcessServer"
|
||||||
|
|
||||||
|
val classpath = getSourceSetsFrom(":compiler:tests-common-jvm6")["main"].output.asPath + ":" +
|
||||||
|
getSourceSetsFrom(":kotlin-stdlib")["main"].output.asPath + ":" +
|
||||||
|
getSourceSetsFrom(":kotlin-stdlib")["builtins"].output.asPath + ":" +
|
||||||
|
getSourceSetsFrom(":kotlin-test:kotlin-test-jvm")["main"].output.asPath
|
||||||
|
|
||||||
|
logger.debug("Server classpath: $classpath")
|
||||||
|
|
||||||
|
val builder = ProcessBuilder(executable, "-cp", classpath, main, port)
|
||||||
|
builder.directory(rootDir)
|
||||||
|
|
||||||
|
builder.inheritIO()
|
||||||
|
builder.redirectErrorStream(true)
|
||||||
|
|
||||||
|
logger.info("Starting JDK 6 server $executable")
|
||||||
|
jdkProcess = builder.start()
|
||||||
|
|
||||||
|
}
|
||||||
systemProperty("kotlin.test.default.jvm.target", "1.6")
|
systemProperty("kotlin.test.default.jvm.target", "1.6")
|
||||||
systemProperty("kotlin.test.java.compilation.target", "1.6")
|
systemProperty("kotlin.test.java.compilation.target", "1.6")
|
||||||
systemProperty("kotlin.test.box.in.separate.process.port", "5100")
|
systemProperty("kotlin.test.box.in.separate.process.port", port)
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
logger.info("Stopping JDK 6 server...")
|
||||||
|
jdkProcess?.destroy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
codegenTest("codegen-target6-jvm9-test") {
|
codegenTest("codegen-target6-jvm9-test", "JDK_9") {
|
||||||
systemProperty("kotlin.test.default.jvm.target", "1.6")
|
systemProperty("kotlin.test.default.jvm.target", "1.6")
|
||||||
jvmArgs = listOf("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codegenTest("codegen-target8-jvm8-test") {
|
codegenTest("codegen-target8-jvm8-test", "JDK_18") {
|
||||||
systemProperty("kotlin.test.default.jvm.target", "1.8")
|
systemProperty("kotlin.test.default.jvm.target", "1.8")
|
||||||
}
|
}
|
||||||
|
|
||||||
codegenTest("codegen-target8-jvm9-test") {
|
codegenTest("codegen-target8-jvm9-test", "JDK_9") {
|
||||||
systemProperty("kotlin.test.default.jvm.target", "1.8")
|
systemProperty("kotlin.test.default.jvm.target", "1.8")
|
||||||
jvmArgs = listOf("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codegenTest("codegen-target9-jvm9-test") {
|
codegenTest("codegen-target9-jvm9-test", "JDK_9") {
|
||||||
systemProperty("kotlin.test.default.jvm.target", "1.8")
|
systemProperty("kotlin.test.default.jvm.target", "1.8")
|
||||||
systemProperty("kotlin.test.substitute.bytecode.1.8.to.1.9", "true")
|
systemProperty("kotlin.test.substitute.bytecode.1.8.to.1.9", "true")
|
||||||
jvmArgs = listOf("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -48,11 +48,12 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
|||||||
var sourceMapPrefix: String? by FreezableVar(null)
|
var sourceMapPrefix: String? by FreezableVar(null)
|
||||||
|
|
||||||
@Argument(
|
@Argument(
|
||||||
value = "-source-map-source-roots",
|
value = "-source-map-base-dirs",
|
||||||
|
deprecatedName = "-source-map-source-roots",
|
||||||
valueDescription = "<path>",
|
valueDescription = "<path>",
|
||||||
description = "Base directories which are used to calculate relative paths to source files in source map"
|
description = "Base directories which are used to calculate relative paths to source files in source map"
|
||||||
)
|
)
|
||||||
var sourceMapSourceRoots: String? by FreezableVar(null)
|
var sourceMapBaseDirs: String? by FreezableVar(null)
|
||||||
|
|
||||||
@GradleOption(DefaultValues.JsSourceMapContentModes::class)
|
@GradleOption(DefaultValues.JsSourceMapContentModes::class)
|
||||||
@Argument(
|
@Argument(
|
||||||
|
|||||||
+80
-13
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
|||||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||||
import org.jetbrains.kotlin.config.JvmTarget
|
import org.jetbrains.kotlin.config.JvmTarget
|
||||||
import org.jetbrains.kotlin.utils.Jsr305State
|
import org.jetbrains.kotlin.utils.Jsr305State
|
||||||
|
import org.jetbrains.kotlin.utils.ReportLevel
|
||||||
|
|
||||||
class K2JVMCompilerArguments : CommonCompilerArguments() {
|
class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -165,10 +166,15 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
@Argument(
|
@Argument(
|
||||||
value = "-Xjsr305",
|
value = "-Xjsr305",
|
||||||
deprecatedName = "-Xjsr305-annotations",
|
deprecatedName = "-Xjsr305-annotations",
|
||||||
valueDescription = "{ignore|strict|warn}",
|
valueDescription = "{ignore|strict|warn}" +
|
||||||
description = "Specify global behavior for JSR-305 nullability annotations: ignore, treat as other supported nullability annotations, or report a warning"
|
"|under-migration:{ignore-strict-warn}" +
|
||||||
|
"|@<fully qualified class name>:{ignore|strict|warn}",
|
||||||
|
description = "Specify behaviors for JSR-305 nullability annotations for: " +
|
||||||
|
"global, annotated with @UnderMigration or custom annotation " +
|
||||||
|
"with specific value: ignore, treat as other supported nullability annotations, or report a warning. " +
|
||||||
|
"Note that strict value is experimental yet"
|
||||||
)
|
)
|
||||||
var jsr305: String? by FreezableVar(Jsr305State.DEFAULT.description)
|
var jsr305: Array<String>? by FreezableVar(null)
|
||||||
|
|
||||||
@Argument(
|
@Argument(
|
||||||
value = "-Xno-exception-on-explicit-equals-for-boxed-null",
|
value = "-Xno-exception-on-explicit-equals-for-boxed-null",
|
||||||
@@ -181,20 +187,81 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
|
|
||||||
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
|
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
|
||||||
val result = super.configureAnalysisFlags(collector)
|
val result = super.configureAnalysisFlags(collector)
|
||||||
|
result[AnalysisFlag.jsr305] = parseJsr305(collector)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
if (jsr305 == "enable") {
|
fun parseJsr305(collector: MessageCollector): Jsr305State {
|
||||||
collector.report(
|
var global: ReportLevel? = null
|
||||||
CompilerMessageSeverity.STRONG_WARNING,
|
var migration: ReportLevel? = null
|
||||||
"Option 'enable' for -Xjsr305 flag is deprecated. Please use 'strict' instead"
|
val userDefined = mutableMapOf<String, ReportLevel>()
|
||||||
)
|
|
||||||
result.put(AnalysisFlag.jsr305, Jsr305State.STRICT)
|
fun parseJsr305UnderMigration(collector: MessageCollector, item: String): ReportLevel? {
|
||||||
|
val rawState = item.split(":").takeIf { it.size == 2 }?.get(1)
|
||||||
|
return ReportLevel.findByDescription(rawState) ?: reportUnrecognizedJsr305(collector, item).let { null }
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Jsr305State.findByDescription(jsr305)?.let {
|
jsr305?.forEach { item ->
|
||||||
result.put(AnalysisFlag.jsr305, it)
|
when {
|
||||||
|
item.startsWith("@") -> {
|
||||||
|
val (name, state) = parseJsr305UserDefined(collector, item) ?: return@forEach
|
||||||
|
val current = userDefined[name]
|
||||||
|
if (current != null) {
|
||||||
|
reportDuplicateJsr305(collector, "@$name:${current.description}", item)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
userDefined[name] = state
|
||||||
|
}
|
||||||
|
item.startsWith("under-migration") -> {
|
||||||
|
if (migration != null) {
|
||||||
|
reportDuplicateJsr305(collector, "under-migration:${migration?.description}", item)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
migration = parseJsr305UnderMigration(collector, item)
|
||||||
|
}
|
||||||
|
item == "enable" -> {
|
||||||
|
collector.report(
|
||||||
|
CompilerMessageSeverity.STRONG_WARNING,
|
||||||
|
"Option 'enable' for -Xjsr305 flag is deprecated. Please use 'strict' instead"
|
||||||
|
)
|
||||||
|
if (global != null) return@forEach
|
||||||
|
|
||||||
|
global = ReportLevel.STRICT
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (global != null) {
|
||||||
|
reportDuplicateJsr305(collector, global!!.description, item)
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
global = ReportLevel.findByDescription(item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
val state = Jsr305State(global ?: ReportLevel.WARN, migration, userDefined)
|
||||||
|
return if (state == Jsr305State.DISABLED) Jsr305State.DISABLED else state
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportUnrecognizedJsr305(collector: MessageCollector, item: String) {
|
||||||
|
collector.report(CompilerMessageSeverity.ERROR, "Unrecognized -Xjsr305 value: $item")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reportDuplicateJsr305(collector: MessageCollector, first: String, second: String) {
|
||||||
|
collector.report(CompilerMessageSeverity.ERROR, "Conflict duplicating -Xjsr305 value: $first, $second")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseJsr305UserDefined(collector: MessageCollector, item: String): Pair<String, ReportLevel>? {
|
||||||
|
val (name, rawState) = item.substring(1).split(":").takeIf { it.size == 2 } ?: run {
|
||||||
|
reportUnrecognizedJsr305(collector, item)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = ReportLevel.findByDescription(rawState) ?: run {
|
||||||
|
reportUnrecognizedJsr305(collector, item)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return name to state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,8 +175,9 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
|||||||
configuration.put(CLIConfigurationKeys.IS_API_VERSION_EXPLICIT, true);
|
configuration.put(CLIConfigurationKeys.IS_API_VERSION_EXPLICIT, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageCollector collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY);
|
||||||
if (apiVersion.compareTo(languageVersion) > 0) {
|
if (apiVersion.compareTo(languageVersion) > 0) {
|
||||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
collector.report(
|
||||||
ERROR,
|
ERROR,
|
||||||
"-api-version (" + apiVersion.getVersionString() + ") cannot be greater than " +
|
"-api-version (" + apiVersion.getVersionString() + ") cannot be greater than " +
|
||||||
"-language-version (" + languageVersion.getVersionString() + ")",
|
"-language-version (" + languageVersion.getVersionString() + ")",
|
||||||
@@ -185,7 +186,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!languageVersion.isStable()) {
|
if (!languageVersion.isStable()) {
|
||||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
collector.report(
|
||||||
STRONG_WARNING,
|
STRONG_WARNING,
|
||||||
"Language version " + languageVersion.getVersionString() + " is experimental, there are " +
|
"Language version " + languageVersion.getVersionString() + " is experimental, there are " +
|
||||||
"no backwards compatibility guarantees for new language and library features",
|
"no backwards compatibility guarantees for new language and library features",
|
||||||
@@ -206,7 +207,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
|||||||
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, new LanguageVersionSettingsImpl(
|
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, new LanguageVersionSettingsImpl(
|
||||||
languageVersion,
|
languageVersion,
|
||||||
ApiVersion.createByLanguageVersion(apiVersion),
|
ApiVersion.createByLanguageVersion(apiVersion),
|
||||||
arguments.configureAnalysisFlags(configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)),
|
arguments.configureAnalysisFlags(collector),
|
||||||
extraLanguageFeatures
|
extraLanguageFeatures
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,8 +357,8 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
|||||||
configuration.put(JSConfigurationKeys.SOURCE_MAP_PREFIX, arguments.getSourceMapPrefix());
|
configuration.put(JSConfigurationKeys.SOURCE_MAP_PREFIX, arguments.getSourceMapPrefix());
|
||||||
}
|
}
|
||||||
|
|
||||||
String sourceMapSourceRoots = arguments.getSourceMapSourceRoots() != null ?
|
String sourceMapSourceRoots = arguments.getSourceMapBaseDirs() != null ?
|
||||||
arguments.getSourceMapSourceRoots() :
|
arguments.getSourceMapBaseDirs() :
|
||||||
calculateSourceMapSourceRoot(messageCollector, arguments);
|
calculateSourceMapSourceRoot(messageCollector, arguments);
|
||||||
List<String> sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator);
|
List<String> sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator);
|
||||||
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList);
|
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList);
|
||||||
@@ -367,7 +367,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
|||||||
if (arguments.getSourceMapPrefix() != null) {
|
if (arguments.getSourceMapPrefix() != null) {
|
||||||
messageCollector.report(WARNING, "source-map-prefix argument has no effect without source map", null);
|
messageCollector.report(WARNING, "source-map-prefix argument has no effect without source map", null);
|
||||||
}
|
}
|
||||||
if (arguments.getSourceMapSourceRoots() != null) {
|
if (arguments.getSourceMapBaseDirs() != null) {
|
||||||
messageCollector.report(WARNING, "source-map-source-root argument has no effect without source map", null);
|
messageCollector.report(WARNING, "source-map-source-root argument has no effect without source map", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.isValidJavaFqName
|
import org.jetbrains.kotlin.name.isValidJavaFqName
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||||
import java.io.File
|
import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import java.util.jar.Manifest
|
import java.util.jar.Manifest
|
||||||
@@ -51,55 +51,82 @@ class ClasspathRootsResolver(
|
|||||||
private val additionalModules: List<String>,
|
private val additionalModules: List<String>,
|
||||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
|
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
|
||||||
private val javaModuleFinder: CliJavaModuleFinder,
|
private val javaModuleFinder: CliJavaModuleFinder,
|
||||||
private val requireStdlibModule: Boolean
|
private val requireStdlibModule: Boolean,
|
||||||
|
private val outputDirectory: VirtualFile?
|
||||||
) {
|
) {
|
||||||
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||||
|
|
||||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
||||||
|
|
||||||
fun convertClasspathRoots(contentRoots: List<ContentRoot>): RootsAndModules {
|
private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?)
|
||||||
val result = mutableListOf<JavaRoot>()
|
|
||||||
|
|
||||||
val modules = ArrayList<JavaModule>()
|
fun convertClasspathRoots(contentRoots: List<ContentRoot>): RootsAndModules {
|
||||||
|
val javaSourceRoots = mutableListOf<RootWithPrefix>()
|
||||||
|
val jvmClasspathRoots = mutableListOf<VirtualFile>()
|
||||||
|
val jvmModulePathRoots = mutableListOf<VirtualFile>()
|
||||||
|
|
||||||
for (contentRoot in contentRoots) {
|
for (contentRoot in contentRoots) {
|
||||||
if (contentRoot !is JvmContentRoot) continue
|
if (contentRoot !is JvmContentRoot) continue
|
||||||
val root = contentRootToVirtualFile(contentRoot) ?: continue
|
val root = contentRootToVirtualFile(contentRoot) ?: continue
|
||||||
|
|
||||||
when (contentRoot) {
|
when (contentRoot) {
|
||||||
is JavaSourceRoot -> {
|
is JavaSourceRoot -> javaSourceRoots += RootWithPrefix(root, contentRoot.packagePrefix)
|
||||||
val modularRoot = modularSourceRoot(root)
|
is JvmClasspathRoot -> jvmClasspathRoots += root
|
||||||
if (modularRoot != null) {
|
is JvmModulePathRoot -> jvmModulePathRoots += root
|
||||||
modules += modularRoot
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result += JavaRoot(root, JavaRoot.RootType.SOURCE, contentRoot.packagePrefix?.let { prefix ->
|
|
||||||
if (isValidJavaFqName(prefix)) FqName(prefix)
|
|
||||||
else null.also {
|
|
||||||
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is JvmClasspathRoot -> {
|
|
||||||
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
|
||||||
}
|
|
||||||
is JvmModulePathRoot -> {
|
|
||||||
val module = modularBinaryRoot(root, contentRoot.file)
|
|
||||||
if (module != null) {
|
|
||||||
modules += module
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("Unknown root type: $contentRoot")
|
else -> error("Unknown root type: $contentRoot")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return computeRoots(javaSourceRoots, jvmClasspathRoots, jvmModulePathRoots)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeRoots(
|
||||||
|
javaSourceRoots: List<RootWithPrefix>,
|
||||||
|
jvmClasspathRoots: List<VirtualFile>,
|
||||||
|
jvmModulePathRoots: List<VirtualFile>
|
||||||
|
): RootsAndModules {
|
||||||
|
val result = mutableListOf<JavaRoot>()
|
||||||
|
val modules = mutableListOf<JavaModule>()
|
||||||
|
|
||||||
|
val hasOutputDirectoryInClasspath = outputDirectory in jvmClasspathRoots || outputDirectory in jvmModulePathRoots
|
||||||
|
|
||||||
|
for ((root, packagePrefix) in javaSourceRoots) {
|
||||||
|
val modularRoot = modularSourceRoot(root, hasOutputDirectoryInClasspath)
|
||||||
|
if (modularRoot != null) {
|
||||||
|
modules += modularRoot
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result += JavaRoot(root, JavaRoot.RootType.SOURCE, packagePrefix?.let { prefix ->
|
||||||
|
if (isValidJavaFqName(prefix)) FqName(prefix)
|
||||||
|
else null.also {
|
||||||
|
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (root in jvmClasspathRoots) {
|
||||||
|
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
||||||
|
}
|
||||||
|
|
||||||
|
val outputDirectoryAddedAsPartOfModule = modules.any { module -> module.moduleRoots.any { it.file == outputDirectory } }
|
||||||
|
|
||||||
|
for (root in jvmModulePathRoots) {
|
||||||
|
// Do not add output directory as a separate module if we're compiling an explicit named module.
|
||||||
|
// It's going to be included as a root of our module in modularSourceRoot.
|
||||||
|
if (outputDirectoryAddedAsPartOfModule && root == outputDirectory) continue
|
||||||
|
|
||||||
|
val module = modularBinaryRoot(root)
|
||||||
|
if (module != null) {
|
||||||
|
modules += module
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addModularRoots(modules, result)
|
addModularRoots(modules, result)
|
||||||
|
|
||||||
return RootsAndModules(result, modules)
|
return RootsAndModules(result, modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun modularSourceRoot(root: VirtualFile): JavaModule.Explicit? {
|
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, PsiJavaModule>? {
|
||||||
val moduleInfoFile =
|
val moduleInfoFile =
|
||||||
when {
|
when {
|
||||||
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
|
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
|
||||||
@@ -109,10 +136,21 @@ class ClasspathRootsResolver(
|
|||||||
|
|
||||||
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
|
val psiFile = psiManager.findFile(moduleInfoFile) ?: return null
|
||||||
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
|
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
|
||||||
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), root, moduleInfoFile, isBinary = false)
|
|
||||||
|
return moduleInfoFile to psiJavaModule
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun modularBinaryRoot(root: VirtualFile, originalFile: File): JavaModule? {
|
private fun modularSourceRoot(root: VirtualFile, hasOutputDirectoryInClasspath: Boolean): JavaModule.Explicit? {
|
||||||
|
val (moduleInfoFile, psiJavaModule) = findSourceModuleInfo(root) ?: return null
|
||||||
|
val sourceRoot = JavaModule.Root(root, isBinary = false)
|
||||||
|
val roots =
|
||||||
|
if (hasOutputDirectoryInClasspath)
|
||||||
|
listOf(sourceRoot, JavaModule.Root(outputDirectory!!, isBinary = true))
|
||||||
|
else listOf(sourceRoot)
|
||||||
|
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun modularBinaryRoot(root: VirtualFile): JavaModule? {
|
||||||
val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL
|
val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL
|
||||||
val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
|
val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
|
||||||
|
|
||||||
@@ -124,22 +162,25 @@ class ClasspathRootsResolver(
|
|||||||
|
|
||||||
if (moduleInfoFile != null) {
|
if (moduleInfoFile != null) {
|
||||||
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
|
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
|
||||||
return JavaModule.Explicit(moduleInfo, root, moduleInfoFile, isBinary = true)
|
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only .jar files can be automatic modules
|
// Only .jar files can be automatic modules
|
||||||
if (isJar) {
|
if (isJar) {
|
||||||
|
val moduleRoot = listOf(JavaModule.Root(root, isBinary = true))
|
||||||
|
|
||||||
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
|
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
|
||||||
if (automaticModuleName != null) {
|
if (automaticModuleName != null) {
|
||||||
return JavaModule.Automatic(automaticModuleName, root)
|
return JavaModule.Automatic(automaticModuleName, moduleRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val originalFile = VfsUtilCore.virtualToIoFile(root)
|
||||||
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension)
|
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension)
|
||||||
if (moduleName.isEmpty()) {
|
if (moduleName.isEmpty()) {
|
||||||
report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
|
report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return JavaModule.Automatic(moduleName, root)
|
return JavaModule.Automatic(moduleName, moduleRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
@@ -156,7 +197,9 @@ class ClasspathRootsResolver(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
|
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
|
||||||
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filterNot(JavaModule::isBinary)
|
// In current implementation, at most one source module is supported. This can be relaxed in the future if we support another
|
||||||
|
// compilation mode, similar to java's --module-source-path
|
||||||
|
val sourceModules = modules.filterIsInstance<JavaModule.Explicit>().filter(JavaModule::isSourceModule)
|
||||||
if (sourceModules.size > 1) {
|
if (sourceModules.size > 1) {
|
||||||
for (module in sourceModules) {
|
for (module in sourceModules) {
|
||||||
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
|
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
|
||||||
@@ -169,11 +212,15 @@ class ClasspathRootsResolver(
|
|||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
javaModuleFinder.addUserModule(module)
|
javaModuleFinder.addUserModule(module)
|
||||||
}
|
}
|
||||||
else if (module.moduleRoot != existing.moduleRoot) {
|
else if (module.moduleRoots != existing.moduleRoots) {
|
||||||
val jar = VfsUtilCore.getVirtualFileForJar(module.moduleRoot) ?: module.moduleRoot
|
fun JavaModule.getRootFile() =
|
||||||
val existingPath = (VfsUtilCore.getVirtualFileForJar(existing.moduleRoot) ?: existing.moduleRoot).path
|
moduleRoots.firstOrNull()?.file?.let { VfsUtilCore.getVirtualFileForJar(it) ?: it }
|
||||||
|
|
||||||
|
val thisFile = module.getRootFile()
|
||||||
|
val existingFile = existing.getRootFile()
|
||||||
|
val atExistingPath = if (existingFile == null) "" else " at: ${existingFile.path}"
|
||||||
report(STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " +
|
report(STRONG_WARNING, "The root is ignored because a module with the same name '${module.name}' " +
|
||||||
"has been found earlier on the module path at: $existingPath", jar)
|
"has been found earlier on the module path$atExistingPath", thisFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,18 +259,17 @@ class ClasspathRootsResolver(
|
|||||||
report(ERROR, "Module $moduleName cannot be found in the module graph")
|
report(ERROR, "Module $moduleName cannot be found in the module graph")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.add(JavaRoot(
|
for ((root, isBinary) in module.moduleRoots) {
|
||||||
module.moduleRoot,
|
result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE))
|
||||||
if (module.isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE
|
}
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requireStdlibModule && sourceModule != null && "kotlin.stdlib" !in allDependencies) {
|
if (requireStdlibModule && sourceModule != null && KOTLIN_STDLIB_MODULE_NAME !in allDependencies) {
|
||||||
report(
|
report(
|
||||||
ERROR,
|
ERROR,
|
||||||
"The Kotlin standard library is not found in the module graph. " +
|
"The Kotlin standard library is not found in the module graph. " +
|
||||||
"Please ensure you have the 'requires kotlin.stdlib' clause in your module definition",
|
"Please ensure you have the 'requires $KOTLIN_STDLIB_MODULE_NAME' clause in your module definition",
|
||||||
sourceModule.moduleInfoFile
|
sourceModule.moduleInfoFile
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,13 +204,19 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
||||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val outputDirectory =
|
||||||
|
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||||
|
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath
|
||||||
|
|
||||||
classpathRootsResolver = ClasspathRootsResolver(
|
classpathRootsResolver = ClasspathRootsResolver(
|
||||||
PsiManager.getInstance(project),
|
PsiManager.getInstance(project),
|
||||||
messageCollector,
|
messageCollector,
|
||||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||||
this::contentRootToVirtualFile,
|
this::contentRootToVirtualFile,
|
||||||
javaModuleFinder,
|
javaModuleFinder,
|
||||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE)
|
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||||
|
outputDirectory?.let(this::findLocalFile)
|
||||||
)
|
)
|
||||||
|
|
||||||
val (initialRoots, javaModules) =
|
val (initialRoots, javaModules) =
|
||||||
@@ -331,7 +337,8 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun findLocalFile(path: String) = applicationEnvironment.localFileSystem.findFileByPath(path)
|
internal fun findLocalFile(path: String): VirtualFile? =
|
||||||
|
applicationEnvironment.localFileSystem.findFileByPath(path)
|
||||||
|
|
||||||
private fun findLocalFile(root: JvmContentRoot): VirtualFile? {
|
private fun findLocalFile(root: JvmContentRoot): VirtualFile? {
|
||||||
return findLocalFile(root.file.absolutePath).also {
|
return findLocalFile(root.file.absolutePath).also {
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
|
|||||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
||||||
val moduleInfo = JavaModuleInfo.read(file) ?: return null
|
val moduleInfo = JavaModuleInfo.read(file) ?: return null
|
||||||
return JavaModule.Explicit(moduleInfo, moduleRoot, file, isBinary = true)
|
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,12 +32,12 @@ class CliJavaModuleResolver(
|
|||||||
private val systemModules: List<JavaModule.Explicit>
|
private val systemModules: List<JavaModule.Explicit>
|
||||||
) : JavaModuleResolver {
|
) : JavaModuleResolver {
|
||||||
init {
|
init {
|
||||||
assert(userModules.count { !it.isBinary } <= 1) {
|
assert(userModules.count(JavaModule::isSourceModule) <= 1) {
|
||||||
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $userModules"
|
"Modules computed by ClasspathRootsResolver cannot have more than one source module: $userModules"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sourceModule: JavaModule? = userModules.firstOrNull { !it.isBinary }
|
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
|
||||||
|
|
||||||
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
||||||
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) {
|
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) {
|
||||||
@@ -46,13 +46,13 @@ class CliJavaModuleResolver(
|
|||||||
|
|
||||||
return when (file.fileType) {
|
return when (file.fileType) {
|
||||||
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
|
KotlinFileType.INSTANCE, JavaFileType.INSTANCE -> sourceModule
|
||||||
JavaClassFileType.INSTANCE -> userModules.firstOrNull { module -> module.isBinary && file in module }
|
JavaClassFileType.INSTANCE -> userModules.firstOrNull { module -> file in module }
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private operator fun JavaModule.contains(file: VirtualFile): Boolean =
|
private operator fun JavaModule.contains(file: VirtualFile): Boolean =
|
||||||
VfsUtilCore.isAncestor(moduleRoot, file, false)
|
moduleRoots.any { (root, isBinary) -> isBinary && VfsUtilCore.isAncestor(root, file, false) }
|
||||||
|
|
||||||
override fun checkAccessibility(
|
override fun checkAccessibility(
|
||||||
fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?
|
fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?
|
||||||
|
|||||||
+2
@@ -41,4 +41,6 @@ open class DelegatePackageMemberDeclarationProvider(var delegate: PackageMemberD
|
|||||||
override fun getClassOrObjectDeclarations(name: Name) = delegate.getClassOrObjectDeclarations(name)
|
override fun getClassOrObjectDeclarations(name: Name) = delegate.getClassOrObjectDeclarations(name)
|
||||||
|
|
||||||
override fun getTypeAliasDeclarations(name: Name) = delegate.getTypeAliasDeclarations(name)
|
override fun getTypeAliasDeclarations(name: Name) = delegate.getTypeAliasDeclarations(name)
|
||||||
|
|
||||||
|
override fun getDeclarationNames() = delegate.getDeclarationNames()
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -48,7 +48,9 @@ class IncrementalCompilationOptions(
|
|||||||
/** @See [ReportSeverity] */
|
/** @See [ReportSeverity] */
|
||||||
reportSeverity: Int,
|
reportSeverity: Int,
|
||||||
/** @See [CompilationResultCategory]] */
|
/** @See [CompilationResultCategory]] */
|
||||||
requestedCompilationResults: Array<Int>
|
requestedCompilationResults: Array<Int>,
|
||||||
|
val resultDifferenceFile: File? = null,
|
||||||
|
val friendDifferenceFile: File? = null
|
||||||
) : CompilationOptions(compilerMode, targetPlatform, reportCategories, reportSeverity, requestedCompilationResults) {
|
) : CompilationOptions(compilerMode, targetPlatform, reportCategories, reportSeverity, requestedCompilationResults) {
|
||||||
companion object {
|
companion object {
|
||||||
const val serialVersionUID: Long = 0
|
const val serialVersionUID: Long = 0
|
||||||
|
|||||||
@@ -507,9 +507,12 @@ class CompileServiceImpl(
|
|||||||
workingDir,
|
workingDir,
|
||||||
enabled = true)
|
enabled = true)
|
||||||
|
|
||||||
return IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions, reporter, annotationFileUpdater,
|
val compiler = IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions,
|
||||||
artifactChanges, changesRegistry)
|
reporter, annotationFileUpdater,
|
||||||
.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, { changedFiles })
|
artifactChanges, changesRegistry,
|
||||||
|
buildHistoryFile = incrementalCompilationOptions.resultDifferenceFile,
|
||||||
|
friendBuildHistoryFile = incrementalCompilationOptions.friendDifferenceFile)
|
||||||
|
return compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, { changedFiles })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun leaseReplSession(
|
override fun leaseReplSession(
|
||||||
|
|||||||
+36
-3
@@ -22,9 +22,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
|
||||||
import java.lang.reflect.Array
|
import java.lang.reflect.Array
|
||||||
|
|
||||||
internal class AnnotationsCollectorMethodVisitor(
|
internal class AnnotationsCollectorMethodVisitor(
|
||||||
@@ -55,6 +53,27 @@ internal class AnnotationsCollectorMethodVisitor(
|
|||||||
|
|
||||||
return BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser)
|
return BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String, visible: Boolean): AnnotationVisitor? {
|
||||||
|
// TODO: support annotations on type arguments
|
||||||
|
if (typePath != null) return null
|
||||||
|
|
||||||
|
val typeReference = TypeReference(typeRef)
|
||||||
|
|
||||||
|
return when (typeReference.sort) {
|
||||||
|
TypeReference.METHOD_RETURN -> member.safeAs<BinaryJavaMethod>()?.returnType?.let {
|
||||||
|
BinaryJavaAnnotation.addTypeAnnotation(it, desc, context, signatureParser)
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeReference.METHOD_FORMAL_PARAMETER ->
|
||||||
|
BinaryJavaAnnotation.addTypeAnnotation(
|
||||||
|
member.valueParameters[typeReference.formalParameterIndex].type,
|
||||||
|
desc, context, signatureParser
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BinaryJavaAnnotation private constructor(
|
class BinaryJavaAnnotation private constructor(
|
||||||
@@ -87,6 +106,20 @@ class BinaryJavaAnnotation private constructor(
|
|||||||
|
|
||||||
return annotationVisitor
|
return annotationVisitor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addTypeAnnotation(
|
||||||
|
type: JavaType,
|
||||||
|
desc: String,
|
||||||
|
context: ClassifierResolutionContext,
|
||||||
|
signatureParser: BinaryClassSignatureParser
|
||||||
|
): AnnotationVisitor? {
|
||||||
|
type as? PlainJavaClassifierType ?: return null
|
||||||
|
|
||||||
|
val (javaAnnotation, annotationVisitor) = createAnnotationAndVisitor(desc, context, signatureParser)
|
||||||
|
type.addAnnotation(javaAnnotation)
|
||||||
|
|
||||||
|
return annotationVisitor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val classifierResolutionResult by lazy(LazyThreadSafetyMode.NONE) {
|
private val classifierResolutionResult by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
|||||||
+6
@@ -162,6 +162,12 @@ class BinaryJavaClass(
|
|||||||
object : FieldVisitor(ASM_API_VERSION_FOR_CLASS_READING) {
|
object : FieldVisitor(ASM_API_VERSION_FOR_CLASS_READING) {
|
||||||
override fun visitAnnotation(desc: String, visible: Boolean) =
|
override fun visitAnnotation(desc: String, visible: Boolean) =
|
||||||
BinaryJavaAnnotation.addAnnotation(this@run.annotations, desc, context, signatureParser)
|
BinaryJavaAnnotation.addAnnotation(this@run.annotations, desc, context, signatureParser)
|
||||||
|
|
||||||
|
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String, visible: Boolean) =
|
||||||
|
if (typePath == null)
|
||||||
|
BinaryJavaAnnotation.addTypeAnnotation(type, desc, context, signatureParser)
|
||||||
|
else
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-3
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.load.java.structure.impl.classFiles
|
package org.jetbrains.kotlin.load.java.structure.impl.classFiles
|
||||||
|
|
||||||
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.load.java.structure.*
|
import org.jetbrains.kotlin.load.java.structure.*
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -39,9 +40,18 @@ internal class PlainJavaClassifierType(
|
|||||||
get() = typeArguments.isEmpty() &&
|
get() = typeArguments.isEmpty() &&
|
||||||
classifierResolverResult.classifier?.safeAs<JavaClass>()?.typeParameters?.isNotEmpty() == true
|
classifierResolverResult.classifier?.safeAs<JavaClass>()?.typeParameters?.isNotEmpty() == true
|
||||||
|
|
||||||
// TODO: support type annotations
|
private var _annotations = emptyList<JavaAnnotation>()
|
||||||
override val annotations get() = emptyList<JavaAnnotation>()
|
override val annotations get() = _annotations
|
||||||
override fun findAnnotation(fqName: FqName) = null
|
|
||||||
|
override fun findAnnotation(fqName: FqName) = annotations.find { it.classId?.asSingleFqName() == fqName }
|
||||||
|
|
||||||
|
internal fun addAnnotation(annotation: JavaAnnotation) {
|
||||||
|
if (_annotations.isEmpty()) {
|
||||||
|
_annotations = ContainerUtil.newSmartList()
|
||||||
|
}
|
||||||
|
|
||||||
|
(_annotations as MutableList).add(annotation)
|
||||||
|
}
|
||||||
|
|
||||||
override val isDeprecatedInJavaDoc get() = false
|
override val isDeprecatedInJavaDoc get() = false
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.jvm.modules
|
package org.jetbrains.kotlin.resolve.jvm.modules
|
||||||
|
|
||||||
|
import com.intellij.ide.highlighter.JavaFileType
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
@@ -28,11 +29,11 @@ interface JavaModule {
|
|||||||
val name: String
|
val name: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A directory or the root of a .jar file on the module path. Can also be the path to the `module-info.java` file if that path
|
* A non-empty list of directories or roots of .jar files on the module path. Can also contain the path to the `module-info.java` file
|
||||||
* was passed as an explicit argument to the compiler.
|
* if that path was passed as an explicit argument to the compiler.
|
||||||
* In case of an explicit module, module-info.class file can be found in its children.
|
* In case of an explicit module, module-info.class or module-info.java file must exist in at least one of these roots (the first wins).
|
||||||
*/
|
*/
|
||||||
val moduleRoot: VirtualFile
|
val moduleRoots: List<Root>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A module-info.class or module-info.java file where this module was loaded from.
|
* A module-info.class or module-info.java file where this module was loaded from.
|
||||||
@@ -40,10 +41,11 @@ interface JavaModule {
|
|||||||
val moduleInfoFile: VirtualFile?
|
val moduleInfoFile: VirtualFile?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `true` if this module is either an automatic module on the module path, or an explicit module loaded from module-info.class.
|
* `true` if this module is an explicit module loaded from module-info.java, `false` otherwise. This usually corresponds to the module
|
||||||
* `false` if this module is an explicit module loaded from module-info.java.
|
* currently being compiled.
|
||||||
|
* Note that in case of partial/incremental compilation, the source module may contain both binary roots and non-binary roots.
|
||||||
*/
|
*/
|
||||||
val isBinary: Boolean
|
val isSourceModule: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `true` if this module exports the package with the given FQ name to all dependent modules.
|
* `true` if this module exports the package with the given FQ name to all dependent modules.
|
||||||
@@ -64,10 +66,12 @@ interface JavaModule {
|
|||||||
*/
|
*/
|
||||||
fun exportsTo(packageFqName: FqName, moduleName: String): Boolean
|
fun exportsTo(packageFqName: FqName, moduleName: String): Boolean
|
||||||
|
|
||||||
class Automatic(override val name: String, override val moduleRoot: VirtualFile) : JavaModule {
|
data class Root(val file: VirtualFile, val isBinary: Boolean)
|
||||||
|
|
||||||
|
class Automatic(override val name: String, override val moduleRoots: List<Root>) : JavaModule {
|
||||||
override val moduleInfoFile: VirtualFile? get() = null
|
override val moduleInfoFile: VirtualFile? get() = null
|
||||||
|
|
||||||
override val isBinary: Boolean get() = true
|
override val isSourceModule: Boolean get() = false
|
||||||
|
|
||||||
override fun exports(packageFqName: FqName): Boolean = true
|
override fun exports(packageFqName: FqName): Boolean = true
|
||||||
|
|
||||||
@@ -78,13 +82,15 @@ interface JavaModule {
|
|||||||
|
|
||||||
class Explicit(
|
class Explicit(
|
||||||
val moduleInfo: JavaModuleInfo,
|
val moduleInfo: JavaModuleInfo,
|
||||||
override val moduleRoot: VirtualFile,
|
override val moduleRoots: List<Root>,
|
||||||
override val moduleInfoFile: VirtualFile,
|
override val moduleInfoFile: VirtualFile
|
||||||
override val isBinary: Boolean
|
|
||||||
) : JavaModule {
|
) : JavaModule {
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = moduleInfo.moduleName
|
get() = moduleInfo.moduleName
|
||||||
|
|
||||||
|
override val isSourceModule: Boolean
|
||||||
|
get() = moduleInfoFile.fileType == JavaFileType.INSTANCE
|
||||||
|
|
||||||
override fun exports(packageFqName: FqName): Boolean {
|
override fun exports(packageFqName: FqName): Boolean {
|
||||||
return moduleInfo.exports.any { (fqName, toModules) ->
|
return moduleInfo.exports.any { (fqName, toModules) ->
|
||||||
fqName == packageFqName && toModules.isEmpty()
|
fqName == packageFqName && toModules.isEmpty()
|
||||||
@@ -100,3 +106,5 @@ interface JavaModule {
|
|||||||
override fun toString(): String = name
|
override fun toString(): String = name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val KOTLIN_STDLIB_MODULE_NAME = "kotlin.stdlib"
|
||||||
@@ -118,7 +118,6 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
|||||||
moduleDescriptor.setDependencies(LazyModuleDependencies(
|
moduleDescriptor.setDependencies(LazyModuleDependencies(
|
||||||
projectContext.storageManager,
|
projectContext.storageManager,
|
||||||
module,
|
module,
|
||||||
modulePlatforms,
|
|
||||||
firstDependency,
|
firstDependency,
|
||||||
this))
|
this))
|
||||||
|
|
||||||
@@ -270,7 +269,6 @@ abstract class AnalyzerFacade {
|
|||||||
class LazyModuleDependencies<M: ModuleInfo>(
|
class LazyModuleDependencies<M: ModuleInfo>(
|
||||||
storageManager: StorageManager,
|
storageManager: StorageManager,
|
||||||
private val module: M,
|
private val module: M,
|
||||||
modulePlatforms: (M) -> MultiTargetPlatform?,
|
|
||||||
firstDependency: M? = null,
|
firstDependency: M? = null,
|
||||||
private val resolverForProject: ResolverForProjectImpl<M>
|
private val resolverForProject: ResolverForProjectImpl<M>
|
||||||
) : ModuleDependencies {
|
) : ModuleDependencies {
|
||||||
@@ -292,14 +290,6 @@ class LazyModuleDependencies<M: ModuleInfo>(
|
|||||||
}.toList()
|
}.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val implementingModules = storageManager.createLazyValue {
|
|
||||||
if (modulePlatforms(module) != MultiTargetPlatform.Common) emptySet<M>()
|
|
||||||
else resolverForProject.modules
|
|
||||||
.filterTo(mutableSetOf()) {
|
|
||||||
modulePlatforms(it) != MultiTargetPlatform.Common && module in it.dependencies()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val allDependencies: List<ModuleDescriptorImpl> get() = dependencies()
|
override val allDependencies: List<ModuleDescriptorImpl> get() = dependencies()
|
||||||
|
|
||||||
override val modulesWhoseInternalsAreVisible: Set<ModuleDescriptorImpl>
|
override val modulesWhoseInternalsAreVisible: Set<ModuleDescriptorImpl>
|
||||||
@@ -308,8 +298,6 @@ class LazyModuleDependencies<M: ModuleInfo>(
|
|||||||
resolverForProject.descriptorForModule(it as M)
|
resolverForProject.descriptorForModule(it as M)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val allImplementingModules: Set<ModuleDescriptorImpl>
|
|
||||||
get() = implementingModules().mapTo(mutableSetOf()) { resolverForProject.descriptorForModule(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
|||||||
typealias ImmutableMap<K, V> = javaslang.collection.Map<K, V>
|
typealias ImmutableMap<K, V> = javaslang.collection.Map<K, V>
|
||||||
typealias ImmutableHashMap<K, V> = javaslang.collection.HashMap<K, V>
|
typealias ImmutableHashMap<K, V> = javaslang.collection.HashMap<K, V>
|
||||||
|
|
||||||
abstract class ControlFlowInfo<S : ControlFlowInfo<S, D>, D>
|
abstract class ControlFlowInfo<S : ControlFlowInfo<S, D>, D : Any>
|
||||||
internal constructor(
|
internal constructor(
|
||||||
protected val map: ImmutableMap<VariableDescriptor, D> = ImmutableHashMap.empty()
|
protected val map: ImmutableMap<VariableDescriptor, D> = ImmutableHashMap.empty()
|
||||||
) : ImmutableMap<VariableDescriptor, D> by map {
|
) : ImmutableMap<VariableDescriptor, D> by map, ReadOnlyControlFlowInfo<D> {
|
||||||
abstract protected fun copy(newMap: ImmutableMap<VariableDescriptor, D>): S
|
abstract protected fun copy(newMap: ImmutableMap<VariableDescriptor, D>): S
|
||||||
|
|
||||||
override fun put(key: VariableDescriptor, value: D): S = put(key, value, this[key].getOrElse(null as D?))
|
override fun put(key: VariableDescriptor, value: D): S = put(key, value, this[key].getOrElse(null as D?))
|
||||||
@@ -41,6 +41,9 @@ internal constructor(
|
|||||||
return copy(map.put(key, value))
|
return copy(map.put(key, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getOrNull(variableDescriptor: VariableDescriptor): D? = this[variableDescriptor].getOrElse(null as D?)
|
||||||
|
override fun asMap() = this
|
||||||
|
|
||||||
fun retainAll(predicate: (VariableDescriptor) -> Boolean): S = copy(map.removeAll(map.keySet().filterNot(predicate)))
|
fun retainAll(predicate: (VariableDescriptor) -> Boolean): S = copy(map.removeAll(map.keySet().filterNot(predicate)))
|
||||||
|
|
||||||
override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*, *>)?.map
|
override fun equals(other: Any?) = map == (other as? ControlFlowInfo<*, *>)?.map
|
||||||
@@ -53,16 +56,26 @@ internal constructor(
|
|||||||
operator fun <T> Tuple2<T, *>.component1(): T = _1()
|
operator fun <T> Tuple2<T, *>.component1(): T = _1()
|
||||||
operator fun <T> Tuple2<*, T>.component2(): T = _2()
|
operator fun <T> Tuple2<*, T>.component2(): T = _2()
|
||||||
|
|
||||||
fun <K, V> ImmutableMap<K, V>.getOrNull(k: K): V? = this[k].getOrElse(null as V?)
|
interface ReadOnlyControlFlowInfo<D : Any> {
|
||||||
|
fun getOrNull(variableDescriptor: VariableDescriptor): D?
|
||||||
|
// Only used in tests
|
||||||
|
fun asMap(): ImmutableMap<VariableDescriptor, D>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReadOnlyInitControlFlowInfo : ReadOnlyControlFlowInfo<VariableControlFlowState> {
|
||||||
|
fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo<VariableUseState>
|
||||||
|
|
||||||
class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlFlowState> = ImmutableHashMap.empty()) :
|
class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlFlowState> = ImmutableHashMap.empty()) :
|
||||||
ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map) {
|
ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map), ReadOnlyInitControlFlowInfo {
|
||||||
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) = InitControlFlowInfo(newMap)
|
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) = InitControlFlowInfo(newMap)
|
||||||
|
|
||||||
// this = output of EXHAUSTIVE_WHEN_ELSE instruction
|
// this = output of EXHAUSTIVE_WHEN_ELSE instruction
|
||||||
// merge = input of MergeInstruction
|
// merge = input of MergeInstruction
|
||||||
// returns true if definite initialization in when happens here
|
// returns true if definite initialization in when happens here
|
||||||
fun checkDefiniteInitializationInWhen(merge: InitControlFlowInfo): Boolean {
|
override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean {
|
||||||
for ((key, value) in iterator()) {
|
for ((key, value) in iterator()) {
|
||||||
if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
|
if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
|
||||||
merge.getOrNull(key)?.initState == InitState.INITIALIZED) {
|
merge.getOrNull(key)?.initState == InitState.INITIALIZED) {
|
||||||
@@ -74,7 +87,7 @@ class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlF
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UseControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableUseState> = ImmutableHashMap.empty()) :
|
class UseControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableUseState> = ImmutableHashMap.empty()) :
|
||||||
ControlFlowInfo<UseControlFlowInfo, VariableUseState>(map) {
|
ControlFlowInfo<UseControlFlowInfo, VariableUseState>(map), ReadOnlyUseControlFlowInfo {
|
||||||
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableUseState>) = UseControlFlowInfo(newMap)
|
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableUseState>) = UseControlFlowInfo(newMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,8 +274,8 @@ class ControlFlowInformationProvider private constructor(
|
|||||||
|
|
||||||
pseudocode.traverse(TraversalOrder.FORWARD, initializers) {
|
pseudocode.traverse(TraversalOrder.FORWARD, initializers) {
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
enterData: ImmutableMap<VariableDescriptor, VariableControlFlowState>,
|
enterData: ReadOnlyInitControlFlowInfo,
|
||||||
exitData: ImmutableMap<VariableDescriptor, VariableControlFlowState> ->
|
exitData: ReadOnlyInitControlFlowInfo ->
|
||||||
|
|
||||||
val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo)
|
val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo)
|
||||||
if (ctxt.variableDescriptor == null) return@traverse
|
if (ctxt.variableDescriptor == null) return@traverse
|
||||||
@@ -486,18 +486,21 @@ class ControlFlowInformationProvider private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAssignmentBeforeDeclaration(ctxt: VariableInitContext, expression: KtExpression) =
|
private fun checkAssignmentBeforeDeclaration(ctxt: VariableInitContext, expression: KtExpression) =
|
||||||
if (ctxt.enterInitState?.isDeclared == true
|
if (ctxt.isInitializationBeforeDeclaration()) {
|
||||||
|| ctxt.exitInitState?.isDeclared == true
|
|
||||||
|| ctxt.enterInitState?.mayBeInitialized() == true
|
|
||||||
|| ctxt.exitInitState?.mayBeInitialized() != true) {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (ctxt.variableDescriptor != null) {
|
if (ctxt.variableDescriptor != null) {
|
||||||
report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt)
|
report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun VariableInitContext.isInitializationBeforeDeclaration(): Boolean =
|
||||||
|
// is not declared
|
||||||
|
enterInitState?.isDeclared != true && exitInitState?.isDeclared != true &&
|
||||||
|
// wasn't initialized before current instruction
|
||||||
|
enterInitState?.mayBeInitialized() != true
|
||||||
|
|
||||||
private fun checkInitializationForCustomSetter(ctxt: VariableInitContext, expression: KtExpression): Boolean {
|
private fun checkInitializationForCustomSetter(ctxt: VariableInitContext, expression: KtExpression): Boolean {
|
||||||
val variableDescriptor = ctxt.variableDescriptor
|
val variableDescriptor = ctxt.variableDescriptor
|
||||||
@@ -532,7 +535,7 @@ class ControlFlowInformationProvider private constructor(
|
|||||||
|
|
||||||
private fun recordInitializedVariables(
|
private fun recordInitializedVariables(
|
||||||
pseudocode: Pseudocode,
|
pseudocode: Pseudocode,
|
||||||
initializersMap: Map<Instruction, Edges<InitControlFlowInfo>>
|
initializersMap: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>>
|
||||||
) {
|
) {
|
||||||
val initializers = initializersMap[pseudocode.exitInstruction] ?: return
|
val initializers = initializersMap[pseudocode.exitInstruction] ?: return
|
||||||
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false)
|
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false)
|
||||||
@@ -554,8 +557,8 @@ class ControlFlowInformationProvider private constructor(
|
|||||||
val usedValueExpressions = hashSetOf<KtExpression>()
|
val usedValueExpressions = hashSetOf<KtExpression>()
|
||||||
pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) {
|
pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) {
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
enterData: ImmutableMap<VariableDescriptor, VariableUseState>,
|
enterData: ReadOnlyUseControlFlowInfo,
|
||||||
_: ImmutableMap<VariableDescriptor, VariableUseState> ->
|
_: ReadOnlyUseControlFlowInfo ->
|
||||||
|
|
||||||
val ctxt = VariableUseContext(instruction, reportedDiagnosticMap)
|
val ctxt = VariableUseContext(instruction, reportedDiagnosticMap)
|
||||||
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false)
|
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false)
|
||||||
@@ -1030,8 +1033,8 @@ class ControlFlowInformationProvider private constructor(
|
|||||||
private inner class VariableInitContext(
|
private inner class VariableInitContext(
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
map: MutableMap<Instruction, DiagnosticFactory<*>>,
|
map: MutableMap<Instruction, DiagnosticFactory<*>>,
|
||||||
`in`: ImmutableMap<VariableDescriptor, VariableControlFlowState>,
|
`in`: ReadOnlyInitControlFlowInfo,
|
||||||
out: ImmutableMap<VariableDescriptor, VariableControlFlowState>,
|
out: ReadOnlyInitControlFlowInfo,
|
||||||
blockScopeVariableInfo: BlockScopeVariableInfo
|
blockScopeVariableInfo: BlockScopeVariableInfo
|
||||||
) : VariableContext(instruction, map) {
|
) : VariableContext(instruction, map) {
|
||||||
internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`)
|
internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`)
|
||||||
@@ -1040,7 +1043,7 @@ class ControlFlowInformationProvider private constructor(
|
|||||||
private fun initialize(
|
private fun initialize(
|
||||||
variableDescriptor: VariableDescriptor?,
|
variableDescriptor: VariableDescriptor?,
|
||||||
blockScopeVariableInfo: BlockScopeVariableInfo,
|
blockScopeVariableInfo: BlockScopeVariableInfo,
|
||||||
map: ImmutableMap<VariableDescriptor, VariableControlFlowState>
|
map: ReadOnlyInitControlFlowInfo
|
||||||
): VariableControlFlowState? {
|
): VariableControlFlowState? {
|
||||||
val state = map.getOrNull(variableDescriptor ?: return null)
|
val state = map.getOrNull(variableDescriptor ?: return null)
|
||||||
if (state != null) return state
|
if (state != null) return state
|
||||||
|
|||||||
@@ -26,67 +26,128 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstructi
|
|||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges
|
||||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtProperty
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContextUtils.variableDescriptorForDeclaration
|
import org.jetbrains.kotlin.resolve.BindingContextUtils.variableDescriptorForDeclaration
|
||||||
import java.util.*
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
|
private typealias ImmutableSet<T> = javaslang.collection.Set<T>
|
||||||
|
private typealias ImmutableHashSet<T> = javaslang.collection.HashSet<T>
|
||||||
|
|
||||||
class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingContext: BindingContext) {
|
class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingContext: BindingContext) {
|
||||||
|
private val containsDoWhile = pseudocode.rootPseudocode.containsDoWhile
|
||||||
private val pseudocodeVariableDataCollector = PseudocodeVariableDataCollector(bindingContext, pseudocode)
|
private val pseudocodeVariableDataCollector = PseudocodeVariableDataCollector(bindingContext, pseudocode)
|
||||||
|
|
||||||
private val declaredVariablesForDeclaration = hashMapOf<Pseudocode, Set<VariableDescriptor>>()
|
private class VariablesForDeclaration(
|
||||||
|
val valsWithTrivialInitializer: Set<VariableDescriptor>,
|
||||||
|
val nonTrivialVariables: Set<VariableDescriptor>
|
||||||
|
) {
|
||||||
|
val allVars =
|
||||||
|
if (nonTrivialVariables.isEmpty())
|
||||||
|
valsWithTrivialInitializer
|
||||||
|
else
|
||||||
|
LinkedHashSet(valsWithTrivialInitializer).also { it.addAll(nonTrivialVariables) }
|
||||||
|
}
|
||||||
|
|
||||||
val variableInitializers: Map<Instruction, Edges<InitControlFlowInfo>> by lazy {
|
private val declaredVariablesForDeclaration = hashMapOf<Pseudocode, VariablesForDeclaration>()
|
||||||
|
private val rootVariables by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val variableInitializers: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> by lazy {
|
||||||
computeVariableInitializers()
|
computeVariableInitializers()
|
||||||
}
|
}
|
||||||
|
|
||||||
val blockScopeVariableInfo: BlockScopeVariableInfo
|
val blockScopeVariableInfo: BlockScopeVariableInfo
|
||||||
get() = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
get() = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
||||||
|
|
||||||
fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> {
|
fun getDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): Set<VariableDescriptor> =
|
||||||
|
getAllDeclaredVariables(pseudocode, includeInsideLocalDeclarations).allVars
|
||||||
|
|
||||||
|
fun isVariableWithTrivialInitializer(variableDescriptor: VariableDescriptor) =
|
||||||
|
variableDescriptor in rootVariables.valsWithTrivialInitializer
|
||||||
|
|
||||||
|
private fun getAllDeclaredVariables(pseudocode: Pseudocode, includeInsideLocalDeclarations: Boolean): VariablesForDeclaration {
|
||||||
if (!includeInsideLocalDeclarations) {
|
if (!includeInsideLocalDeclarations) {
|
||||||
return getUpperLevelDeclaredVariables(pseudocode)
|
return getUpperLevelDeclaredVariables(pseudocode)
|
||||||
}
|
}
|
||||||
val declaredVariables = linkedSetOf<VariableDescriptor>()
|
val nonTrivialVariables = linkedSetOf<VariableDescriptor>()
|
||||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(pseudocode))
|
val valsWithTrivialInitializer = linkedSetOf<VariableDescriptor>()
|
||||||
|
addVariablesFromPseudocode(pseudocode, nonTrivialVariables, valsWithTrivialInitializer)
|
||||||
|
|
||||||
for (localFunctionDeclarationInstruction in pseudocode.localDeclarations) {
|
for (localFunctionDeclarationInstruction in pseudocode.localDeclarations) {
|
||||||
val localPseudocode = localFunctionDeclarationInstruction.body
|
val localPseudocode = localFunctionDeclarationInstruction.body
|
||||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(localPseudocode))
|
addVariablesFromPseudocode(localPseudocode, nonTrivialVariables, valsWithTrivialInitializer)
|
||||||
}
|
}
|
||||||
return declaredVariables
|
return VariablesForDeclaration(valsWithTrivialInitializer, nonTrivialVariables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
private fun addVariablesFromPseudocode(
|
||||||
var declaredVariables = declaredVariablesForDeclaration[pseudocode]
|
pseudocode: Pseudocode,
|
||||||
if (declaredVariables == null) {
|
nonTrivialVariables: MutableSet<VariableDescriptor>,
|
||||||
declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode)
|
valsWithTrivialInitializer: MutableSet<VariableDescriptor>
|
||||||
declaredVariablesForDeclaration.put(pseudocode, declaredVariables)
|
) {
|
||||||
|
getUpperLevelDeclaredVariables(pseudocode).let {
|
||||||
|
nonTrivialVariables.addAll(it.nonTrivialVariables)
|
||||||
|
valsWithTrivialInitializer.addAll(it.valsWithTrivialInitializer)
|
||||||
}
|
}
|
||||||
return declaredVariables
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode) = declaredVariablesForDeclaration.getOrPut(pseudocode) {
|
||||||
val declaredVariables = linkedSetOf<VariableDescriptor>()
|
computeDeclaredVariablesForPseudocode(pseudocode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): VariablesForDeclaration {
|
||||||
|
val valsWithTrivialInitializer = linkedSetOf<VariableDescriptor>()
|
||||||
|
val nonTrivialVariables = linkedSetOf<VariableDescriptor>()
|
||||||
for (instruction in pseudocode.instructions) {
|
for (instruction in pseudocode.instructions) {
|
||||||
if (instruction is VariableDeclarationInstruction) {
|
if (instruction is VariableDeclarationInstruction) {
|
||||||
val variableDeclarationElement = instruction.variableDeclarationElement
|
val variableDeclarationElement = instruction.variableDeclarationElement
|
||||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
|
val descriptor =
|
||||||
variableDescriptorForDeclaration(descriptor)?.let {
|
variableDescriptorForDeclaration(
|
||||||
declaredVariables.add(it)
|
bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
|
||||||
|
) ?: continue
|
||||||
|
|
||||||
|
if (!containsDoWhile && isValWithTrivialInitializer(variableDeclarationElement, descriptor)) {
|
||||||
|
valsWithTrivialInitializer.add(descriptor)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nonTrivialVariables.add(descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableSet(declaredVariables)
|
|
||||||
|
return VariablesForDeclaration(valsWithTrivialInitializer, nonTrivialVariables)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isValWithTrivialInitializer(variableDeclarationElement: KtDeclaration, descriptor: VariableDescriptor) =
|
||||||
|
variableDeclarationElement is KtParameter || variableDeclarationElement is KtObjectDeclaration ||
|
||||||
|
variableDeclarationElement.safeAs<KtVariableDeclaration>()?.isVariableWithTrivialInitializer(descriptor) == true
|
||||||
|
|
||||||
|
private fun KtVariableDeclaration.isVariableWithTrivialInitializer(descriptor: VariableDescriptor): Boolean {
|
||||||
|
if (descriptor.isPropertyWithoutBackingField()) return true
|
||||||
|
if (isVar) return false
|
||||||
|
return initializer != null || safeAs<KtProperty>()?.delegate != null || this is KtDestructuringDeclarationEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun VariableDescriptor.isPropertyWithoutBackingField(): Boolean {
|
||||||
|
if (this !is PropertyDescriptor) return false
|
||||||
|
return bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, this) != true
|
||||||
}
|
}
|
||||||
|
|
||||||
// variable initializers
|
// variable initializers
|
||||||
|
|
||||||
private fun computeVariableInitializers(): Map<Instruction, Edges<InitControlFlowInfo>> {
|
private fun computeVariableInitializers(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> {
|
||||||
|
|
||||||
val blockScopeVariableInfo = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
val blockScopeVariableInfo = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
||||||
|
|
||||||
|
val resultForValsWithTrivialInitializer = computeInitInfoForTrivialVals()
|
||||||
|
|
||||||
|
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForValsWithTrivialInitializer
|
||||||
|
|
||||||
return pseudocodeVariableDataCollector.collectData(TraversalOrder.FORWARD, InitControlFlowInfo()) {
|
return pseudocodeVariableDataCollector.collectData(TraversalOrder.FORWARD, InitControlFlowInfo()) {
|
||||||
instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
|
instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
|
||||||
|
|
||||||
@@ -94,6 +155,88 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
|||||||
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
|
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
|
||||||
instruction, enterInstructionData, blockScopeVariableInfo)
|
instruction, enterInstructionData, blockScopeVariableInfo)
|
||||||
Edges(enterInstructionData, exitInstructionData)
|
Edges(enterInstructionData, exitInstructionData)
|
||||||
|
}.mapValues {
|
||||||
|
(instruction, edges) ->
|
||||||
|
val trivialEdges = resultForValsWithTrivialInitializer[instruction]!!
|
||||||
|
Edges(trivialEdges.incoming.replaceDelegate(edges.incoming), trivialEdges.outgoing.replaceDelegate(edges.outgoing))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeInitInfoForTrivialVals(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfoImpl>> {
|
||||||
|
val result = hashMapOf<Instruction, Edges<ReadOnlyInitControlFlowInfoImpl>>()
|
||||||
|
var declaredSet = ImmutableHashSet.empty<VariableDescriptor>()
|
||||||
|
var initSet = ImmutableHashSet.empty<VariableDescriptor>()
|
||||||
|
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||||
|
val enterState = ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, null)
|
||||||
|
when (instruction) {
|
||||||
|
is VariableDeclarationInstruction ->
|
||||||
|
extractValWithTrivialInitializer(instruction)?.let {
|
||||||
|
variableDescriptor ->
|
||||||
|
declaredSet = declaredSet.add(variableDescriptor)
|
||||||
|
}
|
||||||
|
is WriteValueInstruction -> {
|
||||||
|
val variableDescriptor = extractValWithTrivialInitializer(instruction)
|
||||||
|
if (variableDescriptor != null && instruction.isTrivialInitializer()) {
|
||||||
|
initSet = initSet.add(variableDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val afterState = ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, null)
|
||||||
|
|
||||||
|
result[instruction] = Edges(enterState, afterState)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun WriteValueInstruction.isTrivialInitializer() =
|
||||||
|
element is KtVariableDeclaration || element is KtParameter
|
||||||
|
|
||||||
|
private inner class ReadOnlyInitControlFlowInfoImpl(
|
||||||
|
val declaredSet: ImmutableSet<VariableDescriptor>,
|
||||||
|
val initSet: ImmutableSet<VariableDescriptor>,
|
||||||
|
private val delegate: ReadOnlyInitControlFlowInfo?
|
||||||
|
) : ReadOnlyInitControlFlowInfo {
|
||||||
|
override fun getOrNull(variableDescriptor: VariableDescriptor): VariableControlFlowState? {
|
||||||
|
if (variableDescriptor in declaredSet) {
|
||||||
|
return VariableControlFlowState.create(isInitialized = variableDescriptor in initSet, isDeclared = true)
|
||||||
|
}
|
||||||
|
return delegate?.getOrNull(variableDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkDefiniteInitializationInWhen(merge: ReadOnlyInitControlFlowInfo): Boolean =
|
||||||
|
delegate?.checkDefiniteInitializationInWhen(merge) ?: false
|
||||||
|
|
||||||
|
fun replaceDelegate(newDelegate: ReadOnlyInitControlFlowInfo): ReadOnlyInitControlFlowInfo =
|
||||||
|
ReadOnlyInitControlFlowInfoImpl(declaredSet, initSet, newDelegate)
|
||||||
|
|
||||||
|
override fun asMap(): ImmutableMap<VariableDescriptor, VariableControlFlowState> {
|
||||||
|
val initial = delegate?.asMap() ?: ImmutableHashMap.empty()
|
||||||
|
|
||||||
|
return declaredSet.fold(initial) {
|
||||||
|
acc, variableDescriptor ->
|
||||||
|
acc.put(variableDescriptor, getOrNull(variableDescriptor)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as ReadOnlyInitControlFlowInfoImpl
|
||||||
|
|
||||||
|
if (declaredSet != other.declaredSet) return false
|
||||||
|
if (initSet != other.initSet) return false
|
||||||
|
if (delegate != other.delegate) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = declaredSet.hashCode()
|
||||||
|
result = 31 * result + initSet.hashCode()
|
||||||
|
result = 31 * result + (delegate?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +258,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
|||||||
if (instruction !is WriteValueInstruction && instruction !is VariableDeclarationInstruction) {
|
if (instruction !is WriteValueInstruction && instruction !is VariableDeclarationInstruction) {
|
||||||
return enterInstructionData
|
return enterInstructionData
|
||||||
}
|
}
|
||||||
val variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext) ?: return enterInstructionData
|
val variable =
|
||||||
|
PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext)
|
||||||
|
?.takeIf { it in rootVariables.nonTrivialVariables }
|
||||||
|
?: return enterInstructionData
|
||||||
var exitInstructionData = enterInstructionData
|
var exitInstructionData = enterInstructionData
|
||||||
if (instruction is WriteValueInstruction) {
|
if (instruction is WriteValueInstruction) {
|
||||||
// if writing to already initialized object
|
// if writing to already initialized object
|
||||||
@@ -129,10 +275,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// instruction instanceof VariableDeclarationInstruction
|
// instruction instanceof VariableDeclarationInstruction
|
||||||
var enterInitState: VariableControlFlowState? = enterInstructionData.getOrNull(variable)
|
val enterInitState =
|
||||||
if (enterInitState == null) {
|
enterInstructionData.getOrNull(variable)
|
||||||
enterInitState = getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
|
?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
|
||||||
}
|
|
||||||
if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
|
if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
|
||||||
val variableDeclarationInfo = VariableControlFlowState.create(enterInitState.initState, isDeclared = true)
|
val variableDeclarationInfo = VariableControlFlowState.create(enterInitState.initState, isDeclared = true)
|
||||||
exitInstructionData = exitInstructionData.put(variable, variableDeclarationInfo, enterInitState)
|
exitInstructionData = exitInstructionData.put(variable, variableDeclarationInfo, enterInitState)
|
||||||
@@ -143,46 +289,128 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
|||||||
|
|
||||||
// variable use
|
// variable use
|
||||||
|
|
||||||
val variableUseStatusData: Map<Instruction, Edges<UseControlFlowInfo>>
|
val variableUseStatusData: Map<Instruction, Edges<ReadOnlyUseControlFlowInfo>>
|
||||||
get() = pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) {
|
get() {
|
||||||
instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
val resultForTrivialVals = computeUseInfoForTrivialVals()
|
||||||
|
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForTrivialVals
|
||||||
|
|
||||||
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
|
return pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) { instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
||||||
incomingEdgesData.single()
|
|
||||||
}
|
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
|
||||||
else {
|
incomingEdgesData.single()
|
||||||
incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData ->
|
}
|
||||||
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
|
else {
|
||||||
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
|
incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData ->
|
||||||
|
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
|
||||||
|
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val variableDescriptor = PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext)
|
val variableDescriptor =
|
||||||
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext)
|
||||||
Edges(enterResult, enterResult)
|
?.takeIf { it in rootVariables.nonTrivialVariables }
|
||||||
}
|
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
||||||
else {
|
Edges(enterResult, enterResult)
|
||||||
val exitResult =
|
}
|
||||||
if (instruction is ReadValueInstruction) {
|
else {
|
||||||
enterResult.put(variableDescriptor, VariableUseState.READ)
|
val exitResult =
|
||||||
}
|
if (instruction is ReadValueInstruction) {
|
||||||
else {
|
enterResult.put(variableDescriptor, VariableUseState.READ)
|
||||||
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
|
}
|
||||||
if (variableUseState == null) {
|
else {
|
||||||
variableUseState = VariableUseState.UNUSED
|
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
|
||||||
}
|
if (variableUseState == null) {
|
||||||
when (variableUseState) {
|
variableUseState = VariableUseState.UNUSED
|
||||||
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
|
}
|
||||||
enterResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
|
when (variableUseState) {
|
||||||
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
|
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_READ ->
|
||||||
enterResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
|
enterResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ)
|
||||||
}
|
VariableUseState.WRITTEN_AFTER_READ, VariableUseState.READ ->
|
||||||
}
|
enterResult.put(variableDescriptor, VariableUseState.WRITTEN_AFTER_READ)
|
||||||
Edges(enterResult, exitResult)
|
}
|
||||||
|
}
|
||||||
|
Edges(enterResult, exitResult)
|
||||||
|
}
|
||||||
|
}.mapValues {
|
||||||
|
(instruction, edges) ->
|
||||||
|
val edgeForTrivialVals = resultForTrivialVals[instruction]!!
|
||||||
|
|
||||||
|
Edges(
|
||||||
|
edgeForTrivialVals.incoming.replaceDelegate(edges.incoming),
|
||||||
|
edgeForTrivialVals.outgoing.replaceDelegate(edges.outgoing)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun computeUseInfoForTrivialVals(): Map<Instruction, Edges<ReadOnlyUseControlFlowInfoImpl>> {
|
||||||
|
val used = hashSetOf<VariableDescriptor>()
|
||||||
|
|
||||||
|
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||||
|
if (instruction is ReadValueInstruction) {
|
||||||
|
extractValWithTrivialInitializer(instruction)?.let {
|
||||||
|
used.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val constantUseInfo = ReadOnlyUseControlFlowInfoImpl(used, null)
|
||||||
|
val constantEdges = Edges(constantUseInfo, constantUseInfo)
|
||||||
|
val result = hashMapOf<Instruction, Edges<ReadOnlyUseControlFlowInfoImpl>>()
|
||||||
|
|
||||||
|
pseudocode.traverse(TraversalOrder.FORWARD) { instruction ->
|
||||||
|
result[instruction] = constantEdges
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractValWithTrivialInitializer(instruction: Instruction): VariableDescriptor? {
|
||||||
|
return PseudocodeUtil.extractVariableDescriptorIfAny(instruction, bindingContext)?.takeIf {
|
||||||
|
it in rootVariables.valsWithTrivialInitializer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ReadOnlyUseControlFlowInfoImpl(
|
||||||
|
val used: Set<VariableDescriptor>,
|
||||||
|
val delegate: ReadOnlyUseControlFlowInfo?
|
||||||
|
) : ReadOnlyUseControlFlowInfo {
|
||||||
|
override fun getOrNull(variableDescriptor: VariableDescriptor): VariableUseState? {
|
||||||
|
if (variableDescriptor in used) return VariableUseState.READ
|
||||||
|
return delegate?.getOrNull(variableDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replaceDelegate(newDelegate: ReadOnlyUseControlFlowInfo): ReadOnlyUseControlFlowInfo =
|
||||||
|
ReadOnlyUseControlFlowInfoImpl(used, newDelegate)
|
||||||
|
|
||||||
|
override fun asMap(): ImmutableMap<VariableDescriptor, VariableUseState> {
|
||||||
|
val initial = delegate?.asMap() ?: ImmutableHashMap.empty()
|
||||||
|
|
||||||
|
return used.fold(initial) {
|
||||||
|
acc, variableDescriptor ->
|
||||||
|
acc.put(variableDescriptor, getOrNull(variableDescriptor)!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as ReadOnlyUseControlFlowInfoImpl
|
||||||
|
|
||||||
|
if (used != other.used) return false
|
||||||
|
if (delegate != other.delegate) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = used.hashCode()
|
||||||
|
result = 31 * result + (delegate?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|||||||
+5
-2
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.cfg.pseudocode
|
|||||||
import com.intellij.util.containers.Stack
|
import com.intellij.util.containers.Stack
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.cfg.*
|
import org.jetbrains.kotlin.cfg.*
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
|
||||||
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||||
@@ -113,6 +112,10 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
|||||||
override fun createUnboundLabel(name: String): Label = pseudocode.createLabel("L" + labelCount++, name)
|
override fun createUnboundLabel(name: String): Label = pseudocode.createLabel("L" + labelCount++, name)
|
||||||
|
|
||||||
override fun enterLoop(expression: KtLoopExpression): LoopInfo {
|
override fun enterLoop(expression: KtLoopExpression): LoopInfo {
|
||||||
|
if (expression is KtDoWhileExpression) {
|
||||||
|
(pseudocode.rootPseudocode as PseudocodeImpl).containsDoWhile = true
|
||||||
|
}
|
||||||
|
|
||||||
val info = LoopInfo(
|
val info = LoopInfo(
|
||||||
expression,
|
expression,
|
||||||
createUnboundLabel("loop entry point"),
|
createUnboundLabel("loop entry point"),
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ interface Pseudocode {
|
|||||||
|
|
||||||
val enterInstruction: SubroutineEnterInstruction
|
val enterInstruction: SubroutineEnterInstruction
|
||||||
|
|
||||||
|
val containsDoWhile: Boolean
|
||||||
|
val rootPseudocode: Pseudocode
|
||||||
|
|
||||||
fun getElementValue(element: KtElement?): PseudoValue?
|
fun getElementValue(element: KtElement?): PseudoValue?
|
||||||
|
|
||||||
fun getValueElements(value: PseudoValue?): List<KtElement>
|
fun getValueElements(value: PseudoValue?): List<KtElement>
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
|||||||
|
|
||||||
private var postPrecessed = false
|
private var postPrecessed = false
|
||||||
|
|
||||||
|
override var containsDoWhile: Boolean = false
|
||||||
|
internal set
|
||||||
|
|
||||||
private fun getLocalDeclarations(pseudocode: Pseudocode): Set<LocalFunctionDeclarationInstruction> {
|
private fun getLocalDeclarations(pseudocode: Pseudocode): Set<LocalFunctionDeclarationInstruction> {
|
||||||
val localDeclarations = linkedSetOf<LocalFunctionDeclarationInstruction>()
|
val localDeclarations = linkedSetOf<LocalFunctionDeclarationInstruction>()
|
||||||
for (instruction in (pseudocode as PseudocodeImpl).mutableInstructionList) {
|
for (instruction in (pseudocode as PseudocodeImpl).mutableInstructionList) {
|
||||||
@@ -88,7 +91,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
|||||||
return localDeclarations
|
return localDeclarations
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootPseudocode: Pseudocode
|
override val rootPseudocode: Pseudocode
|
||||||
get() {
|
get() {
|
||||||
var parent = parent
|
var parent = parent
|
||||||
while (parent != null) {
|
while (parent != null) {
|
||||||
|
|||||||
@@ -562,6 +562,9 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<KtConstructor<?>> EXPECTED_ENUM_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtConstructor<?>> EXPECTED_ENUM_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtEnumEntry> EXPECTED_ENUM_ENTRY_WITH_BODY = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtEnumEntry> EXPECTED_ENUM_ENTRY_WITH_BODY = DiagnosticFactory0.create(ERROR);
|
||||||
DiagnosticFactory0<KtExpression> EXPECTED_PROPERTY_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> EXPECTED_PROPERTY_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<KtPropertyDelegate> EXPECTED_DELEGATED_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<PsiElement> EXPECTED_LATEINIT_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<PsiElement> SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<KtTypeAlias> ACTUAL_TYPE_ALIAS_NOT_TO_CLASS = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
DiagnosticFactory0<KtTypeAlias> ACTUAL_TYPE_ALIAS_NOT_TO_CLASS = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||||
DiagnosticFactory0<KtTypeAlias>
|
DiagnosticFactory0<KtTypeAlias>
|
||||||
@@ -622,7 +625,8 @@ public interface Errors {
|
|||||||
DiagnosticFactory1<KtExpression, KotlinType> MISSING_RECEIVER = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<KtExpression, KotlinType> MISSING_RECEIVER = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory0<KtExpression> NO_RECEIVER_ALLOWED = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> NO_RECEIVER_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<KtExpression> ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory1<KtExpression, KotlinType> ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION = DiagnosticFactory1.create(WARNING);
|
||||||
|
DiagnosticFactory0<KtExpression> ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
// Call resolution
|
// Call resolution
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -270,13 +270,16 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(EXPECTED_ENUM_CONSTRUCTOR, "Expected enum class cannot have a constructor");
|
MAP.put(EXPECTED_ENUM_CONSTRUCTOR, "Expected enum class cannot have a constructor");
|
||||||
MAP.put(EXPECTED_ENUM_ENTRY_WITH_BODY, "Expected enum entry cannot have a body");
|
MAP.put(EXPECTED_ENUM_ENTRY_WITH_BODY, "Expected enum entry cannot have a body");
|
||||||
MAP.put(EXPECTED_PROPERTY_INITIALIZER, "Expected property cannot have an initializer");
|
MAP.put(EXPECTED_PROPERTY_INITIALIZER, "Expected property cannot have an initializer");
|
||||||
|
MAP.put(EXPECTED_DELEGATED_PROPERTY, "Expected property cannot be delegated");
|
||||||
|
MAP.put(EXPECTED_LATEINIT_PROPERTY, "Expected property cannot be lateinit");
|
||||||
|
MAP.put(SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS, "Expected classes cannot initialize supertypes");
|
||||||
|
|
||||||
MAP.put(ACTUAL_TYPE_ALIAS_NOT_TO_CLASS, "Right-hand side of actual type alias should be a class, not another type alias");
|
MAP.put(ACTUAL_TYPE_ALIAS_NOT_TO_CLASS, "Right-hand side of actual type alias should be a class, not another type alias");
|
||||||
MAP.put(ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE, "Aliased class should not have type parameters with declaration-site variance");
|
MAP.put(ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE, "Aliased class should not have type parameters with declaration-site variance");
|
||||||
MAP.put(ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE, "Right-hand side of actual type alias cannot contain use-site variance or star projections");
|
MAP.put(ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE, "Right-hand side of actual type alias cannot contain use-site variance or star projections");
|
||||||
MAP.put(ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION, "Type arguments in the right-hand side of actual type alias should be its type parameters in the same order, e.g. 'actual typealias Foo<A, B> = Bar<A, B>'");
|
MAP.put(ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION, "Type arguments in the right-hand side of actual type alias should be its type parameters in the same order, e.g. 'actual typealias Foo<A, B> = Bar<A, B>'");
|
||||||
|
|
||||||
MAP.put(NO_ACTUAL_FOR_EXPECT, "Expected {0} has no actual in module{1}{2}", DECLARATION_NAME_WITH_KIND,
|
MAP.put(NO_ACTUAL_FOR_EXPECT, "Expected {0} has no actual declaration in module{1}{2}", DECLARATION_NAME_WITH_KIND,
|
||||||
PLATFORM, PlatformIncompatibilityDiagnosticRenderer.TEXT);
|
PLATFORM, PlatformIncompatibilityDiagnosticRenderer.TEXT);
|
||||||
MAP.put(ACTUAL_WITHOUT_EXPECT, "Actual {0} has no corresponding expected declaration{1}", DECLARATION_NAME_WITH_KIND,
|
MAP.put(ACTUAL_WITHOUT_EXPECT, "Actual {0} has no corresponding expected declaration{1}", DECLARATION_NAME_WITH_KIND,
|
||||||
PlatformIncompatibilityDiagnosticRenderer.TEXT);
|
PlatformIncompatibilityDiagnosticRenderer.TEXT);
|
||||||
@@ -553,8 +556,8 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(MANY_CLASSES_IN_SUPERTYPE_LIST, "Only one class may appear in a supertype list");
|
MAP.put(MANY_CLASSES_IN_SUPERTYPE_LIST, "Only one class may appear in a supertype list");
|
||||||
MAP.put(SUPERTYPE_NOT_A_CLASS_OR_INTERFACE, "Only classes and interfaces may serve as supertypes");
|
MAP.put(SUPERTYPE_NOT_A_CLASS_OR_INTERFACE, "Only classes and interfaces may serve as supertypes");
|
||||||
MAP.put(SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE, "Extension function type is not allowed as supertypes");
|
MAP.put(SUPERTYPE_IS_EXTENSION_FUNCTION_TYPE, "Extension function type is not allowed as supertypes");
|
||||||
MAP.put(SUPERTYPE_IS_SUSPEND_FUNCTION_TYPE, "Suspend function type is not allowed as supertypes");
|
|
||||||
MAP.put(SUPERTYPE_INITIALIZED_IN_INTERFACE, "Interfaces cannot initialize supertypes");
|
MAP.put(SUPERTYPE_INITIALIZED_IN_INTERFACE, "Interfaces cannot initialize supertypes");
|
||||||
|
MAP.put(SUPERTYPE_IS_SUSPEND_FUNCTION_TYPE, "Suspend function type is not allowed as supertypes");
|
||||||
MAP.put(CLASS_IN_SUPERTYPE_FOR_ENUM, "Enum class cannot inherit from classes");
|
MAP.put(CLASS_IN_SUPERTYPE_FOR_ENUM, "Enum class cannot inherit from classes");
|
||||||
MAP.put(CONSTRUCTOR_IN_INTERFACE, "An interface may not have a constructor");
|
MAP.put(CONSTRUCTOR_IN_INTERFACE, "An interface may not have a constructor");
|
||||||
MAP.put(METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE, "An interface may not implement a method of 'Any'");
|
MAP.put(METHOD_OF_ANY_IMPLEMENTED_IN_INTERFACE, "An interface may not implement a method of 'Any'");
|
||||||
@@ -757,7 +760,8 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter ''{0}''", NAME);
|
MAP.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter ''{0}''", NAME);
|
||||||
MAP.put(MISSING_RECEIVER, "A receiver of type {0} is required", RENDER_TYPE);
|
MAP.put(MISSING_RECEIVER, "A receiver of type {0} is required", RENDER_TYPE);
|
||||||
MAP.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property");
|
MAP.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property");
|
||||||
MAP.put(ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM, "Assigning single elements to varargs in named form is deprecated");
|
MAP.put(ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION, "Assigning single elements to varargs in named form is deprecated", TO_STRING);
|
||||||
|
MAP.put(ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION, "Assigning single elements to varargs in named form is deprecated");
|
||||||
|
|
||||||
MAP.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class");
|
MAP.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class");
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -101,7 +101,7 @@ private fun StringBuilder.renderIncompatibilityInformation(
|
|||||||
|
|
||||||
if (incompatibility is Incompatible.ClassScopes) {
|
if (incompatibility is Incompatible.ClassScopes) {
|
||||||
append(indent)
|
append(indent)
|
||||||
append("No actuals are found for expected members listed below:")
|
append("No actual members are found for expected members listed below:")
|
||||||
mode.newLine(this)
|
mode.newLine(this)
|
||||||
renderIncompatibleClassScopes(incompatibility.unfulfilled, indent, context, mode)
|
renderIncompatibleClassScopes(incompatibility.unfulfilled, indent, context, mode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,8 +76,18 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
|
|||||||
return if (ast != null) ast.psi as KtPackageDirective else null
|
return if (ast != null) ast.psi as KtPackageDirective else null
|
||||||
}
|
}
|
||||||
|
|
||||||
val packageFqName: FqName
|
var packageFqName: FqName
|
||||||
get() = stub?.getPackageFqName() ?: packageFqNameByTree
|
get() = stub?.getPackageFqName() ?: packageFqNameByTree
|
||||||
|
set(value) {
|
||||||
|
val packageDirective = packageDirective
|
||||||
|
if (packageDirective != null) {
|
||||||
|
packageDirective.fqName = value
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val newPackageDirective = KtPsiFactory(this).createPackageDirectiveIfNeeded(value) ?: return
|
||||||
|
addAfter(newPackageDirective, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val packageFqNameByTree: FqName
|
val packageFqNameByTree: FqName
|
||||||
get() = packageDirectiveByTree?.fqName ?: FqName.ROOT
|
get() = packageDirectiveByTree?.fqName ?: FqName.ROOT
|
||||||
|
|||||||
@@ -64,7 +64,10 @@ abstract class KtNamedDeclarationNotStubbed extends KtDeclarationImpl implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
|
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
|
||||||
return getNameIdentifier().replace(KtPsiFactory(this).createNameIdentifier(name));
|
PsiElement identifier = getNameIdentifier();
|
||||||
|
if (identifier == null) throw new IncorrectOperationException();
|
||||||
|
|
||||||
|
return identifier.replace(KtPsiFactory(this).createNameIdentifier(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -530,11 +530,19 @@ public class KtPsiUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// '(x operator y)' case
|
if (innerExpression instanceof KtBinaryExpression) {
|
||||||
if (innerExpression instanceof KtBinaryExpression &&
|
// '(x operator return [...]) operator ...' case
|
||||||
innerOperation != KtTokens.ELVIS &&
|
if (parentElement instanceof KtBinaryExpression) {
|
||||||
isKeepBinaryExpressionParenthesized((KtBinaryExpression) innerExpression)) {
|
KtBinaryExpression innerBinary = (KtBinaryExpression) innerExpression;
|
||||||
return true;
|
if (innerBinary.getRight() instanceof KtReturnExpression) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// '(x operator y)' case
|
||||||
|
if (innerOperation != KtTokens.ELVIS &&
|
||||||
|
isKeepBinaryExpressionParenthesized((KtBinaryExpression) innerExpression)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int innerPriority = getPriority(innerExpression);
|
int innerPriority = getPriority(innerExpression);
|
||||||
@@ -846,7 +854,7 @@ public class KtPsiUtil {
|
|||||||
return (KtElement) current;
|
return (KtElement) current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (current instanceof KtDelegatedSuperTypeEntry) {
|
if (current instanceof KtDelegatedSuperTypeEntry || current instanceof KtSuperTypeCallEntry) {
|
||||||
PsiElement grandParent = current.getParent().getParent();
|
PsiElement grandParent = current.getParent().getParent();
|
||||||
if (grandParent instanceof KtClassOrObject && !(grandParent.getParent() instanceof KtObjectLiteralExpression)) {
|
if (grandParent instanceof KtClassOrObject && !(grandParent.getParent() instanceof KtObjectLiteralExpression)) {
|
||||||
return (KtElement) grandParent;
|
return (KtElement) grandParent;
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ fun KtExpression.getOutermostParenthesizerOrThis(): KtExpression {
|
|||||||
|
|
||||||
fun PsiElement.isFunctionalExpression(): Boolean = this is KtNamedFunction && nameIdentifier == null
|
fun PsiElement.isFunctionalExpression(): Boolean = this is KtNamedFunction && nameIdentifier == null
|
||||||
|
|
||||||
private val BAD_NEIGHBOUR_FOR_SIMPLE_TEMPLATE_ENTRY_PATTERN = Regex("[a-zA-Z0-9_].*")
|
private val BAD_NEIGHBOUR_FOR_SIMPLE_TEMPLATE_ENTRY_PATTERN = Regex("([a-zA-Z0-9_]|[^\\p{ASCII}]).*")
|
||||||
|
|
||||||
fun canPlaceAfterSimpleNameEntry(element: PsiElement?): Boolean {
|
fun canPlaceAfterSimpleNameEntry(element: PsiElement?): Boolean {
|
||||||
val entryText = element?.text ?: return true
|
val entryText = element?.text ?: return true
|
||||||
|
|||||||
+1
@@ -131,6 +131,7 @@ class SyntheticClassOrObjectDescriptor(
|
|||||||
override fun getDestructuringDeclarationsEntries(name: Name): Collection<KtDestructuringDeclarationEntry> = emptyList()
|
override fun getDestructuringDeclarationsEntries(name: Name): Collection<KtDestructuringDeclarationEntry> = emptyList()
|
||||||
override fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo> = emptyList()
|
override fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo> = emptyList()
|
||||||
override fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias> = emptyList()
|
override fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias> = emptyList()
|
||||||
|
override fun getDeclarationNames() = emptySet<Name>()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inner class SyntheticDeclaration(
|
internal inner class SyntheticDeclaration(
|
||||||
|
|||||||
@@ -22,15 +22,17 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.scopes.BaseImportingScope
|
import org.jetbrains.kotlin.resolve.scopes.BaseImportingScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.computeAllNames
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable
|
||||||
|
|
||||||
class AllUnderImportScope(
|
class AllUnderImportScope(
|
||||||
descriptor: DeclarationDescriptor,
|
descriptor: DeclarationDescriptor,
|
||||||
excludedImportNames: Collection<FqName>
|
excludedImportNames: Collection<FqName>
|
||||||
) : BaseImportingScope(null) {
|
) : BaseImportingScope(null) {
|
||||||
|
|
||||||
private val scopes: List<ResolutionScope> = if (descriptor is ClassDescriptor) {
|
private val scopes: List<MemberScope> = if (descriptor is ClassDescriptor) {
|
||||||
listOf(descriptor.staticScope, descriptor.unsubstitutedInnerClassesScope)
|
listOf(descriptor.staticScope, descriptor.unsubstitutedInnerClassesScope)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -49,6 +51,8 @@ class AllUnderImportScope(
|
|||||||
excludedImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }.toSet()
|
excludedImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun computeImportedNames(): Set<Name>? = scopes.flatMapToNullable(hashSetOf(), MemberScope::computeAllNames)
|
||||||
|
|
||||||
override fun getContributedDescriptors(
|
override fun getContributedDescriptors(
|
||||||
kindFilter: DescriptorKindFilter,
|
kindFilter: DescriptorKindFilter,
|
||||||
nameFilter: (Name) -> Boolean,
|
nameFilter: (Name) -> Boolean,
|
||||||
@@ -82,6 +86,10 @@ class AllUnderImportScope(
|
|||||||
return scopes.flatMap { it.getContributedFunctions(name, location) }
|
return scopes.flatMap { it.getContributedFunctions(name, location) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name, location: LookupLocation) {
|
||||||
|
scopes.forEach { it.recordLookup(name, location) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun printStructure(p: Printer) {
|
override fun printStructure(p: Printer) {
|
||||||
p.println(this::class.java.simpleName)
|
p.println(this::class.java.simpleName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,9 @@ public class BodyResolver {
|
|||||||
if (descriptor.getKind() == ClassKind.INTERFACE) {
|
if (descriptor.getKind() == ClassKind.INTERFACE) {
|
||||||
trace.report(SUPERTYPE_INITIALIZED_IN_INTERFACE.on(elementToMark));
|
trace.report(SUPERTYPE_INITIALIZED_IN_INTERFACE.on(elementToMark));
|
||||||
}
|
}
|
||||||
|
if (descriptor.isExpect()) {
|
||||||
|
trace.report(SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS.on(elementToMark));
|
||||||
|
}
|
||||||
KtTypeReference typeReference = call.getTypeReference();
|
KtTypeReference typeReference = call.getTypeReference();
|
||||||
if (typeReference == null) return;
|
if (typeReference == null) return;
|
||||||
if (primaryConstructor == null) {
|
if (primaryConstructor == null) {
|
||||||
|
|||||||
@@ -646,6 +646,9 @@ class DeclarationsChecker(
|
|||||||
if (inInterface) {
|
if (inInterface) {
|
||||||
trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate))
|
trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate))
|
||||||
}
|
}
|
||||||
|
else if (isExpect) {
|
||||||
|
trace.report(EXPECTED_DELEGATED_PROPERTY.on(delegate))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
|
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
|
||||||
@@ -667,9 +670,14 @@ class DeclarationsChecker(
|
|||||||
else if (noExplicitTypeOrGetterType(property)) {
|
else if (noExplicitTypeOrGetterType(property)) {
|
||||||
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
||||||
}
|
}
|
||||||
if (backingFieldRequired && !inInterface && propertyDescriptor.isLateInit && !isUninitialized &&
|
|
||||||
trace[MUST_BE_LATEINIT, propertyDescriptor] != true) {
|
if (propertyDescriptor.isLateInit) {
|
||||||
trace.report(UNNECESSARY_LATEINIT.on(property))
|
if (propertyDescriptor.isExpect) {
|
||||||
|
trace.report(EXPECTED_LATEINIT_PROPERTY.on(property.modifierList?.getModifier(KtTokens.LATEINIT_KEYWORD) ?: property))
|
||||||
|
}
|
||||||
|
if (backingFieldRequired && !inInterface && !isUninitialized && trace[MUST_BE_LATEINIT, propertyDescriptor] != true) {
|
||||||
|
trace.report(UNNECESSARY_LATEINIT.on(property))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ class LazyExplicitImportScope(
|
|||||||
return descriptors
|
return descriptors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun computeImportedNames() = setOf(aliasName)
|
||||||
|
|
||||||
override fun printStructure(p: Printer) {
|
override fun printStructure(p: Printer) {
|
||||||
p.println(this::class.java.simpleName, ": ", aliasName)
|
p.println(this::class.java.simpleName, ": ", aliasName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,9 +86,9 @@ object ModifierCheckerCore {
|
|||||||
CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY),
|
CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY),
|
||||||
OPERATOR_KEYWORD to EnumSet.of(FUNCTION),
|
OPERATOR_KEYWORD to EnumSet.of(FUNCTION),
|
||||||
INFIX_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),
|
HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, 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),
|
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),
|
||||||
EXPECT_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS),
|
EXPECT_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS),
|
||||||
ACTUAL_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, MEMBER_FUNCTION, TOP_LEVEL_PROPERTY, MEMBER_PROPERTY, CONSTRUCTOR, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, TYPEALIAS)
|
ACTUAL_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, MEMBER_FUNCTION, TOP_LEVEL_PROPERTY, MEMBER_PROPERTY, CONSTRUCTOR, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS, TYPEALIAS)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -191,9 +191,6 @@ object ModifierCheckerCore {
|
|||||||
// (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md)
|
// (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md)
|
||||||
result += incompatibilityRegister(SEALED_KEYWORD, INNER_KEYWORD)
|
result += incompatibilityRegister(SEALED_KEYWORD, INNER_KEYWORD)
|
||||||
|
|
||||||
// lateinit is incompatible with header / expect
|
|
||||||
result += incompatibilityRegister(LATEINIT_KEYWORD, HEADER_KEYWORD, EXPECT_KEYWORD)
|
|
||||||
|
|
||||||
// header / expect / impl / actual are all incompatible
|
// header / expect / impl / actual are all incompatible
|
||||||
result += incompatibilityRegister(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD, ACTUAL_KEYWORD)
|
result += incompatibilityRegister(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD, ACTUAL_KEYWORD)
|
||||||
|
|
||||||
|
|||||||
+6
-5
@@ -53,7 +53,7 @@ class AssigningNamedArgumentToVarargChecker : CallChecker {
|
|||||||
checkAssignmentOfSingleElementInAnnotation(argument, argumentExpression, context)
|
checkAssignmentOfSingleElementInAnnotation(argument, argumentExpression, context)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context)
|
checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context, parameterDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,21 +64,22 @@ class AssigningNamedArgumentToVarargChecker : CallChecker {
|
|||||||
) {
|
) {
|
||||||
if (isArrayOrArrayLiteral(argument, context)) {
|
if (isArrayOrArrayLiteral(argument, context)) {
|
||||||
if (argument.hasSpread()) {
|
if (argument.hasSpread()) {
|
||||||
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression))
|
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION.on(argumentExpression))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression))
|
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION.on(argumentExpression))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAssignmentOfSingleElementInFunction(
|
private fun checkAssignmentOfSingleElementInFunction(
|
||||||
argument: ValueArgument,
|
argument: ValueArgument,
|
||||||
argumentExpression: KtExpression,
|
argumentExpression: KtExpression,
|
||||||
context: ResolutionContext<*>
|
context: ResolutionContext<*>,
|
||||||
|
parameterDescriptor: ValueParameterDescriptor
|
||||||
) {
|
) {
|
||||||
if (!argument.hasSpread()) {
|
if (!argument.hasSpread()) {
|
||||||
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression))
|
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION.on(argumentExpression, parameterDescriptor.type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -168,10 +168,10 @@ class NewResolutionOldInference(
|
|||||||
val processor = kind.createTowerProcessor(this, nameToResolve, tracing, scopeTower, detailedReceiver, context)
|
val processor = kind.createTowerProcessor(this, nameToResolve, tracing, scopeTower, detailedReceiver, context)
|
||||||
|
|
||||||
if (context.collectAllCandidates) {
|
if (context.collectAllCandidates) {
|
||||||
return allCandidatesResult(towerResolver.collectAllCandidates(scopeTower, processor))
|
return allCandidatesResult(towerResolver.collectAllCandidates(scopeTower, processor, nameToResolve))
|
||||||
}
|
}
|
||||||
|
|
||||||
var candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference)
|
var candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kind != ResolutionKind.CallableReference, name = nameToResolve)
|
||||||
|
|
||||||
// Temporary hack to resolve 'rem' as 'mod' if the first is do not present
|
// Temporary hack to resolve 'rem' as 'mod' if the first is do not present
|
||||||
val emptyOrInapplicableCandidates = candidates.isEmpty() ||
|
val emptyOrInapplicableCandidates = candidates.isEmpty() ||
|
||||||
@@ -179,7 +179,7 @@ class NewResolutionOldInference(
|
|||||||
if (isBinaryRemOperator && shouldUseOperatorRem && emptyOrInapplicableCandidates) {
|
if (isBinaryRemOperator && shouldUseOperatorRem && emptyOrInapplicableCandidates) {
|
||||||
val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[name]
|
val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[name]
|
||||||
val processorForDeprecatedName = kind.createTowerProcessor(this, deprecatedName!!, tracing, scopeTower, detailedReceiver, context)
|
val processorForDeprecatedName = kind.createTowerProcessor(this, deprecatedName!!, tracing, scopeTower, detailedReceiver, context)
|
||||||
candidates = towerResolver.runResolve(scopeTower, processorForDeprecatedName, useOrder = kind != ResolutionKind.CallableReference)
|
candidates = towerResolver.runResolve(scopeTower, processorForDeprecatedName, useOrder = kind != ResolutionKind.CallableReference, name = deprecatedName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (candidates.isEmpty()) {
|
if (candidates.isEmpty()) {
|
||||||
|
|||||||
+24
-3
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2016 JetBrains s.r.o.
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -131,7 +131,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
|||||||
if (!hasExpectedModifier) {
|
if (!hasExpectedModifier) {
|
||||||
if (Compatible !in compatibility) return
|
if (Compatible !in compatibility) return
|
||||||
|
|
||||||
if (checkExpected) {
|
// we suppress error, because annotation classes can only have one constructor and it's a 100% boilerplate
|
||||||
|
// to require every annotation constructor with additional parameters with default values be marked with the `actual` modifier
|
||||||
|
if (checkExpected && !descriptor.isAnnotationConstructor()) {
|
||||||
diagnosticHolder.report(Errors.ACTUAL_MISSING.on(reportOn))
|
diagnosticHolder.report(Errors.ACTUAL_MISSING.on(reportOn))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,7 +364,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
|||||||
|
|
||||||
val aParams = a.valueParameters
|
val aParams = a.valueParameters
|
||||||
val bParams = b.valueParameters
|
val bParams = b.valueParameters
|
||||||
if (aParams.size != bParams.size) return Incompatible.ParameterCount
|
if (!valueParametersCountCompatible(a, b, aParams, bParams)) {
|
||||||
|
return Incompatible.ParameterCount
|
||||||
|
}
|
||||||
|
|
||||||
val aTypeParams = a.typeParameters
|
val aTypeParams = a.typeParameters
|
||||||
val bTypeParams = b.typeParameters
|
val bTypeParams = b.typeParameters
|
||||||
@@ -401,6 +405,23 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
|||||||
return Compatible
|
return Compatible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun valueParametersCountCompatible(
|
||||||
|
a: CallableMemberDescriptor,
|
||||||
|
b: CallableMemberDescriptor,
|
||||||
|
aParams: List<ValueParameterDescriptor>,
|
||||||
|
bParams: List<ValueParameterDescriptor>
|
||||||
|
): Boolean {
|
||||||
|
if (aParams.size == bParams.size) return true
|
||||||
|
|
||||||
|
return if (a.isAnnotationConstructor() && b.isAnnotationConstructor())
|
||||||
|
aParams.isEmpty() && bParams.all { it.declaresDefaultValue() }
|
||||||
|
else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MemberDescriptor.isAnnotationConstructor(): Boolean =
|
||||||
|
this is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(this.constructedClass)
|
||||||
|
|
||||||
private fun areCompatibleTypes(a: KotlinType?, b: KotlinType?, platformModule: ModuleDescriptor): Boolean {
|
private fun areCompatibleTypes(a: KotlinType?, b: KotlinType?, platformModule: ModuleDescriptor): Boolean {
|
||||||
if (a == null) return b == null
|
if (a == null) return b == null
|
||||||
if (b == null) return false
|
if (b == null) return false
|
||||||
|
|||||||
@@ -27,10 +27,7 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.psi.KtImportDirective
|
import org.jetbrains.kotlin.psi.KtImportDirective
|
||||||
import org.jetbrains.kotlin.psi.KtImportsFactory
|
import org.jetbrains.kotlin.psi.KtImportsFactory
|
||||||
import org.jetbrains.kotlin.resolve.*
|
import org.jetbrains.kotlin.resolve.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
import org.jetbrains.kotlin.resolve.scopes.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.SubpackagesImportingScope
|
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||||
import org.jetbrains.kotlin.script.getScriptExternalDependencies
|
import org.jetbrains.kotlin.script.getScriptExternalDependencies
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
@@ -68,6 +65,41 @@ class FileScopeFactory(
|
|||||||
return FilesScopesBuilder(file, existingImports, packageFragment, packageView).result
|
return FilesScopesBuilder(file, existingImports, packageFragment, packageView).result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createDefaultImportResolvers(extraImports: Collection<KtImportDirective>, aliasImportNames: Collection<FqName>): Pair<LazyImportResolver, LazyImportResolver> {
|
||||||
|
val tempTrace = TemporaryBindingTrace.create(bindingTrace, "Transient trace for default imports lazy resolve", false)
|
||||||
|
val allImplicitImports = defaultImports concat extraImports
|
||||||
|
|
||||||
|
val defaultImportsFiltered = if (aliasImportNames.isEmpty()) { // optimization
|
||||||
|
allImplicitImports
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
allImplicitImports.filter { it.isAllUnder || it.importedFqName !in aliasImportNames }
|
||||||
|
}
|
||||||
|
|
||||||
|
val defaultExplicitImportResolver = createImportResolver(ExplicitImportsIndexed(defaultImportsFiltered), tempTrace, packageFragment = null, aliasImportNames = aliasImportNames)
|
||||||
|
val defaultAllUnderImportResolver =
|
||||||
|
createImportResolver(AllUnderImportsIndexed(defaultImportsFiltered), tempTrace, packageFragment = null, aliasImportNames = aliasImportNames, excludedImports = defaultImportProvider.excludedImports)
|
||||||
|
|
||||||
|
return defaultExplicitImportResolver to defaultAllUnderImportResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
private val defaultImportResolvers by storageManager.createLazyValue {
|
||||||
|
createDefaultImportResolvers(emptyList(), emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createImportResolver(
|
||||||
|
indexedImports: IndexedImports,
|
||||||
|
trace: BindingTrace,
|
||||||
|
aliasImportNames: Collection<FqName>,
|
||||||
|
packageFragment: PackageFragmentDescriptor?,
|
||||||
|
excludedImports: List<FqName>? = null
|
||||||
|
) = LazyImportResolver(
|
||||||
|
storageManager, qualifiedExpressionResolver, moduleDescriptor, platformToKotlinClassMap, languageVersionSettings,
|
||||||
|
indexedImports, aliasImportNames concat excludedImports, trace, packageFragment,
|
||||||
|
deprecationResolver
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
private inner class FilesScopesBuilder(
|
private inner class FilesScopesBuilder(
|
||||||
private val file: KtFile,
|
private val file: KtFile,
|
||||||
private val existingImports: ImportingScope?,
|
private val existingImports: ImportingScope?,
|
||||||
@@ -77,8 +109,8 @@ class FileScopeFactory(
|
|||||||
val imports = file.importDirectives
|
val imports = file.importDirectives
|
||||||
val aliasImportNames = imports.mapNotNull { if (it.aliasName != null) it.importedFqName else null }
|
val aliasImportNames = imports.mapNotNull { if (it.aliasName != null) it.importedFqName else null }
|
||||||
|
|
||||||
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace)
|
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace, aliasImportNames, packageFragment)
|
||||||
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace) // TODO: should we count excludedImports here also?
|
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace, aliasImportNames, packageFragment) // TODO: should we count excludedImports here also?
|
||||||
|
|
||||||
val lazyImportingScope = object : ImportingScope by ImportingScope.Empty {
|
val lazyImportingScope = object : ImportingScope by ImportingScope.Empty {
|
||||||
// avoid constructing the scope before we query it
|
// avoid constructing the scope before we query it
|
||||||
@@ -107,33 +139,21 @@ class FileScopeFactory(
|
|||||||
|
|
||||||
val result = FileScopes(lexicalScope, lazyImportingScope, importResolver)
|
val result = FileScopes(lexicalScope, lazyImportingScope, importResolver)
|
||||||
|
|
||||||
fun createImportResolver(indexedImports: IndexedImports, trace: BindingTrace, excludedImports: List<FqName>? = null) =
|
private fun createDefaultImportResolversForFile(): Pair<LazyImportResolver, LazyImportResolver> {
|
||||||
LazyImportResolver(
|
|
||||||
storageManager, qualifiedExpressionResolver, moduleDescriptor, platformToKotlinClassMap, languageVersionSettings,
|
|
||||||
indexedImports, aliasImportNames concat excludedImports, trace, packageFragment,
|
|
||||||
deprecationResolver
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
fun createImportingScope(): LazyImportScope {
|
|
||||||
val tempTrace = TemporaryBindingTrace.create(bindingTrace, "Transient trace for default imports lazy resolve", false)
|
|
||||||
|
|
||||||
val extraImports = file.originalFile.virtualFile?.let { vFile ->
|
val extraImports = file.originalFile.virtualFile?.let { vFile ->
|
||||||
val scriptExternalDependencies = getScriptExternalDependencies(vFile, file.project)
|
val scriptExternalDependencies = getScriptExternalDependencies(vFile, file.project)
|
||||||
ktImportsFactory.createImportDirectives(scriptExternalDependencies?.imports?.map { ImportPath.fromString(it) }.orEmpty())
|
ktImportsFactory.createImportDirectives(scriptExternalDependencies?.imports?.map { ImportPath.fromString(it) }.orEmpty())
|
||||||
|
}.orEmpty()
|
||||||
|
|
||||||
|
if (extraImports.isEmpty() && aliasImportNames.isEmpty()) {
|
||||||
|
return defaultImportResolvers
|
||||||
}
|
}
|
||||||
|
|
||||||
val allImplicitImports = defaultImports concat extraImports
|
return createDefaultImportResolvers(extraImports, aliasImportNames)
|
||||||
|
}
|
||||||
|
|
||||||
val defaultImportsFiltered = if (aliasImportNames.isEmpty()) { // optimization
|
fun createImportingScope(): LazyImportScope {
|
||||||
allImplicitImports
|
val (defaultExplicitImportResolver, defaultAllUnderImportResolver) = createDefaultImportResolversForFile()
|
||||||
}
|
|
||||||
else {
|
|
||||||
allImplicitImports.filter { it.isAllUnder || it.importedFqName !in aliasImportNames }
|
|
||||||
}
|
|
||||||
|
|
||||||
val defaultExplicitImportResolver = createImportResolver(ExplicitImportsIndexed(defaultImportsFiltered), tempTrace)
|
|
||||||
val defaultAllUnderImportResolver = createImportResolver(AllUnderImportsIndexed(defaultImportsFiltered), tempTrace, defaultImportProvider.excludedImports)
|
|
||||||
|
|
||||||
val dummyContainerDescriptor = DummyContainerDescriptor(file, packageFragment)
|
val dummyContainerDescriptor = DummyContainerDescriptor(file, packageFragment)
|
||||||
|
|
||||||
@@ -164,8 +184,6 @@ class FileScopeFactory(
|
|||||||
return LazyImportScope(scope, explicitImportResolver, LazyImportScope.FilteringKind.ALL, "Explicit imports in $debugName")
|
return LazyImportScope(scope, explicitImportResolver, LazyImportScope.FilteringKind.ALL, "Explicit imports in $debugName")
|
||||||
}
|
}
|
||||||
|
|
||||||
private infix fun <T> Collection<T>.concat(other: Collection<T>?) =
|
|
||||||
if (other == null || other.isEmpty()) this else this + other
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class FilteringKind {
|
private enum class FilteringKind {
|
||||||
@@ -180,6 +198,7 @@ class FileScopeFactory(
|
|||||||
parentScope: ImportingScope
|
parentScope: ImportingScope
|
||||||
): ImportingScope {
|
): ImportingScope {
|
||||||
val scope = packageView.memberScope
|
val scope = packageView.memberScope
|
||||||
|
val names by lazy(LazyThreadSafetyMode.PUBLICATION) { scope.computeAllNames () }
|
||||||
val packageName = packageView.fqName
|
val packageName = packageView.fqName
|
||||||
val excludedNames = aliasImportNames.mapNotNull { if (it.parent() == packageName) it.shortName() else null }
|
val excludedNames = aliasImportNames.mapNotNull { if (it.parent() == packageName) it.shortName() else null }
|
||||||
|
|
||||||
@@ -220,6 +239,10 @@ class FileScopeFactory(
|
|||||||
).filter { it !is PackageViewDescriptor } // subpackages of the current package not accessible by the short name
|
).filter { it !is PackageViewDescriptor } // subpackages of the current package not accessible by the short name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun computeImportedNames() = packageView.memberScope.computeAllNames()
|
||||||
|
|
||||||
|
override fun definitelyDoesNotContainName(name: Name) = names?.let { name !in it } == true
|
||||||
|
|
||||||
override fun toString() = "Scope for current package (${filteringKind.name})"
|
override fun toString() = "Scope for current package (${filteringKind.name})"
|
||||||
|
|
||||||
override fun printStructure(p: Printer) {
|
override fun printStructure(p: Printer) {
|
||||||
@@ -252,3 +275,6 @@ class FileScopeFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private infix fun <T> Collection<T>.concat(other: Collection<T>?) =
|
||||||
|
if (other == null || other.isEmpty()) this else this + other
|
||||||
|
|||||||
@@ -1,308 +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.resolve.lazy;
|
|
||||||
|
|
||||||
import com.intellij.psi.PsiElement;
|
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.kotlin.context.GlobalContext;
|
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
|
||||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation;
|
|
||||||
import org.jetbrains.kotlin.incremental.components.LookupLocation;
|
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
|
||||||
import org.jetbrains.kotlin.name.Name;
|
|
||||||
import org.jetbrains.kotlin.psi.*;
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.AbstractLazyMemberScope;
|
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor;
|
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
|
||||||
import org.jetbrains.kotlin.storage.LockBasedLazyResolveStorageManager;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class LazyDeclarationResolver {
|
|
||||||
|
|
||||||
@NotNull private final TopLevelDescriptorProvider topLevelDescriptorProvider;
|
|
||||||
@NotNull private final AbsentDescriptorHandler absentDescriptorHandler;
|
|
||||||
@NotNull private final BindingTrace trace;
|
|
||||||
|
|
||||||
protected DeclarationScopeProvider scopeProvider;
|
|
||||||
|
|
||||||
// component dependency cycle
|
|
||||||
@Inject
|
|
||||||
public void setDeclarationScopeProvider(@NotNull DeclarationScopeProviderImpl scopeProvider) {
|
|
||||||
this.scopeProvider = scopeProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public LazyDeclarationResolver(
|
|
||||||
@NotNull GlobalContext globalContext,
|
|
||||||
@NotNull BindingTrace delegationTrace,
|
|
||||||
@NotNull TopLevelDescriptorProvider topLevelDescriptorProvider,
|
|
||||||
@NotNull AbsentDescriptorHandler absentDescriptorHandler
|
|
||||||
) {
|
|
||||||
this.topLevelDescriptorProvider = topLevelDescriptorProvider;
|
|
||||||
this.absentDescriptorHandler = absentDescriptorHandler;
|
|
||||||
LockBasedLazyResolveStorageManager lockBasedLazyResolveStorageManager =
|
|
||||||
new LockBasedLazyResolveStorageManager(globalContext.getStorageManager());
|
|
||||||
|
|
||||||
this.trace = lockBasedLazyResolveStorageManager.createSafeTrace(delegationTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ClassDescriptor getClassDescriptor(@NotNull KtClassOrObject classOrObject, @NotNull LookupLocation location) {
|
|
||||||
return findClassDescriptor(classOrObject, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ScriptDescriptor getScriptDescriptor(@NotNull KtScript script, @NotNull LookupLocation location) {
|
|
||||||
return (ScriptDescriptor) findClassDescriptor(script, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private ClassDescriptor findClassDescriptor(
|
|
||||||
@NotNull KtNamedDeclaration classObjectOrScript,
|
|
||||||
@NotNull LookupLocation location
|
|
||||||
) {
|
|
||||||
MemberScope scope = getMemberScopeDeclaredIn(classObjectOrScript, location);
|
|
||||||
|
|
||||||
// Why not use the result here. Because it may be that there is a redeclaration:
|
|
||||||
// class A {} class A { fun foo(): A<completion here>}
|
|
||||||
// and if we find the class by name only, we may b-not get the right one.
|
|
||||||
// This call is only needed to make sure the classes are written to trace
|
|
||||||
ClassifierDescriptor scopeDescriptor = scope.getContributedClassifier(classObjectOrScript.getNameAsSafeName(), location);
|
|
||||||
DeclarationDescriptor descriptor = getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, classObjectOrScript);
|
|
||||||
|
|
||||||
if (descriptor == null) {
|
|
||||||
String providerInfoString = null;
|
|
||||||
if (scope instanceof AbstractLazyMemberScope) {
|
|
||||||
AbstractLazyMemberScope lazyMemberScope = (AbstractLazyMemberScope) scope;
|
|
||||||
providerInfoString = lazyMemberScope.toProviderString();
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
String.format(
|
|
||||||
"Could not find a classifier for %s.\n" +
|
|
||||||
"Found descriptor: %s (%s).\n" +
|
|
||||||
"Scope: %s.\n" +
|
|
||||||
"Provider: %s.",
|
|
||||||
PsiUtilsKt.getElementTextWithContext(classObjectOrScript),
|
|
||||||
scopeDescriptor != null ? DescriptorRenderer.DEBUG_TEXT.render(scopeDescriptor) : "null",
|
|
||||||
scopeDescriptor != null ? (scopeDescriptor.getContainingDeclaration().getClass()) : null,
|
|
||||||
scope,
|
|
||||||
providerInfoString != null ? providerInfoString : "null"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ClassDescriptor) descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private BindingContext getBindingContext() {
|
|
||||||
return trace.getBindingContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration) {
|
|
||||||
return resolveToDescriptor(declaration, /*track =*/true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private DeclarationDescriptor resolveToDescriptor(@NotNull KtDeclaration declaration, boolean track) {
|
|
||||||
DeclarationDescriptor result = declaration.accept(new KtVisitor<DeclarationDescriptor, Void>() {
|
|
||||||
@NotNull
|
|
||||||
private LookupLocation lookupLocationFor(@NotNull KtDeclaration declaration, boolean isTopLevel) {
|
|
||||||
return isTopLevel && track ? new KotlinLookupLocation(declaration) : NoLookupLocation.WHEN_RESOLVE_DECLARATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitClass(@NotNull KtClass klass, Void data) {
|
|
||||||
return getClassDescriptor(klass, lookupLocationFor(klass, klass.isTopLevel()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitObjectDeclaration(@NotNull KtObjectDeclaration declaration, Void data) {
|
|
||||||
return getClassDescriptor(declaration, lookupLocationFor(declaration, declaration.isTopLevel()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitTypeParameter(@NotNull KtTypeParameter parameter, Void data) {
|
|
||||||
KtTypeParameterListOwner ownerElement = PsiTreeUtil.getParentOfType(parameter, KtTypeParameterListOwner.class);
|
|
||||||
assert ownerElement != null : "Owner not found for type parameter: " + parameter.getText();
|
|
||||||
DeclarationDescriptor ownerDescriptor = resolveToDescriptor(ownerElement, /*track =*/false);
|
|
||||||
|
|
||||||
List<TypeParameterDescriptor> typeParameters;
|
|
||||||
if (ownerDescriptor instanceof CallableDescriptor) {
|
|
||||||
CallableDescriptor callableDescriptor = (CallableDescriptor) ownerDescriptor;
|
|
||||||
typeParameters = callableDescriptor.getTypeParameters();
|
|
||||||
}
|
|
||||||
else if (ownerDescriptor instanceof ClassifierDescriptorWithTypeParameters) {
|
|
||||||
ClassifierDescriptorWithTypeParameters classifierDescriptor = (ClassifierDescriptorWithTypeParameters) ownerDescriptor;
|
|
||||||
typeParameters = classifierDescriptor.getTypeConstructor().getParameters();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException("Unknown owner kind for a type parameter: " + ownerDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Name name = parameter.getNameAsSafeName();
|
|
||||||
for (TypeParameterDescriptor typeParameterDescriptor : typeParameters) {
|
|
||||||
if (typeParameterDescriptor.getName().equals(name)) {
|
|
||||||
return typeParameterDescriptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException("Type parameter " + name + " not found for " + ownerDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitNamedFunction(@NotNull KtNamedFunction function, Void data) {
|
|
||||||
LookupLocation location = lookupLocationFor(function, function.isTopLevel());
|
|
||||||
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(function, location);
|
|
||||||
scopeForDeclaration.getContributedFunctions(function.getNameAsSafeName(), location);
|
|
||||||
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, function);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitParameter(@NotNull KtParameter parameter, Void data) {
|
|
||||||
PsiElement grandFather = parameter.getParent().getParent();
|
|
||||||
if (grandFather instanceof KtPrimaryConstructor) {
|
|
||||||
KtClassOrObject jetClass = ((KtPrimaryConstructor) grandFather).getContainingClassOrObject();
|
|
||||||
// This is a primary constructor parameter
|
|
||||||
ClassDescriptor classDescriptor = getClassDescriptor(jetClass, lookupLocationFor(jetClass, false));
|
|
||||||
if (parameter.hasValOrVar()) {
|
|
||||||
classDescriptor.getDefaultType().getMemberScope().getContributedVariables(parameter.getNameAsSafeName(), lookupLocationFor(parameter, false));
|
|
||||||
return getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ConstructorDescriptor constructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
|
|
||||||
assert constructor != null: "There are constructor parameters found, so a constructor should also exist";
|
|
||||||
constructor.getValueParameters();
|
|
||||||
return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (grandFather instanceof KtNamedFunction) {
|
|
||||||
FunctionDescriptor function = (FunctionDescriptor) visitNamedFunction((KtNamedFunction) grandFather, data);
|
|
||||||
function.getValueParameters();
|
|
||||||
return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
|
|
||||||
}
|
|
||||||
else if (grandFather instanceof KtSecondaryConstructor) {
|
|
||||||
ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) visitSecondaryConstructor(
|
|
||||||
(KtSecondaryConstructor) grandFather, data
|
|
||||||
);
|
|
||||||
constructorDescriptor.getValueParameters();
|
|
||||||
return getBindingContext().get(BindingContext.VALUE_PARAMETER, parameter);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//TODO: support parameters in accessors and other places(?)
|
|
||||||
return super.visitParameter(parameter, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitSecondaryConstructor(@NotNull KtSecondaryConstructor constructor, Void data) {
|
|
||||||
getClassDescriptor((KtClassOrObject) constructor.getParent().getParent(), lookupLocationFor(constructor, false)).getConstructors();
|
|
||||||
return getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitPrimaryConstructor(@NotNull KtPrimaryConstructor constructor, Void data) {
|
|
||||||
getClassDescriptor(constructor.getContainingClassOrObject(), lookupLocationFor(constructor, false)).getConstructors();
|
|
||||||
return getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitProperty(@NotNull KtProperty property, Void data) {
|
|
||||||
LookupLocation location = lookupLocationFor(property, property.isTopLevel());
|
|
||||||
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(property, location);
|
|
||||||
scopeForDeclaration.getContributedVariables(property.getNameAsSafeName(), location);
|
|
||||||
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, property);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitDestructuringDeclarationEntry(
|
|
||||||
@NotNull KtDestructuringDeclarationEntry destructuringDeclarationEntry, Void data
|
|
||||||
) {
|
|
||||||
LookupLocation location = lookupLocationFor(destructuringDeclarationEntry, false);
|
|
||||||
KtDestructuringDeclaration destructuringDeclaration = ((KtDestructuringDeclaration) destructuringDeclarationEntry.getParent());
|
|
||||||
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(destructuringDeclaration, location);
|
|
||||||
scopeForDeclaration.getContributedVariables(destructuringDeclarationEntry.getNameAsSafeName(), location);
|
|
||||||
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, destructuringDeclarationEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitTypeAlias(@NotNull KtTypeAlias typeAlias, Void data) {
|
|
||||||
LookupLocation location = lookupLocationFor(typeAlias, typeAlias.isTopLevel());
|
|
||||||
MemberScope scopeForDeclaration = getMemberScopeDeclaredIn(typeAlias, location);
|
|
||||||
scopeForDeclaration.getContributedClassifier(typeAlias.getNameAsSafeName(), location);
|
|
||||||
return getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, typeAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitScript(@NotNull KtScript script, Void data) {
|
|
||||||
return getScriptDescriptor(script, lookupLocationFor(script, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DeclarationDescriptor visitKtElement(@NotNull KtElement element, Void data) {
|
|
||||||
throw new IllegalArgumentException("Unsupported declaration type: " + element + " " +
|
|
||||||
PsiUtilsKt.getElementTextWithContext(element));
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
if (result == null) {
|
|
||||||
return absentDescriptorHandler.diagnoseDescriptorNotFound(declaration);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
/*package*/ MemberScope getMemberScopeDeclaredIn(@NotNull KtDeclaration declaration, @NotNull LookupLocation location) {
|
|
||||||
KtDeclaration parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration);
|
|
||||||
boolean isTopLevel = parentDeclaration == null;
|
|
||||||
if (isTopLevel) { // for top level declarations we search directly in package because of possible conflicts with imports
|
|
||||||
KtFile ktFile = (KtFile) declaration.getContainingFile();
|
|
||||||
FqName fqName = ktFile.getPackageFqName();
|
|
||||||
topLevelDescriptorProvider.assertValid();
|
|
||||||
LazyPackageDescriptor packageDescriptor = topLevelDescriptorProvider.getPackageFragment(fqName);
|
|
||||||
if (packageDescriptor == null) {
|
|
||||||
if (topLevelDescriptorProvider instanceof LazyClassContext) {
|
|
||||||
((LazyClassContext) topLevelDescriptorProvider).getDeclarationProviderFactory().diagnoseMissingPackageFragment(ktFile);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException("Cannot find package fragment for file " + ktFile.getName() + " with package " + fqName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packageDescriptor.getMemberScope();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (parentDeclaration instanceof KtClassOrObject) {
|
|
||||||
return getClassDescriptor((KtClassOrObject) parentDeclaration, location).getUnsubstitutedMemberScope();
|
|
||||||
}
|
|
||||||
else if (parentDeclaration instanceof KtScript) {
|
|
||||||
return getScriptDescriptor((KtScript) parentDeclaration, location).getUnsubstitutedMemberScope();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException("Don't call this method for local declarations: " + declaration + "\n" +
|
|
||||||
PsiUtilsKt.getElementTextWithContext(declaration));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* 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.resolve.lazy
|
||||||
|
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import org.jetbrains.kotlin.context.GlobalContext
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
||||||
|
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
|
import org.jetbrains.kotlin.storage.LockBasedLazyResolveStorageManager
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
open class LazyDeclarationResolver @Deprecated("") constructor(
|
||||||
|
globalContext: GlobalContext,
|
||||||
|
delegationTrace: BindingTrace,
|
||||||
|
private val topLevelDescriptorProvider: TopLevelDescriptorProvider,
|
||||||
|
private val absentDescriptorHandler: AbsentDescriptorHandler
|
||||||
|
) {
|
||||||
|
private val trace: BindingTrace
|
||||||
|
|
||||||
|
protected lateinit var scopeProvider: DeclarationScopeProvider
|
||||||
|
|
||||||
|
private val bindingContext: BindingContext
|
||||||
|
get() = trace.bindingContext
|
||||||
|
|
||||||
|
// component dependency cycle
|
||||||
|
@Inject
|
||||||
|
fun setDeclarationScopeProvider(scopeProvider: DeclarationScopeProviderImpl) {
|
||||||
|
this.scopeProvider = scopeProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val lockBasedLazyResolveStorageManager = LockBasedLazyResolveStorageManager(globalContext.storageManager)
|
||||||
|
|
||||||
|
this.trace = lockBasedLazyResolveStorageManager.createSafeTrace(delegationTrace)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getClassDescriptorIfAny(classOrObject: KtClassOrObject, location: LookupLocation): ClassDescriptor? =
|
||||||
|
findClassDescriptorIfAny(classOrObject, location)
|
||||||
|
|
||||||
|
open fun getClassDescriptor(classOrObject: KtClassOrObject, location: LookupLocation): ClassDescriptor =
|
||||||
|
findClassDescriptor(classOrObject, location)
|
||||||
|
|
||||||
|
fun getScriptDescriptor(script: KtScript, location: LookupLocation): ScriptDescriptor =
|
||||||
|
findClassDescriptor(script, location) as ScriptDescriptor
|
||||||
|
|
||||||
|
private fun findClassDescriptorIfAny(
|
||||||
|
classObjectOrScript: KtNamedDeclaration,
|
||||||
|
location: LookupLocation
|
||||||
|
): ClassDescriptor? {
|
||||||
|
val scope = getMemberScopeDeclaredIn(classObjectOrScript, location)
|
||||||
|
|
||||||
|
// Why not use the result here. Because it may be that there is a redeclaration:
|
||||||
|
// class A {} class A { fun foo(): A<completion here>}
|
||||||
|
// and if we find the class by name only, we may b-not get the right one.
|
||||||
|
// This call is only needed to make sure the classes are written to trace
|
||||||
|
scope.getContributedClassifier(classObjectOrScript.nameAsSafeName, location)
|
||||||
|
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classObjectOrScript)
|
||||||
|
|
||||||
|
return descriptor as? ClassDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findClassDescriptor(
|
||||||
|
classObjectOrScript: KtNamedDeclaration,
|
||||||
|
location: LookupLocation
|
||||||
|
): ClassDescriptor =
|
||||||
|
findClassDescriptorIfAny(classObjectOrScript, location)
|
||||||
|
?: (absentDescriptorHandler.diagnoseDescriptorNotFound(classObjectOrScript) as ClassDescriptor)
|
||||||
|
|
||||||
|
fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor =
|
||||||
|
resolveToDescriptor(declaration, /*track =*/true) ?: absentDescriptorHandler.diagnoseDescriptorNotFound(declaration)
|
||||||
|
|
||||||
|
private fun resolveToDescriptor(declaration: KtDeclaration, track: Boolean): DeclarationDescriptor? {
|
||||||
|
return declaration.accept(object : KtVisitor<DeclarationDescriptor?, Nothing?>() {
|
||||||
|
private fun lookupLocationFor(declaration: KtDeclaration, isTopLevel: Boolean): LookupLocation =
|
||||||
|
if (isTopLevel && track) KotlinLookupLocation(declaration)
|
||||||
|
else NoLookupLocation.WHEN_RESOLVE_DECLARATION
|
||||||
|
|
||||||
|
override fun visitClass(klass: KtClass, data: Nothing?): DeclarationDescriptor? =
|
||||||
|
getClassDescriptorIfAny(klass, lookupLocationFor(klass, klass.isTopLevel()))
|
||||||
|
|
||||||
|
override fun visitObjectDeclaration(declaration: KtObjectDeclaration, data: Nothing?): DeclarationDescriptor? =
|
||||||
|
getClassDescriptorIfAny(declaration, lookupLocationFor(declaration, declaration.isTopLevel()))
|
||||||
|
|
||||||
|
override fun visitTypeParameter(parameter: KtTypeParameter, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
val ownerElement = PsiTreeUtil.getParentOfType(parameter, KtTypeParameterListOwner::class.java) ?: error("Owner not found for type parameter: " + parameter.text)
|
||||||
|
val ownerDescriptor = resolveToDescriptor(ownerElement, /*track =*/false) ?: return null
|
||||||
|
|
||||||
|
val typeParameters: List<TypeParameterDescriptor>
|
||||||
|
typeParameters = when (ownerDescriptor) {
|
||||||
|
is CallableDescriptor -> ownerDescriptor.typeParameters
|
||||||
|
is ClassifierDescriptorWithTypeParameters -> ownerDescriptor.typeConstructor.parameters
|
||||||
|
else -> throw IllegalStateException("Unknown owner kind for a type parameter: " + ownerDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
val name = parameter.nameAsSafeName
|
||||||
|
return typeParameters.firstOrNull { it.name == name }
|
||||||
|
?: throw IllegalStateException("Type parameter $name not found for $ownerDescriptor")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitNamedFunction(function: KtNamedFunction, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
val location = lookupLocationFor(function, function.isTopLevel)
|
||||||
|
val scopeForDeclaration = getMemberScopeDeclaredIn(function, location)
|
||||||
|
scopeForDeclaration.getContributedFunctions(function.nameAsSafeName, location)
|
||||||
|
return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, function)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitParameter(parameter: KtParameter, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
val grandFather = parameter.parent.parent
|
||||||
|
when (grandFather) {
|
||||||
|
is KtPrimaryConstructor -> {
|
||||||
|
val jetClass = grandFather.getContainingClassOrObject()
|
||||||
|
// This is a primary constructor parameter
|
||||||
|
val classDescriptor = getClassDescriptorIfAny(jetClass, lookupLocationFor(jetClass, false))
|
||||||
|
return when {
|
||||||
|
classDescriptor == null -> null
|
||||||
|
parameter.hasValOrVar() -> {
|
||||||
|
classDescriptor.defaultType.memberScope.getContributedVariables(
|
||||||
|
parameter.nameAsSafeName, lookupLocationFor(parameter, false))
|
||||||
|
bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val constructor = classDescriptor.unsubstitutedPrimaryConstructor
|
||||||
|
?: error("There are constructor parameters found, so a constructor should also exist")
|
||||||
|
constructor.valueParameters
|
||||||
|
bindingContext.get(BindingContext.VALUE_PARAMETER, parameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is KtNamedFunction -> {
|
||||||
|
val function = visitNamedFunction(grandFather, data) as? FunctionDescriptor
|
||||||
|
function?.valueParameters
|
||||||
|
return bindingContext.get(BindingContext.VALUE_PARAMETER, parameter)
|
||||||
|
}
|
||||||
|
is KtSecondaryConstructor -> {
|
||||||
|
val constructorDescriptor = visitSecondaryConstructor(
|
||||||
|
grandFather, data
|
||||||
|
) as? ConstructorDescriptor
|
||||||
|
constructorDescriptor?.valueParameters
|
||||||
|
return bindingContext.get(BindingContext.VALUE_PARAMETER, parameter)
|
||||||
|
}
|
||||||
|
else -> //TODO: support parameters in accessors and other places(?)
|
||||||
|
return super.visitParameter(parameter, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
getClassDescriptorIfAny(constructor.parent.parent as KtClassOrObject, lookupLocationFor(constructor, false))?.constructors
|
||||||
|
return bindingContext.get(BindingContext.CONSTRUCTOR, constructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
getClassDescriptorIfAny(constructor.getContainingClassOrObject(), lookupLocationFor(constructor, false))?.constructors
|
||||||
|
return bindingContext.get(BindingContext.CONSTRUCTOR, constructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitProperty(property: KtProperty, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
val location = lookupLocationFor(property, property.isTopLevel)
|
||||||
|
val scopeForDeclaration = getMemberScopeDeclaredIn(property, location)
|
||||||
|
scopeForDeclaration.getContributedVariables(property.nameAsSafeName, location)
|
||||||
|
return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, property)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitDestructuringDeclarationEntry(
|
||||||
|
destructuringDeclarationEntry: KtDestructuringDeclarationEntry, data: Nothing?
|
||||||
|
): DeclarationDescriptor? {
|
||||||
|
val location = lookupLocationFor(destructuringDeclarationEntry, false)
|
||||||
|
val destructuringDeclaration = destructuringDeclarationEntry.parent as? KtDestructuringDeclaration ?: return null
|
||||||
|
val scopeForDeclaration = getMemberScopeDeclaredIn(destructuringDeclaration, location)
|
||||||
|
scopeForDeclaration.getContributedVariables(destructuringDeclarationEntry.nameAsSafeName, location)
|
||||||
|
return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, destructuringDeclarationEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitTypeAlias(typeAlias: KtTypeAlias, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
val location = lookupLocationFor(typeAlias, typeAlias.isTopLevel())
|
||||||
|
val scopeForDeclaration = getMemberScopeDeclaredIn(typeAlias, location)
|
||||||
|
scopeForDeclaration.getContributedClassifier(typeAlias.nameAsSafeName, location)
|
||||||
|
return bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, typeAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitScript(script: KtScript, data: Nothing?): DeclarationDescriptor =
|
||||||
|
getScriptDescriptor(script, lookupLocationFor(script, true))
|
||||||
|
|
||||||
|
override fun visitKtElement(element: KtElement, data: Nothing?): DeclarationDescriptor? {
|
||||||
|
throw IllegalArgumentException("Unsupported declaration type: " + element + " " +
|
||||||
|
element.getElementTextWithContext())
|
||||||
|
}
|
||||||
|
}, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getMemberScopeDeclaredIn(declaration: KtDeclaration, location: LookupLocation):
|
||||||
|
/*package*/ MemberScope {
|
||||||
|
val parentDeclaration = KtStubbedPsiUtil.getContainingDeclaration(declaration)
|
||||||
|
val isTopLevel = parentDeclaration == null
|
||||||
|
if (isTopLevel) { // for top level declarations we search directly in package because of possible conflicts with imports
|
||||||
|
val ktFile = declaration.containingFile as KtFile
|
||||||
|
val fqName = ktFile.packageFqName
|
||||||
|
topLevelDescriptorProvider.assertValid()
|
||||||
|
val packageDescriptor = topLevelDescriptorProvider.getPackageFragment(fqName)
|
||||||
|
if (packageDescriptor == null) {
|
||||||
|
if (topLevelDescriptorProvider is LazyClassContext) {
|
||||||
|
topLevelDescriptorProvider.declarationProviderFactory.diagnoseMissingPackageFragment(ktFile)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw IllegalStateException("Cannot find package fragment for file " + ktFile.name + " with package " + fqName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packageDescriptor!!.getMemberScope()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return when (parentDeclaration) {
|
||||||
|
is KtClassOrObject -> getClassDescriptor(parentDeclaration, location).unsubstitutedMemberScope
|
||||||
|
is KtScript -> getScriptDescriptor(parentDeclaration, location).unsubstitutedMemberScope
|
||||||
|
else -> throw IllegalStateException("Don't call this method for local declarations: " + declaration + "\n" +
|
||||||
|
declaration.getElementTextWithContext())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.storage.StorageManager
|
|||||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||||
import org.jetbrains.kotlin.util.collectionUtils.concat
|
import org.jetbrains.kotlin.util.collectionUtils.concat
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface IndexedImports {
|
interface IndexedImports {
|
||||||
@@ -81,7 +82,7 @@ class LazyImportResolver(
|
|||||||
val indexedImports: IndexedImports,
|
val indexedImports: IndexedImports,
|
||||||
excludedImportNames: Collection<FqName>,
|
excludedImportNames: Collection<FqName>,
|
||||||
private val traceForImportResolve: BindingTrace,
|
private val traceForImportResolve: BindingTrace,
|
||||||
private val packageFragment: PackageFragmentDescriptor,
|
private val packageFragment: PackageFragmentDescriptor?,
|
||||||
val deprecationResolver: DeprecationResolver
|
val deprecationResolver: DeprecationResolver
|
||||||
) : ImportResolver {
|
) : ImportResolver {
|
||||||
private val importedScopesProvider = storageManager.createMemoizedFunctionWithNullableValues {
|
private val importedScopesProvider = storageManager.createMemoizedFunctionWithNullableValues {
|
||||||
@@ -191,6 +192,19 @@ class LazyImportResolver(
|
|||||||
fun getImportScope(directive: KtImportDirective): ImportingScope {
|
fun getImportScope(directive: KtImportDirective): ImportingScope {
|
||||||
return importedScopesProvider(directive) ?: ImportingScope.Empty
|
return importedScopesProvider(directive) ?: ImportingScope.Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val allNames: Set<Name>? by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||||
|
indexedImports.imports.flatMapToNullable(hashSetOf()) { getImportScope(it).computeImportedNames() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun definitelyDoesNotContainName(name: Name) = allNames?.let { name !in it } == true
|
||||||
|
|
||||||
|
fun recordLookup(name: Name, location: LookupLocation) {
|
||||||
|
if (allNames == null) return
|
||||||
|
indexedImports.importsForName(name).forEach {
|
||||||
|
getImportScope(it).recordLookup(name, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LazyImportScope(
|
class LazyImportScope(
|
||||||
@@ -269,4 +283,12 @@ class LazyImportScope(
|
|||||||
p.popIndent()
|
p.popIndent()
|
||||||
p.println("}")
|
p.println("}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun definitelyDoesNotContainName(name: Name) = importResolver.definitelyDoesNotContainName(name)
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name, location: LookupLocation) {
|
||||||
|
importResolver.recordLookup(name, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun computeImportedNames() = importResolver.allNames
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-1
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.resolve.lazy.declarations
|
package org.jetbrains.kotlin.resolve.lazy.declarations
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap
|
import com.google.common.collect.ArrayListMultimap
|
||||||
|
import com.google.common.collect.Sets
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils.safeNameForLazyResolve
|
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils.safeNameForLazyResolve
|
||||||
@@ -37,6 +38,7 @@ abstract class AbstractPsiBasedDeclarationProvider(storageManager: StorageManage
|
|||||||
val classesAndObjects = ArrayListMultimap.create<Name, KtClassLikeInfo>() // order matters here
|
val classesAndObjects = ArrayListMultimap.create<Name, KtClassLikeInfo>() // order matters here
|
||||||
val typeAliases = ArrayListMultimap.create<Name, KtTypeAlias>()
|
val typeAliases = ArrayListMultimap.create<Name, KtTypeAlias>()
|
||||||
val destructuringDeclarationsEntries = ArrayListMultimap.create<Name, KtDestructuringDeclarationEntry>()
|
val destructuringDeclarationsEntries = ArrayListMultimap.create<Name, KtDestructuringDeclarationEntry>()
|
||||||
|
val names = Sets.newHashSet<Name>()
|
||||||
|
|
||||||
fun putToIndex(declaration: KtDeclaration) {
|
fun putToIndex(declaration: KtDeclaration) {
|
||||||
if (declaration is KtAnonymousInitializer || declaration is KtSecondaryConstructor) return
|
if (declaration is KtAnonymousInitializer || declaration is KtSecondaryConstructor) return
|
||||||
@@ -57,7 +59,9 @@ abstract class AbstractPsiBasedDeclarationProvider(storageManager: StorageManage
|
|||||||
}
|
}
|
||||||
is KtDestructuringDeclaration -> {
|
is KtDestructuringDeclaration -> {
|
||||||
for (entry in declaration.entries) {
|
for (entry in declaration.entries) {
|
||||||
destructuringDeclarationsEntries.put(safeNameForLazyResolve(entry.nameAsName), entry)
|
val name = safeNameForLazyResolve(entry.nameAsName)
|
||||||
|
destructuringDeclarationsEntries.put(name, entry)
|
||||||
|
names.add(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is KtParameter -> {
|
is KtParameter -> {
|
||||||
@@ -65,11 +69,17 @@ abstract class AbstractPsiBasedDeclarationProvider(storageManager: StorageManage
|
|||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Unknown declaration: " + declaration)
|
else -> throw IllegalArgumentException("Unknown declaration: " + declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (declaration) {
|
||||||
|
is KtNamedDeclaration -> names.add(safeNameForLazyResolve(declaration))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = "allDeclarations: " + allDeclarations.mapNotNull { it.name }
|
override fun toString() = "allDeclarations: " + allDeclarations.mapNotNull { it.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDeclarationNames() = index().names
|
||||||
|
|
||||||
private val index = storageManager.createLazyValue<Index> {
|
private val index = storageManager.createLazyValue<Index> {
|
||||||
val index = Index()
|
val index = Index()
|
||||||
doCreateIndex(index)
|
doCreateIndex(index)
|
||||||
|
|||||||
+2
@@ -41,4 +41,6 @@ class CombinedPackageMemberDeclarationProvider(
|
|||||||
override fun getClassOrObjectDeclarations(name: Name) = providers.flatMap { it.getClassOrObjectDeclarations(name) }
|
override fun getClassOrObjectDeclarations(name: Name) = providers.flatMap { it.getClassOrObjectDeclarations(name) }
|
||||||
|
|
||||||
override fun getTypeAliasDeclarations(name: Name) = providers.flatMap { it.getTypeAliasDeclarations(name) }
|
override fun getTypeAliasDeclarations(name: Name) = providers.flatMap { it.getTypeAliasDeclarations(name) }
|
||||||
|
|
||||||
|
override fun getDeclarationNames(): Set<Name> = providers.flatMapTo(HashSet()) { it.getDeclarationNames() }
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -33,4 +33,6 @@ interface DeclarationProvider {
|
|||||||
fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo>
|
fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo>
|
||||||
|
|
||||||
fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias>
|
fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias>
|
||||||
|
|
||||||
|
fun getDeclarationNames(): Set<Name>
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -219,8 +219,6 @@ protected constructor(
|
|||||||
return result.toList()
|
return result.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun recordLookup(name: Name, from: LookupLocation)
|
|
||||||
|
|
||||||
// Do not change this, override in concrete subclasses:
|
// Do not change this, override in concrete subclasses:
|
||||||
// it is very easy to compromise laziness of this class, and fail all the debugging
|
// it is very easy to compromise laziness of this class, and fail all the debugging
|
||||||
// a generic implementation can't do this properly
|
// a generic implementation can't do this properly
|
||||||
|
|||||||
+4
@@ -58,6 +58,10 @@ class LazyPackageMemberScope(
|
|||||||
c.lookupTracker.record(from, thisDescriptor, name)
|
c.lookupTracker.record(from, thisDescriptor, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getClassifierNames(): Set<Name>? = declarationProvider.getDeclarationNames()
|
||||||
|
override fun getFunctionNames() = declarationProvider.getDeclarationNames()
|
||||||
|
override fun getVariableNames() = declarationProvider.getDeclarationNames()
|
||||||
|
|
||||||
// Do not add details here, they may compromise the laziness during debugging
|
// Do not add details here, they may compromise the laziness during debugging
|
||||||
override fun toString() = "lazy scope for package " + thisDescriptor.name
|
override fun toString() = "lazy scope for package " + thisDescriptor.name
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -18,10 +18,8 @@ package org.jetbrains.kotlin.types.expressions
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.util.PsiTreeUtil
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
import org.jetbrains.kotlin.config.TargetPlatformVersion
|
import org.jetbrains.kotlin.config.TargetPlatformVersion
|
||||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
|
||||||
import org.jetbrains.kotlin.container.get
|
import org.jetbrains.kotlin.container.get
|
||||||
import org.jetbrains.kotlin.context.GlobalContext
|
import org.jetbrains.kotlin.context.GlobalContext
|
||||||
import org.jetbrains.kotlin.context.withModule
|
import org.jetbrains.kotlin.context.withModule
|
||||||
@@ -200,6 +198,13 @@ class LocalLazyDeclarationResolver(
|
|||||||
}
|
}
|
||||||
return super.getClassDescriptor(classOrObject, location)
|
return super.getClassDescriptor(classOrObject, location)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getClassDescriptorIfAny(classOrObject: KtClassOrObject, location: LookupLocation): ClassDescriptor? {
|
||||||
|
if (localClassDescriptorManager.isMyClass(classOrObject)) {
|
||||||
|
return localClassDescriptorManager.getClassDescriptor(classOrObject, scopeProvider)
|
||||||
|
}
|
||||||
|
return super.getClassDescriptorIfAny(classOrObject, location)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.incremental
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
import java.io.ObjectOutputStream
|
||||||
|
|
||||||
|
data class BuildDifference(val ts: Long, val isIncremental: Boolean, val dirtyData: DirtyData)
|
||||||
|
|
||||||
|
// todo: storage format can be optimized by compressing fq-names
|
||||||
|
data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
||||||
|
companion object {
|
||||||
|
fun readFromFile(file: File, reporter: ICReporter?): BuildDiffsStorage? {
|
||||||
|
fun reportFail(reason: String) {
|
||||||
|
reporter?.report { "Could not read diff from file $file: $reason" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.exists()) return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectInputStream(file.inputStream().buffered()).use { input ->
|
||||||
|
val version = input.readInt()
|
||||||
|
if (version != CURRENT_VERSION) {
|
||||||
|
reportFail("incompatible version $version, actual version is $CURRENT_VERSION")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val size = input.readInt()
|
||||||
|
val result = ArrayList<BuildDifference>(size)
|
||||||
|
repeat(size) {
|
||||||
|
result.add(input.readBuildDifference())
|
||||||
|
}
|
||||||
|
return BuildDiffsStorage(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: IOException) {
|
||||||
|
reportFail(e.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeToFile(file: File, storage: BuildDiffsStorage, reporter: ICReporter?) {
|
||||||
|
file.parentFile.mkdirs()
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectOutputStream(file.outputStream().buffered()).use { output ->
|
||||||
|
output.writeInt(CURRENT_VERSION)
|
||||||
|
|
||||||
|
val diffsToWrite = storage.buildDiffs.sortedBy { it.ts }.takeLast(MAX_DIFFS_ENTRIES)
|
||||||
|
output.writeInt(diffsToWrite.size)
|
||||||
|
for (diff in diffsToWrite) {
|
||||||
|
output.writeBuildDifference(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: IOException) {
|
||||||
|
reporter?.report { "Could not write diff to file $file: $e" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectInputStream.readBuildDifference(): BuildDifference {
|
||||||
|
val ts = readLong()
|
||||||
|
val isIncremental = readBoolean()
|
||||||
|
val dirtyData = readDirtyData()
|
||||||
|
return BuildDifference(ts, isIncremental, dirtyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeBuildDifference(diff: BuildDifference) {
|
||||||
|
writeLong(diff.ts)
|
||||||
|
writeBoolean(diff.isIncremental)
|
||||||
|
writeDirtyData(diff.dirtyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectInputStream.readDirtyData(): DirtyData {
|
||||||
|
val lookupSymbolSize = readInt()
|
||||||
|
val lookupSymbols = ArrayList<LookupSymbol>(lookupSymbolSize)
|
||||||
|
repeat(lookupSymbolSize) {
|
||||||
|
val name = readUTF()
|
||||||
|
val scope = readUTF()
|
||||||
|
lookupSymbols.add(LookupSymbol(name = name, scope = scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
val dirtyClassesSize = readInt()
|
||||||
|
val dirtyClassesFqNames = ArrayList<FqName>(dirtyClassesSize)
|
||||||
|
repeat(dirtyClassesSize) {
|
||||||
|
val fqNameString = readUTF()
|
||||||
|
dirtyClassesFqNames.add(FqName(fqNameString))
|
||||||
|
}
|
||||||
|
|
||||||
|
return DirtyData(lookupSymbols, dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeDirtyData(dirtyData: DirtyData) {
|
||||||
|
val lookupSymbols = dirtyData.dirtyLookupSymbols
|
||||||
|
writeInt(lookupSymbols.size)
|
||||||
|
for ((name, scope) in lookupSymbols) {
|
||||||
|
writeUTF(name)
|
||||||
|
writeUTF(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
val dirtyClassesFqNames = dirtyData.dirtyClassesFqNames
|
||||||
|
writeInt(dirtyClassesFqNames.size)
|
||||||
|
for (fqName in dirtyClassesFqNames) {
|
||||||
|
writeUTF(fqName.asString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val MAX_DIFFS_ENTRIES: Int = 10
|
||||||
|
|
||||||
|
@set:TestOnly
|
||||||
|
var CURRENT_VERSION: Int = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.incremental
|
|||||||
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
internal data class BuildInfo(val startTS: Long) : Serializable {
|
data class BuildInfo(val startTS: Long) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
fun read(file: File): BuildInfo? =
|
fun read(file: File): BuildInfo? =
|
||||||
try {
|
try {
|
||||||
|
|||||||
+15
-10
@@ -255,16 +255,9 @@ abstract class IncrementalCompilerRunner<
|
|||||||
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
||||||
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
||||||
}
|
}
|
||||||
if (changesRegistry != null) {
|
|
||||||
if (compilationMode is CompilationMode.Incremental) {
|
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
||||||
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
|
||||||
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
|
||||||
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitCode == ExitCode.OK) {
|
if (exitCode == ExitCode.OK) {
|
||||||
cacheVersions.forEach { it.saveIfNeeded() }
|
cacheVersions.forEach { it.saveIfNeeded() }
|
||||||
@@ -273,6 +266,18 @@ abstract class IncrementalCompilerRunner<
|
|||||||
return exitCode
|
return exitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
|
||||||
|
if (changesRegistry == null) return
|
||||||
|
|
||||||
|
if (compilationMode is CompilationMode.Incremental) {
|
||||||
|
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
||||||
|
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
||||||
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
||||||
|
|||||||
+73
-23
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
|
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
|
||||||
import org.jetbrains.kotlin.build.GeneratedFile
|
import org.jetbrains.kotlin.build.GeneratedFile
|
||||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||||
import org.jetbrains.kotlin.build.isModuleMappingFile
|
|
||||||
import org.jetbrains.kotlin.build.JvmSourceRoot
|
import org.jetbrains.kotlin.build.JvmSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
fun makeIncrementally(
|
fun makeIncrementally(
|
||||||
cachesDir: File,
|
cachesDir: File,
|
||||||
@@ -89,7 +90,9 @@ class IncrementalJvmCompilerRunner(
|
|||||||
reporter: ICReporter,
|
reporter: ICReporter,
|
||||||
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
||||||
artifactChangesProvider: ArtifactChangesProvider? = null,
|
artifactChangesProvider: ArtifactChangesProvider? = null,
|
||||||
changesRegistry: ChangesRegistry? = null
|
changesRegistry: ChangesRegistry? = null,
|
||||||
|
private val buildHistoryFile: File? = null,
|
||||||
|
private val friendBuildHistoryFile: File? = null
|
||||||
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
|
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
|
||||||
workingDir,
|
workingDir,
|
||||||
"caches-jvm",
|
"caches-jvm",
|
||||||
@@ -110,16 +113,58 @@ class IncrementalJvmCompilerRunner(
|
|||||||
private var javaFilesProcessor = ChangedJavaFilesProcessor(reporter)
|
private var javaFilesProcessor = ChangedJavaFilesProcessor(reporter)
|
||||||
|
|
||||||
override fun calculateSourcesToCompile(caches: IncrementalJvmCachesManager, changedFiles: ChangedFiles.Known, args: K2JVMCompilerArguments): CompilationMode {
|
override fun calculateSourcesToCompile(caches: IncrementalJvmCachesManager, changedFiles: ChangedFiles.Known, args: K2JVMCompilerArguments): CompilationMode {
|
||||||
val removedClassFiles = changedFiles.removed.filter(File::isClassFile)
|
val dirtyFiles = getDirtyFiles(changedFiles)
|
||||||
if (removedClassFiles.any()) return CompilationMode.Rebuild { "Removed class files: ${reporter.pathsAsString(removedClassFiles)}" }
|
|
||||||
|
|
||||||
val modifiedClassFiles = changedFiles.modified.filter(File::isClassFile)
|
fun markDirtyBy(lookupSymbols: Collection<LookupSymbol>) {
|
||||||
if (modifiedClassFiles.any()) return CompilationMode.Rebuild { "Modified class files: ${reporter.pathsAsString(modifiedClassFiles)}" }
|
if (lookupSymbols.isEmpty()) return
|
||||||
|
|
||||||
|
val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, reporter)
|
||||||
|
dirtyFiles.addAll(dirtyFilesFromLookups)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markDirtyBy(dirtyClassesFqNames: Collection<FqName>) {
|
||||||
|
if (dirtyClassesFqNames.isEmpty()) return
|
||||||
|
|
||||||
|
val fqNamesWithSubtypes = dirtyClassesFqNames.flatMap { withSubtypes(it, listOf(caches.platformCache)) }
|
||||||
|
val dirtyFilesFromFqNames = mapClassesFqNamesToFiles(listOf(caches.platformCache), fqNamesWithSubtypes, reporter)
|
||||||
|
dirtyFiles.addAll(dirtyFilesFromFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
||||||
|
reporter.report { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||||
|
|
||||||
|
val changesFromFriend by lazy {
|
||||||
|
val myLastTS = lastBuildInfo?.startTS ?: return@lazy ChangesEither.Unknown()
|
||||||
|
val storage = friendBuildHistoryFile?.let { BuildDiffsStorage.readFromFile(it, reporter) } ?: return@lazy ChangesEither.Unknown()
|
||||||
|
|
||||||
|
val (prevDiffs, newDiffs) = storage.buildDiffs.partition { it.ts < myLastTS }
|
||||||
|
if (prevDiffs.isEmpty()) return@lazy ChangesEither.Unknown()
|
||||||
|
|
||||||
|
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
|
val dirtyClassesFqNames = HashSet<FqName>()
|
||||||
|
for ((_, isIncremental, dirtyData) in newDiffs) {
|
||||||
|
if (!isIncremental) return@lazy ChangesEither.Unknown()
|
||||||
|
|
||||||
|
dirtyLookupSymbols.addAll(dirtyData.dirtyLookupSymbols)
|
||||||
|
dirtyClassesFqNames.addAll(dirtyData.dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
markDirtyBy(dirtyLookupSymbols)
|
||||||
|
markDirtyBy(dirtyClassesFqNames)
|
||||||
|
ChangesEither.Known(dirtyLookupSymbols, dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
val friendDirs = args.friendPaths?.map { File(it) } ?: emptyList()
|
||||||
|
for (file in changedFiles.removed.asSequence() + changedFiles.modified.asSequence()) {
|
||||||
|
if (!file.isClassFile()) continue
|
||||||
|
|
||||||
|
val isFriendClassFile = friendDirs.any { FileUtil.isAncestor(it, file, false) }
|
||||||
|
if (isFriendClassFile && changesFromFriend is ChangesEither.Known) continue
|
||||||
|
|
||||||
|
return CompilationMode.Rebuild { "Cannot get changes from modified or removed class file: ${reporter.pathsAsString(file)}" }
|
||||||
|
}
|
||||||
|
|
||||||
val classpathSet = args.classpathAsList.toHashSet()
|
val classpathSet = args.classpathAsList.toHashSet()
|
||||||
val modifiedClasspathEntries = changedFiles.modified.filter { it in classpathSet }
|
val modifiedClasspathEntries = changedFiles.modified.filter { it in classpathSet }
|
||||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
|
||||||
reporter.report { "Last Kotlin Build info -- $lastBuildInfo" }
|
|
||||||
val classpathChanges = getClasspathChanges(modifiedClasspathEntries, lastBuildInfo)
|
val classpathChanges = getClasspathChanges(modifiedClasspathEntries, lastBuildInfo)
|
||||||
if (classpathChanges !is ChangesEither.Known) {
|
if (classpathChanges !is ChangesEither.Known) {
|
||||||
return CompilationMode.Rebuild { "could not get changes from modified classpath entries: ${reporter.pathsAsString(modifiedClasspathEntries)}" }
|
return CompilationMode.Rebuild { "could not get changes from modified classpath entries: ${reporter.pathsAsString(modifiedClasspathEntries)}" }
|
||||||
@@ -131,21 +176,9 @@ class IncrementalJvmCompilerRunner(
|
|||||||
is ChangesEither.Unknown -> return CompilationMode.Rebuild { "Could not get changes for java files" }
|
is ChangesEither.Unknown -> return CompilationMode.Rebuild { "Could not get changes for java files" }
|
||||||
}
|
}
|
||||||
|
|
||||||
val dirtyFiles = getDirtyFiles(changedFiles)
|
markDirtyBy(affectedJavaSymbols)
|
||||||
val lookupSymbols = HashSet<LookupSymbol>()
|
markDirtyBy(classpathChanges.lookupSymbols)
|
||||||
lookupSymbols.addAll(affectedJavaSymbols)
|
markDirtyBy(classpathChanges.fqNames)
|
||||||
lookupSymbols.addAll(classpathChanges.lookupSymbols)
|
|
||||||
|
|
||||||
if (lookupSymbols.any()) {
|
|
||||||
val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, reporter)
|
|
||||||
dirtyFiles.addAll(dirtyFilesFromLookups)
|
|
||||||
}
|
|
||||||
|
|
||||||
val dirtyClassesFqNames = classpathChanges.fqNames.flatMap { withSubtypes(it, listOf(caches.platformCache)) }
|
|
||||||
if (dirtyClassesFqNames.any()) {
|
|
||||||
val dirtyFilesFromFqNames = mapClassesFqNamesToFiles(listOf(caches.platformCache), dirtyClassesFqNames, reporter)
|
|
||||||
dirtyFiles.addAll(dirtyFilesFromFqNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompilationMode.Incremental(dirtyFiles)
|
return CompilationMode.Incremental(dirtyFiles)
|
||||||
}
|
}
|
||||||
@@ -263,6 +296,23 @@ class IncrementalJvmCompilerRunner(
|
|||||||
override fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
override fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
||||||
javaFilesProcessor.allChangedSymbols
|
javaFilesProcessor.allChangedSymbols
|
||||||
|
|
||||||
|
override fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
|
||||||
|
super.processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
|
||||||
|
|
||||||
|
if (buildHistoryFile == null) return
|
||||||
|
|
||||||
|
val prevDiffs = BuildDiffsStorage.readFromFile(buildHistoryFile, reporter)?.buildDiffs ?: emptyList()
|
||||||
|
val newDiff = if (compilationMode is CompilationMode.Incremental) {
|
||||||
|
BuildDifference(currentBuildInfo.startTS, true, dirtyData)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val emptyDirtyData = DirtyData()
|
||||||
|
BuildDifference(currentBuildInfo.startTS, false, emptyDirtyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildDiffsStorage.writeToFile(buildHistoryFile, BuildDiffsStorage(prevDiffs + newDiff), reporter)
|
||||||
|
}
|
||||||
|
|
||||||
override fun makeServices(
|
override fun makeServices(
|
||||||
args: K2JVMCompilerArguments,
|
args: K2JVMCompilerArguments,
|
||||||
lookupTracker: LookupTracker,
|
lookupTracker: LookupTracker,
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* 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.incremental
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class BuildDiffsStorageTest {
|
||||||
|
lateinit var storageFile: File
|
||||||
|
private val random = Random(System.currentTimeMillis())
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
storageFile = File.createTempFile("BuildDiffsStorageTest", "storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
storageFile.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testToString() {
|
||||||
|
val lookupSymbols = listOf(LookupSymbol("foo", "bar"))
|
||||||
|
val fqNames = listOf(FqName("fizz.Buzz"))
|
||||||
|
val diff = BuildDifference(100, true, DirtyData(lookupSymbols, fqNames))
|
||||||
|
val diffs = BuildDiffsStorage(listOf(diff))
|
||||||
|
Assert.assertEquals("BuildDiffsStorage(buildDiffs=[BuildDifference(ts=100, isIncremental=true, dirtyData=DirtyData(dirtyLookupSymbols=[LookupSymbol(name=foo, scope=bar)], dirtyClassesFqNames=[fizz.Buzz]))])",
|
||||||
|
diffs.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun writeReadSimple() {
|
||||||
|
val diffs = BuildDiffsStorage(listOf(getRandomDiff()))
|
||||||
|
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||||
|
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
Assert.assertEquals(diffs.toString(), diffsDeserialized.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun writeReadMany() {
|
||||||
|
val generated = Array(20) { getRandomDiff() }.toList()
|
||||||
|
val diffs = BuildDiffsStorage(generated)
|
||||||
|
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||||
|
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
val expected = generated.sortedBy { it.ts }.takeLast(BuildDiffsStorage.MAX_DIFFS_ENTRIES).toTypedArray()
|
||||||
|
Assert.assertArrayEquals(expected, diffsDeserialized?.buildDiffs?.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun readFileNotExist() {
|
||||||
|
storageFile.delete()
|
||||||
|
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
Assert.assertEquals(null, diffsDeserialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun versionChanged() {
|
||||||
|
val diffs = BuildDiffsStorage(listOf(getRandomDiff()))
|
||||||
|
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||||
|
|
||||||
|
val versionBackup = BuildDiffsStorage.CURRENT_VERSION
|
||||||
|
try {
|
||||||
|
BuildDiffsStorage.CURRENT_VERSION++
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
Assert.assertEquals(null, diffsDeserialized)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
BuildDiffsStorage.CURRENT_VERSION = versionBackup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRandomDiff(): BuildDifference {
|
||||||
|
val ts = random.nextLong()
|
||||||
|
val lookupSymbols = listOf(LookupSymbol("foo", "bar"))
|
||||||
|
val fqNames = listOf(FqName("fizz.Buzz"))
|
||||||
|
return BuildDifference(ts, true, DirtyData(lookupSymbols, fqNames))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -183,7 +183,13 @@ class KtLightAnnotationForSourceEntry(
|
|||||||
private val _initializers by lazyPub {
|
private val _initializers by lazyPub {
|
||||||
delegate.initializers.mapIndexed { i, it ->
|
delegate.initializers.mapIndexed { i, it ->
|
||||||
wrapAnnotationValue(it, this, {
|
wrapAnnotationValue(it, this, {
|
||||||
(originalExpression as KtCallElement).valueArguments[i].getArgumentExpression()!!
|
originalExpression.let {
|
||||||
|
when (it) {
|
||||||
|
is KtCallElement -> it.valueArguments[i].getArgumentExpression()!!
|
||||||
|
is KtCollectionLiteralExpression -> it.getInnerExpressions()[i]
|
||||||
|
else -> throw UnsupportedOperationException("cant process $it of type ${it?.javaClass}")
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ class KotlinCallResolver(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (collectAllCandidates) {
|
if (collectAllCandidates) {
|
||||||
val allCandidates = towerResolver.collectAllCandidates(scopeTower, processor)
|
val allCandidates = towerResolver.collectAllCandidates(scopeTower, processor, kotlinCall.name)
|
||||||
return kotlinCallCompleter.createAllCandidatesResult(allCandidates, expectedType, resolutionCallbacks)
|
return kotlinCallCompleter.createAllCandidatesResult(allCandidates, expectedType, resolutionCallbacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kotlinCall.callKind != KotlinCallKind.UNSUPPORTED)
|
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = kotlinCall.callKind != KotlinCallKind.UNSUPPORTED, name = kotlinCall.name)
|
||||||
|
|
||||||
return choseMostSpecific(candidateFactory, resolutionCallbacks, expectedType, candidates)
|
return choseMostSpecific(candidateFactory, resolutionCallbacks, expectedType, candidates)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -115,7 +115,7 @@ class CallableReferenceResolver(
|
|||||||
): Set<CallableReferenceCandidate> {
|
): Set<CallableReferenceCandidate> {
|
||||||
val factory = CallableReferencesCandidateFactory(callableReference, callComponents, scopeTower, compatibilityChecker, expectedType)
|
val factory = CallableReferencesCandidateFactory(callableReference, callComponents, scopeTower, compatibilityChecker, expectedType)
|
||||||
val processor = createCallableReferenceProcessor(factory)
|
val processor = createCallableReferenceProcessor(factory)
|
||||||
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = true)
|
val candidates = towerResolver.runResolve(scopeTower, processor, useOrder = true, name = callableReference.rhsName)
|
||||||
return callableReferenceOverloadConflictResolver.chooseMaximallySpecificCandidates(
|
return callableReferenceOverloadConflictResolver.chooseMaximallySpecificCandidates(
|
||||||
candidates,
|
candidates,
|
||||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
|
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ interface ScopeTowerLevel {
|
|||||||
fun getObjects(name: Name, extensionReceiver: ReceiverValueWithSmartCastInfo?): Collection<CandidateWithBoundDispatchReceiver>
|
fun getObjects(name: Name, extensionReceiver: ReceiverValueWithSmartCastInfo?): Collection<CandidateWithBoundDispatchReceiver>
|
||||||
|
|
||||||
fun getFunctions(name: Name, extensionReceiver: ReceiverValueWithSmartCastInfo?): Collection<CandidateWithBoundDispatchReceiver>
|
fun getFunctions(name: Name, extensionReceiver: ReceiverValueWithSmartCastInfo?): Collection<CandidateWithBoundDispatchReceiver>
|
||||||
|
|
||||||
|
fun recordLookup(name: Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CandidateWithBoundDispatchReceiver {
|
interface CandidateWithBoundDispatchReceiver {
|
||||||
|
|||||||
+53
-17
@@ -27,41 +27,54 @@ import java.util.*
|
|||||||
|
|
||||||
abstract class AbstractInvokeTowerProcessor<C : Candidate>(
|
abstract class AbstractInvokeTowerProcessor<C : Candidate>(
|
||||||
protected val factoryProviderForInvoke: CandidateFactoryProviderForInvoke<C>,
|
protected val factoryProviderForInvoke: CandidateFactoryProviderForInvoke<C>,
|
||||||
private val variableProcessor: ScopeTowerProcessor<C>
|
protected val variableProcessor: ScopeTowerProcessor<C>
|
||||||
) : ScopeTowerProcessor<C> {
|
) : ScopeTowerProcessor<C> {
|
||||||
// todo optimize it
|
// todo optimize it
|
||||||
private val previousData = ArrayList<TowerData>()
|
private val previousData = ArrayList<TowerData>()
|
||||||
private val invokeProcessors: MutableList<Collection<VariableInvokeProcessor>> = ArrayList()
|
private val invokeProcessors: MutableList<Collection<VariableInvokeProcessor>> = ArrayList()
|
||||||
|
|
||||||
private inner class VariableInvokeProcessor(val variableCandidate: C): ScopeTowerProcessor<C> {
|
protected fun hasInvokeProcessors() = invokeProcessors.isNotEmpty()
|
||||||
val invokeProcessor: ScopeTowerProcessor<C> = createInvokeProcessor(variableCandidate)
|
|
||||||
|
private inner class VariableInvokeProcessor(
|
||||||
|
var variableCandidate: C,
|
||||||
|
val invokeProcessor: ScopeTowerProcessor<C>
|
||||||
|
): ScopeTowerProcessor<C> {
|
||||||
|
|
||||||
override fun process(data: TowerData)
|
override fun process(data: TowerData)
|
||||||
= invokeProcessor.process(data).map { candidateGroup ->
|
= invokeProcessor.process(data).map { candidateGroup ->
|
||||||
candidateGroup.map { factoryProviderForInvoke.transformCandidate(variableCandidate, it) }
|
candidateGroup.map { factoryProviderForInvoke.transformCandidate(variableCandidate, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
invokeProcessor.recordLookups(skippedData, name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun createInvokeProcessor(variableCandidate: C): ScopeTowerProcessor<C>
|
private fun createVariableInvokeProcessor(variableCandidate: C): VariableInvokeProcessor? =
|
||||||
|
createInvokeProcessor(variableCandidate)?.let { VariableInvokeProcessor(variableCandidate, it) }
|
||||||
|
|
||||||
|
protected abstract fun createInvokeProcessor(variableCandidate: C): ScopeTowerProcessor<C>?
|
||||||
|
|
||||||
|
protected abstract fun mayDataBeApplicable(data: TowerData): Boolean
|
||||||
|
|
||||||
override fun process(data: TowerData): List<Collection<C>> {
|
override fun process(data: TowerData): List<Collection<C>> {
|
||||||
previousData.add(data)
|
|
||||||
|
|
||||||
val candidateGroups = ArrayList<Collection<C>>(0)
|
val candidateGroups = ArrayList<Collection<C>>(0)
|
||||||
|
|
||||||
for (processorsGroup in invokeProcessors) {
|
if (mayDataBeApplicable(data)) {
|
||||||
candidateGroups.addAll(processorsGroup.processVariableGroup(data))
|
previousData.add(data)
|
||||||
|
for (processorsGroup in invokeProcessors) {
|
||||||
|
candidateGroups.addAll(processorsGroup.processVariableGroup(data))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (variableCandidates in variableProcessor.process(data)) {
|
for (variableCandidates in variableProcessor.process(data)) {
|
||||||
val successfulVariables = variableCandidates.filter {
|
val variableProcessors = variableCandidates.mapNotNull {
|
||||||
it.isSuccessful
|
if (it.isSuccessful) createVariableInvokeProcessor(it) else null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successfulVariables.isNotEmpty()) {
|
if (variableProcessors.isNotEmpty()) {
|
||||||
val variableProcessors = successfulVariables.map { VariableInvokeProcessor(it) }
|
|
||||||
invokeProcessors.add(variableProcessors)
|
invokeProcessors.add(variableProcessors)
|
||||||
|
|
||||||
for (oldData in previousData) {
|
for (oldData in previousData) {
|
||||||
candidateGroups.addAll(variableProcessors.processVariableGroup(oldData))
|
candidateGroups.addAll(variableProcessors.processVariableGroup(oldData))
|
||||||
}
|
}
|
||||||
@@ -95,11 +108,25 @@ class InvokeTowerProcessor<C : Candidate>(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
// todo filter by operator
|
// todo filter by operator
|
||||||
override fun createInvokeProcessor(variableCandidate: C): ScopeTowerProcessor<C> {
|
override fun createInvokeProcessor(variableCandidate: C): ScopeTowerProcessor<C>? {
|
||||||
val (variableReceiver, invokeContext) = factoryProviderForInvoke.factoryForInvoke(variableCandidate, useExplicitReceiver = false)
|
val (variableReceiver, invokeContext) = factoryProviderForInvoke.factoryForInvoke(variableCandidate, useExplicitReceiver = false)
|
||||||
?: return KnownResultProcessor(emptyList())
|
?: return null
|
||||||
return ExplicitReceiverScopeTowerProcessor(scopeTower, invokeContext, variableReceiver) { getFunctions(OperatorNameConventions.INVOKE, it) }
|
return ExplicitReceiverScopeTowerProcessor(scopeTower, invokeContext, variableReceiver) { getFunctions(OperatorNameConventions.INVOKE, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mayDataBeApplicable(data: TowerData) =
|
||||||
|
data == TowerData.Empty || data is TowerData.TowerLevel
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
variableProcessor.recordLookups(skippedData, name)
|
||||||
|
if (!hasInvokeProcessors()) return
|
||||||
|
|
||||||
|
skippedData.forEach {
|
||||||
|
if (it is TowerData.TowerLevel) {
|
||||||
|
it.level.recordLookup(OperatorNameConventions.INVOKE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvokeExtensionTowerProcessor<C : Candidate>(
|
class InvokeExtensionTowerProcessor<C : Candidate>(
|
||||||
@@ -112,13 +139,19 @@ class InvokeExtensionTowerProcessor<C : Candidate>(
|
|||||||
createVariableAndObjectProcessor(scopeTower, name, factoryProviderForInvoke.factoryForVariable(stripExplicitReceiver = true), explicitReceiver = null)
|
createVariableAndObjectProcessor(scopeTower, name, factoryProviderForInvoke.factoryForVariable(stripExplicitReceiver = true), explicitReceiver = null)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun createInvokeProcessor(variableCandidate: C): ScopeTowerProcessor<C> {
|
override fun createInvokeProcessor(variableCandidate: C): ScopeTowerProcessor<C>? {
|
||||||
val (variableReceiver, invokeContext) = factoryProviderForInvoke.factoryForInvoke(variableCandidate, useExplicitReceiver = true)
|
val (variableReceiver, invokeContext) = factoryProviderForInvoke.factoryForInvoke(variableCandidate, useExplicitReceiver = true)
|
||||||
?: return KnownResultProcessor(emptyList())
|
?: return null
|
||||||
val invokeDescriptor = scopeTower.getExtensionInvokeCandidateDescriptor(variableReceiver)
|
val invokeDescriptor = scopeTower.getExtensionInvokeCandidateDescriptor(variableReceiver)
|
||||||
?: return KnownResultProcessor(emptyList())
|
?: return null
|
||||||
return InvokeExtensionScopeTowerProcessor(invokeContext, invokeDescriptor, explicitReceiver)
|
return InvokeExtensionScopeTowerProcessor(invokeContext, invokeDescriptor, explicitReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun mayDataBeApplicable(data: TowerData): Boolean = data == TowerData.Empty || data is TowerData.OnlyImplicitReceiver
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
variableProcessor.recordLookups(skippedData, name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InvokeExtensionScopeTowerProcessor<C : Candidate>(
|
private class InvokeExtensionScopeTowerProcessor<C : Candidate>(
|
||||||
@@ -138,6 +171,9 @@ private class InvokeExtensionScopeTowerProcessor<C : Candidate>(
|
|||||||
|
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No lookups happen in `simpleProcess`
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo debug info
|
// todo debug info
|
||||||
|
|||||||
+31
@@ -28,6 +28,8 @@ class KnownResultProcessor<out C>(
|
|||||||
): ScopeTowerProcessor<C> {
|
): ScopeTowerProcessor<C> {
|
||||||
override fun process(data: TowerData)
|
override fun process(data: TowerData)
|
||||||
= if (data == TowerData.Empty) listOfNotNull(result.takeIf { it.isNotEmpty() }) else emptyList()
|
= if (data == TowerData.Empty) listOfNotNull(result.takeIf { it.isNotEmpty() }) else emptyList()
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use this if processors priority is important
|
// use this if processors priority is important
|
||||||
@@ -35,6 +37,11 @@ class PrioritizedCompositeScopeTowerProcessor<out C>(
|
|||||||
vararg val processors: ScopeTowerProcessor<C>
|
vararg val processors: ScopeTowerProcessor<C>
|
||||||
) : ScopeTowerProcessor<C> {
|
) : ScopeTowerProcessor<C> {
|
||||||
override fun process(data: TowerData): List<Collection<C>> = processors.flatMap { it.process(data) }
|
override fun process(data: TowerData): List<Collection<C>> = processors.flatMap { it.process(data) }
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
processors.forEach { it.recordLookups(skippedData, name) }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use this if all processors has same priority
|
// use this if all processors has same priority
|
||||||
@@ -42,6 +49,10 @@ class SamePriorityCompositeScopeTowerProcessor<out C>(
|
|||||||
private vararg val processors: SimpleScopeTowerProcessor<C>
|
private vararg val processors: SimpleScopeTowerProcessor<C>
|
||||||
): SimpleScopeTowerProcessor<C> {
|
): SimpleScopeTowerProcessor<C> {
|
||||||
override fun simpleProcess(data: TowerData): Collection<C> = processors.flatMap { it.simpleProcess(data) }
|
override fun simpleProcess(data: TowerData): Collection<C> = processors.flatMap { it.simpleProcess(data) }
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
processors.forEach { it.recordLookups(skippedData, name) }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class AbstractSimpleScopeTowerProcessor<C: Candidate>(
|
internal abstract class AbstractSimpleScopeTowerProcessor<C: Candidate>(
|
||||||
@@ -84,6 +95,14 @@ internal class ExplicitReceiverScopeTowerProcessor<C: Candidate>(
|
|||||||
}
|
}
|
||||||
return extensions
|
return extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
for (data in skippedData) {
|
||||||
|
if (data is TowerData.TowerLevel) {
|
||||||
|
data.level.recordLookup(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QualifierScopeTowerProcessor<C: Candidate>(
|
private class QualifierScopeTowerProcessor<C: Candidate>(
|
||||||
@@ -103,6 +122,9 @@ private class QualifierScopeTowerProcessor<C: Candidate>(
|
|||||||
}
|
}
|
||||||
return staticMembers
|
return staticMembers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QualifierScopeTowerProcessor works only with TowerData.Empty that should not be ignored
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoExplicitReceiverScopeTowerProcessor<C: Candidate>(
|
private class NoExplicitReceiverScopeTowerProcessor<C: Candidate>(
|
||||||
@@ -132,6 +154,15 @@ private class NoExplicitReceiverScopeTowerProcessor<C: Candidate>(
|
|||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||||
|
for (data in skippedData) {
|
||||||
|
when (data) {
|
||||||
|
is TowerData.TowerLevel -> data.level.recordLookup(name)
|
||||||
|
is TowerData.BothTowerLevelAndImplicitReceiver -> data.level.recordLookup(name)
|
||||||
|
is TowerData.ForLookupForNoExplicitReceiver -> data.level.recordLookup(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <C : Candidate> createSimpleProcessorWithoutClassValueReceiver(
|
private fun <C : Candidate> createSimpleProcessorWithoutClassValueReceiver(
|
||||||
|
|||||||
@@ -169,6 +169,13 @@ internal class MemberScopeTowerLevel(
|
|||||||
syntheticScopes.collectSyntheticMemberFunctions(listOfNotNull(it), name, location)
|
syntheticScopes.collectSyntheticMemberFunctions(listOfNotNull(it), name, location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name) {
|
||||||
|
dispatchReceiver.receiverValue.type.memberScope.recordLookup(name, location)
|
||||||
|
dispatchReceiver.possibleTypes.forEach {
|
||||||
|
it.memberScope.recordLookup(name, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class QualifierScopeTowerLevel(scopeTower: ImplicitScopeTower, val qualifier: QualifierReceiver) : AbstractScopeTowerLevel(scopeTower) {
|
internal class QualifierScopeTowerLevel(scopeTower: ImplicitScopeTower, val qualifier: QualifierReceiver) : AbstractScopeTowerLevel(scopeTower) {
|
||||||
@@ -189,6 +196,8 @@ internal class QualifierScopeTowerLevel(scopeTower: ImplicitScopeTower, val qual
|
|||||||
qualifier.staticScope).map {
|
qualifier.staticScope).map {
|
||||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// KT-3335 Creating imported super class' inner class fails in codegen
|
// KT-3335 Creating imported super class' inner class fails in codegen
|
||||||
@@ -216,6 +225,10 @@ internal open class ScopeBasedTowerLevel protected constructor(
|
|||||||
resolutionScope).map {
|
resolutionScope).map {
|
||||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name) {
|
||||||
|
resolutionScope.recordLookup(name, location)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
internal class ImportingScopeBasedTowerLevel(
|
internal class ImportingScopeBasedTowerLevel(
|
||||||
scopeTower: ImplicitScopeTower,
|
scopeTower: ImplicitScopeTower,
|
||||||
@@ -247,6 +260,10 @@ internal class SyntheticScopeBasedTowerLevel(
|
|||||||
extensionReceiver: ReceiverValueWithSmartCastInfo?
|
extensionReceiver: ReceiverValueWithSmartCastInfo?
|
||||||
): Collection<CandidateWithBoundDispatchReceiver> =
|
): Collection<CandidateWithBoundDispatchReceiver> =
|
||||||
emptyList()
|
emptyList()
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class HidesMembersTowerLevel(scopeTower: ImplicitScopeTower): AbstractScopeTowerLevel(scopeTower) {
|
internal class HidesMembersTowerLevel(scopeTower: ImplicitScopeTower): AbstractScopeTowerLevel(scopeTower) {
|
||||||
@@ -272,6 +289,8 @@ internal class HidesMembersTowerLevel(scopeTower: ImplicitScopeTower): AbstractS
|
|||||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun recordLookup(name: Name) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinType.getClassifierFromMeAndSuperclasses(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
private fun KotlinType.getClassifierFromMeAndSuperclasses(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||||
|
|||||||
+174
-84
@@ -16,12 +16,18 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.resolve.calls.tower
|
package org.jetbrains.kotlin.resolve.calls.tower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
|
||||||
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.isDynamic
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface Candidate {
|
interface Candidate {
|
||||||
@@ -56,12 +62,16 @@ sealed class TowerData {
|
|||||||
class OnlyImplicitReceiver(val implicitReceiver: ReceiverValueWithSmartCastInfo): TowerData()
|
class OnlyImplicitReceiver(val implicitReceiver: ReceiverValueWithSmartCastInfo): TowerData()
|
||||||
class TowerLevel(val level: ScopeTowerLevel) : TowerData()
|
class TowerLevel(val level: ScopeTowerLevel) : TowerData()
|
||||||
class BothTowerLevelAndImplicitReceiver(val level: ScopeTowerLevel, val implicitReceiver: ReceiverValueWithSmartCastInfo) : TowerData()
|
class BothTowerLevelAndImplicitReceiver(val level: ScopeTowerLevel, val implicitReceiver: ReceiverValueWithSmartCastInfo) : TowerData()
|
||||||
|
// Has the same meaning as BothTowerLevelAndImplicitReceiver, but it's only used for names lookup, so it doesn't need implicit receiver
|
||||||
|
class ForLookupForNoExplicitReceiver(val level: ScopeTowerLevel) : TowerData()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScopeTowerProcessor<out C> {
|
interface ScopeTowerProcessor<out C> {
|
||||||
// Candidates with matched receivers (dispatch receiver was already matched in ScopeTowerLevel)
|
// Candidates with matched receivers (dispatch receiver was already matched in ScopeTowerLevel)
|
||||||
// Candidates in one groups have same priority, first group has highest priority.
|
// Candidates in one groups have same priority, first group has highest priority.
|
||||||
fun process(data: TowerData): List<Collection<C>>
|
fun process(data: TowerData): List<Collection<C>>
|
||||||
|
|
||||||
|
fun recordLookups(skippedData: Collection<TowerData>, name: Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SimpleScopeTowerProcessor<out C> : ScopeTowerProcessor<C> {
|
interface SimpleScopeTowerProcessor<out C> : ScopeTowerProcessor<C> {
|
||||||
@@ -74,103 +84,183 @@ class TowerResolver {
|
|||||||
fun <C: Candidate> runResolve(
|
fun <C: Candidate> runResolve(
|
||||||
scopeTower: ImplicitScopeTower,
|
scopeTower: ImplicitScopeTower,
|
||||||
processor: ScopeTowerProcessor<C>,
|
processor: ScopeTowerProcessor<C>,
|
||||||
useOrder: Boolean
|
useOrder: Boolean,
|
||||||
): Collection<C> = scopeTower.run(processor, SuccessfulResultCollector(), useOrder)
|
name: Name
|
||||||
|
): Collection<C> = scopeTower.run(processor, SuccessfulResultCollector(), useOrder, name)
|
||||||
|
|
||||||
fun <C: Candidate> collectAllCandidates(
|
fun <C: Candidate> collectAllCandidates(
|
||||||
scopeTower: ImplicitScopeTower,
|
scopeTower: ImplicitScopeTower,
|
||||||
processor: ScopeTowerProcessor<C>
|
processor: ScopeTowerProcessor<C>,
|
||||||
|
name: Name
|
||||||
): Collection<C>
|
): Collection<C>
|
||||||
= scopeTower.run(processor, AllCandidatesCollector(), false)
|
= scopeTower.run(processor, AllCandidatesCollector(), false, name)
|
||||||
|
|
||||||
private fun ImplicitScopeTower.createNonLocalLevels(): List<ScopeTowerLevel> {
|
fun <C : Candidate> ImplicitScopeTower.run(
|
||||||
val result = ArrayList<ScopeTowerLevel>()
|
|
||||||
|
|
||||||
lexicalScope.parentsWithSelf.forEach { scope ->
|
|
||||||
if (scope is LexicalScope) {
|
|
||||||
if (!scope.kind.withLocalDescriptors) result.add(ScopeBasedTowerLevel(this, scope))
|
|
||||||
|
|
||||||
getImplicitReceiver(scope)?.let { result.add(MemberScopeTowerLevel(this, it)) }
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result.add(ImportingScopeBasedTowerLevel(this, scope as ImportingScope))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <C : Candidate> ImplicitScopeTower.run(
|
|
||||||
processor: ScopeTowerProcessor<C>,
|
processor: ScopeTowerProcessor<C>,
|
||||||
resultCollector: ResultCollector<C>,
|
resultCollector: ResultCollector<C>,
|
||||||
useOrder: Boolean
|
useOrder: Boolean,
|
||||||
): Collection<C> {
|
name: Name
|
||||||
fun TowerData.process() = processTowerData(processor, resultCollector, useOrder, this)
|
): Collection<C> = Task(this, processor, resultCollector, useOrder, name).run()
|
||||||
|
|
||||||
val localLevels = lexicalScope.parentsWithSelf.
|
private inner class Task<out C : Candidate>(
|
||||||
filterIsInstance<LexicalScope>().filter { it.kind.withLocalDescriptors }.
|
private val implicitScopeTower: ImplicitScopeTower,
|
||||||
map { ScopeBasedTowerLevel(this@run, it) }
|
private val processor: ScopeTowerProcessor<C>,
|
||||||
|
private val resultCollector: ResultCollector<C>,
|
||||||
|
private val useOrder: Boolean,
|
||||||
|
private val name: Name
|
||||||
|
) {
|
||||||
|
private val isNameForHidesMember = name in HIDES_MEMBERS_NAME_LIST
|
||||||
|
private val skippedDataForLookup = mutableListOf<TowerData>()
|
||||||
|
|
||||||
// Lazy calculation
|
private val localLevels: Collection<ScopeTowerLevel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
var nonLocalLevels: Collection<ScopeTowerLevel>? = null
|
implicitScopeTower.lexicalScope.parentsWithSelf.
|
||||||
val hidesMembersLevel = HidesMembersTowerLevel(this)
|
filterIsInstance<LexicalScope>().filter { it.kind.withLocalDescriptors && it.mayFitForName(name) }.
|
||||||
val syntheticLevel = SyntheticScopeBasedTowerLevel(this, syntheticScopes)
|
map { ScopeBasedTowerLevel(implicitScopeTower, it) }.toList()
|
||||||
|
|
||||||
// hides members extensions for explicit receiver
|
|
||||||
TowerData.TowerLevel(hidesMembersLevel).process()?.let { return it }
|
|
||||||
// possibly there is explicit member
|
|
||||||
TowerData.Empty.process()?.let { return it }
|
|
||||||
// synthetic property for explicit receiver
|
|
||||||
TowerData.TowerLevel(syntheticLevel).process()?.let { return it }
|
|
||||||
|
|
||||||
// local non-extensions or extension for explicit receiver
|
|
||||||
for (localLevel in localLevels) {
|
|
||||||
TowerData.TowerLevel(localLevel).process()?.let { return it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (scope in lexicalScope.parentsWithSelf) {
|
private val nonLocalLevels: Collection<ScopeTowerLevel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
if (scope is LexicalScope) {
|
implicitScopeTower.createNonLocalLevels()
|
||||||
// statics
|
|
||||||
if (!scope.kind.withLocalDescriptors) {
|
|
||||||
TowerData.TowerLevel(ScopeBasedTowerLevel(this, scope)).process()?.let { return it }
|
|
||||||
}
|
|
||||||
|
|
||||||
val implicitReceiver = getImplicitReceiver(scope)
|
|
||||||
if (implicitReceiver != null) {
|
|
||||||
// hides members extensions
|
|
||||||
TowerData.BothTowerLevelAndImplicitReceiver(hidesMembersLevel, implicitReceiver).process()?.let { return it }
|
|
||||||
|
|
||||||
// members of implicit receiver or member extension for explicit receiver
|
|
||||||
TowerData.TowerLevel(MemberScopeTowerLevel(this, implicitReceiver)).process()?.let { return it }
|
|
||||||
|
|
||||||
// synthetic properties
|
|
||||||
TowerData.BothTowerLevelAndImplicitReceiver(syntheticLevel, implicitReceiver).process()?.let { return it }
|
|
||||||
|
|
||||||
// invokeExtension on local variable
|
|
||||||
TowerData.OnlyImplicitReceiver(implicitReceiver).process()?.let { return it }
|
|
||||||
|
|
||||||
// local extensions for implicit receiver
|
|
||||||
for (localLevel in localLevels) {
|
|
||||||
TowerData.BothTowerLevelAndImplicitReceiver(localLevel, implicitReceiver).process()?.let { return it }
|
|
||||||
}
|
|
||||||
|
|
||||||
// extension for implicit receiver
|
|
||||||
if (nonLocalLevels == null) {
|
|
||||||
nonLocalLevels = createNonLocalLevels()
|
|
||||||
}
|
|
||||||
|
|
||||||
for (nonLocalLevel in nonLocalLevels) {
|
|
||||||
TowerData.BothTowerLevelAndImplicitReceiver(nonLocalLevel, implicitReceiver).process()?.let { return it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// functions with no receiver or extension for explicit receiver
|
|
||||||
TowerData.TowerLevel(ImportingScopeBasedTowerLevel(this, scope as ImportingScope)).process()?.let { return it }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultCollector.getFinalCandidates()
|
val hidesMembersLevel = HidesMembersTowerLevel(implicitScopeTower)
|
||||||
|
val syntheticLevel = SyntheticScopeBasedTowerLevel(implicitScopeTower, implicitScopeTower.syntheticScopes)
|
||||||
|
|
||||||
|
private fun ImplicitScopeTower.createNonLocalLevels(): Collection<ScopeTowerLevel> {
|
||||||
|
val mainResult = mutableListOf<ScopeTowerLevel>()
|
||||||
|
|
||||||
|
fun addLevel(scopeTowerLevel: ScopeTowerLevel, mayFitForName: Boolean) {
|
||||||
|
if (mayFitForName) {
|
||||||
|
mainResult.add(scopeTowerLevel)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
skippedDataForLookup.add(TowerData.ForLookupForNoExplicitReceiver(scopeTowerLevel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lexicalScope.parentsWithSelf.forEach { scope ->
|
||||||
|
if (scope is LexicalScope) {
|
||||||
|
if (!scope.kind.withLocalDescriptors) {
|
||||||
|
addLevel(
|
||||||
|
ScopeBasedTowerLevel(this@createNonLocalLevels, scope),
|
||||||
|
scope.mayFitForName(name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getImplicitReceiver(scope)?.let {
|
||||||
|
addLevel(
|
||||||
|
MemberScopeTowerLevel(this@createNonLocalLevels, it),
|
||||||
|
it.mayFitForName(name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addLevel(
|
||||||
|
ImportingScopeBasedTowerLevel(this@createNonLocalLevels, scope as ImportingScope),
|
||||||
|
scope.mayFitForName(name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainResult
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TowerData.process() = processTowerData(processor, resultCollector, useOrder, this)?.also {
|
||||||
|
recordLookups()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TowerData.process(mayFitForName: Boolean): Collection<C>? {
|
||||||
|
if (!mayFitForName) {
|
||||||
|
skippedDataForLookup.add(this)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return process()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun run(): Collection<C> {
|
||||||
|
if (isNameForHidesMember) {
|
||||||
|
// hides members extensions for explicit receiver
|
||||||
|
TowerData.TowerLevel(hidesMembersLevel).process()?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
// possibly there is explicit member
|
||||||
|
TowerData.Empty.process()?.let { return it }
|
||||||
|
// synthetic property for explicit receiver
|
||||||
|
TowerData.TowerLevel(syntheticLevel).process()?.let { return it }
|
||||||
|
|
||||||
|
// local non-extensions or extension for explicit receiver
|
||||||
|
for (localLevel in localLevels) {
|
||||||
|
TowerData.TowerLevel(localLevel).process()?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
for (scope in implicitScopeTower.lexicalScope.parentsWithSelf) {
|
||||||
|
if (scope is LexicalScope) {
|
||||||
|
// statics
|
||||||
|
if (!scope.kind.withLocalDescriptors) {
|
||||||
|
TowerData.TowerLevel(ScopeBasedTowerLevel(implicitScopeTower, scope))
|
||||||
|
.process(scope.mayFitForName(name))?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitScopeTower.getImplicitReceiver(scope)
|
||||||
|
?.let(this::processImplicitReceiver)
|
||||||
|
?.let { return it }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TowerData.TowerLevel(ImportingScopeBasedTowerLevel(implicitScopeTower, scope as ImportingScope))
|
||||||
|
.process(scope.mayFitForName(name))?.let { return it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recordLookups()
|
||||||
|
|
||||||
|
return resultCollector.getFinalCandidates()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processImplicitReceiver(implicitReceiver: ReceiverValueWithSmartCastInfo): Collection<C>? {
|
||||||
|
if (isNameForHidesMember) {
|
||||||
|
// hides members extensions
|
||||||
|
TowerData.BothTowerLevelAndImplicitReceiver(hidesMembersLevel, implicitReceiver).process()?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
// members of implicit receiver or member extension for explicit receiver
|
||||||
|
TowerData.TowerLevel(MemberScopeTowerLevel(implicitScopeTower, implicitReceiver))
|
||||||
|
.process(implicitReceiver.mayFitForName(name))?.let { return it }
|
||||||
|
|
||||||
|
// synthetic properties
|
||||||
|
TowerData.BothTowerLevelAndImplicitReceiver(syntheticLevel, implicitReceiver).process()?.let { return it }
|
||||||
|
|
||||||
|
// invokeExtension on local variable
|
||||||
|
TowerData.OnlyImplicitReceiver(implicitReceiver).process()?.let { return it }
|
||||||
|
|
||||||
|
// local extensions for implicit receiver
|
||||||
|
for (localLevel in localLevels) {
|
||||||
|
TowerData.BothTowerLevelAndImplicitReceiver(localLevel, implicitReceiver).process()?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
// extension for implicit receiver
|
||||||
|
for (nonLocalLevel in nonLocalLevels) {
|
||||||
|
TowerData.BothTowerLevelAndImplicitReceiver(nonLocalLevel, implicitReceiver).process()?.let { return it }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recordLookups() {
|
||||||
|
processor.recordLookups(skippedDataForLookup, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ReceiverValueWithSmartCastInfo.mayFitForName(name: Name): Boolean {
|
||||||
|
if (receiverValue.type.mayFitForName(name)) return true
|
||||||
|
if (possibleTypes.isEmpty()) return false
|
||||||
|
return possibleTypes.any { it.mayFitForName(name) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KotlinType.mayFitForName(name: Name) =
|
||||||
|
isDynamic() ||
|
||||||
|
!memberScope.definitelyDoesNotContainName(name) ||
|
||||||
|
!memberScope.definitelyDoesNotContainName(OperatorNameConventions.INVOKE)
|
||||||
|
|
||||||
|
private fun ResolutionScope.mayFitForName(name: Name) =
|
||||||
|
!definitelyDoesNotContainName(name) || !definitelyDoesNotContainName(OperatorNameConventions.INVOKE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <C : Candidate> runWithEmptyTowerData(
|
fun <C : Candidate> runWithEmptyTowerData(
|
||||||
|
|||||||
@@ -124,4 +124,7 @@ abstract class LexicalScopeStorage(
|
|||||||
} while (rest != null)
|
} while (rest != null)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun definitelyDoesNotContainName(name: Name) =
|
||||||
|
functionsByName?.get(name) == null && variablesAndClassifiersByName?.get(name) == null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ interface LexicalScope : HierarchicalScope {
|
|||||||
override val kind: LexicalScopeKind
|
override val kind: LexicalScopeKind
|
||||||
get() = LexicalScopeKind.EMPTY
|
get() = LexicalScopeKind.EMPTY
|
||||||
|
|
||||||
|
override fun definitelyDoesNotContainName(name: Name) = true
|
||||||
|
|
||||||
override fun printStructure(p: Printer) {
|
override fun printStructure(p: Printer) {
|
||||||
p.println("Base lexical scope with owner = $ownerDescriptor and parent = $parent")
|
p.println("Base lexical scope with owner = $ownerDescriptor and parent = $parent")
|
||||||
}
|
}
|
||||||
@@ -119,10 +121,16 @@ interface ImportingScope : HierarchicalScope {
|
|||||||
return getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased = false)
|
return getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun computeImportedNames(): Set<Name>?
|
||||||
|
|
||||||
object Empty : BaseImportingScope(null) {
|
object Empty : BaseImportingScope(null) {
|
||||||
override fun printStructure(p: Printer) {
|
override fun printStructure(p: Printer) {
|
||||||
p.println("ImportingScope.Empty")
|
p.println("ImportingScope.Empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun computeImportedNames() = emptySet<Name>()
|
||||||
|
|
||||||
|
override fun definitelyDoesNotContainName(name: Name) = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -48,4 +48,5 @@ class SubpackagesImportingScope(
|
|||||||
//TODO: kept old behavior, but it seems very strange (super call seems more applicable)
|
//TODO: kept old behavior, but it seems very strange (super call seems more applicable)
|
||||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, changeNamesForAliased: Boolean): Collection<DeclarationDescriptor>
|
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, changeNamesForAliased: Boolean): Collection<DeclarationDescriptor>
|
||||||
= emptyList()
|
= emptyList()
|
||||||
|
override fun computeImportedNames() = emptySet<Name>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,8 @@ private class MemberScopeToImportingScopeAdapter(override val parent: ImportingS
|
|||||||
|
|
||||||
override fun toString() = "${this::class.java.simpleName} for $memberScope"
|
override fun toString() = "${this::class.java.simpleName} for $memberScope"
|
||||||
|
|
||||||
|
override fun computeImportedNames() = memberScope.computeAllNames()
|
||||||
|
|
||||||
override fun printStructure(p: Printer) {
|
override fun printStructure(p: Printer) {
|
||||||
p.println(this::class.java.simpleName)
|
p.println(this::class.java.simpleName)
|
||||||
p.pushIndent()
|
p.pushIndent()
|
||||||
|
|||||||
+40
-40
@@ -38,66 +38,66 @@ fun foo(dir: Direction): Int {
|
|||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
v(dir: Direction) INIT: in: {} out: {dir=D}
|
v(dir: Direction) INIT: in: {} out: {}
|
||||||
magic[FAKE_INITIALIZER](dir: Direction) -> <v0> INIT: in: {dir=D} out: {dir=D}
|
magic[FAKE_INITIALIZER](dir: Direction) -> <v0> INIT: in: {} out: {}
|
||||||
w(dir|<v0>) INIT: in: {dir=D} out: {dir=ID} USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ}
|
w(dir|<v0>) INIT: in: {} out: {} USE: in: {} out: {}
|
||||||
2 mark({ val res: Int when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 } return res }) INIT: in: {dir=ID} out: {dir=ID}
|
2 mark({ val res: Int when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 } return res }) INIT: in: {} out: {}
|
||||||
v(val res: Int) INIT: in: {dir=ID} out: {dir=ID, res=D}
|
v(val res: Int) INIT: in: {} out: {res=D}
|
||||||
mark(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ, res=READ}
|
mark(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) INIT: in: {res=D} out: {res=D}
|
||||||
r(dir) -> <v1> USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ, res=READ}
|
r(dir) -> <v1>
|
||||||
mark(Direction.NORTH -> res = 1)
|
mark(Direction.NORTH -> res = 1)
|
||||||
mark(Direction.NORTH)
|
mark(Direction.NORTH)
|
||||||
mark(Direction.NORTH) USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ}
|
mark(Direction.NORTH)
|
||||||
r(NORTH) -> <v2> USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ}
|
r(NORTH) -> <v2>
|
||||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.NORTH|<v1>, <v2>) -> <v3>
|
magic[EQUALS_IN_WHEN_CONDITION](Direction.NORTH|<v1>, <v2>) -> <v3>
|
||||||
jmp?(L4|<v3>) USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ}
|
jmp?(L4|<v3>) USE: in: {res=READ} out: {res=READ}
|
||||||
L3 ['when' entry body]:
|
L3 ['when' entry body]:
|
||||||
r(1) -> <v4> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
r(1) -> <v4> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
w(res|<v4>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
w(res|<v4>) INIT: in: {res=D} out: {res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
jmp(L2) INIT: in: {res=ID} out: {res=ID}
|
||||||
L4 [next 'when' entry]:
|
L4 [next 'when' entry]:
|
||||||
mark(Direction.SOUTH -> res = 2) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D}
|
mark(Direction.SOUTH -> res = 2) INIT: in: {res=D} out: {res=D}
|
||||||
mark(Direction.SOUTH)
|
mark(Direction.SOUTH)
|
||||||
mark(Direction.SOUTH) USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ}
|
mark(Direction.SOUTH)
|
||||||
r(SOUTH) -> <v5> USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ}
|
r(SOUTH) -> <v5>
|
||||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.SOUTH|<v1>, <v5>) -> <v6>
|
magic[EQUALS_IN_WHEN_CONDITION](Direction.SOUTH|<v1>, <v5>) -> <v6>
|
||||||
jmp?(L6|<v6>) USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ}
|
jmp?(L6|<v6>) USE: in: {res=READ} out: {res=READ}
|
||||||
L5 ['when' entry body]:
|
L5 ['when' entry body]:
|
||||||
r(2) -> <v7> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
r(2) -> <v7> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
w(res|<v7>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
w(res|<v7>) INIT: in: {res=D} out: {res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
jmp(L2) INIT: in: {res=ID} out: {res=ID}
|
||||||
L6 [next 'when' entry]:
|
L6 [next 'when' entry]:
|
||||||
mark(Direction.WEST -> res = 3) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D}
|
mark(Direction.WEST -> res = 3) INIT: in: {res=D} out: {res=D}
|
||||||
mark(Direction.WEST)
|
mark(Direction.WEST)
|
||||||
mark(Direction.WEST) USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ}
|
mark(Direction.WEST)
|
||||||
r(WEST) -> <v8> USE: in: {EAST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ}
|
r(WEST) -> <v8>
|
||||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.WEST|<v1>, <v8>) -> <v9>
|
magic[EQUALS_IN_WHEN_CONDITION](Direction.WEST|<v1>, <v8>) -> <v9>
|
||||||
jmp?(L8|<v9>) USE: in: {EAST=READ, res=READ} out: {EAST=READ, res=READ}
|
jmp?(L8|<v9>) USE: in: {res=READ} out: {res=READ}
|
||||||
L7 ['when' entry body]:
|
L7 ['when' entry body]:
|
||||||
r(3) -> <v10> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
r(3) -> <v10> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
w(res|<v10>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
w(res|<v10>) INIT: in: {res=D} out: {res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
jmp(L2) INIT: in: {res=ID} out: {res=ID}
|
||||||
L8 [next 'when' entry]:
|
L8 [next 'when' entry]:
|
||||||
mark(Direction.EAST -> res = 4) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D}
|
mark(Direction.EAST -> res = 4) INIT: in: {res=D} out: {res=D}
|
||||||
mark(Direction.EAST)
|
mark(Direction.EAST)
|
||||||
mark(Direction.EAST) USE: in: {EAST=READ, res=READ} out: {EAST=READ, res=READ}
|
mark(Direction.EAST)
|
||||||
r(EAST) -> <v11> USE: in: {res=READ} out: {EAST=READ, res=READ}
|
r(EAST) -> <v11>
|
||||||
magic[EQUALS_IN_WHEN_CONDITION](Direction.EAST|<v1>, <v11>) -> <v12>
|
magic[EQUALS_IN_WHEN_CONDITION](Direction.EAST|<v1>, <v11>) -> <v12>
|
||||||
jmp?(L10|<v12>) USE: in: {res=READ} out: {res=READ}
|
jmp?(L10|<v12>) USE: in: {res=READ} out: {res=READ}
|
||||||
L9 ['when' entry body]:
|
L9 ['when' entry body]:
|
||||||
r(4) -> <v13> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
r(4) -> <v13> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
w(res|<v13>) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
w(res|<v13>) INIT: in: {res=D} out: {res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ}
|
||||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID}
|
jmp(L2) INIT: in: {res=ID} out: {res=ID}
|
||||||
L10 [next 'when' entry]:
|
L10 [next 'when' entry]:
|
||||||
magic[EXHAUSTIVE_WHEN_ELSE](when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) -> <v14> INIT: in: {dir=ID, res=D} out: {dir=ID, res=IED}
|
magic[EXHAUSTIVE_WHEN_ELSE](when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) -> <v14> INIT: in: {res=D} out: {res=IED}
|
||||||
L2 [after 'when' expression]:
|
L2 [after 'when' expression]:
|
||||||
merge(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }|!<v15>, !<v16>, !<v17>, !<v18>) -> <v19> INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
merge(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }|!<v15>, !<v16>, !<v17>, !<v18>) -> <v19> INIT: in: {res=ID} out: {res=ID} USE: in: {res=READ} out: {res=READ}
|
||||||
r(res) -> <v20> USE: in: {} out: {res=READ}
|
r(res) -> <v20> USE: in: {} out: {res=READ}
|
||||||
ret(*|<v20>) L1
|
ret(*|<v20>) L1
|
||||||
L1:
|
L1:
|
||||||
1 <END> INIT: in: {dir=ID} out: {dir=ID}
|
1 <END> INIT: in: {} out: {}
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {dir=I?} out: {dir=I?} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
|
|||||||
@@ -42,15 +42,15 @@ fun use(vararg a: Any?) = a
|
|||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
v(vararg a: Any?) INIT: in: {} out: {a=D}
|
v(vararg a: Any?) INIT: in: {} out: {}
|
||||||
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
|
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {} out: {}
|
||||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
|
w(a|<v0>) INIT: in: {} out: {}
|
||||||
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
|
r(a) -> <v1> INIT: in: {} out: {}
|
||||||
ret(*|<v1>) L1
|
ret(*|<v1>) L1
|
||||||
L1:
|
L1:
|
||||||
<END>
|
<END>
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
+28
-28
@@ -8,24 +8,24 @@ fun foo() {
|
|||||||
}
|
}
|
||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {} USE: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
2 mark({ val a = 1 val f = { x: Int -> val y = x + a use(a) } })
|
2 mark({ val a = 1 val f = { x: Int -> val y = x + a use(a) } })
|
||||||
v(val a = 1) INIT: in: {} out: {a=D}
|
v(val a = 1) INIT: in: {} out: {}
|
||||||
r(1) -> <v0> INIT: in: {a=D} out: {a=D}
|
r(1) -> <v0> INIT: in: {} out: {}
|
||||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID}
|
w(a|<v0>) INIT: in: {} out: {}
|
||||||
v(val f = { x: Int -> val y = x + a use(a) }) INIT: in: {a=ID} out: {a=ID, f=D}
|
v(val f = { x: Int -> val y = x + a use(a) }) INIT: in: {} out: {}
|
||||||
mark({ x: Int -> val y = x + a use(a) }) INIT: in: {a=ID, f=D} out: {a=ID, f=D}
|
mark({ x: Int -> val y = x + a use(a) }) INIT: in: {} out: {}
|
||||||
jmp?(L2)
|
jmp?(L2)
|
||||||
d({ x: Int -> val y = x + a use(a) }) INIT: in: {a=I, f=I?} out: {a=I, f=I?} USE: in: {a=READ} out: {a=READ}
|
d({ x: Int -> val y = x + a use(a) }) INIT: in: {} out: {}
|
||||||
L2 [after local declaration]:
|
L2 [after local declaration]:
|
||||||
r({ x: Int -> val y = x + a use(a) }) -> <v1> INIT: in: {a=ID, f=D} out: {a=ID, f=D}
|
r({ x: Int -> val y = x + a use(a) }) -> <v1>
|
||||||
w(f|<v1>) INIT: in: {a=ID, f=D} out: {a=ID, f=ID}
|
w(f|<v1>) INIT: in: {} out: {}
|
||||||
L1:
|
L1:
|
||||||
1 <END> INIT: in: {} out: {}
|
1 <END> INIT: in: {} out: {}
|
||||||
error:
|
error:
|
||||||
<ERROR>
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
== anonymous_0 ==
|
== anonymous_0 ==
|
||||||
{ x: Int ->
|
{ x: Int ->
|
||||||
@@ -34,42 +34,42 @@ sink:
|
|||||||
}
|
}
|
||||||
---------------------
|
---------------------
|
||||||
L3:
|
L3:
|
||||||
3 <START> INIT: in: {a=ID, f=D} out: {a=ID, f=D}
|
3 <START> INIT: in: {} out: {}
|
||||||
v(x: Int) INIT: in: {a=ID, f=D} out: {a=ID, f=D, x=D}
|
v(x: Int) INIT: in: {} out: {}
|
||||||
magic[FAKE_INITIALIZER](x: Int) -> <v0> INIT: in: {a=ID, f=D, x=D} out: {a=ID, f=D, x=D}
|
magic[FAKE_INITIALIZER](x: Int) -> <v0> INIT: in: {} out: {}
|
||||||
w(x|<v0>) INIT: in: {a=ID, f=D, x=D} out: {a=ID, f=D, x=ID}
|
w(x|<v0>) INIT: in: {} out: {}
|
||||||
4 mark(val y = x + a use(a)) INIT: in: {a=ID, f=D, x=ID} out: {a=ID, f=D, x=ID}
|
4 mark(val y = x + a use(a)) INIT: in: {} out: {}
|
||||||
v(val y = x + a) INIT: in: {a=ID, f=D, x=ID} out: {a=ID, f=D, x=ID, y=D} USE: in: {a=READ, x=READ} out: {a=READ, x=READ}
|
v(val y = x + a) INIT: in: {} out: {}
|
||||||
r(x) -> <v1> INIT: in: {a=ID, f=D, x=ID, y=D} out: {a=ID, f=D, x=ID, y=D} USE: in: {a=READ} out: {a=READ, x=READ}
|
r(x) -> <v1> INIT: in: {} out: {}
|
||||||
r(a) -> <v2>
|
r(a) -> <v2>
|
||||||
mark(x + a)
|
mark(x + a)
|
||||||
call(x + a, plus|<v1>, <v2>) -> <v3>
|
call(x + a, plus|<v1>, <v2>) -> <v3>
|
||||||
w(y|<v3>) INIT: in: {a=ID, f=D, x=ID, y=D} out: {a=ID, f=D, x=ID, y=ID} USE: in: {a=READ} out: {a=READ}
|
w(y|<v3>) INIT: in: {} out: {}
|
||||||
r(a) -> <v4> INIT: in: {a=ID, f=D, x=ID, y=ID} out: {a=ID, f=D, x=ID, y=ID} USE: in: {} out: {a=READ}
|
r(a) -> <v4> INIT: in: {} out: {}
|
||||||
mark(use(a))
|
mark(use(a))
|
||||||
call(use(a), use|<v4>) -> <v5>
|
call(use(a), use|<v4>) -> <v5>
|
||||||
3 ret(*|<v5>) L4 INIT: in: {a=ID, f=D, x=ID} out: {a=ID, f=D, x=ID}
|
3 ret(*|<v5>) L4
|
||||||
L4:
|
L4:
|
||||||
<END>
|
<END>
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {a=I, f=I?, x=I?} out: {a=I, f=I?, x=I?} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
== use ==
|
== use ==
|
||||||
fun use(vararg a: Any?) = a
|
fun use(vararg a: Any?) = a
|
||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
v(vararg a: Any?) INIT: in: {} out: {a=D}
|
v(vararg a: Any?) INIT: in: {} out: {}
|
||||||
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
|
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {} out: {}
|
||||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
|
w(a|<v0>) INIT: in: {} out: {}
|
||||||
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
|
r(a) -> <v1> INIT: in: {} out: {}
|
||||||
ret(*|<v1>) L1
|
ret(*|<v1>) L1
|
||||||
L1:
|
L1:
|
||||||
<END>
|
<END>
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
+6
-6
@@ -3,19 +3,19 @@ fun bar(f: () -> Unit) = f()
|
|||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
v(f: () -> Unit) INIT: in: {} out: {f=D}
|
v(f: () -> Unit) INIT: in: {} out: {}
|
||||||
magic[FAKE_INITIALIZER](f: () -> Unit) -> <v0> INIT: in: {f=D} out: {f=D}
|
magic[FAKE_INITIALIZER](f: () -> Unit) -> <v0> INIT: in: {} out: {}
|
||||||
w(f|<v0>) INIT: in: {f=D} out: {f=ID} USE: in: {f=READ} out: {f=READ}
|
w(f|<v0>) INIT: in: {} out: {}
|
||||||
r(f) -> <v1> INIT: in: {f=ID} out: {f=ID} USE: in: {} out: {f=READ}
|
r(f) -> <v1> INIT: in: {} out: {}
|
||||||
mark(f())
|
mark(f())
|
||||||
call(f(), invoke|<v1>) -> <v2>
|
call(f(), invoke|<v1>) -> <v2>
|
||||||
ret(*|<v2>) L1
|
ret(*|<v2>) L1
|
||||||
L1:
|
L1:
|
||||||
<END>
|
<END>
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {f=I?} out: {f=I?} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
== foo ==
|
== foo ==
|
||||||
fun foo() {
|
fun foo() {
|
||||||
|
|||||||
+24
-24
@@ -7,21 +7,21 @@ fun foo() {
|
|||||||
}
|
}
|
||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {} USE: in: {} out: {}
|
1 <START> INIT: in: {} out: {} USE: in: {} out: {}
|
||||||
2 mark({ val a = 1 val b: Int b = 2 42 })
|
2 mark({ val a = 1 val b: Int b = 2 42 })
|
||||||
v(val a = 1) INIT: in: {} out: {a=D}
|
v(val a = 1) INIT: in: {} out: {}
|
||||||
r(1) -> <v0> INIT: in: {a=D} out: {a=D}
|
r(1) -> <v0> INIT: in: {} out: {}
|
||||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID}
|
w(a|<v0>) INIT: in: {} out: {}
|
||||||
v(val b: Int) INIT: in: {a=ID} out: {a=ID, b=D}
|
v(val b: Int) INIT: in: {} out: {b=D}
|
||||||
r(2) -> <v1> INIT: in: {a=ID, b=D} out: {a=ID, b=D} USE: in: {b=ONLY_WRITTEN_NEVER_READ} out: {b=ONLY_WRITTEN_NEVER_READ}
|
r(2) -> <v1> INIT: in: {b=D} out: {b=D} USE: in: {b=ONLY_WRITTEN_NEVER_READ} out: {b=ONLY_WRITTEN_NEVER_READ}
|
||||||
w(b|<v1>) INIT: in: {a=ID, b=D} out: {a=ID, b=ID} USE: in: {} out: {b=ONLY_WRITTEN_NEVER_READ}
|
w(b|<v1>) INIT: in: {b=D} out: {b=ID} USE: in: {} out: {b=ONLY_WRITTEN_NEVER_READ}
|
||||||
r(42) -> <v2> INIT: in: {a=ID, b=ID} out: {a=ID, b=ID}
|
r(42) -> <v2> INIT: in: {b=ID} out: {b=ID}
|
||||||
L1:
|
L1:
|
||||||
1 <END> INIT: in: {} out: {}
|
1 <END> INIT: in: {} out: {}
|
||||||
error:
|
error:
|
||||||
<ERROR>
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
== bar ==
|
== bar ==
|
||||||
fun bar(foo: Foo) {
|
fun bar(foo: Foo) {
|
||||||
@@ -32,23 +32,23 @@ fun bar(foo: Foo) {
|
|||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
v(foo: Foo) INIT: in: {} out: {foo=D}
|
v(foo: Foo) INIT: in: {} out: {}
|
||||||
magic[FAKE_INITIALIZER](foo: Foo) -> <v0> INIT: in: {foo=D} out: {foo=D}
|
magic[FAKE_INITIALIZER](foo: Foo) -> <v0> INIT: in: {} out: {}
|
||||||
w(foo|<v0>) INIT: in: {foo=D} out: {foo=ID}
|
w(foo|<v0>) INIT: in: {} out: {}
|
||||||
2 mark({ foo.c foo.c = 2 42 }) INIT: in: {foo=ID} out: {foo=ID}
|
2 mark({ foo.c foo.c = 2 42 }) INIT: in: {} out: {}
|
||||||
mark(foo.c)
|
mark(foo.c)
|
||||||
r(foo) -> <v1> USE: in: {c=READ, foo=READ} out: {c=READ, foo=READ}
|
r(foo) -> <v1>
|
||||||
r(c|<v1>) -> <v2> USE: in: {c=ONLY_WRITTEN_NEVER_READ, foo=READ} out: {c=READ, foo=READ}
|
r(c|<v1>) -> <v2>
|
||||||
r(foo) -> <v3> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ, foo=READ}
|
r(foo) -> <v3>
|
||||||
r(2) -> <v4> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ}
|
r(2) -> <v4>
|
||||||
w(foo.c|<v3>, <v4>) USE: in: {} out: {c=ONLY_WRITTEN_NEVER_READ}
|
w(foo.c|<v3>, <v4>)
|
||||||
r(42) -> <v5>
|
r(42) -> <v5>
|
||||||
L1:
|
L1:
|
||||||
1 <END>
|
1 <END>
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {foo=I?} out: {foo=I?} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
== Foo ==
|
== Foo ==
|
||||||
interface Foo {
|
interface Foo {
|
||||||
@@ -57,11 +57,11 @@ interface Foo {
|
|||||||
---------------------
|
---------------------
|
||||||
L0:
|
L0:
|
||||||
1 <START> INIT: in: {} out: {}
|
1 <START> INIT: in: {} out: {}
|
||||||
v(var c: Int) INIT: in: {} out: {c=D}
|
v(var c: Int) INIT: in: {} out: {}
|
||||||
L1:
|
L1:
|
||||||
<END> INIT: in: {c=D} out: {c=D}
|
<END> INIT: in: {} out: {}
|
||||||
error:
|
error:
|
||||||
<ERROR> INIT: in: {} out: {}
|
<ERROR>
|
||||||
sink:
|
sink:
|
||||||
<SINK> INIT: in: {c=-} out: {c=-} USE: in: {} out: {}
|
<SINK> USE: in: {} out: {}
|
||||||
=====================
|
=====================
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user