Merge branch 'master' into KT-18765

This commit is contained in:
Andrey Mischenko
2017-10-04 17:43:11 +08:00
committed by GitHub
1030 changed files with 14609 additions and 3982 deletions
+7 -1
View File
@@ -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
View File
@@ -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>
+10
View File
@@ -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>
+8 -3
View File
@@ -1,6 +1,6 @@
[![official project](http://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![official project](http://jb.gg/badges/official.svg)](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>
[![TeamCity (simple build status)](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/bt345.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=bt345&branch_Kotlin=%3Cdefault%3E&tab=buildTypeStatusDiv) [![TeamCity (simple build status)](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/Kotlin_dev_Compiler.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_dev_Compiler&branch_Kotlin_dev=%3Cdefault%3E&tab=buildTypeStatusDiv)
[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlin/kotlin-maven-plugin.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jetbrains.kotlin%22) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlin/kotlin-maven-plugin.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jetbrains.kotlin%22)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](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
+2
View File
@@ -24,6 +24,8 @@ sourceSets {
} }
runtimeJar() runtimeJar()
sourcesJar()
javadocJar()
testsJar() testsJar()
@@ -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
View File
@@ -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"
+170
View File
@@ -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")
}
}
}
+2 -1
View File
@@ -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) {
@@ -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()))
} }
} }
@@ -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)
}
} }
} }
@@ -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;
@@ -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(),
@@ -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)
}
@@ -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() })
}
} }
@@ -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
View File
@@ -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")
} }
@@ -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(
@@ -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?
@@ -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()
} }
@@ -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(
@@ -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) {
@@ -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
} }
} }
} }
@@ -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
@@ -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
@@ -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");
@@ -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
@@ -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)
@@ -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))
} }
} }
@@ -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()) {
@@ -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
} }
@@ -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)
@@ -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() }
} }
@@ -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>
} }
@@ -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
@@ -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
} }
@@ -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)
}
} }
@@ -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
}
}
@@ -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 {
@@ -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"
@@ -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,
@@ -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)
} }
@@ -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 {
@@ -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
@@ -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? {
@@ -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
} }
} }
@@ -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()
@@ -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: {}
===================== =====================
@@ -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: {}
===================== =====================
@@ -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() {
@@ -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