mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-17 00:21:27 +00:00
Compare commits
10 Commits
push/mg-ja
...
ssb/separa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7fd140329 | ||
|
|
4bfa5ad532 | ||
|
|
b694fcdac6 | ||
|
|
b991d3cd4c | ||
|
|
f95c1a0996 | ||
|
|
216e306e58 | ||
|
|
1b6ff4d8bd | ||
|
|
65358742dd | ||
|
|
e05c139668 | ||
|
|
d7283d2983 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,7 +12,7 @@
|
||||
/android-studio/sdk
|
||||
out/
|
||||
/tmp
|
||||
/intellij
|
||||
kotlin-ide/
|
||||
workspace.xml
|
||||
*.versionsBackup
|
||||
/idea/testData/debugger/tinyApp/classes*
|
||||
@@ -68,4 +68,3 @@ distTmp/
|
||||
outTmp/
|
||||
/test.output
|
||||
/kotlin-native/dist
|
||||
kotlin-ide/
|
||||
|
||||
1
.idea/dictionaries/igor.xml
generated
1
.idea/dictionaries/igor.xml
generated
@@ -1,7 +1,6 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="igor">
|
||||
<words>
|
||||
<w>addr</w>
|
||||
<w>descr</w>
|
||||
<w>exprs</w>
|
||||
</words>
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||
import org.jetbrains.kotlin.incremental.storage.*
|
||||
import org.jetbrains.kotlin.inline.inlineFunctionsJvmNames
|
||||
import org.jetbrains.kotlin.load.kotlin.FileBasedKotlinClass
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.JvmPackagePartProto
|
||||
@@ -114,7 +113,7 @@ open class IncrementalJvmCache(
|
||||
}
|
||||
|
||||
open fun saveFileToCache(generatedClass: GeneratedJvmClass, changesCollector: ChangesCollector) {
|
||||
saveClassToCache(KotlinClassInfo.createFrom(generatedClass.outputClass), generatedClass.sourceFiles, changesCollector)
|
||||
saveClassToCache(KotlinClassInfo(generatedClass.outputClass), generatedClass.sourceFiles, changesCollector)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -613,6 +612,16 @@ class KotlinClassInfo private constructor(
|
||||
val inlineFunctionsMap: LinkedHashMap<String, Long>
|
||||
) {
|
||||
|
||||
constructor(kotlinClass: LocalFileKotlinClass) : this(
|
||||
kotlinClass.classId,
|
||||
kotlinClass.classHeader.kind,
|
||||
kotlinClass.classHeader.data,
|
||||
kotlinClass.classHeader.strings,
|
||||
kotlinClass.classHeader.multifileClassName,
|
||||
getConstantsMap(kotlinClass.fileContents),
|
||||
getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
|
||||
)
|
||||
|
||||
val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
|
||||
|
||||
fun scopeFqName(companion: Boolean = false) = when (classKind) {
|
||||
@@ -621,36 +630,6 @@ class KotlinClassInfo private constructor(
|
||||
}
|
||||
else -> className.packageFqName
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createFrom(kotlinClass: LocalFileKotlinClass): KotlinClassInfo {
|
||||
return KotlinClassInfo(
|
||||
kotlinClass.classId,
|
||||
kotlinClass.classHeader.kind,
|
||||
kotlinClass.classHeader.data,
|
||||
kotlinClass.classHeader.strings,
|
||||
kotlinClass.classHeader.multifileClassName,
|
||||
getConstantsMap(kotlinClass.fileContents),
|
||||
getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
|
||||
)
|
||||
}
|
||||
|
||||
/** Creates [KotlinClassInfo] from the given classContents, or returns `null` if the class is not a kotlinc-generated class. */
|
||||
fun tryCreateFrom(classContents: ByteArray): KotlinClassInfo? {
|
||||
return FileBasedKotlinClass.create(classContents) { classId, _, classHeader, _ ->
|
||||
KotlinClassInfo(
|
||||
classId,
|
||||
classHeader.kind,
|
||||
classHeader.data,
|
||||
classHeader.strings,
|
||||
classHeader.multifileClassName,
|
||||
getConstantsMap(classContents),
|
||||
getInlineFunctionsMap(classHeader, classContents)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConstantsMap(bytes: ByteArray): LinkedHashMap<String, Any> {
|
||||
@@ -715,4 +694,4 @@ private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray):
|
||||
}, 0)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -48,18 +48,13 @@ open class LookupStorage(
|
||||
|
||||
@Volatile
|
||||
private var size: Int = 0
|
||||
private var oldSize: Int = 0
|
||||
|
||||
init {
|
||||
try {
|
||||
if (countersFile.exists()) {
|
||||
val lines = countersFile.readLines()
|
||||
size = lines.firstOrNull()?.toIntOrNull() ?: throw IOException("$countersFile exists, but it is empty. " +
|
||||
"Counters file is corrupted")
|
||||
oldSize = size
|
||||
size = lines[0].toInt()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
throw IOException("Could not read $countersFile", e)
|
||||
}
|
||||
@@ -125,15 +120,13 @@ open class LookupStorage(
|
||||
@Synchronized
|
||||
override fun flush(memoryCachesOnly: Boolean) {
|
||||
try {
|
||||
if (size != oldSize) {
|
||||
if (size > 0) {
|
||||
if (!countersFile.exists()) {
|
||||
countersFile.parentFile.mkdirs()
|
||||
countersFile.createNewFile()
|
||||
}
|
||||
|
||||
countersFile.writeText("$size\n0")
|
||||
if (size > 0) {
|
||||
if (!countersFile.exists()) {
|
||||
countersFile.parentFile.mkdirs()
|
||||
countersFile.createNewFile()
|
||||
}
|
||||
|
||||
countersFile.writeText("$size\n0")
|
||||
}
|
||||
} finally {
|
||||
super.flush(memoryCachesOnly)
|
||||
|
||||
@@ -33,19 +33,15 @@ class CachingLazyStorage<K, V>(
|
||||
private val valueExternalizer: DataExternalizer<V>
|
||||
) : LazyStorage<K, V> {
|
||||
private var storage: PersistentHashMap<K, V>? = null
|
||||
private var isStorageFileExist = true
|
||||
|
||||
private fun getStorageIfExists(): PersistentHashMap<K, V>? {
|
||||
if (storage != null) return storage
|
||||
|
||||
if (!isStorageFileExist) return null
|
||||
|
||||
if (storageFile.exists()) {
|
||||
storage = createMap()
|
||||
return storage
|
||||
}
|
||||
|
||||
isStorageFileExist = false
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.IOUtil
|
||||
@@ -27,6 +26,7 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import java.io.DataInput
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutput
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ object ConstantsMapExternalizer : DataExternalizer<Map<String, Any>> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): Map<String, Any> {
|
||||
override fun read(input: DataInput): Map<String, Any>? {
|
||||
val size = input.readInt()
|
||||
val map = HashMap<String, Any>(size)
|
||||
|
||||
@@ -197,33 +197,15 @@ object IntExternalizer : DataExternalizer<Int> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Should be consistent with org.jetbrains.jps.incremental.storage.PathStringDescriptor for correct work of portable caches
|
||||
object PathStringDescriptor : EnumeratorStringDescriptor() {
|
||||
private const val PORTABLE_CACHES_PROPERTY = "org.jetbrains.jps.portable.caches"
|
||||
private val PORTABLE_CACHES = java.lang.Boolean.getBoolean(PORTABLE_CACHES_PROPERTY)
|
||||
override fun getHashCode(value: String) = FileUtil.pathHashCode(value)
|
||||
|
||||
override fun getHashCode(path: String): Int {
|
||||
if (!PORTABLE_CACHES) return FileUtil.pathHashCode(path)
|
||||
// On case insensitive OS hash calculated from value converted to lower case
|
||||
return if (StringUtil.isEmpty(path)) 0 else FileUtil.toCanonicalPath(path).hashCode()
|
||||
}
|
||||
|
||||
override fun isEqual(val1: String, val2: String?): Boolean {
|
||||
if (!PORTABLE_CACHES) return FileUtil.pathsEqual(val1, val2)
|
||||
// On case insensitive OS hash calculated from path converted to lower case
|
||||
if (val1 == val2) return true
|
||||
if (val2 == null) return false
|
||||
|
||||
val path1 = FileUtil.toCanonicalPath(val1)
|
||||
val path2 = FileUtil.toCanonicalPath(val2)
|
||||
return path1 == path2
|
||||
}
|
||||
override fun isEqual(val1: String, val2: String?) = FileUtil.pathsEqual(val1, val2)
|
||||
}
|
||||
|
||||
open class CollectionExternalizer<T>(
|
||||
private val elementExternalizer: DataExternalizer<T>,
|
||||
private val newCollection: () -> MutableCollection<T>
|
||||
private val elementExternalizer: DataExternalizer<T>,
|
||||
private val newCollection: () -> MutableCollection<T>
|
||||
) : DataExternalizer<Collection<T>> {
|
||||
override fun read(input: DataInput): Collection<T> {
|
||||
val result = newCollection()
|
||||
|
||||
@@ -29,7 +29,7 @@ buildscript {
|
||||
dependencies {
|
||||
bootstrapCompilerClasspath(kotlin("compiler-embeddable", bootstrapKotlinVersion))
|
||||
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.32")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
classpath(kotlin("gradle-plugin", bootstrapKotlinVersion))
|
||||
classpath(kotlin("serialization", bootstrapKotlinVersion))
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17")
|
||||
@@ -315,7 +315,15 @@ extra["compilerArtifactsForIde"] = listOf(
|
||||
|
||||
// TODO: fix remaining warnings and remove this property.
|
||||
extra["tasksWithWarnings"] = listOf(
|
||||
":kotlin-gradle-plugin:compileKotlin"
|
||||
":kotlin-stdlib:compileTestKotlin",
|
||||
":kotlin-stdlib-jdk7:compileTestKotlin",
|
||||
":kotlin-stdlib-jdk8:compileTestKotlin",
|
||||
":plugins:uast-kotlin-base:compileKotlin",
|
||||
":plugins:uast-kotlin-base:compileTestKotlin",
|
||||
":plugins:uast-kotlin:compileKotlin",
|
||||
":plugins:uast-kotlin:compileTestKotlin",
|
||||
":plugins:uast-kotlin-fir:compileKotlin",
|
||||
":plugins:uast-kotlin-fir:compileTestKotlin"
|
||||
)
|
||||
|
||||
val tasksWithWarnings: List<String> by extra
|
||||
@@ -435,12 +443,6 @@ allprojects {
|
||||
project.configureShadowJarSubstitutionInCompileClasspath()
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.compilerArgs.add("-Xlint:deprecation")
|
||||
options.compilerArgs.add("-Xlint:unchecked")
|
||||
options.compilerArgs.add("-Werror")
|
||||
}
|
||||
|
||||
val commonCompilerArgs = listOfNotNull(
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-progressive".takeIf { hasProperty("test.progressive.mode") }
|
||||
@@ -458,7 +460,6 @@ allprojects {
|
||||
"-Xjvm-default=compatibility",
|
||||
"-Xno-optimized-callable-references",
|
||||
"-Xno-kotlin-nothing-value-exception",
|
||||
"-Xskip-runtime-version-check",
|
||||
"-Xsuppress-deprecated-jvm-target-warning" // Remove as soon as there are no modules for JDK 1.6 & 1.7
|
||||
)
|
||||
|
||||
@@ -474,10 +475,15 @@ allprojects {
|
||||
}
|
||||
|
||||
if (!kotlinBuildProperties.disableWerror) {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
// For compiler and stdlib, allWarningsAsErrors is configured in the corresponding "root" projects
|
||||
// (compiler/build.gradle.kts and libraries/commonConfiguration.gradle).
|
||||
val projectsWithWarningsAsErrors = listOf("core", "plugins").map { File(it).absoluteFile }
|
||||
if (projectsWithWarningsAsErrors.any(projectDir::startsWith)) {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.32")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
classpath("org.jetbrains.kotlin:kotlin-sam-with-receiver:${project.bootstrapKotlinVersion}")
|
||||
}
|
||||
@@ -143,7 +143,7 @@ java {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib", embeddedKotlinVersion))
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.32")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
implementation("com.gradle.publish:plugin-publish-plugin:0.14.0")
|
||||
|
||||
implementation("net.rubygrapefruit:native-platform:${property("versions.native-platform")}")
|
||||
|
||||
@@ -79,6 +79,7 @@ val jpsStandalone by configurations.creating
|
||||
val jpsStandaloneForIde by configurations.creating
|
||||
val intellijCore by configurations.creating
|
||||
val intellijCoreForIde by configurations.creating
|
||||
val nodeJSPlugin by configurations.creating
|
||||
|
||||
/**
|
||||
* Special repository for annotations.jar required for idea runtime only.
|
||||
|
||||
@@ -22,14 +22,5 @@ val KotlinBuildProperties.ignoreTestFailures: Boolean get() = getBoolean("ignore
|
||||
val KotlinBuildProperties.disableWerror: Boolean
|
||||
get() = getBoolean("kotlin.build.disable.werror") || useFir || isInJpsBuildIdeaSync || getBoolean("test.progressive.mode")
|
||||
|
||||
val KotlinBuildProperties.pathToKotlinModularizedTestData: String?
|
||||
get() = getOrNull("kotlin.fir.modularized.testdata.kotlin") as? String
|
||||
|
||||
val KotlinBuildProperties.pathToIntellijModularizedTestData: String?
|
||||
get() = getOrNull("kotlin.fir.modularized.testdata.intellij") as? String
|
||||
|
||||
val KotlinBuildProperties.pathToYoutrackModularizedTestData: String?
|
||||
get() = getOrNull("kotlin.fir.modularized.testdata.youtrack") as? String
|
||||
|
||||
val KotlinBuildProperties.isObsoleteJdkOverrideEnabled: Boolean
|
||||
get() = getBoolean("kotlin.build.isObsoleteJdkOverrideEnabled", false)
|
||||
|
||||
@@ -89,4 +89,4 @@ val Project.isIdeaActive
|
||||
get() = providers.systemProperty("idea.active").forUseAtConfigurationTime().isPresent
|
||||
|
||||
val Project.intellijCommunityDir: File
|
||||
get() = rootDir.resolve("intellij/community").takeIf { it.isDirectory } ?: rootDir.resolve("intellij")
|
||||
get() = rootDir.resolve("kotlin-ide/intellij/community").takeIf { it.isDirectory } ?: rootDir.resolve("kotlin-ide/intellij")
|
||||
@@ -41,6 +41,7 @@ tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.apiVersion = "1.4"
|
||||
kotlinOptions.freeCompilerArgs += listOf(
|
||||
"-Xskip-prerelease-check",
|
||||
"-Xskip-runtime-version-check",
|
||||
"-Xsuppress-version-warnings",
|
||||
"-Xuse-ir" // Needed as long as languageVersion is less than 1.5.
|
||||
)
|
||||
|
||||
@@ -82,6 +82,8 @@ fun Project.intellijCoreDep() = "kotlin.build:intellij-core:${rootProject.extra[
|
||||
|
||||
fun Project.jpsStandalone() = "kotlin.build:jps-standalone:${rootProject.extra["versions.intellijSdk"]}"
|
||||
|
||||
fun Project.nodeJSPlugin() = "kotlin.build:NodeJS:${rootProject.extra["versions.idea.NodeJS"]}"
|
||||
|
||||
fun Project.jpsBuildTest() = "com.jetbrains.intellij.idea:jps-build-test:${rootProject.extra["versions.intellijSdk"]}"
|
||||
|
||||
fun Project.kotlinxCollectionsImmutable() = "org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:${rootProject.extra["versions.kotlinx-collections-immutable"]}"
|
||||
|
||||
@@ -5,15 +5,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.coroutinesIntrinsicsPackageFqName
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isTopLevelInPackage
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(): Boolean =
|
||||
fun FunctionDescriptor.isBuiltInIntercepted(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||
!languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines) &&
|
||||
isTopLevelInPackage("intercepted", languageVersionSettings.coroutinesIntrinsicsPackageFqName().asString())
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||
isTopLevelInPackage(
|
||||
"suspendCoroutineUninterceptedOrReturn",
|
||||
StandardNames.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME.asString()
|
||||
languageVersionSettings.coroutinesIntrinsicsPackageFqName().asString()
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
|
||||
@@ -26,7 +26,7 @@ class AccessorForConstructorDescriptor(
|
||||
containingDeclaration: DeclarationDescriptor,
|
||||
override val superCallTarget: ClassDescriptor?,
|
||||
override val accessorKind: AccessorKind
|
||||
) : AbstractAccessorForFunctionDescriptor(containingDeclaration, SpecialNames.INIT),
|
||||
) : AbstractAccessorForFunctionDescriptor(containingDeclaration, Name.special("<init>")),
|
||||
ClassConstructorDescriptor,
|
||||
AccessorForCallableDescriptor<ConstructorDescriptor> {
|
||||
|
||||
|
||||
@@ -475,7 +475,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
|
||||
List<Type> superCtorArgTypes = new ArrayList<>();
|
||||
if (superClassAsmType.equals(LAMBDA) || functionReferenceTarget != null ||
|
||||
CoroutineCodegenUtilKt.isCoroutineSuperClass(superClassAsmType.getInternalName())
|
||||
CoroutineCodegenUtilKt.isCoroutineSuperClass(state.getLanguageVersionSettings(), superClassAsmType.getInternalName())
|
||||
) {
|
||||
iv.iconst(CodegenUtilKt.getArity(funDescriptor));
|
||||
superCtorArgTypes.add(Type.INT_TYPE);
|
||||
|
||||
@@ -89,6 +89,7 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.ClassicTypeSystemContextImpl;
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker;
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker;
|
||||
import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
@@ -1287,11 +1288,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
@NotNull
|
||||
private static CallableDescriptor unwrapOriginalReceiverOwnerForSuspendLambda(@NotNull MethodContext context) {
|
||||
FunctionDescriptor originalForInvokeSuspend =
|
||||
context.getFunctionDescriptor().getUserData(CoroutineCodegenUtilKt.INITIAL_SUSPEND_DESCRIPTOR_FOR_INVOKE_SUSPEND);
|
||||
FunctionDescriptor originalForDoResume =
|
||||
context.getFunctionDescriptor().getUserData(CoroutineCodegenUtilKt.INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME);
|
||||
|
||||
if (originalForInvokeSuspend != null) {
|
||||
return originalForInvokeSuspend;
|
||||
if (originalForDoResume != null) {
|
||||
return originalForDoResume;
|
||||
}
|
||||
|
||||
if (context.getFunctionDescriptor().isSuspend()) {
|
||||
@@ -2616,6 +2617,26 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
? coroutineInstanceValueForSuspensionPoint
|
||||
: getContinuationParameterFromEnclosingSuspendFunction(resolvedCall);
|
||||
|
||||
if (coroutineInstanceValue != null && needsExperimentalCoroutinesWrapper(resolvedCall.getCandidateDescriptor())) {
|
||||
StackValue releaseContinuation = coroutineInstanceValue;
|
||||
coroutineInstanceValue = new StackValue(CoroutineCodegenUtilKt.EXPERIMENTAL_CONTINUATION_ASM_TYPE) {
|
||||
@Override
|
||||
public void putSelector(
|
||||
@NotNull Type type, @Nullable KotlinType kotlinType, @NotNull InstructionAdapter v
|
||||
) {
|
||||
releaseContinuation.put(CoroutineCodegenUtilKt.RELEASE_CONTINUATION_ASM_TYPE, v);
|
||||
invokeCoroutineMigrationMethod(
|
||||
v,
|
||||
"toExperimentalContinuation",
|
||||
Type.getMethodDescriptor(
|
||||
CoroutineCodegenUtilKt.EXPERIMENTAL_CONTINUATION_ASM_TYPE,
|
||||
CoroutineCodegenUtilKt.RELEASE_CONTINUATION_ASM_TYPE
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tempVariables.put(continuationExpression, coroutineInstanceValue);
|
||||
}
|
||||
|
||||
@@ -2753,7 +2774,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
SuspensionPointKind suspensionPointKind =
|
||||
CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, this);
|
||||
CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, this, state.getLanguageVersionSettings());
|
||||
boolean maybeSuspensionPoint = suspensionPointKind != SuspensionPointKind.NEVER && !insideCallableReference();
|
||||
boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
|
||||
if (!(callableMethod instanceof IntrinsicWithSpecialReceiver)) {
|
||||
@@ -2812,7 +2833,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
KotlinType unboxedInlineClass = CoroutineCodegenUtilKt.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass(
|
||||
(FunctionDescriptor) resolvedCall.getResultingDescriptor(), typeMapper);
|
||||
if (unboxedInlineClass != null) {
|
||||
CoroutineCodegenUtilKt.generateCoroutineSuspendedCheck(v);
|
||||
CoroutineCodegenUtilKt.generateCoroutineSuspendedCheck(v, state.getLanguageVersionSettings());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2927,7 +2948,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
FunctionDescriptor original =
|
||||
CoroutineCodegenUtilKt.getOriginalSuspendFunctionView(
|
||||
unwrapInitialSignatureDescriptor(DescriptorUtils.unwrapFakeOverride((FunctionDescriptor) descriptor.getOriginal())),
|
||||
bindingContext
|
||||
bindingContext, state
|
||||
);
|
||||
|
||||
FunctionDescriptor functionDescriptor =
|
||||
@@ -5222,7 +5243,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
}
|
||||
|
||||
CodegenUtilKt.generateAsCast(
|
||||
v, rightKotlinType, boxedRightType, safeAs, state.getUnifiedNullChecks()
|
||||
v, rightKotlinType, boxedRightType, safeAs, state.getLanguageVersionSettings(), state.getUnifiedNullChecks()
|
||||
);
|
||||
|
||||
return Unit.INSTANCE;
|
||||
@@ -5276,7 +5297,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
return null;
|
||||
}
|
||||
|
||||
CodegenUtilKt.generateIsCheck(v, rhsKotlinType, type);
|
||||
CodegenUtilKt.generateIsCheck(v, rhsKotlinType, type, state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -481,7 +481,8 @@ public class FunctionCodegen {
|
||||
}
|
||||
|
||||
if (!functionDescriptor.isExternal()) {
|
||||
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen, state.getJvmDefaultMode());
|
||||
generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, memberCodegen, state.getJvmDefaultMode(),
|
||||
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
|
||||
}
|
||||
else if (staticInCompanionObject) {
|
||||
// native @JvmStatic foo() in companion object should delegate to the static native function moved to the outer class
|
||||
@@ -582,7 +583,8 @@ public class FunctionCodegen {
|
||||
@NotNull JvmMethodSignature signature,
|
||||
@NotNull FunctionGenerationStrategy strategy,
|
||||
@NotNull MemberCodegen<?> parentCodegen,
|
||||
@NotNull JvmDefaultMode jvmDefaultMode
|
||||
@NotNull JvmDefaultMode jvmDefaultMode,
|
||||
boolean isReleaseCoroutines
|
||||
) {
|
||||
mv.visitCode();
|
||||
|
||||
@@ -592,7 +594,8 @@ public class FunctionCodegen {
|
||||
KotlinTypeMapper typeMapper = parentCodegen.typeMapper;
|
||||
if (BuiltinSpecialBridgesUtil.shouldHaveTypeSafeBarrier(functionDescriptor, typeMapper::mapAsmMethod)) {
|
||||
generateTypeCheckBarrierIfNeeded(
|
||||
new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), null, typeMapper);
|
||||
new InstructionAdapter(mv), functionDescriptor, signature.getReturnType(), null, typeMapper,
|
||||
isReleaseCoroutines);
|
||||
}
|
||||
|
||||
Label methodEntry = null;
|
||||
@@ -1427,7 +1430,8 @@ public class FunctionCodegen {
|
||||
MemberCodegen.markLineNumberForDescriptor(owner.getThisDescriptor(), iv);
|
||||
|
||||
if (delegateTo.getArgumentTypes().length > 0 && isSpecialBridge) {
|
||||
generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes(), typeMapper);
|
||||
generateTypeCheckBarrierIfNeeded(iv, descriptor, bridge.getReturnType(), delegateTo.getArgumentTypes(), typeMapper,
|
||||
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ReleaseCoroutines));
|
||||
}
|
||||
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
@@ -1473,7 +1477,8 @@ public class FunctionCodegen {
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull Type returnType,
|
||||
@Nullable Type[] delegateParameterTypes,
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
@NotNull KotlinTypeMapper typeMapper,
|
||||
boolean isReleaseCoroutines
|
||||
) {
|
||||
BuiltinMethodsWithSpecialGenericSignature.TypeSafeBarrierDescription typeSafeBarrierDescription =
|
||||
BuiltinMethodsWithSpecialGenericSignature.getDefaultValueForOverriddenBuiltinFunction(descriptor);
|
||||
@@ -1507,7 +1512,7 @@ public class FunctionCodegen {
|
||||
} else {
|
||||
targetBoxedType = boxType(delegateParameterTypes[i]);
|
||||
}
|
||||
CodegenUtilKt.generateIsCheck(iv, kotlinType, targetBoxedType);
|
||||
CodegenUtilKt.generateIsCheck(iv, kotlinType, targetBoxedType, isReleaseCoroutines);
|
||||
iv.ifeq(defaultBranch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.createFunctionType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName
|
||||
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isSuspendLambdaOrLocalFunction
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
@@ -32,7 +33,8 @@ class JvmRuntimeTypes(
|
||||
private val generateOptimizedCallableReferenceSuperClasses: Boolean
|
||||
) {
|
||||
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
|
||||
private val kotlinCoroutinesJvmInternalPackage = MutablePackageFragmentDescriptor(module, COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME)
|
||||
private val kotlinCoroutinesJvmInternalPackage =
|
||||
MutablePackageFragmentDescriptor(module, languageVersionSettings.coroutinesJvmInternalPackageFqName())
|
||||
|
||||
private fun internal(className: String, packageFragment: PackageFragmentDescriptor = kotlinJvmInternalPackage): Lazy<ClassDescriptor> =
|
||||
lazy { createClass(packageFragment, className) }
|
||||
@@ -51,16 +53,24 @@ class JvmRuntimeTypes(
|
||||
private val localVariableReference: ClassDescriptor by internal("LocalVariableReference")
|
||||
private val mutableLocalVariableReference: ClassDescriptor by internal("MutableLocalVariableReference")
|
||||
|
||||
private val coroutineImpl: ClassDescriptor by internal("CoroutineImpl", kotlinCoroutinesJvmInternalPackage)
|
||||
private val continuationImpl: ClassDescriptor by coroutinesInternal("ContinuationImpl")
|
||||
private val restrictedContinuationImpl: ClassDescriptor by coroutinesInternal("RestrictedContinuationImpl")
|
||||
private val suspendLambda: ClassDescriptor by coroutinesInternal("SuspendLambda")
|
||||
private val restrictedSuspendLambda: ClassDescriptor by coroutinesInternal("RestrictedSuspendLambda")
|
||||
|
||||
private val suspendFunctionInterface: ClassDescriptor? by lazy {
|
||||
createClass(kotlinCoroutinesJvmInternalPackage, "SuspendFunction", ClassKind.INTERFACE)
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
createClass(kotlinCoroutinesJvmInternalPackage, "SuspendFunction", ClassKind.INTERFACE)
|
||||
else null
|
||||
}
|
||||
|
||||
private fun createCoroutineSuperClass(className: String): ClassDescriptor = createClass(kotlinCoroutinesJvmInternalPackage, className)
|
||||
private fun createCoroutineSuperClass(className: String): ClassDescriptor {
|
||||
return if (languageVersionSettings.isReleaseCoroutines())
|
||||
createClass(kotlinCoroutinesJvmInternalPackage, className)
|
||||
else
|
||||
coroutineImpl
|
||||
}
|
||||
|
||||
private val propertyReferences: List<ClassDescriptor> by propertyClasses("PropertyReference", "")
|
||||
private val mutablePropertyReferences: List<ClassDescriptor> by propertyClasses("MutablePropertyReference", "")
|
||||
@@ -84,7 +94,7 @@ class JvmRuntimeTypes(
|
||||
fun getSupertypesForClosure(descriptor: FunctionDescriptor): Collection<KotlinType> {
|
||||
val actualFunctionDescriptor =
|
||||
if (descriptor.isSuspend)
|
||||
getOrCreateJvmSuspendFunctionView(descriptor)
|
||||
getOrCreateJvmSuspendFunctionView(descriptor, languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
else
|
||||
descriptor
|
||||
|
||||
@@ -108,7 +118,7 @@ class JvmRuntimeTypes(
|
||||
if (descriptor.isSuspend) {
|
||||
return mutableListOf<KotlinType>().apply {
|
||||
if (actualFunctionDescriptor.extensionReceiverParameter?.type
|
||||
?.isRestrictsSuspensionReceiver() == true
|
||||
?.isRestrictsSuspensionReceiver(languageVersionSettings) == true
|
||||
) {
|
||||
if (descriptor.isSuspendLambdaOrLocalFunction()) {
|
||||
add(restrictedSuspendLambda.defaultType)
|
||||
|
||||
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.name.SpecialNames;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinFileStub;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
@@ -299,7 +298,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
|
||||
private String getName(ClassDescriptor classDescriptor) {
|
||||
String base = peekFromStack(nameStack);
|
||||
Name descriptorName = SpecialNames.safeIdentifier(classDescriptor.getName());
|
||||
Name descriptorName = safeIdentifier(classDescriptor.getName());
|
||||
if (DescriptorUtils.isTopLevelDeclaration(classDescriptor)) {
|
||||
return base.isEmpty() ? descriptorName.asString() : base + '/' + descriptorName;
|
||||
}
|
||||
@@ -477,6 +476,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
SimpleFunctionDescriptor jvmSuspendFunctionView =
|
||||
CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(
|
||||
functionDescriptor,
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
|
||||
this.bindingContext
|
||||
);
|
||||
|
||||
@@ -713,6 +713,54 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
super.visitCallExpression(expression);
|
||||
checkSamCall(expression);
|
||||
checkCrossinlineCall(expression);
|
||||
recordSuspendFunctionTypeWrapperForArguments(expression);
|
||||
}
|
||||
|
||||
private void recordSuspendFunctionTypeWrapperForArguments(@NotNull KtCallExpression expression) {
|
||||
ResolvedCall<?> call = CallUtilKt.getResolvedCall(expression, bindingContext);
|
||||
if (call == null) return;
|
||||
|
||||
CallableDescriptor descriptor = call.getResultingDescriptor();
|
||||
if (!CodegenUtilKt.needsExperimentalCoroutinesWrapper(descriptor)) return;
|
||||
|
||||
List<ResolvedValueArgument> argumentsByIndex = call.getValueArgumentsByIndex();
|
||||
if (argumentsByIndex == null) return;
|
||||
|
||||
for (ValueParameterDescriptor parameter : descriptor.getValueParameters()) {
|
||||
ResolvedValueArgument resolvedValueArgument = argumentsByIndex.get(parameter.getIndex());
|
||||
if (!(resolvedValueArgument instanceof ExpressionValueArgument)) continue;
|
||||
ValueArgument valueArgument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
|
||||
if (valueArgument == null) continue;
|
||||
KtExpression argumentExpression = valueArgument.getArgumentExpression();
|
||||
if (argumentExpression == null) continue;
|
||||
|
||||
recordSuspendFunctionTypeWrapperForArgument(parameter, argumentExpression);
|
||||
}
|
||||
|
||||
ReceiverValue receiver = call.getExtensionReceiver();
|
||||
if (descriptor.getExtensionReceiverParameter() != null && receiver instanceof ExpressionReceiver) {
|
||||
recordSuspendFunctionTypeWrapperForArgument(
|
||||
descriptor.getExtensionReceiverParameter(),
|
||||
((ExpressionReceiver) receiver).getExpression()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordSuspendFunctionTypeWrapperForArgument(ParameterDescriptor parameter, KtExpression argumentExpression) {
|
||||
if (FunctionTypesKt.isSuspendFunctionTypeOrSubtype(parameter.getType())) {
|
||||
|
||||
// SuspendFunctionN type is mapped to is mapped to FunctionTypeN+1, but we also need to remove an argument for return type
|
||||
// So, it could be parameter.getType().getArguments().size() + 1 - 1
|
||||
int functionTypeArity = parameter.getType().getArguments().size();
|
||||
|
||||
Type functionType = Type.getObjectType(NUMBERED_FUNCTION_PREFIX + functionTypeArity);
|
||||
|
||||
bindingTrace.record(
|
||||
FUNCTION_TYPE_FOR_SUSPEND_WRAPPER,
|
||||
argumentExpression,
|
||||
functionType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCrossinlineCall(@NotNull KtCallExpression expression) {
|
||||
|
||||
@@ -13,13 +13,15 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
||||
import org.jetbrains.kotlin.codegen.context.MultifileClassFacadeContext
|
||||
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
|
||||
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
|
||||
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
|
||||
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
@@ -43,6 +45,8 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
@@ -66,7 +70,8 @@ internal val JAVA_LANG_DEPRECATED = Type.getType(Deprecated::class.java).descrip
|
||||
fun generateIsCheck(
|
||||
v: InstructionAdapter,
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type
|
||||
asmType: Type,
|
||||
isReleaseCoroutines: Boolean
|
||||
) {
|
||||
if (TypeUtils.isNullableType(kotlinType)) {
|
||||
val nope = Label()
|
||||
@@ -77,7 +82,7 @@ fun generateIsCheck(
|
||||
|
||||
ifnull(nope)
|
||||
|
||||
TypeIntrinsics.instanceOf(this, kotlinType, asmType)
|
||||
TypeIntrinsics.instanceOf(this, kotlinType, asmType, isReleaseCoroutines)
|
||||
|
||||
goTo(end)
|
||||
|
||||
@@ -88,7 +93,7 @@ fun generateIsCheck(
|
||||
mark(end)
|
||||
}
|
||||
} else {
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType, isReleaseCoroutines)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +102,7 @@ fun generateAsCast(
|
||||
kotlinType: KotlinType,
|
||||
asmType: Type,
|
||||
isSafe: Boolean,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
unifiedNullChecks: Boolean,
|
||||
) {
|
||||
if (!isSafe) {
|
||||
@@ -106,7 +112,7 @@ fun generateAsCast(
|
||||
} else {
|
||||
with(v) {
|
||||
dup()
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
|
||||
TypeIntrinsics.instanceOf(v, kotlinType, asmType, languageVersionSettings.isReleaseCoroutines())
|
||||
val ok = Label()
|
||||
ifne(ok)
|
||||
pop()
|
||||
@@ -438,10 +444,14 @@ fun KotlinType.isInlineClassTypeWithPrimitiveEquality(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun CallableDescriptor.needsExperimentalCoroutinesWrapper() =
|
||||
(this as? DeserializedMemberDescriptor)?.coroutinesExperimentalCompatibilityMode == CoroutinesCompatibilityMode.NEEDS_WRAPPER
|
||||
|
||||
fun recordCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingTrace: BindingTrace) {
|
||||
val labelName = getCallLabelForLambdaArgument(declaration, bindingTrace.bindingContext) ?: return
|
||||
val functionDescriptor = bindingTrace[BindingContext.FUNCTION, declaration] ?: return
|
||||
bindingTrace.record(CodegenBinding.CALL_LABEL_FOR_LAMBDA_ARGUMENT, functionDescriptor, labelName)
|
||||
|
||||
}
|
||||
|
||||
fun getCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingContext: BindingContext): String? {
|
||||
@@ -637,7 +647,7 @@ private fun generateLambdaForRunSuspend(
|
||||
}
|
||||
|
||||
visitVarInsn(ALOAD, 1)
|
||||
val continuationInternalName = CONTINUATION_ASM_TYPE.internalName
|
||||
val continuationInternalName = state.languageVersionSettings.continuationAsmType().internalName
|
||||
|
||||
visitTypeInsn(
|
||||
CHECKCAST,
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
package org.jetbrains.kotlin.codegen.context
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.binding.MutableClosure
|
||||
import org.jetbrains.kotlin.config.coroutinesPackageFqName
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.isTopLevelInPackage
|
||||
@@ -17,26 +17,25 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
|
||||
class InlineLambdaContext(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
contextKind: OwnerKind,
|
||||
parentContext: CodegenContext<*>,
|
||||
closure: MutableClosure?,
|
||||
val isCrossInline: Boolean,
|
||||
private val isPropertyReference: Boolean
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
contextKind: OwnerKind,
|
||||
parentContext: CodegenContext<*>,
|
||||
closure: MutableClosure?,
|
||||
val isCrossInline: Boolean,
|
||||
private val isPropertyReference: Boolean
|
||||
) : MethodContext(functionDescriptor, contextKind, parentContext, closure, false) {
|
||||
|
||||
override fun getFirstCrossInlineOrNonInlineContext(): CodegenContext<*> {
|
||||
if (isCrossInline && !isSuspendIntrinsicParameter()) return this
|
||||
|
||||
val parent = if (isPropertyReference) parentContext as? AnonymousClassContext else {
|
||||
parentContext as? ClosureContext
|
||||
} ?: throw AssertionError(
|
||||
"Parent of inlining lambda body should be " +
|
||||
"${if (isPropertyReference) "ClosureContext" else "AnonymousClassContext"}, but: $parentContext"
|
||||
)
|
||||
val parent = if (isPropertyReference) parentContext as? AnonymousClassContext else { parentContext as? ClosureContext } ?:
|
||||
throw AssertionError(
|
||||
"Parent of inlining lambda body should be " +
|
||||
"${if (isPropertyReference) "ClosureContext" else "AnonymousClassContext"}, but: $parentContext"
|
||||
)
|
||||
|
||||
val grandParent =
|
||||
parent.parentContext ?: throw AssertionError("Parent context of lambda class context should exist: $contextDescriptor")
|
||||
val grandParent = parent.parentContext ?:
|
||||
throw AssertionError("Parent context of lambda class context should exist: $contextDescriptor")
|
||||
return grandParent.firstCrossInlineOrNonInlineContext
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ class InlineLambdaContext(
|
||||
if (contextDescriptor !is AnonymousFunctionDescriptor) return false
|
||||
val resolvedCall = (contextDescriptor.source.getPsi() as? KtElement).getParentResolvedCall(state.bindingContext) ?: return false
|
||||
val descriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: return false
|
||||
return descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn()
|
||||
|| descriptor.isTopLevelInPackage("suspendCoroutine", StandardNames.COROUTINES_PACKAGE_FQ_NAME.asString())
|
||||
return descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(state.languageVersionSettings)
|
||||
|| descriptor.isTopLevelInPackage("suspendCoroutine", state.languageVersionSettings.coroutinesPackageFqName().asString())
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isPrimitiveBoxing
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
@@ -16,9 +15,10 @@ import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
|
||||
private val BOXING_CLASS_INTERNAL_NAME =
|
||||
StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName().child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
object ChangeBoxingMethodTransformer : MethodTransformer() {
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.CAPTURES_CROSSINLINE_LAMBDA
|
||||
@@ -18,6 +17,8 @@ import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHO
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
@@ -40,6 +41,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
@@ -54,7 +56,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
element: KtElement,
|
||||
closureContext: ClosureContext,
|
||||
classBuilder: ClassBuilder,
|
||||
private val userDataForInvokeSuspend: Map<out CallableDescriptor.UserDataKey<*>, *>? = null
|
||||
private val userDataForDoResume: Map<out CallableDescriptor.UserDataKey<*>, *>? = null
|
||||
) : ClosureCodegen(
|
||||
outerExpressionCodegen.state,
|
||||
element, null, closureContext, null,
|
||||
@@ -65,10 +67,17 @@ abstract class AbstractCoroutineCodegen(
|
||||
protected val languageVersionSettings = outerExpressionCodegen.state.languageVersionSettings
|
||||
|
||||
protected val methodToImplement =
|
||||
createImplMethod(
|
||||
INVOKE_SUSPEND_METHOD_NAME,
|
||||
SUSPEND_CALL_RESULT_NAME to classDescriptor.module.getResult(classDescriptor.builtIns.anyType)
|
||||
)
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
createImplMethod(
|
||||
INVOKE_SUSPEND_METHOD_NAME,
|
||||
SUSPEND_CALL_RESULT_NAME to classDescriptor.module.getResult(classDescriptor.builtIns.anyType)
|
||||
)
|
||||
else
|
||||
createImplMethod(
|
||||
DO_RESUME_METHOD_NAME,
|
||||
"data" to classDescriptor.builtIns.nullableAnyType,
|
||||
"throwable" to classDescriptor.builtIns.throwable.defaultType.makeNullable()
|
||||
)
|
||||
|
||||
private fun createImplMethod(name: String, vararg parameters: Pair<String, KotlinType>) =
|
||||
SimpleFunctionDescriptorImpl.create(
|
||||
@@ -85,7 +94,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
builtIns.nullableAnyType,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC,
|
||||
userDataForInvokeSuspend
|
||||
userDataForDoResume
|
||||
)
|
||||
}
|
||||
|
||||
@@ -100,7 +109,7 @@ abstract class AbstractCoroutineCodegen(
|
||||
|
||||
override fun generateConstructor(): Method {
|
||||
val args = calculateConstructorParameters(typeMapper, languageVersionSettings, closure, asmType)
|
||||
val argTypes = args.map { it.fieldType }.plus(CONTINUATION_ASM_TYPE).toTypedArray()
|
||||
val argTypes = args.map { it.fieldType }.plus(languageVersionSettings.continuationAsmType()).toTypedArray()
|
||||
|
||||
val constructor = Method("<init>", Type.VOID_TYPE, argTypes)
|
||||
val mv = v.newMethod(
|
||||
@@ -115,17 +124,18 @@ abstract class AbstractCoroutineCodegen(
|
||||
iv.generateClosureFieldsInitializationFromParameters(closure, args)
|
||||
|
||||
iv.load(0, AsmTypes.OBJECT_TYPE)
|
||||
if (passArityToSuperClass) {
|
||||
iv.iconst(funDescriptor.arity)
|
||||
val hasArityParameter = !languageVersionSettings.isReleaseCoroutines() || passArityToSuperClass
|
||||
if (hasArityParameter) {
|
||||
iv.iconst(if (passArityToSuperClass) funDescriptor.arity else 0)
|
||||
}
|
||||
|
||||
iv.load(argTypes.map { it.size }.sum(), AsmTypes.OBJECT_TYPE)
|
||||
|
||||
val parameters =
|
||||
if (passArityToSuperClass)
|
||||
listOf(Type.INT_TYPE, CONTINUATION_ASM_TYPE)
|
||||
if (hasArityParameter)
|
||||
listOf(Type.INT_TYPE, languageVersionSettings.continuationAsmType())
|
||||
else
|
||||
listOf(CONTINUATION_ASM_TYPE)
|
||||
listOf(languageVersionSettings.continuationAsmType())
|
||||
|
||||
val superClassConstructorDescriptor = Type.getMethodDescriptor(
|
||||
Type.VOID_TYPE,
|
||||
@@ -138,7 +148,9 @@ abstract class AbstractCoroutineCodegen(
|
||||
FunctionCodegen.endVisit(iv, "constructor", element)
|
||||
}
|
||||
|
||||
v.newField(JvmDeclarationOrigin.NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "label", "I", null, null)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
v.newField(JvmDeclarationOrigin.NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "label", "I", null, null)
|
||||
}
|
||||
|
||||
return constructor
|
||||
}
|
||||
@@ -155,7 +167,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
private val forInline: Boolean
|
||||
) : AbstractCoroutineCodegen(
|
||||
outerExpressionCodegen, element, closureContext, classBuilder,
|
||||
userDataForInvokeSuspend = mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_INVOKE_SUSPEND to originalSuspendFunctionDescriptor)
|
||||
userDataForDoResume = mapOf(INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME to originalSuspendFunctionDescriptor)
|
||||
) {
|
||||
private val builtIns = funDescriptor.builtIns
|
||||
|
||||
@@ -203,7 +215,8 @@ class CoroutineCodegenForLambda private constructor(
|
||||
funDescriptor.typeParameters,
|
||||
funDescriptor.valueParameters,
|
||||
funDescriptor.module.getContinuationOfTypeOrAny(
|
||||
builtIns.unitType
|
||||
builtIns.unitType,
|
||||
state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
),
|
||||
funDescriptor.modality,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
@@ -218,11 +231,14 @@ class CoroutineCodegenForLambda private constructor(
|
||||
"too many arguments of create to have an erased signature: $argumentsNum: $typedCreate"
|
||||
}
|
||||
return typedCreate.module.resolveClassByFqName(
|
||||
StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(
|
||||
Name.identifier("BaseContinuationImpl")
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
Name.identifier("BaseContinuationImpl")
|
||||
else
|
||||
Name.identifier("CoroutineImpl")
|
||||
),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).sure { "BaseContinuationImpl is not found" }.defaultType.memberScope
|
||||
).sure { "BaseContinuationImpl or CoroutineImpl is not found" }.defaultType.memberScope
|
||||
.getContributedFunctions(typedCreate.name, NoLookupLocation.FROM_BACKEND)
|
||||
.find { it.valueParameters.size == argumentsNum }
|
||||
.sure { "erased parent of $typedCreate is not found" }
|
||||
@@ -349,7 +365,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
v.thisName,
|
||||
typeMapper.mapFunctionName(createCoroutineDescriptor, null),
|
||||
Type.getMethodDescriptor(
|
||||
CONTINUATION_ASM_TYPE,
|
||||
languageVersionSettings.continuationAsmType(),
|
||||
*createArgumentTypes.toTypedArray()
|
||||
),
|
||||
false
|
||||
@@ -357,7 +373,11 @@ class CoroutineCodegenForLambda private constructor(
|
||||
checkcast(Type.getObjectType(v.thisName))
|
||||
|
||||
// .doResume(Unit)
|
||||
invokeInvokeSuspendWithUnit(v.thisName)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
invokeInvokeSuspendWithUnit(v.thisName)
|
||||
} else {
|
||||
invokeDoResumeWithUnit(v.thisName)
|
||||
}
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
|
||||
@@ -520,14 +540,15 @@ class CoroutineCodegenForLambda private constructor(
|
||||
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
|
||||
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||
mv, access, name, desc, null, null,
|
||||
containingClassInternalName = v.thisName,
|
||||
obtainClassBuilderForCoroutineState = { v },
|
||||
isForNamedFunction = false,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element, state, it) },
|
||||
lineNumber = CodegenUtil.getLineNumberForElement(element, false) ?: 0,
|
||||
sourceFile = element.containingKtFile.name,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = v.thisName,
|
||||
isForNamedFunction = false,
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS),
|
||||
initialVarsCountByType = varsCountByType
|
||||
)
|
||||
@@ -604,7 +625,7 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
private val labelFieldStackValue by lazy {
|
||||
StackValue.field(
|
||||
FieldInfo.createForHiddenField(
|
||||
Type.getObjectType(v.thisName),
|
||||
computeLabelOwner(languageVersionSettings, v.thisName),
|
||||
Type.INT_TYPE,
|
||||
COROUTINE_LABEL_FIELD_NAME
|
||||
),
|
||||
@@ -626,25 +647,44 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
}
|
||||
|
||||
override fun generateClosureBody() {
|
||||
generateInvokeSuspend()
|
||||
generateResumeImpl()
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
generateGetLabelMethod()
|
||||
generateSetLabelMethod()
|
||||
}
|
||||
|
||||
v.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_SYNTHETIC or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
|
||||
CONTINUATION_RESULT_FIELD_NAME, AsmTypes.OBJECT_TYPE.descriptor, null, null
|
||||
languageVersionSettings.dataFieldName(), AsmTypes.OBJECT_TYPE.descriptor, null, null
|
||||
)
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
v.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_SYNTHETIC or AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
|
||||
EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor, null, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateInvokeSuspend() {
|
||||
private fun generateResumeImpl() {
|
||||
functionCodegen.generateMethod(
|
||||
OtherOrigin(element),
|
||||
methodToImplement,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
StackValue.field(
|
||||
AsmTypes.OBJECT_TYPE, Type.getObjectType(v.thisName), CONTINUATION_RESULT_FIELD_NAME, false,
|
||||
AsmTypes.OBJECT_TYPE, Type.getObjectType(v.thisName), languageVersionSettings.dataFieldName(), false,
|
||||
StackValue.LOCAL_0
|
||||
).store(StackValue.local(1, AsmTypes.OBJECT_TYPE), codegen.v)
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
StackValue.field(
|
||||
AsmTypes.JAVA_THROWABLE_TYPE, Type.getObjectType(v.thisName), EXCEPTION_FIELD_NAME, false,
|
||||
StackValue.LOCAL_0
|
||||
).store(StackValue.local(2, AsmTypes.JAVA_THROWABLE_TYPE), codegen.v)
|
||||
}
|
||||
|
||||
labelFieldStackValue.store(
|
||||
StackValue.operation(Type.INT_TYPE) {
|
||||
labelFieldStackValue.put(Type.INT_TYPE, it)
|
||||
@@ -696,7 +736,7 @@ class CoroutineCodegenForNamedFunction private constructor(
|
||||
with(codegen.v) {
|
||||
// We need to box the returned inline class in resume path.
|
||||
// But first, check for COROUTINE_SUSPENDED, since the function can return it
|
||||
generateCoroutineSuspendedCheck()
|
||||
generateCoroutineSuspendedCheck(languageVersionSettings)
|
||||
// Now we box the inline class
|
||||
StackValue.coerce(AsmTypes.OBJECT_TYPE, typeMapper.mapType(inlineClassToBoxInInvokeSuspend), this)
|
||||
StackValue.boxInlineClass(inlineClassToBoxInInvokeSuspend, this, typeMapper)
|
||||
|
||||
@@ -9,10 +9,15 @@ import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
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.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
@@ -44,6 +49,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
obtainClassBuilderForCoroutineState: () -> ClassBuilder,
|
||||
private val isForNamedFunction: Boolean,
|
||||
private val shouldPreserveClassInitialization: Boolean,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
// Since tail-call optimization of functions with Unit return type relies on ability of call-site to recognize them,
|
||||
// in order to ignore return value and push Unit, when we cannot ensure this ability, for example, when the function overrides function,
|
||||
// returning Any, we need to disable tail-call optimization for these functions.
|
||||
@@ -67,6 +73,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
private var continuationIndex = if (isForNamedFunction) -1 else 0
|
||||
private var dataIndex = if (isForNamedFunction) -1 else 1
|
||||
private var exceptionIndex = if (isForNamedFunction || languageVersionSettings.isReleaseCoroutines()) -1 else 2
|
||||
|
||||
override fun performTransformations(methodNode: MethodNode) {
|
||||
removeFakeContinuationConstructorCall(methodNode)
|
||||
@@ -82,7 +89,9 @@ class CoroutineTransformerMethodVisitor(
|
||||
val suspensionPoints = collectSuspensionPoints(methodNode)
|
||||
RedundantLocalsEliminationMethodTransformer(suspensionPoints)
|
||||
.transform(containingClassInternalName, methodNode)
|
||||
ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode)
|
||||
}
|
||||
updateMaxStack(methodNode)
|
||||
|
||||
checkForSuspensionPointInsideMonitor(methodNode, suspensionPoints)
|
||||
@@ -96,6 +105,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
val examiner = MethodNodeExaminer(
|
||||
languageVersionSettings,
|
||||
containingClassInternalName,
|
||||
methodNode,
|
||||
suspensionPoints,
|
||||
@@ -109,6 +119,9 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
dataIndex = methodNode.maxLocals++
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
exceptionIndex = methodNode.maxLocals++
|
||||
}
|
||||
continuationIndex = methodNode.maxLocals++
|
||||
|
||||
prepareMethodNodePreludeForNamedFunction(methodNode)
|
||||
@@ -145,7 +158,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
insertBefore(
|
||||
actualCoroutineStart,
|
||||
insnListOf(
|
||||
*withInstructionAdapter { loadCoroutineSuspendedMarker() }.toArray(),
|
||||
*withInstructionAdapter { loadCoroutineSuspendedMarker(languageVersionSettings) }.toArray(),
|
||||
tableSwitchLabel,
|
||||
// Allow debugger to stop on enter into suspend function
|
||||
LineNumberNode(lineNumber, tableSwitchLabel),
|
||||
@@ -163,7 +176,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
)
|
||||
|
||||
insert(firstStateLabel, withInstructionAdapter {
|
||||
generateResumeWithExceptionCheck(dataIndex)
|
||||
generateResumeWithExceptionCheck(languageVersionSettings.isReleaseCoroutines(), dataIndex, exceptionIndex)
|
||||
})
|
||||
insert(last, defaultLabel)
|
||||
|
||||
@@ -179,9 +192,11 @@ class CoroutineTransformerMethodVisitor(
|
||||
dropUnboxInlineClassMarkers(methodNode, suspensionPoints)
|
||||
methodNode.removeEmptyCatchBlocks()
|
||||
|
||||
updateLvtAccordingToLiveness(methodNode, isForNamedFunction, stateLabels)
|
||||
updateLvtAccordingToLiveness(methodNode, isForNamedFunction)
|
||||
|
||||
writeDebugMetadata(methodNode, suspensionPointLineNumbers, spilledToVariableMapping)
|
||||
if (languageVersionSettings.isReleaseCoroutines()) {
|
||||
writeDebugMetadata(methodNode, suspensionPointLineNumbers, spilledToVariableMapping)
|
||||
}
|
||||
}
|
||||
|
||||
// When suspension point is inlined, it is in range of fake inliner variables.
|
||||
@@ -248,7 +263,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
methodNode.localVariables.add(
|
||||
LocalVariableNode(
|
||||
SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME,
|
||||
CONTINUATION_ASM_TYPE.descriptor,
|
||||
languageVersionSettings.continuationAsmType().descriptor,
|
||||
null,
|
||||
startLabel,
|
||||
endLabel,
|
||||
@@ -387,7 +402,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
methodNode.instructions.add(withInstructionAdapter { mark(endLabel) })
|
||||
methodNode.visitLocalVariable(
|
||||
CONTINUATION_VARIABLE_NAME,
|
||||
CONTINUATION_ASM_TYPE.descriptor,
|
||||
languageVersionSettings.continuationAsmType().descriptor,
|
||||
null,
|
||||
startLabel,
|
||||
endLabel,
|
||||
@@ -415,17 +430,33 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.getLabel() {
|
||||
getfield(
|
||||
Type.getObjectType(classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
if (isForNamedFunction && !languageVersionSettings.isReleaseCoroutines())
|
||||
invokevirtual(
|
||||
classBuilderForCoroutineState.thisName,
|
||||
"getLabel",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE),
|
||||
false
|
||||
)
|
||||
else
|
||||
getfield(
|
||||
computeLabelOwner(languageVersionSettings, classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.setLabel() {
|
||||
putfield(
|
||||
Type.getObjectType(classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
if (isForNamedFunction && !languageVersionSettings.isReleaseCoroutines())
|
||||
invokevirtual(
|
||||
classBuilderForCoroutineState.thisName,
|
||||
"setLabel",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
|
||||
false
|
||||
)
|
||||
else
|
||||
putfield(
|
||||
computeLabelOwner(languageVersionSettings, classBuilderForCoroutineState.thisName).internalName,
|
||||
COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateMaxStack(methodNode: MethodNode) {
|
||||
@@ -503,7 +534,8 @@ class CoroutineTransformerMethodVisitor(
|
||||
needDispatchReceiver,
|
||||
internalNameForDispatchReceiver,
|
||||
containingClassInternalName,
|
||||
classBuilderForCoroutineState
|
||||
classBuilderForCoroutineState,
|
||||
languageVersionSettings
|
||||
)
|
||||
|
||||
visitVarInsn(Opcodes.ASTORE, continuationIndex)
|
||||
@@ -511,13 +543,19 @@ class CoroutineTransformerMethodVisitor(
|
||||
visitLabel(afterCoroutineStateCreated)
|
||||
|
||||
visitVarInsn(Opcodes.ALOAD, continuationIndex)
|
||||
getfield(classBuilderForCoroutineState.thisName, CONTINUATION_RESULT_FIELD_NAME, AsmTypes.OBJECT_TYPE.descriptor)
|
||||
getfield(classBuilderForCoroutineState.thisName, languageVersionSettings.dataFieldName(), AsmTypes.OBJECT_TYPE.descriptor)
|
||||
visitVarInsn(Opcodes.ASTORE, dataIndex)
|
||||
|
||||
val resultStartLabel = Label()
|
||||
visitLabel(resultStartLabel)
|
||||
|
||||
addContinuationAndResultToLvt(methodNode, afterCoroutineStateCreated, resultStartLabel)
|
||||
|
||||
if (!languageVersionSettings.isReleaseCoroutines()) {
|
||||
visitVarInsn(Opcodes.ALOAD, continuationIndex)
|
||||
getfield(classBuilderForCoroutineState.thisName, EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor)
|
||||
visitVarInsn(Opcodes.ASTORE, exceptionIndex)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -654,7 +692,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
// k + 1 - data
|
||||
// k + 2 - exception
|
||||
for (slot in 0 until localsCount) {
|
||||
if (slot == continuationIndex || slot == dataIndex) continue
|
||||
if (slot == continuationIndex || slot == dataIndex || slot == exceptionIndex) continue
|
||||
val value = frame.getLocal(slot)
|
||||
if (value.type == null || !livenessFrame.isAlive(slot)) continue
|
||||
|
||||
@@ -954,7 +992,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
insert(possibleTryCatchBlockStart, withInstructionAdapter {
|
||||
nop()
|
||||
generateResumeWithExceptionCheck(dataIndex)
|
||||
generateResumeWithExceptionCheck(languageVersionSettings.isReleaseCoroutines(), dataIndex, exceptionIndex)
|
||||
|
||||
// Load continuation argument just like suspending function returns it
|
||||
load(dataIndex, AsmTypes.OBJECT_TYPE)
|
||||
@@ -1080,7 +1118,8 @@ internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
needDispatchReceiver: Boolean,
|
||||
internalNameForDispatchReceiver: String?,
|
||||
containingClassInternalName: String,
|
||||
classBuilderForCoroutineState: ClassBuilder
|
||||
classBuilderForCoroutineState: ClassBuilder,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
anew(objectTypeForState)
|
||||
dup()
|
||||
@@ -1089,7 +1128,8 @@ internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
getParameterTypesIndicesForCoroutineConstructor(
|
||||
methodNode.desc,
|
||||
methodNode.access,
|
||||
needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName
|
||||
needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName,
|
||||
languageVersionSettings
|
||||
)
|
||||
for ((type, index) in parameterTypesAndIndices) {
|
||||
load(index, type)
|
||||
@@ -1109,11 +1149,22 @@ internal fun InstructionAdapter.generateContinuationConstructorCall(
|
||||
)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.generateResumeWithExceptionCheck(dataIndex: Int) {
|
||||
private fun InstructionAdapter.generateResumeWithExceptionCheck(isReleaseCoroutines: Boolean, dataIndex: Int, exceptionIndex: Int) {
|
||||
// Check if resumeWithException has been called
|
||||
|
||||
load(dataIndex, AsmTypes.OBJECT_TYPE)
|
||||
invokestatic("kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false)
|
||||
if (isReleaseCoroutines) {
|
||||
load(dataIndex, AsmTypes.OBJECT_TYPE)
|
||||
invokestatic("kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false)
|
||||
} else {
|
||||
load(exceptionIndex, AsmTypes.OBJECT_TYPE)
|
||||
dup()
|
||||
val noExceptionLabel = Label()
|
||||
ifnull(noExceptionLabel)
|
||||
athrow()
|
||||
|
||||
mark(noExceptionLabel)
|
||||
pop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Type.fieldNameForVar(index: Int) = descriptor.first() + "$" + index
|
||||
@@ -1184,15 +1235,16 @@ private fun getParameterTypesIndicesForCoroutineConstructor(
|
||||
desc: String,
|
||||
containingFunctionAccess: Int,
|
||||
needDispatchReceiver: Boolean,
|
||||
thisName: String
|
||||
thisName: String,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): Collection<Pair<Type, Int>> {
|
||||
return mutableListOf<Pair<Type, Int>>().apply {
|
||||
if (needDispatchReceiver) {
|
||||
add(Type.getObjectType(thisName) to 0)
|
||||
}
|
||||
val continuationIndex =
|
||||
getAllParameterTypes(desc, !isStatic(containingFunctionAccess), thisName).dropLast(1).sumOf(Type::getSize)
|
||||
add(CONTINUATION_ASM_TYPE to continuationIndex)
|
||||
getAllParameterTypes(desc, !isStatic(containingFunctionAccess), thisName).dropLast(1).map(Type::getSize).sum()
|
||||
add(languageVersionSettings.continuationAsmType() to continuationIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1217,7 +1269,7 @@ private fun MethodNode.nodeTextWithLiveness(liveness: List<VariableLivenessFrame
|
||||
* This means, that function parameters do not longer span the whole function, including `this`.
|
||||
* This might and will break some bytecode processors, including old versions of R8. See KT-24510.
|
||||
*/
|
||||
private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: Boolean, suspensionPoints: List<LabelNode>) {
|
||||
private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: Boolean) {
|
||||
val liveness = analyzeLiveness(method)
|
||||
|
||||
fun List<LocalVariableNode>.findRecord(insnIndex: Int, variableIndex: Int): LocalVariableNode? {
|
||||
@@ -1236,8 +1288,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
fun nextLabel(node: AbstractInsnNode?): LabelNode? {
|
||||
var current = node
|
||||
while (current != null) {
|
||||
if (current is LabelNode) return current
|
||||
current = current.next
|
||||
if (current is LabelNode) return current as LabelNode
|
||||
current = current!!.next
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1294,17 +1346,33 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
|
||||
// Attempt to extend existing local variable node corresponding to the record in
|
||||
// the original local variable table, if there is no back-edge
|
||||
val latest = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
||||
// if we can extend the previous range to where the local variable dies, we do not need a
|
||||
// new entry, we know we cannot extend it to the lvt.endOffset, if we could we would have
|
||||
// done so when we added it below.
|
||||
val extended = latest?.extendRecordIfPossible(method, suspensionPoints, lvtRecord.end) ?: false
|
||||
if (!extended) {
|
||||
val new = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = new
|
||||
method.localVariables.add(new)
|
||||
// see if we can extend it all the way to the old end
|
||||
new.extendRecordIfPossible(method, suspensionPoints, lvtRecord.end)
|
||||
val recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
||||
var recordExtended = false
|
||||
if (recordToExtend != null) {
|
||||
var hasBackEdgeOrStore = false
|
||||
var current: AbstractInsnNode? = recordToExtend.end
|
||||
while (current != null && current != endLabel) {
|
||||
if (current is JumpInsnNode) {
|
||||
if (method.instructions.indexOf((current as JumpInsnNode).label) < method.instructions.indexOf(current)) {
|
||||
hasBackEdgeOrStore = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (current!!.isStoreOperation() && (current as VarInsnNode).`var` == recordToExtend.index) {
|
||||
hasBackEdgeOrStore = true
|
||||
break
|
||||
}
|
||||
current = current!!.next
|
||||
}
|
||||
if (!hasBackEdgeOrStore) {
|
||||
recordToExtend.end = endLabel
|
||||
recordExtended = true
|
||||
}
|
||||
}
|
||||
if (!recordExtended) {
|
||||
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
method.localVariables.add(node)
|
||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = node
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1327,35 +1395,3 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We cannot extend a record if there is STORE instruction or a back-edge.
|
||||
* STORE instructions can signify a unspilling operation, in which case, the variable will become visible before it unspilled,
|
||||
* back-edges occur in loops.
|
||||
*
|
||||
* @return true if the range has been extended
|
||||
*/
|
||||
private fun LocalVariableNode.extendRecordIfPossible(
|
||||
method: MethodNode,
|
||||
suspensionPoints: List<LabelNode>,
|
||||
endLabel: LabelNode
|
||||
): Boolean {
|
||||
val nextSuspensionPointLabel = suspensionPoints.find { it in InsnSequence(end, endLabel) } ?: endLabel
|
||||
|
||||
var current: AbstractInsnNode? = end
|
||||
while (current != null && current != nextSuspensionPointLabel) {
|
||||
if (current is JumpInsnNode) {
|
||||
if (method.instructions.indexOf(current.label) < method.instructions.indexOf(current)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// TODO: HACK
|
||||
// TODO: Find correct label, which is OK to be used as end label.
|
||||
if (current.opcode == Opcodes.ARETURN && nextSuspensionPointLabel != endLabel) return false
|
||||
if (current.isStoreOperation() && (current as VarInsnNode).`var` == index) {
|
||||
return false
|
||||
}
|
||||
current = current.next
|
||||
}
|
||||
end = nextSuspensionPointLabel
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -88,17 +88,18 @@ class SuspendFunctionGenerationStrategy(
|
||||
return CoroutineTransformerMethodVisitor(
|
||||
mv, access, name, desc, null, null, containingClassInternalName, this::classBuilderForCoroutineState,
|
||||
isForNamedFunction = true,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = originalSuspendDescriptor.returnType?.isUnit() == true &&
|
||||
originalSuspendDescriptor.overriddenDescriptors.isNotEmpty() &&
|
||||
!originalSuspendDescriptor.allOverriddenFunctionsReturnUnit(),
|
||||
reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(declaration, state, it) },
|
||||
lineNumber = CodegenUtil.getLineNumberForElement(declaration, false) ?: 0,
|
||||
sourceFile = declaration.containingKtFile.name,
|
||||
shouldPreserveClassInitialization = constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
needDispatchReceiver = originalSuspendDescriptor.dispatchReceiverParameter != null,
|
||||
internalNameForDispatchReceiver = (originalSuspendDescriptor.containingDeclaration as? ClassDescriptor)?.let {
|
||||
if (it.isInlineClass()) state.typeMapper.mapType(it).internalName else null
|
||||
} ?: containingClassInternalNameOrNull(),
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = originalSuspendDescriptor.returnType?.isUnit() == true &&
|
||||
originalSuspendDescriptor.overriddenDescriptors.isNotEmpty() &&
|
||||
!originalSuspendDescriptor.allOverriddenFunctionsReturnUnit(),
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS)
|
||||
)
|
||||
}
|
||||
@@ -154,7 +155,8 @@ class SuspendFunctionGenerationStrategy(
|
||||
needDispatchReceiver,
|
||||
internalNameForDispatchReceiver,
|
||||
containingClassInternalName,
|
||||
classBuilderForCoroutineState
|
||||
classBuilderForCoroutineState,
|
||||
languageVersionSettings
|
||||
)
|
||||
addFakeContinuationConstructorCallMarker(this, false)
|
||||
pop() // Otherwise stack-transformation breaks
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -26,6 +27,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
internal class MethodNodeExaminer(
|
||||
val languageVersionSettings: LanguageVersionSettings,
|
||||
containingClassInternalName: String,
|
||||
val methodNode: MethodNode,
|
||||
suspensionPoints: List<SuspensionPoint>,
|
||||
@@ -114,7 +116,7 @@ internal class MethodNodeExaminer(
|
||||
val label = Label()
|
||||
methodNode.instructions.insertBefore(pop, withInstructionAdapter {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
ifacmpne(label)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
mark(label)
|
||||
|
||||
@@ -9,8 +9,6 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalClassDescriptor
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
@@ -56,27 +54,67 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
const val COROUTINE_LABEL_FIELD_NAME = "label"
|
||||
const val SUSPEND_FUNCTION_CREATE_METHOD_NAME = "create"
|
||||
const val DO_RESUME_METHOD_NAME = "doResume"
|
||||
const val INVOKE_SUSPEND_METHOD_NAME = "invokeSuspend"
|
||||
const val CONTINUATION_RESULT_FIELD_NAME = "result"
|
||||
const val EXCEPTION_FIELD_NAME = "exception"
|
||||
|
||||
private const val GET_CONTEXT_METHOD_NAME = "getContext"
|
||||
val RELEASE_COROUTINES_VERSION_SETTINGS = LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3)
|
||||
|
||||
val DEBUG_METADATA_ANNOTATION_ASM_TYPE: Type =
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("DebugMetadata")).topLevelClassAsmType()
|
||||
fun LanguageVersionSettings.isResumeImplMethodName(name: String) =
|
||||
if (isReleaseCoroutines())
|
||||
name == INVOKE_SUSPEND_METHOD_NAME
|
||||
else
|
||||
name == DO_RESUME_METHOD_NAME
|
||||
|
||||
fun coroutineContextAsmType(): Type =
|
||||
StandardNames.COROUTINES_PACKAGE_FQ_NAME.child(Name.identifier("CoroutineContext")).topLevelClassAsmType()
|
||||
fun LanguageVersionSettings.dataFieldName(): String = if (isReleaseCoroutines()) "result" else "data"
|
||||
|
||||
fun String.isCoroutineSuperClass(): Boolean =
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("ContinuationImpl") == this ||
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("RestrictedContinuationImpl") == this ||
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("SuspendLambda") == this ||
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.identifiedChild("RestrictedSuspendLambda") == this
|
||||
fun isResumeImplMethodNameFromAnyLanguageSettings(name: String) = name == INVOKE_SUSPEND_METHOD_NAME || name == DO_RESUME_METHOD_NAME
|
||||
|
||||
fun LanguageVersionSettings.coroutinesJvmInternalPackageFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("jvm")).child(Name.identifier("internal"))
|
||||
|
||||
val DEBUG_METADATA_ANNOTATION_ASM_TYPE = RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName()
|
||||
.child(Name.identifier("DebugMetadata")).topLevelClassAsmType()
|
||||
|
||||
fun LanguageVersionSettings.continuationAsmType() =
|
||||
continuationInterfaceFqName().topLevelClassAsmType()
|
||||
|
||||
fun continuationAsmTypes() = listOf(
|
||||
LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3).continuationAsmType(),
|
||||
LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_2, ApiVersion.KOTLIN_1_2).continuationAsmType()
|
||||
)
|
||||
|
||||
fun LanguageVersionSettings.coroutineContextAsmType() =
|
||||
coroutinesPackageFqName().child(Name.identifier("CoroutineContext")).topLevelClassAsmType()
|
||||
|
||||
fun LanguageVersionSettings.isCoroutineSuperClass(internalName: String): Boolean {
|
||||
val coroutinesJvmInternalPackage = coroutinesJvmInternalPackageFqName()
|
||||
|
||||
return if (isReleaseCoroutines())
|
||||
coroutinesJvmInternalPackage.identifiedChild("ContinuationImpl") == internalName ||
|
||||
coroutinesJvmInternalPackage.identifiedChild("RestrictedContinuationImpl") == internalName ||
|
||||
coroutinesJvmInternalPackage.identifiedChild("SuspendLambda") == internalName ||
|
||||
coroutinesJvmInternalPackage.identifiedChild("RestrictedSuspendLambda") == internalName
|
||||
else
|
||||
coroutinesJvmInternalPackage.identifiedChild("CoroutineImpl") == internalName
|
||||
}
|
||||
|
||||
private fun FqName.identifiedChild(name: String) = child(Name.identifier(name)).topLevelClassInternalName()
|
||||
|
||||
private val coroutinesIntrinsicsFileFacadeInternalName: Type =
|
||||
COROUTINES_INTRINSICS_PACKAGE_FQ_NAME.child(Name.identifier("IntrinsicsKt")).topLevelClassAsmType()
|
||||
private fun LanguageVersionSettings.coroutinesIntrinsicsFileFacadeInternalName() =
|
||||
coroutinesIntrinsicsPackageFqName().child(Name.identifier("IntrinsicsKt")).topLevelClassAsmType()
|
||||
|
||||
private fun LanguageVersionSettings.internalCoroutineIntrinsicsOwnerInternalName() =
|
||||
coroutinesJvmInternalPackageFqName().child(Name.identifier("CoroutineIntrinsics")).topLevelClassInternalName()
|
||||
|
||||
fun computeLabelOwner(languageVersionSettings: LanguageVersionSettings, thisName: String): Type =
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
Type.getObjectType(thisName)
|
||||
else
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(Name.identifier("CoroutineImpl")).topLevelClassAsmType()
|
||||
|
||||
private const val NORMALIZE_CONTINUATION_METHOD_NAME = "normalizeContinuation"
|
||||
private const val GET_CONTEXT_METHOD_NAME = "getContext"
|
||||
|
||||
data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val fakeContinuationExpression: KtExpression)
|
||||
|
||||
@@ -84,7 +122,7 @@ data class ResolvedCallWithRealDescriptor(val resolvedCall: ResolvedCall<*>, val
|
||||
val INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION = object : CallableDescriptor.UserDataKey<FunctionDescriptor> {}
|
||||
|
||||
@JvmField
|
||||
val INITIAL_SUSPEND_DESCRIPTOR_FOR_INVOKE_SUSPEND = object : CallableDescriptor.UserDataKey<FunctionDescriptor> {}
|
||||
val INITIAL_SUSPEND_DESCRIPTOR_FOR_DO_RESUME = object : CallableDescriptor.UserDataKey<FunctionDescriptor> {}
|
||||
|
||||
val CONTINUATION_PARAMETER_NAME = Name.identifier("continuation")
|
||||
|
||||
@@ -120,9 +158,9 @@ fun ResolvedCall<*>.replaceSuspensionFunctionWithRealDescriptor(
|
||||
val newCandidateDescriptor =
|
||||
when (function) {
|
||||
is FunctionImportedFromObject ->
|
||||
getOrCreateJvmSuspendFunctionView(function.callableFromObject, bindingContext).asImportedFromObject()
|
||||
getOrCreateJvmSuspendFunctionView(function.callableFromObject, isReleaseCoroutines, bindingContext).asImportedFromObject()
|
||||
is SimpleFunctionDescriptor ->
|
||||
getOrCreateJvmSuspendFunctionView(function, bindingContext)
|
||||
getOrCreateJvmSuspendFunctionView(function, isReleaseCoroutines, bindingContext)
|
||||
else ->
|
||||
throw AssertionError("Unexpected suspend function descriptor: $function")
|
||||
}
|
||||
@@ -185,10 +223,10 @@ private fun NewResolvedCallImpl<VariableDescriptor>.asDummyOldResolvedCall(bindi
|
||||
|
||||
enum class SuspensionPointKind { NEVER, NOT_INLINE, ALWAYS }
|
||||
|
||||
fun ResolvedCall<*>.isSuspensionPoint(codegen: ExpressionCodegen): SuspensionPointKind {
|
||||
fun ResolvedCall<*>.isSuspensionPoint(codegen: ExpressionCodegen, languageVersionSettings: LanguageVersionSettings): SuspensionPointKind {
|
||||
val functionDescriptor = resultingDescriptor as? FunctionDescriptor ?: return SuspensionPointKind.NEVER
|
||||
if (!functionDescriptor.unwrapInitialDescriptorForSuspendFunction().isSuspend) return SuspensionPointKind.NEVER
|
||||
if (functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm()) return SuspensionPointKind.ALWAYS
|
||||
if (functionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm(languageVersionSettings)) return SuspensionPointKind.ALWAYS
|
||||
if (functionDescriptor.isInline) return SuspensionPointKind.NEVER
|
||||
|
||||
val isInlineLambda = this.safeAs<VariableAsFunctionResolvedCall>()
|
||||
@@ -204,6 +242,7 @@ fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean {
|
||||
|
||||
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, state: GenerationState): D = getOrCreateJvmSuspendFunctionView(
|
||||
function,
|
||||
state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines),
|
||||
state.bindingContext
|
||||
)
|
||||
|
||||
@@ -213,6 +252,7 @@ fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(function: D, stat
|
||||
@JvmOverloads
|
||||
fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(
|
||||
function: D,
|
||||
isReleaseCoroutines: Boolean,
|
||||
bindingContext: BindingContext? = null
|
||||
): D {
|
||||
assert(function.isSuspend) {
|
||||
@@ -232,7 +272,7 @@ fun <D : FunctionDescriptor> getOrCreateJvmSuspendFunctionView(
|
||||
outType = if (function.containingDeclaration.safeAs<ClassDescriptor>()?.isBuiltinFunctionalClassDescriptor == true)
|
||||
function.builtIns.nullableAnyType
|
||||
else
|
||||
function.getContinuationParameterTypeOfSuspendFunction(),
|
||||
function.getContinuationParameterTypeOfSuspendFunction(isReleaseCoroutines),
|
||||
declaresDefaultValue = false, isCrossinline = false,
|
||||
isNoinline = false, varargElementType = null,
|
||||
source = SourceElement.NO_SOURCE
|
||||
@@ -269,7 +309,8 @@ fun <D : FunctionDescriptor> D.createCustomCopy(
|
||||
return result as D
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction() = module.getContinuationOfTypeOrAny(returnType!!)
|
||||
private fun FunctionDescriptor.getContinuationParameterTypeOfSuspendFunction(isReleaseCoroutines: Boolean) =
|
||||
module.getContinuationOfTypeOrAny(returnType!!, if (this.needsExperimentalCoroutinesWrapper()) false else isReleaseCoroutines)
|
||||
|
||||
fun ModuleDescriptor.getResult(kotlinType: KotlinType) =
|
||||
module.resolveTopLevelClass(
|
||||
@@ -282,11 +323,44 @@ fun ModuleDescriptor.getResult(kotlinType: KotlinType) =
|
||||
)
|
||||
} ?: ErrorUtils.createErrorType("For Result")
|
||||
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm() =
|
||||
getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION)?.isBuiltInSuspendCoroutineUninterceptedOrReturn() == true
|
||||
private fun MethodNode.invokeNormalizeContinuation(languageVersionSettings: LanguageVersionSettings) {
|
||||
visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
languageVersionSettings.internalCoroutineIntrinsicsOwnerInternalName(),
|
||||
NORMALIZE_CONTINUATION_METHOD_NAME,
|
||||
Type.getMethodDescriptor(languageVersionSettings.continuationAsmType(), languageVersionSettings.continuationAsmType()),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInCoroutineContext()) {
|
||||
fun FunctionDescriptor.isBuiltInSuspendCoroutineUninterceptedOrReturnInJvm(languageVersionSettings: LanguageVersionSettings) =
|
||||
getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION)?.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) == true
|
||||
|
||||
fun createMethodNodeForIntercepted(languageVersionSettings: LanguageVersionSettings): MethodNode {
|
||||
val node =
|
||||
MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
Type.getMethodDescriptor(languageVersionSettings.continuationAsmType(), languageVersionSettings.continuationAsmType()),
|
||||
null, null
|
||||
)
|
||||
|
||||
node.visitVarInsn(Opcodes.ALOAD, 0)
|
||||
|
||||
node.invokeNormalizeContinuation(languageVersionSettings)
|
||||
|
||||
node.visitInsn(Opcodes.ARETURN)
|
||||
node.visitMaxs(1, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun createMethodNodeForCoroutineContext(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): MethodNode {
|
||||
assert(functionDescriptor.isBuiltInCoroutineContext(languageVersionSettings)) {
|
||||
"functionDescriptor must be kotlin.coroutines.intrinsics.coroutineContext property getter"
|
||||
}
|
||||
|
||||
@@ -295,7 +369,7 @@ fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor):
|
||||
Opcodes.API_VERSION,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
Type.getMethodDescriptor(coroutineContextAsmType()),
|
||||
Type.getMethodDescriptor(languageVersionSettings.coroutineContextAsmType()),
|
||||
null, null
|
||||
)
|
||||
|
||||
@@ -303,20 +377,20 @@ fun createMethodNodeForCoroutineContext(functionDescriptor: FunctionDescriptor):
|
||||
|
||||
addFakeContinuationMarker(v)
|
||||
|
||||
v.invokeGetContext()
|
||||
v.invokeGetContext(languageVersionSettings)
|
||||
|
||||
node.visitMaxs(1, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(): MethodNode {
|
||||
fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(languageVersionSettings: LanguageVersionSettings): MethodNode {
|
||||
val node =
|
||||
MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
Opcodes.ACC_STATIC,
|
||||
"fake",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, AsmTypes.FUNCTION1, CONTINUATION_ASM_TYPE),
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, AsmTypes.FUNCTION1, languageVersionSettings.continuationAsmType()),
|
||||
null, null
|
||||
)
|
||||
|
||||
@@ -331,22 +405,25 @@ fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(): MethodNode {
|
||||
"($OBJECT_TYPE)$OBJECT_TYPE"
|
||||
)
|
||||
|
||||
val elseLabel = Label()
|
||||
// if (result === COROUTINE_SUSPENDED) {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker()
|
||||
ifacmpne(elseLabel)
|
||||
// DebugProbesKt.probeCoroutineSuspended(continuation)
|
||||
load(1, OBJECT_TYPE) // continuation
|
||||
checkcast(CONTINUATION_ASM_TYPE)
|
||||
invokestatic(
|
||||
COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("DebugProbesKt")).topLevelClassAsmType().internalName,
|
||||
"probeCoroutineSuspended",
|
||||
"($CONTINUATION_ASM_TYPE)V",
|
||||
false
|
||||
)
|
||||
// }
|
||||
mark(elseLabel)
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)) {
|
||||
val elseLabel = Label()
|
||||
// if (result === COROUTINE_SUSPENDED) {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
ifacmpne(elseLabel)
|
||||
// DebugProbesKt.probeCoroutineSuspended(continuation)
|
||||
load(1, OBJECT_TYPE) // continuation
|
||||
checkcast(languageVersionSettings.continuationAsmType())
|
||||
invokestatic(
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(Name.identifier("DebugProbesKt"))
|
||||
.topLevelClassAsmType().internalName,
|
||||
"probeCoroutineSuspended",
|
||||
"(${languageVersionSettings.continuationAsmType()})V",
|
||||
false
|
||||
)
|
||||
// }
|
||||
mark(elseLabel)
|
||||
}
|
||||
}
|
||||
|
||||
node.visitInsn(Opcodes.ARETURN)
|
||||
@@ -356,13 +433,13 @@ fun createMethodNodeForSuspendCoroutineUninterceptedOrReturn(): MethodNode {
|
||||
}
|
||||
|
||||
|
||||
private fun InstructionAdapter.invokeGetContext() {
|
||||
private fun InstructionAdapter.invokeGetContext(languageVersionSettings: LanguageVersionSettings) {
|
||||
invokeinterface(
|
||||
CONTINUATION_ASM_TYPE.internalName,
|
||||
languageVersionSettings.continuationAsmType().internalName,
|
||||
GET_CONTEXT_METHOD_NAME,
|
||||
Type.getMethodDescriptor(coroutineContextAsmType())
|
||||
Type.getMethodDescriptor(languageVersionSettings.coroutineContextAsmType())
|
||||
)
|
||||
areturn(coroutineContextAsmType())
|
||||
areturn(languageVersionSettings.coroutineContextAsmType())
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -370,12 +447,15 @@ fun <D : CallableDescriptor?> D.unwrapInitialDescriptorForSuspendFunction(): D =
|
||||
this.safeAs<SimpleFunctionDescriptor>()?.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) as D ?: this
|
||||
|
||||
|
||||
fun FunctionDescriptor.getOriginalSuspendFunctionView(bindingContext: BindingContext): FunctionDescriptor =
|
||||
fun FunctionDescriptor.getOriginalSuspendFunctionView(bindingContext: BindingContext, isReleaseCoroutines: Boolean): FunctionDescriptor =
|
||||
if (isSuspend)
|
||||
getOrCreateJvmSuspendFunctionView(unwrapInitialDescriptorForSuspendFunction().original, bindingContext)
|
||||
getOrCreateJvmSuspendFunctionView(unwrapInitialDescriptorForSuspendFunction().original, isReleaseCoroutines, bindingContext)
|
||||
else
|
||||
this
|
||||
|
||||
fun FunctionDescriptor.getOriginalSuspendFunctionView(bindingContext: BindingContext, state: GenerationState) =
|
||||
getOriginalSuspendFunctionView(bindingContext, state.languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
|
||||
// For each suspend function, we have a corresponding JVM view function that has an extra continuation parameter,
|
||||
// and, more importantly, returns 'kotlin.Any' (so that it can return as a reference value or a special COROUTINE_SUSPENDED object).
|
||||
// This also causes boxing of primitives and inline class values.
|
||||
@@ -402,24 +482,38 @@ fun FunctionDescriptor.originalReturnTypeOfSuspendFunctionReturningUnboxedInline
|
||||
return originalReturnType
|
||||
}
|
||||
|
||||
fun InstructionAdapter.loadCoroutineSuspendedMarker() {
|
||||
fun InstructionAdapter.loadCoroutineSuspendedMarker(languageVersionSettings: LanguageVersionSettings) {
|
||||
invokestatic(
|
||||
coroutinesIntrinsicsFileFacadeInternalName.internalName,
|
||||
languageVersionSettings.coroutinesIntrinsicsFileFacadeInternalName().internalName,
|
||||
"get$COROUTINE_SUSPENDED_NAME",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.generateCoroutineSuspendedCheck() {
|
||||
fun InstructionAdapter.generateCoroutineSuspendedCheck(languageVersionSettings: LanguageVersionSettings) {
|
||||
dup()
|
||||
loadCoroutineSuspendedMarker()
|
||||
loadCoroutineSuspendedMarker(languageVersionSettings)
|
||||
val elseLabel = Label()
|
||||
ifacmpne(elseLabel)
|
||||
areturn(OBJECT_TYPE)
|
||||
mark(elseLabel)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.invokeDoResumeWithUnit(thisName: String) {
|
||||
// .doResume(Unit, null)
|
||||
StackValue.putUnitInstance(this)
|
||||
|
||||
aconst(null)
|
||||
|
||||
invokevirtual(
|
||||
thisName,
|
||||
DO_RESUME_METHOD_NAME,
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, AsmTypes.JAVA_THROWABLE_TYPE),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.invokeInvokeSuspendWithUnit(thisName: String) {
|
||||
StackValue.putUnitInstance(this)
|
||||
|
||||
@@ -443,14 +537,18 @@ fun FunctionDescriptor.isSuspendLambdaOrLocalFunction() = this.isSuspend && when
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isLocalSuspendFunctionNotSuspendLambda() = isSuspendLambdaOrLocalFunction() && this !is AnonymousFunctionDescriptor
|
||||
|
||||
@JvmField
|
||||
val CONTINUATION_ASM_TYPE = StandardNames.CONTINUATION_INTERFACE_FQ_NAME.topLevelClassAsmType()
|
||||
val EXPERIMENTAL_CONTINUATION_ASM_TYPE = StandardNames.CONTINUATION_INTERFACE_FQ_NAME_EXPERIMENTAL.topLevelClassAsmType()
|
||||
|
||||
@JvmField
|
||||
val RELEASE_CONTINUATION_ASM_TYPE = StandardNames.CONTINUATION_INTERFACE_FQ_NAME_RELEASE.topLevelClassAsmType()
|
||||
|
||||
fun FunctionDescriptor.isInvokeSuspendOfLambda(): Boolean {
|
||||
if (this !is SimpleFunctionDescriptor) return false
|
||||
if (valueParameters.size != 1 ||
|
||||
valueParameters[0].name.asString() != SUSPEND_CALL_RESULT_NAME ||
|
||||
name.asString() != INVOKE_SUSPEND_METHOD_NAME
|
||||
name.asString() != "invokeSuspend"
|
||||
) return false
|
||||
return containingDeclaration is SyntheticClassDescriptorForLambda
|
||||
}
|
||||
@@ -62,7 +62,7 @@ class AnonymousObjectTransformer(
|
||||
createClassReader().accept(object : ClassVisitor(Opcodes.API_VERSION, classBuilder.visitor) {
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
||||
classBuilder.defineClass(null, maxOf(version, state.classFileVersion), access, name, signature, superName, interfaces)
|
||||
if (superName.isCoroutineSuperClass()) {
|
||||
if (languageVersionSettings.isCoroutineSuperClass(superName)) {
|
||||
inliningContext.isContinuation = true
|
||||
}
|
||||
superClassName = superName
|
||||
|
||||
@@ -1,386 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isMethodInsnWith
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.updateMaxStack
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class InplaceArgumentsMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
val methodContext = parseMethodOrNull(methodNode)
|
||||
if (methodContext != null) {
|
||||
if (methodContext.calls.isEmpty()) return
|
||||
|
||||
collectStartToEnd(methodContext)
|
||||
collectLvtEntryInstructions(methodContext)
|
||||
collectSuspensionPoints(methodContext)
|
||||
|
||||
transformMethod(methodContext)
|
||||
updateLvtEntriesForMovedInstructions(methodContext)
|
||||
|
||||
methodNode.removeUnusedLocalVariables()
|
||||
methodNode.updateMaxStack()
|
||||
}
|
||||
stripMarkers(methodNode)
|
||||
}
|
||||
|
||||
private class MethodContext(
|
||||
val methodNode: MethodNode,
|
||||
val calls: List<CallContext>
|
||||
) {
|
||||
val startArgToEndArg = HashMap<AbstractInsnNode, AbstractInsnNode>()
|
||||
val lvtEntryForInstruction = HashMap<AbstractInsnNode, LocalVariableNode>()
|
||||
val varInstructionMoved = HashMap<AbstractInsnNode, CallContext>()
|
||||
val suspensionJumpLabels = HashSet<LabelNode>()
|
||||
}
|
||||
|
||||
private class CallContext(
|
||||
val callStartMarker: AbstractInsnNode,
|
||||
val callEndMarker: AbstractInsnNode,
|
||||
val args: List<ArgContext>,
|
||||
val calls: List<CallContext>,
|
||||
val endLabel: LabelNode
|
||||
)
|
||||
|
||||
private class ArgContext(
|
||||
val argStartMarker: AbstractInsnNode,
|
||||
val argEndMarker: AbstractInsnNode,
|
||||
val calls: List<CallContext>,
|
||||
val storeInsn: VarInsnNode
|
||||
) {
|
||||
val loadOpcode = storeInsn.opcode - Opcodes.ISTORE + Opcodes.ILOAD
|
||||
|
||||
val varIndex = storeInsn.`var`
|
||||
}
|
||||
|
||||
private fun parseMethodOrNull(methodNode: MethodNode): MethodContext? {
|
||||
// We assume that the method body structure follows this grammar:
|
||||
// METHOD ::= insn* (CALL insn*)*
|
||||
// CALL ::= callStartMarker insn* (ARG insn*)* (CALL insn*)* callEndMarker
|
||||
// ARG ::= argStartMarker insn* (CALL insn*)* argEndMarker storeInsn
|
||||
|
||||
val iter = methodNode.instructions.iterator()
|
||||
val calls = ArrayList<CallContext>()
|
||||
try {
|
||||
while (iter.hasNext()) {
|
||||
val insn = iter.next()
|
||||
when {
|
||||
insn.isInplaceCallStartMarker() ->
|
||||
calls.add(parseCall(methodNode, insn, iter))
|
||||
insn.isInplaceCallEndMarker() || insn.isInplaceArgumentStartMarker() || insn.isInplaceArgumentEndMarker() ->
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
} catch (e: ParseErrorException) {
|
||||
return null
|
||||
}
|
||||
return MethodContext(methodNode, calls)
|
||||
}
|
||||
|
||||
private fun parseCall(methodNode: MethodNode, start: AbstractInsnNode, iter: ListIterator<AbstractInsnNode>): CallContext {
|
||||
// CALL ::= callStartMarker insn* (ARG insn*)* (CALL insn*)* callEndMarker
|
||||
val args = ArrayList<ArgContext>()
|
||||
val calls = ArrayList<CallContext>()
|
||||
while (iter.hasNext()) {
|
||||
val insn = iter.next()
|
||||
when {
|
||||
insn.isInplaceCallStartMarker() ->
|
||||
calls.add(parseCall(methodNode, insn, iter))
|
||||
insn.isInplaceCallEndMarker() -> {
|
||||
val previous = insn.previous
|
||||
val endLabel =
|
||||
if (previous.type == AbstractInsnNode.LABEL)
|
||||
previous as LabelNode
|
||||
else
|
||||
LabelNode(Label()).also {
|
||||
// Make sure each call with inplace arguments has an endLabel
|
||||
// (we need it to update LVT after transformation).
|
||||
methodNode.instructions.insertBefore(insn, it)
|
||||
}
|
||||
return CallContext(start, insn, args, calls, endLabel)
|
||||
}
|
||||
insn.isInplaceArgumentStartMarker() ->
|
||||
args.add(parseArg(methodNode, insn, iter))
|
||||
insn.isInplaceArgumentEndMarker() ->
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
// Reached instruction list end, didn't find inplace-call-end marker
|
||||
throw ParseErrorException()
|
||||
}
|
||||
|
||||
private fun parseArg(methodNode: MethodNode, start: AbstractInsnNode, iter: ListIterator<AbstractInsnNode>): ArgContext {
|
||||
// ARG ::= argStartMarker insn* (CALL insn*)* argEndMarker storeInsn
|
||||
val calls = ArrayList<CallContext>()
|
||||
while (iter.hasNext()) {
|
||||
val insn = iter.next()
|
||||
when {
|
||||
insn.isInplaceCallStartMarker() ->
|
||||
calls.add(parseCall(methodNode, insn, iter))
|
||||
insn.isInplaceArgumentEndMarker() -> {
|
||||
val next = insn.next
|
||||
if (next is VarInsnNode && next.opcode in Opcodes.ISTORE..Opcodes.ASTORE) {
|
||||
iter.next()
|
||||
return ArgContext(start, insn, calls, next)
|
||||
} else {
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
insn.isInplaceCallEndMarker() || insn.isInplaceArgumentStartMarker() ->
|
||||
throw ParseErrorException()
|
||||
}
|
||||
}
|
||||
// Reached instruction list end, didn't find inplace-argument-end marker
|
||||
throw ParseErrorException()
|
||||
}
|
||||
|
||||
private class ParseErrorException : RuntimeException() {
|
||||
override fun fillInStackTrace(): Throwable = this
|
||||
}
|
||||
|
||||
private fun collectStartToEnd(methodContext: MethodContext) {
|
||||
for (call in methodContext.calls) {
|
||||
collectStartToEnd(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectStartToEnd(methodContext: MethodContext, callContext: CallContext) {
|
||||
for (arg in callContext.args) {
|
||||
collectStartToEnd(methodContext, arg)
|
||||
}
|
||||
for (call in callContext.calls) {
|
||||
collectStartToEnd(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectStartToEnd(methodContext: MethodContext, argContext: ArgContext) {
|
||||
methodContext.startArgToEndArg[argContext.argStartMarker] = argContext.argEndMarker
|
||||
for (call in argContext.calls) {
|
||||
collectStartToEnd(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectLvtEntryInstructions(methodContext: MethodContext) {
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
val insnArray = insnList.toArray()
|
||||
for (lv in methodContext.methodNode.localVariables) {
|
||||
val lvStartIndex = insnList.indexOf(lv.start)
|
||||
val lvEndIndex = insnList.indexOf(lv.end)
|
||||
for (i in lvStartIndex until lvEndIndex) {
|
||||
val insn = insnArray[i]
|
||||
if (insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD || insn.opcode in Opcodes.ISTORE..Opcodes.ASTORE) {
|
||||
if ((insn as VarInsnNode).`var` == lv.index) {
|
||||
methodContext.lvtEntryForInstruction[insn] = lv
|
||||
}
|
||||
} else if (insn.opcode == Opcodes.IINC) {
|
||||
if ((insn as IincInsnNode).`var` == lv.index) {
|
||||
methodContext.lvtEntryForInstruction[insn] = lv
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectSuspensionPoints(methodContext: MethodContext) {
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
var insn = insnList.first
|
||||
while (
|
||||
!insn.isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner == "kotlin/coroutines/intrinsics/IntrinsicsKt" &&
|
||||
name == "getCOROUTINE_SUSPENDED" &&
|
||||
desc == "()Ljava/lang/Object;"
|
||||
}
|
||||
) {
|
||||
insn = insn.next ?: return
|
||||
}
|
||||
|
||||
// Find a first TABLESWITCH and record its jump destinations
|
||||
while (insn != null) {
|
||||
if (insn.opcode != Opcodes.TABLESWITCH || insn.previous.opcode != Opcodes.GETFIELD) {
|
||||
insn = insn.next
|
||||
continue
|
||||
}
|
||||
val getFiendInsn = insn.previous as FieldInsnNode
|
||||
if (getFiendInsn.name != "label" || getFiendInsn.desc != "I") {
|
||||
insn = insn.next
|
||||
continue
|
||||
}
|
||||
val tableSwitchInsn = insn as TableSwitchInsnNode
|
||||
methodContext.suspensionJumpLabels.addAll(tableSwitchInsn.labels)
|
||||
methodContext.suspensionJumpLabels.add(tableSwitchInsn.dflt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformMethod(methodContext: MethodContext) {
|
||||
for (call in methodContext.calls) {
|
||||
transformCall(methodContext, call)
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformCall(methodContext: MethodContext, callContext: CallContext) {
|
||||
// Transform nested calls
|
||||
for (arg in callContext.args) {
|
||||
for (nestedCall in arg.calls) {
|
||||
transformCall(methodContext, nestedCall)
|
||||
}
|
||||
}
|
||||
for (call in callContext.calls) {
|
||||
transformCall(methodContext, call)
|
||||
}
|
||||
|
||||
// If an inplace argument contains a non-local jump,
|
||||
// moving such argument inside inline function body can interfere with stack normalization.
|
||||
// TODO investigate complex cases
|
||||
if (callContext.args.any { it.isUnsafeToMove(methodContext) }) {
|
||||
// Do not transform such call, just strip call and argument markers.
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
for (arg in callContext.args) {
|
||||
insnList.remove(arg.argStartMarker)
|
||||
insnList.remove(arg.argEndMarker)
|
||||
}
|
||||
insnList.remove(callContext.callStartMarker)
|
||||
insnList.remove(callContext.callEndMarker)
|
||||
return
|
||||
}
|
||||
|
||||
moveInplaceArgumentsFromStoresToLoads(methodContext, callContext)
|
||||
}
|
||||
|
||||
private fun ArgContext.isUnsafeToMove(methodContext: MethodContext): Boolean {
|
||||
val argInsns = InsnSequence(this.argStartMarker, this.argEndMarker)
|
||||
val localLabels = argInsns.filterTo(HashSet()) { it is LabelNode }
|
||||
return argInsns.any { insn ->
|
||||
insn in methodContext.suspensionJumpLabels ||
|
||||
insn.opcode == Opcodes.GOTO && (insn as JumpInsnNode).label !in localLabels
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveInplaceArgumentsFromStoresToLoads(methodContext: MethodContext, callContext: CallContext) {
|
||||
// Transform call
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
val args = callContext.args.associateBy { it.varIndex }
|
||||
var argsProcessed = 0
|
||||
|
||||
var insn: AbstractInsnNode = callContext.callStartMarker
|
||||
while (insn != callContext.callEndMarker) {
|
||||
when {
|
||||
insn.isInplaceArgumentStartMarker() -> {
|
||||
// Skip argument body
|
||||
insn = methodContext.startArgToEndArg[insn]!!
|
||||
}
|
||||
|
||||
insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD -> {
|
||||
// Load instruction
|
||||
val loadInsn = insn as VarInsnNode
|
||||
val varIndex = loadInsn.`var`
|
||||
val arg = args[varIndex]
|
||||
|
||||
if (arg == null || arg.loadOpcode != insn.opcode) {
|
||||
// Not an argument load
|
||||
insn = insn.next
|
||||
} else {
|
||||
// For each argument within this call we have
|
||||
// <inplaceArgStartMarker>
|
||||
// <argumentBody>
|
||||
// <inplaceArgEndMarker>
|
||||
// store [arg]
|
||||
// ...
|
||||
// load [arg]
|
||||
// Replace 'load [arg]' with '<argumentBody>', drop 'store [arg]' and argument markers.
|
||||
|
||||
var argInsn = arg.argStartMarker.next
|
||||
while (argInsn != arg.argEndMarker) {
|
||||
// If a LOAD/STORE/IINC instruction was moved,
|
||||
// record it so that we can update corresponding LVT entry if needed.
|
||||
// NB it's better to do so after all transformations, so that we don't recalculate node indices.
|
||||
if (argInsn.opcode in Opcodes.ILOAD..Opcodes.ALOAD ||
|
||||
argInsn.opcode in Opcodes.ISTORE..Opcodes.ASTORE ||
|
||||
argInsn.opcode == Opcodes.IINC
|
||||
) {
|
||||
methodContext.varInstructionMoved[argInsn] = callContext
|
||||
}
|
||||
|
||||
val argInsnNext = argInsn.next
|
||||
insnList.remove(argInsn)
|
||||
insnList.insertBefore(loadInsn, argInsn)
|
||||
argInsn = argInsnNext
|
||||
}
|
||||
|
||||
// Remove argument load and corresponding argument store instructions
|
||||
insnList.remove(arg.storeInsn)
|
||||
insn = loadInsn.next
|
||||
insnList.remove(loadInsn)
|
||||
|
||||
// Replace subsequent argument loads with DUP instructions of appropriate size
|
||||
while (insn.opcode == loadInsn.opcode && (insn as VarInsnNode).`var` == varIndex) {
|
||||
if (insn.opcode == Opcodes.LLOAD || insn.opcode == Opcodes.DLOAD) {
|
||||
insnList.insertBefore(insn, InsnNode(Opcodes.DUP2))
|
||||
} else {
|
||||
insnList.insertBefore(insn, InsnNode(Opcodes.DUP))
|
||||
}
|
||||
val next = insn.next
|
||||
insnList.remove(insn)
|
||||
insn = next
|
||||
}
|
||||
|
||||
// Remove argument markers
|
||||
insnList.remove(arg.argStartMarker)
|
||||
insnList.remove(arg.argEndMarker)
|
||||
|
||||
// If there are no more inplace arguments left to process, we are done
|
||||
++argsProcessed
|
||||
if (argsProcessed >= callContext.args.size)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
insn = insn.next
|
||||
}
|
||||
}
|
||||
|
||||
// Remove call start and call end markers
|
||||
insnList.remove(callContext.callStartMarker)
|
||||
insnList.remove(callContext.callEndMarker)
|
||||
}
|
||||
|
||||
private fun updateLvtEntriesForMovedInstructions(methodContext: MethodContext) {
|
||||
val insnList = methodContext.methodNode.instructions
|
||||
for ((insn, callContext) in methodContext.varInstructionMoved.entries) {
|
||||
// Extend local variable interval to call end label if needed
|
||||
val lv = methodContext.lvtEntryForInstruction[insn] ?: continue
|
||||
val lvEndIndex = insnList.indexOf(lv.end)
|
||||
val endLabelIndex = insnList.indexOf(callContext.endLabel)
|
||||
if (endLabelIndex > lvEndIndex) {
|
||||
lv.end = callContext.endLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stripMarkers(methodNode: MethodNode) {
|
||||
var insn = methodNode.instructions.first
|
||||
while (insn != null) {
|
||||
if (insn.isInplaceCallStartMarker() ||
|
||||
insn.isInplaceCallEndMarker() ||
|
||||
insn.isInplaceArgumentStartMarker() ||
|
||||
insn.isInplaceArgumentEndMarker()
|
||||
) {
|
||||
val next = insn.next
|
||||
methodNode.instructions.remove(insn)
|
||||
insn = next
|
||||
continue
|
||||
}
|
||||
insn = insn.next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ class DefaultLambda(info: ExtractedDefaultLambda, sourceCompiler: SourceCompiler
|
||||
// TODO: suspend lambdas are their own continuations, so the body is pre-inlined into `invokeSuspend`
|
||||
// and thus can't be detangled from the state machine. To make them inlinable, this needs to be redesigned.
|
||||
// See `SuspendLambdaLowering`.
|
||||
require(!superName.isCoroutineSuperClass()) {
|
||||
require(!sourceCompiler.state.languageVersionSettings.isCoroutineSuperClass(superName)) {
|
||||
"suspend default lambda ${lambdaClassType.internalName} cannot be inlined; use a function reference instead"
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
|
||||
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
|
||||
import org.jetbrains.kotlin.codegen.inline.FieldRemapper.Companion.foldName
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.markNoinlineLambdaIfSuspend
|
||||
@@ -688,7 +688,7 @@ class MethodInliner(
|
||||
}
|
||||
|
||||
for ((index, param) in paramTypes.reversed().withIndex()) {
|
||||
if (param != CONTINUATION_ASM_TYPE && param != OBJECT_TYPE) continue
|
||||
if (param != languageVersionSettings.continuationAsmType() && param != OBJECT_TYPE) continue
|
||||
val sourceIndices = (frame.getStack(frame.stackSize - index - 1) as? Aload0BasicValue)?.indices ?: continue
|
||||
for (sourceIndex in sourceIndices) {
|
||||
val src = processingNode.instructions[sourceIndex]
|
||||
@@ -733,7 +733,6 @@ class MethodInliner(
|
||||
|
||||
private fun preprocessNodeBeforeInline(node: MethodNode, returnLabels: Map<String, Label?>) {
|
||||
try {
|
||||
InplaceArgumentsMethodTransformer().transform("fake", node)
|
||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||
} catch (e: Throwable) {
|
||||
throw wrapException(e, node, "couldn't inline method call")
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -157,7 +158,8 @@ class PsiSourceCompilerForInline(
|
||||
}
|
||||
|
||||
FunctionCodegen.generateMethodBody(
|
||||
adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode
|
||||
adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode,
|
||||
state.languageVersionSettings.isReleaseCoroutines()
|
||||
)
|
||||
|
||||
if (isLambda) {
|
||||
@@ -233,7 +235,7 @@ class PsiSourceCompilerForInline(
|
||||
}
|
||||
|
||||
override fun compileInlineFunction(jvmSignature: JvmMethodSignature): SMAPAndMethodNode {
|
||||
generateInlineIntrinsic(functionDescriptor, jvmSignature.asmMethod, codegen.typeSystem)?.let {
|
||||
generateInlineIntrinsic(state.languageVersionSettings, functionDescriptor, jvmSignature.asmMethod, codegen.typeSystem)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -231,7 +233,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
if (stubCheckcast !is TypeInsnNode) return false
|
||||
|
||||
val newMethodNode = MethodNode(Opcodes.API_VERSION)
|
||||
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, unifiedNullChecks)
|
||||
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe, languageVersionSettings, unifiedNullChecks)
|
||||
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
// Keep stubCheckcast to avoid VerifyErrors on 1.8+ bytecode,
|
||||
@@ -255,7 +257,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
if (stubInstanceOf !is TypeInsnNode) return false
|
||||
|
||||
val newMethodNode = MethodNode(Opcodes.API_VERSION)
|
||||
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
|
||||
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType, languageVersionSettings.isReleaseCoroutines())
|
||||
|
||||
instructions.insert(insn, newMethodNode.instructions)
|
||||
instructions.remove(stubInstanceOf)
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
@@ -48,16 +49,18 @@ class CoroutineTransformer(
|
||||
fun suspendLambdaWithGeneratedStateMachine(node: MethodNode): Boolean =
|
||||
!isContinuationNotLambda() && isSuspendLambda(node) && isStateMachine(node)
|
||||
|
||||
private fun isContinuationNotLambda(): Boolean = inliningContext.isContinuation && superClassName.endsWith("ContinuationImpl")
|
||||
private fun isContinuationNotLambda(): Boolean = inliningContext.isContinuation &&
|
||||
if (state.languageVersionSettings.isReleaseCoroutines()) superClassName.endsWith("ContinuationImpl")
|
||||
else methods.any { it.name == "getLabel" }
|
||||
|
||||
private fun isStateMachine(node: MethodNode): Boolean =
|
||||
node.instructions.asSequence().any { insn -> insn is LdcInsnNode && insn.cst == ILLEGAL_STATE_ERROR_MESSAGE }
|
||||
|
||||
private fun isSuspendLambda(node: MethodNode) = isInvokeSuspend(node)
|
||||
private fun isSuspendLambda(node: MethodNode) = isResumeImpl(node)
|
||||
|
||||
fun newMethod(node: MethodNode): DeferredMethodVisitor {
|
||||
return when {
|
||||
isInvokeSuspend(node) -> {
|
||||
isResumeImpl(node) -> {
|
||||
assert(!isStateMachine(node)) {
|
||||
"Inlining/transforming state-machine"
|
||||
}
|
||||
@@ -68,8 +71,9 @@ class CoroutineTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInvokeSuspend(node: MethodNode): Boolean =
|
||||
node.name.removeSuffix(FOR_INLINE_SUFFIX) == INVOKE_SUSPEND_METHOD_NAME && inliningContext.isContinuation
|
||||
private fun isResumeImpl(node: MethodNode): Boolean =
|
||||
state.languageVersionSettings.isResumeImplMethodName(node.name.removeSuffix(FOR_INLINE_SUFFIX)) &&
|
||||
inliningContext.isContinuation
|
||||
|
||||
private fun isSuspendFunctionWithFakeConstructorCall(node: MethodNode): Boolean = findFakeContinuationConstructorClassName(node) != null
|
||||
|
||||
@@ -84,15 +88,16 @@ class CoroutineTransformer(
|
||||
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
||||
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
obtainClassBuilderForCoroutineState = { classBuilder },
|
||||
isForNamedFunction = false,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||
// TODO: this linenumbers might not be correct and since they are used only for step-over, check them.
|
||||
lineNumber = inliningContext.callSiteInfo.lineNumber,
|
||||
sourceFile = inliningContext.callSiteInfo.file?.name ?: "",
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
isForNamedFunction = false,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = false,
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS)
|
||||
)
|
||||
|
||||
@@ -118,16 +123,17 @@ class CoroutineTransformer(
|
||||
val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline
|
||||
val stateMachineBuilder = CoroutineTransformerMethodVisitor(
|
||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
||||
isForNamedFunction = true,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
|
||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||
lineNumber = inliningContext.callSiteInfo.lineNumber,
|
||||
sourceFile = inliningContext.callSiteInfo.file?.name ?: "",
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
isForNamedFunction = true,
|
||||
needDispatchReceiver = true,
|
||||
internalNameForDispatchReceiver = classBuilder.thisName,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization,
|
||||
putContinuationParameterToLvt = !state.isIrBackend,
|
||||
useOldSpilledVarTypeAnalysis = state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS)
|
||||
)
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isParameterCheckedForNull
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
|
||||
fun canInlineArgumentsInPlace(methodNode: MethodNode): Boolean {
|
||||
// Usual inline functions are inlined in the following way:
|
||||
// <evaluate argument #1>
|
||||
// <store argument to an argument variable V1>
|
||||
// ...
|
||||
// <evaluate argument #N>
|
||||
// <store argument to an argument variable VN>
|
||||
// <inline function method body with parameter variables Pi remapped to argument variables Vi>
|
||||
// If an argument #k is already stored in a local variable W, this variable W is reused.
|
||||
// When inlining arguments in-place, we instead replace corresponding variable load instructions in the inline function method body
|
||||
// with bytecode for evaluating a given argument.
|
||||
// We can do so if such transformation keeps the evaluation order intact, possibly disregarding class initialization.
|
||||
//
|
||||
// This is true for many simple @InlineOnly functions from Kotlin standard library.
|
||||
// For example, bytecode for 'inline fun println(message: Any?)' is:
|
||||
// GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
|
||||
// ALOAD 0
|
||||
// INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
|
||||
// Basic inlining for 'println("Hello, world!")' would produce (skipping labels and line numbers):
|
||||
// // evaluate arguments, storing them to local variables
|
||||
// LDC "Hello, world!"
|
||||
// ASTORE 0
|
||||
// GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
|
||||
// ALOAD 0
|
||||
// INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
|
||||
// With argument "Hello, world!" inlined in-place it would be:
|
||||
// GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
|
||||
// LDC "Hello, world!"
|
||||
// INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
|
||||
// Such inlining is possible because we consider it OK to reorder 'GETSTATIC java/lang/System.out : Ljava/io/PrintStream;' instruction
|
||||
// with any argument evaluation instructions ('LDC "Hello, world!"' in this case).
|
||||
|
||||
val tcbStartLabels = methodNode.tryCatchBlocks.mapTo(HashSet()) { it.start }
|
||||
|
||||
val methodParameterTypes = Type.getArgumentTypes(methodNode.desc)
|
||||
|
||||
val jvmArgumentTypes = ArrayList<Type>(methodParameterTypes.size + 1)
|
||||
if (methodNode.access and Opcodes.ACC_STATIC == 0) {
|
||||
// Here we don't care much about the exact 'this' type,
|
||||
// it's only important to remember that variable slot #0 holds an object reference.
|
||||
jvmArgumentTypes.add(AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
jvmArgumentTypes.addAll(methodParameterTypes)
|
||||
|
||||
val argumentVarEnd = jvmArgumentTypes.sumOf { it.size }
|
||||
var expectedArgumentVar = 0
|
||||
var lastArgIndex = 0
|
||||
|
||||
var insn = methodNode.instructions.first
|
||||
|
||||
// During arguments evaluation, make sure that all arguments are loaded in expected order
|
||||
// and there are no unexpected side effects in-between.
|
||||
while (insn != null && expectedArgumentVar < argumentVarEnd) {
|
||||
// Entering a try-catch block before all arguments are loaded breaks evaluation order.
|
||||
if (insn in tcbStartLabels)
|
||||
return false
|
||||
|
||||
// Some instructions break evaluation order.
|
||||
if (insn.isProhibitedDuringArgumentsEvaluation())
|
||||
return false
|
||||
|
||||
// Allow a limited list of 'GETSTATIC <owner> <name> <desc>' instructions.
|
||||
if (insn.opcode == Opcodes.GETSTATIC) {
|
||||
val fieldInsn = insn as FieldInsnNode
|
||||
val fieldSignature = FieldSignature(fieldInsn.owner, fieldInsn.name, fieldInsn.desc)
|
||||
if (fieldSignature !in whitelistedStaticFields)
|
||||
return false
|
||||
}
|
||||
|
||||
// Writing to or incrementing an argument variable forbids in-place argument inlining.
|
||||
if (insn.opcode in Opcodes.ISTORE..Opcodes.ASTORE && (insn as VarInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
if (insn.opcode == Opcodes.IINC && (insn as IincInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
|
||||
// Analyze variable loads.
|
||||
if (insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD) {
|
||||
// Skip parameter null check: 'aload x; ldc "..."; invokestatic <check>'
|
||||
if (insn.opcode == Opcodes.ALOAD && insn.isParameterCheckedForNull()) {
|
||||
// Go directly to the instruction after 'invokestatic <check>'
|
||||
insn = insn.next.next.next
|
||||
continue
|
||||
}
|
||||
|
||||
val varInsn = insn as VarInsnNode
|
||||
val varIndex = (varInsn).`var`
|
||||
if (varIndex == expectedArgumentVar) {
|
||||
// Expected argument variable loaded.
|
||||
expectedArgumentVar += jvmArgumentTypes[lastArgIndex].size
|
||||
++lastArgIndex
|
||||
// Skip a sequence of load instructions referring to the same argument variable
|
||||
// (such sequence is present in functions like 'Array.copyOf' and can be replaced with DUP instructions).
|
||||
do {
|
||||
insn = insn.next
|
||||
} while (insn != null && insn.opcode == varInsn.opcode && (insn as VarInsnNode).`var` == varIndex)
|
||||
continue
|
||||
} else if (varIndex < argumentVarEnd) {
|
||||
// Loaded an argument variable, but not an expected one => broken evaluation order
|
||||
return false
|
||||
} else {
|
||||
// It's OK to load any non-argument variable during argument evaluation.
|
||||
insn = insn.next
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Anything else is fine.
|
||||
insn = insn.next
|
||||
}
|
||||
|
||||
// Method body is over, but not all arguments were loaded on stack.
|
||||
if (expectedArgumentVar < argumentVarEnd)
|
||||
return false
|
||||
|
||||
// After arguments evaluation make sure that argument variables are no longer accessed
|
||||
// (we are not going to store anything to those variables anyway).
|
||||
while (insn != null) {
|
||||
if (insn.opcode in Opcodes.ILOAD..Opcodes.ALOAD || insn.opcode in Opcodes.ISTORE..Opcodes.ASTORE) {
|
||||
if ((insn as VarInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
} else if (insn.opcode == Opcodes.IINC) {
|
||||
if ((insn as IincInsnNode).`var` < argumentVarEnd)
|
||||
return false
|
||||
}
|
||||
insn = insn.next
|
||||
}
|
||||
|
||||
// Didn't encounter anything suspicious.
|
||||
return true
|
||||
}
|
||||
|
||||
internal data class FieldSignature(
|
||||
val owner: String,
|
||||
val name: String,
|
||||
val desc: String
|
||||
)
|
||||
|
||||
private val whitelistedStaticFields: Set<FieldSignature> =
|
||||
hashSetOf(
|
||||
FieldSignature("java/lang/System", "out", "Ljava/io/PrintStream;"),
|
||||
FieldSignature("kotlin/Result", "Companion", "Lkotlin/Result\$Companion;"),
|
||||
FieldSignature("kotlin/_Assertions", "ENABLED", "Z")
|
||||
)
|
||||
|
||||
private fun AbstractInsnNode.isProhibitedDuringArgumentsEvaluation() =
|
||||
opcode in opcodeProhibitedDuringArgumentsEvaluation.indices &&
|
||||
opcodeProhibitedDuringArgumentsEvaluation[opcode]
|
||||
|
||||
private val opcodeProhibitedDuringArgumentsEvaluation = BooleanArray(256).also { a ->
|
||||
// Any kind of jump during arguments evaluation is a hazard.
|
||||
// This includes all conditional jump instructions, switch instructions, return and throw instructions.
|
||||
// Very conservative, but enough for practical cases.
|
||||
for (i in Opcodes.IFEQ..Opcodes.RETURN) a[i] = true
|
||||
a[Opcodes.IFNULL] = true
|
||||
a[Opcodes.IFNONNULL] = true
|
||||
a[Opcodes.ATHROW] = true
|
||||
|
||||
// Instruction with non-trivial side effects is a hazard.
|
||||
// NB GETSTATIC is taken care of separately.
|
||||
a[Opcodes.PUTSTATIC] = true
|
||||
a[Opcodes.PUTFIELD] = true
|
||||
a[Opcodes.INVOKEVIRTUAL] = true
|
||||
a[Opcodes.INVOKESPECIAL] = true
|
||||
a[Opcodes.INVOKESTATIC] = true
|
||||
a[Opcodes.INVOKEINTERFACE] = true
|
||||
a[Opcodes.INVOKEDYNAMIC] = true
|
||||
a[Opcodes.MONITORENTER] = true
|
||||
a[Opcodes.MONITOREXIT] = true
|
||||
|
||||
// Integer division instructions can throw exception
|
||||
a[Opcodes.IDIV] = true
|
||||
a[Opcodes.LDIV] = true
|
||||
a[Opcodes.IREM] = true
|
||||
a[Opcodes.LREM] = true
|
||||
|
||||
// CHECKCAST can throw exception
|
||||
a[Opcodes.CHECKCAST] = true
|
||||
|
||||
// Array creation can throw exception (in case of negative array size)
|
||||
a[Opcodes.NEWARRAY] = true
|
||||
a[Opcodes.ANEWARRAY] = true
|
||||
a[Opcodes.MULTIANEWARRAY] = true
|
||||
|
||||
// Array access instructions can throw exception
|
||||
for (i in Opcodes.IALOAD..Opcodes.SALOAD) a[i] = true
|
||||
for (i in Opcodes.IASTORE..Opcodes.SASTORE) a[i] = true
|
||||
}
|
||||
|
||||
|
||||
private const val MARKER_INPLACE_CALL_START = "<INPLACE-CALL-START>"
|
||||
private const val MARKER_INPLACE_ARGUMENT_START = "<INPLACE-ARGUMENT-START>"
|
||||
private const val MARKER_INPLACE_ARGUMENT_END = "<INPLACE-ARGUMENT-END>"
|
||||
private const val MARKER_INPLACE_CALL_END = "<INPLACE-CALL-END>"
|
||||
|
||||
|
||||
private fun InstructionAdapter.addMarker(name: String) {
|
||||
visitMethodInsn(Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME, name, "()V", false)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.addInplaceCallStartMarker() = addMarker(MARKER_INPLACE_CALL_START)
|
||||
fun InstructionAdapter.addInplaceCallEndMarker() = addMarker(MARKER_INPLACE_CALL_END)
|
||||
fun InstructionAdapter.addInplaceArgumentStartMarker() = addMarker(MARKER_INPLACE_ARGUMENT_START)
|
||||
fun InstructionAdapter.addInplaceArgumentEndMarker() = addMarker(MARKER_INPLACE_ARGUMENT_END)
|
||||
|
||||
internal fun AbstractInsnNode.isInplaceCallStartMarker() = isInlineMarker(this, MARKER_INPLACE_CALL_START)
|
||||
internal fun AbstractInsnNode.isInplaceCallEndMarker() = isInlineMarker(this, MARKER_INPLACE_CALL_END)
|
||||
internal fun AbstractInsnNode.isInplaceArgumentStartMarker() = isInlineMarker(this, MARKER_INPLACE_ARGUMENT_START)
|
||||
internal fun AbstractInsnNode.isInplaceArgumentEndMarker() = isInlineMarker(this, MARKER_INPLACE_ARGUMENT_END)
|
||||
@@ -60,7 +60,7 @@ const val INLINE_FUN_VAR_SUFFIX = "\$iv"
|
||||
internal const val FIRST_FUN_LABEL = "$$$$\$ROOT$$$$$"
|
||||
internal const val SPECIAL_TRANSFORMATION_NAME = "\$special"
|
||||
const val INLINE_TRANSFORMATION_SUFFIX = "\$inlined"
|
||||
internal const val INLINE_CALL_TRANSFORMATION_SUFFIX = "$$INLINE_TRANSFORMATION_SUFFIX"
|
||||
internal const val INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX
|
||||
internal const val INLINE_FUN_THIS_0_SUFFIX = "\$inline_fun"
|
||||
internal const val DEFAULT_LAMBDA_FAKE_CALL = "$$\$DEFAULT_LAMBDA_FAKE_CALL$$$"
|
||||
internal const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
|
||||
@@ -68,10 +68,11 @@ internal const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
|
||||
private const val NON_LOCAL_RETURN = "$$$$\$NON_LOCAL_RETURN$$$$$"
|
||||
const val CAPTURED_FIELD_PREFIX = "$"
|
||||
private const val NON_CAPTURED_FIELD_PREFIX = "$$"
|
||||
internal const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
|
||||
private const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
|
||||
private const val INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall"
|
||||
private const val INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall"
|
||||
private const val INLINE_MARKER_FINALLY_START = "finallyStart"
|
||||
|
||||
private const val INLINE_MARKER_FINALLY_END = "finallyEnd"
|
||||
private const val INLINE_MARKER_BEFORE_SUSPEND_ID = 0
|
||||
private const val INLINE_MARKER_AFTER_SUSPEND_ID = 1
|
||||
@@ -301,7 +302,7 @@ internal fun firstLabelInChain(node: LabelNode): LabelNode {
|
||||
internal fun areLabelsBeforeSameInsn(first: LabelNode, second: LabelNode): Boolean =
|
||||
firstLabelInChain(first) == firstLabelInChain(second)
|
||||
|
||||
val MethodNode?.nodeText: String
|
||||
internal val MethodNode?.nodeText: String
|
||||
get() {
|
||||
if (this == null) {
|
||||
return "Not generated"
|
||||
@@ -534,15 +535,17 @@ internal fun isInlineMarker(insn: AbstractInsnNode): Boolean {
|
||||
return isInlineMarker(insn, null)
|
||||
}
|
||||
|
||||
internal fun isInlineMarker(insn: AbstractInsnNode, name: String?): Boolean {
|
||||
if (insn.opcode != Opcodes.INVOKESTATIC) return false
|
||||
private fun isInlineMarker(insn: AbstractInsnNode, name: String?): Boolean {
|
||||
if (insn !is MethodInsnNode) {
|
||||
return false
|
||||
}
|
||||
|
||||
val methodInsn = insn as MethodInsnNode
|
||||
return methodInsn.owner == INLINE_MARKER_CLASS_NAME &&
|
||||
return insn.getOpcode() == Opcodes.INVOKESTATIC &&
|
||||
insn.owner == INLINE_MARKER_CLASS_NAME &&
|
||||
if (name != null)
|
||||
methodInsn.name == name
|
||||
insn.name == name
|
||||
else
|
||||
methodInsn.name == INLINE_MARKER_BEFORE_METHOD_NAME || methodInsn.name == INLINE_MARKER_AFTER_METHOD_NAME
|
||||
insn.name == INLINE_MARKER_BEFORE_METHOD_NAME || insn.name == INLINE_MARKER_AFTER_METHOD_NAME
|
||||
}
|
||||
|
||||
internal fun isBeforeInlineMarker(insn: AbstractInsnNode): Boolean {
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInIntercepted
|
||||
import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForCoroutineContext
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForIntercepted
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForSuspendCoroutineUninterceptedOrReturn
|
||||
import org.jetbrains.kotlin.codegen.createMethodNodeForAlwaysEnabledAssert
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructors
|
||||
@@ -28,22 +30,25 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
fun generateInlineIntrinsicForIr(descriptor: FunctionDescriptor): SMAPAndMethodNode? =
|
||||
fun generateInlineIntrinsicForIr(languageVersionSettings: LanguageVersionSettings, descriptor: FunctionDescriptor): SMAPAndMethodNode? =
|
||||
when {
|
||||
// TODO: implement these as codegen intrinsics (see IrIntrinsicMethods)
|
||||
descriptor.isBuiltInCoroutineContext() ->
|
||||
createMethodNodeForCoroutineContext(descriptor)
|
||||
descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn() ->
|
||||
createMethodNodeForSuspendCoroutineUninterceptedOrReturn()
|
||||
descriptor.isBuiltInIntercepted(languageVersionSettings) ->
|
||||
createMethodNodeForIntercepted(languageVersionSettings)
|
||||
descriptor.isBuiltInCoroutineContext(languageVersionSettings) ->
|
||||
createMethodNodeForCoroutineContext(descriptor, languageVersionSettings)
|
||||
descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersionSettings) ->
|
||||
createMethodNodeForSuspendCoroutineUninterceptedOrReturn(languageVersionSettings)
|
||||
else -> null
|
||||
}?.let { SMAPAndMethodNode(it, SMAP(listOf())) }
|
||||
|
||||
internal fun generateInlineIntrinsic(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
descriptor: FunctionDescriptor,
|
||||
asmMethod: Method,
|
||||
typeSystem: TypeSystemCommonBackendContext
|
||||
): SMAPAndMethodNode? {
|
||||
return generateInlineIntrinsicForIr(descriptor) ?: when {
|
||||
return generateInlineIntrinsicForIr(languageVersionSettings, descriptor) ?: when {
|
||||
isSpecialEnumMethod(descriptor) ->
|
||||
createSpecialEnumMethodBody(descriptor.name.asString(), descriptor.original.typeParameters.single(), typeSystem)
|
||||
TypeOfChecker.isTypeOf(descriptor) ->
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
object TypeIntrinsics {
|
||||
@JvmStatic
|
||||
fun instanceOf(v: InstructionAdapter, jetType: KotlinType, boxedAsmType: Type) {
|
||||
fun instanceOf(v: InstructionAdapter, jetType: KotlinType, boxedAsmType: Type, isReleaseCoroutines: Boolean) {
|
||||
val functionTypeArity = getFunctionTypeArity(jetType)
|
||||
if (functionTypeArity >= 0) {
|
||||
v.iconst(functionTypeArity)
|
||||
@@ -27,26 +27,28 @@ object TypeIntrinsics {
|
||||
return
|
||||
}
|
||||
|
||||
val suspendFunctionTypeArity = getSuspendFunctionTypeArity(jetType)
|
||||
if (suspendFunctionTypeArity >= 0) {
|
||||
val notSuspendLambda = Label()
|
||||
val end = Label()
|
||||
if (isReleaseCoroutines) {
|
||||
val suspendFunctionTypeArity = getSuspendFunctionTypeArity(jetType)
|
||||
if (suspendFunctionTypeArity >= 0) {
|
||||
val notSuspendLambda = Label()
|
||||
val end = Label()
|
||||
|
||||
with(v) {
|
||||
dup()
|
||||
instanceOf(AsmTypes.SUSPEND_FUNCTION_TYPE)
|
||||
ifeq(notSuspendLambda)
|
||||
iconst(suspendFunctionTypeArity + 1)
|
||||
typeIntrinsic(IS_FUNCTON_OF_ARITY_METHOD_NAME, IS_FUNCTON_OF_ARITY_DESCRIPTOR)
|
||||
goTo(end)
|
||||
with(v) {
|
||||
dup()
|
||||
instanceOf(AsmTypes.SUSPEND_FUNCTION_TYPE)
|
||||
ifeq(notSuspendLambda)
|
||||
iconst(suspendFunctionTypeArity + 1)
|
||||
typeIntrinsic(IS_FUNCTON_OF_ARITY_METHOD_NAME, IS_FUNCTON_OF_ARITY_DESCRIPTOR)
|
||||
goTo(end)
|
||||
|
||||
mark(notSuspendLambda)
|
||||
pop()
|
||||
iconst(0)
|
||||
mark(notSuspendLambda)
|
||||
pop()
|
||||
iconst(0)
|
||||
|
||||
mark(end)
|
||||
mark(end)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val isMutableCollectionMethodName = getIsMutableCollectionMethodName(jetType)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.inline.InplaceArgumentsMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.PopBackwardPropagationTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.StackPeepholeOptimizationsTransformer
|
||||
@@ -41,7 +40,6 @@ class OptimizationMethodVisitor(
|
||||
UninitializedStoresMethodTransformer(generationState.constructorCallNormalizationMode)
|
||||
|
||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||
InplaceArgumentsMethodTransformer(),
|
||||
FixStackWithLabelNormalizationMethodTransformer(),
|
||||
MethodVerifier("AFTER mandatory stack transformations", generationState)
|
||||
)
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.coroutines.RELEASE_COROUTINES_VERSION_SETTINGS
|
||||
import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
@@ -215,7 +216,7 @@ fun AbstractInsnNode.isPrimitiveBoxing() =
|
||||
}
|
||||
|
||||
private val BOXING_CLASS_INTERNAL_NAME =
|
||||
StandardNames.COROUTINES_JVM_INTERNAL_PACKAGE_FQ_NAME.child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName().child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
|
||||
private fun isJvmPrimitiveName(name: String) = JvmPrimitiveType.values().any { it.javaKeywordName == name }
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -35,7 +36,7 @@ val AbstractInsnNode.isMeaningful: Boolean
|
||||
|
||||
val AbstractInsnNode.isBranchOrCall: Boolean
|
||||
get() =
|
||||
when (this.type) {
|
||||
when(this.type) {
|
||||
AbstractInsnNode.JUMP_INSN,
|
||||
AbstractInsnNode.TABLESWITCH_INSN,
|
||||
AbstractInsnNode.LOOKUPSWITCH_INSN,
|
||||
@@ -84,17 +85,13 @@ fun MethodNode.prepareForEmitting() {
|
||||
|
||||
current = prev
|
||||
}
|
||||
updateMaxStack()
|
||||
}
|
||||
|
||||
fun MethodNode.updateMaxStack() {
|
||||
maxStack = -1
|
||||
accept(
|
||||
MaxStackFrameSizeAndLocalsCalculator(
|
||||
API_VERSION, access, desc,
|
||||
object : MethodVisitor(API_VERSION) {
|
||||
Opcodes.API_VERSION, access, desc,
|
||||
object : MethodVisitor(Opcodes.API_VERSION) {
|
||||
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
|
||||
this@updateMaxStack.maxStack = maxStack
|
||||
this@prepareForEmitting.maxStack = maxStack
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -103,10 +100,10 @@ fun MethodNode.updateMaxStack() {
|
||||
fun MethodNode.stripOptimizationMarkers() {
|
||||
var insn = instructions.first
|
||||
while (insn != null) {
|
||||
insn = if (isOptimizationMarker(insn)) {
|
||||
instructions.removeNodeGetNext(insn)
|
||||
if (isOptimizationMarker(insn)) {
|
||||
insn = instructions.removeNodeGetNext(insn)
|
||||
} else {
|
||||
insn.next
|
||||
insn = insn.next
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +123,7 @@ fun MethodNode.removeUnusedLocalVariables() {
|
||||
// Arguments are always used whether or not they are in the local variable table
|
||||
// or used by instructions.
|
||||
var argumentIndex = 0
|
||||
val isStatic = (access and ACC_STATIC) != 0
|
||||
val isStatic = (access and Opcodes.ACC_STATIC) != 0
|
||||
if (!isStatic) {
|
||||
used[argumentIndex++] = true
|
||||
}
|
||||
@@ -233,8 +230,8 @@ val AbstractInsnNode.intConstant: Int?
|
||||
|
||||
fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
|
||||
|
||||
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in ISTORE..ASTORE
|
||||
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in ILOAD..ALOAD
|
||||
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
|
||||
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
|
||||
|
||||
val AbstractInsnNode?.debugText
|
||||
get() =
|
||||
|
||||
@@ -441,7 +441,7 @@ fun MethodNode.usesLocalExceptParameterNullCheck(index: Int): Boolean =
|
||||
it is VarInsnNode && it.opcode == Opcodes.ALOAD && it.`var` == index && !it.isParameterCheckedForNull()
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isParameterCheckedForNull(): Boolean =
|
||||
internal fun AbstractInsnNode.isParameterCheckedForNull(): Boolean =
|
||||
next?.takeIf { it.opcode == Opcodes.LDC }?.next?.isCheckParameterIsNotNull() == true
|
||||
|
||||
internal fun AbstractInsnNode.isCheckParameterIsNotNull() =
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.PrecomputedSuppressCache
|
||||
@@ -157,7 +158,8 @@ class GenerationState private constructor(
|
||||
CompilerDeserializationConfiguration(languageVersionSettings)
|
||||
|
||||
val deprecationProvider = DeprecationResolver(
|
||||
LockBasedStorageManager.NO_LOCKS, languageVersionSettings, JavaDeprecationSettings
|
||||
LockBasedStorageManager.NO_LOCKS, languageVersionSettings, CoroutineCompatibilitySupport.ENABLED,
|
||||
JavaDeprecationSettings
|
||||
)
|
||||
|
||||
init {
|
||||
|
||||
@@ -193,7 +193,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
if (descriptor.isSuspendFunctionNotSuspensionView()) {
|
||||
return mapReturnType(getOrCreateJvmSuspendFunctionView(descriptor as SimpleFunctionDescriptor), sw)
|
||||
return mapReturnType(getOrCreateJvmSuspendFunctionView(descriptor as SimpleFunctionDescriptor, isReleaseCoroutines), sw)
|
||||
}
|
||||
|
||||
if (hasVoidReturnType(descriptor)) {
|
||||
@@ -804,7 +804,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
if (f.isSuspendFunctionNotSuspensionView()) {
|
||||
return mapSignature(getOrCreateJvmSuspendFunctionView(f), kind, skipGenericSignature)
|
||||
return mapSignature(getOrCreateJvmSuspendFunctionView(f, isReleaseCoroutines), kind, skipGenericSignature)
|
||||
}
|
||||
|
||||
if (isDeclarationOfBigArityFunctionInvoke(f) || isDeclarationOfBigArityCreateCoroutineMethod(f)) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
|
||||
import org.jetbrains.kotlin.ideaExt.idea
|
||||
import java.io.File
|
||||
|
||||
@@ -9,6 +10,8 @@ plugins {
|
||||
val compilerModules: Array<String> by rootProject.extra
|
||||
val otherCompilerModules = compilerModules.filter { it != path }
|
||||
|
||||
val tasksWithWarnings: List<String> by rootProject.extra
|
||||
|
||||
val antLauncherJar by configurations.creating
|
||||
|
||||
dependencies {
|
||||
@@ -65,6 +68,18 @@ if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!kotlinBuildProperties.disableWerror) {
|
||||
allprojects {
|
||||
tasks.withType<KotlinCompile<*>> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectTest(parallel = true) {
|
||||
dependsOn(":dist")
|
||||
|
||||
|
||||
@@ -381,12 +381,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
)
|
||||
var unrestrictedBuilderInference: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xself-upper-bound-inference",
|
||||
description = "Support inferring type arguments based on only self upper bounds of the corresponding type parameters"
|
||||
)
|
||||
var selfUpperBoundInference: Boolean by FreezableVar(false)
|
||||
|
||||
open fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
|
||||
return HashMap<AnalysisFlag<*>, Any>().apply {
|
||||
put(AnalysisFlags.skipMetadataVersionCheck, skipMetadataVersionCheck)
|
||||
@@ -429,10 +423,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
put(LanguageFeature.UnrestrictedBuilderInference, LanguageFeature.State.ENABLED)
|
||||
}
|
||||
|
||||
if (selfUpperBoundInference) {
|
||||
put(LanguageFeature.TypeInferenceOnCallsWithSelfTypes, LanguageFeature.State.ENABLED)
|
||||
}
|
||||
|
||||
if (newInference) {
|
||||
put(LanguageFeature.NewInference, LanguageFeature.State.ENABLED)
|
||||
put(LanguageFeature.SamConversionPerArgument, LanguageFeature.State.ENABLED)
|
||||
|
||||
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.ir.backend.js.ic.buildCache
|
||||
import org.jetbrains.kotlin.ir.backend.js.ic.checkCaches
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
||||
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
|
||||
import org.jetbrains.kotlin.js.config.*
|
||||
import org.jetbrains.kotlin.library.KLIB_FILE_EXTENSION
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
@@ -206,6 +205,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
cachePath = outputFilePath,
|
||||
project = projectJs,
|
||||
mainModule = mainModule,
|
||||
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
||||
configuration = config.configuration,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
@@ -219,37 +219,18 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
return OK
|
||||
}
|
||||
|
||||
// Run analysis if main module is sources
|
||||
lateinit var sourceModule: ModulesStructure
|
||||
if (arguments.includes == null) {
|
||||
do {
|
||||
sourceModule = prepareAnalyzedSourceModule(
|
||||
projectJs,
|
||||
environmentForJS.getSourceFiles(),
|
||||
configurationJs,
|
||||
libraries,
|
||||
friendLibraries,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
icUseGlobalSignatures = icCaches.isNotEmpty(),
|
||||
icUseStdlibCache = icCaches.isNotEmpty(),
|
||||
icCache = if (icCaches.isNotEmpty()) checkCaches(libraries, icCaches, skipLib = arguments.includes).data else emptyMap()
|
||||
)
|
||||
val result = sourceModule.jsFrontEndResult.jsAnalysisResult
|
||||
if (result is JsAnalysisResult.RetryWithAdditionalRoots) {
|
||||
environmentForJS.addKotlinSourceRoots(result.additionalKotlinRoots)
|
||||
}
|
||||
} while (result is JsAnalysisResult.RetryWithAdditionalRoots)
|
||||
if (!sourceModule.jsFrontEndResult.jsAnalysisResult.shouldGenerateCode)
|
||||
return OK
|
||||
}
|
||||
|
||||
if (arguments.irProduceKlibDir || arguments.irProduceKlibFile) {
|
||||
if (arguments.irProduceKlibFile) {
|
||||
require(outputFile.extension == KLIB_FILE_EXTENSION) { "Please set up .klib file as output" }
|
||||
}
|
||||
|
||||
generateKLib(
|
||||
sourceModule,
|
||||
project = config.project,
|
||||
files = sourcesFiles,
|
||||
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
||||
configuration = config.configuration,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
irFactory = PersistentIrFactory(), // TODO IrFactoryImpl?
|
||||
outputKlibPath = outputFile.path,
|
||||
nopack = arguments.irProduceKlibDir,
|
||||
@@ -265,33 +246,28 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
|
||||
val includes = arguments.includes
|
||||
|
||||
val module = if (includes != null) {
|
||||
val mainModule = if (includes != null) {
|
||||
if (sourcesFiles.isNotEmpty()) {
|
||||
messageCollector.report(ERROR, "Source files are not supported when -Xinclude is present")
|
||||
}
|
||||
val includesPath = File(includes).canonicalPath
|
||||
val mainLibPath = libraries.find { File(it).canonicalPath == includesPath }
|
||||
?: error("No library with name $includes ($includesPath) found")
|
||||
val kLib = MainModule.Klib(mainLibPath)
|
||||
ModulesStructure(
|
||||
projectJs,
|
||||
kLib,
|
||||
configurationJs,
|
||||
libraries,
|
||||
friendLibraries,
|
||||
icUseGlobalSignatures = icCaches.isNotEmpty(),
|
||||
icUseStdlibCache = icCaches.isNotEmpty(),
|
||||
icCache = if (icCaches.isNotEmpty()) checkCaches(libraries, icCaches, skipLib = includes).data else emptyMap()
|
||||
)
|
||||
MainModule.Klib(mainLibPath)
|
||||
} else {
|
||||
sourceModule
|
||||
MainModule.SourceFiles(sourcesFiles)
|
||||
}
|
||||
|
||||
if (arguments.wasm) {
|
||||
val res = compileWasm(
|
||||
module,
|
||||
projectJs,
|
||||
mainModule,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
config.configuration,
|
||||
PhaseConfig(wasmPhases),
|
||||
IrFactoryImpl,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
exportedDeclarations = setOf(FqName("main"))
|
||||
)
|
||||
val outputWasmFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wasm")!!
|
||||
@@ -302,7 +278,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
val runner = """
|
||||
const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary');
|
||||
const wasmModule = new WebAssembly.Module(wasmBinary);
|
||||
wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code });
|
||||
const wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code });
|
||||
wasmInstance.exports.main();
|
||||
""".trimIndent()
|
||||
|
||||
@@ -313,9 +289,14 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
val start = System.currentTimeMillis()
|
||||
|
||||
val compiledModule = compile(
|
||||
module,
|
||||
projectJs,
|
||||
mainModule,
|
||||
AnalyzerWithCompilerReport(config.configuration),
|
||||
config.configuration,
|
||||
phaseConfig,
|
||||
if (arguments.irDceDriven) PersistentIrFactory() else IrFactoryImpl,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
mainArguments = mainCallArguments,
|
||||
generateFullJs = !arguments.irDce,
|
||||
generateDceJs = arguments.irDce,
|
||||
@@ -335,6 +316,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
messageCollector
|
||||
),
|
||||
lowerPerModule = icCaches.isNotEmpty(),
|
||||
useStdlibCache = icCaches.isNotEmpty(),
|
||||
icCache = if (icCaches.isNotEmpty()) checkCaches(libraries, icCaches, skipLib = includes).data else emptyMap(),
|
||||
)
|
||||
|
||||
messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms")
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.load.java.components.JavaDeprecationSettings
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.deprecation.CoroutineCompatibilitySupport
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -83,6 +84,7 @@ class CliLightClassGenerationSupport(
|
||||
get() = DeprecationResolver(
|
||||
LockBasedStorageManager.NO_LOCKS,
|
||||
languageVersionSettings,
|
||||
CoroutineCompatibilitySupport.ENABLED,
|
||||
JavaDeprecationSettings
|
||||
)
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
class ByteArrayCharSequence(
|
||||
private val bytes: ByteArray,
|
||||
private val start: Int = 0,
|
||||
private val end: Int = bytes.size
|
||||
) : CharSequence {
|
||||
|
||||
override fun hashCode(): Int {
|
||||
error("Do not try computing hashCode ByteArrayCharSequence")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
error("Do not try comparing ByteArrayCharSequence")
|
||||
}
|
||||
|
||||
override val length get() = end - start
|
||||
|
||||
override fun get(index: Int): Char = bytes[index + start].toChar()
|
||||
|
||||
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
||||
if (startIndex == 0 && endIndex == length) return this
|
||||
return ByteArrayCharSequence(bytes, start + startIndex, start + endIndex)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val chars = CharArray(length)
|
||||
|
||||
for (i in 0 until length) {
|
||||
chars[i] = bytes[i + start].toChar()
|
||||
}
|
||||
|
||||
return String(chars)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
@@ -15,17 +14,15 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
|
||||
private val myRoot: VirtualFile?
|
||||
internal val file = File(path)
|
||||
|
||||
private val ourEntryMap: Map<String, ZipEntryDescription>
|
||||
private val cachedManifest: ByteArray?
|
||||
|
||||
init {
|
||||
val entries: List<ZipEntryDescription>
|
||||
RandomAccessFile(file, "r").use { randomAccessFile ->
|
||||
val mappedByteBuffer = randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length())
|
||||
try {
|
||||
entries = mappedByteBuffer.parseCentralDirectory()
|
||||
cachedManifest =
|
||||
entries.singleOrNull { StringUtil.equals(MANIFEST_PATH, it.relativePath) }
|
||||
?.let(mappedByteBuffer::contentsToByteArray)
|
||||
ourEntryMap = mappedByteBuffer.parseCentralDirectory().associateBy { it.relativePath }
|
||||
cachedManifest = ourEntryMap[MANIFEST_PATH]?.let(mappedByteBuffer::contentsToByteArray)
|
||||
} finally {
|
||||
with(fileSystem) {
|
||||
mappedByteBuffer.unmapBuffer()
|
||||
@@ -33,14 +30,12 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
|
||||
}
|
||||
}
|
||||
|
||||
myRoot = FastJarVirtualFile(this, "", -1, parent = null, entryDescription = null)
|
||||
myRoot = FastJarVirtualFile(this, "", -1, null)
|
||||
|
||||
// ByteArrayCharSequence should not be used instead of String
|
||||
// because the former class does not support equals/hashCode properly
|
||||
val filesByRelativePath = HashMap<String, FastJarVirtualFile>(entries.size)
|
||||
val filesByRelativePath = HashMap<String, FastJarVirtualFile>(ourEntryMap.size)
|
||||
filesByRelativePath[""] = myRoot
|
||||
|
||||
for (entryDescription in entries) {
|
||||
for (entryDescription in ourEntryMap.values) {
|
||||
if (!entryDescription.isDirectory) {
|
||||
createFile(entryDescription, filesByRelativePath)
|
||||
} else {
|
||||
@@ -64,21 +59,20 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
|
||||
return FastJarVirtualFile(
|
||||
this, shortName,
|
||||
if (entry.isDirectory) -1 else entry.uncompressedSize,
|
||||
parentFile,
|
||||
entry,
|
||||
parentFile
|
||||
)
|
||||
}
|
||||
|
||||
private fun getOrCreateDirectory(entryName: CharSequence, directories: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
|
||||
return directories.getOrPut(entryName.toString()) {
|
||||
private fun getOrCreateDirectory(entryName: String, directories: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
|
||||
return directories.getOrPut(entryName) {
|
||||
val (parentPath, shortName) = entryName.splitPath()
|
||||
val parentFile = getOrCreateDirectory(parentPath, directories)
|
||||
|
||||
FastJarVirtualFile(this, shortName, -1, parentFile, entryDescription = null)
|
||||
FastJarVirtualFile(this, shortName, -1, parentFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CharSequence.splitPath(): Pair<CharSequence, CharSequence> {
|
||||
private fun String.splitPath(): Pair<String, String> {
|
||||
var slashIndex = this.length - 1
|
||||
|
||||
while (slashIndex >= 0 && this[slashIndex] != '/') {
|
||||
@@ -86,16 +80,16 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
|
||||
}
|
||||
|
||||
if (slashIndex == -1) return Pair("", this)
|
||||
return Pair(subSequence(0, slashIndex), subSequence(slashIndex + 1, this.length))
|
||||
return Pair(substring(0, slashIndex), substring(slashIndex + 1))
|
||||
}
|
||||
|
||||
fun findFileByPath(pathInJar: String): VirtualFile? {
|
||||
return myRoot?.findFileByRelativePath(pathInJar)
|
||||
}
|
||||
|
||||
fun contentsToByteArray(zipEntryDescription: ZipEntryDescription): ByteArray {
|
||||
val relativePath = zipEntryDescription.relativePath
|
||||
if (StringUtil.equals(relativePath, MANIFEST_PATH)) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath")
|
||||
fun contentsToByteArray(relativePath: String): ByteArray {
|
||||
if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath")
|
||||
val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath")
|
||||
return fileSystem.cachedOpenFileHandles[file].use {
|
||||
synchronized(it) {
|
||||
it.get().second.contentsToByteArray(zipEntryDescription)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler.jarfs
|
||||
|
||||
import com.intellij.openapi.util.Couple
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
@@ -13,18 +14,17 @@ import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
internal class FastJarVirtualFile(
|
||||
private val handler: FastJarHandler,
|
||||
private val name: CharSequence,
|
||||
private val length: Int,
|
||||
private val parent: FastJarVirtualFile?,
|
||||
private val entryDescription: ZipEntryDescription?,
|
||||
private val myHandler: FastJarHandler,
|
||||
private val myName: CharSequence,
|
||||
private val myLength: Int,
|
||||
private val myParent: FastJarVirtualFile?
|
||||
) : VirtualFile() {
|
||||
|
||||
private var myChildrenArray = EMPTY_ARRAY
|
||||
private val myChildrenList: MutableList<VirtualFile> = mutableListOf()
|
||||
|
||||
init {
|
||||
parent?.myChildrenList?.add(this)
|
||||
myParent?.myChildrenList?.add(this)
|
||||
}
|
||||
|
||||
fun initChildrenArrayFromList() {
|
||||
@@ -33,28 +33,28 @@ internal class FastJarVirtualFile(
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return name.toString()
|
||||
return myName.toString()
|
||||
}
|
||||
|
||||
override fun getNameSequence(): CharSequence {
|
||||
return name
|
||||
return myName
|
||||
}
|
||||
|
||||
override fun getFileSystem(): VirtualFileSystem {
|
||||
return handler.fileSystem
|
||||
return myHandler.fileSystem
|
||||
}
|
||||
|
||||
override fun getPath(): String {
|
||||
if (parent == null) {
|
||||
return FileUtil.toSystemIndependentName(handler.file.path) + "!/"
|
||||
if (myParent == null) {
|
||||
return FileUtil.toSystemIndependentName(myHandler.file.path) + "!/"
|
||||
}
|
||||
val parentPath = parent.path
|
||||
val answer = StringBuilder(parentPath.length + 1 + name.length)
|
||||
val parentPath = myParent.path
|
||||
val answer = StringBuilder(parentPath.length + 1 + myName.length)
|
||||
answer.append(parentPath)
|
||||
if (answer[answer.length - 1] != '/') {
|
||||
answer.append('/')
|
||||
}
|
||||
answer.append(name)
|
||||
answer.append(myName)
|
||||
return answer.toString()
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ internal class FastJarVirtualFile(
|
||||
}
|
||||
|
||||
override fun isDirectory(): Boolean {
|
||||
return length < 0
|
||||
return myLength < 0
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
@@ -71,7 +71,7 @@ internal class FastJarVirtualFile(
|
||||
}
|
||||
|
||||
override fun getParent(): VirtualFile? {
|
||||
return parent
|
||||
return myParent
|
||||
}
|
||||
|
||||
override fun getChildren(): Array<VirtualFile> {
|
||||
@@ -85,13 +85,15 @@ internal class FastJarVirtualFile(
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentsToByteArray(): ByteArray {
|
||||
if (entryDescription == null) return EMPTY_BYTE_ARRAY
|
||||
return handler.contentsToByteArray(entryDescription)
|
||||
val pair: Couple<String> = FastJarFileSystem.splitPath(
|
||||
path
|
||||
)
|
||||
return myHandler.contentsToByteArray(pair.second)
|
||||
}
|
||||
|
||||
override fun getTimeStamp(): Long = 0
|
||||
|
||||
override fun getLength(): Long = length.toLong()
|
||||
override fun getLength(): Long = myLength.toLong()
|
||||
|
||||
override fun refresh(asynchronous: Boolean, recursive: Boolean, postRunnable: Runnable?) {}
|
||||
|
||||
@@ -104,5 +106,3 @@ internal class FastJarVirtualFile(
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
private val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
|
||||
@@ -12,12 +12,12 @@ import java.util.zip.Inflater
|
||||
|
||||
|
||||
class ZipEntryDescription(
|
||||
val relativePath: CharSequence,
|
||||
val relativePath: String,
|
||||
val compressedSize: Int,
|
||||
val uncompressedSize: Int,
|
||||
val offsetInFile: Int,
|
||||
val compressionKind: CompressionKind,
|
||||
val fileNameSize: Int,
|
||||
val fileNameSize: Int
|
||||
) {
|
||||
|
||||
enum class CompressionKind {
|
||||
@@ -62,11 +62,9 @@ fun MappedByteBuffer.contentsToByteArray(
|
||||
fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
|
||||
var endOfCentralDirectoryOffset = capacity() - END_OF_CENTRAL_DIR_SIZE
|
||||
while (endOfCentralDirectoryOffset >= 0) {
|
||||
val endOfCentralDirectoryOffset = (capacity() - END_OF_CENTRAL_DIR_SIZE downTo 0).first { offset ->
|
||||
// header of "End of central directory"
|
||||
if (getInt(endOfCentralDirectoryOffset) == 0x06054b50) break
|
||||
endOfCentralDirectoryOffset--
|
||||
getInt(offset) == 0x06054b50
|
||||
}
|
||||
|
||||
val entriesNumber = getUnsignedShort(endOfCentralDirectoryOffset + 10)
|
||||
@@ -101,7 +99,7 @@ fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
||||
|
||||
val name =
|
||||
if (bytesForName.all { it >= 0 })
|
||||
ByteArrayCharSequence(bytesForName)
|
||||
String(bytesForName.asASCICharArray())
|
||||
else
|
||||
String(bytesForName, Charsets.UTF_8)
|
||||
|
||||
@@ -126,4 +124,6 @@ fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ByteArray.asASCICharArray(): CharArray = CharArray(size) { this@asASCICharArray[it].toChar() }
|
||||
|
||||
private fun ByteBuffer.getUnsignedShort(offset: Int): Int = java.lang.Short.toUnsignedInt(getShort(offset))
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.config
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
fun LanguageVersionSettings.coroutinesPackageFqName(): FqName {
|
||||
return coroutinesPackageFqName(isReleaseCoroutines())
|
||||
}
|
||||
|
||||
fun LanguageVersionSettings.isReleaseCoroutines() = supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
|
||||
private fun coroutinesPackageFqName(isReleaseCoroutines: Boolean): FqName {
|
||||
return if (isReleaseCoroutines)
|
||||
StandardNames.COROUTINES_PACKAGE_FQ_NAME_RELEASE
|
||||
else
|
||||
StandardNames.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL
|
||||
}
|
||||
|
||||
fun LanguageVersionSettings.coroutinesIntrinsicsPackageFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("intrinsics"))
|
||||
|
||||
fun LanguageVersionSettings.continuationInterfaceFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("Continuation"))
|
||||
|
||||
fun LanguageVersionSettings.restrictsSuspensionFqName() =
|
||||
coroutinesPackageFqName().child(Name.identifier("RestrictsSuspension"))
|
||||
|
||||
fun FqName.isBuiltInCoroutineContext(languageVersionSettings: LanguageVersionSettings) =
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines))
|
||||
this == StandardNames.COROUTINES_PACKAGE_FQ_NAME_RELEASE.child(Name.identifier("coroutineContext"))
|
||||
else
|
||||
this == StandardNames.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.child(Name.identifier("coroutineContext")) ||
|
||||
this == StandardNames.COROUTINES_INTRINSICS_PACKAGE_FQ_NAME_EXPERIMENTAL.child(Name.identifier("coroutineContext"))
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.daemon.client.experimental
|
||||
|
||||
import io.ktor.network.sockets.*
|
||||
import io.ktor.network.sockets.Socket
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
@@ -438,7 +438,6 @@ class KotlinCompilerClient : KotlinCompilerDaemonClient {
|
||||
.thenBy(FileAgeComparator()) { it.runFile }
|
||||
val optsCopy = daemonJVMOptions.copy()
|
||||
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
|
||||
@Suppress("DEPRECATION") // TODO: replace with maxWithOrNull as soon as minimal version of Gradle that we support has Kotlin 1.4+.
|
||||
aliveWithMetadata.maxWith(comparator)
|
||||
?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }
|
||||
?.let {
|
||||
|
||||
@@ -359,7 +359,6 @@ object KotlinCompilerClient {
|
||||
.thenBy(FileAgeComparator()) { it.runFile }
|
||||
val optsCopy = daemonJVMOptions.copy()
|
||||
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
|
||||
@Suppress("DEPRECATION") // TODO: replace with maxWithOrNull as soon as minimal version of Gradle that we support has Kotlin 1.4+.
|
||||
return aliveWithMetadata.maxWith(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let {
|
||||
Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions))
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalPathApi::class, DelicateCoroutinesApi::class)
|
||||
@file:OptIn(ExperimentalPathApi::class)
|
||||
|
||||
package org.jetbrains.kotlin.daemon.experimental.integration
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.daemon.experimental.unit
|
||||
|
||||
import io.ktor.network.sockets.aSocket
|
||||
import io.ktor.util.*
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -32,7 +31,6 @@ class TestServer(val serverPort: Int = 6999) {
|
||||
private val serverSocket = aSocket(selectorMgr).tcp().bind(InetSocketAddress(serverPort))
|
||||
private val log = Logger.getLogger("TestServer")
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun awaitClient() = GlobalScope.async {
|
||||
log.info("accepting clientSocket...")
|
||||
val client = serverSocket.accept()
|
||||
@@ -63,7 +61,7 @@ class ClientSerializationTest : KotlinIntegrationTestBase() {
|
||||
}
|
||||
}
|
||||
log.info("printed")
|
||||
var client2: T?
|
||||
var client2: T? = null
|
||||
var connected = false
|
||||
runBlocking {
|
||||
val clientAwait = testServer.awaitClient()
|
||||
|
||||
@@ -621,11 +621,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/arguments/namedArrayInAnnotation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClassInAnnotationArgument.kt")
|
||||
public void testNestedClassInAnnotationArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/arguments/nestedClassInAnnotationArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noParameterForName.kt")
|
||||
public void testNoParameterForName() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/arguments/noParameterForName.kt");
|
||||
@@ -2701,11 +2696,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/overrides/protobufExt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sameValueParametersDifferentReceiver.kt")
|
||||
public void testSameValueParametersDifferentReceiver() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/overrides/sameValueParametersDifferentReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/overrides/simple.kt");
|
||||
@@ -3069,11 +3059,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/equalsAndIdentity.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("incorrectSmartcastToNothing.kt")
|
||||
public void testIncorrectSmartcastToNothing() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/incorrectSmartcastToNothing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt10240.kt")
|
||||
public void testKt10240() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/kt10240.kt");
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
FILE: nestedClassInAnnotationArgument.kt
|
||||
public final annotation class Ann : R|kotlin/Annotation| {
|
||||
public constructor(kClass: R|kotlin/reflect/KClass<*>|): R|Ann| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val kClass: R|kotlin/reflect/KClass<*>| = R|<local>/kClass|
|
||||
public get(): R|kotlin/reflect/KClass<*>|
|
||||
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
@R|Ann|(<getClass>(Q|A.EmptyList|)) public final fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
public final object EmptyList : R|kotlin/Any| {
|
||||
private constructor(): R|A.EmptyList| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
annotation class Ann(val kClass: KClass<*>)
|
||||
|
||||
class A {
|
||||
@Ann(EmptyList::class)
|
||||
fun foo() {}
|
||||
|
||||
object EmptyList
|
||||
}
|
||||
@@ -7,7 +7,7 @@ FILE: typeAliasWithNotNullBound.kt
|
||||
}
|
||||
public final typealias MyAlias = R|Inv<out kotlin/CharSequence>|
|
||||
public final fun foo(p: R|MyAlias|): R|kotlin/Unit| {
|
||||
R|/bar|<R|CapturedType(out kotlin/CharSequence)|>(R|<local>/p|).R|kotlin/CharSequence.length|
|
||||
R|/bar|<R|kotlin/CharSequence|>(R|<local>/p|).R|kotlin/CharSequence.length|
|
||||
}
|
||||
public final fun <T : R|kotlin/Any|> bar(x: R|Inv<T>|): R|T| {
|
||||
^bar R|kotlin/TODO|()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// !DUMP_CFG
|
||||
<!NOTHING_TO_INLINE!>inline<!> fun foo(vararg x: Any) {}
|
||||
inline fun foo(vararg x: Any) {}
|
||||
|
||||
fun test(a: Any, b: Any, c: Any) {
|
||||
foo(a, { "" }, b)
|
||||
|
||||
@@ -23,16 +23,6 @@ FILE: const.kt
|
||||
public get(): R|kotlin/Int|
|
||||
public final const val l: R|kotlin/Int| = R|/k|
|
||||
public get(): R|kotlin/Int|
|
||||
public final const val m: R|kotlin/String| = String(123).R|kotlin/Any.toString|()
|
||||
public get(): R|kotlin/String|
|
||||
public final const val n: R|kotlin/Int| = String(456).R|kotlin/String.length|
|
||||
public get(): R|kotlin/Int|
|
||||
public final val o: R|kotlin/String| = String(789)
|
||||
public get(): R|kotlin/String|
|
||||
public final const val p: R|kotlin/String| = R|/o|.R|kotlin/Any.toString|()
|
||||
public get(): R|kotlin/String|
|
||||
public final const val q: R|kotlin/Int| = R|/o|.R|kotlin/String.length|
|
||||
public get(): R|kotlin/Int|
|
||||
public final class ForConst : R|kotlin/Any| {
|
||||
public constructor(): R|ForConst| {
|
||||
super<R|kotlin/Any|>()
|
||||
|
||||
@@ -18,11 +18,6 @@ const val i = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>ForConst.one() + "one"<!>
|
||||
const val j = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>4 * ForConst.two()<!>
|
||||
val k = 3 - ForConst.two()
|
||||
const val l = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>k<!>
|
||||
const val m = "123".toString()
|
||||
const val n = "456".length
|
||||
val o = "789"
|
||||
const val p = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>o.toString()<!>
|
||||
const val q = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>o.length<!>
|
||||
|
||||
class ForConst{
|
||||
companion object {
|
||||
|
||||
@@ -38,5 +38,5 @@ object F {
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
<!WRONG_MODIFIER_TARGET!>const<!> val a = "2"
|
||||
const val a = "2"
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ fun test() {}
|
||||
|
||||
fun test(z: Int, c: Char) {}
|
||||
|
||||
open class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>A<!> {
|
||||
<!REDECLARATION!>open class A {
|
||||
open fun rest(s: String) {}
|
||||
|
||||
open val u = 20
|
||||
}
|
||||
}<!>
|
||||
|
||||
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>A<!> {
|
||||
<!REDECLARATION!>class A {
|
||||
|
||||
}
|
||||
}<!>
|
||||
|
||||
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!> : <!FINAL_SUPERTYPE, SUPERTYPE_NOT_INITIALIZED!>A<!> {
|
||||
<!REDECLARATION!>class B : <!FINAL_SUPERTYPE, SUPERTYPE_NOT_INITIALIZED!>A<!> {
|
||||
<!CONFLICTING_OVERLOADS!><!NOTHING_TO_OVERRIDE!>override<!> fun rest(s: String)<!> {}
|
||||
|
||||
<!CONFLICTING_OVERLOADS!>fun rest(s: String)<!> {}
|
||||
@@ -26,17 +26,17 @@ class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!> : <!FINAL_SUPERTYPE, SUPERTYPE
|
||||
fun rest(l: Long) {}
|
||||
|
||||
<!NOTHING_TO_OVERRIDE!>override<!> val u = 310
|
||||
}
|
||||
}<!>
|
||||
|
||||
interface <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!>
|
||||
<!REDECLARATION!>interface B<!>
|
||||
|
||||
enum class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!>
|
||||
<!REDECLARATION!>enum class B<!>
|
||||
|
||||
val <!REDECLARATION!>u<!> = 10
|
||||
val <!REDECLARATION!>u<!> = 20
|
||||
<!REDECLARATION!>val u = 10<!>
|
||||
<!REDECLARATION!>val u = 20<!>
|
||||
|
||||
typealias <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>TA<!> = A
|
||||
typealias <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>TA<!> = B
|
||||
<!REDECLARATION!>typealias TA = A<!>
|
||||
<!REDECLARATION!>typealias TA = B<!>
|
||||
|
||||
typealias BA = A
|
||||
|
||||
@@ -52,7 +52,7 @@ fun lol(a: Array<Boolean>) {}
|
||||
|
||||
class M {
|
||||
companion <!REDECLARATION!>object<!> {}
|
||||
val <!REDECLARATION!>Companion<!> = object : Any {}
|
||||
<!REDECLARATION!>val Companion = object : Any {}<!>
|
||||
}
|
||||
|
||||
fun B.foo() {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!INCOMPATIBLE_MODIFIERS!>private<!> <!INCOMPATIBLE_MODIFIERS!>public<!> class B
|
||||
<!WRONG_MODIFIER_CONTAINING_DECLARATION!>protected<!> <!INCOMPATIBLE_MODIFIERS!>internal<!> class C
|
||||
<!INCOMPATIBLE_MODIFIERS!>protected<!> <!INCOMPATIBLE_MODIFIERS!>internal<!> class C
|
||||
|
||||
<!INCOMPATIBLE_MODIFIERS!>final<!> <!INCOMPATIBLE_MODIFIERS!>abstract<!> class D
|
||||
<!INCOMPATIBLE_MODIFIERS!>final<!> <!INCOMPATIBLE_MODIFIERS!>open<!> class E
|
||||
|
||||
@@ -6,11 +6,11 @@ infix fun Int.good(x: Int) {}
|
||||
|
||||
<!INAPPLICABLE_INFIX_MODIFIER!>infix fun baz(x: Int, y: Int) {}<!>
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>infix<!> class A
|
||||
infix class A
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>infix<!> typealias B = A
|
||||
infix typealias B = A
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>infix<!> val x = 1
|
||||
infix val x = 1
|
||||
|
||||
class C {
|
||||
infix fun good(x: Int) {}
|
||||
|
||||
@@ -37,7 +37,7 @@ class F(var a: Int, b: Int, closure: () -> Unit, instance: F?) {
|
||||
val a = 10
|
||||
<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>
|
||||
test(<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>)
|
||||
<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>.a = 20
|
||||
<!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>.<!UNRESOLVED_REFERENCE!>a<!> = 20
|
||||
}, <!INSTANCE_ACCESS_BEFORE_SUPER_CALL!>this<!>) {
|
||||
this.a = 30
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ FILE: notASupertype.kt
|
||||
}
|
||||
|
||||
public final fun g(): R|kotlin/Unit| {
|
||||
this@R|/B|.super<<ERROR TYPE REF: Not a super type>>.<Unresolved name: f>#()
|
||||
this@R|/B|.super<R|kotlin/String|>.<Unresolved name: f>#()
|
||||
this@R|/B|.super<R|A|>.R|/A.f|()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ open class A {
|
||||
|
||||
class B : <!SUPERTYPE_NOT_INITIALIZED!>A<!> {
|
||||
fun g() {
|
||||
super<<!NOT_A_SUPERTYPE!>String<!>>.f()
|
||||
<!NOT_A_SUPERTYPE!>super<String><!>.<!UNRESOLVED_REFERENCE!>f<!>()
|
||||
super<A>.f()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
sealed class A
|
||||
|
||||
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!> : A()
|
||||
<!REDECLARATION!>class B : A()<!>
|
||||
|
||||
interface C : <!INTERFACE_WITH_SUPERCLASS!>A<!>
|
||||
|
||||
@@ -30,7 +30,7 @@ sealed class P {
|
||||
|
||||
class K : P()
|
||||
|
||||
object <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>B<!> {
|
||||
<!REDECLARATION!>object B<!> {
|
||||
class I : P()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
FILE: superNotAvailable.kt
|
||||
public final fun R|kotlin/String|.f(): R|kotlin/Unit| {
|
||||
<Super not available>#.<Unresolved name: compareTo>#(String())
|
||||
<Super not available>#.<Unresolved name: compareTo>#(String())
|
||||
super<<ERROR TYPE REF: No super type>>@f#.<Unresolved name: compareTo>#(String())
|
||||
super<<ERROR TYPE REF: No super type>>.<Unresolved name: compareTo>#(String())
|
||||
}
|
||||
public final fun foo(): R|kotlin/Unit| {
|
||||
<Super not allowed>#
|
||||
<Super not available>#.<Unresolved name: foo>#()
|
||||
<Super not available>#.<Unresolved name: foo>#()
|
||||
super<<ERROR TYPE REF: No super type>>
|
||||
super<<ERROR TYPE REF: No super type>>.<Unresolved name: foo>#()
|
||||
super<R|kotlin/Nothing|>.<Unresolved name: foo>#()
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
fun String.f() {
|
||||
<!SUPER_NOT_AVAILABLE!>super@f<!>.compareTo("")
|
||||
<!SUPER_NOT_AVAILABLE!>super<!>.compareTo("")
|
||||
<!SUPER_NOT_AVAILABLE!>super@f<!>.<!UNRESOLVED_REFERENCE!>compareTo<!>("")
|
||||
<!SUPER_NOT_AVAILABLE!>super<!>.<!UNRESOLVED_REFERENCE!>compareTo<!>("")
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
<!SUPER_IS_NOT_AN_EXPRESSION!>super<!>
|
||||
<!SUPER_NOT_AVAILABLE!>super<!>.foo()
|
||||
<!SUPER_NOT_AVAILABLE!>super<Nothing><!>.foo()
|
||||
<!SUPER_NOT_AVAILABLE!>super<!>
|
||||
<!SUPER_NOT_AVAILABLE!>super<!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||
<!SUPER_NOT_AVAILABLE!>super<Nothing><!>.<!UNRESOLVED_REFERENCE!>foo<!>()
|
||||
}
|
||||
|
||||
class A {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
fun x() {}
|
||||
|
||||
operator fun Int.invoke(): Foo = this<!UNRESOLVED_LABEL!>@Foo<!>
|
||||
operator fun Int.invoke(): Foo = <!UNRESOLVED_LABEL!>this@Foo<!>
|
||||
|
||||
class Foo {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
object <!REDECLARATION!>A<!>
|
||||
<!REDECLARATION!>object A<!>
|
||||
|
||||
val <!REDECLARATION!>A<!> = 10
|
||||
<!REDECLARATION!>val A = 10<!>
|
||||
|
||||
|
||||
fun foo() = A
|
||||
|
||||
@@ -137,7 +137,7 @@ fun main(args: Array<String?>) {
|
||||
<!ASSIGNED_VALUE_IS_NEVER_READ!>a<!> = args[0]
|
||||
} else {
|
||||
a = args.toString()
|
||||
if (<!SENSELESS_COMPARISON!>a != null<!> && a.equals("cde")) return
|
||||
if (a != null && a.equals("cde")) return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Interface {
|
||||
// Redundant
|
||||
<!REDUNDANT_MODALITY_MODIFIER!>abstract<!> fun foo()
|
||||
// error
|
||||
<!PRIVATE_FUNCTION_WITH_NO_BODY!>private<!> <!WRONG_MODIFIER_CONTAINING_DECLARATION!>final<!> fun bar()
|
||||
<!PRIVATE_FUNCTION_WITH_NO_BODY!>private<!> final fun bar()
|
||||
|
||||
<!REDUNDANT_MODALITY_MODIFIER!>open<!> fun goo() {}
|
||||
<!REDUNDANT_MODALITY_MODIFIER!>abstract<!> fun tar()
|
||||
@@ -63,7 +63,7 @@ interface Derived : Interface {
|
||||
// Redundant
|
||||
override <!REDUNDANT_MODALITY_MODIFIER!>open<!> fun foo() {}
|
||||
// error
|
||||
<!WRONG_MODIFIER_CONTAINING_DECLARATION!>final<!> class Nested
|
||||
final class Nested
|
||||
}
|
||||
// Derived abstract class
|
||||
abstract class AbstractDerived1(override final val gav: Int) : Interface {
|
||||
@@ -82,4 +82,4 @@ abstract interface AbstractInterface
|
||||
// Redundant final object
|
||||
<!REDUNDANT_MODALITY_MODIFIER!>final<!> object FinalObject
|
||||
// Open interface
|
||||
<!REDUNDANT_MODIFIER_FOR_TARGET!>open<!> interface OpenInterface
|
||||
open interface OpenInterface
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
annotation class Ann
|
||||
|
||||
var <!REDECLARATION!>x<!>: Int
|
||||
<!REDECLARATION!>var x: Int
|
||||
get() = 1
|
||||
set(@Ann <!WRONG_MODIFIER_TARGET!>private<!> x) { }
|
||||
set(@Ann private x) { }<!>
|
||||
|
||||
|
||||
var <!REDECLARATION!>x<!>: String = ""
|
||||
<!REDECLARATION!>var x: String = ""
|
||||
set(param: <!REDUNDANT_SETTER_PARAMETER_TYPE!>String<!>) {
|
||||
field = "$param "
|
||||
}
|
||||
}<!>
|
||||
|
||||
class My {
|
||||
var y: Int = 1
|
||||
@@ -25,4 +25,4 @@ class My {
|
||||
set(param) {
|
||||
field = !param
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
fun f() {
|
||||
<!REDUNDANT_VISIBILITY_MODIFIER, WRONG_MODIFIER_TARGET!>public<!> <!CAN_BE_VAL!>var<!> <!UNUSED_VARIABLE!>baz<!> = 0
|
||||
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> <!CAN_BE_VAL!>var<!> <!UNUSED_VARIABLE!>baz<!> = 0
|
||||
class LocalClass {
|
||||
<!REDUNDANT_VISIBILITY_MODIFIER!>internal<!> var foo = 0
|
||||
}
|
||||
LocalClass().foo = 1
|
||||
}
|
||||
|
||||
internal <!NOTHING_TO_INLINE!>inline<!> fun internal() {
|
||||
internal inline fun internal() {
|
||||
f()
|
||||
}
|
||||
|
||||
class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>C<!> {
|
||||
<!REDECLARATION!>class C {
|
||||
internal val z = object {
|
||||
fun foo() = 13
|
||||
}
|
||||
}
|
||||
}<!>
|
||||
|
||||
class Foo2<
|
||||
T1,
|
||||
@@ -26,12 +26,12 @@ class Foo2<
|
||||
internal inner class B<T,T2,>
|
||||
}
|
||||
|
||||
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> class <!PACKAGE_OR_CLASSIFIER_REDECLARATION!>C<!> {
|
||||
<!REDECLARATION!><!REDUNDANT_VISIBILITY_MODIFIER!>public<!> class C {
|
||||
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> val foo: Int = 0
|
||||
|
||||
<!REDUNDANT_VISIBILITY_MODIFIER!>public<!> fun bar() {}
|
||||
|
||||
}
|
||||
}<!>
|
||||
|
||||
open class D {
|
||||
protected open fun willRemainProtected() {
|
||||
|
||||
@@ -15,7 +15,7 @@ open class B {
|
||||
override fun foo() {
|
||||
super.foo()
|
||||
|
||||
<!AMBIGUOUS_SUPER!>super<!>.bar() // should be ambiguity (NB: really we should have overridden bar in C)
|
||||
super.<!UNRESOLVED_REFERENCE!>bar<!>() // should be ambiguity (NB: really we should have overridden bar in C)
|
||||
|
||||
super.baz() // Ok
|
||||
baz() // Ok
|
||||
|
||||
@@ -4,5 +4,5 @@ FILE: main.kt
|
||||
public final fun <D : R|A|> foo(b: R|B<D>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun main(b: R|B<*>|): R|kotlin/Unit| {
|
||||
R|/foo|<R|CapturedType(*)|>(R|<local>/b|)
|
||||
R|/foo|<R|ft<A, A?>|>(R|<local>/b|)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ FILE: kt40131.kt
|
||||
}
|
||||
public final val <T : R|kotlin/reflect/KClass<*>|> R|T|.myJava1: R|java/lang/Class<*>|
|
||||
public get(): R|java/lang/Class<*>| {
|
||||
^ this@R|/myJava1|.R|/javaImpl|<R|CapturedType(*)|>
|
||||
^ this@R|/myJava1|.R|/javaImpl|<R|kotlin/Any|>
|
||||
}
|
||||
public final val <E : R|kotlin/Any|, T : R|kotlin/reflect/KClass<E>|> R|T|.myJava2: R|java/lang/Class<E>|
|
||||
public get(): R|java/lang/Class<E>| {
|
||||
|
||||
@@ -15,19 +15,19 @@ FILE: receiverWithCapturedType.kt
|
||||
^updateD R|<local>/d|
|
||||
}
|
||||
public final fun test_1_1(resolvedCall: R|ResolvedCall<out CallableDescriptor>|): R|kotlin/Unit| {
|
||||
R|<local>/resolvedCall|.R|/getParameterForArgument|<R|CapturedType(out CallableDescriptor)|>()
|
||||
R|<local>/resolvedCall|.R|/getParameterForArgument|<R|CallableDescriptor|>()
|
||||
}
|
||||
public final fun test_1_2(resolvedCall: R|ResolvedCall<in CallableDescriptor>|): R|kotlin/Unit| {
|
||||
R|<local>/resolvedCall|.R|/getParameterForArgument|<R|CapturedType(in CallableDescriptor)|>()
|
||||
R|<local>/resolvedCall|.R|/getParameterForArgument|<R|CallableDescriptor|>()
|
||||
}
|
||||
public final fun test_1_3(resolvedCall: R|ResolvedCall<CallableDescriptor>|): R|kotlin/Unit| {
|
||||
R|<local>/resolvedCall|.R|/getParameterForArgument|<R|CallableDescriptor|>()
|
||||
}
|
||||
public final fun test_2_1(resolvedCall: R|ResolvedCall<out CallableDescriptor>|, d: R|CallableDescriptor|): R|kotlin/Unit| {
|
||||
lval x: R|CallableDescriptor| = R|<local>/resolvedCall|.<Inapplicable(INAPPLICABLE): /updateD>#<R|CapturedType(out CallableDescriptor)|>(R|<local>/d|)
|
||||
lval x: R|CallableDescriptor| = R|<local>/resolvedCall|.<Inapplicable(INAPPLICABLE): /updateD>#<R|CallableDescriptor|>(R|<local>/d|)
|
||||
}
|
||||
public final fun test_2_2(resolvedCall: R|ResolvedCall<in CallableDescriptor>|, d: R|CallableDescriptor|): R|kotlin/Unit| {
|
||||
lval x: R|CallableDescriptor| = R|<local>/resolvedCall|.R|/updateD|<R|CapturedType(in CallableDescriptor)|>(R|<local>/d|)
|
||||
lval x: R|CallableDescriptor| = R|<local>/resolvedCall|.R|/updateD|<R|CallableDescriptor|>(R|<local>/d|)
|
||||
}
|
||||
public final fun test_2_3(resolvedCall: R|ResolvedCall<CallableDescriptor>|, d: R|CallableDescriptor|): R|kotlin/Unit| {
|
||||
lval x: R|CallableDescriptor| = R|<local>/resolvedCall|.R|/updateD|<R|CallableDescriptor|>(R|<local>/d|)
|
||||
|
||||
@@ -19,10 +19,10 @@ FILE: simpleCapturedTypes.kt
|
||||
^ this@R|/idP|
|
||||
}
|
||||
private final fun getSetterInfos(kc: R|KC<out Ann>|): R|kotlin/Unit| {
|
||||
R|/id|<R|CapturedType(out Ann)|>(R|<local>/kc|).R|SubstitutionOverride</KC.x: R|CapturedType(out Ann)|>|.R|/Ann.foo|()
|
||||
R|<local>/kc|.R|/idR|<R|CapturedType(out Ann)|>().R|SubstitutionOverride</KC.x: R|CapturedType(out Ann)|>|.R|/Ann.foo|()
|
||||
R|<local>/kc|.R|/idP|<R|CapturedType(out Ann)|>.R|SubstitutionOverride</KC.x: R|CapturedType(out Ann)|>|.R|/Ann.foo|()
|
||||
lval x1: R|KC<out Ann>| = R|/id|<R|CapturedType(out Ann)|>(R|<local>/kc|)
|
||||
lval x2: R|KC<out Ann>| = R|<local>/kc|.R|/idR|<R|CapturedType(out Ann)|>()
|
||||
lval x3: R|KC<out Ann>| = R|<local>/kc|.R|/idP|<R|CapturedType(out Ann)|>
|
||||
R|/id|<R|Ann|>(R|<local>/kc|).R|SubstitutionOverride</KC.x: R|CapturedType(out Ann)|>|.R|/Ann.foo|()
|
||||
R|<local>/kc|.R|/idR|<R|Ann|>().R|SubstitutionOverride</KC.x: R|CapturedType(out Ann)|>|.R|/Ann.foo|()
|
||||
R|<local>/kc|.R|/idP|<R|Ann|>.R|SubstitutionOverride</KC.x: R|CapturedType(out Ann)|>|.R|/Ann.foo|()
|
||||
lval x1: R|KC<out Ann>| = R|/id|<R|Ann|>(R|<local>/kc|)
|
||||
lval x2: R|KC<out Ann>| = R|<local>/kc|.R|/idR|<R|Ann|>()
|
||||
lval x3: R|KC<out Ann>| = R|<local>/kc|.R|/idP|<R|Ann|>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class A {
|
||||
<!INLINE_CLASS_NOT_TOP_LEVEL!>inline<!> inner class B(val x: Int)
|
||||
fun foo() {
|
||||
<!INLINE_CLASS_NOT_TOP_LEVEL, WRONG_MODIFIER_TARGET!>inline<!> class C(val x: Int)
|
||||
<!INLINE_CLASS_NOT_TOP_LEVEL!>inline<!> class C(val x: Int)
|
||||
}
|
||||
inner <!INLINE_CLASS_NOT_TOP_LEVEL!>value<!> class D(val x: Int)
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
FILE: sameValueParametersDifferentReceiver.kt
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun R|kotlin/String|.foo(from: R|kotlin/String|, to: R|kotlin/String|): R|kotlin/Int| {
|
||||
^foo Int(1)
|
||||
}
|
||||
|
||||
public final fun <T> R|T|.foo(from: R|kotlin/String|, to: R|kotlin/String|): R|kotlin/Int| {
|
||||
^foo Int(1)
|
||||
}
|
||||
|
||||
}
|
||||
public final class B : R|A| {
|
||||
public constructor(): R|B| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
open class A {
|
||||
fun String.foo(from: String, to: String): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
fun <T> T.foo(from: String, to: String): Int {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
class B : A()
|
||||
@@ -40,8 +40,8 @@ FILE: referenceToExtension.kt
|
||||
}
|
||||
|
||||
public final fun test_2(): R|kotlin/Unit| {
|
||||
lval extensionValRef: R|kotlin/reflect/KProperty1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|/GenericTest.extensionVal<CapturedType(*)>|
|
||||
lval extensionFunRef: R|@ExtensionFunctionType kotlin/reflect/KFunction1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|/GenericTest.extensionFun<CapturedType(*)>|
|
||||
lval extensionValRef: R|kotlin/reflect/KProperty1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|/GenericTest.extensionVal<kotlin/Any?>|
|
||||
lval extensionFunRef: R|@ExtensionFunctionType kotlin/reflect/KFunction1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|/GenericTest.extensionFun<kotlin/Any?>|
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -702,45 +702,44 @@ digraph boundSmartcastsInBranches_kt {
|
||||
293 [label="Access variable R|<local>/x|"];
|
||||
294 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
295 [label="Access variable R|<local>/y|"];
|
||||
296 [label="Stub" style="filled" fillcolor=gray];
|
||||
297 [label="Access variable R|kotlin/String.length|" style="filled" fillcolor=gray];
|
||||
298 [label="Access variable R|<local>/z|" style="filled" fillcolor=gray];
|
||||
299 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#" style="filled" fillcolor=gray];
|
||||
300 [label="Exit block" style="filled" fillcolor=gray];
|
||||
296 [label="Access variable R|kotlin/String.length|"];
|
||||
297 [label="Access variable R|<local>/z|"];
|
||||
298 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
299 [label="Exit block"];
|
||||
}
|
||||
301 [label="Exit when branch result" style="filled" fillcolor=gray];
|
||||
302 [label="Exit when"];
|
||||
300 [label="Exit when branch result"];
|
||||
301 [label="Exit when"];
|
||||
}
|
||||
subgraph cluster_67 {
|
||||
color=blue
|
||||
303 [label="Enter when"];
|
||||
302 [label="Enter when"];
|
||||
subgraph cluster_68 {
|
||||
color=blue
|
||||
304 [label="Enter when branch condition "];
|
||||
305 [label="Access variable R|<local>/z|"];
|
||||
306 [label="Const: Null(null)"];
|
||||
307 [label="Equality operator !="];
|
||||
308 [label="Exit when branch condition"];
|
||||
303 [label="Enter when branch condition "];
|
||||
304 [label="Access variable R|<local>/z|"];
|
||||
305 [label="Const: Null(null)"];
|
||||
306 [label="Equality operator !="];
|
||||
307 [label="Exit when branch condition"];
|
||||
}
|
||||
309 [label="Synthetic else branch"];
|
||||
310 [label="Enter when branch result"];
|
||||
308 [label="Synthetic else branch"];
|
||||
309 [label="Enter when branch result"];
|
||||
subgraph cluster_69 {
|
||||
color=blue
|
||||
311 [label="Enter block"];
|
||||
312 [label="Access variable R|<local>/x|"];
|
||||
313 [label="Access variable R|kotlin/String.length|"];
|
||||
314 [label="Access variable R|<local>/y|"];
|
||||
315 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
316 [label="Access variable R|<local>/z|"];
|
||||
317 [label="Access variable R|kotlin/String.length|"];
|
||||
318 [label="Exit block"];
|
||||
310 [label="Enter block"];
|
||||
311 [label="Access variable R|<local>/x|"];
|
||||
312 [label="Access variable R|kotlin/String.length|"];
|
||||
313 [label="Access variable R|<local>/y|"];
|
||||
314 [label="Access variable <Inapplicable(UNSAFE_CALL): kotlin/String.length>#"];
|
||||
315 [label="Access variable R|<local>/z|"];
|
||||
316 [label="Access variable R|kotlin/String.length|"];
|
||||
317 [label="Exit block"];
|
||||
}
|
||||
319 [label="Exit when branch result"];
|
||||
320 [label="Exit when"];
|
||||
318 [label="Exit when branch result"];
|
||||
319 [label="Exit when"];
|
||||
}
|
||||
321 [label="Exit block"];
|
||||
320 [label="Exit block"];
|
||||
}
|
||||
322 [label="Exit function test_7" style="filled" fillcolor=red];
|
||||
321 [label="Exit function test_7" style="filled" fillcolor=red];
|
||||
}
|
||||
202 -> {203};
|
||||
203 -> {204};
|
||||
@@ -830,27 +829,26 @@ digraph boundSmartcastsInBranches_kt {
|
||||
287 -> {288};
|
||||
288 -> {289};
|
||||
289 -> {291 290};
|
||||
290 -> {302};
|
||||
290 -> {301};
|
||||
291 -> {292};
|
||||
292 -> {293};
|
||||
293 -> {294};
|
||||
294 -> {295};
|
||||
295 -> {322} [label=onUncaughtException];
|
||||
295 -> {296} [style=dotted];
|
||||
296 -> {297} [style=dotted];
|
||||
297 -> {298} [style=dotted];
|
||||
298 -> {299} [style=dotted];
|
||||
299 -> {300} [style=dotted];
|
||||
300 -> {301} [style=dotted];
|
||||
301 -> {302} [style=dotted];
|
||||
295 -> {296};
|
||||
296 -> {297};
|
||||
297 -> {298};
|
||||
298 -> {299};
|
||||
299 -> {300};
|
||||
300 -> {301};
|
||||
301 -> {302};
|
||||
302 -> {303};
|
||||
303 -> {304};
|
||||
304 -> {305};
|
||||
305 -> {306};
|
||||
306 -> {307};
|
||||
307 -> {308};
|
||||
308 -> {310 309};
|
||||
309 -> {320};
|
||||
307 -> {309 308};
|
||||
308 -> {319};
|
||||
309 -> {310};
|
||||
310 -> {311};
|
||||
311 -> {312};
|
||||
312 -> {313};
|
||||
@@ -862,6 +860,5 @@ digraph boundSmartcastsInBranches_kt {
|
||||
318 -> {319};
|
||||
319 -> {320};
|
||||
320 -> {321};
|
||||
321 -> {322};
|
||||
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ fun test_7() {
|
||||
y<!UNSAFE_CALL!>.<!>length // Bad
|
||||
z.length // OK
|
||||
}
|
||||
if (<!SENSELESS_COMPARISON!>y != null<!>) {
|
||||
if (y != null) {
|
||||
x<!UNSAFE_CALL!>.<!>length // Bad
|
||||
y.length // OK
|
||||
z<!UNSAFE_CALL!>.<!>length // Bad
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
digraph incorrectSmartcastToNothing_kt {
|
||||
graph [nodesep=3]
|
||||
node [shape=box penwidth=2]
|
||||
edge [penwidth=2]
|
||||
|
||||
subgraph cluster_0 {
|
||||
color=red
|
||||
0 [label="Enter property" style="filled" fillcolor=red];
|
||||
1 [label="Const: String(foo)"];
|
||||
2 [label="Function call: R|java/io/File.File|(...)"];
|
||||
3 [label="Exit property" style="filled" fillcolor=red];
|
||||
}
|
||||
0 -> {1};
|
||||
1 -> {2};
|
||||
2 -> {3};
|
||||
|
||||
subgraph cluster_1 {
|
||||
color=red
|
||||
4 [label="Enter function test" style="filled" fillcolor=red];
|
||||
subgraph cluster_2 {
|
||||
color=blue
|
||||
5 [label="Enter block"];
|
||||
subgraph cluster_3 {
|
||||
color=blue
|
||||
6 [label="Enter when"];
|
||||
subgraph cluster_4 {
|
||||
color=blue
|
||||
7 [label="Enter when branch condition "];
|
||||
8 [label="Access variable R|<local>/cacheExtSetting|"];
|
||||
9 [label="Const: Null(null)"];
|
||||
10 [label="Equality operator =="];
|
||||
11 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_5 {
|
||||
color=blue
|
||||
12 [label="Enter when branch condition "];
|
||||
13 [label="Access variable R|<local>/cacheExtSetting|"];
|
||||
14 [label="Function call: R|<local>/cacheExtSetting|.R|kotlin/text/isBlank|()"];
|
||||
15 [label="Exit when branch condition"];
|
||||
}
|
||||
subgraph cluster_6 {
|
||||
color=blue
|
||||
16 [label="Enter when branch condition else"];
|
||||
17 [label="Exit when branch condition"];
|
||||
}
|
||||
18 [label="Enter when branch result"];
|
||||
subgraph cluster_7 {
|
||||
color=blue
|
||||
19 [label="Enter block"];
|
||||
20 [label="Access variable R|<local>/cacheExtSetting|"];
|
||||
21 [label="Function call: R|java/io/File.File|(...)"];
|
||||
22 [label="Exit block"];
|
||||
}
|
||||
23 [label="Exit when branch result"];
|
||||
24 [label="Enter when branch result"];
|
||||
subgraph cluster_8 {
|
||||
color=blue
|
||||
25 [label="Enter block"];
|
||||
26 [label="Const: Null(null)"];
|
||||
27 [label="Exit block"];
|
||||
}
|
||||
28 [label="Exit when branch result"];
|
||||
29 [label="Enter when branch result"];
|
||||
subgraph cluster_9 {
|
||||
color=blue
|
||||
30 [label="Enter block"];
|
||||
31 [label="Access variable R|/cache|"];
|
||||
32 [label="Enter safe call"];
|
||||
33 [label="Postponed enter to lambda"];
|
||||
subgraph cluster_10 {
|
||||
color=blue
|
||||
43 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
|
||||
subgraph cluster_11 {
|
||||
color=blue
|
||||
44 [label="Enter block"];
|
||||
45 [label="Access variable R|<local>/it|"];
|
||||
46 [label="Const: String(main.kts.compiled.cache)"];
|
||||
47 [label="Function call: R|java/io/File.File|(...)"];
|
||||
48 [label="Exit block"];
|
||||
}
|
||||
49 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
|
||||
}
|
||||
34 [label="Postponed exit from lambda"];
|
||||
35 [label="Function call: $subj$.R|kotlin/let|<R|java/io/File|, R|java/io/File|>(...)"];
|
||||
36 [label="Exit safe call"];
|
||||
37 [label="Exit block"];
|
||||
}
|
||||
38 [label="Exit when branch result"];
|
||||
39 [label="Exit when"];
|
||||
}
|
||||
40 [label="Variable declaration: lval cacheBaseDir: R|java/io/File?|"];
|
||||
41 [label="Exit block"];
|
||||
}
|
||||
42 [label="Exit function test" style="filled" fillcolor=red];
|
||||
}
|
||||
4 -> {5};
|
||||
5 -> {6};
|
||||
6 -> {7};
|
||||
7 -> {8};
|
||||
8 -> {9};
|
||||
9 -> {10};
|
||||
10 -> {11};
|
||||
11 -> {29 12};
|
||||
12 -> {13};
|
||||
13 -> {14};
|
||||
14 -> {15};
|
||||
15 -> {24 16};
|
||||
16 -> {17};
|
||||
17 -> {18};
|
||||
18 -> {19};
|
||||
19 -> {20};
|
||||
20 -> {21};
|
||||
21 -> {22};
|
||||
22 -> {23};
|
||||
23 -> {39};
|
||||
24 -> {25};
|
||||
25 -> {26};
|
||||
26 -> {27};
|
||||
27 -> {28};
|
||||
28 -> {39};
|
||||
29 -> {30};
|
||||
30 -> {31};
|
||||
31 -> {32 36};
|
||||
32 -> {33};
|
||||
33 -> {43};
|
||||
33 -> {34} [color=red];
|
||||
33 -> {43} [style=dashed];
|
||||
34 -> {35};
|
||||
35 -> {36};
|
||||
36 -> {37};
|
||||
37 -> {38};
|
||||
38 -> {39};
|
||||
39 -> {40};
|
||||
40 -> {41};
|
||||
41 -> {42};
|
||||
43 -> {44};
|
||||
44 -> {45};
|
||||
45 -> {46};
|
||||
46 -> {47};
|
||||
47 -> {48};
|
||||
48 -> {49};
|
||||
49 -> {34} [color=green];
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
FILE: incorrectSmartcastToNothing.kt
|
||||
public final val cache: R|java/io/File?| = R|java/io/File.File|(String(foo))
|
||||
public get(): R|java/io/File?|
|
||||
public final fun test(cacheExtSetting: R|kotlin/String?|): R|kotlin/Unit| {
|
||||
lval cacheBaseDir: R|java/io/File?| = when () {
|
||||
==(R|<local>/cacheExtSetting|, Null(null)) -> {
|
||||
R|/cache|?.{ $subj$.R|kotlin/let|<R|java/io/File|, R|java/io/File|>(<L> = let@fun <anonymous>(it: R|java/io/File|): R|java/io/File| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
^ R|java/io/File.File|(R|<local>/it|, String(main.kts.compiled.cache))
|
||||
}
|
||||
) }
|
||||
}
|
||||
R|<local>/cacheExtSetting|.R|kotlin/text/isBlank|() -> {
|
||||
Null(null)
|
||||
}
|
||||
else -> {
|
||||
R|java/io/File.File|(R|<local>/cacheExtSetting|)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FULL_JDK
|
||||
// DUMP_CFG
|
||||
|
||||
import java.io.File
|
||||
|
||||
val cache: File? = File("foo")
|
||||
|
||||
fun test(cacheExtSetting: String?) {
|
||||
val cacheBaseDir = when {
|
||||
cacheExtSetting == null -> cache?.let { File(it, "main.kts.compiled.cache") }
|
||||
cacheExtSetting.isBlank() -> null
|
||||
else -> File(cacheExtSetting)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ var Any?.isNotNull: Boolean
|
||||
set(value) {
|
||||
contract {
|
||||
returns() implies (this@isNotNull != null)
|
||||
<!ERROR_IN_CONTRACT_DESCRIPTION!>require(<!SENSELESS_COMPARISON!>this != null<!>)<!>
|
||||
<!ERROR_IN_CONTRACT_DESCRIPTION!>require(this != null)<!>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
FILE: DailyAggregatedDoubleFactor.kt
|
||||
public abstract interface DailyAggregatedDoubleFactor : R|kotlin/Any| {
|
||||
}
|
||||
private final fun R|DailyAggregatedDoubleFactor|.aggregateBy(reduce: R|(kotlin/Double, kotlin/Double) -> kotlin/Double|): R|kotlin/collections/Map<kotlin/String, kotlin/Double>| {
|
||||
^aggregateBy R|kotlin/collections/mutableMapOf|<R|kotlin/String|, R|kotlin/Double|>()
|
||||
}
|
||||
public final fun R|DailyAggregatedDoubleFactor|.aggregateMin(): R|kotlin/collections/Map<kotlin/String, kotlin/Double>| {
|
||||
^aggregateMin this@R|/aggregateMin|.R|/aggregateBy|(::R|kotlin/comparisons/minOf|)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
interface DailyAggregatedDoubleFactor
|
||||
|
||||
private fun DailyAggregatedDoubleFactor.aggregateBy(reduce: (Double, Double) -> Double): Map<String, Double> {
|
||||
return mutableMapOf<String, Double>()
|
||||
}
|
||||
|
||||
fun DailyAggregatedDoubleFactor.aggregateMin(): Map<String, Double> = aggregateBy(::minOf)
|
||||
@@ -1,28 +0,0 @@
|
||||
FILE: EnumWithToString.kt
|
||||
public final enum class Some : R|kotlin/Enum<Some>| {
|
||||
private constructor(): R|Some| {
|
||||
super<R|kotlin/Enum<Some>|>()
|
||||
}
|
||||
|
||||
public final static enum entry ENTRY: R|Some| = object : R|Some| {
|
||||
private constructor(): R|<anonymous>| {
|
||||
super<R|Some|>()
|
||||
}
|
||||
|
||||
public final override fun toString(): R|kotlin/String| {
|
||||
^toString String(Entry)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public open override fun toString(): R|kotlin/String| {
|
||||
^toString String(Some)
|
||||
}
|
||||
|
||||
public final static fun values(): R|kotlin/Array<Some>| {
|
||||
}
|
||||
|
||||
public final static fun valueOf(value: R|kotlin/String|): R|Some| {
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user