mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-05-01 00:21:29 +00:00
Compare commits
6 Commits
prr/punzki
...
rr/faster_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b33a50783 | ||
|
|
cf7d0a1566 | ||
|
|
8b46dce8a2 | ||
|
|
5785e29e32 | ||
|
|
2376033218 | ||
|
|
ddd1a0a376 |
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>
|
||||
|
||||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -13,6 +13,6 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="KotlinCompilerSettings">
|
||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check" />
|
||||
<option name="additionalArguments" value="-version -Xallow-kotlin-package -Xskip-metadata-version-check -Xread-deserialized-contracts" />
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/runConfigurations/Test__Commonizer.xml
generated
2
.idea/runConfigurations/Test__Commonizer.xml
generated
@@ -4,7 +4,7 @@
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="--tests "org.jetbrains.kotlin.gradle.CommonizerHierarchicalIT" --tests "org.jetbrains.kotlin.gradle.CommonizerIT" --tests "org.jetbrains.kotlin.commonizer.**" --tests "org.jetbrains.kotlin.gradle.native.CocoaPodsIT.testCinteropCommonization*"" />
|
||||
<option name="scriptParameters" value="--tests "org.jetbrains.kotlin.gradle.CommonizerHierarchicalIT" --tests "org.jetbrains.kotlin.gradle.CommonizerIT" --tests "org.jetbrains.kotlin.commonizer.**"" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
|
||||
@@ -56,7 +56,7 @@ Alternatively, it is still possible to only provide required JDKs via environmen
|
||||
from environmental variables - disable Gradle toolchain auto-detection by passing `-Porg.gradle.java.installations.auto-detect=false` option
|
||||
(or put it into `$GRADLE_USER_HOME/gradle.properties`).
|
||||
|
||||
For local development, if you're not working on the standard library, it's OK to avoid installing JDK 1.6 and JDK 1.7.
|
||||
For local development, if you're not working on bytecode generation or the standard library, it's OK to avoid installing JDK 1.6 and JDK 1.7.
|
||||
Add `kotlin.build.isObsoleteJdkOverrideEnabled=true` to the `local.properties` file, so build will only use JDK 1.8+. Note, that in this
|
||||
case, build will have Gradle remote build cache misses for some tasks.
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -40,11 +39,12 @@ import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
val KOTLIN_CACHE_DIRECTORY_NAME = "kotlin"
|
||||
|
||||
open class IncrementalJvmCache(
|
||||
targetDataRoot: File,
|
||||
private val targetDataRoot: File,
|
||||
targetOutputDir: File?,
|
||||
pathConverter: FileToPathConverter
|
||||
) : AbstractIncrementalCache<JvmClassName>(
|
||||
@@ -114,43 +114,32 @@ open class IncrementalJvmCache(
|
||||
}
|
||||
|
||||
open fun saveFileToCache(generatedClass: GeneratedJvmClass, changesCollector: ChangesCollector) {
|
||||
saveClassToCache(KotlinClassInfo.createFrom(generatedClass.outputClass), generatedClass.sourceFiles, changesCollector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves information about the given (kotlinc-generated) class to this cache, and stores changes between this class and its previous
|
||||
* version into the given [ChangesCollector].
|
||||
*
|
||||
* @param kotlinClassInfo A kotlin-generated class
|
||||
* @param sourceFiles The source files that the given class was generated from, or `null` if this information is not available
|
||||
* @param changesCollector A [ChangesCollector]
|
||||
*/
|
||||
fun saveClassToCache(kotlinClassInfo: KotlinClassInfo, sourceFiles: List<File>?, changesCollector: ChangesCollector) {
|
||||
val className = kotlinClassInfo.className
|
||||
val sourceFiles: Collection<File> = generatedClass.sourceFiles
|
||||
val kotlinClass: LocalFileKotlinClass = generatedClass.outputClass
|
||||
val className = kotlinClass.className
|
||||
|
||||
dirtyOutputClassesMap.notDirty(className)
|
||||
sourceFiles?.forEach {
|
||||
sourceFiles.forEach {
|
||||
sourceToClassesMap.add(it, className)
|
||||
}
|
||||
|
||||
sourceFiles?.let { internalNameToSource[className.internalName] = it }
|
||||
internalNameToSource[className.internalName] = sourceFiles
|
||||
|
||||
if (kotlinClassInfo.classId.isLocal) return
|
||||
if (kotlinClass.classId.isLocal) return
|
||||
|
||||
when (kotlinClassInfo.classKind) {
|
||||
val header = kotlinClass.classHeader
|
||||
when (header.kind) {
|
||||
KotlinClassHeader.Kind.FILE_FACADE -> {
|
||||
if (sourceFiles != null) {
|
||||
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
|
||||
}
|
||||
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
|
||||
packagePartMap.addPackagePart(className)
|
||||
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
protoMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
|
||||
val partNames = kotlinClassInfo.classHeaderData?.toList()
|
||||
?: throw AssertionError("Multifile class has no parts: $className")
|
||||
val partNames = kotlinClass.classHeader.data?.toList()
|
||||
?: throw AssertionError("Multifile class has no parts: ${kotlinClass.className}")
|
||||
multifileFacadeToParts[className] = partNames
|
||||
// When a class is replaced with a facade with the same name,
|
||||
// the class' proto wouldn't ever be deleted,
|
||||
@@ -165,29 +154,25 @@ open class IncrementalJvmCache(
|
||||
internalNameToSource.remove(className.internalName)
|
||||
|
||||
// TODO NO_CHANGES? (delegates only)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
||||
if (sourceFiles != null) {
|
||||
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
||||
}
|
||||
assert(sourceFiles.size == 1) { "Multifile class part from several source files: $sourceFiles" }
|
||||
packagePartMap.addPackagePart(className)
|
||||
partToMultifileFacade.set(className.internalName, kotlinClassInfo.multifileClassName!!)
|
||||
partToMultifileFacade.set(className.internalName, header.multifileClassName!!)
|
||||
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
protoMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
if (sourceFiles != null) {
|
||||
assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" }
|
||||
addToClassStorage(kotlinClassInfo, sourceFiles.first())
|
||||
}
|
||||
assert(sourceFiles.size == 1) { "Class is expected to have only one source file: $sourceFiles" }
|
||||
addToClassStorage(kotlinClass, sourceFiles.first())
|
||||
|
||||
protoMap.process(kotlinClassInfo, changesCollector)
|
||||
constantsMap.process(kotlinClassInfo, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClassInfo, changesCollector)
|
||||
protoMap.process(kotlinClass, changesCollector)
|
||||
constantsMap.process(kotlinClass, changesCollector)
|
||||
inlineFunctionsMap.process(kotlinClass, changesCollector)
|
||||
}
|
||||
KotlinClassHeader.Kind.UNKNOWN, KotlinClassHeader.Kind.SYNTHETIC_CLASS -> {
|
||||
}
|
||||
@@ -293,8 +278,8 @@ open class IncrementalJvmCache(
|
||||
private inner class ProtoMap(storageFile: File) : BasicStringMap<ProtoMapValue>(storageFile, ProtoMapValueExternalizer) {
|
||||
|
||||
@Synchronized
|
||||
fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
return put(kotlinClassInfo, changesCollector)
|
||||
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
return put(kotlinClass, changesCollector)
|
||||
}
|
||||
|
||||
// A module mapping (.kotlin_module file) is stored in a cache,
|
||||
@@ -310,17 +295,19 @@ open class IncrementalJvmCache(
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun put(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClassInfo.className.internalName
|
||||
private fun put(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
val header = kotlinClass.classHeader
|
||||
|
||||
val key = kotlinClass.className.internalName
|
||||
val oldData = storage[key]
|
||||
val newData = ProtoMapValue(
|
||||
kotlinClassInfo.classKind != KotlinClassHeader.Kind.CLASS,
|
||||
BitEncoding.decodeBytes(kotlinClassInfo.classHeaderData!!),
|
||||
kotlinClassInfo.classHeaderStrings!!
|
||||
header.kind != KotlinClassHeader.Kind.CLASS,
|
||||
BitEncoding.decodeBytes(header.data!!),
|
||||
header.strings!!
|
||||
)
|
||||
storage[key] = newData
|
||||
|
||||
val packageFqName = kotlinClassInfo.className.packageFqName
|
||||
val packageFqName = kotlinClass.className.packageFqName
|
||||
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName), packageProtoKey = key)
|
||||
}
|
||||
|
||||
@@ -381,16 +368,31 @@ open class IncrementalJvmCache(
|
||||
|
||||
// todo: reuse code with InlineFunctionsMap?
|
||||
private inner class ConstantsMap(storageFile: File) : BasicStringMap<Map<String, Any>>(storageFile, ConstantsMapExternalizer) {
|
||||
private fun getConstantsMap(bytes: ByteArray): Map<String, Any> {
|
||||
val result = HashMap<String, Any>()
|
||||
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_PRIVATE
|
||||
if (value != null && access and staticFinal == Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) {
|
||||
result[name] = value
|
||||
}
|
||||
return null
|
||||
}
|
||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
operator fun contains(className: JvmClassName): Boolean =
|
||||
className.internalName in storage
|
||||
|
||||
@Synchronized
|
||||
fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClassInfo.className.internalName
|
||||
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClass.className.internalName
|
||||
val oldMap = storage[key] ?: emptyMap()
|
||||
|
||||
val newMap = kotlinClassInfo.constantsMap
|
||||
val newMap = getConstantsMap(kotlinClass.fileContents)
|
||||
if (newMap.isNotEmpty()) {
|
||||
storage[key] = newMap
|
||||
} else {
|
||||
@@ -399,18 +401,8 @@ open class IncrementalJvmCache(
|
||||
|
||||
for (const in oldMap.keys + newMap.keys) {
|
||||
//Constant can be declared via companion object or via const field declaration
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
kotlinClassInfo.scopeFqName(companion = true),
|
||||
const,
|
||||
oldMap[const],
|
||||
newMap[const]
|
||||
)
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
kotlinClassInfo.scopeFqName(companion = false),
|
||||
const,
|
||||
oldMap[const],
|
||||
newMap[const]
|
||||
)
|
||||
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(companion = true), const, oldMap[const], newMap[const])
|
||||
changesCollector.collectMemberIfValueWasChanged(kotlinClass.scopeFqName(companion = false), const, oldMap[const], newMap[const])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,20 +490,67 @@ open class IncrementalJvmCache(
|
||||
value.dumpCollection()
|
||||
}
|
||||
|
||||
private fun addToClassStorage(classInfo: KotlinClassInfo, srcFile: File) {
|
||||
val (nameResolver, proto) = JvmProtoBufUtil.readClassDataFrom(classInfo.classHeaderData!!, classInfo.classHeaderStrings!!)
|
||||
private fun addToClassStorage(kotlinClass: LocalFileKotlinClass, srcFile: File) {
|
||||
val (nameResolver, proto) = JvmProtoBufUtil.readClassDataFrom(kotlinClass.classHeader.data!!, kotlinClass.classHeader.strings!!)
|
||||
addToClassStorage(proto, nameResolver, srcFile)
|
||||
}
|
||||
|
||||
private inner class InlineFunctionsMap(storageFile: File) :
|
||||
BasicStringMap<Map<String, Long>>(storageFile, StringToLongMapExternalizer) {
|
||||
private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray): Map<String, Long> {
|
||||
val inlineFunctions = inlineFunctionsJvmNames(header)
|
||||
if (inlineFunctions.isEmpty()) return emptyMap()
|
||||
|
||||
val result = HashMap<String, Long>()
|
||||
var dummyVersion: Int = -1
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String?,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
dummyVersion = version
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val dummyClassWriter = ClassWriter(0)
|
||||
dummyClassWriter.visit(dummyVersion, 0, "dummy", null, AsmTypes.OBJECT_TYPE.internalName, null)
|
||||
|
||||
return object : MethodVisitor(Opcodes.API_VERSION, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) {
|
||||
override fun visitEnd() {
|
||||
val jvmName = name + desc
|
||||
if (jvmName !in inlineFunctions) return
|
||||
|
||||
val dummyBytes = dummyClassWriter.toByteArray()!!
|
||||
|
||||
val hash = dummyBytes.md5()
|
||||
result[jvmName] = hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 0)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun process(kotlinClassInfo: KotlinClassInfo, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClassInfo.className.internalName
|
||||
fun process(kotlinClass: LocalFileKotlinClass, changesCollector: ChangesCollector) {
|
||||
val key = kotlinClass.className.internalName
|
||||
val oldMap = storage[key] ?: emptyMap()
|
||||
|
||||
val newMap = kotlinClassInfo.inlineFunctionsMap
|
||||
val newMap = getInlineFunctionsMap(kotlinClass.classHeader, kotlinClass.fileContents)
|
||||
if (newMap.isNotEmpty()) {
|
||||
storage[key] = newMap
|
||||
} else {
|
||||
@@ -520,7 +559,7 @@ open class IncrementalJvmCache(
|
||||
|
||||
for (fn in oldMap.keys + newMap.keys) {
|
||||
changesCollector.collectMemberIfValueWasChanged(
|
||||
kotlinClassInfo.scopeFqName(),
|
||||
kotlinClass.scopeFqName(),
|
||||
functionNameBySignature(fn),
|
||||
oldMap[fn],
|
||||
newMap[fn]
|
||||
@@ -563,6 +602,13 @@ sealed class ChangeInfo(val fqName: FqName) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun LocalFileKotlinClass.scopeFqName(companion: Boolean = false) = when (classHeader.kind) {
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
className.fqNameForClassNameWithoutDollars.let { if (companion) it.child(DEFAULT_NAME_FOR_COMPANION_OBJECT) else it }
|
||||
}
|
||||
else -> className.packageFqName
|
||||
}
|
||||
|
||||
fun ByteArray.md5(): Long {
|
||||
val d = MessageDigest.getInstance("MD5").digest(this)!!
|
||||
return ((d[0].toLong() and 0xFFL)
|
||||
@@ -594,125 +640,3 @@ fun <K : Comparable<K>, V> Map<K, V>.dumpMap(dumpValue: (V) -> String): String =
|
||||
@TestOnly
|
||||
fun <T : Comparable<T>> Collection<T>.dumpCollection(): String =
|
||||
"[${sorted().joinToString(", ", transform = Any::toString)}]"
|
||||
|
||||
/**
|
||||
* Minimal information about a kotlinc-generated class that will be used to compute recompilation-triggered changes to support incremental
|
||||
* compilation (see [IncrementalJvmCache.saveClassToCache]).
|
||||
*
|
||||
* It's important that this class contain only the minimal required information, as it will be part of the classpath snapshot of the
|
||||
* `KotlinCompile` task and the task needs to support compile avoidance. For example, this class should contain public method signatures,
|
||||
* and should not contain private method signatures, or method implementations.
|
||||
*/
|
||||
class KotlinClassInfo private constructor(
|
||||
val classId: ClassId,
|
||||
val classKind: KotlinClassHeader.Kind,
|
||||
val classHeaderData: Array<String>?,
|
||||
val classHeaderStrings: Array<String>?,
|
||||
@Suppress("SpellCheckingInspection") val multifileClassName: String?,
|
||||
val constantsMap: LinkedHashMap<String, Any>,
|
||||
val inlineFunctionsMap: LinkedHashMap<String, Long>
|
||||
) {
|
||||
|
||||
val className: JvmClassName by lazy { JvmClassName.byClassId(classId) }
|
||||
|
||||
fun scopeFqName(companion: Boolean = false) = when (classKind) {
|
||||
KotlinClassHeader.Kind.CLASS -> {
|
||||
className.fqNameForClassNameWithoutDollars.let { if (companion) it.child(DEFAULT_NAME_FOR_COMPANION_OBJECT) else it }
|
||||
}
|
||||
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> {
|
||||
val result = LinkedHashMap<String, Any>()
|
||||
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
val staticFinal = Opcodes.ACC_STATIC or Opcodes.ACC_FINAL or Opcodes.ACC_PRIVATE
|
||||
if (value != null && access and staticFinal == Opcodes.ACC_STATIC or Opcodes.ACC_FINAL) {
|
||||
result[name] = value
|
||||
}
|
||||
return null
|
||||
}
|
||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getInlineFunctionsMap(header: KotlinClassHeader, bytes: ByteArray): LinkedHashMap<String, Long> {
|
||||
val inlineFunctions = inlineFunctionsJvmNames(header)
|
||||
if (inlineFunctions.isEmpty()) return LinkedHashMap()
|
||||
|
||||
val result = LinkedHashMap<String, Long>()
|
||||
var dummyVersion: Int = -1
|
||||
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String?,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
dummyVersion = version
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
val dummyClassWriter = ClassWriter(0)
|
||||
dummyClassWriter.visit(dummyVersion, 0, "dummy", null, AsmTypes.OBJECT_TYPE.internalName, null)
|
||||
|
||||
return object : MethodVisitor(Opcodes.API_VERSION, dummyClassWriter.visitMethod(0, name, desc, null, exceptions)) {
|
||||
override fun visitEnd() {
|
||||
val jvmName = name + desc
|
||||
if (jvmName !in inlineFunctions) return
|
||||
|
||||
val dummyBytes = dummyClassWriter.toByteArray()!!
|
||||
|
||||
val hash = dummyBytes.md5()
|
||||
result[jvmName] = hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 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\n")
|
||||
}
|
||||
} 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")
|
||||
@@ -148,9 +148,11 @@ extra["versions.kotlinx-collections-immutable-jvm"] = immutablesVersion
|
||||
extra["versions.ktor-network"] = "1.0.1"
|
||||
|
||||
if (!project.hasProperty("versions.kotlin-native")) {
|
||||
extra["versions.kotlin-native"] = "1.6.0-dev-1728"
|
||||
extra["versions.kotlin-native"] = "1.6.0-dev-248"
|
||||
}
|
||||
|
||||
val effectSystemEnabled by extra(project.getBooleanProperty("kotlin.compiler.effectSystemEnabled") ?: false)
|
||||
val newInferenceEnabled by extra(project.getBooleanProperty("kotlin.compiler.newInferenceEnabled") ?: false)
|
||||
val useJvmFir by extra(project.kotlinBuildProperties.useFir)
|
||||
|
||||
val intellijSeparateSdks = project.getBooleanProperty("intellijSeparateSdks") ?: false
|
||||
@@ -159,7 +161,11 @@ extra["intellijSeparateSdks"] = intellijSeparateSdks
|
||||
|
||||
extra["IntellijCoreDependencies"] =
|
||||
listOf(
|
||||
"asm-all-9.0",
|
||||
when {
|
||||
Platform[203].orHigher() -> "asm-all-9.0"
|
||||
Platform[202].orHigher() -> "asm-all-8.0.1"
|
||||
else -> "asm-all-7.0.1"
|
||||
},
|
||||
"guava",
|
||||
"jdom",
|
||||
"jna",
|
||||
@@ -315,7 +321,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,14 +449,9 @@ 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",
|
||||
"-Xread-deserialized-contracts",
|
||||
"-progressive".takeIf { hasProperty("test.progressive.mode") }
|
||||
)
|
||||
|
||||
@@ -458,8 +467,7 @@ 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
|
||||
"-Xnormalize-constructor-calls=enable"
|
||||
)
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
@@ -473,11 +481,16 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
if (!kotlinBuildProperties.disableWerror) {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
if (!kotlinBuildProperties.isInJpsBuildIdeaSync && !kotlinBuildProperties.useFir && !kotlinBuildProperties.disableWerror) {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -721,8 +734,6 @@ tasks {
|
||||
dependsOn(":kotlin-scripting-jsr223-test:embeddableTest")
|
||||
dependsOn(":kotlin-main-kts-test:test")
|
||||
dependsOn(":kotlin-main-kts-test:testWithIr")
|
||||
dependsOn(":kotlin-scripting-ide-services-test:test")
|
||||
dependsOn(":kotlin-scripting-ide-services-test:embeddableTest")
|
||||
dependsOn(":kotlin-scripting-js-test:test")
|
||||
}
|
||||
|
||||
@@ -1126,4 +1137,4 @@ afterEvaluate {
|
||||
"https://cache-redirector.jetbrains.com/github.com/yarnpkg/yarn/releases/download"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -19,17 +19,7 @@ val KotlinBuildProperties.jarCompression: Boolean get() = getBoolean("kotlin.bui
|
||||
|
||||
val KotlinBuildProperties.ignoreTestFailures: Boolean get() = getBoolean("ignoreTestFailures", isTeamcityBuild)
|
||||
|
||||
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.disableWerror: Boolean get() = getBoolean("kotlin.build.disable.werror", false)
|
||||
|
||||
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")
|
||||
@@ -26,7 +26,7 @@ fun CompatibilityPredicate.or(other: CompatibilityPredicate): CompatibilityPredi
|
||||
}
|
||||
|
||||
enum class Platform : CompatibilityPredicate {
|
||||
P203;
|
||||
P202, P203;
|
||||
|
||||
val version: Int = name.drop(1).toInt()
|
||||
|
||||
@@ -43,7 +43,10 @@ enum class Platform : CompatibilityPredicate {
|
||||
}
|
||||
|
||||
enum class Ide(val platform: Platform) : CompatibilityPredicate {
|
||||
IJ203(Platform.P203);
|
||||
IJ202(Platform.P202),
|
||||
IJ203(Platform.P203),
|
||||
|
||||
AS42(Platform.P202);
|
||||
|
||||
val kind = Kind.values().first { it.shortName == name.take(2) }
|
||||
val version = name.dropWhile { !it.isDigit() }.toInt()
|
||||
|
||||
@@ -22,8 +22,7 @@ enum class JdkMajorVersion(
|
||||
JDK_10(10, mandatory = false, overrideMajorVersion = 11),
|
||||
JDK_11(11, mandatory = false),
|
||||
JDK_15(15, mandatory = false),
|
||||
JDK_16(16, mandatory = false),
|
||||
JDK_17(17, mandatory = false);
|
||||
JDK_16(16, mandatory = false);
|
||||
|
||||
fun isMandatory(): Boolean = mandatory
|
||||
|
||||
@@ -181,4 +180,4 @@ fun Project.getJdkVersionWithOverride(jdkVersion: JdkMajorVersion): JdkMajorVers
|
||||
} else {
|
||||
jdkVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,12 @@ import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
@JvmOverloads
|
||||
fun Project.configureJava9Compilation(
|
||||
@@ -55,3 +59,17 @@ private class Java9AdditionalArgumentsProvider(
|
||||
"-Xlint:-requires-transitive-automatic" // suppress automatic module transitive dependencies in kotlin.test
|
||||
)
|
||||
}
|
||||
|
||||
fun Project.disableDeprecatedJvmTargetWarning() {
|
||||
if (!kotlinBuildProperties.useFir && !kotlinBuildProperties.disableWerror) {
|
||||
val tasksWithWarnings: List<String> by rootProject.extra
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
if (!tasksWithWarnings.contains(path)) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
freeCompilerArgs += "-Xsuppress-deprecated-jvm-target-warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,11 @@ configurations {
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.languageVersion = "1.4"
|
||||
kotlinOptions.apiVersion = "1.4"
|
||||
kotlinOptions.languageVersion = "1.3"
|
||||
kotlinOptions.apiVersion = "1.3"
|
||||
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"]}"
|
||||
|
||||
@@ -171,18 +171,16 @@ fun Project.configureDefaultPublishing() {
|
||||
.all { configureRepository() }
|
||||
}
|
||||
|
||||
private fun Project.getSensitiveProperty(name: String): String? {
|
||||
return project.findProperty(name) as? String ?: System.getenv(name)
|
||||
}
|
||||
|
||||
private fun Project.configureSigning() {
|
||||
configure<SigningExtension> {
|
||||
sign(extensions.getByType<PublishingExtension>().publications) // all publications
|
||||
|
||||
val signKeyId = project.getSensitiveProperty("signKeyId")
|
||||
val signKeyId = project.findProperty("signKeyId") as? String
|
||||
if (!signKeyId.isNullOrBlank()) {
|
||||
val signKeyPrivate = project.getSensitiveProperty("signKeyPrivate") ?: error("Parameter `signKeyPrivate` not found")
|
||||
val signKeyPassphrase = project.getSensitiveProperty("signKeyPassphrase") ?: error("Parameter `signKeyPassphrase` not found")
|
||||
val signKeyPrivate = project.findProperty("signKeyPrivate") as? String
|
||||
?: error("Parameter `signKeyPrivate` not found")
|
||||
val signKeyPassphrase = project.findProperty("signKeyPassphrase") as? String
|
||||
?: error("Parameter `signKeyPassphrase` not found")
|
||||
useInMemoryPgpKeys(signKeyId, signKeyPrivate, signKeyPassphrase)
|
||||
} else {
|
||||
useGpgCmd()
|
||||
|
||||
@@ -15,10 +15,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestFiles
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -26,11 +23,11 @@ import org.jetbrains.kotlin.test.*
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.model.ResultingArtifact
|
||||
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.impl.BackendKindExtractorImpl
|
||||
import org.jetbrains.kotlin.test.services.impl.TemporaryDirectoryManagerImpl
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CodegenHelpersSourceFilesProvider
|
||||
@@ -385,7 +382,6 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
"test${testDataFile.nameWithoutExtension.replaceFirstChar(Char::uppercaseChar)}",
|
||||
emptySet()
|
||||
)
|
||||
startingArtifactFactory = { ResultingArtifact.Source() }
|
||||
}.build(testDataFile.path)
|
||||
}
|
||||
|
||||
@@ -410,6 +406,7 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
|
||||
assertions = JUnit5Assertions
|
||||
useAdditionalService<TemporaryDirectoryManager>(::TemporaryDirectoryManagerImpl)
|
||||
useAdditionalService<BackendKindExtractor>(::BackendKindExtractorImpl)
|
||||
useSourcePreprocessor(*AbstractKotlinCompilerTest.defaultPreprocessors.toTypedArray())
|
||||
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -87,7 +87,7 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
paramTypes.add(type)
|
||||
paramSlots += type.size
|
||||
template.append("\u0001")
|
||||
if (paramSlots >= 199) {
|
||||
if (paramSlots >= 200) {
|
||||
// Concatenate current arguments into string
|
||||
// because of `StringConcatFactory` limitation add use it as new argument for further processing:
|
||||
// "The number of parameter slots in {@code concatType} is less than or equal to 200"
|
||||
@@ -166,4 +166,4 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
StringConcatGenerator(state.runtimeStringConcat, mv)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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? {
|
||||
@@ -1250,8 +1302,6 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
oldLvt += record
|
||||
}
|
||||
method.localVariables.clear()
|
||||
|
||||
val oldLvtNodeToLatestNewLvtNode = mutableMapOf<LocalVariableNode, LocalVariableNode>()
|
||||
// Skip `this` for suspend lambda
|
||||
val start = if (isForNamedFunction) 0 else 1
|
||||
for (variableIndex in start until method.maxLocals) {
|
||||
@@ -1291,25 +1341,33 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
val endLabel = nextLabel(insn.next)?.let { min(lvtRecord.end, it) } ?: lvtRecord.end
|
||||
// startLabel can be null in case of parameters
|
||||
@Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start
|
||||
|
||||
// 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 node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
method.localVariables.add(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge consequent LVT records, otherwise, atomicfu goes crazy (KT-47749)
|
||||
val toRemove = arrayListOf<LocalVariableNode>()
|
||||
val sortedLVT = method.localVariables.sortedBy { method.instructions.indexOf(it.start) }
|
||||
for (i in sortedLVT.indices) {
|
||||
var endIndex = method.instructions.indexOf(sortedLVT[i].end)
|
||||
for (j in (i + 1) until sortedLVT.size) {
|
||||
val startIndex = method.instructions.indexOf(sortedLVT[j].start)
|
||||
if (endIndex < startIndex) break
|
||||
if (endIndex != startIndex ||
|
||||
sortedLVT[i].index != sortedLVT[j].index ||
|
||||
sortedLVT[i].name != sortedLVT[j].name ||
|
||||
sortedLVT[i].desc != sortedLVT[j].desc
|
||||
) continue
|
||||
sortedLVT[i].end = sortedLVT[j].end
|
||||
endIndex = method.instructions.indexOf(sortedLVT[j].end)
|
||||
toRemove += sortedLVT[j]
|
||||
}
|
||||
}
|
||||
|
||||
method.localVariables.removeAll(toRemove)
|
||||
|
||||
for (variable in oldLvt) {
|
||||
// $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables
|
||||
// For example, $continuation is used to create async stack trace
|
||||
@@ -1327,35 +1385,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
|
||||
}
|
||||
@@ -171,11 +171,10 @@ class UninitializedStoresProcessor(
|
||||
|
||||
assert(insn.opcode == Opcodes.INVOKESPECIAL) { "Expected opcode Opcodes.INVOKESPECIAL for <init>, but ${insn.opcode} found" }
|
||||
val paramsCountIncludingReceiver = Type.getArgumentTypes((insn as MethodInsnNode).desc).size + 1
|
||||
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue
|
||||
?: if (isInSpecialMethod)
|
||||
return null
|
||||
else
|
||||
error("Expected value generated with NEW")
|
||||
val newValue = peek(paramsCountIncludingReceiver) as? UninitializedNewValue ?: if (isInSpecialMethod)
|
||||
return null
|
||||
else
|
||||
error("Expected value generated with NEW")
|
||||
|
||||
assert(peek(paramsCountIncludingReceiver - 1) is UninitializedNewValue) {
|
||||
"Next value after NEW should be one generated by DUP"
|
||||
|
||||
@@ -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
|
||||
@@ -320,8 +320,7 @@ class AnonymousObjectTransformer(
|
||||
inliningContext.callSiteInfo.isInlineOrInsideInline,
|
||||
inliningContext.callSiteInfo.file,
|
||||
inliningContext.callSiteInfo.lineNumber
|
||||
),
|
||||
null
|
||||
), null
|
||||
).doInline(deferringVisitor, LocalVarRemapper(parameters, 0), false, mapOf())
|
||||
reifiedTypeParametersUsages?.let(result.reifiedTypeParametersUsages::mergeAll)
|
||||
deferringVisitor.visitMaxs(-1, -1)
|
||||
|
||||
@@ -187,9 +187,6 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
|
||||
processor.tryBlocksMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
processor.localVarsMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
finallyNode.localVariables.forEach {
|
||||
processor.localVarsMetaInfo.addNewInterval(LocalVarNodeWrapper(it))
|
||||
}
|
||||
}
|
||||
|
||||
curInstr = curInstr.next
|
||||
|
||||
@@ -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,15 +6,19 @@
|
||||
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
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.surroundInvokesWithSuspendMarkersIfNeeded
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
@@ -688,7 +692,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 +737,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")
|
||||
@@ -744,8 +747,6 @@ class MethodInliner(
|
||||
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
|
||||
}
|
||||
|
||||
removeFakeVariablesInitializationIfPresent(node)
|
||||
|
||||
val frames = FastStackAnalyzer("<fake>", node, FixStackInterpreter()).analyze()
|
||||
|
||||
val localReturnsNormalizer = LocalReturnsNormalizer()
|
||||
@@ -768,73 +769,6 @@ class MethodInliner(
|
||||
localReturnsNormalizer.transform(node)
|
||||
}
|
||||
|
||||
private fun removeFakeVariablesInitializationIfPresent(node: MethodNode) {
|
||||
// Before 1.6, we generated fake variable initialization instructions
|
||||
// ICONST_0
|
||||
// ISTORE x
|
||||
// for all inline functions. Original intent was to mark inline function body for the debugger with corresponding LVT entry.
|
||||
// However, for @InlineOnly functions corresponding LVT entries were not copied (assuming that nobody is actually debugging
|
||||
// @InlineOnly functions).
|
||||
// Since 1.6, we no longer generate fake variables for @InlineOnly functions
|
||||
// Here we erase fake variable initialization for @InlineOnly functions inlined into existing bytecode (e.g., inline function
|
||||
// inside third-party library).
|
||||
// We consider a sequence of instructions 'ICONST_0; ISTORE x' a fake variable initialization if the corresponding variable 'x'
|
||||
// is not used in the bytecode (see below).
|
||||
|
||||
val insnArray = node.instructions.toArray()
|
||||
|
||||
// Very conservative variable usage check.
|
||||
// Here we look at integer variables only (this includes integral primitive types: byte, char, short, boolean).
|
||||
// Variable is considered "used" if:
|
||||
// - it's loaded with ILOAD instruction
|
||||
// - it's incremented with IINC instruction
|
||||
// - there's a local variable table entry for this variable
|
||||
val usedIntegerVar = BooleanArray(node.maxLocals)
|
||||
for (insn in insnArray) {
|
||||
if (insn.type == AbstractInsnNode.VAR_INSN && insn.opcode == Opcodes.ILOAD) {
|
||||
usedIntegerVar[(insn as VarInsnNode).`var`] = true
|
||||
} else if (insn.type == AbstractInsnNode.IINC_INSN) {
|
||||
usedIntegerVar[(insn as IincInsnNode).`var`] = true
|
||||
}
|
||||
}
|
||||
for (localVariable in node.localVariables) {
|
||||
val d0 = localVariable.desc[0]
|
||||
// byte || char || short || int || boolean
|
||||
if (d0 == 'B' || d0 == 'C' || d0 == 'S' || d0 == 'I' || d0 == 'Z') {
|
||||
usedIntegerVar[localVariable.index] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Looking for sequences of instructions:
|
||||
// p0: ICONST_0
|
||||
// p1: ISTORE x
|
||||
// p2: <label>
|
||||
// If variable 'x' is not "used" (see above), remove p0 and p1 instructions.
|
||||
var changes = false
|
||||
for (p0 in insnArray) {
|
||||
if (p0.opcode != Opcodes.ICONST_0) continue
|
||||
|
||||
val p1 = p0.next ?: break
|
||||
if (p1.opcode != Opcodes.ISTORE) continue
|
||||
|
||||
val p2 = p1.next ?: break
|
||||
if (p2.type != AbstractInsnNode.LABEL) continue
|
||||
|
||||
val varIndex = (p1 as VarInsnNode).`var`
|
||||
if (!usedIntegerVar[varIndex]) {
|
||||
changes = true
|
||||
node.instructions.remove(p0)
|
||||
node.instructions.remove(p1)
|
||||
}
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
// If we removed some instructions, some TCBs could (in theory) become empty.
|
||||
// Remove empty TCBs if there are any.
|
||||
node.removeEmptyCatchBlocks()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAnonymousClassThatMustBeRegenerated(type: Type?): Boolean {
|
||||
if (type == null || type.sort != Type.OBJECT) return false
|
||||
return inliningContext.isRegeneratedAnonymousObject(type.internalName)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
@@ -13,12 +12,12 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_SUSPEND_TYPE
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -73,14 +72,6 @@ class PsiInlineIntrinsicsSupport(
|
||||
|
||||
override fun toKotlinType(type: KotlinType): KotlinType = type
|
||||
|
||||
override fun checkAnnotatedType(type: KotlinType) {
|
||||
if (type.annotations.hasAnnotation(StandardNames.FqNames.extensionFunctionType)) {
|
||||
state.diagnostics.report(TYPEOF_EXTENSION_FUNCTION_TYPE.on(reportErrorsOn))
|
||||
} else if (type.annotations.any { it.fqName != JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION }) {
|
||||
state.diagnostics.report(TYPEOF_ANNOTATED_TYPE.on(reportErrorsOn))
|
||||
}
|
||||
}
|
||||
|
||||
override fun reportSuspendTypeUnsupported() {
|
||||
state.diagnostics.report(TYPEOF_SUSPEND_TYPE.on(reportErrorsOn))
|
||||
}
|
||||
|
||||
@@ -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.*
|
||||
@@ -22,11 +23,13 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.isCallableMemberCompiledToJvmDefault
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
@@ -157,7 +160,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 +237,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
|
||||
@@ -73,7 +75,6 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
|
||||
fun toKotlinType(type: KT): KotlinType
|
||||
|
||||
fun checkAnnotatedType(type: KT)
|
||||
fun reportSuspendTypeUnsupported()
|
||||
fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name)
|
||||
}
|
||||
@@ -231,7 +232,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 +256,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) ->
|
||||
|
||||
@@ -95,8 +95,6 @@ fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
|
||||
intrinsicsSupport.reportSuspendTypeUnsupported()
|
||||
}
|
||||
|
||||
intrinsicsSupport.checkAnnotatedType(type)
|
||||
|
||||
if (intrinsicsSupport.state.stableTypeOf) {
|
||||
if (intrinsicsSupport.isMutableCollectionType(type)) {
|
||||
v.invokestatic(REFLECTION, "mutableCollectionType", Type.getMethodDescriptor(K_TYPE, K_TYPE), false)
|
||||
|
||||
@@ -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,13 +17,11 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicVerifier
|
||||
|
||||
class MethodVerifier(private val checkPoint: String, private val generationState: GenerationState) : MethodTransformer() {
|
||||
class MethodVerifier(private val checkPoint: String) : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
if (!generationState.shouldValidateBytecode) return
|
||||
try {
|
||||
analyze(internalClassName, methodNode, BasicVerifier())
|
||||
} catch (e: Throwable) {
|
||||
|
||||
@@ -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,9 +40,8 @@ class OptimizationMethodVisitor(
|
||||
UninitializedStoresMethodTransformer(generationState.constructorCallNormalizationMode)
|
||||
|
||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||
InplaceArgumentsMethodTransformer(),
|
||||
FixStackWithLabelNormalizationMethodTransformer(),
|
||||
MethodVerifier("AFTER mandatory stack transformations", generationState)
|
||||
MethodVerifier("AFTER mandatory stack transformations")
|
||||
)
|
||||
|
||||
val optimizationTransformer = CompositeMethodTransformer(
|
||||
@@ -57,7 +55,7 @@ class OptimizationMethodVisitor(
|
||||
DeadCodeEliminationMethodTransformer(),
|
||||
RedundantGotoMethodTransformer(),
|
||||
RedundantNopsCleanupMethodTransformer(),
|
||||
MethodVerifier("AFTER optimizations", generationState)
|
||||
MethodVerifier("AFTER optimizations")
|
||||
)
|
||||
|
||||
override fun performTransformations(methodNode: MethodNode) {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.insnOpcodeText
|
||||
import org.jetbrains.kotlin.codegen.inline.insnText
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
@@ -55,9 +56,34 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
private val insnsArray = method.instructions.toArray()
|
||||
private val nInsns = method.instructions.size()
|
||||
|
||||
private val isMergeNode = BooleanArray(nInsns)
|
||||
// Single Predecessor Block (SPB) is a continuous sequence of instructions { I1, ... In } such that
|
||||
// if I=insns[i] and J=insns[i+1] both belong to SPB,
|
||||
// then I is a single immediate predecessor of J in a complete method control flow graph
|
||||
// (including exception edges).
|
||||
//
|
||||
// Note that classic basic blocks are SPBs, but the opposite is not true:
|
||||
// SPBs have single entry point, but can have multiple exit points
|
||||
// (which lead to instructions not belonging to the given SPB).
|
||||
// Example:
|
||||
// aload 1
|
||||
// dup
|
||||
// ifnull LA
|
||||
// invokevirtual foo()
|
||||
// dup
|
||||
// ifnull LB
|
||||
// invokevirtual bar()
|
||||
// goto LC
|
||||
// is SPB (but not a basic block).
|
||||
//
|
||||
// For each J=insns[i+1] such that I=insns[i] belongs to the same SPB,
|
||||
// data flow transfer function
|
||||
// Execute( J, Merge( { Out(K) | K <- Pred(J) } ) )
|
||||
// is effectively
|
||||
// Execute( J, Out(I) ) )
|
||||
// so, we don't need to merge frames for such I->J edges.
|
||||
private val singlePredBlock = IntArray(nInsns)
|
||||
|
||||
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||
val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||
|
||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
||||
private val queued = BooleanArray(nInsns)
|
||||
@@ -71,13 +97,14 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
if (nInsns == 0) return frames
|
||||
|
||||
checkAssertions()
|
||||
|
||||
computeExceptionHandlersForEachInsn(method)
|
||||
initMergeNodes()
|
||||
|
||||
initSinglePredBlocks()
|
||||
|
||||
val current = newFrame(method.maxLocals, method.maxStack)
|
||||
val handler = newFrame(method.maxLocals, method.maxStack)
|
||||
initLocals(current)
|
||||
mergeControlFlowEdge(0, current)
|
||||
initControlFlowAnalysis(current, method, owner)
|
||||
|
||||
while (top > 0) {
|
||||
val insn = queue[--top]
|
||||
@@ -90,16 +117,16 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
val insnType = insnNode.type
|
||||
|
||||
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
|
||||
mergeControlFlowEdge(insn + 1, f)
|
||||
visitNopInsn(f, insn)
|
||||
} else {
|
||||
current.init(f).execute(insnNode, interpreter)
|
||||
when {
|
||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
|
||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
|
||||
insnNode is JumpInsnNode ->
|
||||
visitJumpInsnNode(insnNode, current, insn, insnOpcode)
|
||||
insnNode is LookupSwitchInsnNode ->
|
||||
visitLookupSwitchInsnNode(insnNode, current, insn)
|
||||
insnNode is TableSwitchInsnNode ->
|
||||
visitTableSwitchInsnNode(insnNode, current, insn)
|
||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||
visitOpInsn(current, insn)
|
||||
else -> {
|
||||
@@ -114,7 +141,7 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
handler.init(f)
|
||||
handler.clearStack()
|
||||
handler.push(interpreter.newValue(exnType))
|
||||
mergeControlFlowEdge(jump, handler)
|
||||
mergeControlFlowEdge(insn, jump, handler)
|
||||
}
|
||||
|
||||
} catch (e: AnalyzerException) {
|
||||
@@ -128,56 +155,87 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
return frames
|
||||
}
|
||||
|
||||
internal fun initLocals(current: Frame<V>) {
|
||||
current.setReturn(interpreter.newValue(Type.getReturnType(method.desc)))
|
||||
val args = Type.getArgumentTypes(method.desc)
|
||||
var local = 0
|
||||
if ((method.access and Opcodes.ACC_STATIC) == 0) {
|
||||
val ctype = Type.getObjectType(owner)
|
||||
current.setLocal(local++, interpreter.newValue(ctype))
|
||||
}
|
||||
for (arg in args) {
|
||||
current.setLocal(local++, interpreter.newValue(arg))
|
||||
if (arg.size == 2) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
}
|
||||
while (local < method.maxLocals) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.indexOf() =
|
||||
method.instructions.indexOf(this)
|
||||
|
||||
private fun initMergeNodes() {
|
||||
private fun initSinglePredBlocks() {
|
||||
markSinglePredBlockEntries()
|
||||
markSinglePredBlockBodies()
|
||||
}
|
||||
|
||||
private fun markSinglePredBlockEntries() {
|
||||
// Method entry point is SPB entry point.
|
||||
var blockId = 0
|
||||
singlePredBlock[0] = ++blockId
|
||||
|
||||
// Every jump target is SPB entry point.
|
||||
for (insn in insnsArray) {
|
||||
when (insn.type) {
|
||||
AbstractInsnNode.JUMP_INSN -> {
|
||||
val jumpInsn = insn as JumpInsnNode
|
||||
isMergeNode[jumpInsn.label.indexOf()] = true
|
||||
}
|
||||
AbstractInsnNode.LOOKUPSWITCH_INSN -> {
|
||||
val switchInsn = insn as LookupSwitchInsnNode
|
||||
isMergeNode[switchInsn.dflt.indexOf()] = true
|
||||
for (label in switchInsn.labels) {
|
||||
isMergeNode[label.indexOf()] = true
|
||||
when (insn) {
|
||||
is JumpInsnNode -> {
|
||||
val labelIndex = insn.label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
AbstractInsnNode.TABLESWITCH_INSN -> {
|
||||
val switchInsn = insn as TableSwitchInsnNode
|
||||
isMergeNode[switchInsn.dflt.indexOf()] = true
|
||||
for (label in switchInsn.labels) {
|
||||
isMergeNode[label.indexOf()] = true
|
||||
is LookupSwitchInsnNode -> {
|
||||
insn.dflt?.let { dfltLabel ->
|
||||
val dfltIndex = dfltLabel.indexOf()
|
||||
if (singlePredBlock[dfltIndex] == 0) {
|
||||
singlePredBlock[dfltIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
for (label in insn.labels) {
|
||||
val labelIndex = label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
}
|
||||
is TableSwitchInsnNode -> {
|
||||
insn.dflt?.let { dfltLabel ->
|
||||
val dfltIndex = dfltLabel.indexOf()
|
||||
if (singlePredBlock[dfltIndex] == 0) {
|
||||
singlePredBlock[dfltIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
for (label in insn.labels) {
|
||||
val labelIndex = label.indexOf()
|
||||
if (singlePredBlock[labelIndex] == 0) {
|
||||
singlePredBlock[labelIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Every try-catch block handler entry point is SPB entry point
|
||||
for (tcb in method.tryCatchBlocks) {
|
||||
isMergeNode[tcb.handler.indexOf()] = true
|
||||
val handlerIndex = tcb.handler.indexOf()
|
||||
if (singlePredBlock[handlerIndex] == 0) {
|
||||
singlePredBlock[handlerIndex] = ++blockId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun markSinglePredBlockBodies() {
|
||||
var current = 0
|
||||
for ((i, insn) in insnsArray.withIndex()) {
|
||||
if (singlePredBlock[i] == 0) {
|
||||
singlePredBlock[i] = current
|
||||
} else {
|
||||
// Entered a new SPB.
|
||||
current = singlePredBlock[i]
|
||||
}
|
||||
|
||||
// GOTO, ATHROW, *RETURN instructions terminate current SPB.
|
||||
when (insn.opcode) {
|
||||
Opcodes.GOTO,
|
||||
Opcodes.ATHROW,
|
||||
in Opcodes.IRETURN..Opcodes.RETURN ->
|
||||
current = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||
frames[insn.indexOf()]
|
||||
@@ -188,33 +246,62 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
}
|
||||
|
||||
private fun visitOpInsn(current: Frame<V>, insn: Int) {
|
||||
mergeControlFlowEdge(insn + 1, current)
|
||||
mergeControlFlowEdge(insn, insn + 1, current)
|
||||
}
|
||||
|
||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
|
||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>, insn: Int) {
|
||||
var jump = insnNode.dflt.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
// In most cases order of visiting switch labels should not matter
|
||||
// The only one is a tableswitch being added in the beginning of coroutine method, these switch' labels may lead
|
||||
// in the middle of try/catch block, and FixStackAnalyzer is not ready for this (trying to restore stack before it was saved)
|
||||
// So we just fix the order of labels being traversed: the first one should be one at the method beginning
|
||||
// Using 'reversed' is because nodes are processed in LIFO order
|
||||
for (label in insnNode.labels.asReversed()) {
|
||||
mergeControlFlowEdge(label.indexOf(), current)
|
||||
for (label in insnNode.labels.reversed()) {
|
||||
jump = label.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
|
||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>, insn: Int) {
|
||||
var jump = insnNode.dflt.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
for (label in insnNode.labels) {
|
||||
mergeControlFlowEdge(label.indexOf(), current)
|
||||
jump = label.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
||||
if (insnOpcode != Opcodes.GOTO) {
|
||||
mergeControlFlowEdge(insn + 1, current)
|
||||
mergeControlFlowEdge(insn, insn + 1, current)
|
||||
}
|
||||
mergeControlFlowEdge(insnNode.label.indexOf(), current)
|
||||
val jump = insnNode.label.indexOf()
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
}
|
||||
|
||||
private fun visitNopInsn(f: Frame<V>, insn: Int) {
|
||||
mergeControlFlowEdge(insn, insn + 1, f)
|
||||
}
|
||||
|
||||
private fun initControlFlowAnalysis(current: Frame<V>, m: MethodNode, owner: String) {
|
||||
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)))
|
||||
val args = Type.getArgumentTypes(m.desc)
|
||||
var local = 0
|
||||
if ((m.access and Opcodes.ACC_STATIC) == 0) {
|
||||
val ctype = Type.getObjectType(owner)
|
||||
current.setLocal(local++, interpreter.newValue(ctype))
|
||||
}
|
||||
for (arg in args) {
|
||||
current.setLocal(local++, interpreter.newValue(arg))
|
||||
if (arg.size == 2) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
}
|
||||
while (local < m.maxLocals) {
|
||||
current.setLocal(local++, interpreter.newValue(null))
|
||||
}
|
||||
mergeControlFlowEdge(0, 0, current)
|
||||
}
|
||||
|
||||
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
||||
@@ -233,14 +320,15 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeControlFlowEdge(dest: Int, frame: Frame<V>) {
|
||||
private fun mergeControlFlowEdge(src: Int, dest: Int, frame: Frame<V>) {
|
||||
val oldFrame = frames[dest]
|
||||
val changes = when {
|
||||
oldFrame == null -> {
|
||||
frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) }
|
||||
true
|
||||
}
|
||||
!isMergeNode[dest] -> {
|
||||
dest == src + 1 && singlePredBlock[src] == singlePredBlock[dest] -> {
|
||||
// Forward jump within a single predecessor block, no need to merge.
|
||||
oldFrame.init(frame)
|
||||
true
|
||||
}
|
||||
@@ -252,4 +340,35 @@ open class FastMethodAnalyzer<V : Value>(
|
||||
queue[top++] = dest
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun dumpBlocksInfo() {
|
||||
fun LabelNode?.labelText() =
|
||||
if (this != null) "L#${indexOf()}" else "L<null>"
|
||||
|
||||
println("===== ${method.name} ${method.signature} ======")
|
||||
for ((i, insn) in insnsArray.withIndex()) {
|
||||
val insnText = when (insn) {
|
||||
is LabelNode ->
|
||||
"L#$i"
|
||||
is JumpInsnNode ->
|
||||
"${insn.insnOpcodeText} ${insn.label.labelText()}"
|
||||
is TableSwitchInsnNode ->
|
||||
"${insn.insnOpcodeText} min=${insn.min} max=${insn.max} \n\t\t\t" +
|
||||
"[${insn.labels.joinToString { it.labelText() }}] \n\t\t\t" +
|
||||
"dflt:${insn.dflt.labelText()}"
|
||||
is LookupSwitchInsnNode ->
|
||||
"${insn.insnOpcodeText} \n\t\t\t" +
|
||||
"[${insn.keys.zip(insn.labels).joinToString { (key, label) -> "$key: ${label.labelText()}"}}] \n\t\t\t" +
|
||||
"dflt:${insn.dflt.labelText()}"
|
||||
else ->
|
||||
insn.insnText
|
||||
}
|
||||
println("$i\t${singlePredBlock[i]}\t$insnText")
|
||||
}
|
||||
for (tcb in method.tryCatchBlocks) {
|
||||
println("\tTCB start:${tcb.start.labelText()} end:${tcb.end.labelText()} handler:${tcb.handler.labelText()}")
|
||||
}
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() =
|
||||
|
||||
@@ -101,12 +101,12 @@ internal open class FastStackAnalyzer<V : Value>(
|
||||
}
|
||||
|
||||
when {
|
||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current, insn)
|
||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current, insn)
|
||||
insnNode is JumpInsnNode ->
|
||||
visitJumpInsnNode(insnNode, current, insn, insnOpcode)
|
||||
insnNode is LookupSwitchInsnNode ->
|
||||
visitLookupSwitchInsnNode(insnNode, current, insn)
|
||||
insnNode is TableSwitchInsnNode ->
|
||||
visitTableSwitchInsnNode(insnNode, current, insn)
|
||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||
visitOpInsn(current, insn)
|
||||
else -> {
|
||||
@@ -174,7 +174,7 @@ internal open class FastStackAnalyzer<V : Value>(
|
||||
}
|
||||
|
||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
||||
if (insnOpcode != Opcodes.GOTO) {
|
||||
if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) {
|
||||
processControlFlowEdge(current, insn, insn + 1)
|
||||
}
|
||||
val jump = insnNode.label.indexOf()
|
||||
|
||||
@@ -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 {
|
||||
@@ -297,8 +299,6 @@ class GenerationState private constructor(
|
||||
!configuration.getBoolean(JVMConfigurationKeys.NO_UNIFIED_NULL_CHECKS)
|
||||
val functionsWithInlineClassReturnTypesMangled: Boolean =
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.MangleClassMembersReturningInlineClasses)
|
||||
val shouldValidateIr = configuration.getBoolean(JVMConfigurationKeys.VALIDATE_IR)
|
||||
val shouldValidateBytecode = configuration.getBoolean(JVMConfigurationKeys.VALIDATE_BYTECODE)
|
||||
|
||||
val rootContext: CodegenContext<*> = RootContext(this)
|
||||
|
||||
@@ -405,8 +405,8 @@ class GenerationState private constructor(
|
||||
this[KOTLIN_1_2] = oldMetadataVersion
|
||||
this[KOTLIN_1_3] = oldMetadataVersion
|
||||
this[KOTLIN_1_4] = JvmMetadataVersion(1, 4, 3)
|
||||
this[KOTLIN_1_5] = JvmMetadataVersion(1, 5, 1)
|
||||
this[KOTLIN_1_6] = JvmMetadataVersion.INSTANCE
|
||||
this[KOTLIN_1_5] = JvmMetadataVersion.INSTANCE
|
||||
this[KOTLIN_1_6] = JvmMetadataVersion(1, 6, 0)
|
||||
this[KOTLIN_1_7] = JvmMetadataVersion(1, 7, 0)
|
||||
|
||||
check(size == LanguageVersion.values().size) {
|
||||
|
||||
@@ -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,27 @@ plugins {
|
||||
val compilerModules: Array<String> by rootProject.extra
|
||||
val otherCompilerModules = compilerModules.filter { it != path }
|
||||
|
||||
val tasksWithWarnings: List<String> by rootProject.extra
|
||||
|
||||
val effectSystemEnabled: Boolean by rootProject.extra
|
||||
val newInferenceEnabled: Boolean by rootProject.extra
|
||||
|
||||
configureFreeCompilerArg(effectSystemEnabled, "-Xeffect-system")
|
||||
configureFreeCompilerArg(newInferenceEnabled, "-Xnew-inference")
|
||||
configureFreeCompilerArg(true, "-Xuse-mixed-named-arguments")
|
||||
|
||||
fun configureFreeCompilerArg(isEnabled: Boolean, compilerArgument: String) {
|
||||
if (isEnabled) {
|
||||
allprojects {
|
||||
tasks.withType<KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs += listOf(compilerArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val antLauncherJar by configurations.creating
|
||||
|
||||
dependencies {
|
||||
@@ -63,6 +85,16 @@ if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
idea {
|
||||
this.module.generatedSourceDirs.add(generationRoot)
|
||||
}
|
||||
} else if (!kotlinBuildProperties.useFir && !kotlinBuildProperties.disableWerror) {
|
||||
allprojects {
|
||||
tasks.withType<KotlinCompile<*>> {
|
||||
if (path !in tasksWithWarnings) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectTest(parallel = true) {
|
||||
|
||||
@@ -57,10 +57,10 @@ sourceSets {
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
languageVersion = "1.4"
|
||||
apiVersion = "1.4"
|
||||
languageVersion = "1.3"
|
||||
apiVersion = "1.3"
|
||||
freeCompilerArgs = freeCompilerArgs - "-progressive" + listOf(
|
||||
"-Xskip-prerelease-check", "-Xsuppress-version-warnings", "-Xuse-mixed-named-arguments", "-Xnew-inference"
|
||||
"-Xskip-prerelease-check", "-Xsuppress-version-warnings"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<idea-plugin>
|
||||
<id>org.jetbrains.kotlin</id>
|
||||
<version>1.2</version>
|
||||
|
||||
<!-- Don't add more extension points here! Logic in KotlinCoreEnvironment assumes that there is only one EP. -->
|
||||
<!-- And this file should be removed once 202 is no longer supported -->
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.psi.classFileDecompiler"
|
||||
interface="com.intellij.psi.compiled.ClassFileDecompilers$Decompiler"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,10 @@
|
||||
<idea-plugin>
|
||||
<id>org.jetbrains.kotlin</id>
|
||||
<version>1.2</version>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.psi.classFileDecompiler"
|
||||
interface="com.intellij.psi.compiled.ClassFileDecompilers$Decompiler"
|
||||
dynamic="true"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
@@ -369,6 +369,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
)
|
||||
var extendedCompilerChecks: Boolean by FreezableVar(false)
|
||||
|
||||
@GradleOption(DefaultValues.BooleanFalseDefault::class)
|
||||
@Argument(
|
||||
value = "-Xbuiltins-from-sources",
|
||||
description = "Compile builtIns from sources"
|
||||
@@ -381,12 +382,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 +424,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)
|
||||
|
||||
@@ -214,12 +214,6 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var useOldClassFilesReading: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xuse-fast-jar-file-system",
|
||||
description = "Use fast implementation on Jar FS. This may speed up compilation time, but currently it's an experimental mode"
|
||||
)
|
||||
var useFastJarFileSystem: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xdump-declarations-to",
|
||||
valueDescription = "<path>",
|
||||
@@ -511,18 +505,6 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
|
||||
)
|
||||
var serializeIr: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xvalidate-ir",
|
||||
description = "Validate IR before and after lowering"
|
||||
)
|
||||
var validateIr: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xvalidate-bytecode",
|
||||
description = "Validate generated JVM bytecode before and after optimizations"
|
||||
)
|
||||
var validateBytecode: Boolean by FreezableVar(false)
|
||||
|
||||
override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> {
|
||||
val result = super.configureAnalysisFlags(collector, languageVersion)
|
||||
result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics
|
||||
|
||||
@@ -38,11 +38,8 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
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
|
||||
@@ -94,7 +91,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, "repl.kts")
|
||||
|
||||
val environment = KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration)
|
||||
val projectEnv = KotlinCoreEnvironment.ProjectEnvironment(rootDisposable, environment, configuration)
|
||||
val projectEnv = KotlinCoreEnvironment.ProjectEnvironment(rootDisposable, environment)
|
||||
projectEnv.registerExtensionsFromPlugins(configuration)
|
||||
|
||||
val scriptingEvaluators = ScriptEvaluationExtension.getInstances(projectEnv.project)
|
||||
@@ -182,74 +179,18 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
// TODO: Handle non-empty main call arguments
|
||||
val mainCallArguments = if (K2JsArgumentConstants.NO_CALL == arguments.main) null else emptyList<String>()
|
||||
|
||||
val icCaches = configureLibraries(arguments.cacheDirectories)
|
||||
|
||||
if (arguments.irBuildCache) {
|
||||
messageCollector.report(INFO, "")
|
||||
messageCollector.report(INFO, "Building cache:")
|
||||
messageCollector.report(INFO, "to: ${outputFilePath}")
|
||||
messageCollector.report(INFO, arguments.cacheDirectories ?: "")
|
||||
messageCollector.report(INFO, libraries.toString())
|
||||
|
||||
val includes = arguments.includes!!
|
||||
|
||||
// TODO: deduplicate
|
||||
val mainModule = run {
|
||||
if (sourcesFiles.isNotEmpty()) {
|
||||
messageCollector.report(ERROR, "Source files are not supported when -Xinclude is present")
|
||||
}
|
||||
MainModule.Klib(includes)
|
||||
}
|
||||
|
||||
val start = System.currentTimeMillis()
|
||||
if (buildCache(
|
||||
cachePath = outputFilePath,
|
||||
project = projectJs,
|
||||
mainModule = mainModule,
|
||||
configuration = config.configuration,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
icCache = checkCaches(libraries, icCaches, skipLib = mainModule.libPath)
|
||||
)
|
||||
) {
|
||||
messageCollector.report(INFO, "IC cache building duration: ${System.currentTimeMillis() - start}ms")
|
||||
} else {
|
||||
messageCollector.report(INFO, "IC cache up-to-date check duration: ${System.currentTimeMillis() - start}ms")
|
||||
}
|
||||
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,
|
||||
@@ -258,40 +199,32 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
}
|
||||
|
||||
if (arguments.irProduceJs) {
|
||||
messageCollector.report(INFO,"Produce executable: $outputFilePath")
|
||||
messageCollector.report(INFO, arguments.cacheDirectories ?: "")
|
||||
|
||||
val phaseConfig = createPhaseConfig(jsPhases, arguments, messageCollector)
|
||||
|
||||
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 +235,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()
|
||||
|
||||
@@ -310,12 +243,15 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
return OK
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -334,11 +270,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
arguments.irSafeExternalBooleanDiagnostic,
|
||||
messageCollector
|
||||
),
|
||||
lowerPerModule = icCaches.isNotEmpty(),
|
||||
)
|
||||
|
||||
messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms")
|
||||
|
||||
val outputs = if (arguments.irDce && !arguments.irDceDriven) compiledModule.outputsAfterDce!! else compiledModule.outputs!!
|
||||
outputFile.write(outputs)
|
||||
outputs.dependencies.forEach { (name, content) ->
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.util.io;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public final class FileChannelUtil {
|
||||
@NotNull
|
||||
static FileChannel unInterruptible(@NotNull FileChannel channel) {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.extensions
|
||||
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
|
||||
|
||||
@@ -22,6 +22,6 @@ interface ScriptEvaluationExtension {
|
||||
fun eval(
|
||||
arguments: CommonCompilerArguments,
|
||||
configuration: CompilerConfiguration,
|
||||
projectEnvironment: KotlinCoreEnvironment.ProjectEnvironment
|
||||
projectEnvironment: JavaCoreProjectEnvironment
|
||||
): ExitCode
|
||||
}
|
||||
|
||||
@@ -233,8 +233,8 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
jars: List<KotlinLibraryFile>
|
||||
): MavenComparableVersion? {
|
||||
assert(jars.isNotEmpty()) { "'jars' must not be empty" }
|
||||
val oldestVersion = jars.minOf { it.version }
|
||||
val newestVersion = jars.maxOf { it.version }
|
||||
val oldestVersion = jars.minBy { it.version }!!.version
|
||||
val newestVersion = jars.maxBy { it.version }!!.version
|
||||
|
||||
// If the oldest version is the same as the newest version, then all jars have the same version
|
||||
if (oldestVersion == newestVersion) return oldestVersion
|
||||
@@ -250,9 +250,9 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
// we suggest to provide an explicit dependency on version X.
|
||||
// TODO: report this depending on the content of the jars instead
|
||||
val minReflectJar =
|
||||
jars.filter { it.file.name.startsWith("kotlin-reflect") }.minByOrNull { it.version }
|
||||
jars.filter { it.file.name.startsWith("kotlin-reflect") }.minBy { it.version }
|
||||
val maxStdlibJar =
|
||||
jars.filter { it.file.name.startsWith("kotlin-runtime") || it.file.name.startsWith("kotlin-stdlib") }.maxByOrNull { it.version }
|
||||
jars.filter { it.file.name.startsWith("kotlin-runtime") || it.file.name.startsWith("kotlin-stdlib") }.maxBy { it.version }
|
||||
if (minReflectJar != null && maxStdlibJar != null && minReflectJar.version < maxStdlibJar.version) {
|
||||
messageCollector.issue(
|
||||
null,
|
||||
|
||||
@@ -89,8 +89,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
val projectEnvironment =
|
||||
KotlinCoreEnvironment.ProjectEnvironment(
|
||||
rootDisposable,
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration),
|
||||
configuration
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(rootDisposable, configuration)
|
||||
)
|
||||
projectEnvironment.registerExtensionsFromPlugins(configuration)
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -235,7 +235,6 @@ object FirKotlinToJvmBytecodeCompiler {
|
||||
if (commonSession != null) {
|
||||
sourceDependsOnDependencies(listOf(commonSession.moduleData))
|
||||
}
|
||||
friendDependencies(module.getFriendPaths())
|
||||
}
|
||||
|
||||
val commonAnalyzerFacade = commonSession?.let { FirAnalyzerFacade(it, languageVersionSettings, commonKtFiles) }
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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
|
||||
|
||||
import com.intellij.openapi.project.DumbUtil
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
class KotlinCoreDumbUtil : DumbUtil {
|
||||
override fun <T : Any?> filterByDumbAwarenessHonoringIgnoring(collection: Collection<T>): List<T> =
|
||||
when (collection) {
|
||||
is List<T> -> collection
|
||||
else -> ArrayList(collection)
|
||||
}
|
||||
|
||||
override fun mayUseIndices(): Boolean = false
|
||||
}
|
||||
@@ -68,7 +68,6 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_W
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
||||
@@ -93,11 +92,11 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
@@ -108,54 +107,17 @@ import java.nio.file.FileSystems
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class KotlinCoreEnvironment private constructor(
|
||||
val projectEnvironment: ProjectEnvironment,
|
||||
val projectEnvironment: JavaCoreProjectEnvironment,
|
||||
private val initialConfiguration: CompilerConfiguration,
|
||||
configFiles: EnvironmentConfigFiles
|
||||
) {
|
||||
|
||||
class ProjectEnvironment(
|
||||
disposable: Disposable,
|
||||
applicationEnvironment: KotlinCoreApplicationEnvironment,
|
||||
configuration: CompilerConfiguration
|
||||
applicationEnvironment: KotlinCoreApplicationEnvironment
|
||||
) :
|
||||
KotlinCoreProjectEnvironment(disposable, applicationEnvironment) {
|
||||
|
||||
internal val jarFileSystem: VirtualFileSystem
|
||||
|
||||
init {
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
if (configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM)) {
|
||||
messageCollector?.report(
|
||||
STRONG_WARNING,
|
||||
"Using new faster version of JAR FS: it should make your build faster, but the new implementation is experimental"
|
||||
)
|
||||
}
|
||||
|
||||
jarFileSystem = when {
|
||||
configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM) || configuration.getBoolean(CommonConfigurationKeys.USE_FIR) -> {
|
||||
val fastJarFs = FastJarFileSystem.createIfUnmappingPossible()
|
||||
|
||||
if (fastJarFs == null) {
|
||||
messageCollector?.report(
|
||||
STRONG_WARNING,
|
||||
"Your JDK doesn't seem to support mapped buffer unmapping, so the slower (old) version of JAR FS will be used"
|
||||
)
|
||||
applicationEnvironment.jarFileSystem
|
||||
} else {
|
||||
|
||||
Disposer.register(disposable) {
|
||||
fastJarFs.clearHandlersCache()
|
||||
}
|
||||
|
||||
fastJarFs
|
||||
}
|
||||
}
|
||||
|
||||
else -> applicationEnvironment.jarFileSystem
|
||||
}
|
||||
}
|
||||
|
||||
private var extensionRegistered = false
|
||||
|
||||
override fun preregisterServices() {
|
||||
@@ -205,13 +167,6 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
if (configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM)) {
|
||||
messageCollector?.report(
|
||||
STRONG_WARNING,
|
||||
"Using new faster version of JAR FS: it should make your build faster, but the new implementation is experimental"
|
||||
)
|
||||
}
|
||||
|
||||
(projectEnvironment as? ProjectEnvironment)?.registerExtensionsFromPlugins(configuration)
|
||||
// otherwise consider that project environment is properly configured before passing to the environment
|
||||
// TODO: consider some asserts to check important extension points
|
||||
@@ -267,14 +222,11 @@ class KotlinCoreEnvironment private constructor(
|
||||
this.initialRoots.addAll(initialRoots)
|
||||
|
||||
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
|
||||
/*
|
||||
// Temporarily disable until compiler is bootstrapped to 1.6.
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
*/
|
||||
}
|
||||
|
||||
val (roots, singleJavaFileRoots) =
|
||||
@@ -439,7 +391,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
}
|
||||
|
||||
private fun findJarRoot(file: File): VirtualFile? =
|
||||
projectEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
|
||||
private fun getSourceRootsCheckingForDuplicates(): List<KotlinSourceRoot> {
|
||||
val uniqueSourceRoots = hashSetOf<String>()
|
||||
@@ -465,22 +417,17 @@ class KotlinCoreEnvironment private constructor(
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinCoreEnvironment::class.java)
|
||||
|
||||
@PublishedApi
|
||||
internal val APPLICATION_LOCK = Object()
|
||||
|
||||
private val APPLICATION_LOCK = Object()
|
||||
private var ourApplicationEnvironment: KotlinCoreApplicationEnvironment? = null
|
||||
private var ourProjectCount = 0
|
||||
|
||||
inline fun <R> underApplicationLock(action: () -> R): R =
|
||||
synchronized(APPLICATION_LOCK) { action() }
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
setupIdeaStandaloneExecution()
|
||||
val appEnv = getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles)
|
||||
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
@@ -491,7 +438,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
projectEnvironment: ProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
projectEnvironment: JavaCoreProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val environment = KotlinCoreEnvironment(projectEnvironment, configuration, configFiles)
|
||||
|
||||
@@ -512,7 +459,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
val configuration = initialConfiguration.copy()
|
||||
// Tests are supposed to create a single project and dispose it right after use
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
@@ -527,7 +474,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
@TestOnly
|
||||
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
return ProjectEnvironment(parentDisposable, appEnv, configuration)
|
||||
return ProjectEnvironment(parentDisposable, appEnv)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
@@ -546,14 +493,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (ourApplicationEnvironment == null) {
|
||||
val disposable = if (unitTestMode) {
|
||||
parentDisposable
|
||||
} else {
|
||||
// TODO this is a memory leak in the compiler, as this Disposable is not registered and thus is never disposed
|
||||
// but using parentDisposable as disposable in compiler, causes access to application extension points after
|
||||
// they was disposed, this needs to be fixed
|
||||
Disposer.newDisposable("Disposable for the KotlinCoreApplicationEnvironment")
|
||||
}
|
||||
val disposable = Disposer.newDisposable()
|
||||
ourApplicationEnvironment = createApplicationEnvironment(disposable, configuration, unitTestMode)
|
||||
ourProjectCount = 0
|
||||
Disposer.register(disposable, Disposable {
|
||||
@@ -598,6 +538,11 @@ class KotlinCoreEnvironment private constructor(
|
||||
val applicationEnvironment = KotlinCoreApplicationEnvironment.create(parentDisposable, unitTestMode)
|
||||
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
|
||||
// FIX ME WHEN BUNCH 202 REMOVED: this code is required to support compiler bundled to both 202 and 203.
|
||||
// Please, remove "com.intellij.psi.classFileDecompiler" EP registration once 202 is no longer supported by the compiler
|
||||
if (!ApplicationManager.getApplication().extensionArea.hasExtensionPoint("com.intellij.psi.classFileDecompiler")) {
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/core.xml")
|
||||
}
|
||||
|
||||
registerApplicationServicesForCLI(applicationEnvironment)
|
||||
registerApplicationServices(applicationEnvironment)
|
||||
|
||||
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.codeInsight.InferredAnnotationsManager
|
||||
import com.intellij.core.CoreApplicationEnvironment
|
||||
import com.intellij.core.CoreJavaFileManager
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.lang.java.JavaParserDefinition
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.TransactionGuard
|
||||
import com.intellij.openapi.application.TransactionGuardImpl
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.extensions.ExtensionsArea
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType
|
||||
import com.intellij.openapi.project.DumbUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.JavaClassSupersImpl
|
||||
import com.intellij.psi.impl.PsiElementFinderImpl
|
||||
import com.intellij.psi.impl.PsiTreeChangePreprocessor
|
||||
import com.intellij.psi.impl.file.impl.JavaFileManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.JavaClassSupers
|
||||
import com.intellij.util.io.URLUtil
|
||||
import com.intellij.util.lang.UrlClassLoader
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.classes.FacadeCache
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
|
||||
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
|
||||
import org.jetbrains.kotlin.cli.common.config.ContentRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ScriptEvaluationExtension
|
||||
import org.jetbrains.kotlin.cli.common.extensions.ShellExtension
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.toBooleanLenient
|
||||
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.cli.jvm.index.*
|
||||
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleResolver
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.extensions.*
|
||||
import org.jetbrains.kotlin.extensions.internal.CandidateInterceptor
|
||||
import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache
|
||||
import org.jetbrains.kotlin.load.kotlin.MetadataFinderFactory
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
|
||||
import org.jetbrains.kotlin.resolve.ModuleAnnotationsResolver
|
||||
import org.jetbrains.kotlin.resolve.extensions.ExtraImportsProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.SyntheticJavaResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class KotlinCoreEnvironment private constructor(
|
||||
val projectEnvironment: JavaCoreProjectEnvironment,
|
||||
initialConfiguration: CompilerConfiguration,
|
||||
configFiles: EnvironmentConfigFiles
|
||||
) {
|
||||
|
||||
class ProjectEnvironment(
|
||||
disposable: Disposable, applicationEnvironment: KotlinCoreApplicationEnvironment
|
||||
) :
|
||||
KotlinCoreProjectEnvironment(disposable, applicationEnvironment) {
|
||||
|
||||
private var extensionRegistered = false
|
||||
|
||||
override fun preregisterServices() {
|
||||
registerProjectExtensionPoints(project.extensionArea)
|
||||
}
|
||||
|
||||
fun registerExtensionsFromPlugins(configuration: CompilerConfiguration) {
|
||||
if (!extensionRegistered) {
|
||||
registerPluginExtensionPoints(project)
|
||||
registerExtensionsFromPlugins(project, configuration)
|
||||
extensionRegistered = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerJavaPsiFacade() {
|
||||
with(project) {
|
||||
registerService(
|
||||
CoreJavaFileManager::class.java,
|
||||
ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager
|
||||
)
|
||||
|
||||
registerKotlinLightClassSupport(project)
|
||||
|
||||
registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager())
|
||||
registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager())
|
||||
}
|
||||
|
||||
super.registerJavaPsiFacade()
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceFiles = mutableListOf<KtFile>()
|
||||
private val rootsIndex: JvmDependenciesDynamicCompoundIndex
|
||||
private val packagePartProviders = mutableListOf<JvmPackagePartProvider>()
|
||||
|
||||
private val classpathRootsResolver: ClasspathRootsResolver
|
||||
private val initialRoots = ArrayList<JavaRoot>()
|
||||
|
||||
val configuration: CompilerConfiguration = initialConfiguration.apply { setupJdkClasspathRoots(configFiles) }.copy()
|
||||
|
||||
init {
|
||||
PersistentFSConstants::class.java.getDeclaredField("ourMaxIntellisenseFileSize")
|
||||
.apply { isAccessible = true }
|
||||
.setInt(null, FileUtilRt.LARGE_FOR_CONTENT_LOADING)
|
||||
|
||||
val project = projectEnvironment.project
|
||||
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
(projectEnvironment as? ProjectEnvironment)?.registerExtensionsFromPlugins(configuration)
|
||||
// otherwise consider that project environment is properly configured before passing to the environment
|
||||
// TODO: consider some asserts to check important extension points
|
||||
|
||||
project.registerService(DeclarationProviderFactoryService::class.java, CliDeclarationProviderFactoryService(sourceFiles))
|
||||
|
||||
val isJvm = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
project.registerService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl(isJvm))
|
||||
|
||||
registerProjectServicesForCLI(projectEnvironment)
|
||||
|
||||
registerProjectServices(projectEnvironment.project)
|
||||
|
||||
for (extension in CompilerConfigurationExtension.getInstances(project)) {
|
||||
extension.updateConfiguration(configuration)
|
||||
}
|
||||
|
||||
sourceFiles += createKtFiles(project)
|
||||
|
||||
collectAdditionalSources(project)
|
||||
|
||||
sourceFiles.sortBy { it.virtualFile.path }
|
||||
|
||||
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val javaModuleFinder = CliJavaModuleFinder(
|
||||
jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
},
|
||||
javaFileManager
|
||||
)
|
||||
|
||||
val outputDirectory =
|
||||
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||
?: configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)?.absolutePath
|
||||
|
||||
classpathRootsResolver = ClasspathRootsResolver(
|
||||
PsiManager.getInstance(project),
|
||||
messageCollector,
|
||||
configuration.getList(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES),
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder,
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||
outputDirectory?.let(this::findLocalFile),
|
||||
javaFileManager
|
||||
)
|
||||
|
||||
val (initialRoots, javaModules) =
|
||||
classpathRootsResolver.convertClasspathRoots(configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS))
|
||||
this.initialRoots.addAll(initialRoots)
|
||||
|
||||
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
}
|
||||
|
||||
val (roots, singleJavaFileRoots) =
|
||||
initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
|
||||
|
||||
// REPL and kapt2 update classpath dynamically
|
||||
rootsIndex = JvmDependenciesDynamicCompoundIndex().apply {
|
||||
addIndex(JvmDependenciesIndexImpl(roots))
|
||||
updateClasspathFromRootsIndex(this)
|
||||
}
|
||||
|
||||
javaFileManager.initialize(
|
||||
rootsIndex,
|
||||
packagePartProviders,
|
||||
SingleJavaFileRootsIndex(singleJavaFileRoots),
|
||||
configuration.getBoolean(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING)
|
||||
)
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList(), project)
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
project.registerService(MetadataFinderFactory::class.java, finderFactory)
|
||||
project.registerService(VirtualFileFinderFactory::class.java, finderFactory)
|
||||
|
||||
project.putUserData(APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY, fun(roots: List<File>) {
|
||||
updateClasspath(roots.map { JavaSourceRoot(it, null) })
|
||||
})
|
||||
}
|
||||
|
||||
private fun collectAdditionalSources(project: MockProject) {
|
||||
var unprocessedSources: Collection<KtFile> = sourceFiles
|
||||
val processedSources = HashSet<KtFile>()
|
||||
val processedSourcesByExtension = HashMap<CollectAdditionalSourcesExtension, Collection<KtFile>>()
|
||||
// repeat feeding extensions with sources while new sources a being added
|
||||
var sourceCollectionIterations = 0
|
||||
while (unprocessedSources.isNotEmpty()) {
|
||||
if (sourceCollectionIterations++ > 10) { // TODO: consider using some appropriate global constant
|
||||
throw IllegalStateException("Unable to collect additional sources in reasonable number of iterations")
|
||||
}
|
||||
processedSources.addAll(unprocessedSources)
|
||||
val allNewSources = ArrayList<KtFile>()
|
||||
for (extension in CollectAdditionalSourcesExtension.getInstances(project)) {
|
||||
// do not feed the extension with the sources it returned on the previous iteration
|
||||
val sourcesToProcess = unprocessedSources - (processedSourcesByExtension[extension] ?: emptyList())
|
||||
val newSources = extension.collectAdditionalSourcesAndUpdateConfiguration(sourcesToProcess, configuration, project)
|
||||
if (newSources.isNotEmpty()) {
|
||||
allNewSources.addAll(newSources)
|
||||
processedSourcesByExtension[extension] = newSources
|
||||
}
|
||||
}
|
||||
unprocessedSources = allNewSources.filterNot { processedSources.contains(it) }.distinct()
|
||||
sourceFiles += unprocessedSources
|
||||
}
|
||||
}
|
||||
|
||||
fun addKotlinSourceRoots(rootDirs: List<File>) {
|
||||
val roots = rootDirs.map { KotlinSourceRoot(it.absolutePath, isCommon = false) }
|
||||
sourceFiles += createSourceFilesFromSourceRoots(configuration, project, roots)
|
||||
}
|
||||
|
||||
fun createPackagePartProvider(scope: GlobalSearchScope): JvmPackagePartProvider {
|
||||
return JvmPackagePartProvider(configuration.languageVersionSettings, scope).apply {
|
||||
addRoots(initialRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
|
||||
packagePartProviders += this
|
||||
(ModuleAnnotationsResolver.getInstance(project) as CliModuleAnnotationsResolver).addPackagePartProvider(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val VirtualFile.javaFiles: List<VirtualFile>
|
||||
get() = mutableListOf<VirtualFile>().apply {
|
||||
VfsUtilCore.processFilesRecursively(this@javaFiles) { file ->
|
||||
if (file.fileType == JavaFileType.INSTANCE) {
|
||||
add(file)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private val allJavaFiles: List<File>
|
||||
get() = configuration.javaSourceRoots
|
||||
.mapNotNull(this::findLocalFile)
|
||||
.flatMap { it.javaFiles }
|
||||
.map { File(it.canonicalPath) }
|
||||
|
||||
fun registerJavac(
|
||||
javaFiles: List<File> = allJavaFiles,
|
||||
kotlinFiles: List<KtFile> = sourceFiles,
|
||||
arguments: Array<String>? = null,
|
||||
bootClasspath: List<File>? = null,
|
||||
sourcePath: List<File>? = null
|
||||
): Boolean {
|
||||
return JavacWrapperRegistrar.registerJavac(
|
||||
projectEnvironment.project, configuration, javaFiles, kotlinFiles, arguments, bootClasspath, sourcePath,
|
||||
LightClassGenerationSupport.getInstance(project), packagePartProviders
|
||||
)
|
||||
}
|
||||
|
||||
private val applicationEnvironment: CoreApplicationEnvironment
|
||||
get() = projectEnvironment.environment
|
||||
|
||||
val project: Project
|
||||
get() = projectEnvironment.project
|
||||
|
||||
internal fun countLinesOfCode(sourceFiles: List<KtFile>): Int =
|
||||
sourceFiles.sumBy { sourceFile ->
|
||||
val text = sourceFile.text
|
||||
StringUtil.getLineBreakCount(text) + (if (StringUtil.endsWithLineBreak(text)) 0 else 1)
|
||||
}
|
||||
|
||||
private fun updateClasspathFromRootsIndex(index: JvmDependenciesIndex) {
|
||||
index.indexedRoots.forEach {
|
||||
projectEnvironment.addSourcesToClasspath(it.file)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateClasspath(contentRoots: List<ContentRoot>): List<File>? {
|
||||
// TODO: add new Java modules to CliJavaModuleResolver
|
||||
val newRoots = classpathRootsResolver.convertClasspathRoots(contentRoots).roots
|
||||
|
||||
if (packagePartProviders.isEmpty()) {
|
||||
initialRoots.addAll(newRoots)
|
||||
} else {
|
||||
for (packagePartProvider in packagePartProviders) {
|
||||
packagePartProvider.addRoots(newRoots, configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY))
|
||||
}
|
||||
}
|
||||
|
||||
return rootsIndex.addNewIndexForRoots(newRoots)?.let { newIndex ->
|
||||
updateClasspathFromRootsIndex(newIndex)
|
||||
newIndex.indexedRoots.mapNotNull { (file) ->
|
||||
VfsUtilCore.virtualToIoFile(VfsUtilCore.getVirtualFileForJar(file) ?: file)
|
||||
}.toList()
|
||||
}.orEmpty()
|
||||
}
|
||||
|
||||
private fun contentRootToVirtualFile(root: JvmContentRoot): VirtualFile? =
|
||||
when (root) {
|
||||
is JvmClasspathRoot ->
|
||||
if (root.file.isFile) findJarRoot(root.file) else findExistingRoot(root, "Classpath entry")
|
||||
is JvmModulePathRoot ->
|
||||
if (root.file.isFile) findJarRoot(root.file) else findExistingRoot(root, "Java module root")
|
||||
is JavaSourceRoot ->
|
||||
findExistingRoot(root, "Java source root")
|
||||
else ->
|
||||
throw IllegalStateException("Unexpected root: $root")
|
||||
}
|
||||
|
||||
internal fun findLocalFile(path: String): VirtualFile? =
|
||||
applicationEnvironment.localFileSystem.findFileByPath(path)
|
||||
|
||||
private fun findExistingRoot(root: JvmContentRoot, rootDescription: String): VirtualFile? {
|
||||
return findLocalFile(root.file.absolutePath).also {
|
||||
if (it == null) {
|
||||
report(STRONG_WARNING, "$rootDescription points to a non-existent location: ${root.file}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findJarRoot(file: File): VirtualFile? =
|
||||
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
|
||||
private fun getSourceRootsCheckingForDuplicates(): List<KotlinSourceRoot> {
|
||||
val uniqueSourceRoots = hashSetOf<String>()
|
||||
val result = mutableListOf<KotlinSourceRoot>()
|
||||
|
||||
for (root in configuration.kotlinSourceRoots) {
|
||||
if (!uniqueSourceRoots.add(root.path)) {
|
||||
report(STRONG_WARNING, "Duplicate source root: ${root.path}")
|
||||
}
|
||||
result.add(root)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun getSourceFiles(): List<KtFile> = sourceFiles
|
||||
|
||||
private fun createKtFiles(project: Project): List<KtFile> =
|
||||
createSourceFilesFromSourceRoots(configuration, project, getSourceRootsCheckingForDuplicates())
|
||||
|
||||
internal fun report(severity: CompilerMessageSeverity, message: String) = configuration.report(severity, message)
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(KotlinCoreEnvironment::class.java)
|
||||
|
||||
private val APPLICATION_LOCK = Object()
|
||||
private var ourApplicationEnvironment: KotlinCoreApplicationEnvironment? = null
|
||||
private var ourProjectCount = 0
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
setupIdeaStandaloneExecution()
|
||||
val appEnv = getOrCreateApplicationEnvironmentForProduction(parentDisposable, configuration)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
val environment = KotlinCoreEnvironment(projectEnv, configuration, configFiles)
|
||||
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourProjectCount++
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun createForProduction(
|
||||
projectEnvironment: JavaCoreProjectEnvironment, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val environment = KotlinCoreEnvironment(projectEnvironment, configuration, configFiles)
|
||||
|
||||
if (projectEnvironment.environment == applicationEnvironment) {
|
||||
// accounting for core environment disposing
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourProjectCount++
|
||||
}
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
val configuration = initialConfiguration.copy()
|
||||
// Tests are supposed to create a single project and dispose it right after use
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
val projectEnv = ProjectEnvironment(parentDisposable, appEnv)
|
||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@JvmStatic
|
||||
fun createForTests(
|
||||
projectEnvironment: ProjectEnvironment, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
|
||||
): KotlinCoreEnvironment {
|
||||
return KotlinCoreEnvironment(projectEnvironment, initialConfiguration, extensionConfigs)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
|
||||
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
return ProjectEnvironment(parentDisposable, appEnv)
|
||||
}
|
||||
|
||||
// used in the daemon for jar cache cleanup
|
||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||
|
||||
fun getOrCreateApplicationEnvironmentForProduction(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration
|
||||
): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment(parentDisposable, configuration, unitTestMode = false)
|
||||
|
||||
fun getOrCreateApplicationEnvironmentForTests(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration
|
||||
): KotlinCoreApplicationEnvironment = getOrCreateApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
|
||||
|
||||
private fun getOrCreateApplicationEnvironment(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, unitTestMode: Boolean
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (ourApplicationEnvironment == null) {
|
||||
val disposable = Disposer.newDisposable()
|
||||
ourApplicationEnvironment = createApplicationEnvironment(disposable, configuration, unitTestMode)
|
||||
ourProjectCount = 0
|
||||
Disposer.register(disposable, Disposable {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
ourApplicationEnvironment = null
|
||||
}
|
||||
})
|
||||
}
|
||||
// Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally
|
||||
// breaks a lot of tests, therefore it is disabled for production and enabled for tests
|
||||
if (System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY).toBooleanLenient() != true) {
|
||||
// JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ)
|
||||
// All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well
|
||||
Disposer.register(parentDisposable, Disposable {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
if (--ourProjectCount <= 0) {
|
||||
disposeApplicationEnvironment()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ourApplicationEnvironment!!
|
||||
}
|
||||
}
|
||||
|
||||
private fun disposeApplicationEnvironment() {
|
||||
synchronized(APPLICATION_LOCK) {
|
||||
val environment = ourApplicationEnvironment ?: return
|
||||
ourApplicationEnvironment = null
|
||||
Disposer.dispose(environment.parentDisposable)
|
||||
ZipHandler.clearFileAccessorCache()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createApplicationEnvironment(
|
||||
parentDisposable: Disposable, configuration: CompilerConfiguration, unitTestMode: Boolean
|
||||
): KotlinCoreApplicationEnvironment {
|
||||
val applicationEnvironment = KotlinCoreApplicationEnvironment.create(parentDisposable, unitTestMode)
|
||||
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/compiler.xml")
|
||||
registerApplicationExtensionPointsAndExtensionsFrom(configuration, "extensions/core.xml")
|
||||
|
||||
registerApplicationServicesForCLI(applicationEnvironment)
|
||||
registerApplicationServices(applicationEnvironment)
|
||||
|
||||
return applicationEnvironment
|
||||
}
|
||||
|
||||
private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) {
|
||||
fun File.hasConfigFile(configFile: String): Boolean =
|
||||
if (isDirectory) File(this, "META-INF" + File.separator + configFile).exists()
|
||||
else try {
|
||||
ZipFile(this).use {
|
||||
it.getEntry("META-INF/$configFile") != null
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
false
|
||||
}
|
||||
|
||||
val pluginRoot: File =
|
||||
configuration.get(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT)?.let(::File)
|
||||
?: PathUtil.getResourcePathForClass(this::class.java).takeIf { it.hasConfigFile(configFilePath) }
|
||||
// hack for load extensions when compiler run directly from project directory (e.g. in tests)
|
||||
?: File("compiler/cli/cli-common/resources").takeIf { it.hasConfigFile(configFilePath) }
|
||||
?: throw IllegalStateException(
|
||||
"Unable to find extension point configuration $configFilePath " +
|
||||
"(cp:\n ${(Thread.currentThread().contextClassLoader as? UrlClassLoader)?.urls?.joinToString("\n ") { it.file }})"
|
||||
)
|
||||
|
||||
CoreApplicationEnvironment.registerExtensionPointAndExtensions(
|
||||
FileSystems.getDefault().getPath(pluginRoot.path),
|
||||
configFilePath,
|
||||
ApplicationManager.getApplication().extensionArea
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("MemberVisibilityCanPrivate") // made public for CLI Android Lint
|
||||
fun registerPluginExtensionPoints(project: MockProject) {
|
||||
ExpressionCodegenExtension.registerExtensionPoint(project)
|
||||
SyntheticResolveExtension.registerExtensionPoint(project)
|
||||
SyntheticJavaResolveExtension.registerExtensionPoint(project)
|
||||
ClassBuilderInterceptorExtension.registerExtensionPoint(project)
|
||||
AnalysisHandlerExtension.registerExtensionPoint(project)
|
||||
PackageFragmentProviderExtension.registerExtensionPoint(project)
|
||||
StorageComponentContainerContributor.registerExtensionPoint(project)
|
||||
DeclarationAttributeAltererExtension.registerExtensionPoint(project)
|
||||
PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project)
|
||||
JsSyntheticTranslateExtension.registerExtensionPoint(project)
|
||||
CompilerConfigurationExtension.registerExtensionPoint(project)
|
||||
CollectAdditionalSourcesExtension.registerExtensionPoint(project)
|
||||
ExtraImportsProviderExtension.registerExtensionPoint(project)
|
||||
IrGenerationExtension.registerExtensionPoint(project)
|
||||
ScriptEvaluationExtension.registerExtensionPoint(project)
|
||||
ShellExtension.registerExtensionPoint(project)
|
||||
TypeResolutionInterceptor.registerExtensionPoint(project)
|
||||
CandidateInterceptor.registerExtensionPoint(project)
|
||||
DescriptorSerializerPlugin.registerExtensionPoint(project)
|
||||
}
|
||||
|
||||
internal fun registerExtensionsFromPlugins(project: MockProject, configuration: CompilerConfiguration) {
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||
try {
|
||||
registrar.registerProjectComponents(project, configuration)
|
||||
} catch (e: AbstractMethodError) {
|
||||
val message = "The provided plugin ${registrar.javaClass.name} is not compatible with this version of compiler"
|
||||
// Since the scripting plugin is often discovered in the compiler environment, it is often taken from the incompatible
|
||||
// location, and in many cases this is not a fatal error, therefore strong warning is generated instead of exception
|
||||
if (registrar.javaClass.simpleName == "ScriptingCompilerConfigurationComponentRegistrar") {
|
||||
messageCollector?.report(STRONG_WARNING, "Default scripting plugin is disabled: $message")
|
||||
} else {
|
||||
throw IllegalStateException(message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun registerApplicationServicesForCLI(applicationEnvironment: KotlinCoreApplicationEnvironment) {
|
||||
// ability to get text from annotations xml files
|
||||
applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml")
|
||||
applicationEnvironment.registerParserDefinition(JavaParserDefinition())
|
||||
}
|
||||
|
||||
// made public for Upsource
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@JvmStatic
|
||||
fun registerApplicationServices(applicationEnvironment: KotlinCoreApplicationEnvironment) {
|
||||
with(applicationEnvironment) {
|
||||
registerFileType(KotlinFileType.INSTANCE, "kt")
|
||||
registerFileType(KotlinFileType.INSTANCE, KotlinParserDefinition.STD_SCRIPT_SUFFIX)
|
||||
registerParserDefinition(KotlinParserDefinition())
|
||||
application.registerService(KotlinBinaryClassCache::class.java, KotlinBinaryClassCache())
|
||||
application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
|
||||
application.registerService(TransactionGuard::class.java, TransactionGuardImpl::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun registerProjectExtensionPoints(area: ExtensionsArea) {
|
||||
CoreApplicationEnvironment.registerExtensionPoint(
|
||||
area, PsiTreeChangePreprocessor.EP.name, PsiTreeChangePreprocessor::class.java
|
||||
)
|
||||
CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP.name, PsiElementFinder::class.java)
|
||||
|
||||
IdeaExtensionPoints.registerVersionSpecificProjectExtensionPoints(area)
|
||||
}
|
||||
|
||||
// made public for Upsource
|
||||
@JvmStatic
|
||||
@Deprecated("Use registerProjectServices(project) instead.", ReplaceWith("registerProjectServices(projectEnvironment.project)"))
|
||||
fun registerProjectServices(
|
||||
projectEnvironment: JavaCoreProjectEnvironment,
|
||||
@Suppress("UNUSED_PARAMETER") messageCollector: MessageCollector?
|
||||
) {
|
||||
registerProjectServices(projectEnvironment.project)
|
||||
}
|
||||
|
||||
// made public for Android Lint
|
||||
@JvmStatic
|
||||
fun registerProjectServices(project: MockProject) {
|
||||
with(project) {
|
||||
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
|
||||
registerService(FacadeCache::class.java, FacadeCache(this))
|
||||
registerService(ModuleAnnotationsResolver::class.java, CliModuleAnnotationsResolver())
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerProjectServicesForCLI(@Suppress("UNUSED_PARAMETER") projectEnvironment: JavaCoreProjectEnvironment) {
|
||||
/**
|
||||
* Note that Kapt may restart code analysis process, and CLI services should be aware of that.
|
||||
* Use PsiManager.getModificationTracker() to ensure that all the data you cached is still valid.
|
||||
*/
|
||||
}
|
||||
|
||||
// made public for Android Lint
|
||||
@JvmStatic
|
||||
fun registerKotlinLightClassSupport(project: MockProject) {
|
||||
with(project) {
|
||||
val traceHolder = CliTraceHolder()
|
||||
val cliLightClassGenerationSupport = CliLightClassGenerationSupport(traceHolder, project)
|
||||
val kotlinAsJavaSupport = CliKotlinAsJavaSupport(this, traceHolder)
|
||||
registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(KotlinAsJavaSupport::class.java, kotlinAsJavaSupport)
|
||||
registerService(CodeAnalyzerInitializer::class.java, traceHolder)
|
||||
@Suppress("UnstableApiUsage")
|
||||
registerService(DumbUtil::class.java, KotlinCoreDumbUtil())
|
||||
|
||||
// We don't pass Disposable because in some tests, we manually unregister these extensions, and that leads to LOG.error
|
||||
// exception from `ExtensionPointImpl.doRegisterExtension`, because the registered extension can no longer be found
|
||||
// when the project is being disposed.
|
||||
// For example, see the `unregisterExtension` call in `GenerationUtils.compileFilesUsingFrontendIR`.
|
||||
// TODO: refactor this to avoid registering unneeded extensions in the first place, and avoid using deprecated API.
|
||||
@Suppress("DEPRECATION")
|
||||
PsiElementFinder.EP.getPoint(project).registerExtension(JavaElementFinder(this))
|
||||
@Suppress("DEPRECATION")
|
||||
PsiElementFinder.EP.getPoint(project).registerExtension(PsiElementFinderImpl(this))
|
||||
}
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.setupJdkClasspathRoots(configFiles: EnvironmentConfigFiles) {
|
||||
if (getBoolean(JVMConfigurationKeys.NO_JDK)) return
|
||||
|
||||
val jvmTarget = configFiles == EnvironmentConfigFiles.JVM_CONFIG_FILES
|
||||
if (!jvmTarget) return
|
||||
|
||||
val jdkHome = get(JVMConfigurationKeys.JDK_HOME)
|
||||
val (javaRoot, classesRoots) = if (jdkHome == null) {
|
||||
val javaHome = File(System.getProperty("java.home"))
|
||||
put(JVMConfigurationKeys.JDK_HOME, javaHome)
|
||||
|
||||
javaHome to PathUtil.getJdkClassesRootsFromCurrentJre()
|
||||
} else {
|
||||
jdkHome to PathUtil.getJdkClassesRoots(jdkHome)
|
||||
}
|
||||
|
||||
if (!CoreJrtFileSystem.isModularJdk(javaRoot)) {
|
||||
if (classesRoots.isEmpty()) {
|
||||
report(ERROR, "No class roots are found in the JDK path: $javaRoot")
|
||||
} else {
|
||||
addJvmSdkRoots(classesRoots)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +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
|
||||
|
||||
import com.intellij.openapi.util.Couple
|
||||
import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||
import com.intellij.util.io.FileAccessorCache
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.MappedByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
private typealias RandomAccessFileAndBuffer = Pair<RandomAccessFile, MappedByteBuffer>
|
||||
|
||||
class FastJarFileSystem private constructor(internal val unmapBuffer: MappedByteBuffer.() -> Unit) : DeprecatedVirtualFileSystem() {
|
||||
private val myHandlers: MutableMap<String, FastJarHandler> =
|
||||
ConcurrentFactoryMap.createMap { key: String -> FastJarHandler(this@FastJarFileSystem, key) }
|
||||
|
||||
internal val cachedOpenFileHandles: FileAccessorCache<File, RandomAccessFileAndBuffer> =
|
||||
object : FileAccessorCache<File, RandomAccessFileAndBuffer>(20, 10) {
|
||||
@Throws(IOException::class)
|
||||
override fun createAccessor(file: File): RandomAccessFileAndBuffer {
|
||||
val randomAccessFile = RandomAccessFile(file, "r")
|
||||
return Pair(randomAccessFile, randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()))
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun disposeAccessor(fileAccessor: RandomAccessFileAndBuffer) {
|
||||
fileAccessor.first.close()
|
||||
fileAccessor.second.unmapBuffer()
|
||||
}
|
||||
|
||||
override fun isEqual(val1: File, val2: File): Boolean {
|
||||
return val1 == val2 // reference equality to handle different jars for different ZipHandlers on the same path
|
||||
}
|
||||
}
|
||||
|
||||
override fun getProtocol(): String {
|
||||
return StandardFileSystems.JAR_PROTOCOL
|
||||
}
|
||||
|
||||
override fun findFileByPath(path: String): VirtualFile? {
|
||||
val pair = splitPath(path)
|
||||
return myHandlers[pair.first]!!.findFileByPath(pair.second)
|
||||
}
|
||||
|
||||
override fun refresh(asynchronous: Boolean) {}
|
||||
override fun refreshAndFindFileByPath(path: String): VirtualFile? {
|
||||
return findFileByPath(path)
|
||||
}
|
||||
|
||||
fun clearHandlersCache() {
|
||||
myHandlers.clear()
|
||||
cachedOpenFileHandles.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun splitPath(path: String): Couple<String> {
|
||||
val separator = path.indexOf("!/")
|
||||
require(separator >= 0) { "Path in JarFileSystem must contain a separator: $path" }
|
||||
val localPath = path.substring(0, separator)
|
||||
val pathInJar = path.substring(separator + 2)
|
||||
return Couple.of(localPath, pathInJar)
|
||||
}
|
||||
|
||||
fun createIfUnmappingPossible(): FastJarFileSystem? {
|
||||
val cleanerCallBack = prepareCleanerCallback() ?: return null
|
||||
return FastJarFileSystem(cleanerCallBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val IS_PRIOR_9_JRE = System.getProperty("java.specification.version", "").startsWith("1.")
|
||||
|
||||
private fun prepareCleanerCallback(): ((ByteBuffer) -> Unit)? {
|
||||
return try {
|
||||
if (IS_PRIOR_9_JRE) {
|
||||
val cleaner = Class.forName("java.nio.DirectByteBuffer").getMethod("cleaner")
|
||||
cleaner.isAccessible = true
|
||||
|
||||
val clean = Class.forName("sun.misc.Cleaner").getMethod("clean")
|
||||
clean.isAccessible = true
|
||||
|
||||
{ buffer: ByteBuffer -> clean.invoke(cleaner.invoke(buffer)) }
|
||||
} else {
|
||||
val unsafeClass = try {
|
||||
Class.forName("sun.misc.Unsafe")
|
||||
} catch (ex: Exception) {
|
||||
// jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
|
||||
// but that method should be added if sun.misc.Unsafe is removed.
|
||||
Class.forName("jdk.internal.misc.Unsafe")
|
||||
}
|
||||
|
||||
val clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer::class.java)
|
||||
clean.isAccessible = true
|
||||
|
||||
val theUnsafeField = unsafeClass.getDeclaredField("theUnsafe")
|
||||
theUnsafeField.isAccessible = true
|
||||
|
||||
val theUnsafe = theUnsafeField.get(null);
|
||||
|
||||
{ buffer: ByteBuffer -> clean.invoke(theUnsafe, buffer) }
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -1,107 +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
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) {
|
||||
private val myRoot: VirtualFile?
|
||||
internal val file = File(path)
|
||||
|
||||
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)
|
||||
} finally {
|
||||
with(fileSystem) {
|
||||
mappedByteBuffer.unmapBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myRoot = FastJarVirtualFile(this, "", -1, parent = null, entryDescription = 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)
|
||||
filesByRelativePath[""] = myRoot
|
||||
|
||||
for (entryDescription in entries) {
|
||||
if (!entryDescription.isDirectory) {
|
||||
createFile(entryDescription, filesByRelativePath)
|
||||
} else {
|
||||
getOrCreateDirectory(entryDescription.relativePath, filesByRelativePath)
|
||||
}
|
||||
}
|
||||
|
||||
for (node in filesByRelativePath.values) {
|
||||
node.initChildrenArrayFromList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFile(entry: ZipEntryDescription, directories: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
|
||||
val (parentName, shortName) = entry.relativePath.splitPath()
|
||||
|
||||
val parentFile = getOrCreateDirectory(parentName, directories)
|
||||
if ("." == shortName) {
|
||||
return parentFile
|
||||
}
|
||||
|
||||
return FastJarVirtualFile(
|
||||
this, shortName,
|
||||
if (entry.isDirectory) -1 else entry.uncompressedSize,
|
||||
parentFile,
|
||||
entry,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getOrCreateDirectory(entryName: CharSequence, directories: MutableMap<String, FastJarVirtualFile>): FastJarVirtualFile {
|
||||
return directories.getOrPut(entryName.toString()) {
|
||||
val (parentPath, shortName) = entryName.splitPath()
|
||||
val parentFile = getOrCreateDirectory(parentPath, directories)
|
||||
|
||||
FastJarVirtualFile(this, shortName, -1, parentFile, entryDescription = null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CharSequence.splitPath(): Pair<CharSequence, CharSequence> {
|
||||
var slashIndex = this.length - 1
|
||||
|
||||
while (slashIndex >= 0 && this[slashIndex] != '/') {
|
||||
slashIndex--
|
||||
}
|
||||
|
||||
if (slashIndex == -1) return Pair("", this)
|
||||
return Pair(subSequence(0, slashIndex), subSequence(slashIndex + 1, this.length))
|
||||
}
|
||||
|
||||
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")
|
||||
return fileSystem.cachedOpenFileHandles[file].use {
|
||||
synchronized(it) {
|
||||
it.get().second.contentsToByteArray(zipEntryDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
|
||||
@@ -1,108 +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
|
||||
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import java.io.IOException
|
||||
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?,
|
||||
) : VirtualFile() {
|
||||
|
||||
private var myChildrenArray = EMPTY_ARRAY
|
||||
private val myChildrenList: MutableList<VirtualFile> = mutableListOf()
|
||||
|
||||
init {
|
||||
parent?.myChildrenList?.add(this)
|
||||
}
|
||||
|
||||
fun initChildrenArrayFromList() {
|
||||
myChildrenArray = myChildrenList.toTypedArray()
|
||||
myChildrenList.clear()
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return name.toString()
|
||||
}
|
||||
|
||||
override fun getNameSequence(): CharSequence {
|
||||
return name
|
||||
}
|
||||
|
||||
override fun getFileSystem(): VirtualFileSystem {
|
||||
return handler.fileSystem
|
||||
}
|
||||
|
||||
override fun getPath(): String {
|
||||
if (parent == null) {
|
||||
return FileUtil.toSystemIndependentName(handler.file.path) + "!/"
|
||||
}
|
||||
val parentPath = parent.path
|
||||
val answer = StringBuilder(parentPath.length + 1 + name.length)
|
||||
answer.append(parentPath)
|
||||
if (answer[answer.length - 1] != '/') {
|
||||
answer.append('/')
|
||||
}
|
||||
answer.append(name)
|
||||
return answer.toString()
|
||||
}
|
||||
|
||||
override fun isWritable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isDirectory(): Boolean {
|
||||
return length < 0
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getParent(): VirtualFile? {
|
||||
return parent
|
||||
}
|
||||
|
||||
override fun getChildren(): Array<VirtualFile> {
|
||||
return myChildrenArray
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun getOutputStream(requestor: Any, newModificationStamp: Long, newTimeStamp: Long): OutputStream {
|
||||
throw UnsupportedOperationException("JarFileSystem is read-only")
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentsToByteArray(): ByteArray {
|
||||
if (entryDescription == null) return EMPTY_BYTE_ARRAY
|
||||
return handler.contentsToByteArray(entryDescription)
|
||||
}
|
||||
|
||||
override fun getTimeStamp(): Long = 0
|
||||
|
||||
override fun getLength(): Long = length.toLong()
|
||||
|
||||
override fun refresh(asynchronous: Boolean, recursive: Boolean, postRunnable: Runnable?) {}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun getInputStream(): InputStream {
|
||||
return BufferExposingByteArrayInputStream(contentsToByteArray())
|
||||
}
|
||||
|
||||
override fun getModificationStamp(): Long {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
private val EMPTY_BYTE_ARRAY = ByteArray(0)
|
||||
@@ -1,129 +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
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.MappedByteBuffer
|
||||
import java.util.zip.Inflater
|
||||
|
||||
|
||||
class ZipEntryDescription(
|
||||
val relativePath: CharSequence,
|
||||
val compressedSize: Int,
|
||||
val uncompressedSize: Int,
|
||||
val offsetInFile: Int,
|
||||
val compressionKind: CompressionKind,
|
||||
val fileNameSize: Int,
|
||||
) {
|
||||
|
||||
enum class CompressionKind {
|
||||
PLAIN, DEFLATE
|
||||
}
|
||||
|
||||
val isDirectory get() = uncompressedSize == 0
|
||||
}
|
||||
|
||||
private const val END_OF_CENTRAL_DIR_SIZE = 22
|
||||
private const val LOCAL_FILE_HEADER_EXTRA_OFFSET = 28
|
||||
private const val LOCAL_FILE_HEADER_SIZE = LOCAL_FILE_HEADER_EXTRA_OFFSET + 2
|
||||
|
||||
fun MappedByteBuffer.contentsToByteArray(
|
||||
zipEntryDescription: ZipEntryDescription
|
||||
): ByteArray {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
val extraSize =
|
||||
getUnsignedShort(zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_EXTRA_OFFSET)
|
||||
|
||||
position(
|
||||
zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_SIZE + zipEntryDescription.fileNameSize + extraSize
|
||||
)
|
||||
val compressed = ByteArray(zipEntryDescription.compressedSize + 1)
|
||||
get(compressed, 0, zipEntryDescription.compressedSize)
|
||||
|
||||
return when (zipEntryDescription.compressionKind) {
|
||||
ZipEntryDescription.CompressionKind.DEFLATE -> {
|
||||
val inflater = Inflater(true)
|
||||
inflater.setInput(compressed, 0, zipEntryDescription.compressedSize)
|
||||
|
||||
val result = ByteArray(zipEntryDescription.uncompressedSize)
|
||||
|
||||
inflater.inflate(result)
|
||||
|
||||
result
|
||||
}
|
||||
ZipEntryDescription.CompressionKind.PLAIN -> compressed.copyOf(zipEntryDescription.compressedSize)
|
||||
}
|
||||
}
|
||||
|
||||
fun MappedByteBuffer.parseCentralDirectory(): List<ZipEntryDescription> {
|
||||
order(ByteOrder.LITTLE_ENDIAN)
|
||||
|
||||
var endOfCentralDirectoryOffset = capacity() - END_OF_CENTRAL_DIR_SIZE
|
||||
while (endOfCentralDirectoryOffset >= 0) {
|
||||
// header of "End of central directory"
|
||||
if (getInt(endOfCentralDirectoryOffset) == 0x06054b50) break
|
||||
endOfCentralDirectoryOffset--
|
||||
}
|
||||
|
||||
val entriesNumber = getUnsignedShort(endOfCentralDirectoryOffset + 10)
|
||||
val offsetOfCentralDirectory = getInt(endOfCentralDirectoryOffset + 16)
|
||||
|
||||
var currentOffset = offsetOfCentralDirectory
|
||||
|
||||
val result = mutableListOf<ZipEntryDescription>()
|
||||
for (i in 0 until entriesNumber) {
|
||||
val headerConst = getInt(currentOffset)
|
||||
require(headerConst == 0x02014b50) {
|
||||
"$i: $headerConst"
|
||||
}
|
||||
|
||||
val versionNeededToExtract =
|
||||
getShort(currentOffset + 6).toInt()
|
||||
|
||||
val compressionMethod = getShort(currentOffset + 10).toInt()
|
||||
|
||||
val compressedSize = getInt(currentOffset + 20)
|
||||
val uncompressedSize = getInt(currentOffset + 24)
|
||||
val fileNameLength = getUnsignedShort(currentOffset + 28).toInt()
|
||||
val extraLength = getUnsignedShort(currentOffset + 30).toInt()
|
||||
val fileCommentLength = getUnsignedShort(currentOffset + 32).toInt()
|
||||
|
||||
val offsetOfFileData = getInt(currentOffset + 42)
|
||||
|
||||
val bytesForName = ByteArray(fileNameLength)
|
||||
|
||||
position(currentOffset + 46)
|
||||
get(bytesForName)
|
||||
|
||||
val name =
|
||||
if (bytesForName.all { it >= 0 })
|
||||
ByteArrayCharSequence(bytesForName)
|
||||
else
|
||||
String(bytesForName, Charsets.UTF_8)
|
||||
|
||||
currentOffset += 46 + fileNameLength + extraLength + fileCommentLength
|
||||
|
||||
require(versionNeededToExtract == 10 || versionNeededToExtract == 20) {
|
||||
"Unexpected versionNeededToExtract ($versionNeededToExtract) at $name"
|
||||
}
|
||||
|
||||
val compressionKind = when (compressionMethod) {
|
||||
0 -> ZipEntryDescription.CompressionKind.PLAIN
|
||||
8 -> ZipEntryDescription.CompressionKind.DEFLATE
|
||||
else -> error("Unexpected compression method ($compressionMethod) at $name")
|
||||
}
|
||||
|
||||
result += ZipEntryDescription(
|
||||
name, compressedSize, uncompressedSize, offsetOfFileData, compressionKind,
|
||||
fileNameLength
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ByteBuffer.getUnsignedShort(offset: Int): Int = java.lang.Short.toUnsignedInt(getShort(offset))
|
||||
@@ -254,9 +254,6 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
|
||||
|
||||
put(JVMConfigurationKeys.SERIALIZE_IR, arguments.serializeIr)
|
||||
|
||||
put(JVMConfigurationKeys.VALIDATE_IR, arguments.validateIr)
|
||||
put(JVMConfigurationKeys.VALIDATE_BYTECODE, arguments.validateBytecode)
|
||||
|
||||
if (!JVMConstructorCallNormalizationMode.isSupportedValue(arguments.constructorCallNormalizationMode)) {
|
||||
messageCollector.report(
|
||||
ERROR,
|
||||
@@ -283,16 +280,11 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
|
||||
put(JVMConfigurationKeys.USE_TYPE_TABLE, arguments.useTypeTable)
|
||||
put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck)
|
||||
put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, arguments.useOldClassFilesReading)
|
||||
put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, arguments.useFastJarFileSystem)
|
||||
|
||||
if (arguments.useOldClassFilesReading) {
|
||||
messageCollector.report(INFO, "Using the old java class files reading implementation")
|
||||
}
|
||||
|
||||
if (arguments.useFastJarFileSystem) {
|
||||
messageCollector.report(INFO, "Using fast Jar FS implementation")
|
||||
}
|
||||
|
||||
put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
|
||||
put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule)
|
||||
put(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS, arguments.useOldSpilledVarTypeAnalysis)
|
||||
|
||||
@@ -99,9 +99,6 @@ public class JVMConfigurationKeys {
|
||||
public static final CompilerConfigurationKey<Boolean> USE_PSI_CLASS_FILES_READING =
|
||||
CompilerConfigurationKey.create("use a slower (PSI-based) class files reading implementation");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> USE_FAST_JAR_FILE_SYSTEM =
|
||||
CompilerConfigurationKey.create("use a faster JAR filesystem implementation");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> USE_JAVAC =
|
||||
CompilerConfigurationKey.create("use javac [experimental]");
|
||||
|
||||
@@ -157,11 +154,5 @@ public class JVMConfigurationKeys {
|
||||
CompilerConfigurationKey.create("Don't automatically include kotlin-reflect.jar into the output if the output is a jar");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> SERIALIZE_IR =
|
||||
CompilerConfigurationKey.create("Serialize IR to class metadata");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> VALIDATE_IR =
|
||||
CompilerConfigurationKey.create("Validate IR");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> VALIDATE_BYTECODE =
|
||||
CompilerConfigurationKey.create("Validate generated JVM bytecode");
|
||||
CompilerConfigurationKey.create("serialize IR to class metadata");
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ enum class JvmTarget(
|
||||
JVM_14("14", Opcodes.V12 + 2),
|
||||
JVM_15("15", Opcodes.V12 + 3),
|
||||
JVM_16("16", Opcodes.V12 + 4),
|
||||
JVM_17("17", Opcodes.V12 + 5),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -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"))
|
||||
@@ -49,7 +49,7 @@ dependencies {
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
apiVersion = "1.4"
|
||||
apiVersion = "1.3"
|
||||
freeCompilerArgs += "-Xsuppress-version-warnings"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -40,8 +40,8 @@ dependencies {
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
// This module is being run from within Gradle, older versions of which only have older kotlin-stdlib in the runtime classpath.
|
||||
apiVersion = "1.4"
|
||||
// This module is being run from within Gradle, older versions of which only have kotlin-stdlib 1.3 in the runtime classpath.
|
||||
apiVersion = "1.3"
|
||||
freeCompilerArgs += "-Xsuppress-version-warnings"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -378,7 +377,7 @@ object KotlinCompilerClient {
|
||||
val javaVersion = CompilerSystemProperties.JAVA_VERSION.value?.toIntOrNull()
|
||||
val javaIllegalAccessWorkaround =
|
||||
if (javaVersion != null && javaVersion >= 16)
|
||||
listOf("--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED")
|
||||
listOf("--illegal-access=permit")
|
||||
else emptyList()
|
||||
val args = listOf(
|
||||
javaExecutable.absolutePath, "-cp", compilerId.compilerClasspath.joinToString(File.pathSeparator)) +
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vfs.impl.ZipHandler
|
||||
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
|
||||
import org.jetbrains.kotlin.build.report.BuildReporter
|
||||
import org.jetbrains.kotlin.build.report.RemoteBuildReporter
|
||||
import org.jetbrains.kotlin.build.report.RemoteReporter
|
||||
import org.jetbrains.kotlin.cli.common.CLICompiler
|
||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
@@ -35,15 +37,10 @@ import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarHandler
|
||||
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.common.*
|
||||
import org.jetbrains.kotlin.daemon.report.CompileServicesFacadeMessageCollector
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporter
|
||||
import org.jetbrains.kotlin.daemon.report.DaemonMessageReporterPrintStreamAdapter
|
||||
import org.jetbrains.kotlin.daemon.report.getBuildReporter
|
||||
import org.jetbrains.kotlin.daemon.report.*
|
||||
import org.jetbrains.kotlin.incremental.*
|
||||
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
|
||||
@@ -66,8 +66,7 @@ abstract class KotlinJvmReplServiceBase(
|
||||
val projectEnvironment =
|
||||
KotlinCoreEnvironment.ProjectEnvironment(
|
||||
disposable,
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(disposable, configuration),
|
||||
configuration,
|
||||
KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(disposable, configuration)
|
||||
)
|
||||
ReplFactoryExtension.registerExtensionPoint(projectEnvironment.project)
|
||||
projectEnvironment.registerExtensionsFromPlugins(configuration)
|
||||
@@ -224,4 +223,4 @@ fun CompilerConfiguration.configureScripting(compilerId: CompilerId) {
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
|
||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
|
||||
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler
|
||||
import org.jetbrains.kotlin.config.Services
|
||||
import org.jetbrains.kotlin.daemon.CompileServiceImplBase
|
||||
|
||||
@@ -36,7 +36,12 @@ dependencies {
|
||||
includeJars("jna", rootProject = rootProject)
|
||||
}
|
||||
|
||||
testRuntimeOnly(intellijDep()) { includeJars("intellij-deps-fastutil-8.4.1-4") }
|
||||
Platform[202] {
|
||||
testRuntimeOnly(intellijDep()) { includeJars("intellij-deps-fastutil-8.3.1-1") }
|
||||
}
|
||||
Platform[203].orHigher {
|
||||
testRuntimeOnly(intellijDep()) { includeJars("intellij-deps-fastutil-8.4.1-4") }
|
||||
}
|
||||
testRuntimeOnly(toolsJar())
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotationOnDeclarationWithDifferentArguments.kt")
|
||||
public void testAnnotationOnDeclarationWithDifferentArguments() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/annotationOnDeclarationWithDifferentArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("annotationUsedAsAnnotationArgument.kt")
|
||||
public void testAnnotationUsedAsAnnotationArgument() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/annotationUsedAsAnnotationArgument.kt");
|
||||
@@ -69,11 +64,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/catchParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classCallInLambda.kt")
|
||||
public void testClassCallInLambda() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/classCallInLambda.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("companion.kt")
|
||||
public void testCompanion() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/companion.kt");
|
||||
@@ -621,11 +611,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");
|
||||
@@ -2590,11 +2575,6 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/localClasses"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousInAnonymous.kt")
|
||||
public void testAnonymousInAnonymous() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/anonymousInAnonymous.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitInAnonymous.kt")
|
||||
public void testImplicitInAnonymous() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/resolve/localClasses/implicitInAnonymous.kt");
|
||||
@@ -2701,11 +2681,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 +3044,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,54 +0,0 @@
|
||||
FILE: annotationOnDeclarationWithDifferentArguments.kt
|
||||
public final enum class SomeEnum : R|kotlin/Enum<SomeEnum>| {
|
||||
private constructor(): R|SomeEnum| {
|
||||
super<R|kotlin/Enum<SomeEnum>|>()
|
||||
}
|
||||
|
||||
public final static enum entry A: R|SomeEnum|
|
||||
public final static enum entry B: R|SomeEnum|
|
||||
public final static fun values(): R|kotlin/Array<SomeEnum>| {
|
||||
}
|
||||
|
||||
public final static fun valueOf(value: R|kotlin/String|): R|SomeEnum| {
|
||||
}
|
||||
|
||||
}
|
||||
public final annotation class MyAnnotation : R|kotlin/Annotation| {
|
||||
public constructor(intValue: R|kotlin/Int|, stringValue: R|kotlin/String|, enumValue: R|SomeEnum|, kClasses: R|kotlin/Array<out kotlin/reflect/KClass<*>>|, annotation: R|MyOtherAnnotation|): R|MyAnnotation| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val intValue: R|kotlin/Int| = R|<local>/intValue|
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public final val stringValue: R|kotlin/String| = R|<local>/stringValue|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
public final val enumValue: R|SomeEnum| = R|<local>/enumValue|
|
||||
public get(): R|SomeEnum|
|
||||
|
||||
public final val kClasses: R|kotlin/Array<out kotlin/reflect/KClass<*>>| = R|<local>/kClasses|
|
||||
public get(): R|kotlin/Array<out kotlin/reflect/KClass<*>>|
|
||||
|
||||
public final val annotation: R|MyOtherAnnotation| = R|<local>/annotation|
|
||||
public get(): R|MyOtherAnnotation|
|
||||
|
||||
}
|
||||
public final annotation class MyOtherAnnotation : R|kotlin/Annotation| {
|
||||
public constructor(intValue: R|kotlin/Int|, stringValue: R|kotlin/String|): R|MyOtherAnnotation| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val intValue: R|kotlin/Int| = R|<local>/intValue|
|
||||
public get(): R|kotlin/Int|
|
||||
|
||||
public final val stringValue: R|kotlin/String| = R|<local>/stringValue|
|
||||
public get(): R|kotlin/String|
|
||||
|
||||
}
|
||||
public final const val constInt: R|kotlin/Int| = Int(10)
|
||||
public get(): R|kotlin/Int|
|
||||
public final const val constString: R|kotlin/String| = String()
|
||||
public get(): R|kotlin/String|
|
||||
@R|MyAnnotation|(intValue = Int(10), stringValue = R|/constString|, enumValue = Q|SomeEnum|.R|/SomeEnum.A|, kClasses = <implicitArrayOf>(<getClass>(Q|kotlin/String|), <getClass>(R|/constString|)), annotation = R|/MyOtherAnnotation.MyOtherAnnotation|(intValue = R|/constInt|, stringValue = String(hello))) public final fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user