Merge branch 'master' into KT-18765
This commit is contained in:
Generated
+7
-1
@@ -52,7 +52,13 @@
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
<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 name="WebServicesPlugin" addRequiredLibraries="true" />
|
||||
<component name="com.sixrr.metrics.MetricsReloaded">
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Generate Tests" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$/generators" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value="generateTests" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
</ExternalSystemSettings>
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
||||
Generated
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProtobufSettings">
|
||||
<option name="includePaths">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,6 +1,6 @@
|
||||
[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||
<a href="http://slack.kotlinlang.org/"><img src="http://slack.kotlinlang.org/badge.svg" height="20"></a>
|
||||
[](https://teamcity.jetbrains.com/viewType.html?buildTypeId=bt345&branch_Kotlin=%3Cdefault%3E&tab=buildTypeStatusDiv)
|
||||
[](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_dev_Compiler&branch_Kotlin_dev=%3Cdefault%3E&tab=buildTypeStatusDiv)
|
||||
[](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jetbrains.kotlin%22)
|
||||
[](http://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.
|
||||
|
||||
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).
|
||||
|
||||
## 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/).
|
||||
|
||||
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
|
||||
`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.
|
||||
|
||||
### 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
|
||||
|
||||
The best way to submit a patch is to [fork the project on github](https://help.github.com/articles/fork-a-repo/) then send us a
|
||||
|
||||
@@ -24,6 +24,8 @@ sourceSets {
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
|
||||
testsJar()
|
||||
|
||||
|
||||
+42
-4
@@ -211,6 +211,22 @@ public final class DebugJsAstProtoBuf {
|
||||
* <code>UNBOX_CHAR = 4;</code>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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; }
|
||||
@@ -239,6 +271,10 @@ public final class DebugJsAstProtoBuf {
|
||||
case 2: return WRAP_FUNCTION;
|
||||
case 3: return TO_BOXED_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;
|
||||
}
|
||||
}
|
||||
@@ -49361,10 +49397,12 @@ public final class DebugJsAstProtoBuf {
|
||||
"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" +
|
||||
"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" +
|
||||
"\022\032\n\026DEFINE_INLINE_FUNCTION\020\001\022\021\n\rWRAP_FUN" +
|
||||
"CTION\020\002\022\021\n\rTO_BOXED_CHAR\020\003\022\016\n\nUNBOX_CHAR" +
|
||||
"\020\004B\024B\022DebugJsAstProtoBuf"
|
||||
"ACE\020\001\022\016\n\nNOT_INLINE\020\002*\275\001\n\017SpecialFunctio" +
|
||||
"n\022\032\n\026DEFINE_INLINE_FUNCTION\020\001\022\021\n\rWRAP_FU" +
|
||||
"NCTION\020\002\022\021\n\rTO_BOXED_CHAR\020\003\022\016\n\nUNBOX_CHA" +
|
||||
"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 =
|
||||
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
|
||||
|
||||
+2
-3
@@ -6,11 +6,11 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
|
||||
buildscript {
|
||||
val repos = listOf(
|
||||
val repos = listOfNotNull(
|
||||
System.getProperty("bootstrap.kotlin.repo"),
|
||||
"https://repo.gradle.org/gradle/repo",
|
||||
"https://plugins.gradle.org/m2",
|
||||
"http://repository.jetbrains.com/utils/").filterNotNull()
|
||||
"http://repository.jetbrains.com/utils/")
|
||||
|
||||
extra["bootstrapKotlinVersion"] = bootstrapKotlinVersion
|
||||
|
||||
@@ -105,7 +105,6 @@ extra["JDK_9"] = jdkPathIfFound("9")
|
||||
extra["versions.protobuf-java"] = "2.6.1"
|
||||
extra["versions.javax.inject"] = "1"
|
||||
extra["versions.jsr305"] = "1.3.9"
|
||||
extra["versions.cli-parser"] = "1.1.2"
|
||||
extra["versions.jansi"] = "1.16"
|
||||
extra["versions.jline"] = "3.3.1"
|
||||
extra["versions.junit"] = "4.12"
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.plugins.ExtensionAware
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import java.io.File
|
||||
|
||||
fun Project.configureInstrumentation() {
|
||||
plugins.matching { it::class.java.canonicalName.startsWith("org.jetbrains.kotlin.gradle.plugin") }.all {
|
||||
// When we change the output classes directory, Gradle will automatically configure
|
||||
// the test compile tasks to use the instrumented classes. Normally this is fine,
|
||||
// however, it causes problems for Kotlin projects:
|
||||
|
||||
// The "internal" modifier can be used to restrict access to the same module.
|
||||
// To make it possible to use internal methods from the main source set in test classes,
|
||||
// the Kotlin Gradle plugin adds the original output directory of the Java task
|
||||
// as "friendly directory" which makes it possible to access internal members
|
||||
// of the main module. Also this directory should be available on classpath during compilation
|
||||
|
||||
// This fails when we change the classes dir. The easiest fix is to prepend the
|
||||
// classes from the "friendly directory" to the compile classpath.
|
||||
val testCompile = tasks.findByName("compileTestKotlin") as AbstractCompile?
|
||||
testCompile?.doFirst {
|
||||
val mainSourceSet = the<JavaPluginConvention>().sourceSets.getByName("main")
|
||||
testCompile.classpath = (testCompile.classpath
|
||||
- mainSourceSet.output.classesDirs
|
||||
+ files((mainSourceSet as ExtensionAware).extra.get("classesDirsCopy")))
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
the<JavaPluginConvention>().sourceSets.all { sourceSetParam ->
|
||||
// This copy will ignore filters, but they are unlikely to be used.
|
||||
val classesDirs = (sourceSetParam.output.classesDirs as ConfigurableFileCollection).from as Collection<Any>
|
||||
|
||||
val classesDirsCopy = project.files(classesDirs.toTypedArray()).filter { it.exists() }
|
||||
(sourceSetParam as ExtensionAware).extra.set("classesDirsCopy", classesDirsCopy)
|
||||
|
||||
logger.info("Saving old sources dir for project ${project.name}")
|
||||
val instrumentedClassesDir = File(project.buildDir, "classes/${sourceSetParam.name}-instrumented")
|
||||
(sourceSetParam.output.classesDirs as ConfigurableFileCollection).setFrom(instrumentedClassesDir)
|
||||
val instrumentTask = project.tasks.create(sourceSetParam.getTaskName("instrument", "classes"), IntelliJInstrumentCodeTask::class.java)
|
||||
instrumentTask.apply {
|
||||
dependsOn(sourceSetParam.classesTaskName).onlyIf { !classesDirsCopy.isEmpty }
|
||||
sourceSet = sourceSetParam
|
||||
originalClassesDirs = classesDirsCopy
|
||||
output = instrumentedClassesDir
|
||||
}
|
||||
|
||||
instrumentTask.outputs.dir(instrumentedClassesDir)
|
||||
// Ensure that our task is invoked when the source set is built
|
||||
sourceSetParam.compiledBy(instrumentTask)
|
||||
@Suppress("UNUSED_EXPRESSION")
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
open class IntelliJInstrumentCodeTask : ConventionTask() {
|
||||
companion object {
|
||||
private const val FILTER_ANNOTATION_REGEXP_CLASS = "com.intellij.ant.ClassFilterAnnotationRegexp"
|
||||
private const val LOADER_REF = "java2.loader"
|
||||
}
|
||||
|
||||
var sourceSet: SourceSet? = null
|
||||
|
||||
@Input
|
||||
var originalClassesDirs: FileCollection? = null
|
||||
|
||||
@get:InputFiles
|
||||
val sourceDirs: FileCollection
|
||||
get() = project.files(sourceSet!!.allSource.srcDirs.filter { !sourceSet!!.resources.contains(it) && it.exists() })
|
||||
|
||||
@get:OutputDirectory
|
||||
var output: File? = null
|
||||
|
||||
@TaskAction
|
||||
fun instrumentClasses() {
|
||||
logger.info("input files are: ${originalClassesDirs?.joinToString("; ", transform = { "'${it.name}'${if (it.exists()) "" else " (does not exists)" }"})}")
|
||||
copyOriginalClasses()
|
||||
|
||||
val classpath = project.ideaSdkDeps("javac2.jar", "jdom.jar", "asm-all.jar", "jgoodies-forms.jar")
|
||||
|
||||
ant.withGroovyBuilder {
|
||||
"taskdef"("name" to "instrumentIdeaExtensions",
|
||||
"classpath" to classpath.asPath,
|
||||
"loaderref" to LOADER_REF,
|
||||
"classname" to "com.intellij.ant.InstrumentIdeaExtensions")
|
||||
}
|
||||
|
||||
logger.info("Compiling forms and instrumenting code with nullability preconditions")
|
||||
val instrumentNotNull = prepareNotNullInstrumenting(classpath)
|
||||
instrumentCode(sourceDirs, instrumentNotNull)
|
||||
}
|
||||
|
||||
private fun copyOriginalClasses() {
|
||||
project.copy {
|
||||
from(originalClassesDirs)
|
||||
into(output)
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareNotNullInstrumenting(classpath: ConfigurableFileCollection): Boolean {
|
||||
ant.withGroovyBuilder {
|
||||
"typedef"("name" to "skip",
|
||||
"classpath" to classpath.asPath,
|
||||
"loaderref" to LOADER_REF,
|
||||
"classname" to FILTER_ANNOTATION_REGEXP_CLASS)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun instrumentCode(srcDirs: FileCollection, instrumentNotNull: Boolean) {
|
||||
val headlessOldValue = System.setProperty("java.awt.headless", "true")
|
||||
|
||||
// Instrumentation needs to have access to sources of forms for inclusion
|
||||
val depSourceDirectorySets = project.configurations["compile"].dependencies.withType(ProjectDependency::class.java)
|
||||
.map { p -> p.dependencyProject.the<JavaPluginConvention>().sourceSets.getByName("main").allSource.sourceDirectories }
|
||||
val instrumentationClasspath =
|
||||
depSourceDirectorySets.fold(sourceSet!!.compileClasspath) { acc, v -> acc + v }.asPath.also {
|
||||
logger.info("Using following dependency source dirs: $it")
|
||||
}
|
||||
|
||||
logger.info("Running instrumentIdeaExtensions with srcdir=${srcDirs.asPath}}, destdir=$output and classpath=$instrumentationClasspath")
|
||||
|
||||
ant.withGroovyBuilder {
|
||||
"instrumentIdeaExtensions"("srcdir" to srcDirs.asPath,
|
||||
"destdir" to output,
|
||||
"classpath" to instrumentationClasspath,
|
||||
"includeantruntime" to false,
|
||||
"instrumentNotNull" to instrumentNotNull) {
|
||||
if (instrumentNotNull) {
|
||||
ant.withGroovyBuilder {
|
||||
"skip"("pattern" to "kotlin/Metadata")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (headlessOldValue != null) {
|
||||
System.setProperty("java.awt.headless", headlessOldValue)
|
||||
} else {
|
||||
System.clearProperty("java.awt.headless")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,8 @@ data class JdkId(val explicit: Boolean, val majorVersion: JdkMajorVersion, var v
|
||||
fun Project.getConfiguredJdks(): List<JdkId> {
|
||||
val res = arrayListOf<JdkId>()
|
||||
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()
|
||||
?: continue
|
||||
val explicitJdk = File(explicitJdkEnvVal)
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||
@@ -161,4 +162,8 @@ object CodegenUtil {
|
||||
(1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" }
|
||||
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.codegen.AsmUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.extractReificationArgument;
|
||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isPossiblyUninitializedSingleton;
|
||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.unwrapInitialSignatureDescriptor;
|
||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.*;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
|
||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*;
|
||||
@@ -1611,7 +1609,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return null;
|
||||
}
|
||||
|
||||
public void returnExpression(KtExpression expr) {
|
||||
public void returnExpression(@NotNull KtExpression expr) {
|
||||
boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction;
|
||||
|
||||
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) {
|
||||
List<KtExpression> statements = ((KtBlockExpression) bodyExpression).getStatements();
|
||||
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;
|
||||
}
|
||||
|
||||
private static boolean isLambdaVoidBody(KtElement bodyExpression, Type returnType) {
|
||||
private static boolean isLambdaVoidBody(@NotNull KtElement bodyExpression, @NotNull Type returnType) {
|
||||
if (bodyExpression instanceof KtBlockExpression) {
|
||||
PsiElement parent = bodyExpression.getParent();
|
||||
if (parent instanceof KtFunctionLiteral) {
|
||||
@@ -2658,6 +2656,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
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
|
||||
public StackValue genVarargs(@NotNull VarargValueArgument valueArgument, @NotNull KotlinType outType) {
|
||||
@@ -2680,10 +2687,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
if (size == 1) {
|
||||
Type arrayType = getArrayType(arrayOfReferences ? AsmTypes.OBJECT_TYPE : elementType);
|
||||
return StackValue.operation(type, adapter -> {
|
||||
gen(arguments.get(0).getArgumentExpression(), type);
|
||||
v.dup();
|
||||
v.arraylength();
|
||||
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
|
||||
KtExpression spreadArgument = arguments.get(0).getArgumentExpression();
|
||||
gen(spreadArgument, type);
|
||||
if (!canSkipArrayCopyForSpreadArgument(spreadArgument)) {
|
||||
v.dup();
|
||||
v.arraylength();
|
||||
v.invokestatic("java/util/Arrays", "copyOf", Type.getMethodDescriptor(arrayType, arrayType, Type.INT_TYPE), false);
|
||||
}
|
||||
if (arrayOfReferences) {
|
||||
v.checkcast(type);
|
||||
}
|
||||
@@ -3904,7 +3914,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
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);
|
||||
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.state.GenerationState;
|
||||
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.org.objectweb.asm.MethodVisitor;
|
||||
|
||||
@@ -49,7 +51,9 @@ public abstract class FunctionGenerationStrategy {
|
||||
|
||||
@Override
|
||||
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);
|
||||
int reg = hasDispatchReceiver ? 1 : 0;
|
||||
boolean accessorIsConstructor = accessorDescriptor instanceof AccessorForConstructorDescriptor;
|
||||
|
||||
int accessorParam = (hasDispatchReceiver && !accessorIsConstructor) ? 1 : 0;
|
||||
int reg = hasDispatchReceiver ? 1 : 0;
|
||||
if (!accessorIsConstructor && functionDescriptor instanceof ConstructorDescriptor) {
|
||||
iv.anew(callableMethod.getOwner());
|
||||
iv.dup();
|
||||
reg = 0;
|
||||
accessorParam = 0;
|
||||
}
|
||||
else if (accessorIsConstructor || (accessorDescriptor != null && KotlinTypeMapper.isAccessor(accessorDescriptor) && hasDispatchReceiver)) {
|
||||
else if (KotlinTypeMapper.isAccessor(accessorDescriptor) && (hasDispatchReceiver || accessorIsConstructor)) {
|
||||
if (!CodegenUtilKt.isJvmStaticInObjectOrClass(functionDescriptor)) {
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
for (Type argType : callableMethod.getParameterTypes()) {
|
||||
if (AsmTypes.DEFAULT_CONSTRUCTOR_MARKER.equals(argType)) {
|
||||
Type[] calleeParameterTypes = callableMethod.getParameterTypes();
|
||||
Type[] accessorParameterTypes = accessorDescriptor != null
|
||||
? typeMapper.mapToCallableMethod(accessorDescriptor, false).getParameterTypes()
|
||||
: calleeParameterTypes;
|
||||
for (Type calleeArgType: calleeParameterTypes) {
|
||||
if (AsmTypes.DEFAULT_CONSTRUCTOR_MARKER.equals(calleeArgType)) {
|
||||
iv.aconst(null);
|
||||
}
|
||||
else {
|
||||
iv.load(reg, argType);
|
||||
reg += argType.getSize();
|
||||
Type accessorParameterType = accessorParameterTypes[accessorParam];
|
||||
iv.load(reg, accessorParameterType);
|
||||
StackValue.coerce(accessorParameterType, calleeArgType, iv);
|
||||
reg += accessorParameterType.getSize();
|
||||
}
|
||||
accessorParam++;
|
||||
}
|
||||
|
||||
callableMethod.genInvokeInstruction(iv);
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.util.ArrayUtil
|
||||
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.MethodContext
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
@@ -241,7 +242,7 @@ class MultifileClassCodegenImpl(
|
||||
private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) {
|
||||
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
|
||||
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) {
|
||||
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
|
||||
if (descriptor !is MemberDescriptor) {
|
||||
@@ -367,7 +368,7 @@ class MultifileClassCodegenImpl(
|
||||
}
|
||||
|
||||
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(
|
||||
facadeFqName: FqName, state: GenerationState
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
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.descriptors.DeclarationDescriptor
|
||||
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.KtProperty
|
||||
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.OtherOrigin
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||
import java.util.*
|
||||
|
||||
class MultifileClassPartCodegen(
|
||||
v: ClassBuilder,
|
||||
@@ -135,7 +132,7 @@ class MultifileClassPartCodegen(
|
||||
}
|
||||
|
||||
override fun generateBody() {
|
||||
for (declaration in element.declarations) {
|
||||
for (declaration in CodegenUtil.getActualDeclarations(element)) {
|
||||
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
|
||||
genSimpleMember(declaration)
|
||||
}
|
||||
@@ -161,25 +158,7 @@ class MultifileClassPartCodegen(
|
||||
}
|
||||
|
||||
override fun generateKotlinMetadataAnnotation() {
|
||||
val members = ArrayList<DeclarationDescriptor>()
|
||||
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 (serializer, packageProto) = PackagePartCodegen.serializePackagePartMembers(this, partType)
|
||||
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
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.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -98,9 +98,7 @@ public class PackageCodegenImpl implements PackageCodegen {
|
||||
|
||||
List<KtClassOrObject> classOrObjects = new ArrayList<>();
|
||||
|
||||
for (KtDeclaration declaration : file.getDeclarations()) {
|
||||
if (PsiUtilsKt.hasExpectModifier(declaration)) continue;
|
||||
|
||||
for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(file)) {
|
||||
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
|
||||
generatePackagePart = true;
|
||||
}
|
||||
|
||||
@@ -17,22 +17,21 @@
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import kotlin.Pair;
|
||||
import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
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.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
||||
@@ -88,9 +87,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
|
||||
@Override
|
||||
protected void generateBody() {
|
||||
for (KtDeclaration declaration : element.getDeclarations()) {
|
||||
if (PsiUtilsKt.hasExpectModifier(declaration)) continue;
|
||||
|
||||
for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(element)) {
|
||||
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) {
|
||||
genSimpleMember(declaration);
|
||||
}
|
||||
@@ -103,34 +100,42 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
|
||||
@Override
|
||||
protected void generateKotlinMetadataAnnotation() {
|
||||
List<DeclarationDescriptor> members = new ArrayList<>();
|
||||
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();
|
||||
Pair<DescriptorSerializer, ProtoBuf.Package> serializedPart = serializePackagePartMembers(this, packagePartType);
|
||||
|
||||
WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.FILE_FACADE, 0, av -> {
|
||||
writeAnnotationData(av, serializer, packageProto);
|
||||
writeAnnotationData(av, serializedPart.getFirst(), serializedPart.getSecond());
|
||||
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
|
||||
protected void generateSyntheticPartsAfterBody() {
|
||||
generateSyntheticAccessors();
|
||||
|
||||
@@ -179,6 +179,19 @@ public abstract class StackValue {
|
||||
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
|
||||
public static StackValue constant(@Nullable Object value, @NotNull Type type) {
|
||||
if (type == Type.BOOLEAN_TYPE) {
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
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.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
@@ -70,6 +71,6 @@ class SuspendFunctionGenerationStrategy(
|
||||
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
this.codegen = codegen
|
||||
codegen.returnExpression(declaration.bodyExpression)
|
||||
codegen.returnExpression(declaration.bodyExpression ?: error("Function has no body: " + declaration.getElementTextWithContext()))
|
||||
}
|
||||
}
|
||||
|
||||
+132
-97
@@ -16,10 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.CustomFramesMethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.insnListOf
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -71,124 +69,161 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
||||
* - restore constructor arguments
|
||||
*/
|
||||
internal fun processUninitializedStores(methodNode: MethodNode) {
|
||||
val interpreter = UninitializedNewValueMarkerInterpreter()
|
||||
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))
|
||||
}
|
||||
}
|
||||
UninitializedStoresProcessor(methodNode, forCoroutines = true).run()
|
||||
}
|
||||
|
||||
private class UninitializedNewValue(
|
||||
val newInsn: TypeInsnNode, val internalName: String
|
||||
) : StrictBasicValue(Type.getObjectType(internalName)) {
|
||||
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
|
||||
}
|
||||
class UninitializedStoresProcessor(private val methodNode: MethodNode, private val forCoroutines: Boolean) {
|
||||
private val isInSpecialMethod = methodNode.name == "<init>" || methodNode.name == "<clinit>"
|
||||
|
||||
private class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame<BasicValue>(nLocals, nStack) {
|
||||
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<BasicValue>?) {
|
||||
val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null
|
||||
fun run() {
|
||||
val interpreter = UninitializedNewValueMarkerInterpreter()
|
||||
|
||||
super.execute(insn, interpreter)
|
||||
val frames = CustomFramesMethodAnalyzer(
|
||||
"fake", methodNode, interpreter,
|
||||
this::UninitializedNewValueFrame
|
||||
).analyze()
|
||||
|
||||
if (replaceTopValueWithInitialized) {
|
||||
// Drop top value
|
||||
val value = pop() as UninitializedNewValue
|
||||
for ((index, insn) in methodNode.instructions.toArray().withIndex()) {
|
||||
val frame = frames[index] ?: continue
|
||||
val uninitializedValue = frame.getUninitializedValueForConstructorCall(insn) ?: continue
|
||||
|
||||
// uninitialized value become initialized after <init> call
|
||||
push(StrictBasicValue(value.type))
|
||||
val newInsn = uninitializedValue.newInsn
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 inner class UninitializedNewValueFrame(nLocals: Int, nStack: Int) : Frame<BasicValue>(nLocals, nStack) {
|
||||
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<BasicValue>?) {
|
||||
val replaceTopValueWithInitialized = getUninitializedValueForConstructorCall(insn) != null
|
||||
|
||||
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 = getStack(stackSize - (paramsCountIncludingReceiver + 1)) as? UninitializedNewValue ?: error("Expected value generated with NEW")
|
||||
super.execute(insn, interpreter)
|
||||
|
||||
assert(getStack(stackSize - paramsCountIncludingReceiver) is UninitializedNewValue) {
|
||||
"Next value after NEW should be one generated by DUP"
|
||||
if (replaceTopValueWithInitialized) {
|
||||
// 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() {
|
||||
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)
|
||||
assert(peek(paramsCountIncludingReceiver - 1) is UninitializedNewValue) {
|
||||
"Next value after NEW should be one generated by DUP"
|
||||
}
|
||||
return super.newOperation(insn)
|
||||
|
||||
return newValue
|
||||
}
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
if (value is UninitializedNewValue) {
|
||||
uninitializedValuesToCopyUsages[value.newInsn]!!.add(insn)
|
||||
return value
|
||||
}
|
||||
return super.copyOperation(insn, value)
|
||||
private class UninitializedNewValue(
|
||||
val newInsn: TypeInsnNode,
|
||||
val internalName: String
|
||||
) : StrictBasicValue(Type.getObjectType(internalName)) {
|
||||
override fun toString() = "UninitializedNewValue(internalName='$internalName')"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
private fun AbstractInsnNode.isConstructorCall() = this is MethodInsnNode && this.name == "<init>"
|
||||
|
||||
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) {
|
||||
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
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
if (value is UninitializedNewValue) {
|
||||
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 v
|
||||
}
|
||||
if (v is UninitializedNewValue || w is UninitializedNewValue) {
|
||||
if ((v as? UninitializedNewValue)?.newInsn !== (w as? UninitializedNewValue)?.newInsn) {
|
||||
// Merge of two different ANEW result is possible, but such values should not be used further
|
||||
return StrictBasicValue.UNINITIALIZED_VALUE
|
||||
}
|
||||
|
||||
return super.merge(v, w)
|
||||
return v
|
||||
}
|
||||
|
||||
return super.merge(v, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -17,9 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder;
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory;
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode;
|
||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||
|
||||
|
||||
+7
-7
@@ -17,9 +17,9 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
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.RedundantBoxingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.prepareForEmitting
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
|
||||
@@ -35,11 +35,10 @@ class OptimizationMethodVisitor(
|
||||
signature: String?,
|
||||
exceptions: Array<String>?
|
||||
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
|
||||
|
||||
override fun performTransformations(methodNode: MethodNode) {
|
||||
MANDATORY_METHOD_TRANSFORMER.transform("fake", methodNode)
|
||||
normalizationMethodTransformer.transform("fake", methodNode)
|
||||
if (canBeOptimized(methodNode) && !disableOptimization) {
|
||||
OPTIMIZATION_TRANSFORMER.transform("fake", methodNode)
|
||||
optimizationTransformer.transform("fake", methodNode)
|
||||
}
|
||||
methodNode.prepareForEmitting()
|
||||
}
|
||||
@@ -47,12 +46,13 @@ class OptimizationMethodVisitor(
|
||||
companion object {
|
||||
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
||||
|
||||
private val MANDATORY_METHOD_TRANSFORMER = CompositeMethodTransformer(
|
||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||
FixStackWithLabelNormalizationMethodTransformer(),
|
||||
UninitializedStoresMethodTransformer(),
|
||||
MethodVerifier("AFTER mandatory stack transformations")
|
||||
)
|
||||
|
||||
private val OPTIMIZATION_TRANSFORMER = CompositeMethodTransformer(
|
||||
val optimizationTransformer = CompositeMethodTransformer(
|
||||
CapturedVarsOptimizationMethodTransformer(),
|
||||
RedundantNullCheckMethodTransformer(),
|
||||
RedundantCheckCastEliminationMethodTransformer(),
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.coroutines.UninitializedStoresProcessor
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class UninitializedStoresMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
UninitializedStoresProcessor(methodNode, forCoroutines = false).run()
|
||||
}
|
||||
}
|
||||
@@ -214,3 +214,6 @@ internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcod
|
||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
||||
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
||||
|
||||
fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) {
|
||||
for (node in nodes) remove(node)
|
||||
}
|
||||
+8
-1
@@ -18,8 +18,15 @@ package org.jetbrains.kotlin.codegen.optimization.transformer
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
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) {
|
||||
transformers.forEach { it.transform(internalClassName, methodNode) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) =
|
||||
CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() })
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -18,10 +18,8 @@ package org.jetbrains.kotlin.codegen.range.forLoop
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
|
||||
class ForInUntilConstantRangeLoopGenerator(
|
||||
@@ -36,5 +34,5 @@ class ForInUntilConstantRangeLoopGenerator(
|
||||
codegen.generateReceiverValue(from, false)
|
||||
|
||||
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 shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
||||
|
||||
init {
|
||||
|
||||
@@ -831,11 +831,11 @@ public class KotlinTypeMapper {
|
||||
JvmCodegenUtil.isJvm8InterfaceWithDefaults(ownerForDefault, isJvm8Target, isJvm8TargetWithDefaults);
|
||||
}
|
||||
|
||||
public static boolean isAccessor(@NotNull CallableMemberDescriptor descriptor) {
|
||||
public static boolean isAccessor(@Nullable CallableMemberDescriptor descriptor) {
|
||||
return descriptor instanceof AccessorForCallableDescriptor<?>;
|
||||
}
|
||||
|
||||
public static boolean isStaticAccessor(@NotNull CallableMemberDescriptor descriptor) {
|
||||
public static boolean isStaticAccessor(@Nullable CallableMemberDescriptor descriptor) {
|
||||
if (descriptor instanceof AccessorForConstructorDescriptor) return false;
|
||||
return isAccessor(descriptor);
|
||||
}
|
||||
|
||||
+63
-13
@@ -32,7 +32,8 @@ val testDistProjects = listOf(
|
||||
":kotlin-daemon-client",
|
||||
":kotlin-preloader",
|
||||
":plugins:android-extensions-compiler",
|
||||
":kotlin-ant")
|
||||
":kotlin-ant",
|
||||
":kotlin-annotations-jvm")
|
||||
|
||||
dependencies {
|
||||
depDistProjects.forEach {
|
||||
@@ -82,36 +83,85 @@ projectTest {
|
||||
|
||||
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())
|
||||
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()
|
||||
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.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")
|
||||
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")
|
||||
}
|
||||
|
||||
codegenTest("codegen-target8-jvm9-test") {
|
||||
codegenTest("codegen-target8-jvm9-test", "JDK_9") {
|
||||
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.substitute.bytecode.1.8.to.1.9", "true")
|
||||
jvmArgs = listOf("--add-opens", "java.desktop/javax.swing=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED")
|
||||
}
|
||||
|
||||
+3
-2
@@ -48,11 +48,12 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
var sourceMapPrefix: String? by FreezableVar(null)
|
||||
|
||||
@Argument(
|
||||
value = "-source-map-source-roots",
|
||||
value = "-source-map-base-dirs",
|
||||
deprecatedName = "-source-map-source-roots",
|
||||
valueDescription = "<path>",
|
||||
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)
|
||||
@Argument(
|
||||
|
||||
+80
-13
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
import org.jetbrains.kotlin.utils.ReportLevel
|
||||
|
||||
class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
companion object {
|
||||
@@ -165,10 +166,15 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(
|
||||
value = "-Xjsr305",
|
||||
deprecatedName = "-Xjsr305-annotations",
|
||||
valueDescription = "{ignore|strict|warn}",
|
||||
description = "Specify global behavior for JSR-305 nullability annotations: ignore, treat as other supported nullability annotations, or report a warning"
|
||||
valueDescription = "{ignore|strict|warn}" +
|
||||
"|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(
|
||||
value = "-Xno-exception-on-explicit-equals-for-boxed-null",
|
||||
@@ -181,20 +187,81 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
|
||||
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
|
||||
val result = super.configureAnalysisFlags(collector)
|
||||
result[AnalysisFlag.jsr305] = parseJsr305(collector)
|
||||
return result
|
||||
}
|
||||
|
||||
if (jsr305 == "enable") {
|
||||
collector.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Option 'enable' for -Xjsr305 flag is deprecated. Please use 'strict' instead"
|
||||
)
|
||||
result.put(AnalysisFlag.jsr305, Jsr305State.STRICT)
|
||||
fun parseJsr305(collector: MessageCollector): Jsr305State {
|
||||
var global: ReportLevel? = null
|
||||
var migration: ReportLevel? = null
|
||||
val userDefined = mutableMapOf<String, ReportLevel>()
|
||||
|
||||
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 {
|
||||
result.put(AnalysisFlag.jsr305, it)
|
||||
|
||||
jsr305?.forEach { item ->
|
||||
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);
|
||||
}
|
||||
|
||||
MessageCollector collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY);
|
||||
if (apiVersion.compareTo(languageVersion) > 0) {
|
||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
||||
collector.report(
|
||||
ERROR,
|
||||
"-api-version (" + apiVersion.getVersionString() + ") cannot be greater than " +
|
||||
"-language-version (" + languageVersion.getVersionString() + ")",
|
||||
@@ -185,7 +186,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
||||
}
|
||||
|
||||
if (!languageVersion.isStable()) {
|
||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
||||
collector.report(
|
||||
STRONG_WARNING,
|
||||
"Language version " + languageVersion.getVersionString() + " is experimental, there are " +
|
||||
"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(
|
||||
languageVersion,
|
||||
ApiVersion.createByLanguageVersion(apiVersion),
|
||||
arguments.configureAnalysisFlags(configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)),
|
||||
arguments.configureAnalysisFlags(collector),
|
||||
extraLanguageFeatures
|
||||
));
|
||||
}
|
||||
|
||||
@@ -357,8 +357,8 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
||||
configuration.put(JSConfigurationKeys.SOURCE_MAP_PREFIX, arguments.getSourceMapPrefix());
|
||||
}
|
||||
|
||||
String sourceMapSourceRoots = arguments.getSourceMapSourceRoots() != null ?
|
||||
arguments.getSourceMapSourceRoots() :
|
||||
String sourceMapSourceRoots = arguments.getSourceMapBaseDirs() != null ?
|
||||
arguments.getSourceMapBaseDirs() :
|
||||
calculateSourceMapSourceRoot(messageCollector, arguments);
|
||||
List<String> sourceMapSourceRootList = StringUtil.split(sourceMapSourceRoots, File.pathSeparator);
|
||||
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceMapSourceRootList);
|
||||
@@ -367,7 +367,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
||||
if (arguments.getSourceMapPrefix() != 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isValidJavaFqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
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.util.jar.Attributes
|
||||
import java.util.jar.Manifest
|
||||
@@ -51,55 +51,82 @@ class ClasspathRootsResolver(
|
||||
private val additionalModules: List<String>,
|
||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
|
||||
private val javaModuleFinder: CliJavaModuleFinder,
|
||||
private val requireStdlibModule: Boolean
|
||||
private val requireStdlibModule: Boolean,
|
||||
private val outputDirectory: VirtualFile?
|
||||
) {
|
||||
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||
|
||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
||||
|
||||
fun convertClasspathRoots(contentRoots: List<ContentRoot>): RootsAndModules {
|
||||
val result = mutableListOf<JavaRoot>()
|
||||
private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?)
|
||||
|
||||
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) {
|
||||
if (contentRoot !is JvmContentRoot) continue
|
||||
val root = contentRootToVirtualFile(contentRoot) ?: continue
|
||||
|
||||
when (contentRoot) {
|
||||
is JavaSourceRoot -> {
|
||||
val modularRoot = modularSourceRoot(root)
|
||||
if (modularRoot != null) {
|
||||
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
|
||||
}
|
||||
}
|
||||
is JavaSourceRoot -> javaSourceRoots += RootWithPrefix(root, contentRoot.packagePrefix)
|
||||
is JvmClasspathRoot -> jvmClasspathRoots += root
|
||||
is JvmModulePathRoot -> jvmModulePathRoots += root
|
||||
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)
|
||||
|
||||
return RootsAndModules(result, modules)
|
||||
}
|
||||
|
||||
private fun modularSourceRoot(root: VirtualFile): JavaModule.Explicit? {
|
||||
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, PsiJavaModule>? {
|
||||
val moduleInfoFile =
|
||||
when {
|
||||
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
|
||||
@@ -109,10 +136,21 @@ class ClasspathRootsResolver(
|
||||
|
||||
val psiFile = psiManager.findFile(moduleInfoFile) ?: 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 manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
|
||||
|
||||
@@ -124,22 +162,25 @@ class ClasspathRootsResolver(
|
||||
|
||||
if (moduleInfoFile != 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
|
||||
if (isJar) {
|
||||
val moduleRoot = listOf(JavaModule.Root(root, isBinary = true))
|
||||
|
||||
val automaticModuleName = manifest?.getValue(AUTOMATIC_MODULE_NAME)
|
||||
if (automaticModuleName != null) {
|
||||
return JavaModule.Automatic(automaticModuleName, root)
|
||||
return JavaModule.Automatic(automaticModuleName, moduleRoot)
|
||||
}
|
||||
|
||||
val originalFile = VfsUtilCore.virtualToIoFile(root)
|
||||
val moduleName = LightJavaModule.moduleName(originalFile.nameWithoutExtension)
|
||||
if (moduleName.isEmpty()) {
|
||||
report(ERROR, "Cannot infer automatic module name for the file", VfsUtilCore.getVirtualFileForJar(root) ?: root)
|
||||
return null
|
||||
}
|
||||
return JavaModule.Automatic(moduleName, root)
|
||||
return JavaModule.Automatic(moduleName, moduleRoot)
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -156,7 +197,9 @@ class ClasspathRootsResolver(
|
||||
}
|
||||
|
||||
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) {
|
||||
for (module in sourceModules) {
|
||||
report(ERROR, "Too many source module declarations found", module.moduleInfoFile)
|
||||
@@ -169,11 +212,15 @@ class ClasspathRootsResolver(
|
||||
if (existing == null) {
|
||||
javaModuleFinder.addUserModule(module)
|
||||
}
|
||||
else if (module.moduleRoot != existing.moduleRoot) {
|
||||
val jar = VfsUtilCore.getVirtualFileForJar(module.moduleRoot) ?: module.moduleRoot
|
||||
val existingPath = (VfsUtilCore.getVirtualFileForJar(existing.moduleRoot) ?: existing.moduleRoot).path
|
||||
else if (module.moduleRoots != existing.moduleRoots) {
|
||||
fun JavaModule.getRootFile() =
|
||||
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}' " +
|
||||
"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")
|
||||
}
|
||||
else {
|
||||
result.add(JavaRoot(
|
||||
module.moduleRoot,
|
||||
if (module.isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE
|
||||
))
|
||||
for ((root, isBinary) in module.moduleRoots) {
|
||||
result.add(JavaRoot(root, if (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(
|
||||
ERROR,
|
||||
"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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -204,13 +204,19 @@ class KotlinCoreEnvironment private constructor(
|
||||
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
})
|
||||
|
||||
val outputDirectory =
|
||||
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath
|
||||
|
||||
classpathRootsResolver = ClasspathRootsResolver(
|
||||
PsiManager.getInstance(project),
|
||||
messageCollector,
|
||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder,
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE)
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||
outputDirectory?.let(this::findLocalFile)
|
||||
)
|
||||
|
||||
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? {
|
||||
return findLocalFile(root.file.absolutePath).also {
|
||||
|
||||
@@ -42,6 +42,6 @@ class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
|
||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_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>
|
||||
) : JavaModuleResolver {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceModule: JavaModule? = userModules.firstOrNull { !it.isBinary }
|
||||
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
|
||||
|
||||
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
||||
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) {
|
||||
@@ -46,13 +46,13 @@ class CliJavaModuleResolver(
|
||||
|
||||
return when (file.fileType) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
fileFromOurModule: VirtualFile?, referencedFile: VirtualFile, referencedPackage: FqName?
|
||||
|
||||
+2
@@ -41,4 +41,6 @@ open class DelegatePackageMemberDeclarationProvider(var delegate: PackageMemberD
|
||||
override fun getClassOrObjectDeclarations(name: Name) = delegate.getClassOrObjectDeclarations(name)
|
||||
|
||||
override fun getTypeAliasDeclarations(name: Name) = delegate.getTypeAliasDeclarations(name)
|
||||
|
||||
override fun getDeclarationNames() = delegate.getDeclarationNames()
|
||||
}
|
||||
|
||||
+3
-1
@@ -48,7 +48,9 @@ class IncrementalCompilationOptions(
|
||||
/** @See [ReportSeverity] */
|
||||
reportSeverity: Int,
|
||||
/** @See [CompilationResultCategory]] */
|
||||
requestedCompilationResults: Array<Int>
|
||||
requestedCompilationResults: Array<Int>,
|
||||
val resultDifferenceFile: File? = null,
|
||||
val friendDifferenceFile: File? = null
|
||||
) : CompilationOptions(compilerMode, targetPlatform, reportCategories, reportSeverity, requestedCompilationResults) {
|
||||
companion object {
|
||||
const val serialVersionUID: Long = 0
|
||||
|
||||
@@ -507,9 +507,12 @@ class CompileServiceImpl(
|
||||
workingDir,
|
||||
enabled = true)
|
||||
|
||||
return IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions, reporter, annotationFileUpdater,
|
||||
artifactChanges, changesRegistry)
|
||||
.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, { changedFiles })
|
||||
val compiler = IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions,
|
||||
reporter, annotationFileUpdater,
|
||||
artifactChanges, changesRegistry,
|
||||
buildHistoryFile = incrementalCompilationOptions.resultDifferenceFile,
|
||||
friendBuildHistoryFile = incrementalCompilationOptions.friendDifferenceFile)
|
||||
return compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, { changedFiles })
|
||||
}
|
||||
|
||||
override fun leaseReplSession(
|
||||
|
||||
+36
-3
@@ -22,9 +22,7 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.lang.reflect.Array
|
||||
|
||||
internal class AnnotationsCollectorMethodVisitor(
|
||||
@@ -55,6 +53,27 @@ internal class AnnotationsCollectorMethodVisitor(
|
||||
|
||||
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(
|
||||
@@ -87,6 +106,20 @@ class BinaryJavaAnnotation private constructor(
|
||||
|
||||
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) {
|
||||
|
||||
+6
@@ -162,6 +162,12 @@ class BinaryJavaClass(
|
||||
object : FieldVisitor(ASM_API_VERSION_FOR_CLASS_READING) {
|
||||
override fun visitAnnotation(desc: String, visible: Boolean) =
|
||||
BinaryJavaAnnotation.addAnnotation(this@run.annotations, desc, context, signatureParser)
|
||||
|
||||
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String, visible: Boolean) =
|
||||
if (typePath == null)
|
||||
BinaryJavaAnnotation.addTypeAnnotation(type, desc, context, signatureParser)
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.java.structure.impl.classFiles
|
||||
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -39,9 +40,18 @@ internal class PlainJavaClassifierType(
|
||||
get() = typeArguments.isEmpty() &&
|
||||
classifierResolverResult.classifier?.safeAs<JavaClass>()?.typeParameters?.isNotEmpty() == true
|
||||
|
||||
// TODO: support type annotations
|
||||
override val annotations get() = emptyList<JavaAnnotation>()
|
||||
override fun findAnnotation(fqName: FqName) = null
|
||||
private var _annotations = emptyList<JavaAnnotation>()
|
||||
override val annotations get() = _annotations
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.modules
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
@@ -28,11 +29,11 @@ interface JavaModule {
|
||||
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
|
||||
* 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.
|
||||
* 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
|
||||
* if that path was passed as an explicit argument to the compiler.
|
||||
* 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.
|
||||
@@ -40,10 +41,11 @@ interface JavaModule {
|
||||
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.
|
||||
* `false` if this module is an explicit module loaded from module-info.java.
|
||||
* `true` if this module is an explicit module loaded from module-info.java, `false` otherwise. This usually corresponds to the module
|
||||
* 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.
|
||||
@@ -64,10 +66,12 @@ interface JavaModule {
|
||||
*/
|
||||
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 isBinary: Boolean get() = true
|
||||
override val isSourceModule: Boolean get() = false
|
||||
|
||||
override fun exports(packageFqName: FqName): Boolean = true
|
||||
|
||||
@@ -78,13 +82,15 @@ interface JavaModule {
|
||||
|
||||
class Explicit(
|
||||
val moduleInfo: JavaModuleInfo,
|
||||
override val moduleRoot: VirtualFile,
|
||||
override val moduleInfoFile: VirtualFile,
|
||||
override val isBinary: Boolean
|
||||
override val moduleRoots: List<Root>,
|
||||
override val moduleInfoFile: VirtualFile
|
||||
) : JavaModule {
|
||||
override val name: String
|
||||
get() = moduleInfo.moduleName
|
||||
|
||||
override val isSourceModule: Boolean
|
||||
get() = moduleInfoFile.fileType == JavaFileType.INSTANCE
|
||||
|
||||
override fun exports(packageFqName: FqName): Boolean {
|
||||
return moduleInfo.exports.any { (fqName, toModules) ->
|
||||
fqName == packageFqName && toModules.isEmpty()
|
||||
@@ -100,3 +106,5 @@ interface JavaModule {
|
||||
override fun toString(): String = name
|
||||
}
|
||||
}
|
||||
|
||||
const val KOTLIN_STDLIB_MODULE_NAME = "kotlin.stdlib"
|
||||
@@ -118,7 +118,6 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
||||
moduleDescriptor.setDependencies(LazyModuleDependencies(
|
||||
projectContext.storageManager,
|
||||
module,
|
||||
modulePlatforms,
|
||||
firstDependency,
|
||||
this))
|
||||
|
||||
@@ -270,7 +269,6 @@ abstract class AnalyzerFacade {
|
||||
class LazyModuleDependencies<M: ModuleInfo>(
|
||||
storageManager: StorageManager,
|
||||
private val module: M,
|
||||
modulePlatforms: (M) -> MultiTargetPlatform?,
|
||||
firstDependency: M? = null,
|
||||
private val resolverForProject: ResolverForProjectImpl<M>
|
||||
) : ModuleDependencies {
|
||||
@@ -292,14 +290,6 @@ class LazyModuleDependencies<M: ModuleInfo>(
|
||||
}.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 modulesWhoseInternalsAreVisible: Set<ModuleDescriptorImpl>
|
||||
@@ -308,8 +298,6 @@ class LazyModuleDependencies<M: ModuleInfo>(
|
||||
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 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(
|
||||
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
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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)))
|
||||
|
||||
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>.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()) :
|
||||
ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map) {
|
||||
ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map), ReadOnlyInitControlFlowInfo {
|
||||
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) = InitControlFlowInfo(newMap)
|
||||
|
||||
// this = output of EXHAUSTIVE_WHEN_ELSE instruction
|
||||
// merge = input of MergeInstruction
|
||||
// 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()) {
|
||||
if (value.initState == InitState.INITIALIZED_EXHAUSTIVELY &&
|
||||
merge.getOrNull(key)?.initState == InitState.INITIALIZED) {
|
||||
@@ -74,7 +87,7 @@ class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlF
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -274,8 +274,8 @@ class ControlFlowInformationProvider private constructor(
|
||||
|
||||
pseudocode.traverse(TraversalOrder.FORWARD, initializers) {
|
||||
instruction: Instruction,
|
||||
enterData: ImmutableMap<VariableDescriptor, VariableControlFlowState>,
|
||||
exitData: ImmutableMap<VariableDescriptor, VariableControlFlowState> ->
|
||||
enterData: ReadOnlyInitControlFlowInfo,
|
||||
exitData: ReadOnlyInitControlFlowInfo ->
|
||||
|
||||
val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo)
|
||||
if (ctxt.variableDescriptor == null) return@traverse
|
||||
@@ -486,18 +486,21 @@ class ControlFlowInformationProvider private constructor(
|
||||
}
|
||||
|
||||
private fun checkAssignmentBeforeDeclaration(ctxt: VariableInitContext, expression: KtExpression) =
|
||||
if (ctxt.enterInitState?.isDeclared == true
|
||||
|| ctxt.exitInitState?.isDeclared == true
|
||||
|| ctxt.enterInitState?.mayBeInitialized() == true
|
||||
|| ctxt.exitInitState?.mayBeInitialized() != true) {
|
||||
false
|
||||
}
|
||||
else {
|
||||
if (ctxt.isInitializationBeforeDeclaration()) {
|
||||
if (ctxt.variableDescriptor != null) {
|
||||
report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt)
|
||||
}
|
||||
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 {
|
||||
val variableDescriptor = ctxt.variableDescriptor
|
||||
@@ -532,7 +535,7 @@ class ControlFlowInformationProvider private constructor(
|
||||
|
||||
private fun recordInitializedVariables(
|
||||
pseudocode: Pseudocode,
|
||||
initializersMap: Map<Instruction, Edges<InitControlFlowInfo>>
|
||||
initializersMap: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>>
|
||||
) {
|
||||
val initializers = initializersMap[pseudocode.exitInstruction] ?: return
|
||||
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false)
|
||||
@@ -554,8 +557,8 @@ class ControlFlowInformationProvider private constructor(
|
||||
val usedValueExpressions = hashSetOf<KtExpression>()
|
||||
pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) {
|
||||
instruction: Instruction,
|
||||
enterData: ImmutableMap<VariableDescriptor, VariableUseState>,
|
||||
_: ImmutableMap<VariableDescriptor, VariableUseState> ->
|
||||
enterData: ReadOnlyUseControlFlowInfo,
|
||||
_: ReadOnlyUseControlFlowInfo ->
|
||||
|
||||
val ctxt = VariableUseContext(instruction, reportedDiagnosticMap)
|
||||
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false)
|
||||
@@ -1030,8 +1033,8 @@ class ControlFlowInformationProvider private constructor(
|
||||
private inner class VariableInitContext(
|
||||
instruction: Instruction,
|
||||
map: MutableMap<Instruction, DiagnosticFactory<*>>,
|
||||
`in`: ImmutableMap<VariableDescriptor, VariableControlFlowState>,
|
||||
out: ImmutableMap<VariableDescriptor, VariableControlFlowState>,
|
||||
`in`: ReadOnlyInitControlFlowInfo,
|
||||
out: ReadOnlyInitControlFlowInfo,
|
||||
blockScopeVariableInfo: BlockScopeVariableInfo
|
||||
) : VariableContext(instruction, map) {
|
||||
internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`)
|
||||
@@ -1040,7 +1043,7 @@ class ControlFlowInformationProvider private constructor(
|
||||
private fun initialize(
|
||||
variableDescriptor: VariableDescriptor?,
|
||||
blockScopeVariableInfo: BlockScopeVariableInfo,
|
||||
map: ImmutableMap<VariableDescriptor, VariableControlFlowState>
|
||||
map: ReadOnlyInitControlFlowInfo
|
||||
): VariableControlFlowState? {
|
||||
val state = map.getOrNull(variableDescriptor ?: return null)
|
||||
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.pseudocodeTraverser.Edges
|
||||
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.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
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) {
|
||||
private val containsDoWhile = pseudocode.rootPseudocode.containsDoWhile
|
||||
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()
|
||||
}
|
||||
|
||||
val blockScopeVariableInfo: 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) {
|
||||
return getUpperLevelDeclaredVariables(pseudocode)
|
||||
}
|
||||
val declaredVariables = linkedSetOf<VariableDescriptor>()
|
||||
declaredVariables.addAll(getUpperLevelDeclaredVariables(pseudocode))
|
||||
val nonTrivialVariables = linkedSetOf<VariableDescriptor>()
|
||||
val valsWithTrivialInitializer = linkedSetOf<VariableDescriptor>()
|
||||
addVariablesFromPseudocode(pseudocode, nonTrivialVariables, valsWithTrivialInitializer)
|
||||
|
||||
for (localFunctionDeclarationInstruction in pseudocode.localDeclarations) {
|
||||
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> {
|
||||
var declaredVariables = declaredVariablesForDeclaration[pseudocode]
|
||||
if (declaredVariables == null) {
|
||||
declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode)
|
||||
declaredVariablesForDeclaration.put(pseudocode, declaredVariables)
|
||||
private fun addVariablesFromPseudocode(
|
||||
pseudocode: Pseudocode,
|
||||
nonTrivialVariables: MutableSet<VariableDescriptor>,
|
||||
valsWithTrivialInitializer: MutableSet<VariableDescriptor>
|
||||
) {
|
||||
getUpperLevelDeclaredVariables(pseudocode).let {
|
||||
nonTrivialVariables.addAll(it.nonTrivialVariables)
|
||||
valsWithTrivialInitializer.addAll(it.valsWithTrivialInitializer)
|
||||
}
|
||||
return declaredVariables
|
||||
}
|
||||
|
||||
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): Set<VariableDescriptor> {
|
||||
val declaredVariables = linkedSetOf<VariableDescriptor>()
|
||||
private fun getUpperLevelDeclaredVariables(pseudocode: Pseudocode) = declaredVariablesForDeclaration.getOrPut(pseudocode) {
|
||||
computeDeclaredVariablesForPseudocode(pseudocode)
|
||||
}
|
||||
|
||||
private fun computeDeclaredVariablesForPseudocode(pseudocode: Pseudocode): VariablesForDeclaration {
|
||||
val valsWithTrivialInitializer = linkedSetOf<VariableDescriptor>()
|
||||
val nonTrivialVariables = linkedSetOf<VariableDescriptor>()
|
||||
for (instruction in pseudocode.instructions) {
|
||||
if (instruction is VariableDeclarationInstruction) {
|
||||
val variableDeclarationElement = instruction.variableDeclarationElement
|
||||
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement)
|
||||
variableDescriptorForDeclaration(descriptor)?.let {
|
||||
declaredVariables.add(it)
|
||||
val descriptor =
|
||||
variableDescriptorForDeclaration(
|
||||
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
|
||||
|
||||
private fun computeVariableInitializers(): Map<Instruction, Edges<InitControlFlowInfo>> {
|
||||
private fun computeVariableInitializers(): Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> {
|
||||
|
||||
val blockScopeVariableInfo = pseudocodeVariableDataCollector.blockScopeVariableInfo
|
||||
|
||||
val resultForValsWithTrivialInitializer = computeInitInfoForTrivialVals()
|
||||
|
||||
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForValsWithTrivialInitializer
|
||||
|
||||
return pseudocodeVariableDataCollector.collectData(TraversalOrder.FORWARD, InitControlFlowInfo()) {
|
||||
instruction: Instruction, incomingEdgesData: Collection<InitControlFlowInfo> ->
|
||||
|
||||
@@ -94,6 +155,88 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
val exitInstructionData = addVariableInitStateFromCurrentInstructionIfAny(
|
||||
instruction, enterInstructionData, blockScopeVariableInfo)
|
||||
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) {
|
||||
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
|
||||
if (instruction is WriteValueInstruction) {
|
||||
// if writing to already initialized object
|
||||
@@ -129,10 +275,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
}
|
||||
else {
|
||||
// instruction instanceof VariableDeclarationInstruction
|
||||
var enterInitState: VariableControlFlowState? = enterInstructionData.getOrNull(variable)
|
||||
if (enterInitState == null) {
|
||||
enterInitState = getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
|
||||
}
|
||||
val enterInitState =
|
||||
enterInstructionData.getOrNull(variable)
|
||||
?: getDefaultValueForInitializers(variable, instruction, blockScopeVariableInfo)
|
||||
|
||||
if (!enterInitState.mayBeInitialized() || !enterInitState.isDeclared) {
|
||||
val variableDeclarationInfo = VariableControlFlowState.create(enterInitState.initState, isDeclared = true)
|
||||
exitInstructionData = exitInstructionData.put(variable, variableDeclarationInfo, enterInitState)
|
||||
@@ -143,46 +289,128 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
|
||||
// variable use
|
||||
|
||||
val variableUseStatusData: Map<Instruction, Edges<UseControlFlowInfo>>
|
||||
get() = pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) {
|
||||
instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
||||
val variableUseStatusData: Map<Instruction, Edges<ReadOnlyUseControlFlowInfo>>
|
||||
get() {
|
||||
val resultForTrivialVals = computeUseInfoForTrivialVals()
|
||||
if (rootVariables.nonTrivialVariables.isEmpty()) return resultForTrivialVals
|
||||
|
||||
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
|
||||
incomingEdgesData.single()
|
||||
}
|
||||
else {
|
||||
incomingEdgesData.fold(UseControlFlowInfo()) { result, edgeData ->
|
||||
edgeData.iterator().fold(result) { subResult, (variableDescriptor, variableUseState) ->
|
||||
subResult.put(variableDescriptor, variableUseState.merge(subResult.getOrNull(variableDescriptor)))
|
||||
return pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) { instruction: Instruction, incomingEdgesData: Collection<UseControlFlowInfo> ->
|
||||
|
||||
val enterResult: UseControlFlowInfo = if (incomingEdgesData.size == 1) {
|
||||
incomingEdgesData.single()
|
||||
}
|
||||
else {
|
||||
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)
|
||||
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
||||
Edges(enterResult, enterResult)
|
||||
}
|
||||
else {
|
||||
val exitResult =
|
||||
if (instruction is ReadValueInstruction) {
|
||||
enterResult.put(variableDescriptor, VariableUseState.READ)
|
||||
}
|
||||
else {
|
||||
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
|
||||
if (variableUseState == null) {
|
||||
variableUseState = VariableUseState.UNUSED
|
||||
}
|
||||
when (variableUseState) {
|
||||
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_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)
|
||||
val variableDescriptor =
|
||||
PseudocodeUtil.extractVariableDescriptorFromReference(instruction, bindingContext)
|
||||
?.takeIf { it in rootVariables.nonTrivialVariables }
|
||||
if (variableDescriptor == null || instruction !is ReadValueInstruction && instruction !is WriteValueInstruction) {
|
||||
Edges(enterResult, enterResult)
|
||||
}
|
||||
else {
|
||||
val exitResult =
|
||||
if (instruction is ReadValueInstruction) {
|
||||
enterResult.put(variableDescriptor, VariableUseState.READ)
|
||||
}
|
||||
else {
|
||||
var variableUseState: VariableUseState? = enterResult.getOrNull(variableDescriptor)
|
||||
if (variableUseState == null) {
|
||||
variableUseState = VariableUseState.UNUSED
|
||||
}
|
||||
when (variableUseState) {
|
||||
VariableUseState.UNUSED, VariableUseState.ONLY_WRITTEN_NEVER_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)
|
||||
}
|
||||
}.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 {
|
||||
|
||||
@JvmStatic
|
||||
|
||||
+5
-2
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.cfg.pseudocode
|
||||
import com.intellij.util.containers.Stack
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
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.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
|
||||
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.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
|
||||
import java.util.*
|
||||
|
||||
class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
@@ -113,6 +112,10 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
override fun createUnboundLabel(name: String): Label = pseudocode.createLabel("L" + labelCount++, name)
|
||||
|
||||
override fun enterLoop(expression: KtLoopExpression): LoopInfo {
|
||||
if (expression is KtDoWhileExpression) {
|
||||
(pseudocode.rootPseudocode as PseudocodeImpl).containsDoWhile = true
|
||||
}
|
||||
|
||||
val info = LoopInfo(
|
||||
expression,
|
||||
createUnboundLabel("loop entry point"),
|
||||
|
||||
@@ -45,6 +45,9 @@ interface Pseudocode {
|
||||
|
||||
val enterInstruction: SubroutineEnterInstruction
|
||||
|
||||
val containsDoWhile: Boolean
|
||||
val rootPseudocode: Pseudocode
|
||||
|
||||
fun getElementValue(element: KtElement?): PseudoValue?
|
||||
|
||||
fun getValueElements(value: PseudoValue?): List<KtElement>
|
||||
|
||||
@@ -77,6 +77,9 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
|
||||
private var postPrecessed = false
|
||||
|
||||
override var containsDoWhile: Boolean = false
|
||||
internal set
|
||||
|
||||
private fun getLocalDeclarations(pseudocode: Pseudocode): Set<LocalFunctionDeclarationInstruction> {
|
||||
val localDeclarations = linkedSetOf<LocalFunctionDeclarationInstruction>()
|
||||
for (instruction in (pseudocode as PseudocodeImpl).mutableInstructionList) {
|
||||
@@ -88,7 +91,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
return localDeclarations
|
||||
}
|
||||
|
||||
val rootPseudocode: Pseudocode
|
||||
override val rootPseudocode: Pseudocode
|
||||
get() {
|
||||
var parent = parent
|
||||
while (parent != null) {
|
||||
|
||||
@@ -562,6 +562,9 @@ public interface Errors {
|
||||
DiagnosticFactory0<KtConstructor<?>> EXPECTED_ENUM_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtEnumEntry> EXPECTED_ENUM_ENTRY_WITH_BODY = 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>
|
||||
@@ -622,7 +625,8 @@ public interface Errors {
|
||||
DiagnosticFactory1<KtExpression, KotlinType> MISSING_RECEIVER = DiagnosticFactory1.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
|
||||
|
||||
|
||||
+7
-3
@@ -270,13 +270,16 @@ public class DefaultErrorMessages {
|
||||
MAP.put(EXPECTED_ENUM_CONSTRUCTOR, "Expected enum class cannot have a constructor");
|
||||
MAP.put(EXPECTED_ENUM_ENTRY_WITH_BODY, "Expected enum entry cannot have a body");
|
||||
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_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_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);
|
||||
MAP.put(ACTUAL_WITHOUT_EXPECT, "Actual {0} has no corresponding expected declaration{1}", DECLARATION_NAME_WITH_KIND,
|
||||
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(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_SUSPEND_FUNCTION_TYPE, "Suspend function type is not allowed as 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(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'");
|
||||
@@ -757,7 +760,8 @@ public class DefaultErrorMessages {
|
||||
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(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");
|
||||
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ private fun StringBuilder.renderIncompatibilityInformation(
|
||||
|
||||
if (incompatibility is Incompatible.ClassScopes) {
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
val packageFqName: FqName
|
||||
var packageFqName: FqName
|
||||
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
|
||||
get() = packageDirectiveByTree?.fqName ?: FqName.ROOT
|
||||
|
||||
@@ -64,7 +64,10 @@ abstract class KtNamedDeclarationNotStubbed extends KtDeclarationImpl implements
|
||||
|
||||
@Override
|
||||
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
|
||||
|
||||
@@ -530,11 +530,19 @@ public class KtPsiUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
// '(x operator y)' case
|
||||
if (innerExpression instanceof KtBinaryExpression &&
|
||||
innerOperation != KtTokens.ELVIS &&
|
||||
isKeepBinaryExpressionParenthesized((KtBinaryExpression) innerExpression)) {
|
||||
return true;
|
||||
if (innerExpression instanceof KtBinaryExpression) {
|
||||
// '(x operator return [...]) operator ...' case
|
||||
if (parentElement instanceof KtBinaryExpression) {
|
||||
KtBinaryExpression innerBinary = (KtBinaryExpression) innerExpression;
|
||||
if (innerBinary.getRight() instanceof KtReturnExpression) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// '(x operator y)' case
|
||||
if (innerOperation != KtTokens.ELVIS &&
|
||||
isKeepBinaryExpressionParenthesized((KtBinaryExpression) innerExpression)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int innerPriority = getPriority(innerExpression);
|
||||
@@ -846,7 +854,7 @@ public class KtPsiUtil {
|
||||
return (KtElement) current;
|
||||
}
|
||||
}
|
||||
if (current instanceof KtDelegatedSuperTypeEntry) {
|
||||
if (current instanceof KtDelegatedSuperTypeEntry || current instanceof KtSuperTypeCallEntry) {
|
||||
PsiElement grandParent = current.getParent().getParent();
|
||||
if (grandParent instanceof KtClassOrObject && !(grandParent.getParent() instanceof KtObjectLiteralExpression)) {
|
||||
return (KtElement) grandParent;
|
||||
|
||||
@@ -460,7 +460,7 @@ fun KtExpression.getOutermostParenthesizerOrThis(): KtExpression {
|
||||
|
||||
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 {
|
||||
val entryText = element?.text ?: return true
|
||||
|
||||
+1
@@ -131,6 +131,7 @@ class SyntheticClassOrObjectDescriptor(
|
||||
override fun getDestructuringDeclarationsEntries(name: Name): Collection<KtDestructuringDeclarationEntry> = emptyList()
|
||||
override fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo> = emptyList()
|
||||
override fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias> = emptyList()
|
||||
override fun getDeclarationNames() = emptySet<Name>()
|
||||
}
|
||||
|
||||
internal inner class SyntheticDeclaration(
|
||||
|
||||
@@ -22,15 +22,17 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.BaseImportingScope
|
||||
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.addToStdlib.flatMapToNullable
|
||||
|
||||
class AllUnderImportScope(
|
||||
descriptor: DeclarationDescriptor,
|
||||
excludedImportNames: Collection<FqName>
|
||||
) : 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)
|
||||
}
|
||||
else {
|
||||
@@ -49,6 +51,8 @@ class AllUnderImportScope(
|
||||
excludedImportNames.mapNotNull { if (it.parent() == fqName) it.shortName() else null }.toSet()
|
||||
}
|
||||
|
||||
override fun computeImportedNames(): Set<Name>? = scopes.flatMapToNullable(hashSetOf(), MemberScope::computeAllNames)
|
||||
|
||||
override fun getContributedDescriptors(
|
||||
kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean,
|
||||
@@ -82,6 +86,10 @@ class AllUnderImportScope(
|
||||
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) {
|
||||
p.println(this::class.java.simpleName)
|
||||
}
|
||||
|
||||
@@ -316,6 +316,9 @@ public class BodyResolver {
|
||||
if (descriptor.getKind() == ClassKind.INTERFACE) {
|
||||
trace.report(SUPERTYPE_INITIALIZED_IN_INTERFACE.on(elementToMark));
|
||||
}
|
||||
if (descriptor.isExpect()) {
|
||||
trace.report(SUPERTYPE_INITIALIZED_IN_EXPECTED_CLASS.on(elementToMark));
|
||||
}
|
||||
KtTypeReference typeReference = call.getTypeReference();
|
||||
if (typeReference == null) return;
|
||||
if (primaryConstructor == null) {
|
||||
|
||||
@@ -646,6 +646,9 @@ class DeclarationsChecker(
|
||||
if (inInterface) {
|
||||
trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate))
|
||||
}
|
||||
else if (isExpect) {
|
||||
trace.report(EXPECTED_DELEGATED_PROPERTY.on(delegate))
|
||||
}
|
||||
}
|
||||
else {
|
||||
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
|
||||
@@ -667,9 +670,14 @@ class DeclarationsChecker(
|
||||
else if (noExplicitTypeOrGetterType(property)) {
|
||||
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
||||
}
|
||||
if (backingFieldRequired && !inInterface && propertyDescriptor.isLateInit && !isUninitialized &&
|
||||
trace[MUST_BE_LATEINIT, propertyDescriptor] != true) {
|
||||
trace.report(UNNECESSARY_LATEINIT.on(property))
|
||||
|
||||
if (propertyDescriptor.isLateInit) {
|
||||
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
|
||||
}
|
||||
|
||||
override fun computeImportedNames() = setOf(aliasName)
|
||||
|
||||
override fun printStructure(p: Printer) {
|
||||
p.println(this::class.java.simpleName, ": ", aliasName)
|
||||
}
|
||||
@@ -148,4 +150,4 @@ class LazyExplicitImportScope(
|
||||
private fun <D : CallableMemberDescriptor> Collection<D>.choseOnlyVisibleOrAll() =
|
||||
filter { isVisible(it, packageFragmentForVisibilityCheck, position = QualifierPosition.IMPORT) }.
|
||||
takeIf { it.isNotEmpty() } ?: this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ object ModifierCheckerCore {
|
||||
CONST_KEYWORD to EnumSet.of(MEMBER_PROPERTY, TOP_LEVEL_PROPERTY),
|
||||
OPERATOR_KEYWORD to EnumSet.of(FUNCTION),
|
||||
INFIX_KEYWORD to EnumSet.of(FUNCTION),
|
||||
HEADER_KEYWORD to EnumSet.of(TOP_LEVEL_FUNCTION, TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE, CLASS_ONLY, OBJECT, INTERFACE, ENUM_CLASS, ANNOTATION_CLASS),
|
||||
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),
|
||||
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)
|
||||
)
|
||||
|
||||
@@ -191,9 +191,6 @@ object ModifierCheckerCore {
|
||||
// (see the KEEP https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md)
|
||||
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
|
||||
result += incompatibilityRegister(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD, ACTUAL_KEYWORD)
|
||||
|
||||
|
||||
+6
-5
@@ -53,7 +53,7 @@ class AssigningNamedArgumentToVarargChecker : CallChecker {
|
||||
checkAssignmentOfSingleElementInAnnotation(argument, argumentExpression, context)
|
||||
}
|
||||
else {
|
||||
checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context)
|
||||
checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context, parameterDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,21 +64,22 @@ class AssigningNamedArgumentToVarargChecker : CallChecker {
|
||||
) {
|
||||
if (isArrayOrArrayLiteral(argument, context)) {
|
||||
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 {
|
||||
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(
|
||||
argument: ValueArgument,
|
||||
argumentExpression: KtExpression,
|
||||
context: ResolutionContext<*>
|
||||
context: ResolutionContext<*>,
|
||||
parameterDescriptor: ValueParameterDescriptor
|
||||
) {
|
||||
if (!argument.hasSpread()) {
|
||||
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM.on(argumentExpression))
|
||||
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION.on(argumentExpression, parameterDescriptor.type))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -168,10 +168,10 @@ class NewResolutionOldInference(
|
||||
val processor = kind.createTowerProcessor(this, nameToResolve, tracing, scopeTower, detailedReceiver, context)
|
||||
|
||||
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
|
||||
val emptyOrInapplicableCandidates = candidates.isEmpty() ||
|
||||
@@ -179,7 +179,7 @@ class NewResolutionOldInference(
|
||||
if (isBinaryRemOperator && shouldUseOperatorRem && emptyOrInapplicableCandidates) {
|
||||
val deprecatedName = OperatorConventions.REM_TO_MOD_OPERATION_NAMES[name]
|
||||
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()) {
|
||||
|
||||
+24
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -131,7 +131,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
if (!hasExpectedModifier) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -362,7 +364,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
|
||||
val aParams = a.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 bTypeParams = b.typeParameters
|
||||
@@ -401,6 +405,23 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
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 {
|
||||
if (a == null) return b == null
|
||||
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.KtImportsFactory
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
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.scopes.*
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.script.getScriptExternalDependencies
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
@@ -68,6 +65,41 @@ class FileScopeFactory(
|
||||
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 val file: KtFile,
|
||||
private val existingImports: ImportingScope?,
|
||||
@@ -77,8 +109,8 @@ class FileScopeFactory(
|
||||
val imports = file.importDirectives
|
||||
val aliasImportNames = imports.mapNotNull { if (it.aliasName != null) it.importedFqName else null }
|
||||
|
||||
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace)
|
||||
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace) // TODO: should we count excludedImports here also?
|
||||
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace, aliasImportNames, packageFragment)
|
||||
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace, aliasImportNames, packageFragment) // TODO: should we count excludedImports here also?
|
||||
|
||||
val lazyImportingScope = object : ImportingScope by ImportingScope.Empty {
|
||||
// avoid constructing the scope before we query it
|
||||
@@ -107,33 +139,21 @@ class FileScopeFactory(
|
||||
|
||||
val result = FileScopes(lexicalScope, lazyImportingScope, importResolver)
|
||||
|
||||
fun createImportResolver(indexedImports: IndexedImports, trace: BindingTrace, excludedImports: List<FqName>? = null) =
|
||||
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)
|
||||
|
||||
private fun createDefaultImportResolversForFile(): Pair<LazyImportResolver, LazyImportResolver> {
|
||||
val extraImports = file.originalFile.virtualFile?.let { vFile ->
|
||||
val scriptExternalDependencies = getScriptExternalDependencies(vFile, file.project)
|
||||
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
|
||||
allImplicitImports
|
||||
}
|
||||
else {
|
||||
allImplicitImports.filter { it.isAllUnder || it.importedFqName !in aliasImportNames }
|
||||
}
|
||||
|
||||
val defaultExplicitImportResolver = createImportResolver(ExplicitImportsIndexed(defaultImportsFiltered), tempTrace)
|
||||
val defaultAllUnderImportResolver = createImportResolver(AllUnderImportsIndexed(defaultImportsFiltered), tempTrace, defaultImportProvider.excludedImports)
|
||||
fun createImportingScope(): LazyImportScope {
|
||||
val (defaultExplicitImportResolver, defaultAllUnderImportResolver) = createDefaultImportResolversForFile()
|
||||
|
||||
val dummyContainerDescriptor = DummyContainerDescriptor(file, packageFragment)
|
||||
|
||||
@@ -164,8 +184,6 @@ class FileScopeFactory(
|
||||
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 {
|
||||
@@ -180,6 +198,7 @@ class FileScopeFactory(
|
||||
parentScope: ImportingScope
|
||||
): ImportingScope {
|
||||
val scope = packageView.memberScope
|
||||
val names by lazy(LazyThreadSafetyMode.PUBLICATION) { scope.computeAllNames () }
|
||||
val packageName = packageView.fqName
|
||||
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
|
||||
}
|
||||
|
||||
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 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.util.collectionUtils.concat
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.flatMapToNullable
|
||||
import java.util.*
|
||||
|
||||
interface IndexedImports {
|
||||
@@ -81,7 +82,7 @@ class LazyImportResolver(
|
||||
val indexedImports: IndexedImports,
|
||||
excludedImportNames: Collection<FqName>,
|
||||
private val traceForImportResolve: BindingTrace,
|
||||
private val packageFragment: PackageFragmentDescriptor,
|
||||
private val packageFragment: PackageFragmentDescriptor?,
|
||||
val deprecationResolver: DeprecationResolver
|
||||
) : ImportResolver {
|
||||
private val importedScopesProvider = storageManager.createMemoizedFunctionWithNullableValues {
|
||||
@@ -191,6 +192,19 @@ class LazyImportResolver(
|
||||
fun getImportScope(directive: KtImportDirective): ImportingScope {
|
||||
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(
|
||||
@@ -269,4 +283,12 @@ class LazyImportScope(
|
||||
p.popIndent()
|
||||
p.println("}")
|
||||
}
|
||||
|
||||
override fun definitelyDoesNotContainName(name: Name) = importResolver.definitelyDoesNotContainName(name)
|
||||
|
||||
override fun recordLookup(name: Name, location: LookupLocation) {
|
||||
importResolver.recordLookup(name, location)
|
||||
}
|
||||
|
||||
override fun computeImportedNames() = importResolver.allNames
|
||||
}
|
||||
|
||||
+11
-1
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.resolve.lazy.declarations
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap
|
||||
import com.google.common.collect.Sets
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
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 typeAliases = ArrayListMultimap.create<Name, KtTypeAlias>()
|
||||
val destructuringDeclarationsEntries = ArrayListMultimap.create<Name, KtDestructuringDeclarationEntry>()
|
||||
val names = Sets.newHashSet<Name>()
|
||||
|
||||
fun putToIndex(declaration: KtDeclaration) {
|
||||
if (declaration is KtAnonymousInitializer || declaration is KtSecondaryConstructor) return
|
||||
@@ -57,7 +59,9 @@ abstract class AbstractPsiBasedDeclarationProvider(storageManager: StorageManage
|
||||
}
|
||||
is KtDestructuringDeclaration -> {
|
||||
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 -> {
|
||||
@@ -65,11 +69,17 @@ abstract class AbstractPsiBasedDeclarationProvider(storageManager: StorageManage
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown declaration: " + declaration)
|
||||
}
|
||||
|
||||
when (declaration) {
|
||||
is KtNamedDeclaration -> names.add(safeNameForLazyResolve(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "allDeclarations: " + allDeclarations.mapNotNull { it.name }
|
||||
}
|
||||
|
||||
override fun getDeclarationNames() = index().names
|
||||
|
||||
private val index = storageManager.createLazyValue<Index> {
|
||||
val index = Index()
|
||||
doCreateIndex(index)
|
||||
|
||||
+2
@@ -41,4 +41,6 @@ class CombinedPackageMemberDeclarationProvider(
|
||||
override fun getClassOrObjectDeclarations(name: Name) = providers.flatMap { it.getClassOrObjectDeclarations(name) }
|
||||
|
||||
override fun getTypeAliasDeclarations(name: Name) = providers.flatMap { it.getTypeAliasDeclarations(name) }
|
||||
|
||||
override fun getDeclarationNames(): Set<Name> = providers.flatMapTo(HashSet()) { it.getDeclarationNames() }
|
||||
}
|
||||
|
||||
+2
@@ -33,4 +33,6 @@ interface DeclarationProvider {
|
||||
fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo>
|
||||
|
||||
fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias>
|
||||
|
||||
fun getDeclarationNames(): Set<Name>
|
||||
}
|
||||
|
||||
-2
@@ -219,8 +219,6 @@ protected constructor(
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
abstract fun recordLookup(name: Name, from: LookupLocation)
|
||||
|
||||
// Do not change this, override in concrete subclasses:
|
||||
// it is very easy to compromise laziness of this class, and fail all the debugging
|
||||
// a generic implementation can't do this properly
|
||||
|
||||
+4
@@ -58,6 +58,10 @@ class LazyPackageMemberScope(
|
||||
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
|
||||
override fun toString() = "lazy scope for package " + thisDescriptor.name
|
||||
}
|
||||
|
||||
+7
-2
@@ -18,10 +18,8 @@ package org.jetbrains.kotlin.types.expressions
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.TargetPlatformVersion
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.context.GlobalContext
|
||||
import org.jetbrains.kotlin.context.withModule
|
||||
@@ -200,6 +198,13 @@ class LocalLazyDeclarationResolver(
|
||||
}
|
||||
return super.getClassDescriptor(classOrObject, location)
|
||||
}
|
||||
|
||||
override fun getClassDescriptorIfAny(classOrObject: KtClassOrObject, location: LookupLocation): ClassDescriptor? {
|
||||
if (localClassDescriptorManager.isMyClass(classOrObject)) {
|
||||
return localClassDescriptorManager.getClassDescriptor(classOrObject, scopeProvider)
|
||||
}
|
||||
return super.getClassDescriptorIfAny(classOrObject, location)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
|
||||
data class BuildDifference(val ts: Long, val isIncremental: Boolean, val dirtyData: DirtyData)
|
||||
|
||||
// todo: storage format can be optimized by compressing fq-names
|
||||
data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
||||
companion object {
|
||||
fun readFromFile(file: File, reporter: ICReporter?): BuildDiffsStorage? {
|
||||
fun reportFail(reason: String) {
|
||||
reporter?.report { "Could not read diff from file $file: $reason" }
|
||||
}
|
||||
|
||||
if (!file.exists()) return null
|
||||
|
||||
try {
|
||||
ObjectInputStream(file.inputStream().buffered()).use { input ->
|
||||
val version = input.readInt()
|
||||
if (version != CURRENT_VERSION) {
|
||||
reportFail("incompatible version $version, actual version is $CURRENT_VERSION")
|
||||
return null
|
||||
}
|
||||
|
||||
val size = input.readInt()
|
||||
val result = ArrayList<BuildDifference>(size)
|
||||
repeat(size) {
|
||||
result.add(input.readBuildDifference())
|
||||
}
|
||||
return BuildDiffsStorage(result)
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
reportFail(e.toString())
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun writeToFile(file: File, storage: BuildDiffsStorage, reporter: ICReporter?) {
|
||||
file.parentFile.mkdirs()
|
||||
|
||||
try {
|
||||
ObjectOutputStream(file.outputStream().buffered()).use { output ->
|
||||
output.writeInt(CURRENT_VERSION)
|
||||
|
||||
val diffsToWrite = storage.buildDiffs.sortedBy { it.ts }.takeLast(MAX_DIFFS_ENTRIES)
|
||||
output.writeInt(diffsToWrite.size)
|
||||
for (diff in diffsToWrite) {
|
||||
output.writeBuildDifference(diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
reporter?.report { "Could not write diff to file $file: $e" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun ObjectInputStream.readBuildDifference(): BuildDifference {
|
||||
val ts = readLong()
|
||||
val isIncremental = readBoolean()
|
||||
val dirtyData = readDirtyData()
|
||||
return BuildDifference(ts, isIncremental, dirtyData)
|
||||
}
|
||||
|
||||
private fun ObjectOutputStream.writeBuildDifference(diff: BuildDifference) {
|
||||
writeLong(diff.ts)
|
||||
writeBoolean(diff.isIncremental)
|
||||
writeDirtyData(diff.dirtyData)
|
||||
}
|
||||
|
||||
private fun ObjectInputStream.readDirtyData(): DirtyData {
|
||||
val lookupSymbolSize = readInt()
|
||||
val lookupSymbols = ArrayList<LookupSymbol>(lookupSymbolSize)
|
||||
repeat(lookupSymbolSize) {
|
||||
val name = readUTF()
|
||||
val scope = readUTF()
|
||||
lookupSymbols.add(LookupSymbol(name = name, scope = scope))
|
||||
}
|
||||
|
||||
val dirtyClassesSize = readInt()
|
||||
val dirtyClassesFqNames = ArrayList<FqName>(dirtyClassesSize)
|
||||
repeat(dirtyClassesSize) {
|
||||
val fqNameString = readUTF()
|
||||
dirtyClassesFqNames.add(FqName(fqNameString))
|
||||
}
|
||||
|
||||
return DirtyData(lookupSymbols, dirtyClassesFqNames)
|
||||
}
|
||||
|
||||
private fun ObjectOutputStream.writeDirtyData(dirtyData: DirtyData) {
|
||||
val lookupSymbols = dirtyData.dirtyLookupSymbols
|
||||
writeInt(lookupSymbols.size)
|
||||
for ((name, scope) in lookupSymbols) {
|
||||
writeUTF(name)
|
||||
writeUTF(scope)
|
||||
}
|
||||
|
||||
val dirtyClassesFqNames = dirtyData.dirtyClassesFqNames
|
||||
writeInt(dirtyClassesFqNames.size)
|
||||
for (fqName in dirtyClassesFqNames) {
|
||||
writeUTF(fqName.asString())
|
||||
}
|
||||
}
|
||||
|
||||
internal val MAX_DIFFS_ENTRIES: Int = 10
|
||||
|
||||
@set:TestOnly
|
||||
var CURRENT_VERSION: Int = 0
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.incremental
|
||||
|
||||
import java.io.*
|
||||
|
||||
internal data class BuildInfo(val startTS: Long) : Serializable {
|
||||
data class BuildInfo(val startTS: Long) : Serializable {
|
||||
companion object {
|
||||
fun read(file: File): BuildInfo? =
|
||||
try {
|
||||
|
||||
+15
-10
@@ -255,16 +255,9 @@ abstract class IncrementalCompilerRunner<
|
||||
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
||||
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
||||
}
|
||||
if (changesRegistry != null) {
|
||||
if (compilationMode is CompilationMode.Incremental) {
|
||||
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
||||
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
||||
}
|
||||
else {
|
||||
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
||||
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
||||
}
|
||||
}
|
||||
|
||||
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
||||
processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
|
||||
|
||||
if (exitCode == ExitCode.OK) {
|
||||
cacheVersions.forEach { it.saveIfNeeded() }
|
||||
@@ -273,6 +266,18 @@ abstract class IncrementalCompilerRunner<
|
||||
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 {
|
||||
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
||||
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
||||
|
||||
+73
-23
@@ -16,10 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
|
||||
import org.jetbrains.kotlin.build.GeneratedFile
|
||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||
import org.jetbrains.kotlin.build.isModuleMappingFile
|
||||
import org.jetbrains.kotlin.build.JvmSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
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 java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
fun makeIncrementally(
|
||||
cachesDir: File,
|
||||
@@ -89,7 +90,9 @@ class IncrementalJvmCompilerRunner(
|
||||
reporter: ICReporter,
|
||||
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
||||
artifactChangesProvider: ArtifactChangesProvider? = null,
|
||||
changesRegistry: ChangesRegistry? = null
|
||||
changesRegistry: ChangesRegistry? = null,
|
||||
private val buildHistoryFile: File? = null,
|
||||
private val friendBuildHistoryFile: File? = null
|
||||
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
|
||||
workingDir,
|
||||
"caches-jvm",
|
||||
@@ -110,16 +113,58 @@ class IncrementalJvmCompilerRunner(
|
||||
private var javaFilesProcessor = ChangedJavaFilesProcessor(reporter)
|
||||
|
||||
override fun calculateSourcesToCompile(caches: IncrementalJvmCachesManager, changedFiles: ChangedFiles.Known, args: K2JVMCompilerArguments): CompilationMode {
|
||||
val removedClassFiles = changedFiles.removed.filter(File::isClassFile)
|
||||
if (removedClassFiles.any()) return CompilationMode.Rebuild { "Removed class files: ${reporter.pathsAsString(removedClassFiles)}" }
|
||||
val dirtyFiles = getDirtyFiles(changedFiles)
|
||||
|
||||
val modifiedClassFiles = changedFiles.modified.filter(File::isClassFile)
|
||||
if (modifiedClassFiles.any()) return CompilationMode.Rebuild { "Modified class files: ${reporter.pathsAsString(modifiedClassFiles)}" }
|
||||
fun markDirtyBy(lookupSymbols: Collection<LookupSymbol>) {
|
||||
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 modifiedClasspathEntries = changedFiles.modified.filter { it in classpathSet }
|
||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
||||
reporter.report { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||
val classpathChanges = getClasspathChanges(modifiedClasspathEntries, lastBuildInfo)
|
||||
if (classpathChanges !is ChangesEither.Known) {
|
||||
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" }
|
||||
}
|
||||
|
||||
val dirtyFiles = getDirtyFiles(changedFiles)
|
||||
val lookupSymbols = HashSet<LookupSymbol>()
|
||||
lookupSymbols.addAll(affectedJavaSymbols)
|
||||
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)
|
||||
}
|
||||
markDirtyBy(affectedJavaSymbols)
|
||||
markDirtyBy(classpathChanges.lookupSymbols)
|
||||
markDirtyBy(classpathChanges.fqNames)
|
||||
|
||||
return CompilationMode.Incremental(dirtyFiles)
|
||||
}
|
||||
@@ -263,6 +296,23 @@ class IncrementalJvmCompilerRunner(
|
||||
override fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
||||
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(
|
||||
args: K2JVMCompilerArguments,
|
||||
lookupTracker: LookupTracker,
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
class BuildDiffsStorageTest {
|
||||
lateinit var storageFile: File
|
||||
private val random = Random(System.currentTimeMillis())
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
storageFile = File.createTempFile("BuildDiffsStorageTest", "storage")
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
storageFile.delete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToString() {
|
||||
val lookupSymbols = listOf(LookupSymbol("foo", "bar"))
|
||||
val fqNames = listOf(FqName("fizz.Buzz"))
|
||||
val diff = BuildDifference(100, true, DirtyData(lookupSymbols, fqNames))
|
||||
val diffs = BuildDiffsStorage(listOf(diff))
|
||||
Assert.assertEquals("BuildDiffsStorage(buildDiffs=[BuildDifference(ts=100, isIncremental=true, dirtyData=DirtyData(dirtyLookupSymbols=[LookupSymbol(name=foo, scope=bar)], dirtyClassesFqNames=[fizz.Buzz]))])",
|
||||
diffs.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeReadSimple() {
|
||||
val diffs = BuildDiffsStorage(listOf(getRandomDiff()))
|
||||
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||
|
||||
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||
Assert.assertEquals(diffs.toString(), diffsDeserialized.toString())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun writeReadMany() {
|
||||
val generated = Array(20) { getRandomDiff() }.toList()
|
||||
val diffs = BuildDiffsStorage(generated)
|
||||
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||
|
||||
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||
val expected = generated.sortedBy { it.ts }.takeLast(BuildDiffsStorage.MAX_DIFFS_ENTRIES).toTypedArray()
|
||||
Assert.assertArrayEquals(expected, diffsDeserialized?.buildDiffs?.toTypedArray())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readFileNotExist() {
|
||||
storageFile.delete()
|
||||
|
||||
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||
Assert.assertEquals(null, diffsDeserialized)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun versionChanged() {
|
||||
val diffs = BuildDiffsStorage(listOf(getRandomDiff()))
|
||||
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||
|
||||
val versionBackup = BuildDiffsStorage.CURRENT_VERSION
|
||||
try {
|
||||
BuildDiffsStorage.CURRENT_VERSION++
|
||||
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||
Assert.assertEquals(null, diffsDeserialized)
|
||||
}
|
||||
finally {
|
||||
BuildDiffsStorage.CURRENT_VERSION = versionBackup
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRandomDiff(): BuildDifference {
|
||||
val ts = random.nextLong()
|
||||
val lookupSymbols = listOf(LookupSymbol("foo", "bar"))
|
||||
val fqNames = listOf(FqName("fizz.Buzz"))
|
||||
return BuildDifference(ts, true, DirtyData(lookupSymbols, fqNames))
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,13 @@ class KtLightAnnotationForSourceEntry(
|
||||
private val _initializers by lazyPub {
|
||||
delegate.initializers.mapIndexed { i, it ->
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -55,11 +55,11 @@ class KotlinCallResolver(
|
||||
}
|
||||
|
||||
if (collectAllCandidates) {
|
||||
val allCandidates = towerResolver.collectAllCandidates(scopeTower, processor)
|
||||
val allCandidates = towerResolver.collectAllCandidates(scopeTower, processor, kotlinCall.name)
|
||||
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)
|
||||
}
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ class CallableReferenceResolver(
|
||||
): Set<CallableReferenceCandidate> {
|
||||
val factory = CallableReferencesCandidateFactory(callableReference, callComponents, scopeTower, compatibilityChecker, expectedType)
|
||||
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(
|
||||
candidates,
|
||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
|
||||
|
||||
@@ -50,6 +50,8 @@ interface ScopeTowerLevel {
|
||||
fun getObjects(name: Name, extensionReceiver: ReceiverValueWithSmartCastInfo?): Collection<CandidateWithBoundDispatchReceiver>
|
||||
|
||||
fun getFunctions(name: Name, extensionReceiver: ReceiverValueWithSmartCastInfo?): Collection<CandidateWithBoundDispatchReceiver>
|
||||
|
||||
fun recordLookup(name: Name)
|
||||
}
|
||||
|
||||
interface CandidateWithBoundDispatchReceiver {
|
||||
|
||||
+53
-17
@@ -27,41 +27,54 @@ import java.util.*
|
||||
|
||||
abstract class AbstractInvokeTowerProcessor<C : Candidate>(
|
||||
protected val factoryProviderForInvoke: CandidateFactoryProviderForInvoke<C>,
|
||||
private val variableProcessor: ScopeTowerProcessor<C>
|
||||
protected val variableProcessor: ScopeTowerProcessor<C>
|
||||
) : ScopeTowerProcessor<C> {
|
||||
// todo optimize it
|
||||
private val previousData = ArrayList<TowerData>()
|
||||
private val invokeProcessors: MutableList<Collection<VariableInvokeProcessor>> = ArrayList()
|
||||
|
||||
private inner class VariableInvokeProcessor(val variableCandidate: C): ScopeTowerProcessor<C> {
|
||||
val invokeProcessor: ScopeTowerProcessor<C> = createInvokeProcessor(variableCandidate)
|
||||
protected fun hasInvokeProcessors() = invokeProcessors.isNotEmpty()
|
||||
|
||||
private inner class VariableInvokeProcessor(
|
||||
var variableCandidate: C,
|
||||
val invokeProcessor: ScopeTowerProcessor<C>
|
||||
): ScopeTowerProcessor<C> {
|
||||
|
||||
override fun process(data: TowerData)
|
||||
= invokeProcessor.process(data).map { candidateGroup ->
|
||||
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>> {
|
||||
previousData.add(data)
|
||||
|
||||
val candidateGroups = ArrayList<Collection<C>>(0)
|
||||
|
||||
for (processorsGroup in invokeProcessors) {
|
||||
candidateGroups.addAll(processorsGroup.processVariableGroup(data))
|
||||
if (mayDataBeApplicable(data)) {
|
||||
previousData.add(data)
|
||||
for (processorsGroup in invokeProcessors) {
|
||||
candidateGroups.addAll(processorsGroup.processVariableGroup(data))
|
||||
}
|
||||
}
|
||||
|
||||
for (variableCandidates in variableProcessor.process(data)) {
|
||||
val successfulVariables = variableCandidates.filter {
|
||||
it.isSuccessful
|
||||
val variableProcessors = variableCandidates.mapNotNull {
|
||||
if (it.isSuccessful) createVariableInvokeProcessor(it) else null
|
||||
}
|
||||
|
||||
if (successfulVariables.isNotEmpty()) {
|
||||
val variableProcessors = successfulVariables.map { VariableInvokeProcessor(it) }
|
||||
if (variableProcessors.isNotEmpty()) {
|
||||
invokeProcessors.add(variableProcessors)
|
||||
|
||||
for (oldData in previousData) {
|
||||
candidateGroups.addAll(variableProcessors.processVariableGroup(oldData))
|
||||
}
|
||||
@@ -95,11 +108,25 @@ class InvokeTowerProcessor<C : Candidate>(
|
||||
) {
|
||||
|
||||
// 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)
|
||||
?: return KnownResultProcessor(emptyList())
|
||||
?: return null
|
||||
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>(
|
||||
@@ -112,13 +139,19 @@ class InvokeExtensionTowerProcessor<C : Candidate>(
|
||||
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)
|
||||
?: return KnownResultProcessor(emptyList())
|
||||
?: return null
|
||||
val invokeDescriptor = scopeTower.getExtensionInvokeCandidateDescriptor(variableReceiver)
|
||||
?: return KnownResultProcessor(emptyList())
|
||||
?: return null
|
||||
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>(
|
||||
@@ -138,6 +171,9 @@ private class InvokeExtensionScopeTowerProcessor<C : Candidate>(
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// No lookups happen in `simpleProcess`
|
||||
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {}
|
||||
}
|
||||
|
||||
// todo debug info
|
||||
|
||||
+31
@@ -28,6 +28,8 @@ class KnownResultProcessor<out C>(
|
||||
): ScopeTowerProcessor<C> {
|
||||
override fun process(data: TowerData)
|
||||
= 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
|
||||
@@ -35,6 +37,11 @@ class PrioritizedCompositeScopeTowerProcessor<out C>(
|
||||
vararg val processors: ScopeTowerProcessor<C>
|
||||
) : ScopeTowerProcessor<C> {
|
||||
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
|
||||
@@ -42,6 +49,10 @@ class SamePriorityCompositeScopeTowerProcessor<out C>(
|
||||
private vararg val processors: SimpleScopeTowerProcessor<C>
|
||||
): SimpleScopeTowerProcessor<C> {
|
||||
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>(
|
||||
@@ -84,6 +95,14 @@ internal class ExplicitReceiverScopeTowerProcessor<C: Candidate>(
|
||||
}
|
||||
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>(
|
||||
@@ -103,6 +122,9 @@ private class QualifierScopeTowerProcessor<C: Candidate>(
|
||||
}
|
||||
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>(
|
||||
@@ -132,6 +154,15 @@ private class NoExplicitReceiverScopeTowerProcessor<C: Candidate>(
|
||||
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(
|
||||
|
||||
@@ -169,6 +169,13 @@ internal class MemberScopeTowerLevel(
|
||||
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) {
|
||||
@@ -189,6 +196,8 @@ internal class QualifierScopeTowerLevel(scopeTower: ImplicitScopeTower, val qual
|
||||
qualifier.staticScope).map {
|
||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||
}
|
||||
|
||||
override fun recordLookup(name: Name) {}
|
||||
}
|
||||
|
||||
// KT-3335 Creating imported super class' inner class fails in codegen
|
||||
@@ -216,6 +225,10 @@ internal open class ScopeBasedTowerLevel protected constructor(
|
||||
resolutionScope).map {
|
||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||
}
|
||||
|
||||
override fun recordLookup(name: Name) {
|
||||
resolutionScope.recordLookup(name, location)
|
||||
}
|
||||
}
|
||||
internal class ImportingScopeBasedTowerLevel(
|
||||
scopeTower: ImplicitScopeTower,
|
||||
@@ -247,6 +260,10 @@ internal class SyntheticScopeBasedTowerLevel(
|
||||
extensionReceiver: ReceiverValueWithSmartCastInfo?
|
||||
): Collection<CandidateWithBoundDispatchReceiver> =
|
||||
emptyList()
|
||||
|
||||
override fun recordLookup(name: Name) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal class HidesMembersTowerLevel(scopeTower: ImplicitScopeTower): AbstractScopeTowerLevel(scopeTower) {
|
||||
@@ -272,6 +289,8 @@ internal class HidesMembersTowerLevel(scopeTower: ImplicitScopeTower): AbstractS
|
||||
createCandidateDescriptor(it, dispatchReceiver = null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun recordLookup(name: Name) {}
|
||||
}
|
||||
|
||||
private fun KotlinType.getClassifierFromMeAndSuperclasses(name: Name, location: LookupLocation): ClassifierDescriptor? {
|
||||
|
||||
+178
-88
@@ -16,12 +16,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.tower
|
||||
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
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.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.ResolutionScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
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.*
|
||||
|
||||
interface Candidate {
|
||||
@@ -56,12 +62,16 @@ sealed class TowerData {
|
||||
class OnlyImplicitReceiver(val implicitReceiver: ReceiverValueWithSmartCastInfo): TowerData()
|
||||
class TowerLevel(val level: ScopeTowerLevel) : 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> {
|
||||
// Candidates with matched receivers (dispatch receiver was already matched in ScopeTowerLevel)
|
||||
// Candidates in one groups have same priority, first group has highest priority.
|
||||
fun process(data: TowerData): List<Collection<C>>
|
||||
|
||||
fun recordLookups(skippedData: Collection<TowerData>, name: Name)
|
||||
}
|
||||
|
||||
interface SimpleScopeTowerProcessor<out C> : ScopeTowerProcessor<C> {
|
||||
@@ -74,103 +84,183 @@ class TowerResolver {
|
||||
fun <C: Candidate> runResolve(
|
||||
scopeTower: ImplicitScopeTower,
|
||||
processor: ScopeTowerProcessor<C>,
|
||||
useOrder: Boolean
|
||||
): Collection<C> = scopeTower.run(processor, SuccessfulResultCollector(), useOrder)
|
||||
useOrder: Boolean,
|
||||
name: Name
|
||||
): Collection<C> = scopeTower.run(processor, SuccessfulResultCollector(), useOrder, name)
|
||||
|
||||
fun <C: Candidate> collectAllCandidates(
|
||||
scopeTower: ImplicitScopeTower,
|
||||
processor: ScopeTowerProcessor<C>
|
||||
processor: ScopeTowerProcessor<C>,
|
||||
name: Name
|
||||
): Collection<C>
|
||||
= scopeTower.run(processor, AllCandidatesCollector(), false)
|
||||
= scopeTower.run(processor, AllCandidatesCollector(), false, name)
|
||||
|
||||
private fun ImplicitScopeTower.createNonLocalLevels(): List<ScopeTowerLevel> {
|
||||
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(
|
||||
fun <C : Candidate> ImplicitScopeTower.run(
|
||||
processor: ScopeTowerProcessor<C>,
|
||||
resultCollector: ResultCollector<C>,
|
||||
useOrder: Boolean
|
||||
): Collection<C> {
|
||||
fun TowerData.process() = processTowerData(processor, resultCollector, useOrder, this)
|
||||
useOrder: Boolean,
|
||||
name: Name
|
||||
): Collection<C> = Task(this, processor, resultCollector, useOrder, name).run()
|
||||
|
||||
val localLevels = lexicalScope.parentsWithSelf.
|
||||
filterIsInstance<LexicalScope>().filter { it.kind.withLocalDescriptors }.
|
||||
map { ScopeBasedTowerLevel(this@run, it) }
|
||||
private inner class Task<out C : Candidate>(
|
||||
private val implicitScopeTower: ImplicitScopeTower,
|
||||
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
|
||||
var nonLocalLevels: Collection<ScopeTowerLevel>? = null
|
||||
val hidesMembersLevel = HidesMembersTowerLevel(this)
|
||||
val syntheticLevel = SyntheticScopeBasedTowerLevel(this, syntheticScopes)
|
||||
|
||||
// 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 }
|
||||
private val localLevels: Collection<ScopeTowerLevel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
implicitScopeTower.lexicalScope.parentsWithSelf.
|
||||
filterIsInstance<LexicalScope>().filter { it.kind.withLocalDescriptors && it.mayFitForName(name) }.
|
||||
map { ScopeBasedTowerLevel(implicitScopeTower, it) }.toList()
|
||||
}
|
||||
|
||||
for (scope in lexicalScope.parentsWithSelf) {
|
||||
if (scope is LexicalScope) {
|
||||
// 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 }
|
||||
}
|
||||
private val nonLocalLevels: Collection<ScopeTowerLevel> by lazy(LazyThreadSafetyMode.NONE) {
|
||||
implicitScopeTower.createNonLocalLevels()
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -234,7 +324,7 @@ class TowerResolver {
|
||||
val firstGroupWithResolved = candidateGroups.firstOrNull {
|
||||
it.any { it.resultingApplicability == ResolutionCandidateApplicability.RESOLVED }
|
||||
} ?: return null
|
||||
|
||||
|
||||
return firstGroupWithResolved.filter { it.resultingApplicability == ResolutionCandidateApplicability.RESOLVED }
|
||||
}
|
||||
|
||||
@@ -261,8 +351,8 @@ class TowerResolver {
|
||||
|
||||
return moreSuitableGroup.filter { it.resultingApplicability == groupApplicability }
|
||||
}
|
||||
|
||||
private val Collection<C>.groupApplicability get() =
|
||||
minBy { it.resultingApplicability }?.resultingApplicability ?: ResolutionCandidateApplicability.HIDDEN
|
||||
|
||||
private val Collection<C>.groupApplicability get() =
|
||||
minBy { it.resultingApplicability }?.resultingApplicability ?: ResolutionCandidateApplicability.HIDDEN
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,4 +124,7 @@ abstract class LexicalScopeStorage(
|
||||
} while (rest != null)
|
||||
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
|
||||
get() = LexicalScopeKind.EMPTY
|
||||
|
||||
override fun definitelyDoesNotContainName(name: Name) = true
|
||||
|
||||
override fun printStructure(p: Printer) {
|
||||
p.println("Base lexical scope with owner = $ownerDescriptor and parent = $parent")
|
||||
}
|
||||
@@ -119,10 +121,16 @@ interface ImportingScope : HierarchicalScope {
|
||||
return getContributedDescriptors(kindFilter, nameFilter, changeNamesForAliased = false)
|
||||
}
|
||||
|
||||
fun computeImportedNames(): Set<Name>?
|
||||
|
||||
object Empty : BaseImportingScope(null) {
|
||||
override fun printStructure(p: Printer) {
|
||||
p.println("ImportingScope.Empty")
|
||||
}
|
||||
|
||||
override fun computeImportedNames() = emptySet<Name>()
|
||||
|
||||
override fun definitelyDoesNotContainName(name: Name) = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -48,4 +48,5 @@ class SubpackagesImportingScope(
|
||||
//TODO: kept old behavior, but it seems very strange (super call seems more applicable)
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, changeNamesForAliased: Boolean): Collection<DeclarationDescriptor>
|
||||
= 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 computeImportedNames() = memberScope.computeAllNames()
|
||||
|
||||
override fun printStructure(p: Printer) {
|
||||
p.println(this::class.java.simpleName)
|
||||
p.pushIndent()
|
||||
|
||||
+40
-40
@@ -38,66 +38,66 @@ fun foo(dir: Direction): Int {
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(dir: Direction) INIT: in: {} out: {dir=D}
|
||||
magic[FAKE_INITIALIZER](dir: Direction) -> <v0> INIT: in: {dir=D} out: {dir=D}
|
||||
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}
|
||||
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}
|
||||
v(val res: Int) INIT: in: {dir=ID} out: {dir=ID, 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}
|
||||
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}
|
||||
v(dir: Direction) INIT: in: {} out: {}
|
||||
magic[FAKE_INITIALIZER](dir: Direction) -> <v0> INIT: in: {} out: {}
|
||||
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: {} out: {}
|
||||
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: {res=D} out: {res=D}
|
||||
r(dir) -> <v1>
|
||||
mark(Direction.NORTH -> res = 1)
|
||||
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}
|
||||
r(NORTH) -> <v2> USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ}
|
||||
mark(Direction.NORTH)
|
||||
r(NORTH) -> <v2>
|
||||
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]:
|
||||
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}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
||||
r(1) -> <v4> USE: in: {res=WRITTEN_AFTER_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: {res=ID} out: {res=ID}
|
||||
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) USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ}
|
||||
r(SOUTH) -> <v5> USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ}
|
||||
mark(Direction.SOUTH)
|
||||
r(SOUTH) -> <v5>
|
||||
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]:
|
||||
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}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
||||
r(2) -> <v7> USE: in: {res=WRITTEN_AFTER_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: {res=ID} out: {res=ID}
|
||||
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) USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ}
|
||||
r(WEST) -> <v8> USE: in: {EAST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ}
|
||||
mark(Direction.WEST)
|
||||
r(WEST) -> <v8>
|
||||
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]:
|
||||
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}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ}
|
||||
r(3) -> <v10> USE: in: {res=WRITTEN_AFTER_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: {res=ID} out: {res=ID}
|
||||
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) USE: in: {EAST=READ, res=READ} out: {EAST=READ, res=READ}
|
||||
r(EAST) -> <v11> USE: in: {res=READ} out: {EAST=READ, res=READ}
|
||||
mark(Direction.EAST)
|
||||
r(EAST) -> <v11>
|
||||
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]:
|
||||
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}
|
||||
jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID}
|
||||
r(4) -> <v13> USE: in: {res=WRITTEN_AFTER_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: {res=ID} out: {res=ID}
|
||||
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]:
|
||||
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}
|
||||
r(res) -> <v20> USE: in: {} 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}
|
||||
ret(*|<v20>) L1
|
||||
L1:
|
||||
1 <END> INIT: in: {dir=ID} out: {dir=ID}
|
||||
1 <END> INIT: in: {} out: {}
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
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:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(vararg a: Any?) INIT: in: {} out: {a=D}
|
||||
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
|
||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
|
||||
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
|
||||
v(vararg a: Any?) INIT: in: {} out: {}
|
||||
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {} out: {}
|
||||
w(a|<v0>) INIT: in: {} out: {}
|
||||
r(a) -> <v1> INIT: in: {} out: {}
|
||||
ret(*|<v1>) L1
|
||||
L1:
|
||||
<END>
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {}
|
||||
=====================
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
|
||||
+29
-29
@@ -8,24 +8,24 @@ fun foo() {
|
||||
}
|
||||
---------------------
|
||||
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) } })
|
||||
v(val a = 1) INIT: in: {} out: {a=D}
|
||||
r(1) -> <v0> INIT: in: {a=D} out: {a=D}
|
||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID}
|
||||
v(val f = { x: Int -> val y = x + a use(a) }) INIT: in: {a=ID} out: {a=ID, f=D}
|
||||
mark({ x: Int -> val y = x + a use(a) }) INIT: in: {a=ID, f=D} out: {a=ID, f=D}
|
||||
v(val a = 1) INIT: in: {} out: {}
|
||||
r(1) -> <v0> INIT: in: {} out: {}
|
||||
w(a|<v0>) INIT: in: {} out: {}
|
||||
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: {} out: {}
|
||||
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]:
|
||||
r({ x: Int -> val y = x + a use(a) }) -> <v1> INIT: in: {a=ID, f=D} out: {a=ID, f=D}
|
||||
w(f|<v1>) INIT: in: {a=ID, f=D} out: {a=ID, f=ID}
|
||||
r({ x: Int -> val y = x + a use(a) }) -> <v1>
|
||||
w(f|<v1>) INIT: in: {} out: {}
|
||||
L1:
|
||||
1 <END> INIT: in: {} out: {}
|
||||
error:
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> USE: in: {} out: {}
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
== anonymous_0 ==
|
||||
{ x: Int ->
|
||||
@@ -34,42 +34,42 @@ sink:
|
||||
}
|
||||
---------------------
|
||||
L3:
|
||||
3 <START> INIT: in: {a=ID, f=D} out: {a=ID, f=D}
|
||||
v(x: Int) INIT: in: {a=ID, f=D} out: {a=ID, f=D, x=D}
|
||||
magic[FAKE_INITIALIZER](x: Int) -> <v0> INIT: in: {a=ID, f=D, x=D} out: {a=ID, f=D, x=D}
|
||||
w(x|<v0>) INIT: in: {a=ID, f=D, x=D} out: {a=ID, f=D, x=ID}
|
||||
4 mark(val y = x + a use(a)) INIT: in: {a=ID, f=D, x=ID} out: {a=ID, f=D, x=ID}
|
||||
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}
|
||||
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}
|
||||
3 <START> INIT: in: {} out: {}
|
||||
v(x: Int) INIT: in: {} out: {}
|
||||
magic[FAKE_INITIALIZER](x: Int) -> <v0> INIT: in: {} out: {}
|
||||
w(x|<v0>) INIT: in: {} out: {}
|
||||
4 mark(val y = x + a use(a)) INIT: in: {} out: {}
|
||||
v(val y = x + a) INIT: in: {} out: {}
|
||||
r(x) -> <v1> INIT: in: {} out: {}
|
||||
r(a) -> <v2>
|
||||
mark(x + a)
|
||||
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}
|
||||
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}
|
||||
w(y|<v3>) INIT: in: {} out: {}
|
||||
r(a) -> <v4> INIT: in: {} out: {}
|
||||
mark(use(a))
|
||||
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:
|
||||
<END>
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
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 ==
|
||||
fun use(vararg a: Any?) = a
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(vararg a: Any?) INIT: in: {} out: {a=D}
|
||||
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {a=D} out: {a=D}
|
||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID} USE: in: {a=READ} out: {a=READ}
|
||||
r(a) -> <v1> INIT: in: {a=ID} out: {a=ID} USE: in: {} out: {a=READ}
|
||||
v(vararg a: Any?) INIT: in: {} out: {}
|
||||
magic[FAKE_INITIALIZER](vararg a: Any?) -> <v0> INIT: in: {} out: {}
|
||||
w(a|<v0>) INIT: in: {} out: {}
|
||||
r(a) -> <v1> INIT: in: {} out: {}
|
||||
ret(*|<v1>) L1
|
||||
L1:
|
||||
<END>
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> INIT: in: {a=I?} out: {a=I?} USE: in: {} out: {}
|
||||
=====================
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
|
||||
+6
-6
@@ -3,19 +3,19 @@ fun bar(f: () -> Unit) = f()
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(f: () -> Unit) INIT: in: {} out: {f=D}
|
||||
magic[FAKE_INITIALIZER](f: () -> Unit) -> <v0> INIT: in: {f=D} out: {f=D}
|
||||
w(f|<v0>) INIT: in: {f=D} out: {f=ID} USE: in: {f=READ} out: {f=READ}
|
||||
r(f) -> <v1> INIT: in: {f=ID} out: {f=ID} USE: in: {} out: {f=READ}
|
||||
v(f: () -> Unit) INIT: in: {} out: {}
|
||||
magic[FAKE_INITIALIZER](f: () -> Unit) -> <v0> INIT: in: {} out: {}
|
||||
w(f|<v0>) INIT: in: {} out: {}
|
||||
r(f) -> <v1> INIT: in: {} out: {}
|
||||
mark(f())
|
||||
call(f(), invoke|<v1>) -> <v2>
|
||||
ret(*|<v2>) L1
|
||||
L1:
|
||||
<END>
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> INIT: in: {f=I?} out: {f=I?} USE: in: {} out: {}
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
== foo ==
|
||||
fun foo() {
|
||||
|
||||
+24
-24
@@ -7,21 +7,21 @@ fun foo() {
|
||||
}
|
||||
---------------------
|
||||
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 })
|
||||
v(val a = 1) INIT: in: {} out: {a=D}
|
||||
r(1) -> <v0> INIT: in: {a=D} out: {a=D}
|
||||
w(a|<v0>) INIT: in: {a=D} out: {a=ID}
|
||||
v(val b: Int) INIT: in: {a=ID} out: {a=ID, 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}
|
||||
w(b|<v1>) INIT: in: {a=ID, b=D} out: {a=ID, b=ID} USE: in: {} out: {b=ONLY_WRITTEN_NEVER_READ}
|
||||
r(42) -> <v2> INIT: in: {a=ID, b=ID} out: {a=ID, b=ID}
|
||||
v(val a = 1) INIT: in: {} out: {}
|
||||
r(1) -> <v0> INIT: in: {} out: {}
|
||||
w(a|<v0>) INIT: in: {} out: {}
|
||||
v(val b: Int) INIT: in: {} out: {b=D}
|
||||
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: {b=D} out: {b=ID} USE: in: {} out: {b=ONLY_WRITTEN_NEVER_READ}
|
||||
r(42) -> <v2> INIT: in: {b=ID} out: {b=ID}
|
||||
L1:
|
||||
1 <END> INIT: in: {} out: {}
|
||||
error:
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> USE: in: {} out: {}
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
== bar ==
|
||||
fun bar(foo: Foo) {
|
||||
@@ -32,23 +32,23 @@ fun bar(foo: Foo) {
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(foo: Foo) INIT: in: {} out: {foo=D}
|
||||
magic[FAKE_INITIALIZER](foo: Foo) -> <v0> INIT: in: {foo=D} out: {foo=D}
|
||||
w(foo|<v0>) INIT: in: {foo=D} out: {foo=ID}
|
||||
2 mark({ foo.c foo.c = 2 42 }) INIT: in: {foo=ID} out: {foo=ID}
|
||||
v(foo: Foo) INIT: in: {} out: {}
|
||||
magic[FAKE_INITIALIZER](foo: Foo) -> <v0> INIT: in: {} out: {}
|
||||
w(foo|<v0>) INIT: in: {} out: {}
|
||||
2 mark({ foo.c foo.c = 2 42 }) INIT: in: {} out: {}
|
||||
mark(foo.c)
|
||||
r(foo) -> <v1> USE: in: {c=READ, foo=READ} out: {c=READ, foo=READ}
|
||||
r(c|<v1>) -> <v2> USE: in: {c=ONLY_WRITTEN_NEVER_READ, foo=READ} out: {c=READ, foo=READ}
|
||||
r(foo) -> <v3> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ, foo=READ}
|
||||
r(2) -> <v4> USE: in: {c=ONLY_WRITTEN_NEVER_READ} out: {c=ONLY_WRITTEN_NEVER_READ}
|
||||
w(foo.c|<v3>, <v4>) USE: in: {} out: {c=ONLY_WRITTEN_NEVER_READ}
|
||||
r(foo) -> <v1>
|
||||
r(c|<v1>) -> <v2>
|
||||
r(foo) -> <v3>
|
||||
r(2) -> <v4>
|
||||
w(foo.c|<v3>, <v4>)
|
||||
r(42) -> <v5>
|
||||
L1:
|
||||
1 <END>
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> INIT: in: {foo=I?} out: {foo=I?} USE: in: {} out: {}
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
== Foo ==
|
||||
interface Foo {
|
||||
@@ -57,11 +57,11 @@ interface Foo {
|
||||
---------------------
|
||||
L0:
|
||||
1 <START> INIT: in: {} out: {}
|
||||
v(var c: Int) INIT: in: {} out: {c=D}
|
||||
v(var c: Int) INIT: in: {} out: {}
|
||||
L1:
|
||||
<END> INIT: in: {c=D} out: {c=D}
|
||||
<END> INIT: in: {} out: {}
|
||||
error:
|
||||
<ERROR> INIT: in: {} out: {}
|
||||
<ERROR>
|
||||
sink:
|
||||
<SINK> INIT: in: {c=-} out: {c=-} USE: in: {} out: {}
|
||||
<SINK> USE: in: {} out: {}
|
||||
=====================
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user