Merge branch 'master' into KT-18765

This commit is contained in:
Andrey Mischenko
2017-10-04 17:43:11 +08:00
committed by GitHub
1030 changed files with 14609 additions and 3982 deletions
+7 -1
View File
@@ -52,7 +52,13 @@
<output url="file://$PROJECT_DIR$/out" />
</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
View File
@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Generate Tests" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/generators" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="generateTests" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<method />
</configuration>
</component>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProtobufSettings">
<option name="includePaths">
<list>
<option value="$PROJECT_DIR$" />
</list>
</option>
</component>
</project>
+8 -3
View File
@@ -1,6 +1,6 @@
[![official project](http://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
<a href="http://slack.kotlinlang.org/"><img src="http://slack.kotlinlang.org/badge.svg" height="20"></a>
[![TeamCity (simple build status)](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/bt345.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=bt345&branch_Kotlin=%3Cdefault%3E&tab=buildTypeStatusDiv)
[![TeamCity (simple build status)](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/Kotlin_dev_Compiler.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=Kotlin_dev_Compiler&branch_Kotlin_dev=%3Cdefault%3E&tab=buildTypeStatusDiv)
[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlin/kotlin-maven-plugin.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jetbrains.kotlin%22)
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)
@@ -40,6 +40,8 @@ In order to build Kotlin distribution you need to have:
For local development, if you're not working on bytecode generation or the standard library, it's OK to have only JDK 8 installed, and to point all of the environment variables mentioned above to your JDK 8 installation.
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
+2
View File
@@ -24,6 +24,8 @@ sourceSets {
}
runtimeJar()
sourcesJar()
javadocJar()
testsJar()
@@ -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
View File
@@ -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"
+170
View File
@@ -0,0 +1,170 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.gradle.api.Project
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.ConventionTask
import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.kotlin.dsl.*
import java.io.File
fun Project.configureInstrumentation() {
plugins.matching { it::class.java.canonicalName.startsWith("org.jetbrains.kotlin.gradle.plugin") }.all {
// When we change the output classes directory, Gradle will automatically configure
// the test compile tasks to use the instrumented classes. Normally this is fine,
// however, it causes problems for Kotlin projects:
// The "internal" modifier can be used to restrict access to the same module.
// To make it possible to use internal methods from the main source set in test classes,
// the Kotlin Gradle plugin adds the original output directory of the Java task
// as "friendly directory" which makes it possible to access internal members
// of the main module. Also this directory should be available on classpath during compilation
// This fails when we change the classes dir. The easiest fix is to prepend the
// classes from the "friendly directory" to the compile classpath.
val testCompile = tasks.findByName("compileTestKotlin") as AbstractCompile?
testCompile?.doFirst {
val mainSourceSet = the<JavaPluginConvention>().sourceSets.getByName("main")
testCompile.classpath = (testCompile.classpath
- mainSourceSet.output.classesDirs
+ files((mainSourceSet as ExtensionAware).extra.get("classesDirsCopy")))
}
}
afterEvaluate {
the<JavaPluginConvention>().sourceSets.all { sourceSetParam ->
// This copy will ignore filters, but they are unlikely to be used.
val classesDirs = (sourceSetParam.output.classesDirs as ConfigurableFileCollection).from as Collection<Any>
val classesDirsCopy = project.files(classesDirs.toTypedArray()).filter { it.exists() }
(sourceSetParam as ExtensionAware).extra.set("classesDirsCopy", classesDirsCopy)
logger.info("Saving old sources dir for project ${project.name}")
val instrumentedClassesDir = File(project.buildDir, "classes/${sourceSetParam.name}-instrumented")
(sourceSetParam.output.classesDirs as ConfigurableFileCollection).setFrom(instrumentedClassesDir)
val instrumentTask = project.tasks.create(sourceSetParam.getTaskName("instrument", "classes"), IntelliJInstrumentCodeTask::class.java)
instrumentTask.apply {
dependsOn(sourceSetParam.classesTaskName).onlyIf { !classesDirsCopy.isEmpty }
sourceSet = sourceSetParam
originalClassesDirs = classesDirsCopy
output = instrumentedClassesDir
}
instrumentTask.outputs.dir(instrumentedClassesDir)
// Ensure that our task is invoked when the source set is built
sourceSetParam.compiledBy(instrumentTask)
@Suppress("UNUSED_EXPRESSION")
true
}
}
}
@CacheableTask
open class IntelliJInstrumentCodeTask : ConventionTask() {
companion object {
private const val FILTER_ANNOTATION_REGEXP_CLASS = "com.intellij.ant.ClassFilterAnnotationRegexp"
private const val LOADER_REF = "java2.loader"
}
var sourceSet: SourceSet? = null
@Input
var originalClassesDirs: FileCollection? = null
@get:InputFiles
val sourceDirs: FileCollection
get() = project.files(sourceSet!!.allSource.srcDirs.filter { !sourceSet!!.resources.contains(it) && it.exists() })
@get:OutputDirectory
var output: File? = null
@TaskAction
fun instrumentClasses() {
logger.info("input files are: ${originalClassesDirs?.joinToString("; ", transform = { "'${it.name}'${if (it.exists()) "" else " (does not exists)" }"})}")
copyOriginalClasses()
val classpath = project.ideaSdkDeps("javac2.jar", "jdom.jar", "asm-all.jar", "jgoodies-forms.jar")
ant.withGroovyBuilder {
"taskdef"("name" to "instrumentIdeaExtensions",
"classpath" to classpath.asPath,
"loaderref" to LOADER_REF,
"classname" to "com.intellij.ant.InstrumentIdeaExtensions")
}
logger.info("Compiling forms and instrumenting code with nullability preconditions")
val instrumentNotNull = prepareNotNullInstrumenting(classpath)
instrumentCode(sourceDirs, instrumentNotNull)
}
private fun copyOriginalClasses() {
project.copy {
from(originalClassesDirs)
into(output)
}
}
private fun prepareNotNullInstrumenting(classpath: ConfigurableFileCollection): Boolean {
ant.withGroovyBuilder {
"typedef"("name" to "skip",
"classpath" to classpath.asPath,
"loaderref" to LOADER_REF,
"classname" to FILTER_ANNOTATION_REGEXP_CLASS)
}
return true
}
private fun instrumentCode(srcDirs: FileCollection, instrumentNotNull: Boolean) {
val headlessOldValue = System.setProperty("java.awt.headless", "true")
// Instrumentation needs to have access to sources of forms for inclusion
val depSourceDirectorySets = project.configurations["compile"].dependencies.withType(ProjectDependency::class.java)
.map { p -> p.dependencyProject.the<JavaPluginConvention>().sourceSets.getByName("main").allSource.sourceDirectories }
val instrumentationClasspath =
depSourceDirectorySets.fold(sourceSet!!.compileClasspath) { acc, v -> acc + v }.asPath.also {
logger.info("Using following dependency source dirs: $it")
}
logger.info("Running instrumentIdeaExtensions with srcdir=${srcDirs.asPath}}, destdir=$output and classpath=$instrumentationClasspath")
ant.withGroovyBuilder {
"instrumentIdeaExtensions"("srcdir" to srcDirs.asPath,
"destdir" to output,
"classpath" to instrumentationClasspath,
"includeantruntime" to false,
"instrumentNotNull" to instrumentNotNull) {
if (instrumentNotNull) {
ant.withGroovyBuilder {
"skip"("pattern" to "kotlin/Metadata")
}
}
}
}
if (headlessOldValue != null) {
System.setProperty("java.awt.headless", headlessOldValue)
} else {
System.clearProperty("java.awt.headless")
}
}
}
+2 -1
View File
@@ -19,7 +19,8 @@ data class JdkId(val explicit: Boolean, val majorVersion: JdkMajorVersion, var v
fun Project.getConfiguredJdks(): List<JdkId> {
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) {
@@ -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()))
}
}
@@ -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)
}
}
}
@@ -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;
@@ -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(),
@@ -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)
}
@@ -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() })
}
}
@@ -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
View File
@@ -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")
}
@@ -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(
@@ -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?
@@ -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()
}
@@ -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(
@@ -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) {
@@ -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
}
}
}
@@ -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
@@ -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
@@ -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");
@@ -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
@@ -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)
@@ -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))
}
}
@@ -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()) {
@@ -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
}
@@ -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)
@@ -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() }
}
@@ -33,4 +33,6 @@ interface DeclarationProvider {
fun getClassOrObjectDeclarations(name: Name): Collection<KtClassLikeInfo>
fun getTypeAliasDeclarations(name: Name): Collection<KtTypeAlias>
fun getDeclarationNames(): Set<Name>
}
@@ -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
@@ -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
}
@@ -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)
}
}
@@ -0,0 +1,132 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.incremental
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.name.FqName
import java.io.File
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
data class BuildDifference(val ts: Long, val isIncremental: Boolean, val dirtyData: DirtyData)
// todo: storage format can be optimized by compressing fq-names
data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
companion object {
fun readFromFile(file: File, reporter: ICReporter?): BuildDiffsStorage? {
fun reportFail(reason: String) {
reporter?.report { "Could not read diff from file $file: $reason" }
}
if (!file.exists()) return null
try {
ObjectInputStream(file.inputStream().buffered()).use { input ->
val version = input.readInt()
if (version != CURRENT_VERSION) {
reportFail("incompatible version $version, actual version is $CURRENT_VERSION")
return null
}
val size = input.readInt()
val result = ArrayList<BuildDifference>(size)
repeat(size) {
result.add(input.readBuildDifference())
}
return BuildDiffsStorage(result)
}
}
catch (e: IOException) {
reportFail(e.toString())
}
return null
}
fun writeToFile(file: File, storage: BuildDiffsStorage, reporter: ICReporter?) {
file.parentFile.mkdirs()
try {
ObjectOutputStream(file.outputStream().buffered()).use { output ->
output.writeInt(CURRENT_VERSION)
val diffsToWrite = storage.buildDiffs.sortedBy { it.ts }.takeLast(MAX_DIFFS_ENTRIES)
output.writeInt(diffsToWrite.size)
for (diff in diffsToWrite) {
output.writeBuildDifference(diff)
}
}
}
catch (e: IOException) {
reporter?.report { "Could not write diff to file $file: $e" }
}
}
private fun ObjectInputStream.readBuildDifference(): BuildDifference {
val ts = readLong()
val isIncremental = readBoolean()
val dirtyData = readDirtyData()
return BuildDifference(ts, isIncremental, dirtyData)
}
private fun ObjectOutputStream.writeBuildDifference(diff: BuildDifference) {
writeLong(diff.ts)
writeBoolean(diff.isIncremental)
writeDirtyData(diff.dirtyData)
}
private fun ObjectInputStream.readDirtyData(): DirtyData {
val lookupSymbolSize = readInt()
val lookupSymbols = ArrayList<LookupSymbol>(lookupSymbolSize)
repeat(lookupSymbolSize) {
val name = readUTF()
val scope = readUTF()
lookupSymbols.add(LookupSymbol(name = name, scope = scope))
}
val dirtyClassesSize = readInt()
val dirtyClassesFqNames = ArrayList<FqName>(dirtyClassesSize)
repeat(dirtyClassesSize) {
val fqNameString = readUTF()
dirtyClassesFqNames.add(FqName(fqNameString))
}
return DirtyData(lookupSymbols, dirtyClassesFqNames)
}
private fun ObjectOutputStream.writeDirtyData(dirtyData: DirtyData) {
val lookupSymbols = dirtyData.dirtyLookupSymbols
writeInt(lookupSymbols.size)
for ((name, scope) in lookupSymbols) {
writeUTF(name)
writeUTF(scope)
}
val dirtyClassesFqNames = dirtyData.dirtyClassesFqNames
writeInt(dirtyClassesFqNames.size)
for (fqName in dirtyClassesFqNames) {
writeUTF(fqName.asString())
}
}
internal val MAX_DIFFS_ENTRIES: Int = 10
@set:TestOnly
var CURRENT_VERSION: Int = 0
}
}
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.incremental
import java.io.*
internal data class BuildInfo(val startTS: Long) : Serializable {
data class BuildInfo(val startTS: Long) : Serializable {
companion object {
fun read(file: File): BuildInfo? =
try {
@@ -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"
@@ -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,
@@ -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)
}
@@ -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 {
@@ -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
@@ -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? {
@@ -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
}
}
@@ -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()
@@ -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: {}
=====================
@@ -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: {}
=====================
@@ -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() {
@@ -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