mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-04-09 15:52:05 +00:00
Compare commits
3 Commits
push/pdn_p
...
push/pdn_b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef461d4a3a | ||
|
|
eaf870bfd4 | ||
|
|
e8982765c8 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,7 +12,6 @@
|
||||
/android-studio/sdk
|
||||
out/
|
||||
/tmp
|
||||
kotlin-ide/
|
||||
workspace.xml
|
||||
*.versionsBackup
|
||||
/idea/testData/debugger/tinyApp/classes*
|
||||
@@ -58,7 +57,7 @@ build/
|
||||
.idea/artifacts/kotlin_stdlib_wasm_*
|
||||
.idea/jarRepositories.xml
|
||||
.idea/csv-plugin.xml
|
||||
.idea/libraries-with-intellij-classes.xml
|
||||
kotlin-ultimate/
|
||||
node_modules/
|
||||
.rpt2_cache/
|
||||
libraries/tools/kotlin-test-js-runner/lib/
|
||||
|
||||
8
.idea/dictionaries/igor.xml
generated
8
.idea/dictionaries/igor.xml
generated
@@ -1,8 +0,0 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="igor">
|
||||
<words>
|
||||
<w>descr</w>
|
||||
<w>exprs</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
@@ -11,7 +11,7 @@
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":compiler:fir:checkers:generateCheckersComponents" />
|
||||
<option value=":idea-frontend-fir:generateCode" />
|
||||
<option value=":idea:idea-frontend-fir:generateCode" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" value="" />
|
||||
|
||||
12
.idea/vcs.xml
generated
12
.idea/vcs.xml
generated
@@ -8,18 +8,6 @@
|
||||
<inspection_tool class="SubjectLimit" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
<component name="GithubSharedProjectSettings">
|
||||
<option name="branchProtectionPatterns">
|
||||
<list>
|
||||
<option value="master" />
|
||||
<option value="1\.5\.0-M2" />
|
||||
<option value="1\.5\.0" />
|
||||
<option value="1\.5\.20" />
|
||||
<option value="1\.5\.20-M1-release" />
|
||||
<option value="1\.5\.20-RC-release" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="IssueNavigationConfiguration">
|
||||
<option name="links">
|
||||
<list>
|
||||
|
||||
3272
ChangeLog.md
3272
ChangeLog.md
File diff suppressed because it is too large
Load Diff
39
ReadMe.md
39
ReadMe.md
@@ -35,30 +35,26 @@ Support for multiplatform programming is one of Kotlin’s key benefits. It redu
|
||||
|
||||
## Editing Kotlin
|
||||
|
||||
* [Kotlin IntelliJ IDEA Plugin](https://kotlinlang.org/docs/tutorials/getting-started.html) ([source code](https://github.com/JetBrains/intellij-community/tree/master/plugins/kotlin))
|
||||
* [Kotlin IntelliJ IDEA Plugin](https://kotlinlang.org/docs/tutorials/getting-started.html)
|
||||
* [Kotlin Eclipse Plugin](https://kotlinlang.org/docs/tutorials/getting-started-eclipse.html)
|
||||
* [Kotlin Sublime Text Package](https://github.com/vkostyukov/kotlin-sublime-package)
|
||||
|
||||
## Build environment requirements
|
||||
|
||||
This repository is using [Gradle toolchains](https://docs.gradle.org/current/userguide/toolchains.html) feature
|
||||
to select and auto-provision required JDKs from [AdoptOpenJdk](https://adoptopenjdk.net) project.
|
||||
In order to build Kotlin distribution you need to have:
|
||||
|
||||
Unfortunately [AdoptOpenJdk](https://adoptopenjdk.net) project does not provide required JDK 1.6 and 1.7 images,
|
||||
so you could either download them manually and provide path to installation via `JDK_16` and `JDK_17` environment variables or
|
||||
use following SDK managers:
|
||||
- [Asdf-vm](https://asdf-vm.com/)
|
||||
- [Jabba](https://github.com/shyiko/jabba)
|
||||
- [SDKMAN!](https://sdkman.io/)
|
||||
- JDK 1.6, 1.7, 1.8 and 9
|
||||
- Setup environment variables as following:
|
||||
|
||||
Alternatively, it is still possible to only provide required JDKs via environment variables
|
||||
(see [gradle.properties](./gradle.properties#L5) for supported variable names). To ensure Gradle uses only JDKs
|
||||
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`).
|
||||
JAVA_HOME="path to JDK 1.8"
|
||||
JDK_16="path to JDK 1.6"
|
||||
JDK_17="path to JDK 1.7"
|
||||
JDK_18="path to JDK 1.8"
|
||||
JDK_9="path to JDK 9"
|
||||
|
||||
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.
|
||||
For local development, if you're not working on bytecode generation or the standard library, it's OK to have only JDK 1.8 and JDK 9 installed, and to point `JDK_16` and `JDK_17` environment variables to your JDK 1.8 installation.
|
||||
|
||||
You also can use [Gradle properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties) to setup `JDK_*` variables.
|
||||
|
||||
Note: The JDK 6 for MacOS is not available on Oracle's site. You can install it by
|
||||
|
||||
@@ -97,10 +93,13 @@ command line parameters on the first run:
|
||||
|
||||
- `clean` - clean build results
|
||||
- `dist` - assembles the compiler distribution into `dist/kotlinc/` folder
|
||||
- `ideaPlugin` - assembles the Kotlin IDEA plugin distribution into `dist/artifacts/ideaPlugin/Kotlin/` folder
|
||||
- `install` - build and install all public artifacts into local maven repository
|
||||
- `runIde` - build IDEA plugin and run IDEA with it
|
||||
- `coreLibsTest` - build and run stdlib, reflect and kotlin-test tests
|
||||
- `gradlePluginTest` - build and run gradle plugin tests
|
||||
- `compilerTest` - build and run all compiler tests
|
||||
- `ideaPluginTest` - build and run all IDEA plugin tests
|
||||
|
||||
To reproduce TeamCity build use `-Pteamcity=true` flag. Local builds don't run proguard and have jar compression disabled by default.
|
||||
|
||||
@@ -140,6 +139,14 @@ To be able to run tests from IntelliJ easily, check `Delegate IDE build/run acti
|
||||
|
||||
At this time, you can use the latest released `1.3.x` version of the Kotlin plugin for working with the code. To make sure you have the latest version installed, use `Tools` -> `Kotlin` -> `Configure Kotlin Plugin Updates`.
|
||||
|
||||
### Compiling and running
|
||||
|
||||
From this root project there are Run/Debug Configurations for running `IDEA` or the `Generate Compiler Tests` for example; so if you want to try out the latest and greatest IDEA plugin
|
||||
|
||||
* `VCS` -> `Git` -> `Pull`
|
||||
* Run the `IDEA` run configuration in the project
|
||||
* A child IntelliJ IDEA with the Kotlin plugin will then startup
|
||||
|
||||
### Dependency verification
|
||||
|
||||
We have a [dependencies verification](https://docs.gradle.org/current/userguide/dependency_verification.html) feature enabled in the
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import kotlinx.benchmark.gradle.benchmark
|
||||
|
||||
val benchmarks_version = "0.3.1"
|
||||
val benchmarks_version = "0.3.0"
|
||||
|
||||
plugins {
|
||||
java
|
||||
kotlin("jvm")
|
||||
id("org.jetbrains.kotlinx.benchmark") version "0.3.1"
|
||||
id("org.jetbrains.kotlinx.benchmark") version "0.3.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.intellij.psi.PsiFileFactory
|
||||
import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import org.jetbrains.kotlin.ObsoleteTestInfrastructure
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
@@ -148,7 +147,6 @@ abstract class AbstractSimpleFileBenchmark {
|
||||
bh.consume(result.shouldGenerateCode)
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteTestInfrastructure::class)
|
||||
private fun analyzeGreenFileIr(bh: Blackhole) {
|
||||
val scope = GlobalSearchScope.filesScope(env.project, listOf(file.virtualFile))
|
||||
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(env.project))
|
||||
|
||||
@@ -23,6 +23,9 @@ dependencies {
|
||||
testCompile(commonDep("junit:junit"))
|
||||
testCompile(protobufFull())
|
||||
testCompile(kotlinStdlib())
|
||||
Platform[193].orLower {
|
||||
testCompileOnly(intellijDep()) { includeJars("openapi", rootProject = rootProject) }
|
||||
}
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass
|
||||
): T
|
||||
|
||||
fun create(args: CommonCompilerArguments): T {
|
||||
val languageVersion = args.languageVersion?.let { LanguageVersion.fromVersionString(it) } ?: LanguageVersion.LATEST_STABLE
|
||||
val languageVersion = args.languageVersion?.let((LanguageVersion)::fromVersionString) ?: LanguageVersion.LATEST_STABLE
|
||||
|
||||
return create(
|
||||
isEAP = languageVersion.isPreRelease(),
|
||||
@@ -51,7 +51,7 @@ abstract class BuildMetaInfoFactory<T : BuildMetaInfo>(private val metaInfoClass
|
||||
ownVersion = OWN_VERSION,
|
||||
coroutinesVersion = COROUTINES_VERSION,
|
||||
multiplatformVersion = MULTIPLATFORM_VERSION,
|
||||
metadataVersionArray = args.metadataVersion?.let { BinaryVersion.parseVersionArray(it) }
|
||||
metadataVersionArray = args.metadataVersion?.let((BinaryVersion)::parseVersionArray)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,4 +39,4 @@ class GeneratedJvmClass(
|
||||
}
|
||||
}
|
||||
|
||||
fun File.isModuleMappingFile() = extension == ModuleMapping.MAPPING_FILE_EXT && parentFile.name == "META-INF"
|
||||
fun File.isModuleMappingFile() = extension == ModuleMapping.MAPPING_FILE_EXT && parentFile.name == "META-INF"
|
||||
|
||||
@@ -17,7 +17,6 @@ enum class BuildAttributeKind : Serializable {
|
||||
|
||||
enum class BuildAttribute(val kind: BuildAttributeKind) : Serializable {
|
||||
NO_BUILD_HISTORY(BuildAttributeKind.REBUILD_REASON),
|
||||
NO_ABI_SNAPSHOT(BuildAttributeKind.REBUILD_REASON),
|
||||
CACHE_CORRUPTION(BuildAttributeKind.REBUILD_REASON),
|
||||
UNKNOWN_CHANGES_IN_GRADLE_INPUTS(BuildAttributeKind.REBUILD_REASON),
|
||||
JAVA_CHANGE_UNTRACKED_FILE_IS_REMOVED(BuildAttributeKind.REBUILD_REASON),
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.io.Serializable
|
||||
|
||||
@Suppress("Reformat")
|
||||
enum class BuildTime(val parent: BuildTime? = null) : Serializable {
|
||||
GRADLE_TASK_ACTION,
|
||||
GRADLE_TASK,
|
||||
CLEAR_OUTPUT(GRADLE_TASK),
|
||||
BACKUP_OUTPUT(GRADLE_TASK),
|
||||
@@ -21,10 +20,6 @@ enum class BuildTime(val parent: BuildTime? = null) : Serializable {
|
||||
NON_INCREMENTAL_COMPILATION_OUT_OF_PROCESS(RUN_COMPILER),
|
||||
NON_INCREMENTAL_COMPILATION_DAEMON(RUN_COMPILER),
|
||||
INCREMENTAL_COMPILATION(RUN_COMPILER),
|
||||
STORE_BUILD_INFO(INCREMENTAL_COMPILATION),
|
||||
JAR_SNAPSHOT(INCREMENTAL_COMPILATION),
|
||||
SET_UP_ABI_SNAPSHOTS(JAR_SNAPSHOT),
|
||||
IC_ANALYZE_JAR_FILES(JAR_SNAPSHOT),
|
||||
IC_CALCULATE_INITIAL_DIRTY_SET(INCREMENTAL_COMPILATION),
|
||||
IC_ANALYZE_CHANGES_IN_DEPENDENCIES(IC_CALCULATE_INITIAL_DIRTY_SET),
|
||||
IC_FIND_HISTORY_FILES(IC_ANALYZE_CHANGES_IN_DEPENDENCIES),
|
||||
|
||||
@@ -30,28 +30,6 @@ class ChangesCollector {
|
||||
private val changedMembers = hashMapOf<FqName, MutableSet<String>>()
|
||||
private val areSubclassesAffected = hashMapOf<FqName, Boolean>()
|
||||
|
||||
//TODO for test only: ProtoData or ProtoBuf
|
||||
private val storage = hashMapOf<FqName, ProtoData>()
|
||||
private val removed = ArrayList<FqName>()
|
||||
|
||||
//TODO change to immutable map
|
||||
fun protoDataChanges(): Map<FqName, ProtoData> = storage
|
||||
fun protoDataRemoved(): List<FqName> = removed
|
||||
|
||||
companion object {
|
||||
fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>) =
|
||||
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
||||
|
||||
fun ClassProtoData.getNonPrivateMemberNames(): Set<String> {
|
||||
return proto.getNonPrivateNames(
|
||||
nameResolver,
|
||||
ProtoBuf.Class::getConstructorList,
|
||||
ProtoBuf.Class::getFunctionList,
|
||||
ProtoBuf.Class::getPropertyList
|
||||
) + proto.enumEntryList.map { nameResolver.getString(it.name) }
|
||||
}
|
||||
}
|
||||
|
||||
fun changes(): List<ChangeInfo> {
|
||||
val changes = arrayListOf<ChangeInfo>()
|
||||
|
||||
@@ -79,7 +57,7 @@ class ChangesCollector {
|
||||
}
|
||||
|
||||
private fun <T, R> MutableMap<T, MutableSet<R>>.getSet(key: T) =
|
||||
getOrPut(key) { HashSet() }
|
||||
getOrPut(key) { HashSet() }
|
||||
|
||||
private fun collectChangedMember(scope: FqName, name: String) {
|
||||
changedMembers.getSet(scope).add(name)
|
||||
@@ -101,35 +79,11 @@ class ChangesCollector {
|
||||
}
|
||||
}
|
||||
|
||||
fun collectProtoChanges(oldData: ProtoData?, newData: ProtoData?, collectAllMembersForNewClass: Boolean = false, packageProtoKey: String? = null) {
|
||||
fun collectProtoChanges(oldData: ProtoData?, newData: ProtoData?, collectAllMembersForNewClass: Boolean = false) {
|
||||
if (oldData == null && newData == null) {
|
||||
throw IllegalStateException("Old and new value are null")
|
||||
}
|
||||
|
||||
if (newData != null) {
|
||||
when (newData) {
|
||||
is ClassProtoData -> {
|
||||
val fqName = newData.nameResolver.getClassId(newData.proto.fqName).asSingleFqName()
|
||||
storage[fqName] = newData
|
||||
}
|
||||
is PackagePartProtoData -> {
|
||||
//TODO fqName is not unique. It's package and can be present in both java and kotlin
|
||||
val fqName = newData.packageFqName
|
||||
storage[packageProtoKey?.let { FqName(it) } ?: fqName] = newData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (oldData) {
|
||||
is ClassProtoData -> {
|
||||
removed.add(oldData.nameResolver.getClassId(oldData.proto.fqName).asSingleFqName())
|
||||
}
|
||||
is PackagePartProtoData -> {
|
||||
//TODO fqName is not unique. It's package and can be present in both java and kotlin
|
||||
removed.add(packageProtoKey?.let { FqName(it) } ?: oldData.packageFqName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oldData == null) {
|
||||
newData!!.collectAll(isRemoved = false, isAdded = true, collectAllMembersForNewClass = collectAllMembersForNewClass)
|
||||
return
|
||||
@@ -171,8 +125,8 @@ class ChangesCollector {
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>) =
|
||||
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
||||
private fun <T> T.getNonPrivateNames(nameResolver: NameResolver, vararg members: T.() -> List<MessageLite>): Set<String> =
|
||||
members.flatMap { this.it().filterNot { it.isPrivate }.names(nameResolver) }.toSet()
|
||||
|
||||
//TODO remember all sealed parent classes
|
||||
private fun ProtoData.collectAll(isRemoved: Boolean, isAdded: Boolean, collectAllMembersForNewClass: Boolean = false) =
|
||||
@@ -183,15 +137,16 @@ class ChangesCollector {
|
||||
|
||||
private fun PackagePartProtoData.collectAllFromPackage(isRemoved: Boolean) {
|
||||
val memberNames =
|
||||
proto.getNonPrivateNames(
|
||||
nameResolver,
|
||||
ProtoBuf.Package::getFunctionList,
|
||||
ProtoBuf.Package::getPropertyList
|
||||
)
|
||||
proto.getNonPrivateNames(
|
||||
nameResolver,
|
||||
ProtoBuf.Package::getFunctionList,
|
||||
ProtoBuf.Package::getPropertyList
|
||||
)
|
||||
|
||||
if (isRemoved) {
|
||||
collectRemovedMembers(packageFqName, memberNames)
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
collectChangedMembers(packageFqName, memberNames)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +161,8 @@ class ChangesCollector {
|
||||
val collectMember = if (isRemoved) this@ChangesCollector::collectRemovedMember else this@ChangesCollector::collectChangedMember
|
||||
collectMember(classFqName.parent(), classFqName.shortName().asString())
|
||||
memberNames.forEach { collectMember(classFqName, it) }
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (!isRemoved && collectAllMembersForNewClass) {
|
||||
val memberNames = getNonPrivateMemberNames()
|
||||
memberNames.forEach { this@ChangesCollector.collectChangedMember(classFqName, it) }
|
||||
@@ -233,6 +189,15 @@ class ChangesCollector {
|
||||
addChangedParents(fqName, changedParentsFqNames)
|
||||
}
|
||||
|
||||
private fun ClassProtoData.getNonPrivateMemberNames(): Set<String> {
|
||||
return proto.getNonPrivateNames(
|
||||
nameResolver,
|
||||
ProtoBuf.Class::getConstructorList,
|
||||
ProtoBuf.Class::getFunctionList,
|
||||
ProtoBuf.Class::getPropertyList
|
||||
) + proto.enumEntryList.map { nameResolver.getString(it.name) }
|
||||
}
|
||||
|
||||
fun collectMemberIfValueWasChanged(scope: FqName, name: String, oldValue: Any?, newValue: Any?) {
|
||||
if (oldValue == null && newValue == null) {
|
||||
throw IllegalStateException("Old and new value are null for $scope#$name")
|
||||
@@ -240,7 +205,8 @@ class ChangesCollector {
|
||||
|
||||
if (oldValue != null && newValue == null) {
|
||||
collectRemovedMember(scope, name)
|
||||
} else if (oldValue != newValue) {
|
||||
}
|
||||
else if (oldValue != newValue) {
|
||||
collectChangedMember(scope, name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.incremental
|
||||
|
||||
import com.intellij.util.io.DataExternalizer
|
||||
import org.jetbrains.kotlin.build.GeneratedFile
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl
|
||||
import org.jetbrains.kotlin.incremental.js.IrTranslationResultValue
|
||||
import org.jetbrains.kotlin.incremental.js.TranslationResultValue
|
||||
@@ -49,7 +48,6 @@ open class IncrementalJsCache(
|
||||
private const val INLINE_FUNCTIONS = "inline-functions"
|
||||
private const val HEADER_FILE_NAME = "header.meta"
|
||||
private const val PACKAGE_META_FILE = "packages-meta"
|
||||
private const val SOURCE_TO_JS_OUTPUT = "source-to-js-output"
|
||||
|
||||
fun hasHeaderFile(cachesDir: File) = File(cachesDir, HEADER_FILE_NAME).exists()
|
||||
}
|
||||
@@ -62,7 +60,6 @@ open class IncrementalJsCache(
|
||||
private val irTranslationResults = registerMap(IrTranslationResultMap(IR_TRANSLATION_RESULT_MAP.storageFile, pathConverter))
|
||||
private val inlineFunctions = registerMap(InlineFunctionsMap(INLINE_FUNCTIONS.storageFile, pathConverter))
|
||||
private val packageMetadata = registerMap(PackageMetadataMap(PACKAGE_META_FILE.storageFile))
|
||||
private val sourceToJsOutputsMap = registerMap(SourceToJsOutputMap(SOURCE_TO_JS_OUTPUT.storageFile, pathConverter))
|
||||
|
||||
private val dirtySources = hashSetOf<File>()
|
||||
|
||||
@@ -78,7 +75,6 @@ open class IncrementalJsCache(
|
||||
|
||||
override fun markDirty(removedAndCompiledSources: Collection<File>) {
|
||||
removedAndCompiledSources.forEach { sourceFile ->
|
||||
sourceToJsOutputsMap.remove(sourceFile)
|
||||
// The common prefix of all FQN parents has to be the file package
|
||||
sourceToClassesMap[sourceFile].map { it.parentOrNull()?.asString() ?: "" }.minByOrNull { it.length }?.let {
|
||||
packageMetadata.remove(it)
|
||||
@@ -99,10 +95,6 @@ open class IncrementalJsCache(
|
||||
}
|
||||
}
|
||||
|
||||
fun getOutputsBySource(sourceFile: File): Collection<File> {
|
||||
return sourceToJsOutputsMap.get(sourceFile)
|
||||
}
|
||||
|
||||
fun compareAndUpdate(incrementalResults: IncrementalResultsConsumerImpl, changesCollector: ChangesCollector) {
|
||||
val translatedFiles = incrementalResults.packageParts
|
||||
|
||||
@@ -137,8 +129,8 @@ open class IncrementalJsCache(
|
||||
}
|
||||
|
||||
for ((srcFile, irData) in incrementalResults.irFileData) {
|
||||
val (fileData, types, signatures, strings, declarations, bodies, fqn, debugInfos) = irData
|
||||
irTranslationResults.put(srcFile, fileData, types, signatures, strings, declarations, bodies, fqn, debugInfos)
|
||||
val (fileData, types, signatures, strings, declarations, bodies, fqn) = irData
|
||||
irTranslationResults.put(srcFile, fileData, types, signatures, strings, declarations, bodies, fqn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,17 +175,6 @@ open class IncrementalJsCache(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSourceToOutputMap(
|
||||
generatedFiles: Iterable<GeneratedFile>,
|
||||
) {
|
||||
for (generatedFile in generatedFiles) {
|
||||
for (source in generatedFile.sourceFiles) {
|
||||
if (dirtySources.contains(source))
|
||||
sourceToJsOutputsMap.add(source, generatedFile.outputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object TranslationResultValueExternalizer : DataExternalizer<TranslationResultValue> {
|
||||
@@ -234,20 +215,17 @@ private class TranslationResultMap(
|
||||
override fun dumpValue(value: TranslationResultValue): String =
|
||||
"Metadata: ${value.metadata.md5()}, Binary AST: ${value.binaryAst.md5()}, InlineData: ${value.inlineData.md5()}"
|
||||
|
||||
@Synchronized
|
||||
fun put(sourceFile: File, newMetadata: ByteArray, newBinaryAst: ByteArray, newInlineData: ByteArray) {
|
||||
storage[pathConverter.toPath(sourceFile)] =
|
||||
TranslationResultValue(metadata = newMetadata, binaryAst = newBinaryAst, inlineData = newInlineData)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
operator fun get(sourceFile: File): TranslationResultValue? =
|
||||
storage[pathConverter.toPath(sourceFile)]
|
||||
|
||||
fun keys(): Collection<File> =
|
||||
storage.keys.map { pathConverter.toFile(it) }
|
||||
|
||||
@Synchronized
|
||||
fun remove(sourceFile: File, changesCollector: ChangesCollector) {
|
||||
val path = pathConverter.toPath(sourceFile)
|
||||
val protoBytes = storage[path]!!.metadata
|
||||
@@ -269,7 +247,6 @@ private object IrTranslationResultValueExternalizer : DataExternalizer<IrTransla
|
||||
output.writeArray(value.declarations)
|
||||
output.writeArray(value.bodies)
|
||||
output.writeArray(value.fqn)
|
||||
value.debugInfo?.let { output.writeArray(it) }
|
||||
}
|
||||
|
||||
private fun DataOutput.writeArray(array: ByteArray) {
|
||||
@@ -284,17 +261,6 @@ private object IrTranslationResultValueExternalizer : DataExternalizer<IrTransla
|
||||
return filedata
|
||||
}
|
||||
|
||||
private fun DataInput.readArrayOrNull(): ByteArray? {
|
||||
try {
|
||||
val dataSize = readInt()
|
||||
val filedata = ByteArray(dataSize)
|
||||
readFully(filedata)
|
||||
return filedata
|
||||
} catch (e: Throwable) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(input: DataInput): IrTranslationResultValue {
|
||||
val fileData = input.readArray()
|
||||
val types = input.readArray()
|
||||
@@ -303,9 +269,8 @@ private object IrTranslationResultValueExternalizer : DataExternalizer<IrTransla
|
||||
val declarations = input.readArray()
|
||||
val bodies = input.readArray()
|
||||
val fqn = input.readArray()
|
||||
val debugInfos = input.readArrayOrNull()
|
||||
|
||||
return IrTranslationResultValue(fileData, types, signatures, strings, declarations, bodies, fqn, debugInfos)
|
||||
return IrTranslationResultValue(fileData, types, signatures, strings, declarations, bodies, fqn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,11 +295,10 @@ private class IrTranslationResultMap(
|
||||
newStrings: ByteArray,
|
||||
newDeclarations: ByteArray,
|
||||
newBodies: ByteArray,
|
||||
fqn: ByteArray,
|
||||
debugInfos: ByteArray?
|
||||
fqn: ByteArray
|
||||
) {
|
||||
storage[pathConverter.toPath(sourceFile)] =
|
||||
IrTranslationResultValue(newFiledata, newTypes, newSignatures, newStrings, newDeclarations, newBodies, fqn, debugInfos)
|
||||
IrTranslationResultValue(newFiledata, newTypes, newSignatures, newStrings, newDeclarations, newBodies, fqn)
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): IrTranslationResultValue? =
|
||||
@@ -395,7 +359,6 @@ private class InlineFunctionsMap(
|
||||
storageFile: File,
|
||||
private val pathConverter: FileToPathConverter
|
||||
) : BasicStringMap<Map<String, Long>>(storageFile, StringToLongMapExternalizer) {
|
||||
@Synchronized
|
||||
fun process(srcFile: File, newMap: Map<String, Long>, changesCollector: ChangesCollector) {
|
||||
val key = pathConverter.toPath(srcFile)
|
||||
val oldMap = storage[key] ?: emptyMap()
|
||||
@@ -413,7 +376,6 @@ private class InlineFunctionsMap(
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(sourceFile: File) {
|
||||
storage.remove(pathConverter.toPath(sourceFile))
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ open class IncrementalJvmCache(
|
||||
sourceToClassesMap.add(source, jvmClassName)
|
||||
val (proto, nameResolver) = serializedJavaClass.toProtoData()
|
||||
addToClassStorage(proto, nameResolver, source)
|
||||
// collector.addJavaProto(ClassProtoData(proto, nameResolver))
|
||||
|
||||
dirtyOutputClassesMap.notDirty(jvmClassName)
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ open class IncrementalJvmCache(
|
||||
storage[key] = newData
|
||||
|
||||
val packageFqName = kotlinClass.className.packageFqName
|
||||
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName), packageProtoKey = key)
|
||||
changesCollector.collectProtoChanges(oldData?.toProtoData(packageFqName), newData.toProtoData(packageFqName))
|
||||
}
|
||||
|
||||
operator fun contains(className: JvmClassName): Boolean =
|
||||
|
||||
@@ -12,8 +12,7 @@ data class IncrementalModuleEntry(
|
||||
private val projectPath: String,
|
||||
val name: String,
|
||||
val buildDir: File,
|
||||
val buildHistoryFile: File,
|
||||
val abiSnapshot: File
|
||||
val buildHistoryFile: File
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
@@ -27,9 +26,7 @@ class IncrementalModuleInfo(
|
||||
val nameToModules: Map<String, Set<IncrementalModuleEntry>>,
|
||||
val jarToClassListFile: Map<File, File>,
|
||||
// only for js and mpp
|
||||
val jarToModule: Map<File, IncrementalModuleEntry>,
|
||||
//for JVM only
|
||||
val jarToAbiSnapshot: Map<File, File>
|
||||
val jarToModule: Map<File, IncrementalModuleEntry>
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID = 1L
|
||||
|
||||
@@ -41,7 +41,7 @@ open class LookupStorage(
|
||||
private val countersFile = "counters".storageFile
|
||||
private val idToFile = registerMap(IdToFileMap("id-to-file".storageFile, pathConverter))
|
||||
private val fileToId = registerMap(FileToIdMap("file-to-id".storageFile, pathConverter))
|
||||
val lookupMap = registerMap(LookupMap("lookups".storageFile))
|
||||
private val lookupMap = registerMap(LookupMap("lookups".storageFile))
|
||||
|
||||
@Volatile
|
||||
private var size: Int = 0
|
||||
|
||||
@@ -255,23 +255,22 @@ fun withSubtypes(
|
||||
typeFqName: FqName,
|
||||
caches: Iterable<IncrementalCacheCommon>
|
||||
): Set<FqName> {
|
||||
val typesToProccess = LinkedHashSet(listOf(typeFqName))
|
||||
val proccessedTypes = hashSetOf<FqName>()
|
||||
val types = LinkedHashSet(listOf(typeFqName))
|
||||
val subtypes = hashSetOf<FqName>()
|
||||
|
||||
|
||||
while (typesToProccess.isNotEmpty()) {
|
||||
val iterator = typesToProccess.iterator()
|
||||
while (types.isNotEmpty()) {
|
||||
val iterator = types.iterator()
|
||||
val unprocessedType = iterator.next()
|
||||
iterator.remove()
|
||||
|
||||
caches.asSequence()
|
||||
.flatMap { it.getSubtypesOf(unprocessedType) }
|
||||
.filter { it !in proccessedTypes }
|
||||
.forEach { typesToProccess.add(it) }
|
||||
.filter { it !in subtypes }
|
||||
.forEach { types.add(it) }
|
||||
|
||||
proccessedTypes.add(unprocessedType)
|
||||
subtypes.add(unprocessedType)
|
||||
}
|
||||
|
||||
return proccessedTypes
|
||||
return subtypes
|
||||
}
|
||||
|
||||
|
||||
@@ -47,16 +47,10 @@ abstract class BasicMap<K : Comparable<K>, V>(
|
||||
storage.flush(memoryCachesOnly)
|
||||
}
|
||||
|
||||
// avoid unsynchronized close
|
||||
fun close() {
|
||||
storage.close()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun closeForTest() {
|
||||
close()
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun dump(): String {
|
||||
return with(StringBuilder()) {
|
||||
|
||||
@@ -34,6 +34,7 @@ class CachingLazyStorage<K, V>(
|
||||
) : LazyStorage<K, V> {
|
||||
private var storage: PersistentHashMap<K, V>? = null
|
||||
|
||||
@Synchronized
|
||||
private fun getStorageIfExists(): PersistentHashMap<K, V>? {
|
||||
if (storage != null) return storage
|
||||
|
||||
@@ -45,36 +46,32 @@ class CachingLazyStorage<K, V>(
|
||||
return null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun getStorageOrCreateNew(): PersistentHashMap<K, V> {
|
||||
if (storage == null) {
|
||||
storage = createMap()
|
||||
}
|
||||
|
||||
return storage!!
|
||||
}
|
||||
|
||||
override val keys: Collection<K>
|
||||
@Synchronized
|
||||
get() = getStorageIfExists()?.allKeysWithExistingMapping ?: listOf()
|
||||
|
||||
@Synchronized
|
||||
override operator fun contains(key: K): Boolean =
|
||||
getStorageIfExists()?.containsMapping(key) ?: false
|
||||
|
||||
@Synchronized
|
||||
override operator fun get(key: K): V? =
|
||||
getStorageIfExists()?.get(key)
|
||||
|
||||
@Synchronized
|
||||
override operator fun set(key: K, value: V) {
|
||||
getStorageOrCreateNew().put(key, value)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun remove(key: K) {
|
||||
getStorageIfExists()?.remove(key)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun append(key: K, value: V) {
|
||||
getStorageOrCreateNew().appendData(key, { valueExternalizer.save(it, value) })
|
||||
}
|
||||
@@ -106,11 +103,7 @@ class CachingLazyStorage<K, V>(
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
try {
|
||||
storage?.close()
|
||||
} finally {
|
||||
storage = null
|
||||
}
|
||||
storage?.close()
|
||||
}
|
||||
|
||||
private fun createMap(): PersistentHashMap<K, V> = PersistentHashMap(storageFile, keyDescriptor, valueExternalizer)
|
||||
|
||||
@@ -28,7 +28,6 @@ internal open class ClassOneToManyMap(storageFile: File) : BasicStringMap<Collec
|
||||
storage.append(key.asString(), listOf(value.asString()))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
operator fun get(key: FqName): Collection<FqName> =
|
||||
storage[key.asString()]?.map(::FqName) ?: setOf()
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
import java.io.File
|
||||
|
||||
class LookupMap(storage: File) : BasicMap<LookupSymbolKey, Collection<Int>>(storage, LookupSymbolKeyDescriptor, IntCollectionExternalizer) {
|
||||
internal class LookupMap(storage: File) : BasicMap<LookupSymbolKey, Collection<Int>>(storage, LookupSymbolKeyDescriptor, IntCollectionExternalizer) {
|
||||
override fun dumpKey(key: LookupSymbolKey): String = key.toString()
|
||||
|
||||
override fun dumpValue(value: Collection<Int>): String = value.toString()
|
||||
|
||||
@@ -31,6 +31,7 @@ class NonCachingLazyStorage<K, V>(
|
||||
) : LazyStorage<K, V> {
|
||||
private var storage: PersistentHashMap<K, V>? = null
|
||||
|
||||
@Synchronized
|
||||
private fun getStorageIfExists(): PersistentHashMap<K, V>? {
|
||||
if (storage != null) return storage
|
||||
|
||||
@@ -42,6 +43,7 @@ class NonCachingLazyStorage<K, V>(
|
||||
return null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun getStorageOrCreateNew(): PersistentHashMap<K, V> {
|
||||
if (storage == null) {
|
||||
storage = createMap()
|
||||
@@ -51,28 +53,22 @@ class NonCachingLazyStorage<K, V>(
|
||||
}
|
||||
|
||||
override val keys: Collection<K>
|
||||
@Synchronized
|
||||
get() = getStorageIfExists()?.allKeysWithExistingMapping ?: listOf()
|
||||
|
||||
@Synchronized
|
||||
override operator fun contains(key: K): Boolean =
|
||||
getStorageIfExists()?.containsMapping(key) ?: false
|
||||
|
||||
@Synchronized
|
||||
override operator fun get(key: K): V? =
|
||||
getStorageIfExists()?.get(key)
|
||||
|
||||
@Synchronized
|
||||
override operator fun set(key: K, value: V) {
|
||||
getStorageOrCreateNew().put(key, value)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun remove(key: K) {
|
||||
getStorageIfExists()?.remove(key)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun append(key: K, value: V) {
|
||||
getStorageOrCreateNew().appendData(key) { dataOutput -> valueExternalizer.save(dataOutput, value) }
|
||||
}
|
||||
@@ -104,11 +100,7 @@ class NonCachingLazyStorage<K, V>(
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
try {
|
||||
storage?.close()
|
||||
} finally {
|
||||
storage = null
|
||||
}
|
||||
storage?.close()
|
||||
}
|
||||
|
||||
private fun createMap(): PersistentHashMap<K, V> =
|
||||
|
||||
@@ -1,43 +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.incremental.storage
|
||||
|
||||
import org.jetbrains.kotlin.incremental.dumpCollection
|
||||
import java.io.File
|
||||
|
||||
class SourceToJsOutputMap(storageFile: File, private val pathConverter: FileToPathConverter) : BasicStringMap<Collection<String>>(storageFile, StringCollectionExternalizer) {
|
||||
override fun dumpValue(value: Collection<String>): String = value.dumpCollection()
|
||||
|
||||
@Synchronized
|
||||
fun add(key: File, value: File) {
|
||||
storage.append(pathConverter.toPath(key), listOf(pathConverter.toPath(value)))
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): Collection<File> =
|
||||
storage[pathConverter.toPath(sourceFile)]?.map { pathConverter.toFile(it) } ?: setOf()
|
||||
|
||||
|
||||
@Synchronized
|
||||
operator fun set(key: File, values: Collection<File>) {
|
||||
if (values.isEmpty()) {
|
||||
remove(key)
|
||||
return
|
||||
}
|
||||
|
||||
storage[pathConverter.toPath(key)] = values.map { pathConverter.toPath(it) }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(key: File) {
|
||||
storage.remove(pathConverter.toPath(key))
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeValues(key: File, removed: Set<File>) {
|
||||
val notRemoved = this[key].filter { it !in removed }
|
||||
this[key] = notRemoved
|
||||
}
|
||||
}
|
||||
@@ -21,49 +21,23 @@ import com.intellij.util.io.DataExternalizer
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor
|
||||
import com.intellij.util.io.IOUtil
|
||||
import com.intellij.util.io.KeyDescriptor
|
||||
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||
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.*
|
||||
|
||||
/**
|
||||
* Storage versioning:
|
||||
* 0 - only name and value hashes are saved
|
||||
* 1 - name and scope are saved
|
||||
*/
|
||||
object LookupSymbolKeyDescriptor : KeyDescriptor<LookupSymbolKey> {
|
||||
override fun read(input: DataInput): LookupSymbolKey {
|
||||
val version = input.readByte()
|
||||
return when (version.toInt()) {
|
||||
0 -> {
|
||||
val name = input.readUTF()
|
||||
val scope = input.readUTF()
|
||||
LookupSymbolKey(name.hashCode(), scope.hashCode(), name, scope)
|
||||
}
|
||||
1 -> {
|
||||
val first = input.readInt()
|
||||
val second = input.readInt()
|
||||
LookupSymbolKey(first, second, "", "")
|
||||
}
|
||||
else -> throw RuntimeException("Unknown version of LookupSymbolKeyDescriptor=${version}")
|
||||
}
|
||||
val first = input.readInt()
|
||||
val second = input.readInt()
|
||||
|
||||
return LookupSymbolKey(first, second)
|
||||
}
|
||||
|
||||
private val storeFullFqName = CompilerSystemProperties.COMPILE_INCREMENTAL_WITH_CLASSPATH_SHAPSHOTS.value.toBooleanLenient() ?: false
|
||||
|
||||
override fun save(output: DataOutput, value: LookupSymbolKey) {
|
||||
if (storeFullFqName) {
|
||||
output.writeByte(0)
|
||||
output.writeUTF(value.name)
|
||||
output.writeUTF(value.scope)
|
||||
} else {
|
||||
output.writeByte(1)
|
||||
output.writeInt(value.nameHash)
|
||||
output.writeInt(value.scopeHash)
|
||||
}
|
||||
output.writeInt(value.nameHash)
|
||||
output.writeInt(value.scopeHash)
|
||||
}
|
||||
|
||||
override fun getHashCode(value: LookupSymbolKey): Int = value.hashCode()
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.incremental.storage
|
||||
|
||||
data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int, val name:String, val scope:String) : Comparable<LookupSymbolKey> {
|
||||
constructor(name: String, scope: String) : this(name.hashCode(), scope.hashCode(), name, scope)
|
||||
data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int) : Comparable<LookupSymbolKey> {
|
||||
constructor(name: String, scope: String) : this(name.hashCode(), scope.hashCode())
|
||||
|
||||
override fun compareTo(other: LookupSymbolKey): Int {
|
||||
val nameCmp = nameHash.compareTo(other.nameHash)
|
||||
@@ -26,26 +26,6 @@ data class LookupSymbolKey(val nameHash: Int, val scopeHash: Int, val name:Strin
|
||||
|
||||
return scopeHash.compareTo(other.scopeHash)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = nameHash
|
||||
result = 31 * result + scopeHash
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as LookupSymbolKey
|
||||
|
||||
if (nameHash != other.nameHash) return false
|
||||
if (scopeHash != other.scopeHash) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
data class ProtoMapValue(val isPackageFacade: Boolean, val bytes: ByteArray, val strings: Array<String>)
|
||||
|
||||
355
build.gradle.kts
355
build.gradle.kts
@@ -1,6 +1,7 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.crypto.checksum.Checksum
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import proguard.gradle.ProGuardTask
|
||||
|
||||
buildscript {
|
||||
@@ -13,11 +14,8 @@ buildscript {
|
||||
|
||||
if (cacheRedirectorEnabled) {
|
||||
maven("https://cache-redirector.jetbrains.com/plugins.gradle.org/m2")
|
||||
maven("https://cache-redirector.jetbrains.com/repo.maven.apache.org/maven2")
|
||||
|
||||
} else {
|
||||
maven("https://plugins.gradle.org/m2")
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +27,18 @@ buildscript {
|
||||
dependencies {
|
||||
bootstrapCompilerClasspath(kotlin("compiler-embeddable", bootstrapKotlinVersion))
|
||||
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.26")
|
||||
classpath(kotlin("gradle-plugin", bootstrapKotlinVersion))
|
||||
classpath(kotlin("serialization", bootstrapKotlinVersion))
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17")
|
||||
classpath("org.jfrog.buildinfo:build-info-extractor-gradle:4.17.2")
|
||||
}
|
||||
}
|
||||
|
||||
if (kotlinBuildProperties.buildScanServer != null) {
|
||||
apply(from = "gradle/buildScanUserData.gradle")
|
||||
}
|
||||
|
||||
plugins {
|
||||
base
|
||||
idea
|
||||
@@ -55,6 +58,13 @@ pill {
|
||||
|
||||
val isTeamcityBuild = project.kotlinBuildProperties.isTeamcityBuild
|
||||
|
||||
val configuredJdks: List<JdkId> =
|
||||
getConfiguredJdks().also {
|
||||
it.forEach { jdkId ->
|
||||
logger.info("Using ${jdkId.majorVersion} home: ${jdkId.homeDir}")
|
||||
}
|
||||
}
|
||||
|
||||
val defaultSnapshotVersion: String by extra
|
||||
val buildNumber by extra(findProperty("build.number")?.toString() ?: defaultSnapshotVersion)
|
||||
val kotlinVersion by extra(
|
||||
@@ -80,8 +90,10 @@ val distKotlinHomeDir by extra("$distDir/kotlinc")
|
||||
val distLibDir = "$distKotlinHomeDir/lib"
|
||||
val commonLocalDataDir = "$rootDir/local"
|
||||
val ideaSandboxDir = "$commonLocalDataDir/ideaSandbox"
|
||||
val ideaUltimateSandboxDir = "$commonLocalDataDir/ideaUltimateSandbox"
|
||||
val artifactsDir = "$distDir/artifacts"
|
||||
val ideaPluginDir = "$artifactsDir/ideaPlugin/Kotlin"
|
||||
val ideaUltimatePluginDir = "$artifactsDir/ideaUltimatePlugin/Kotlin"
|
||||
|
||||
extra["ktorExcludesForDaemon"] = listOf(
|
||||
"org.jetbrains.kotlin" to "kotlin-reflect",
|
||||
@@ -99,13 +111,46 @@ extra["distLibDir"] = project.file(distLibDir)
|
||||
extra["libsDir"] = project.file(distLibDir)
|
||||
extra["commonLocalDataDir"] = project.file(commonLocalDataDir)
|
||||
extra["ideaSandboxDir"] = project.file(ideaSandboxDir)
|
||||
extra["ideaUltimateSandboxDir"] = project.file(ideaUltimateSandboxDir)
|
||||
extra["ideaPluginDir"] = project.file(ideaPluginDir)
|
||||
extra["ideaUltimatePluginDir"] = project.file(ideaUltimatePluginDir)
|
||||
extra["isSonatypeRelease"] = false
|
||||
val kotlinNativeVersionObject = project.kotlinNativeVersionValue()
|
||||
subprojects {
|
||||
extra["kotlinNativeVersion"] = kotlinNativeVersionObject
|
||||
}
|
||||
|
||||
// Work-around necessary to avoid setting null javaHome. Will be removed after support of lazy task configuration
|
||||
val jdkNotFoundConst = "JDK NOT FOUND"
|
||||
|
||||
if (isTeamcityBuild) {
|
||||
extra["JDK_16"] = jdkPath("1.6")
|
||||
extra["JDK_17"] = jdkPath("1.7")
|
||||
} else {
|
||||
extra["JDK_16"] = jdkPath("1.6", "1.8")
|
||||
extra["JDK_17"] = jdkPath("1.7", "1.8")
|
||||
}
|
||||
extra["JDK_18"] = jdkPath("1.8")
|
||||
extra["JDK_9"] = jdkPath("9")
|
||||
extra["JDK_10"] = jdkPath("10")
|
||||
extra["JDK_11"] = jdkPath("11")
|
||||
extra["JDK_15"] = jdkPath("15")
|
||||
|
||||
// allow opening the project without setting up all env variables (see KT-26413)
|
||||
if (!kotlinBuildProperties.isInIdeaSync) {
|
||||
checkJDK()
|
||||
}
|
||||
|
||||
fun checkJDK() {
|
||||
val missingEnvVars = JdkMajorVersion.values()
|
||||
.filter { it.isMandatory() && extra[it.name] == jdkNotFoundConst }
|
||||
.mapTo(ArrayList()) { it.name }
|
||||
|
||||
if (missingEnvVars.isNotEmpty()) {
|
||||
throw GradleException("Required environment variables are missing: ${missingEnvVars.joinToString()}")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.apply {
|
||||
from(rootProject.file("gradle/versions.gradle.kts"))
|
||||
from(rootProject.file("gradle/report.gradle.kts"))
|
||||
@@ -128,9 +173,8 @@ extra["versions.junit"] = "4.12"
|
||||
extra["versions.javaslang"] = "2.0.6"
|
||||
extra["versions.ant"] = "1.10.7"
|
||||
extra["versions.android"] = "2.3.1"
|
||||
extra["versions.kotlinx-coroutines-core"] = "1.5.0"
|
||||
extra["versions.kotlinx-coroutines-core-jvm"] = "1.5.0"
|
||||
extra["versions.kotlinx-coroutines-jdk8"] = "1.5.0"
|
||||
extra["versions.kotlinx-coroutines-core"] = "1.3.8"
|
||||
extra["versions.kotlinx-coroutines-jdk8"] = "1.3.8"
|
||||
extra["versions.json"] = "20160807"
|
||||
extra["versions.native-platform"] = "0.14"
|
||||
extra["versions.robolectric"] = "4.0"
|
||||
@@ -139,7 +183,7 @@ extra["versions.jflex"] = "1.7.0"
|
||||
extra["versions.markdown"] = "0.1.25"
|
||||
extra["versions.trove4j"] = "1.0.20181211"
|
||||
extra["versions.completion-ranking-kotlin"] = "0.1.3"
|
||||
extra["versions.r8"] = "2.2.64"
|
||||
extra["versions.r8"] = "2.1.96"
|
||||
val immutablesVersion = "0.3.1"
|
||||
extra["versions.kotlinx-collections-immutable"] = immutablesVersion
|
||||
extra["versions.kotlinx-collections-immutable-jvm"] = immutablesVersion
|
||||
@@ -148,9 +192,10 @@ 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-248"
|
||||
extra["versions.kotlin-native"] = "1.5.20-dev-5613"
|
||||
}
|
||||
|
||||
val intellijUltimateEnabled by extra(project.kotlinBuildProperties.intellijUltimateEnabled)
|
||||
val effectSystemEnabled by extra(project.getBooleanProperty("kotlin.compiler.effectSystemEnabled") ?: false)
|
||||
val newInferenceEnabled by extra(project.getBooleanProperty("kotlin.compiler.newInferenceEnabled") ?: false)
|
||||
val useJvmIrBackend by extra(project.kotlinBuildProperties.useIR)
|
||||
@@ -163,7 +208,6 @@ extra["intellijSeparateSdks"] = intellijSeparateSdks
|
||||
extra["IntellijCoreDependencies"] =
|
||||
listOf(
|
||||
when {
|
||||
Platform[203].orHigher() -> "asm-all-9.0"
|
||||
Platform[202].orHigher() -> "asm-all-8.0.1"
|
||||
else -> "asm-all-7.0.1"
|
||||
},
|
||||
@@ -171,6 +215,7 @@ extra["IntellijCoreDependencies"] =
|
||||
"jdom",
|
||||
"jna",
|
||||
"log4j",
|
||||
if (Platform[201].orHigher()) null else "picocontainer",
|
||||
"snappy-in-java",
|
||||
"streamex",
|
||||
"trove4j"
|
||||
@@ -218,7 +263,6 @@ extra["compilerModules"] = arrayOf(
|
||||
":compiler:incremental-compilation-impl",
|
||||
":compiler:compiler.version",
|
||||
":js:js.ast",
|
||||
":js:js.sourcemap",
|
||||
":js:js.serializer",
|
||||
":js:js.parser",
|
||||
":js:js.config",
|
||||
@@ -254,7 +298,6 @@ extra["compilerModules"] = arrayOf(
|
||||
":compiler:fir:java",
|
||||
":compiler:fir:jvm",
|
||||
":compiler:fir:checkers",
|
||||
":compiler:fir:checkers:checkers.jvm",
|
||||
":compiler:fir:entrypoint",
|
||||
":compiler:fir:analysis-tests",
|
||||
":compiler:fir:analysis-tests:legacy-fir-tests",
|
||||
@@ -274,6 +317,7 @@ extra["compilerModulesForJps"] = listOf(
|
||||
":core:compiler.common.jvm",
|
||||
":core:descriptors",
|
||||
":core:descriptors.jvm",
|
||||
":idea:idea-jps-common",
|
||||
":kotlin-preloader",
|
||||
":compiler:util",
|
||||
":compiler:config",
|
||||
@@ -283,54 +327,13 @@ extra["compilerModulesForJps"] = listOf(
|
||||
":compiler:compiler.version"
|
||||
)
|
||||
|
||||
extra["compilerArtifactsForIde"] = listOf(
|
||||
":prepare:ide-plugin-dependencies:android-extensions-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:allopen-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:incremental-compilation-impl-tests-for-ide",
|
||||
":prepare:ide-plugin-dependencies:js-ir-runtime-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-build-common-tests-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-compiler-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-compiler-cli-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-gradle-statistics-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlinx-serialization-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:noarg-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:sam-with-receiver-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:compiler-components-for-jps",
|
||||
":prepare:ide-plugin-dependencies:parcelize-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:lombok-compiler-plugin-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-compiler-tests-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-compiler-testdata-for-ide",
|
||||
":prepare:ide-plugin-dependencies:kotlin-stdlib-minimal-for-test-for-ide",
|
||||
":prepare:ide-plugin-dependencies:low-level-api-fir-for-ide",
|
||||
":prepare:ide-plugin-dependencies:high-level-api-for-ide",
|
||||
":prepare:ide-plugin-dependencies:high-level-api-fir-for-ide",
|
||||
":prepare:ide-plugin-dependencies:high-level-api-fir-tests-for-ide",
|
||||
":kotlin-script-runtime",
|
||||
":kotlin-script-util",
|
||||
":kotlin-scripting-common",
|
||||
":kotlin-scripting-jvm",
|
||||
":kotlin-scripting-compiler",
|
||||
":kotlin-scripting-compiler-impl",
|
||||
":kotlin-android-extensions-runtime",
|
||||
":kotlin-stdlib-common",
|
||||
":kotlin-stdlib",
|
||||
":kotlin-stdlib-jdk7",
|
||||
":kotlin-stdlib-jdk8",
|
||||
":kotlin-reflect",
|
||||
":kotlin-main-kts"
|
||||
)
|
||||
|
||||
// TODO: fix remaining warnings and remove this property.
|
||||
extra["tasksWithWarnings"] = listOf(
|
||||
":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"
|
||||
":plugins:uast-kotlin:compileTestKotlin"
|
||||
)
|
||||
|
||||
val tasksWithWarnings: List<String> by extra
|
||||
@@ -355,12 +358,7 @@ val coreLibProjects = listOfNotNull(
|
||||
val projectsWithDisabledFirBootstrap = coreLibProjects + listOf(
|
||||
":kotlin-gradle-plugin",
|
||||
":kotlinx-metadata",
|
||||
":kotlinx-metadata-jvm",
|
||||
// For some reason stdlib isn't imported correctly for this module
|
||||
// Probably it's related to kotlin-test module usage
|
||||
":kotlin-gradle-statistics",
|
||||
// Requires serialization plugin
|
||||
":wasm:wasm.ir"
|
||||
":kotlinx-metadata-jvm"
|
||||
)
|
||||
|
||||
val gradlePluginProjects = listOf(
|
||||
@@ -380,7 +378,7 @@ apply {
|
||||
}
|
||||
|
||||
apply {
|
||||
if (extra["isDeployStagingRepoGenerationRequired"] as? Boolean == true) {
|
||||
if (extra["isSonatypeRelease"] as? Boolean == true) {
|
||||
logger.info("Applying configuration for sonatype release")
|
||||
from("libraries/prepareSonatypeStaging.gradle")
|
||||
}
|
||||
@@ -394,9 +392,39 @@ fun Task.listConfigurationContents(configName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
val defaultJvmTarget = "1.8"
|
||||
val defaultJavaHome = jdkPath(if (Platform[203].orHigher()) "11" else defaultJvmTarget)
|
||||
val ignoreTestFailures by extra(project.kotlinBuildProperties.ignoreTestFailures)
|
||||
|
||||
allprojects {
|
||||
|
||||
configurations.maybeCreate("embedded").apply {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
||||
}
|
||||
}
|
||||
|
||||
configurations.maybeCreate("embeddedElements").apply {
|
||||
extendsFrom(configurations["embedded"])
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named("embedded-java-runtime"))
|
||||
}
|
||||
}
|
||||
|
||||
jvmTarget = defaultJvmTarget
|
||||
javaHome = defaultJavaHome
|
||||
|
||||
// There are problems with common build dir:
|
||||
// - some tests (in particular js and binary-compatibility-validator depend on the fixed (default) location
|
||||
// - idea seems unable to exclude common buildDir from indexing
|
||||
// therefore it is disabled by default
|
||||
// buildDir = File(commonBuildDir, project.name)
|
||||
|
||||
val mirrorRepo: String? = findProperty("maven.repository.mirror")?.toString()
|
||||
|
||||
repositories {
|
||||
@@ -417,38 +445,7 @@ allprojects {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
if (path.startsWith(":kotlin-ide.")) {
|
||||
return@allprojects
|
||||
}
|
||||
|
||||
configurations.maybeCreate("embedded").apply {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
||||
}
|
||||
}
|
||||
|
||||
configurations.maybeCreate("embeddedElements").apply {
|
||||
extendsFrom(configurations["embedded"])
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, objects.named("embedded-java-runtime"))
|
||||
}
|
||||
}
|
||||
|
||||
// There are problems with common build dir:
|
||||
// - some tests (in particular js and binary-compatibility-validator depend on the fixed (default) location
|
||||
// - idea seems unable to exclude common buildDir from indexing
|
||||
// therefore it is disabled by default
|
||||
// buildDir = File(commonBuildDir, project.name)
|
||||
|
||||
project.configureJvmDefaultToolchain()
|
||||
plugins.withId("java-base") {
|
||||
project.configureShadowJarSubstitutionInCompileClasspath()
|
||||
}
|
||||
configureJvmProject(javaHome!!, jvmTarget!!)
|
||||
|
||||
val commonCompilerArgs = listOfNotNull(
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
@@ -479,7 +476,7 @@ allprojects {
|
||||
useIR = true
|
||||
}
|
||||
|
||||
if (useJvmFir && this@allprojects.path !in projectsWithDisabledFirBootstrap) {
|
||||
if (useJvmFir && this@allprojects.name !in projectsWithDisabledFirBootstrap) {
|
||||
freeCompilerArgs += "-Xuse-fir"
|
||||
freeCompilerArgs += "-Xabi-stability=stable"
|
||||
}
|
||||
@@ -522,16 +519,12 @@ allprojects {
|
||||
outputs.doNotCacheIf("https://youtrack.jetbrains.com/issue/KTI-112") { true }
|
||||
}
|
||||
|
||||
if (isConfigurationCacheDisabled) {
|
||||
// Custom input normolization isn't supported by configuration cache at the moment
|
||||
// See https://github.com/gradle/gradle/issues/13706
|
||||
normalization {
|
||||
runtimeClasspath {
|
||||
ignore("META-INF/MANIFEST.MF")
|
||||
ignore("META-INF/compiler.version")
|
||||
ignore("META-INF/plugin.xml")
|
||||
ignore("kotlin/KotlinVersionCurrentValue.class")
|
||||
}
|
||||
normalization {
|
||||
runtimeClasspath {
|
||||
ignore("META-INF/MANIFEST.MF")
|
||||
ignore("META-INF/compiler.version")
|
||||
ignore("META-INF/plugin.xml")
|
||||
ignore("kotlin/KotlinVersionCurrentValue.class")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,9 +539,12 @@ allprojects {
|
||||
register("checkBuild")
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/gradle/cacheRedirector.gradle.kts")
|
||||
|
||||
afterEvaluate {
|
||||
if (javaHome != defaultJavaHome || jvmTarget != defaultJvmTarget) {
|
||||
logger.info("configuring project $name to compile to the target jvm version $jvmTarget using jdk: $javaHome")
|
||||
configureJvmProject(javaHome!!, jvmTarget!!)
|
||||
} // else we will actually fail during the first task execution. We could not fail before configuration is done due to impact on import in IDE
|
||||
|
||||
fun File.toProjectRootRelativePathOrSelf() = (relativeToOrNull(rootDir)?.takeUnless { it.startsWith("..") } ?: this).path
|
||||
|
||||
fun FileCollection.printClassPath(role: String) =
|
||||
@@ -577,6 +573,7 @@ allprojects {
|
||||
?.exclude("org.jetbrains.kotlin", "kotlin-scripting-compiler-embeddable")
|
||||
}
|
||||
|
||||
apply(from = "$rootDir/gradle/cacheRedirector.gradle.kts")
|
||||
apply(from = "$rootDir/gradle/testRetry.gradle.kts")
|
||||
}
|
||||
}
|
||||
@@ -739,14 +736,8 @@ tasks {
|
||||
dependsOn(":kotlin-scripting-jsr223-test:embeddableTest")
|
||||
dependsOn(":kotlin-main-kts-test:test")
|
||||
dependsOn(":kotlin-main-kts-test:testWithIr")
|
||||
|
||||
if (kotlinBuildProperties.getOrNull("attachedIntellijVersion") == null &&
|
||||
!kotlinBuildProperties.getBoolean("disableKotlinPluginModules", false)
|
||||
) {
|
||||
dependsOn(":kotlin-scripting-ide-services-test:test")
|
||||
dependsOn(":kotlin-scripting-ide-services-test:embeddableTest")
|
||||
}
|
||||
|
||||
dependsOn(":kotlin-scripting-ide-services-test:test")
|
||||
dependsOn(":kotlin-scripting-ide-services-test:embeddableTest")
|
||||
dependsOn(":kotlin-scripting-js-test:test")
|
||||
}
|
||||
|
||||
@@ -790,7 +781,6 @@ tasks {
|
||||
|
||||
register("distTest") {
|
||||
dependsOn("compilerTest")
|
||||
dependsOn("frontendApiTests")
|
||||
dependsOn("toolsTest")
|
||||
dependsOn("gradlePluginTest")
|
||||
dependsOn("examplesTest")
|
||||
@@ -852,22 +842,12 @@ tasks {
|
||||
dependsOn("dist")
|
||||
dependsOn(
|
||||
":idea:idea-fir:test",
|
||||
":idea:idea-frontend-fir:fir-low-level-api-ide-impl:test",
|
||||
":plugins:uast-kotlin-fir:test",
|
||||
":idea:idea-fir-fe10-binding:test"
|
||||
":idea:idea-frontend-api:test",
|
||||
":idea:idea-frontend-fir:test",
|
||||
":idea:idea-frontend-fir:idea-fir-low-level-api:test"
|
||||
)
|
||||
}
|
||||
|
||||
register("frontendApiTests") {
|
||||
dependsOn("dist")
|
||||
dependsOn(
|
||||
":idea-frontend-api:test",
|
||||
":idea-frontend-fir:test",
|
||||
":idea-frontend-fir:idea-fir-low-level-api:test"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
register("android-ide-tests") {
|
||||
dependsOn("dist")
|
||||
@@ -914,6 +894,7 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
register("kaptIdeTest") {
|
||||
dependsOn(":kotlin-annotation-processing:test")
|
||||
dependsOn(":kotlin-annotation-processing-base:test")
|
||||
@@ -927,6 +908,22 @@ tasks {
|
||||
)
|
||||
}
|
||||
|
||||
register("kmmTest", AggregateTest::class) {
|
||||
dependsOn(
|
||||
":idea:idea-gradle:test",
|
||||
":idea:test",
|
||||
":compiler:test",
|
||||
":compiler:container:test",
|
||||
":js:js.tests:test"
|
||||
)
|
||||
|
||||
dependsOn(":kotlin-gradle-plugin-integration-tests:test")
|
||||
if (Ide.AS40.orHigher())
|
||||
dependsOn(":kotlin-ultimate:ide:android-studio-native:test")
|
||||
|
||||
testPatternFile = file("tests/mpp/kmm-patterns.csv")
|
||||
}
|
||||
|
||||
register("test") {
|
||||
doLast {
|
||||
throw GradleException("Don't use directly, use aggregate tasks *-check instead")
|
||||
@@ -953,13 +950,32 @@ tasks {
|
||||
|
||||
register("publishIdeArtifacts") {
|
||||
idePluginDependency {
|
||||
dependsOn((rootProject.extra["compilerArtifactsForIde"] as List<String>).map { "$it:publish" })
|
||||
}
|
||||
}
|
||||
|
||||
register("installIdeArtifacts") {
|
||||
idePluginDependency {
|
||||
dependsOn((rootProject.extra["compilerArtifactsForIde"] as List<String>).map { "$it:install" })
|
||||
dependsOn(
|
||||
":prepare:ide-plugin-dependencies:android-extensions-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:allopen-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:incremental-compilation-impl-tests-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-build-common-tests-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-compiler-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlin-gradle-statistics-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:kotlinx-serialization-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:noarg-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:sam-with-receiver-compiler-plugin-for-ide:publish",
|
||||
":prepare:ide-plugin-dependencies:compiler-components-for-jps:publish",
|
||||
":prepare:ide-plugin-dependencies:parcelize-compiler-plugin-for-ide:publish",
|
||||
":kotlin-script-runtime:publish",
|
||||
":kotlin-script-util:publish",
|
||||
":kotlin-scripting-common:publish",
|
||||
":kotlin-scripting-jvm:publish",
|
||||
":kotlin-scripting-compiler:publish",
|
||||
":kotlin-scripting-compiler-impl:publish",
|
||||
":kotlin-android-extensions-runtime:publish",
|
||||
":kotlin-stdlib-common:publish",
|
||||
":kotlin-stdlib:publish",
|
||||
":kotlin-stdlib-jdk7:publish",
|
||||
":kotlin-stdlib-jdk8:publish",
|
||||
":kotlin-reflect:publish",
|
||||
":kotlin-main-kts:publish"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1011,7 +1027,8 @@ val zipTestData by task<Zip> {
|
||||
val zipPlugin by task<Zip> {
|
||||
val src = when (project.findProperty("pluginArtifactDir") as String?) {
|
||||
"Kotlin" -> ideaPluginDir
|
||||
null -> ideaPluginDir
|
||||
"KotlinUltimate" -> ideaUltimatePluginDir
|
||||
null -> if (project.hasProperty("ultimate")) ideaUltimatePluginDir else ideaPluginDir
|
||||
else -> error("Unsupported plugin artifact dir")
|
||||
}
|
||||
val destPath = project.findProperty("pluginZipPath") as String?
|
||||
@@ -1068,6 +1085,66 @@ configure<IdeaModel> {
|
||||
}
|
||||
}
|
||||
|
||||
fun jdkPathOrNull(version: String): String? {
|
||||
val jdkName = "JDK_${version.replace(".", "")}"
|
||||
val jdkMajorVersion = JdkMajorVersion.valueOf(jdkName)
|
||||
return configuredJdks.find { it.majorVersion == jdkMajorVersion }?.homeDir?.canonicalPath
|
||||
}
|
||||
|
||||
fun jdkPath(version: String, vararg replacementVersions: String): String {
|
||||
return jdkPathOrNull(version) ?: run {
|
||||
replacementVersions.asSequence().map { jdkPathOrNull(it) }.find { it != null }
|
||||
} ?: jdkNotFoundConst
|
||||
}
|
||||
|
||||
fun Project.configureJvmProject(javaHome: String, javaVersion: String) {
|
||||
val currentJavaHome = File(System.getProperty("java.home")!!).canonicalPath
|
||||
val shouldFork = !currentJavaHome.startsWith(File(javaHome).canonicalPath)
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
if (name != "compileJava9Java") {
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
options.isFork = shouldFork
|
||||
options.forkOptions.javaHome = file(javaHome)
|
||||
options.compilerArgs.add("-proc:none")
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jdkHome = javaHome
|
||||
kotlinOptions.jvmTarget = javaVersion
|
||||
kotlinOptions.freeCompilerArgs += "-Xjvm-default=compatibility"
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
executable = File(javaHome, "bin/java").canonicalPath
|
||||
}
|
||||
|
||||
plugins.withId("java-base") {
|
||||
configureShadowJarSubstitutionInCompileClasspath()
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureShadowJarSubstitutionInCompileClasspath() {
|
||||
val substitutionMap = mapOf(":kotlin-reflect" to ":kotlin-reflect-api")
|
||||
|
||||
fun configureSubstitution(substitution: DependencySubstitution) {
|
||||
val requestedProject = (substitution.requested as? ProjectComponentSelector)?.projectPath ?: return
|
||||
val replacementProject = substitutionMap[requestedProject] ?: return
|
||||
substitution.useTarget(project(replacementProject), "Non-default shadow jars should not be used in compile classpath")
|
||||
}
|
||||
|
||||
sourceSets.all {
|
||||
for (configName in listOf(compileOnlyConfigurationName, compileClasspathConfigurationName)) {
|
||||
configurations.getByName(configName).resolutionStrategy.dependencySubstitution {
|
||||
all(::configureSubstitution)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("findShadowJarsInClasspath") {
|
||||
doLast {
|
||||
fun Collection<File>.printSorted(indent: String = " ") {
|
||||
@@ -1135,9 +1212,3 @@ if (disableVerificationTasks) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin::class) {
|
||||
extensions.configure(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension::class.java) {
|
||||
nodeVersion = "16.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.31")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.26")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
classpath("org.jetbrains.kotlin:kotlin-sam-with-receiver:${project.bootstrapKotlinVersion}")
|
||||
}
|
||||
@@ -69,6 +69,7 @@ rootProject.apply {
|
||||
}
|
||||
|
||||
val isTeamcityBuild = kotlinBuildProperties.isTeamcityBuild
|
||||
val intellijUltimateEnabled by extra(kotlinBuildProperties.intellijUltimateEnabled)
|
||||
val intellijSeparateSdks by extra(project.getBooleanProperty("intellijSeparateSdks") ?: false)
|
||||
|
||||
extra["intellijReleaseType"] = when {
|
||||
@@ -143,7 +144,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.31")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.26")
|
||||
implementation("com.gradle.publish:plugin-publish-plugin:0.14.0")
|
||||
|
||||
implementation("net.rubygrapefruit:native-platform:${property("versions.native-platform")}")
|
||||
@@ -155,10 +156,10 @@ dependencies {
|
||||
implementation("net.sf.proguard:proguard-gradle:6.2.2")
|
||||
implementation("org.jetbrains.intellij.deps:asm-all:8.0.1")
|
||||
|
||||
implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.0.1")
|
||||
implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:0.5")
|
||||
|
||||
implementation("org.gradle:test-retry-gradle-plugin:1.2.0")
|
||||
implementation("com.gradle.enterprise:test-distribution-gradle-plugin:2.1")
|
||||
implementation("com.gradle.enterprise:test-distribution-gradle-plugin:1.2.1")
|
||||
|
||||
compileOnly(gradleApi())
|
||||
|
||||
|
||||
@@ -15,28 +15,30 @@ plugins {
|
||||
base
|
||||
}
|
||||
|
||||
val intellijUltimateEnabled: Boolean by rootProject.extra
|
||||
val intellijReleaseType: String by rootProject.extra
|
||||
val intellijVersion = rootProject.extra["versions.intellijSdk"] as String
|
||||
val intellijVersionForIde = rootProject.intellijSdkVersionForIde()
|
||||
val asmVersion = rootProject.findProperty("versions.jar.asm-all") as String?
|
||||
val androidStudioRelease = rootProject.findProperty("versions.androidStudioRelease") as String?
|
||||
val androidStudioBuild = rootProject.findProperty("versions.androidStudioBuild") as String?
|
||||
val intellijSeparateSdks: Boolean by rootProject.extra
|
||||
val installIntellijCommunity = !intellijUltimateEnabled || intellijSeparateSdks
|
||||
val installIntellijUltimate = intellijUltimateEnabled && androidStudioRelease == null
|
||||
|
||||
fun checkIntellijVersion(intellijVersion: String) {
|
||||
val intellijVersionDelimiterIndex = intellijVersion.indexOfAny(charArrayOf('.', '-'))
|
||||
if (intellijVersionDelimiterIndex == -1) {
|
||||
error("Invalid IDEA version $intellijVersion")
|
||||
}
|
||||
val intellijVersionDelimiterIndex = intellijVersion.indexOfAny(charArrayOf('.', '-'))
|
||||
if (intellijVersionDelimiterIndex == -1) {
|
||||
error("Invalid IDEA version $intellijVersion")
|
||||
}
|
||||
checkIntellijVersion(intellijVersion)
|
||||
intellijVersionForIde?.let { checkIntellijVersion(it) }
|
||||
|
||||
val platformBaseVersion = intellijVersion.substring(0, intellijVersionDelimiterIndex)
|
||||
|
||||
logger.info("intellijUltimateEnabled: $intellijUltimateEnabled")
|
||||
logger.info("intellijVersion: $intellijVersion")
|
||||
logger.info("intellijVersionForIde: $intellijVersionForIde")
|
||||
logger.info("androidStudioRelease: $androidStudioRelease")
|
||||
logger.info("androidStudioBuild: $androidStudioBuild")
|
||||
logger.info("intellijSeparateSdks: $intellijSeparateSdks")
|
||||
logger.info("installIntellijCommunity: $installIntellijCommunity")
|
||||
logger.info("installIntellijUltimate: $installIntellijUltimate")
|
||||
|
||||
val androidStudioOs by lazy {
|
||||
when {
|
||||
@@ -71,14 +73,11 @@ repositories {
|
||||
}
|
||||
|
||||
val intellij by configurations.creating
|
||||
val intellijForIde by configurations.creating
|
||||
val intellijUltimate by configurations.creating
|
||||
val androidStudio by configurations.creating
|
||||
val sources by configurations.creating
|
||||
val sourcesForIde by configurations.creating
|
||||
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
|
||||
|
||||
/**
|
||||
@@ -94,6 +93,7 @@ val dependenciesDir = (findProperty("kotlin.build.dependencies.dir") as String?)
|
||||
val customDepsRepoDir = dependenciesDir.resolve("repo")
|
||||
|
||||
val customDepsOrg: String by rootProject.extra
|
||||
val customDepsRevision = intellijVersion
|
||||
val repoDir = File(customDepsRepoDir, customDepsOrg)
|
||||
|
||||
dependencies {
|
||||
@@ -105,8 +105,12 @@ dependencies {
|
||||
|
||||
androidStudio("google:android-studio-ide:$androidStudioBuild@$extension")
|
||||
} else {
|
||||
intellij("com.jetbrains.intellij.idea:ideaIC:$intellijVersion")
|
||||
intellijVersionForIde?.let { intellijForIde("com.jetbrains.intellij.idea:ideaIC:$it") }
|
||||
if (installIntellijCommunity) {
|
||||
intellij("com.jetbrains.intellij.idea:ideaIC:$intellijVersion")
|
||||
}
|
||||
if (installIntellijUltimate) {
|
||||
intellijUltimate("com.jetbrains.intellij.idea:ideaIU:$intellijVersion")
|
||||
}
|
||||
}
|
||||
|
||||
if (asmVersion != null) {
|
||||
@@ -114,104 +118,102 @@ dependencies {
|
||||
}
|
||||
|
||||
sources("com.jetbrains.intellij.idea:ideaIC:$intellijVersion:sources@jar")
|
||||
intellijVersionForIde?.let { sourcesForIde("com.jetbrains.intellij.idea:ideaIC:$it:sources@jar") }
|
||||
jpsStandalone("com.jetbrains.intellij.idea:jps-standalone:$intellijVersion")
|
||||
intellijVersionForIde?.let { jpsStandaloneForIde("com.jetbrains.intellij.idea:jps-standalone:$it") }
|
||||
intellijCore("com.jetbrains.intellij.idea:intellij-core:$intellijVersion")
|
||||
intellijVersionForIde?.let { intellijCoreForIde("com.jetbrains.intellij.idea:intellij-core:$it") }
|
||||
if (intellijUltimateEnabled) {
|
||||
nodeJSPlugin("com.jetbrains.plugins:NodeJS:${rootProject.extra["versions.idea.NodeJS"]}@zip")
|
||||
}
|
||||
}
|
||||
|
||||
fun prepareDeps(
|
||||
intellij: Configuration,
|
||||
intellijCore: Configuration,
|
||||
sources: Configuration,
|
||||
jpsStandalone: Configuration,
|
||||
intellijVersion: String
|
||||
) {
|
||||
val makeIntellijCore = buildIvyRepositoryTask(intellijCore, customDepsOrg, customDepsRepoDir)
|
||||
val makeIntellijCore = buildIvyRepositoryTask(intellijCore, customDepsOrg, customDepsRepoDir)
|
||||
|
||||
val makeIntellijAnnotations = tasks.register("makeIntellijAnnotations${intellij.name.capitalize()}", Copy::class) {
|
||||
dependsOn(makeIntellijCore)
|
||||
val makeIntellijAnnotations by tasks.registering(Copy::class) {
|
||||
dependsOn(makeIntellijCore)
|
||||
|
||||
val intellijCoreRepo = CleanableStore[repoDir.resolve("intellij-core").absolutePath][intellijVersion].use()
|
||||
from(intellijCoreRepo.resolve("artifacts/annotations.jar"))
|
||||
val intellijCoreRepo = CleanableStore[repoDir.resolve("intellij-core").absolutePath][intellijVersion].use()
|
||||
from(intellijCoreRepo.resolve("artifacts/annotations.jar"))
|
||||
|
||||
val annotationsStore = CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath]
|
||||
val targetDir = annotationsStore[intellijVersion].use()
|
||||
into(targetDir)
|
||||
val annotationsStore = CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath]
|
||||
val targetDir = annotationsStore[intellijVersion].use()
|
||||
into(targetDir)
|
||||
|
||||
val ivyFile = File(targetDir, "$intellijRuntimeAnnotations.ivy.xml")
|
||||
outputs.files(ivyFile)
|
||||
val ivyFile = File(targetDir, "$intellijRuntimeAnnotations.ivy.xml")
|
||||
outputs.files(ivyFile)
|
||||
|
||||
doFirst {
|
||||
annotationsStore.cleanStore()
|
||||
}
|
||||
|
||||
doLast {
|
||||
writeIvyXml(
|
||||
customDepsOrg,
|
||||
intellijRuntimeAnnotations,
|
||||
intellijVersion,
|
||||
intellijRuntimeAnnotations,
|
||||
targetDir,
|
||||
targetDir,
|
||||
targetDir,
|
||||
allowAnnotations = true
|
||||
)
|
||||
}
|
||||
doFirst {
|
||||
annotationsStore.cleanStore()
|
||||
}
|
||||
|
||||
val mergeSources = tasks.create("mergeSources${intellij.name.capitalize()}", Jar::class.java) {
|
||||
dependsOn(sources)
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
isZip64 = true
|
||||
if (!kotlinBuildProperties.isTeamcityBuild) {
|
||||
from(provider { sources.map(::zipTree) })
|
||||
}
|
||||
destinationDirectory.set(File(repoDir, sources.name))
|
||||
archiveBaseName.set("intellij")
|
||||
archiveClassifier.set("sources")
|
||||
archiveVersion.set(intellijVersion)
|
||||
}
|
||||
|
||||
val sourcesFile = mergeSources.outputs.files.singleFile
|
||||
|
||||
val makeIde = if (androidStudioBuild != null) {
|
||||
buildIvyRepositoryTask(
|
||||
androidStudio,
|
||||
doLast {
|
||||
writeIvyXml(
|
||||
customDepsOrg,
|
||||
customDepsRepoDir,
|
||||
if (androidStudioOs == "mac")
|
||||
::skipContentsDirectory
|
||||
else
|
||||
::skipToplevelDirectory
|
||||
)
|
||||
} else {
|
||||
val task = buildIvyRepositoryTask(intellij, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
|
||||
task.configure {
|
||||
dependsOn(mergeSources)
|
||||
}
|
||||
|
||||
task
|
||||
}
|
||||
|
||||
val buildJpsStandalone = buildIvyRepositoryTask(jpsStandalone, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn(
|
||||
makeIntellijCore,
|
||||
makeIde,
|
||||
buildJpsStandalone,
|
||||
makeIntellijAnnotations
|
||||
intellijRuntimeAnnotations,
|
||||
intellijVersion,
|
||||
intellijRuntimeAnnotations,
|
||||
targetDir,
|
||||
targetDir,
|
||||
targetDir,
|
||||
allowAnnotations = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
prepareDeps(intellij, intellijCore, sources, jpsStandalone, intellijVersion)
|
||||
if (intellijVersionForIde != null) {
|
||||
prepareDeps(intellijForIde, intellijCoreForIde, sourcesForIde, jpsStandaloneForIde, intellijVersionForIde)
|
||||
val mergeSources by tasks.creating(Jar::class.java) {
|
||||
dependsOn(sources)
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
isZip64 = true
|
||||
if (!kotlinBuildProperties.isTeamcityBuild) {
|
||||
from(provider { sources.map(::zipTree) })
|
||||
}
|
||||
destinationDirectory.set(File(repoDir, sources.name))
|
||||
archiveBaseName.set("intellij")
|
||||
archiveClassifier.set("sources")
|
||||
archiveVersion.set(intellijVersion)
|
||||
}
|
||||
|
||||
val sourcesFile = mergeSources.outputs.files.singleFile
|
||||
|
||||
val makeIde = if (androidStudioBuild != null) {
|
||||
buildIvyRepositoryTask(
|
||||
androidStudio,
|
||||
customDepsOrg,
|
||||
customDepsRepoDir,
|
||||
if (androidStudioOs == "mac")
|
||||
::skipContentsDirectory
|
||||
else
|
||||
::skipToplevelDirectory
|
||||
)
|
||||
} else {
|
||||
val task = if (installIntellijUltimate) {
|
||||
buildIvyRepositoryTask(intellijUltimate, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
} else {
|
||||
buildIvyRepositoryTask(intellij, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
}
|
||||
|
||||
task.configure {
|
||||
dependsOn(mergeSources)
|
||||
}
|
||||
|
||||
task
|
||||
}
|
||||
|
||||
val buildJpsStandalone = buildIvyRepositoryTask(jpsStandalone, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn(
|
||||
makeIntellijCore,
|
||||
makeIde,
|
||||
buildJpsStandalone,
|
||||
makeIntellijAnnotations
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
if (installIntellijUltimate) {
|
||||
val buildNodeJsPlugin =
|
||||
buildIvyRepositoryTask(nodeJSPlugin, customDepsOrg, customDepsRepoDir, ::skipToplevelDirectory, sourcesFile)
|
||||
tasks.named("build") { dependsOn(buildNodeJsPlugin) }
|
||||
}
|
||||
|
||||
tasks.named<Delete>("clean") {
|
||||
@@ -398,11 +400,6 @@ fun skipToplevelDirectory(path: String) = path.substringAfter('/')
|
||||
|
||||
fun skipContentsDirectory(path: String) = path.substringAfter("Contents/")
|
||||
|
||||
fun Project.intellijSdkVersionForIde(): String? {
|
||||
val majorVersion = kotlinBuildProperties.getOrNull("attachedIntellijVersion") as? String ?: return null
|
||||
return rootProject.findProperty("versions.intellijSdk.forIde.$majorVersion") as? String
|
||||
}
|
||||
|
||||
class XMLWriter(private val outputStreamWriter: OutputStreamWriter) : Closeable {
|
||||
|
||||
private val xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStreamWriter)
|
||||
|
||||
@@ -18,11 +18,55 @@ buildscript {
|
||||
} else {
|
||||
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-dependencies" }
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.30")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.26")
|
||||
}
|
||||
}
|
||||
|
||||
include "prepare-deps"
|
||||
def buildProperties = BuildPropertiesKt.getKotlinBuildPropertiesForSettings(settings)
|
||||
def projectVersions = file("../gradle/versions.properties").text
|
||||
|
||||
include "prepare-deps"
|
||||
|
||||
def target_AppCode_Clion = buildProperties.includeCidrPlugins && !projectVersions.contains("versions.androidStudioRelease")
|
||||
def target_AndroidStudio = buildProperties.includeCidrPlugins && projectVersions.contains("versions.androidStudioRelease")
|
||||
def target_IdeaUltimate = buildProperties.includeUltimate
|
||||
|
||||
if (target_AppCode_Clion) {
|
||||
logger.info("Including modules for AC and CL in buildSrc/settings.gradle")
|
||||
|
||||
include ":prepare-deps:kotlin-native-platform-deps"
|
||||
include ":prepare-deps:native-debug-plugin"
|
||||
|
||||
project(":prepare-deps:kotlin-native-platform-deps").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/kotlin-native-platform-deps")
|
||||
project(":prepare-deps:native-debug-plugin").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/native-debug-plugin")
|
||||
} else if (target_AndroidStudio) {
|
||||
logger.info("Including modules for AS (mobile plugin) in buildSrc/settings.gradle")
|
||||
|
||||
include ":prepare-deps:appcode-binaries"
|
||||
include ":prepare-deps:lldb-framework"
|
||||
include ":prepare-deps:lldb-frontend"
|
||||
|
||||
project(":prepare-deps:appcode-binaries").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/appcode-binaries")
|
||||
project(":prepare-deps:lldb-framework").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/lldb-framework")
|
||||
project(":prepare-deps:lldb-frontend").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/lldb-frontend")
|
||||
} else if (target_IdeaUltimate) {
|
||||
logger.info("Including modules for IU in buildSrc/settings.gradle")
|
||||
|
||||
include ":prepare-deps:lldb-frontend"
|
||||
include ":prepare-deps:native-debug-plugin"
|
||||
|
||||
project(":prepare-deps:lldb-frontend").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/lldb-frontend")
|
||||
project(":prepare-deps:native-debug-plugin").projectDir =
|
||||
file("${buildProperties.propertiesProvider.rootProjectDir}/kotlin-ultimate/buildSrc/prepare-deps/native-debug-plugin")
|
||||
|
||||
} else {
|
||||
logger.info("Not including extra modules in buildSrc/settings.gradle")
|
||||
}
|
||||
|
||||
@@ -20,6 +20,3 @@ 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", false)
|
||||
|
||||
val KotlinBuildProperties.isObsoleteJdkOverrideEnabled: Boolean
|
||||
get() = getBoolean("kotlin.build.isObsoleteJdkOverrideEnabled", false)
|
||||
|
||||
@@ -4,36 +4,32 @@
|
||||
*/
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.gradle.internal.jvm.inspection.JvmVersionDetector
|
||||
import proguard.ClassSpecification
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@CacheableTask
|
||||
open class CacheableProguardTask : proguard.gradle.ProGuardTask() {
|
||||
open class CacheableProguardTask @Inject constructor(
|
||||
private val jvmVersionDetector: JvmVersionDetector
|
||||
) : proguard.gradle.ProGuardTask() {
|
||||
|
||||
@get:Internal
|
||||
val javaLauncher: Property<JavaLauncher> = project.objects.property()
|
||||
|
||||
@get:Internal
|
||||
val jdkHomePath: Provider<File> = javaLauncher.map { it.metadata.installationPath.asFile }
|
||||
@Internal
|
||||
var jdkHome: File? = null
|
||||
|
||||
@get:Optional
|
||||
@get:Input
|
||||
internal val jdkMajorVersion: Provider<JavaVersion> = javaLauncher.map {
|
||||
JavaVersion.toVersion(it.metadata.languageVersion.toString())
|
||||
}
|
||||
internal val jdkMajorVersion: String?
|
||||
get() = jdkHome?.let { jvmVersionDetector.getJavaVersion(Jvm.forHome(jdkHome)) }?.majorVersion
|
||||
|
||||
@CompileClasspath
|
||||
override fun getLibraryJarFileCollection(): FileCollection = super.getLibraryJarFileCollection()
|
||||
.filter { libraryFile ->
|
||||
jdkHomePath.orNull?.let { !libraryFile.absoluteFile.startsWith(it.absoluteFile) } ?: true
|
||||
}
|
||||
override fun getLibraryJarFileCollection(): FileCollection = super.getLibraryJarFileCollection().filter { libraryFile ->
|
||||
jdkHome?.let { !libraryFile.absoluteFile.startsWith(it.absoluteFile) } ?: true
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
|
||||
@@ -81,12 +81,3 @@ fun Task.singleOutputFile(): File = when (this) {
|
||||
is ProGuardTask -> project.file(outJarFiles.single()!!)
|
||||
else -> outputs.files.singleFile
|
||||
}
|
||||
|
||||
val Project.isConfigurationCacheDisabled
|
||||
get() = (gradle.startParameter as? org.gradle.api.internal.StartParameterInternal)?.isConfigurationCache != true
|
||||
|
||||
val Project.isIdeaActive
|
||||
get() = providers.systemProperty("idea.active").forUseAtConfigurationTime().isPresent
|
||||
|
||||
val Project.intellijCommunityDir: File
|
||||
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 {
|
||||
P202, P203;
|
||||
P183, P191, P192, P193, P201, P202, P203;
|
||||
|
||||
val version: Int = name.drop(1).toInt()
|
||||
|
||||
@@ -43,9 +43,17 @@ enum class Platform : CompatibilityPredicate {
|
||||
}
|
||||
|
||||
enum class Ide(val platform: Platform) : CompatibilityPredicate {
|
||||
IJ191(Platform.P191),
|
||||
IJ192(Platform.P192),
|
||||
IJ193(Platform.P193),
|
||||
IJ201(Platform.P201),
|
||||
IJ202(Platform.P202),
|
||||
IJ203(Platform.P203),
|
||||
|
||||
AS35(Platform.P183),
|
||||
AS36(Platform.P192),
|
||||
AS40(Platform.P193),
|
||||
AS41(Platform.P201),
|
||||
AS42(Platform.P202);
|
||||
|
||||
val kind = Kind.values().first { it.shortName == name.take(2) }
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
@file:JvmName("JvmToolchain")
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.jvm.toolchain.*
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
enum class JdkMajorVersion(
|
||||
val majorVersion: Int,
|
||||
val targetName: String = majorVersion.toString(),
|
||||
val overrideMajorVersion: Int? = null,
|
||||
private val mandatory: Boolean = true
|
||||
) {
|
||||
JDK_1_6(6, targetName = "1.6", overrideMajorVersion = 8),
|
||||
JDK_1_7(7, targetName = "1.7", overrideMajorVersion = 8),
|
||||
JDK_1_8(8, targetName = "1.8"),
|
||||
JDK_9(9, overrideMajorVersion = 11),
|
||||
JDK_10(10, mandatory = false, overrideMajorVersion = 11),
|
||||
JDK_11(11, mandatory = false),
|
||||
JDK_15(15, mandatory = false),
|
||||
JDK_16(16, mandatory = false);
|
||||
|
||||
fun isMandatory(): Boolean = mandatory
|
||||
|
||||
companion object {
|
||||
fun fromMajorVersion(majorVersion: Int) = values().first { it.majorVersion == majorVersion }
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureJvmDefaultToolchain() {
|
||||
configureJvmToolchain(JdkMajorVersion.JDK_1_8)
|
||||
}
|
||||
|
||||
fun Project.shouldOverrideObsoleteJdk(
|
||||
jdkVersion: JdkMajorVersion
|
||||
): Boolean = kotlinBuildProperties.isObsoleteJdkOverrideEnabled &&
|
||||
jdkVersion.overrideMajorVersion != null
|
||||
|
||||
fun Project.configureJvmToolchain(
|
||||
jdkVersion: JdkMajorVersion
|
||||
) {
|
||||
// Ensure java only modules also set default toolchain
|
||||
configureJavaOnlyToolchain(jdkVersion)
|
||||
|
||||
plugins.withId("org.jetbrains.kotlin.jvm") {
|
||||
val kotlinExtension = extensions.getByType<KotlinTopLevelExtension>()
|
||||
|
||||
if (shouldOverrideObsoleteJdk(jdkVersion)) {
|
||||
kotlinExtension.jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion
|
||||
.set(JavaLanguageVersion.of(jdkVersion.overrideMajorVersion!!))
|
||||
}
|
||||
updateJvmTarget(jdkVersion.targetName)
|
||||
} else {
|
||||
kotlinExtension.jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion
|
||||
.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
|
||||
}
|
||||
}
|
||||
|
||||
tasks
|
||||
.matching { it.name != "compileJava9Java" && it is JavaCompile }
|
||||
.configureEach {
|
||||
with(this as JavaCompile) {
|
||||
options.compilerArgs.add("-proc:none")
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions.freeCompilerArgs += "-Xjvm-default=compatibility"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureJavaOnlyToolchain(
|
||||
jdkVersion: JdkMajorVersion
|
||||
) {
|
||||
plugins.withId("java-base") {
|
||||
val javaExtension = extensions.getByType<JavaPluginExtension>()
|
||||
if (shouldOverrideObsoleteJdk(jdkVersion)) {
|
||||
javaExtension.toolchain {
|
||||
languageVersion.set(
|
||||
JavaLanguageVersion.of(jdkVersion.overrideMajorVersion!!)
|
||||
)
|
||||
}
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
targetCompatibility = jdkVersion.targetName
|
||||
sourceCompatibility = jdkVersion.targetName
|
||||
}
|
||||
} else {
|
||||
javaExtension.toolchain {
|
||||
languageVersion.set(
|
||||
JavaLanguageVersion.of(jdkVersion.majorVersion)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinCompile.configureTaskToolchain(
|
||||
jdkVersion: JdkMajorVersion
|
||||
) {
|
||||
if (project.shouldOverrideObsoleteJdk(jdkVersion)) {
|
||||
kotlinJavaToolchain.toolchain.use(
|
||||
project.getToolchainLauncherFor(
|
||||
JdkMajorVersion.fromMajorVersion(
|
||||
jdkVersion.overrideMajorVersion!!
|
||||
)
|
||||
)
|
||||
)
|
||||
kotlinOptions {
|
||||
jvmTarget = jdkVersion.targetName
|
||||
}
|
||||
} else {
|
||||
kotlinJavaToolchain.toolchain.use(
|
||||
project.getToolchainLauncherFor(jdkVersion)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun JavaCompile.configureTaskToolchain(
|
||||
jdkVersion: JdkMajorVersion
|
||||
) {
|
||||
if (project.shouldOverrideObsoleteJdk(jdkVersion)) {
|
||||
javaCompiler.set(
|
||||
project.getToolchainCompilerFor(
|
||||
JdkMajorVersion.fromMajorVersion(
|
||||
jdkVersion.overrideMajorVersion!!
|
||||
)
|
||||
)
|
||||
)
|
||||
targetCompatibility = jdkVersion.targetName
|
||||
sourceCompatibility = jdkVersion.targetName
|
||||
} else {
|
||||
javaCompiler.set(project.getToolchainCompilerFor(jdkVersion))
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.updateJvmTarget(
|
||||
jvmTarget: String
|
||||
) {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions.jvmTarget = jvmTarget
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = jvmTarget
|
||||
targetCompatibility = jvmTarget
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.getToolchainCompilerFor(
|
||||
jdkVersion: JdkMajorVersion
|
||||
): Provider<JavaCompiler> {
|
||||
val service = project.extensions.getByType<JavaToolchainService>()
|
||||
return service.compilerFor {
|
||||
this.languageVersion.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.getToolchainLauncherFor(
|
||||
jdkVersion: JdkMajorVersion
|
||||
): Provider<JavaLauncher> {
|
||||
val service = project.extensions.getByType<JavaToolchainService>()
|
||||
val jdkVersionWithOverride = project.getJdkVersionWithOverride(jdkVersion)
|
||||
return service.launcherFor {
|
||||
this.languageVersion.set(JavaLanguageVersion.of(jdkVersionWithOverride.majorVersion))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Project.getJdkVersionWithOverride(jdkVersion: JdkMajorVersion): JdkMajorVersion {
|
||||
return if (project.shouldOverrideObsoleteJdk(jdkVersion)) {
|
||||
JdkMajorVersion.fromMajorVersion(jdkVersion.overrideMajorVersion!!)
|
||||
} else {
|
||||
jdkVersion
|
||||
}
|
||||
}
|
||||
@@ -1,75 +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.
|
||||
*/
|
||||
|
||||
@file:JvmName("LibrariesCommon")
|
||||
|
||||
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(
|
||||
moduleName: String,
|
||||
moduleOutputs: Collection<FileCollection> = setOf(sourceSets["main"].output)
|
||||
) {
|
||||
configurations["java9CompileClasspath"].extendsFrom(configurations["compileClasspath"])
|
||||
|
||||
tasks.named("compileJava9Java", JavaCompile::class.java) {
|
||||
dependsOn(moduleOutputs)
|
||||
|
||||
targetCompatibility = JavaVersion.VERSION_1_9.toString()
|
||||
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
|
||||
configureTaskToolchain(JdkMajorVersion.JDK_9)
|
||||
|
||||
// module-info.java should be in java9 source set by convention
|
||||
val java9SourceSet = sourceSets["java9"].java
|
||||
destinationDir = file("${java9SourceSet.outputDir}/META-INF/versions/9")
|
||||
options.sourcepath = files(java9SourceSet.srcDirs)
|
||||
val compileClasspath = configurations["java9CompileClasspath"]
|
||||
val moduleFiles = objects.fileCollection().from(moduleOutputs)
|
||||
val modulePath = compileClasspath.filter { it !in moduleFiles.files }
|
||||
classpath = objects.fileCollection().from()
|
||||
options.compilerArgumentProviders.add(
|
||||
Java9AdditionalArgumentsProvider(
|
||||
moduleName,
|
||||
moduleFiles,
|
||||
modulePath
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class Java9AdditionalArgumentsProvider(
|
||||
private val moduleName: String,
|
||||
private val moduleFiles: FileCollection,
|
||||
private val modulePath: FileCollection
|
||||
) : CommandLineArgumentProvider {
|
||||
override fun asArguments(): Iterable<String> = listOf(
|
||||
"--module-path", modulePath.asPath,
|
||||
"--patch-module", "$moduleName=${moduleFiles.asPath}",
|
||||
"-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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||
|
||||
import com.gradle.publish.PublishTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.ConfigurablePublishArtifact
|
||||
@@ -22,12 +22,12 @@ import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.publish.tasks.GenerateModuleMetadata
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.Upload
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer
|
||||
import plugins.KotlinBuildPublishingPlugin
|
||||
import plugins.mainPublicationName
|
||||
|
||||
|
||||
private const val MAGIC_DO_NOT_CHANGE_TEST_JAR_TASK_NAME = "testJar"
|
||||
@@ -62,6 +62,8 @@ fun Project.removeArtifacts(configuration: Configuration, task: Task) {
|
||||
|
||||
fun Project.noDefaultJar() {
|
||||
tasks.named("jar").configure {
|
||||
enabled = false
|
||||
actions = emptyList()
|
||||
configurations.forEach { cfg ->
|
||||
removeArtifacts(cfg, this)
|
||||
}
|
||||
@@ -110,8 +112,6 @@ fun <T : Jar> Project.runtimeJar(task: TaskProvider<T>, body: T.() -> Unit = {})
|
||||
addVariantsFromConfiguration(runtimeJar) { }
|
||||
}
|
||||
|
||||
(components.findByName("java") as AdhocComponentWithVariants?)?.addVariantsFromConfiguration(runtimeJar) { }
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
@@ -225,20 +225,24 @@ fun Project.publish(moduleMetadata: Boolean = false, configure: MavenPublication
|
||||
|
||||
val publication = extensions.findByType<PublishingExtension>()
|
||||
?.publications
|
||||
?.findByName(mainPublicationName) as MavenPublication
|
||||
?.findByName(KotlinBuildPublishingPlugin.PUBLICATION_NAME) as MavenPublication
|
||||
publication.configure()
|
||||
}
|
||||
|
||||
fun Project.publishGradlePlugin() {
|
||||
mainPublicationName = "pluginMaven"
|
||||
publish()
|
||||
fun Project.publishWithLegacyMavenPlugin(body: Upload.() -> Unit = {}): Upload {
|
||||
apply<plugins.PublishedKotlinModule>()
|
||||
|
||||
if (artifactsRemovedDiagnosticFlag) {
|
||||
error("`publish()` should be called before removing artifacts typically done in `noDefaultJar()` or `runtimeJar()` call")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
tasks.withType<PublishTask> {
|
||||
// Makes plugin publication task reuse poms and metadata from publication named "pluginMaven"
|
||||
useAutomatedPublishing()
|
||||
useGradleModuleMetadataIfAvailable()
|
||||
}
|
||||
if (configurations.findByName("classes-dirs") != null)
|
||||
throw GradleException("classesDirsArtifact() is incompatible with publish(), see sources comments for details")
|
||||
}
|
||||
|
||||
return (tasks.getByName("uploadArchives") as Upload).apply {
|
||||
body()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,41 +253,6 @@ fun Project.idePluginDependency(block: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.publishJarsForIde(projects: List<String>, libraryDependencies: List<String> = emptyList()) {
|
||||
idePluginDependency {
|
||||
publishProjectJars(projects, libraryDependencies)
|
||||
}
|
||||
configurations.all {
|
||||
// Don't allow `ideaIC` from compiler to leak into Kotlin plugin modules. Compiler and
|
||||
// plugin may depend on different versions of IDEA and it will lead to version conflict
|
||||
exclude(module = ideModuleName())
|
||||
}
|
||||
dependencies {
|
||||
projects.forEach {
|
||||
jpsLikeJarDependency(project(it), JpsDepScope.COMPILE, { isTransitive = false }, exported = true)
|
||||
}
|
||||
libraryDependencies.forEach {
|
||||
jpsLikeJarDependency(it, JpsDepScope.COMPILE, exported = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.publishTestJarsForIde(projectNames: List<String>) {
|
||||
idePluginDependency {
|
||||
publishTestJar(projectNames)
|
||||
}
|
||||
configurations.all {
|
||||
// Don't allow `ideaIC` from compiler to leak into Kotlin plugin modules. Compiler and
|
||||
// plugin may depend on different versions of IDEA and it will lead to version conflict
|
||||
exclude(module = ideModuleName())
|
||||
}
|
||||
dependencies {
|
||||
for (projectName in projectNames) {
|
||||
jpsLikeJarDependency(projectTests(projectName), JpsDepScope.COMPILE, exported = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.publishProjectJars(projects: List<String>, libraryDependencies: List<String> = emptyList()) {
|
||||
apply<JavaPlugin>()
|
||||
|
||||
@@ -322,15 +291,13 @@ fun Project.publishProjectJars(projects: List<String>, libraryDependencies: List
|
||||
javadocJar()
|
||||
}
|
||||
|
||||
fun Project.publishTestJar(projects: List<String>) {
|
||||
fun Project.publishTestJar(projectName: String) {
|
||||
apply<JavaPlugin>()
|
||||
|
||||
val fatJarContents by configurations.creating
|
||||
|
||||
dependencies {
|
||||
for (projectName in projects) {
|
||||
fatJarContents(project(projectName, configuration = "tests-jar")) { isTransitive = false }
|
||||
}
|
||||
fatJarContents(project(projectName, configuration = "tests-jar")) { isTransitive = false }
|
||||
}
|
||||
|
||||
publish()
|
||||
@@ -347,7 +314,7 @@ fun Project.publishTestJar(projects: List<String>) {
|
||||
|
||||
sourcesJar {
|
||||
from {
|
||||
projects.map { project(it).testSourceSet.allSource }
|
||||
project(projectName).testSourceSet.allSource
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,13 @@
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
|
||||
import org.gradle.kotlin.dsl.closureOf
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.project
|
||||
import java.io.File
|
||||
@@ -36,7 +34,7 @@ val Project.internalBootstrapRepo: String? get() =
|
||||
when {
|
||||
bootstrapKotlinRepo?.startsWith("https://buildserver.labs.intellij.net") == true ->
|
||||
bootstrapKotlinRepo!!.replace("artifacts/content/maven", "artifacts/content/internal/repo")
|
||||
else -> "https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Aggregate),number:$bootstrapKotlinVersion," +
|
||||
else -> "https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:$bootstrapKotlinVersion," +
|
||||
"branch:default:any/artifacts/content/internal/repo/"
|
||||
}
|
||||
|
||||
@@ -91,6 +89,12 @@ fun Project.preloadedDeps(
|
||||
return files(*matchingFiles.map { it.canonicalPath }.toTypedArray())
|
||||
}
|
||||
|
||||
fun Project.ideaUltimatePreloadedDeps(vararg artifactBaseNames: String, subdir: String? = null): ConfigurableFileCollection {
|
||||
val ultimateDepsDir = fileFrom(rootDir, "ultimate", "dependencies")
|
||||
return if (ultimateDepsDir.isDirectory) preloadedDeps(*artifactBaseNames, baseDir = ultimateDepsDir, subdir = subdir)
|
||||
else files()
|
||||
}
|
||||
|
||||
fun Project.kotlinDep(artifactBaseName: String, version: String, classifier: String? = null): String =
|
||||
listOfNotNull("org.jetbrains.kotlin:kotlin-$artifactBaseName:$version", classifier).joinToString(":")
|
||||
|
||||
@@ -111,95 +115,6 @@ fun DependencyHandler.projectTests(name: String): ProjectDependency = project(na
|
||||
fun DependencyHandler.projectRuntimeJar(name: String): ProjectDependency = project(name, configuration = "runtimeJar")
|
||||
fun DependencyHandler.projectArchives(name: String): ProjectDependency = project(name, configuration = "archives")
|
||||
|
||||
enum class JpsDepScope {
|
||||
COMPILE, TEST, RUNTIME, PROVIDED
|
||||
}
|
||||
|
||||
fun DependencyHandler.add(configurationName: String, dependencyNotation: Any, configure: (ModuleDependency.() -> Unit)?) {
|
||||
// Avoid `dependencyNotation` to `ModuleDependency` class cast exception if possible
|
||||
if (configure != null) {
|
||||
add(configurationName, dependencyNotation, closureOf(configure))
|
||||
} else {
|
||||
add(configurationName, dependencyNotation)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.disableDependencyVerification() {
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
disableDependencyVerification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DependencyHandler.jpsLikeJarDependency(
|
||||
dependencyNotation: Any,
|
||||
scope: JpsDepScope,
|
||||
dependencyConfiguration: (ModuleDependency.() -> Unit)? = null,
|
||||
exported: Boolean = false
|
||||
) {
|
||||
when (scope) {
|
||||
JpsDepScope.COMPILE -> {
|
||||
if (exported) {
|
||||
add("api", dependencyNotation, dependencyConfiguration)
|
||||
add("testCompile", dependencyNotation, dependencyConfiguration)
|
||||
} else {
|
||||
add("implementation", dependencyNotation, dependencyConfiguration)
|
||||
}
|
||||
}
|
||||
JpsDepScope.TEST -> {
|
||||
if (exported) {
|
||||
add("testCompile", dependencyNotation, dependencyConfiguration)
|
||||
} else {
|
||||
add("testImplementation", dependencyNotation, dependencyConfiguration)
|
||||
}
|
||||
}
|
||||
JpsDepScope.RUNTIME -> {
|
||||
add("testRuntimeOnly", dependencyNotation, dependencyConfiguration)
|
||||
}
|
||||
JpsDepScope.PROVIDED -> {
|
||||
if (exported) {
|
||||
add("compileOnlyApi", dependencyNotation, dependencyConfiguration)
|
||||
add("testCompile", dependencyNotation, dependencyConfiguration)
|
||||
} else {
|
||||
add("compileOnly", dependencyNotation, dependencyConfiguration)
|
||||
add("testImplementation", dependencyNotation, dependencyConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DependencyHandler.jpsLikeModuleDependency(moduleName: String, scope: JpsDepScope, exported: Boolean = false) {
|
||||
jpsLikeJarDependency(project(moduleName), scope, exported = exported)
|
||||
when (scope) {
|
||||
JpsDepScope.COMPILE -> {
|
||||
if (exported) {
|
||||
add("testCompile", projectTests(moduleName))
|
||||
} else {
|
||||
add("testImplementation", projectTests(moduleName))
|
||||
}
|
||||
}
|
||||
JpsDepScope.TEST -> {
|
||||
if (exported) {
|
||||
add("testCompile", projectTests(moduleName))
|
||||
} else {
|
||||
add("testImplementation", projectTests(moduleName))
|
||||
}
|
||||
}
|
||||
JpsDepScope.RUNTIME -> {
|
||||
add("runtimeOnly", projectTests(moduleName))
|
||||
}
|
||||
JpsDepScope.PROVIDED -> {
|
||||
if (exported) {
|
||||
add("testCompile", projectTests(moduleName))
|
||||
} else {
|
||||
add("testImplementation", projectTests(moduleName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun Project.testApiJUnit5(
|
||||
vintageEngine: Boolean = false,
|
||||
runner: Boolean = false,
|
||||
@@ -289,16 +204,14 @@ fun Project.firstFromJavaHomeThatExists(vararg paths: String, jdkHome: File = Fi
|
||||
|
||||
fun Project.toolsJarApi(): Any =
|
||||
if (kotlinBuildProperties.isInJpsBuildIdeaSync)
|
||||
toolsJar()
|
||||
files(toolsJarFile() ?: error("tools.jar is not found!"))
|
||||
else
|
||||
dependencies.project(":dependencies:tools-jar-api")
|
||||
|
||||
fun Project.toolsJar(): FileCollection = files(
|
||||
getToolchainLauncherFor(JdkMajorVersion.JDK_1_8)
|
||||
.map {
|
||||
Jvm.forHome(it.metadata.installationPath.asFile).toolsJar ?: throw GradleException("tools.jar not found!")
|
||||
}
|
||||
)
|
||||
fun Project.toolsJar(): FileCollection = files(toolsJarFile() ?: error("tools.jar is not found!"))
|
||||
|
||||
fun Project.toolsJarFile(jdkHome: File = File(this.property("JDK_18") as String)): File? =
|
||||
firstFromJavaHomeThatExists("lib/tools.jar", jdkHome = jdkHome)
|
||||
|
||||
val compilerManifestClassPath
|
||||
get() = "annotations-13.0.jar kotlin-stdlib.jar kotlin-reflect.jar kotlin-script-runtime.jar trove4j.jar"
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySubstitution
|
||||
import org.gradle.api.artifacts.component.ProjectComponentSelector
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
@@ -82,24 +80,6 @@ private fun Project.compilerShadowJar(taskName: String, body: ShadowJar.() -> Un
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureShadowJarSubstitutionInCompileClasspath() {
|
||||
val substitutionMap = mapOf(":kotlin-reflect" to ":kotlin-reflect-api")
|
||||
|
||||
fun configureSubstitution(substitution: DependencySubstitution) {
|
||||
val requestedProject = (substitution.requested as? ProjectComponentSelector)?.projectPath ?: return
|
||||
val replacementProject = substitutionMap[requestedProject] ?: return
|
||||
substitution.useTarget(project(replacementProject), "Non-default shadow jars should not be used in compile classpath")
|
||||
}
|
||||
|
||||
sourceSets.all {
|
||||
for (configName in listOf(compileOnlyConfigurationName, compileClasspathConfigurationName)) {
|
||||
configurations.getByName(configName).resolutionStrategy.dependencySubstitution {
|
||||
all(::configureSubstitution)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.embeddableCompiler(taskName: String = "embeddable", body: ShadowJar.() -> Unit = {}): TaskProvider<out ShadowJar> =
|
||||
compilerShadowJar(taskName) {
|
||||
configureEmbeddableCompilerRelocation()
|
||||
@@ -113,6 +93,7 @@ fun Project.compilerDummyForDependenciesRewriting(
|
||||
exclude(packagesToExcludeFromDummy)
|
||||
body()
|
||||
}
|
||||
|
||||
const val COMPILER_DUMMY_JAR_CONFIGURATION_NAME = "compilerDummyJar"
|
||||
|
||||
fun Project.compilerDummyJar(task: TaskProvider<out Jar>, body: Jar.() -> Unit = {}) {
|
||||
|
||||
@@ -1,51 +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.
|
||||
*/
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
publishGradlePlugin()
|
||||
standardPublicJars()
|
||||
|
||||
extensions.extraProperties["kotlin.stdlib.default.dependency"] = "false"
|
||||
|
||||
dependencies {
|
||||
compileOnly(kotlinStdlib())
|
||||
compileOnly(gradleApi())
|
||||
}
|
||||
|
||||
// These dependencies will be provided by Gradle and we should prevent version conflict
|
||||
fun Configuration.excludeGradleCommonDependencies() {
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-reflect")
|
||||
exclude(group = "org.jetbrains.kotlin", module = "kotlin-script-runtime")
|
||||
}
|
||||
configurations {
|
||||
"implementation" {
|
||||
excludeGradleCommonDependencies()
|
||||
}
|
||||
"api" {
|
||||
excludeGradleCommonDependencies()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.languageVersion = "1.3"
|
||||
kotlinOptions.apiVersion = "1.3"
|
||||
kotlinOptions.freeCompilerArgs += listOf(
|
||||
"-Xskip-prerelease-check",
|
||||
"-Xskip-runtime-version-check",
|
||||
"-Xsuppress-version-warnings"
|
||||
)
|
||||
}
|
||||
|
||||
tasks.named<Jar>("jar") {
|
||||
callGroovy("manifestAttributes", manifest, project)
|
||||
}
|
||||
@@ -24,16 +24,16 @@ fun ProjectSettings.compiler(block: IdeaCompilerConfiguration.() -> Unit) =
|
||||
fun ProjectSettings.delegateActions(block: ActionDelegationConfig.() -> Unit) =
|
||||
(this@delegateActions as ExtensionAware).extensions.configure(block)
|
||||
|
||||
fun ProjectSettings.runConfigurations(block: RunConfigurationContainer.() -> Unit) =
|
||||
fun ProjectSettings.runConfigurations(block: DefaultRunConfigurationContainer.() -> Unit) =
|
||||
(this@runConfigurations as ExtensionAware).extensions.configure("runConfigurations", block)
|
||||
|
||||
inline fun <reified T: RunConfiguration> RunConfigurationContainer.defaults(noinline block: T.() -> Unit) =
|
||||
inline fun <reified T: RunConfiguration> DefaultRunConfigurationContainer.defaults(noinline block: T.() -> Unit) =
|
||||
defaults(T::class.java, block)
|
||||
|
||||
fun RunConfigurationContainer.junit(name: String, block: JUnit.() -> Unit) =
|
||||
fun DefaultRunConfigurationContainer.junit(name: String, block: JUnit.() -> Unit) =
|
||||
create(name, JUnit::class.java, block)
|
||||
|
||||
fun RunConfigurationContainer.application(name: String, block: Application.() -> Unit) =
|
||||
fun DefaultRunConfigurationContainer.application(name: String, block: Application.() -> Unit) =
|
||||
create(name, Application::class.java, block)
|
||||
|
||||
fun ProjectSettings.ideArtifacts(block: NamedDomainObjectContainer<org.jetbrains.gradle.ext.TopLevelArtifact>.() -> Unit) =
|
||||
|
||||
193
buildSrc/src/main/kotlin/jdksFinder.kt
Normal file
193
buildSrc/src/main/kotlin/jdksFinder.kt
Normal file
@@ -0,0 +1,193 @@
|
||||
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||
|
||||
import net.rubygrapefruit.platform.Native
|
||||
import net.rubygrapefruit.platform.WindowsRegistry
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import java.nio.file.Paths
|
||||
import java.io.File
|
||||
import net.rubygrapefruit.platform.WindowsRegistry.Key.HKEY_LOCAL_MACHINE
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
enum class JdkMajorVersion(private val mandatory: Boolean = true) {
|
||||
JDK_16, JDK_17, JDK_18, JDK_9, JDK_10(false), JDK_11(false), /*15.0*/JDK_15(false);
|
||||
|
||||
fun isMandatory(): Boolean = mandatory
|
||||
}
|
||||
|
||||
val jdkAlternativeVarNames = mapOf(JdkMajorVersion.JDK_9 to listOf("JDK_19"), JdkMajorVersion.JDK_15 to listOf("JDK_15_0"))
|
||||
|
||||
data class JdkId(val explicit: Boolean, val majorVersion: JdkMajorVersion, var version: String, var homeDir: File)
|
||||
|
||||
fun Project.getConfiguredJdks(): List<JdkId> {
|
||||
val res = arrayListOf<JdkId>()
|
||||
for (jdkMajorVersion in JdkMajorVersion.values()) {
|
||||
val explicitJdkEnvVal = findProperty(jdkMajorVersion.name)?.toString()
|
||||
?: System.getenv(jdkMajorVersion.name)
|
||||
?: jdkAlternativeVarNames[jdkMajorVersion]?.mapNotNull { System.getenv(it) }?.firstOrNull()
|
||||
?: continue
|
||||
val explicitJdk = Paths.get(explicitJdkEnvVal).toRealPath().toFile()
|
||||
if (!explicitJdk.isDirectory) {
|
||||
throw GradleException("Invalid environment value $jdkMajorVersion: $explicitJdkEnvVal, expecting JDK home path")
|
||||
}
|
||||
res.add(JdkId(true, jdkMajorVersion, "X", explicitJdk))
|
||||
}
|
||||
if (res.size < JdkMajorVersion.values().size) {
|
||||
res.discoverJdks(this)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// see JEP 223
|
||||
private val javaMajorVersionRegex = Regex("""(?:1\.)?(\d+).*""")
|
||||
private val javaVersionRegex = Regex("""(?:1\.)?(\d+)(\.\d+)?([+-_]\w+){0,3}""")
|
||||
|
||||
fun MutableCollection<JdkId>.addIfBetter(project: Project, version: String, id: String, homeDir: File): Boolean {
|
||||
val matchString = javaMajorVersionRegex.matchEntire(version)?.groupValues?.get(1)
|
||||
val majorJdkVersion = when (matchString) {
|
||||
"6" -> JdkMajorVersion.JDK_16
|
||||
"7" -> JdkMajorVersion.JDK_17
|
||||
"8" -> JdkMajorVersion.JDK_18
|
||||
"9" -> JdkMajorVersion.JDK_9
|
||||
else -> {
|
||||
project.logger.info("Cannot recognize version string '$version' (found version '$matchString')")
|
||||
return false
|
||||
}
|
||||
}
|
||||
val prev = find { it.majorVersion == majorJdkVersion }
|
||||
if (prev == null) {
|
||||
add(JdkId(false, majorJdkVersion, version, homeDir))
|
||||
return true
|
||||
}
|
||||
if (prev.explicit) return false
|
||||
val versionsComparisonRes = compareVersions(prev.version, version)
|
||||
if (versionsComparisonRes < 0 || (versionsComparisonRes == 0 && id.contains("64"))) { // prefer 64-bit
|
||||
prev.version = version
|
||||
prev.homeDir = homeDir
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun compareVersions(left: String, right: String): Int {
|
||||
if (left == right) return 0
|
||||
fun MatchResult.extractNumVer(): List<Int> =
|
||||
groups.drop(2).map {
|
||||
it?.value?.filter { it in '0'..'9' }?.toIntOrNull() ?: 0
|
||||
}
|
||||
val lmi = (javaVersionRegex.matchEntire(left)?.extractNumVer() ?: emptyList()).iterator()
|
||||
val rmi = (javaVersionRegex.matchEntire(right)?.extractNumVer() ?: emptyList()).iterator()
|
||||
while (lmi.hasNext() && rmi.hasNext()) {
|
||||
val l = lmi.next()
|
||||
val r = rmi.next()
|
||||
when {
|
||||
l < r -> return -1
|
||||
l > r -> return 1
|
||||
}
|
||||
}
|
||||
return when {
|
||||
rmi.hasNext() -> -1
|
||||
lmi.hasNext() -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdks(project: Project) {
|
||||
val os = OperatingSystem.current()
|
||||
when {
|
||||
os.isWindows -> discoverJdksOnWindows(project)
|
||||
os.isMacOsX -> discoverJdksOnMacOS(project)
|
||||
else -> discoverJdksOnUnix(project)
|
||||
}
|
||||
}
|
||||
|
||||
private val macOsJavaHomeOutRegexes =
|
||||
listOf(
|
||||
Regex("""\s+(\S+),\s+(\S+):\s+".*?"\s+(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.*?)\):\s+(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.*?)\)\s+"[^"]*"\s+-\s+"[^"]*"\s(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.+)\)\s+".+"\s+-\s+".+"\s+(.+)"""))
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnMacOS(project: Project) {
|
||||
val procBuilder = ProcessBuilder("/usr/libexec/java_home", "-V").redirectErrorStream(true)
|
||||
val process = procBuilder.start()
|
||||
val retCode = process.waitFor()
|
||||
if (retCode != 0) throw GradleException("Unable to run 'java_home', return code $retCode")
|
||||
process.inputStream.bufferedReader().forEachLine { line ->
|
||||
for (rex in macOsJavaHomeOutRegexes) {
|
||||
val matchResult = rex.matchEntire(line)
|
||||
if (matchResult != null) {
|
||||
addIfBetter(project, matchResult.groupValues[1], matchResult.groupValues[0], File(matchResult.groupValues[3]))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val unixConventionalJdkLocations = listOf(
|
||||
"/usr/lib/jvm", // *deb, Arch
|
||||
"/opt", // *rpm, Gentoo, HP/UX
|
||||
"/usr/lib", // Slackware 32
|
||||
"/usr/lib64", // Slackware 64
|
||||
"/usr/local", // OpenBSD, FreeBSD
|
||||
"/usr/pkg/java", // NetBSD
|
||||
"/usr/jdk/instances") // Solaris
|
||||
|
||||
private val unixConventionalJdkDirRex = Regex("jdk|jre|java|zulu")
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnUnix(project: Project) {
|
||||
for (loc in unixConventionalJdkLocations) {
|
||||
val installedJdks = File(loc).listFiles { dir ->
|
||||
dir.isDirectory &&
|
||||
unixConventionalJdkDirRex.containsMatchIn(dir.name) &&
|
||||
fileFrom(dir, "bin", "java").isFile
|
||||
} ?: continue
|
||||
for (dir in installedJdks) {
|
||||
val versionMatch = javaVersionRegex.find(dir.name)
|
||||
if (versionMatch == null) {
|
||||
project.logger.info("Unable to extract version from possible JDK dir: $dir")
|
||||
}
|
||||
else {
|
||||
addIfBetter(project, versionMatch.value, dir.name, dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val windowsConventionalJdkRegistryPaths = listOf(
|
||||
"SOFTWARE\\JavaSoft\\Java Development Kit",
|
||||
"SOFTWARE\\Wow6432Node\\JavaSoft\\Java Development Kit",
|
||||
"SOFTWARE\\JavaSoft\\JDK",
|
||||
"SOFTWARE\\Wow6432Node\\JavaSoft\\JDK")
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnWindows(project: Project) {
|
||||
val registry = Native.get(WindowsRegistry::class.java)
|
||||
for (regPath in windowsConventionalJdkRegistryPaths) {
|
||||
val jdkKeys = try {
|
||||
registry.getSubkeys(HKEY_LOCAL_MACHINE, regPath)
|
||||
} catch (e: RuntimeException) {
|
||||
// ignore missing nodes
|
||||
continue
|
||||
}
|
||||
for (jdkKey in jdkKeys) {
|
||||
try {
|
||||
val javaHome = registry.getStringValue(HKEY_LOCAL_MACHINE, regPath + "\\" + jdkKey, "JavaHome")
|
||||
val versionMatch = javaVersionRegex.find(jdkKey)
|
||||
if (versionMatch == null) {
|
||||
project.logger.info("Unable to extract version from possible JDK location: $javaHome ($jdkKey)")
|
||||
}
|
||||
else {
|
||||
javaHome.takeIf { it.isNotEmpty() }
|
||||
?.let { File(it) }
|
||||
?.takeIf { it.isDirectory && fileFrom(it, "bin", "java.exe").isFile }
|
||||
?.let {
|
||||
addIfBetter(project, versionMatch.value, jdkKey, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,26 +35,16 @@ private fun Project.kotlinBuildLocalDependenciesDir(): File =
|
||||
|
||||
private fun Project.kotlinBuildLocalRepoDir(): File = kotlinBuildLocalDependenciesDir().resolve("repo")
|
||||
|
||||
fun Project.ideModuleName() = when (IdeVersionConfigurator.currentIde.kind) {
|
||||
private fun Project.ideModuleName() = when (IdeVersionConfigurator.currentIde.kind) {
|
||||
Ide.Kind.AndroidStudio -> "android-studio-ide"
|
||||
Ide.Kind.IntelliJ -> "ideaIC"
|
||||
}
|
||||
|
||||
private fun Project.ideModuleVersion(forIde: Boolean) = when (IdeVersionConfigurator.currentIde.kind) {
|
||||
Ide.Kind.AndroidStudio -> rootProject.findProperty("versions.androidStudioBuild")
|
||||
Ide.Kind.IntelliJ -> {
|
||||
if (forIde) {
|
||||
intellijSdkVersionForIde()
|
||||
?: error("Please specify 'attachedIntellijVersion' in your local.properties")
|
||||
} else {
|
||||
rootProject.findProperty("versions.intellijSdk")
|
||||
}
|
||||
if (kotlinBuildProperties.intellijUltimateEnabled) "ideaIU" else "ideaIC"
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.intellijSdkVersionForIde(): String? {
|
||||
val majorVersion = kotlinBuildProperties.getOrNull("attachedIntellijVersion") as? String ?: return null
|
||||
return rootProject.findProperty("versions.intellijSdk.forIde.$majorVersion") as? String
|
||||
private fun Project.ideModuleVersion() = when (IdeVersionConfigurator.currentIde.kind) {
|
||||
Ide.Kind.AndroidStudio -> rootProject.findProperty("versions.androidStudioBuild")
|
||||
Ide.Kind.IntelliJ -> rootProject.findProperty("versions.intellijSdk")
|
||||
}
|
||||
|
||||
fun RepositoryHandler.kotlinBuildLocalRepo(project: Project): IvyArtifactRepository = ivy {
|
||||
@@ -68,7 +58,6 @@ fun RepositoryHandler.kotlinBuildLocalRepo(project: Project): IvyArtifactReposit
|
||||
|
||||
artifact("[organisation]/[module]/[revision]/artifacts/lib/[artifact](-[classifier]).[ext]")
|
||||
artifact("[organisation]/[module]/[revision]/artifacts/[artifact](-[classifier]).[ext]")
|
||||
artifact("[organisation]/intellij-core/[revision]/artifacts/[artifact](-[classifier]).[ext]")
|
||||
artifact("[organisation]/${project.ideModuleName()}/[revision]/artifacts/plugins/[module]/lib/[artifact](-[classifier]).[ext]") // bundled plugins
|
||||
artifact("[organisation]/sources/[artifact]-[revision](-[classifier]).[ext]")
|
||||
artifact("[organisation]/[module]/[revision]/[artifact](-[classifier]).[ext]")
|
||||
@@ -79,9 +68,7 @@ fun RepositoryHandler.kotlinBuildLocalRepo(project: Project): IvyArtifactReposit
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun Project.intellijDep(module: String? = null, forIde: Boolean = false) =
|
||||
"kotlin.build:${module ?: ideModuleName()}:${ideModuleVersion(forIde)}"
|
||||
fun Project.intellijDep(module: String? = null) = "kotlin.build:${module ?: ideModuleName()}:${ideModuleVersion()}"
|
||||
|
||||
fun Project.intellijCoreDep() = "kotlin.build:intellij-core:${rootProject.extra["versions.intellijSdk"]}"
|
||||
|
||||
@@ -106,7 +93,11 @@ fun Project.kotlinxCollectionsImmutable() = "org.jetbrains.kotlinx:kotlinx-colle
|
||||
*/
|
||||
fun Project.intellijRuntimeAnnotations() = "kotlin.build:intellij-runtime-annotations:${rootProject.extra["versions.intellijSdk"]}"
|
||||
|
||||
fun Project.intellijPluginDep(plugin: String, forIde: Boolean = false) = intellijDep(plugin, forIde)
|
||||
fun Project.intellijPluginDep(plugin: String) = intellijDep(plugin)
|
||||
|
||||
fun Project.intellijUltimateDep() = intellijDep("ideaIU")
|
||||
|
||||
fun Project.intellijUltimatePluginDep(plugin: String) = intellijDep(plugin)
|
||||
|
||||
fun ModuleDependency.includeJars(vararg names: String, rootProject: Project? = null) {
|
||||
names.forEach {
|
||||
@@ -134,7 +125,7 @@ object IntellijRootUtils {
|
||||
fun getIntellijRootDir(project: Project): File = with(project.rootProject) {
|
||||
return File(
|
||||
getRepositoryRootDir(this),
|
||||
"${ideModuleName()}/${ideModuleVersion(forIde = false)}/artifacts"
|
||||
"${ideModuleName()}/${ideModuleVersion()}/artifacts"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -146,8 +137,19 @@ fun ModuleDependency.includeIntellijCoreJarDependencies(project: Project, jarsFi
|
||||
rootProject = project.rootProject
|
||||
)
|
||||
|
||||
fun Project.isIntellijCommunityAvailable() =
|
||||
!(rootProject.extra["intellijUltimateEnabled"] as Boolean) || rootProject.extra["intellijSeparateSdks"] as Boolean
|
||||
|
||||
fun Project.isIntellijUltimateSdkAvailable() = (rootProject.extra["intellijUltimateEnabled"] as Boolean)
|
||||
|
||||
fun Project.intellijRootDir() = IntellijRootUtils.getIntellijRootDir(project)
|
||||
|
||||
fun Project.intellijUltimateRootDir() =
|
||||
if (isIntellijUltimateSdkAvailable())
|
||||
File(kotlinBuildLocalRepoDir(), "kotlin.build/ideaIU/${rootProject.extra["versions.intellijSdk"]}/artifacts")
|
||||
else
|
||||
throw GradleException("intellij ultimate SDK is not available")
|
||||
|
||||
fun DependencyHandlerScope.excludeInAndroidStudio(rootProject: Project, block: DependencyHandlerScope.() -> Unit) {
|
||||
if (!rootProject.extra.has("versions.androidStudioRelease")) {
|
||||
block()
|
||||
@@ -181,7 +183,9 @@ fun Project.runIdeTask(name: String, ideaPluginDir: File, ideaSandboxDir: File,
|
||||
"-Dplugin.path=${ideaPluginDir.absolutePath}"
|
||||
)
|
||||
|
||||
jvmArgs("-Didea.platform.prefix=Idea")
|
||||
if (Platform[201].orHigher() && !isIntellijUltimateSdkAvailable()) {
|
||||
jvmArgs("-Didea.platform.prefix=Idea")
|
||||
}
|
||||
|
||||
if (rootProject.findProperty("versions.androidStudioRelease") != null) {
|
||||
jvmArgs("-Didea.platform.prefix=AndroidStudio")
|
||||
|
||||
@@ -14,7 +14,6 @@ import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import plugins.KotlinBuildPublishingPlugin
|
||||
import plugins.configureRepository
|
||||
import plugins.mainPublicationName
|
||||
import java.util.*
|
||||
|
||||
internal const val PLUGIN_MARKER_SUFFIX = ".gradle.plugin"
|
||||
@@ -27,7 +26,7 @@ fun Project.publishPluginMarkers(withEmptyJars: Boolean = true) {
|
||||
|
||||
val pluginDevelopment = extensions.getByType<PluginBundleExtension>()
|
||||
val publishingExtension = extensions.getByType<PublishingExtension>()
|
||||
val mainPublication = publishingExtension.publications[mainPublicationName] as MavenPublication
|
||||
val mainPublication = publishingExtension.publications[KotlinBuildPublishingPlugin.PUBLICATION_NAME] as MavenPublication
|
||||
|
||||
pluginDevelopment.plugins.forEach { declaration ->
|
||||
val markerPublication = createMavenMarkerPublication(declaration, mainPublication, publishingExtension.publications)
|
||||
|
||||
@@ -68,7 +68,7 @@ class KotlinBuildPublishingPlugin @Inject constructor(
|
||||
|
||||
configure<PublishingExtension> {
|
||||
publications {
|
||||
create<MavenPublication>(project.mainPublicationName) {
|
||||
create<MavenPublication>(PUBLICATION_NAME) {
|
||||
from(kotlinLibraryComponent)
|
||||
|
||||
configureKotlinPomAttributes(project)
|
||||
@@ -79,26 +79,16 @@ class KotlinBuildPublishingPlugin @Inject constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_MAIN_PUBLICATION_NAME = "Main"
|
||||
const val MAIN_PUBLICATION_NAME_PROPERTY = "MainPublicationName"
|
||||
const val PUBLICATION_NAME = "Main"
|
||||
const val REPOSITORY_NAME = "Maven"
|
||||
const val ADHOC_COMPONENT_NAME = "kotlinLibrary"
|
||||
|
||||
const val COMPILE_CONFIGURATION = "publishedCompile"
|
||||
const val RUNTIME_CONFIGURATION = "publishedRuntime"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var Project.mainPublicationName: String
|
||||
get() {
|
||||
return if (project.extra.has(KotlinBuildPublishingPlugin.MAIN_PUBLICATION_NAME_PROPERTY))
|
||||
project.extra.get(KotlinBuildPublishingPlugin.MAIN_PUBLICATION_NAME_PROPERTY) as String
|
||||
else KotlinBuildPublishingPlugin.DEFAULT_MAIN_PUBLICATION_NAME
|
||||
}
|
||||
set(value) {
|
||||
project.extra.set(KotlinBuildPublishingPlugin.MAIN_PUBLICATION_NAME_PROPERTY, value)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun humanReadableName(name: String) =
|
||||
name.split("-").joinToString(separator = " ") { it.capitalize(Locale.ROOT) }
|
||||
@@ -174,17 +164,7 @@ fun Project.configureDefaultPublishing() {
|
||||
private fun Project.configureSigning() {
|
||||
configure<SigningExtension> {
|
||||
sign(extensions.getByType<PublishingExtension>().publications) // all publications
|
||||
|
||||
val signKeyId = project.findProperty("signKeyId") as? String
|
||||
if (!signKeyId.isNullOrBlank()) {
|
||||
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()
|
||||
}
|
||||
useGpgCmd()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
173
buildSrc/src/main/kotlin/plugins/PublishedKotlinModule.kt
Normal file
173
buildSrc/src/main/kotlin/plugins/PublishedKotlinModule.kt
Normal file
@@ -0,0 +1,173 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
package plugins
|
||||
|
||||
import org.codehaus.groovy.runtime.InvokerHelper
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.maven.Conf2ScopeMappingContainer
|
||||
import org.gradle.api.artifacts.maven.MavenDeployment
|
||||
import org.gradle.api.artifacts.maven.MavenResolver
|
||||
import org.gradle.api.plugins.MavenPluginConvention
|
||||
import org.gradle.api.plugins.MavenRepositoryHandlerConvention
|
||||
import org.gradle.api.publication.maven.internal.deployer.MavenRemoteRepository
|
||||
import org.gradle.api.tasks.Upload
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.plugins.signing.Sign
|
||||
import org.gradle.plugins.signing.SigningExtension
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
* Configures a Kotlin module for publication.
|
||||
*/
|
||||
open class PublishedKotlinModule : Plugin<Project> {
|
||||
|
||||
private fun String.toBooleanOrNull() = listOf(true, false).firstOrNull { it.toString().equals(this, ignoreCase = true) }
|
||||
|
||||
override fun apply(project: Project) {
|
||||
|
||||
project.run {
|
||||
|
||||
plugins.apply("maven")
|
||||
|
||||
configurations.maybeCreate("publishedRuntime").apply {
|
||||
the<MavenPluginConvention>()
|
||||
.conf2ScopeMappings
|
||||
.addMapping(0, this, Conf2ScopeMappingContainer.RUNTIME)
|
||||
}
|
||||
|
||||
configurations.maybeCreate("publishedCompile").apply {
|
||||
the<MavenPluginConvention>()
|
||||
.conf2ScopeMappings
|
||||
.addMapping(0, this, Conf2ScopeMappingContainer.COMPILE)
|
||||
}
|
||||
|
||||
if (!project.hasProperty("prebuiltJar")) {
|
||||
plugins.apply("signing")
|
||||
|
||||
val signingRequired = project.findProperty("signingRequired")?.toString()?.toBooleanOrNull()
|
||||
?: project.property("isSonatypeRelease") as Boolean
|
||||
|
||||
configure<SigningExtension> {
|
||||
isRequired = signingRequired
|
||||
sign(configurations["archives"])
|
||||
useGpgCmd()
|
||||
}
|
||||
|
||||
tasks.named<Sign>("signArchives").configure {
|
||||
enabled = signingRequired
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenResolver.configurePom() {
|
||||
pom.project {
|
||||
withGroovyBuilder {
|
||||
"licenses" {
|
||||
"license" {
|
||||
"name"("The Apache Software License, Version 2.0")
|
||||
"url"("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
"distribution"("repo")
|
||||
}
|
||||
}
|
||||
"name"("${project.group}:${project.name}")
|
||||
"packaging"("jar")
|
||||
// optionally artifactId can be defined here
|
||||
"description"(project.description)
|
||||
"url"("https://kotlinlang.org/")
|
||||
"licenses" {
|
||||
"license" {
|
||||
"name"("The Apache License, Version 2.0")
|
||||
"url"("http://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
}
|
||||
}
|
||||
"scm" {
|
||||
"url"("https://github.com/JetBrains/kotlin")
|
||||
"connection"("scm:git:https://github.com/JetBrains/kotlin.git")
|
||||
"developerConnection"("scm:git:https://github.com/JetBrains/kotlin.git")
|
||||
}
|
||||
"developers" {
|
||||
"developer" {
|
||||
"name"("Kotlin Team")
|
||||
setProperty("organization", "JetBrains")
|
||||
"organizationUrl"("https://www.jetbrains.com")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pom.whenConfigured {
|
||||
dependencies.removeIf {
|
||||
InvokerHelper.getMetaClass(it).getProperty(it, "scope") == "test"
|
||||
}
|
||||
|
||||
dependencies
|
||||
.find {
|
||||
InvokerHelper.getMetaClass(it).getProperty(it, "groupId") == "org.jetbrains.kotlin"
|
||||
&& InvokerHelper.getMetaClass(it).getProperty(it, "artifactId") == "kotlin-stdlib"
|
||||
}
|
||||
?.also {
|
||||
InvokerHelper.getMetaClass(it).setProperty(it, "exclusions", emptyList<Any>())
|
||||
logger.warn("WARNING! Removed exclusions from kotlin-stdlib dependency of ${this.artifactId} artifact's maven metadata, check kotlin-stdlib dependency of ${project.path} project")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<Upload>("uploadArchives").configure {
|
||||
|
||||
val preparePublication = project.rootProject.tasks.named("preparePublication").get()
|
||||
|
||||
dependsOn(preparePublication)
|
||||
|
||||
val username: String? by preparePublication.extra
|
||||
val password: String? by preparePublication.extra
|
||||
val repoUrl: String by preparePublication.extra
|
||||
|
||||
var repository by Delegates.notNull<MavenRemoteRepository>()
|
||||
|
||||
repositories {
|
||||
withConvention(MavenRepositoryHandlerConvention::class) {
|
||||
|
||||
mavenDeployer {
|
||||
withGroovyBuilder {
|
||||
"beforeDeployment" {
|
||||
val signing = project.the<SigningExtension>()
|
||||
if (signing.isRequired)
|
||||
signing.signPom(delegate as MavenDeployment)
|
||||
}
|
||||
|
||||
"repository"("url" to repoUrl)!!.also { repository = it as MavenRemoteRepository }.withGroovyBuilder {
|
||||
if (username != null && password != null) {
|
||||
"authentication"("userName" to username, "password" to password)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurePom()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doFirst {
|
||||
repository.url = repoUrl
|
||||
}
|
||||
}
|
||||
|
||||
val install = if (tasks.names.contains("install")) tasks.getByName("install") as Upload
|
||||
else tasks.create("install", Upload::class.java)
|
||||
install.apply {
|
||||
configuration = project.configurations.getByName(Dependency.ARCHIVES_CONFIGURATION)
|
||||
description = "Installs the 'archives' artifacts into the local Maven repository."
|
||||
repositories {
|
||||
withConvention(MavenRepositoryHandlerConvention::class) {
|
||||
mavenInstaller {
|
||||
configurePom()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("publish") {
|
||||
dependsOn(tasks.named("uploadArchives"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,7 +194,9 @@ fun Project.projectTest(
|
||||
systemProperty("kotlin.ni", if (project.rootProject.hasProperty("newInferenceTests")) "true" else "false")
|
||||
systemProperty("org.jetbrains.kotlin.skip.muted.tests", if (project.rootProject.hasProperty("skipMutedTests")) "true" else "false")
|
||||
|
||||
systemProperty("idea.ignore.disabled.plugins", "true")
|
||||
if (Platform[202].orHigher()) {
|
||||
systemProperty("idea.ignore.disabled.plugins", "true")
|
||||
}
|
||||
|
||||
var subProjectTempRoot: Path? = null
|
||||
val projectName = project.name
|
||||
|
||||
@@ -9,13 +9,13 @@ import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
|
||||
fun Test.configureTestDistribution(configure: TestDistributionExtension.() -> Unit = {}) {
|
||||
if (extensions.findByType(TestDistributionExtension::class.java) == null) return
|
||||
|
||||
val isTeamcityBuild = project.kotlinBuildProperties.isTeamcityBuild
|
||||
val testDistributionEnabled =
|
||||
project.findProperty("kotlin.build.test.distribution.enabled")?.toString()?.toBoolean() ?: false
|
||||
|
||||
useJUnitPlatform()
|
||||
extensions.configure(TestDistributionExtension::class.java) {
|
||||
enabled.set(testDistributionEnabled)
|
||||
enabled.set(true)
|
||||
maxRemoteExecutors.set(20)
|
||||
if (isTeamcityBuild) {
|
||||
requirements.set(setOf("os=${OperatingSystem.current().familyName}"))
|
||||
|
||||
@@ -24,18 +24,24 @@ dependencies {
|
||||
testApi(projectTests(":compiler:tests-compiler-utils"))
|
||||
testApi(projectTests(":compiler:tests-common-new"))
|
||||
|
||||
testCompile(projectTests(":jps-plugin"))
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testCompile(intellijDep()) { includeJars("util", "idea", "idea_rt", rootProject = rootProject) }
|
||||
testCompile(intellijDep()) { includeJars("groovy", rootProject = rootProject) }
|
||||
Platform[193].orLower {
|
||||
testCompile(intellijDep()) { includeJars("openapi", rootProject = rootProject) }
|
||||
}
|
||||
|
||||
testCompile(intellijDep()) { includeJars("util", "idea", "idea_rt", rootProject = rootProject) }
|
||||
Platform[202].orHigher {
|
||||
testCompile(intellijDep()) { includeJars("groovy", rootProject = rootProject) }
|
||||
}
|
||||
Platform[201].orLower {
|
||||
testCompile(intellijDep()) { includeJars("groovy-all", rootProject = rootProject) }
|
||||
}
|
||||
testCompile(intellijPluginDep("java")) { includeJars("jps-builders") }
|
||||
testCompile(jpsStandalone()) { includeJars("jps-model") }
|
||||
testCompile(jpsBuildTest())
|
||||
|
||||
testRuntimeOnly(compile(intellijCoreDep()) { includeJars("intellij-core") })
|
||||
testRuntimeOnly(compile(intellijDep()) { includeJars("jna", rootProject = rootProject) })
|
||||
|
||||
testCompile("org.junit.platform:junit-platform-launcher:${commonVer("org.junit.platform", "")}")
|
||||
}
|
||||
|
||||
|
||||
@@ -354,8 +354,8 @@ class CodegenTestsOnAndroidGenerator private constructor(private val pathManager
|
||||
extractor.provideConfigurationKeys()
|
||||
extractor.configure(keyConfiguration, module.directives)
|
||||
}
|
||||
val kind = JvmEnvironmentConfigurator.extractConfigurationKind(module.directives)
|
||||
val jdkKind = JvmEnvironmentConfigurator.extractJdkKind(module.directives)
|
||||
val kind = configuratorForFlags.extractConfigurationKind(module.directives)
|
||||
val jdkKind = configuratorForFlags.extractJdkKind(module.directives)
|
||||
|
||||
keyConfiguration.languageVersionSettings = module.languageVersionSettings
|
||||
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.sam.getAbstractMembers
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithNothing
|
||||
|
||||
class SamType constructor(val type: KotlinType) {
|
||||
|
||||
@@ -34,3 +37,54 @@ class SamType constructor(val type: KotlinType) {
|
||||
}
|
||||
}
|
||||
|
||||
open class SamTypeFactory {
|
||||
fun createByValueParameter(valueParameter: ValueParameterDescriptor): SamType? {
|
||||
val singleArgumentType: KotlinType
|
||||
val originalSingleArgumentType: KotlinType?
|
||||
val varargElementType = valueParameter.varargElementType
|
||||
if (varargElementType != null) {
|
||||
singleArgumentType = varargElementType
|
||||
originalSingleArgumentType = valueParameter.original.varargElementType
|
||||
assert(originalSingleArgumentType != null) {
|
||||
"Value parameter and original value parameter have inconsistent varargs: " +
|
||||
valueParameter + "; " + valueParameter.original
|
||||
}
|
||||
} else {
|
||||
singleArgumentType = valueParameter.type
|
||||
originalSingleArgumentType = valueParameter.original.type
|
||||
}
|
||||
if (singleArgumentType.isError || originalSingleArgumentType!!.isError) {
|
||||
return null
|
||||
}
|
||||
// This can be true in case when the value parameter is in the method of a generic type with out-projection.
|
||||
// We approximate Inv<Captured#1> to Nothing, while Inv itself can be a SAM interface safe to call here
|
||||
// (see testData genericSamProjectedOut.kt for details)
|
||||
// In such a case we can't have a proper supertype since wildcards are not allowed there,
|
||||
// so we use Nothing arguments instead that leads to a raw type used for a SAM wrapper.
|
||||
// See org.jetbrains.kotlin.codegen.state.KotlinTypeMapper#writeGenericType to understand how
|
||||
// raw types and Nothing arguments relate.
|
||||
val originalTypeToUse =
|
||||
if (KotlinBuiltIns.isNothing(singleArgumentType))
|
||||
originalSingleArgumentType.replaceArgumentsWithNothing()
|
||||
else singleArgumentType
|
||||
return create(originalTypeToUse.removeExternalProjections())
|
||||
}
|
||||
|
||||
open fun isSamType(type: KotlinType): Boolean {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
return descriptor is ClassDescriptor && descriptor.isFun
|
||||
}
|
||||
|
||||
fun create(originalType: KotlinType): SamType? {
|
||||
return if (isSamType(originalType)) SamType(originalType) else null
|
||||
}
|
||||
|
||||
private fun KotlinType.removeExternalProjections(): KotlinType {
|
||||
val newArguments = arguments.map { TypeProjectionImpl(Variance.INVARIANT, it.type) }
|
||||
return replace(newArguments)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val INSTANCE = SamTypeFactory()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +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.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.intersectWrappedTypes
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithNothing
|
||||
|
||||
class SamTypeApproximator(builtIns: KotlinBuiltIns, languageVersionSettings: LanguageVersionSettings) {
|
||||
private val typeApproximator = TypeApproximator(builtIns, languageVersionSettings)
|
||||
|
||||
fun getSamTypeForValueParameter(valueParameter: ValueParameterDescriptor): KotlinType? {
|
||||
val singleArgumentType: KotlinType
|
||||
val originalSingleArgumentType: KotlinType?
|
||||
val varargElementType = valueParameter.varargElementType
|
||||
if (varargElementType != null) {
|
||||
singleArgumentType = varargElementType
|
||||
originalSingleArgumentType = valueParameter.original.varargElementType
|
||||
assert(originalSingleArgumentType != null) {
|
||||
"Value parameter and original value parameter have inconsistent varargs: " +
|
||||
valueParameter + "; " + valueParameter.original
|
||||
}
|
||||
} else {
|
||||
singleArgumentType = valueParameter.type
|
||||
originalSingleArgumentType = valueParameter.original.type
|
||||
}
|
||||
if (singleArgumentType.isError || originalSingleArgumentType!!.isError) {
|
||||
return null
|
||||
}
|
||||
|
||||
// This can be true in case when the value parameter is in the method of a generic type with out-projection.
|
||||
// We approximate Inv<Captured#1> to Nothing, while Inv itself can be a SAM interface safe to call here
|
||||
// (see testData genericSamProjectedOut.kt for details)
|
||||
// In such a case we can't have a proper supertype since wildcards are not allowed there,
|
||||
// so we use Nothing arguments instead that leads to a raw type used for a SAM wrapper.
|
||||
// See org.jetbrains.kotlin.codegen.state.KotlinTypeMapper#writeGenericType to understand how
|
||||
// raw types and Nothing arguments relate.
|
||||
val originalTypeToUse =
|
||||
if (KotlinBuiltIns.isNothing(singleArgumentType))
|
||||
originalSingleArgumentType.replaceArgumentsWithNothing()
|
||||
else
|
||||
singleArgumentType
|
||||
val approximatedOriginalTypeToUse =
|
||||
typeApproximator.approximateToSubType(
|
||||
originalTypeToUse,
|
||||
TypeApproximatorConfiguration.UpperBoundAwareIntersectionTypeApproximator
|
||||
) ?: originalTypeToUse
|
||||
approximatedOriginalTypeToUse as KotlinType
|
||||
|
||||
return approximatedOriginalTypeToUse.removeExternalProjections()
|
||||
}
|
||||
|
||||
private fun KotlinType.removeExternalProjections(): KotlinType {
|
||||
val newArguments = arguments.map { TypeProjectionImpl(Variance.INVARIANT, it.type) }
|
||||
return replace(newArguments)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
open class SamTypeFactory {
|
||||
open fun isSamType(type: KotlinType): Boolean {
|
||||
val descriptor = type.constructor.declarationDescriptor
|
||||
return descriptor is ClassDescriptor && descriptor.isFun
|
||||
}
|
||||
|
||||
fun create(originalType: KotlinType): SamType? {
|
||||
return if (isSamType(originalType)) SamType(originalType) else null
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,7 @@ interface TypeMappingContext<Writer : JvmDescriptorTypeWriter<Type>> {
|
||||
val typeContext: TypeSystemCommonBackendContextForTypeMapping
|
||||
|
||||
fun getClassInternalName(typeConstructor: TypeConstructorMarker): String
|
||||
fun getScriptInternalName(typeConstructor: TypeConstructorMarker): String
|
||||
|
||||
// NB: The counterpart, [KotlinTypeMapper#writeGenericType], doesn't have restriction on [type]
|
||||
fun Writer.writeGenericType(type: KotlinTypeMarker, asmType: Type, mode: TypeMappingMode)
|
||||
fun Writer.writeGenericType(type: SimpleTypeMarker, asmType: Type, mode: TypeMappingMode)
|
||||
}
|
||||
|
||||
object AbstractTypeMapper {
|
||||
@@ -46,7 +43,6 @@ object AbstractTypeMapper {
|
||||
sw: Writer? = null
|
||||
): Type = context.typeContext.mapType(context, type, mode, sw)
|
||||
|
||||
// NB: The counterpart, [descriptorBasedTypeSignatureMapping#mapType] doesn't have restriction on [type].
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun <Writer : JvmDescriptorTypeWriter<Type>> TypeSystemCommonBackendContextForTypeMapping.mapType(
|
||||
context: TypeMappingContext<Writer>,
|
||||
@@ -54,7 +50,10 @@ object AbstractTypeMapper {
|
||||
mode: TypeMappingMode = TypeMappingMode.DEFAULT,
|
||||
sw: Writer? = null
|
||||
): Type {
|
||||
if (type is SimpleTypeMarker && type.isSuspendFunction()) {
|
||||
if (type !is SimpleTypeMarker) {
|
||||
error("Unexpected type: $type (original Kotlin type=$type of ${type.let { it::class }})")
|
||||
}
|
||||
if (type.isSuspendFunction()) {
|
||||
val argumentsCount = type.argumentsCount()
|
||||
val argumentsList = type.asArgumentList()
|
||||
|
||||
@@ -79,7 +78,7 @@ object AbstractTypeMapper {
|
||||
val typeConstructor = type.typeConstructor()
|
||||
|
||||
when {
|
||||
type is SimpleTypeMarker && type.isArrayOrNullableArray() -> {
|
||||
type.isArrayOrNullableArray() -> {
|
||||
val typeArgument = type.asArgumentList()[0]
|
||||
val (variance, memberType) = when {
|
||||
typeArgument.isStarProjection() -> Variance.OUT_VARIANCE to nullableAnyType()
|
||||
@@ -99,7 +98,7 @@ object AbstractTypeMapper {
|
||||
return AsmUtil.getArrayType(arrayElementType)
|
||||
}
|
||||
|
||||
type is SimpleTypeMarker && typeConstructor.isClassTypeConstructor() -> {
|
||||
typeConstructor.isClassTypeConstructor() -> {
|
||||
if (typeConstructor.isInlineClass() && !mode.needInlineClassWrapping) {
|
||||
val expandedType = computeExpandedTypeForInlineClass(type)
|
||||
require(expandedType is SimpleTypeMarker?)
|
||||
@@ -118,7 +117,9 @@ object AbstractTypeMapper {
|
||||
}
|
||||
|
||||
typeConstructor.isScript() -> {
|
||||
return Type.getObjectType(context.getScriptInternalName(typeConstructor))
|
||||
val asmType = AsmTypes.JAVA_CLASS_TYPE
|
||||
with(context) { sw?.writeGenericType(type, asmType, mode) }
|
||||
return asmType
|
||||
}
|
||||
|
||||
typeConstructor.isTypeParameter() -> {
|
||||
|
||||
@@ -34,10 +34,10 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.AnnotationChecker;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker;
|
||||
import org.jetbrains.kotlin.resolve.constants.*;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil;
|
||||
import org.jetbrains.kotlin.types.FlexibleType;
|
||||
import org.jetbrains.kotlin.types.FlexibleTypesKt;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
@@ -376,7 +376,7 @@ public abstract class AnnotationCodegen {
|
||||
// We do not generate annotations whose classes are optional (annotated with `@OptionalExpectation`) because if an annotation entry
|
||||
// is resolved to the expected declaration, this means that annotation has no actual class, and thus should not be generated.
|
||||
// (Otherwise we would've resolved the entry to the actual annotation class.)
|
||||
if (OptionalAnnotationUtil.isOptionalAnnotationClass(classDescriptor)) {
|
||||
if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(classDescriptor)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@ import org.jetbrains.kotlin.codegen.inline.NameGenerator
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.putReifiedOperationMarker
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.OperationKind
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
interface BaseExpressionCodegen {
|
||||
|
||||
val frameMap: FrameMapBase<*>
|
||||
|
||||
val visitor: InstructionAdapter
|
||||
@@ -38,14 +40,24 @@ interface BaseExpressionCodegen {
|
||||
|
||||
fun propagateChildReifiedTypeParametersUsages(reifiedTypeParametersUsages: ReifiedTypeParametersUsages)
|
||||
|
||||
fun pushClosureOnStack(
|
||||
classDescriptor: ClassDescriptor,
|
||||
putThis: Boolean,
|
||||
callGenerator: CallGenerator,
|
||||
functionReferenceReceiver: StackValue?
|
||||
)
|
||||
|
||||
fun markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards: Boolean)
|
||||
|
||||
fun consumeReifiedOperationMarker(typeParameter: TypeParameterMarker)
|
||||
}
|
||||
|
||||
fun BaseExpressionCodegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type: KotlinTypeMarker, operationKind: OperationKind): Boolean {
|
||||
val (typeParameter, second) = typeSystem.extractReificationArgument(type) ?: return false
|
||||
consumeReifiedOperationMarker(typeParameter)
|
||||
putReifiedOperationMarker(operationKind, second, visitor)
|
||||
return true
|
||||
fun putReifiedOperationMarkerIfTypeIsReifiedParameter(type: KotlinTypeMarker, operationKind: OperationKind) {
|
||||
with(typeSystem) {
|
||||
val (typeParameter, second) = extractReificationArgument(type) ?: return
|
||||
if (typeParameter.isReified()) {
|
||||
consumeReifiedOperationMarker(typeParameter)
|
||||
putReifiedOperationMarker(operationKind, second, visitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ enum class ValueKind {
|
||||
DEFAULT_MASK,
|
||||
METHOD_HANDLE_IN_DEFAULT,
|
||||
CAPTURED,
|
||||
DEFAULT_LAMBDA_CAPTURED_PARAMETER,
|
||||
NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND,
|
||||
NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER
|
||||
}
|
||||
@@ -39,9 +40,13 @@ interface CallGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
override fun processHiddenParameters() {}
|
||||
override fun processAndPutHiddenParameters(justProcess: Boolean) {
|
||||
|
||||
override fun putHiddenParamsIntoLocals() {}
|
||||
}
|
||||
|
||||
override fun putHiddenParamsIntoLocals() {
|
||||
|
||||
}
|
||||
|
||||
override fun genValueAndPut(
|
||||
valueParameterDescriptor: ValueParameterDescriptor?,
|
||||
@@ -136,8 +141,9 @@ interface CallGenerator {
|
||||
paramIndex: Int
|
||||
)
|
||||
|
||||
fun processHiddenParameters()
|
||||
fun processAndPutHiddenParameters(justProcess: Boolean)
|
||||
|
||||
/*should be called if justProcess = true in processAndPutHiddenParameters*/
|
||||
fun putHiddenParamsIntoLocals()
|
||||
|
||||
fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>)
|
||||
|
||||
@@ -12,7 +12,6 @@ import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.SamType;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.kotlin.codegen.context.ClosureContext;
|
||||
@@ -32,6 +31,7 @@ import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
@@ -40,7 +40,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.SimpleType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.backend.common.SamType;
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
@@ -376,12 +376,10 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
||||
value = StackValue.local(slot, type, bridgeParameterKotlinTypes.get(i));
|
||||
slot += type.getSize();
|
||||
}
|
||||
if (InlineClassesCodegenUtilKt.isInlineClassWithUnderlyingTypeAnyOrAnyN(parameterType) && functionReferenceCall == null) {
|
||||
ClassDescriptor descriptor = TypeUtils.getClassDescriptor(parameterType);
|
||||
InlineClassRepresentation<SimpleType> representation =
|
||||
descriptor != null ? descriptor.getInlineClassRepresentation() : null;
|
||||
assert representation != null : "Not an inline class type: " + parameterType;
|
||||
parameterType = representation.getUnderlyingType();
|
||||
if (InlineClassesCodegenUtilKt.isInlineClassWithUnderlyingTypeAnyOrAnyN(parameterType) &&
|
||||
functionReferenceCall == null
|
||||
) {
|
||||
parameterType = InlineClassesUtilsKt.unsubstitutedUnderlyingParameter(parameterType).getType();
|
||||
}
|
||||
value.put(typeMapper.mapType(calleeParameter), parameterType, iv);
|
||||
}
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.inline.loadCompiledInlineFunction
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.usesLocalExceptParameterNullCheck
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegen
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.FunctionImportedFromObject
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.jvm.requiresFunctionNameManglingForReturnType
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||
|
||||
class DelegatedPropertiesCodegenHelper(private val state: GenerationState) {
|
||||
|
||||
@@ -63,15 +65,27 @@ class DelegatedPropertiesCodegenHelper(private val state: GenerationState) {
|
||||
this
|
||||
|
||||
private fun isDelegatedPropertyMetadataRequiredForFunctionFromBinaries(calleeDescriptor: FunctionDescriptor): Boolean {
|
||||
require(calleeDescriptor is DescriptorWithContainerSource) {
|
||||
assert(calleeDescriptor is DescriptorWithContainerSource) {
|
||||
"Function descriptor from binaries expected: $calleeDescriptor"
|
||||
}
|
||||
|
||||
val metadataParameterIndex = getMetadataParameterIndex(calleeDescriptor)
|
||||
val containerId = KotlinTypeMapper.getContainingClassesForDeserializedCallable(calleeDescriptor).implClassId
|
||||
val asmMethod = state.typeMapper.mapAsmMethod(calleeDescriptor)
|
||||
val isMangled = requiresFunctionNameManglingForReturnType(calleeDescriptor)
|
||||
val methodNode = loadCompiledInlineFunction(containerId, asmMethod, calleeDescriptor.isSuspend, isMangled, state).node
|
||||
return methodNode.usesLocalExceptParameterNullCheck(metadataParameterIndex)
|
||||
val methodNode = InlineCodegen.createSpecialInlineMethodNodeFromBinaries(calleeDescriptor, state)
|
||||
|
||||
return isMetadataParameterUsedInCompiledMethodBody(metadataParameterIndex, methodNode)
|
||||
}
|
||||
|
||||
private fun isMetadataParameterUsedInCompiledMethodBody(metadataParameterIndex: Int, methodNode: MethodNode): Boolean =
|
||||
methodNode.instructions.toArray().any { insn ->
|
||||
insn is VarInsnNode && insn.opcode == Opcodes.ALOAD && insn.`var` == metadataParameterIndex &&
|
||||
!isParameterNullCheckArgument(insn)
|
||||
}
|
||||
|
||||
private fun isParameterNullCheckArgument(insn: AbstractInsnNode): Boolean {
|
||||
val next1 = insn.next
|
||||
val next2 = next1.next
|
||||
return next1 != null && next2 != null &&
|
||||
next1.opcode == Opcodes.LDC && next2.isCheckParameterIsNotNull()
|
||||
}
|
||||
|
||||
private fun getMetadataParameterIndex(calleeDescriptor: FunctionDescriptor): Int {
|
||||
|
||||
@@ -72,7 +72,7 @@ class ErasedInlineClassBodyCodegen(
|
||||
}
|
||||
|
||||
private fun generateUnboxMethod() {
|
||||
val boxMethodDescriptor = InlineClassDescriptorResolver.createBoxFunctionDescriptor(descriptor)
|
||||
val boxMethodDescriptor = InlineClassDescriptorResolver.createBoxFunctionDescriptor(descriptor) ?: return
|
||||
|
||||
functionCodegen.generateMethod(
|
||||
Synthetic(null, boxMethodDescriptor), boxMethodDescriptor, object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@@ -103,7 +103,7 @@ class ErasedInlineClassBodyCodegen(
|
||||
}
|
||||
|
||||
private fun generateSpecializedEqualsStub() {
|
||||
val specializedEqualsDescriptor = InlineClassDescriptorResolver.createSpecializedEqualsDescriptor(descriptor)
|
||||
val specializedEqualsDescriptor = InlineClassDescriptorResolver.createSpecializedEqualsDescriptor(descriptor) ?: return
|
||||
|
||||
functionCodegen.generateMethod(
|
||||
Synthetic(null, specializedEqualsDescriptor), specializedEqualsDescriptor, object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
|
||||
@@ -20,7 +20,6 @@ import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||
import org.jetbrains.kotlin.backend.common.SamType;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
@@ -93,6 +92,7 @@ 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;
|
||||
import org.jetbrains.kotlin.backend.common.SamType;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
@@ -107,7 +107,6 @@ import java.util.stream.Collectors;
|
||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.boxType;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.BaseExpressionCodegenKt.putReifiedOperationMarkerIfTypeIsReifiedParameter;
|
||||
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.*;
|
||||
import static org.jetbrains.kotlin.codegen.DescriptorAsmUtil.boxType;
|
||||
import static org.jetbrains.kotlin.codegen.DescriptorAsmUtil.*;
|
||||
@@ -266,23 +265,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
);
|
||||
}
|
||||
|
||||
private static void addReifiedParametersFromSignature(@NotNull MemberCodegen<?> member, @NotNull ClassDescriptor descriptor) {
|
||||
private static void addReifiedParametersFromSignature(@NotNull MemberCodegen member, @NotNull ClassDescriptor descriptor) {
|
||||
for (KotlinType type : descriptor.getTypeConstructor().getSupertypes()) {
|
||||
processTypeArguments(member, type);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processTypeArguments(@NotNull MemberCodegen<?> member, KotlinType type) {
|
||||
for (TypeProjection supertypeArgument : type.getArguments()) {
|
||||
if (supertypeArgument.isStarProjection()) continue;
|
||||
|
||||
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(supertypeArgument.getType());
|
||||
if (parameterDescriptor != null) {
|
||||
if (parameterDescriptor.isReified()) {
|
||||
for (TypeProjection supertypeArgument : type.getArguments()) {
|
||||
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(supertypeArgument.getType());
|
||||
if (parameterDescriptor != null && parameterDescriptor.isReified()) {
|
||||
member.getReifiedTypeParametersUsages().addUsedReifiedParameter(parameterDescriptor.getName().asString());
|
||||
}
|
||||
} else {
|
||||
processTypeArguments(member, supertypeArgument.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1262,9 +1251,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
if (closure.isSuspendLambda()) {
|
||||
// When inlining crossinline lambda, the ACONST_NULL is never popped.
|
||||
// Thus, do not generate it. Otherwise, it leads to VerifyError on run-time.
|
||||
boolean isCrossinlineLambda = (callGenerator instanceof PsiInlineCodegen) &&
|
||||
Objects.requireNonNull(((PsiInlineCodegen) callGenerator).getActiveLambda(),
|
||||
"no active lambda found").isCrossInline();
|
||||
boolean isCrossinlineLambda = (callGenerator instanceof InlineCodegen<?>) &&
|
||||
Objects.requireNonNull(((InlineCodegen) callGenerator).getActiveLambda(),
|
||||
"no active lambda found").isCrossInline;
|
||||
if (!isCrossinlineLambda) {
|
||||
v.aconst(null);
|
||||
}
|
||||
@@ -2025,12 +2014,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
// Do not unbox parameters of suspend lambda, they are unboxed in `invoke` method
|
||||
!CoroutineCodegenUtilKt.isInvokeSuspendOfLambda(context.getFunctionDescriptor())
|
||||
) {
|
||||
ClassDescriptor inlineClass = (ClassDescriptor) inlineClassType.getConstructor().getDeclarationDescriptor();
|
||||
InlineClassRepresentation<SimpleType> representation =
|
||||
inlineClass != null ? inlineClass.getInlineClassRepresentation() : null;
|
||||
assert representation != null : "Not an inline class: " + inlineClassType;
|
||||
KotlinType underlyingType = representation.getUnderlyingType();
|
||||
return StackValue.underlyingValueOfInlineClass(typeMapper.mapType(underlyingType), underlyingType, localOrCaptured);
|
||||
KotlinType underlyingType = InlineClassesUtilsKt.underlyingRepresentation(
|
||||
(ClassDescriptor) inlineClassType.getConstructor().getDeclarationDescriptor()).getType();
|
||||
return StackValue.underlyingValueOfInlineClass(
|
||||
typeMapper.mapType(underlyingType), underlyingType, localOrCaptured);
|
||||
}
|
||||
}
|
||||
return localOrCaptured;
|
||||
@@ -2781,8 +2768,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
putReceiverAndInlineMarkerIfNeeded(callableMethod, resolvedCall, receiver, maybeSuspensionPoint, isConstructor);
|
||||
}
|
||||
|
||||
callGenerator.processHiddenParameters();
|
||||
callGenerator.putHiddenParamsIntoLocals();
|
||||
callGenerator.processAndPutHiddenParameters(false);
|
||||
|
||||
List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
|
||||
assert valueArguments != null : "Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor();
|
||||
@@ -2951,12 +2937,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
bindingContext, state
|
||||
);
|
||||
|
||||
PsiSourceCompilerForInline sourceCompiler = new PsiSourceCompilerForInline(this, callElement);
|
||||
FunctionDescriptor functionDescriptor =
|
||||
InlineUtil.isArrayConstructorWithLambda(original)
|
||||
? FictitiousArrayConstructor.create((ConstructorDescriptor) original) : original.getOriginal();
|
||||
PsiSourceCompilerForInline sourceCompiler = new PsiSourceCompilerForInline(this, callElement, functionDescriptor);
|
||||
|
||||
JvmMethodSignature signature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.getContext().getContextKind());
|
||||
sourceCompiler.initializeInlineFunctionContext(functionDescriptor);
|
||||
JvmMethodSignature signature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.getContextKind());
|
||||
if (signature.getAsmMethod().getName().contains("-") &&
|
||||
!state.getConfiguration().getBoolean(JVMConfigurationKeys.USE_OLD_INLINE_CLASSES_MANGLING_SCHEME)
|
||||
) {
|
||||
@@ -2964,17 +2951,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
InlineClassesCodegenUtilKt.classFileContainsMethod(functionDescriptor, state, signature.getAsmMethod());
|
||||
if (classFileContainsMethod != null && !classFileContainsMethod) {
|
||||
typeMapper.setUseOldManglingRulesForFunctionAcceptingInlineClass(true);
|
||||
signature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.getContext().getContextKind());
|
||||
signature = typeMapper.mapSignatureWithGeneric(functionDescriptor, sourceCompiler.getContextKind());
|
||||
typeMapper.setUseOldManglingRulesForFunctionAcceptingInlineClass(false);
|
||||
}
|
||||
}
|
||||
Type methodOwner = typeMapper.mapImplementationOwner(functionDescriptor);
|
||||
if (isDefaultCompilation) {
|
||||
return new InlineCodegenForDefaultBody(functionDescriptor, this, state, signature, sourceCompiler);
|
||||
} else {
|
||||
return new PsiInlineCodegen(
|
||||
this, state, functionDescriptor, signature, typeParameterMappings, sourceCompiler,
|
||||
typeMapper.mapImplementationOwner(functionDescriptor), typeMapper.mapOwner(descriptor), callElement
|
||||
);
|
||||
return new InlineCodegenForDefaultBody(functionDescriptor, this, state, methodOwner, signature, sourceCompiler);
|
||||
}
|
||||
else {
|
||||
return new PsiInlineCodegen(this, state, functionDescriptor, methodOwner, signature, typeParameterMappings, sourceCompiler,
|
||||
typeMapper.mapOwner(descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3558,7 +3545,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
if (TypeUtils.isTypeParameter(type)) {
|
||||
assert TypeUtils.isReifiedTypeParameter(type) :
|
||||
"Non-reified type parameter under ::class should be rejected by type checker: " + type;
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(this, type, ReifiedTypeInliner.OperationKind.JAVA_CLASS);
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS);
|
||||
}
|
||||
|
||||
putJavaLangClassInstance(v, typeMapper.mapType(type), type, typeMapper);
|
||||
@@ -4931,7 +4918,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
public void newArrayInstruction(@NotNull KotlinType arrayType) {
|
||||
if (KotlinBuiltIns.isArray(arrayType)) {
|
||||
KotlinType elementJetType = arrayType.getArguments().get(0).getType();
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(this, elementJetType, ReifiedTypeInliner.OperationKind.NEW_ARRAY);
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
elementJetType,
|
||||
ReifiedTypeInliner.OperationKind.NEW_ARRAY
|
||||
);
|
||||
v.newarray(boxType(typeMapper.mapTypeAsDeclaration(elementJetType)));
|
||||
}
|
||||
else {
|
||||
@@ -5235,7 +5225,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
|
||||
boolean safeAs = opToken == KtTokens.AS_SAFE;
|
||||
if (TypeUtils.isReifiedTypeParameter(rightKotlinType)) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(this, rightKotlinType,
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(rightKotlinType,
|
||||
safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
|
||||
: ReifiedTypeInliner.OperationKind.AS);
|
||||
v.checkcast(boxedRightType);
|
||||
@@ -5292,7 +5282,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
|
||||
Type type = boxType(typeMapper.mapTypeAsDeclaration(rhsKotlinType));
|
||||
if (TypeUtils.isReifiedTypeParameter(rhsKotlinType)) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(this, rhsKotlinType, ReifiedTypeInliner.OperationKind.IS);
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(rhsKotlinType, ReifiedTypeInliner.OperationKind.IS);
|
||||
v.instanceOf(type);
|
||||
return null;
|
||||
}
|
||||
@@ -5544,4 +5534,11 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
parentCodegen.getReifiedTypeParametersUsages().addUsedReifiedParameter(typeParameterDescriptor.getName().asString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
@NotNull KotlinTypeMarker type, @NotNull ReifiedTypeInliner.OperationKind operationKind
|
||||
) {
|
||||
BaseExpressionCodegen.DefaultImpls.putReifiedOperationMarkerIfTypeIsReifiedParameter(this, type, operationKind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,13 +68,6 @@ open class FrameMapBase<T : Any> {
|
||||
return Mark(currentSize)
|
||||
}
|
||||
|
||||
fun skipTo(target: Int): Mark {
|
||||
return mark().also {
|
||||
if (currentSize < target)
|
||||
currentSize = target
|
||||
}
|
||||
}
|
||||
|
||||
inner class Mark(private val myIndex: Int) {
|
||||
|
||||
fun dropTo() {
|
||||
|
||||
@@ -53,7 +53,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.SimpleType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
@@ -370,22 +369,23 @@ public class FunctionCodegen {
|
||||
public static void generateMethodInsideInlineClassWrapper(
|
||||
@NotNull JvmDeclarationOrigin origin,
|
||||
@NotNull FunctionDescriptor functionDescriptor,
|
||||
@NotNull ClassDescriptor inlineClass,
|
||||
@NotNull ClassDescriptor containingDeclaration,
|
||||
@NotNull MethodVisitor mv,
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
) {
|
||||
mv.visitCode();
|
||||
|
||||
Type fieldOwnerType = typeMapper.mapClass(inlineClass);
|
||||
Type fieldOwnerType = typeMapper.mapClass(containingDeclaration);
|
||||
Method erasedMethodImpl = typeMapper.mapAsmMethod(functionDescriptor.getOriginal(), OwnerKind.ERASED_INLINE_CLASS);
|
||||
|
||||
InlineClassRepresentation<SimpleType> representation = inlineClass.getInlineClassRepresentation();
|
||||
assert representation != null : "Not an inline class: " + inlineClass;
|
||||
ValueParameterDescriptor valueRepresentation = InlineClassesUtilsKt.underlyingRepresentation(containingDeclaration);
|
||||
if (valueRepresentation == null) return;
|
||||
|
||||
Type fieldType = typeMapper.mapType(valueRepresentation);
|
||||
|
||||
generateDelegateToStaticErasedVersion(
|
||||
mv, erasedMethodImpl, fieldOwnerType,
|
||||
representation.getUnderlyingPropertyName().asString(),
|
||||
typeMapper.mapType(representation.getUnderlyingType())
|
||||
mv, erasedMethodImpl,
|
||||
fieldOwnerType, valueRepresentation.getName().asString(), fieldType
|
||||
);
|
||||
|
||||
endVisit(mv, null, origin.getElement());
|
||||
|
||||
@@ -55,7 +55,6 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.SimpleType;
|
||||
import org.jetbrains.org.objectweb.asm.FieldVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -275,25 +274,26 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
@Override
|
||||
protected void generateUnboxMethodForInlineClass() {
|
||||
if (!(myClass instanceof KtClass)) return;
|
||||
InlineClassRepresentation<SimpleType> inlineClassRepresentation = descriptor.getInlineClassRepresentation();
|
||||
if (inlineClassRepresentation == null) return;
|
||||
if (!InlineClassesUtilsKt.isInlineClass(descriptor)) return;
|
||||
|
||||
Type ownerType = typeMapper.mapClass(descriptor);
|
||||
Type valueType = typeMapper.mapType(inlineClassRepresentation.getUnderlyingType());
|
||||
ValueParameterDescriptor inlinedValue = InlineClassesUtilsKt.underlyingRepresentation(this.descriptor);
|
||||
if (inlinedValue == null) return;
|
||||
|
||||
Type valueType = typeMapper.mapType(inlinedValue.getType());
|
||||
SimpleFunctionDescriptor functionDescriptor = InlineClassDescriptorResolver.createUnboxFunctionDescriptor(this.descriptor);
|
||||
assert functionDescriptor != null : "FunctionDescriptor for unbox method should be not null during codegen";
|
||||
|
||||
functionCodegen.generateMethod(
|
||||
JvmDeclarationOriginKt.UnboxMethodOfInlineClass(functionDescriptor), functionDescriptor,
|
||||
new FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@Override
|
||||
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
|
||||
public void doGenerateBody(
|
||||
@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature
|
||||
) {
|
||||
InstructionAdapter iv = codegen.v;
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
iv.getfield(
|
||||
ownerType.getInternalName(),
|
||||
inlineClassRepresentation.getUnderlyingPropertyName().asString(),
|
||||
valueType.getDescriptor()
|
||||
);
|
||||
iv.getfield(ownerType.getInternalName(), inlinedValue.getName().asString(), valueType.getDescriptor());
|
||||
iv.areturn(valueType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.SamTypeFactory
|
||||
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.backend.common.SamTypeFactory
|
||||
|
||||
class JvmSamTypeFactory : SamTypeFactory() {
|
||||
override fun isSamType(type: KotlinType) =
|
||||
JavaSingleAbstractMethodUtils.isSamType(type)
|
||||
object JvmSamTypeFactory : SamTypeFactory() {
|
||||
override fun isSamType(type: KotlinType) = JavaSingleAbstractMethodUtils.isSamType(type)
|
||||
}
|
||||
@@ -35,9 +35,9 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.PackageDescriptorUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil;
|
||||
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
@@ -91,8 +91,8 @@ public class PackageCodegenImpl implements PackageCodegen {
|
||||
if (declaration instanceof KtClassOrObject) {
|
||||
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, declaration);
|
||||
if (PsiUtilsKt.hasExpectModifier(declaration)) {
|
||||
if (descriptor != null && OptionalAnnotationUtil.shouldGenerateExpectClass(descriptor)) {
|
||||
assert OptionalAnnotationUtil.isOptionalAnnotationClass(descriptor) :
|
||||
if (descriptor != null && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor)) {
|
||||
assert ExpectedActualDeclarationChecker.isOptionalAnnotationClass(descriptor) :
|
||||
"Expect class should be generated only if it's an optional annotation: " + descriptor;
|
||||
state.getFactory().getPackagePartRegistry().getOptionalAnnotations().add(descriptor);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.SimpleType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
@@ -1328,8 +1329,8 @@ public abstract class StackValue {
|
||||
StackValue newReceiver = StackValue.receiver(call, receiver, codegen, callable);
|
||||
ArgumentGenerator generator = createArgumentGenerator();
|
||||
newReceiver.put(newReceiver.type, newReceiver.kotlinType, v);
|
||||
callGenerator.processHiddenParameters();
|
||||
callGenerator.putHiddenParamsIntoLocals();
|
||||
callGenerator.processAndPutHiddenParameters(false);
|
||||
|
||||
defaultArgs = generator.generate(valueArguments, valueArguments, call.getResultingDescriptor());
|
||||
}
|
||||
|
||||
@@ -1747,8 +1748,7 @@ public abstract class StackValue {
|
||||
assert getterDescriptor != null : "Getter descriptor should be not null for " + descriptor;
|
||||
if (resolvedCall != null && getterDescriptor.isInline()) {
|
||||
CallGenerator callGenerator = codegen.getOrCreateCallGenerator(resolvedCall, ((PropertyDescriptor)resolvedCall.getResultingDescriptor()).getGetter());
|
||||
callGenerator.processHiddenParameters();
|
||||
callGenerator.putHiddenParamsIntoLocals();
|
||||
callGenerator.processAndPutHiddenParameters(false);
|
||||
callGenerator.genCall(getter, resolvedCall, false, codegen);
|
||||
}
|
||||
else {
|
||||
@@ -1832,7 +1832,7 @@ public abstract class StackValue {
|
||||
if (!skipReceiver) {
|
||||
putReceiver(v, false);
|
||||
}
|
||||
callGenerator.processHiddenParameters();
|
||||
callGenerator.processAndPutHiddenParameters(true);
|
||||
callGenerator.putValueIfNeeded(new JvmKotlinType(
|
||||
CollectionsKt.last(setter.getValueParameters()).getAsmType(),
|
||||
CollectionsKt.last(setterDescriptor.getValueParameters()).getType()),
|
||||
|
||||
@@ -22,9 +22,7 @@ import java.lang.StringBuilder
|
||||
class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapter) {
|
||||
|
||||
private val template = StringBuilder("")
|
||||
private val specialSymbolsInTemplate = arrayListOf<String>()
|
||||
private val paramTypes = arrayListOf<Type>()
|
||||
private var paramSlots = 0
|
||||
private var justFlushed = false
|
||||
|
||||
@JvmOverloads
|
||||
@@ -44,16 +42,7 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
if (mode == JvmStringConcat.INDY_WITH_CONSTANTS) {
|
||||
when (stackValue) {
|
||||
is StackValue.Constant -> {
|
||||
val value = stackValue.value
|
||||
if (value is String && (value.contains("\u0001") || value.contains("\u0002"))) {
|
||||
template.append("\u0002") //reference to special symbols added on next line
|
||||
specialSymbolsInTemplate.add(value)
|
||||
} else if (value is Char && (value == 1.toChar() || value == 2.toChar())) {
|
||||
template.append("\u0002") //reference to special symbols added on next line
|
||||
specialSymbolsInTemplate.add(value.toString())
|
||||
} else {
|
||||
template.append(value)
|
||||
}
|
||||
template.append(stackValue.value)
|
||||
return
|
||||
}
|
||||
TRUE -> {
|
||||
@@ -85,9 +74,8 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
} else {
|
||||
justFlushed = false
|
||||
paramTypes.add(type)
|
||||
paramSlots += type.size
|
||||
template.append("\u0001")
|
||||
if (paramSlots >= 200) {
|
||||
if (paramTypes.size == 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"
|
||||
@@ -116,7 +104,7 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
"makeConcatWithConstants",
|
||||
Type.getMethodDescriptor(JAVA_STRING_TYPE, *paramTypes.toTypedArray()),
|
||||
bootstrap,
|
||||
arrayOf(template.toString()) + specialSymbolsInTemplate
|
||||
arrayOf(template.toString())
|
||||
)
|
||||
} else {
|
||||
val bootstrap = Handle(
|
||||
@@ -134,15 +122,10 @@ class StringConcatGenerator(val mode: JvmStringConcat, val mv: InstructionAdapte
|
||||
arrayOf()
|
||||
)
|
||||
}
|
||||
//clear old template
|
||||
template.clear()
|
||||
specialSymbolsInTemplate.clear()
|
||||
paramTypes.clear()
|
||||
|
||||
//add just flushed string
|
||||
paramTypes.add(JAVA_STRING_TYPE)
|
||||
template.append("\u0001")
|
||||
paramSlots = JAVA_STRING_TYPE.size
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import kotlin.Pair;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.SamTypeApproximator;
|
||||
import org.jetbrains.kotlin.builtins.FunctionTypesKt;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes;
|
||||
@@ -35,7 +34,6 @@ import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
|
||||
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.psi.*;
|
||||
@@ -92,7 +90,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
private final ClassBuilderMode classBuilderMode;
|
||||
private final DelegatedPropertiesCodegenHelper delegatedPropertiesCodegenHelper;
|
||||
private final JvmDefaultMode jvmDefaultMode;
|
||||
private final SamTypeApproximator samTypeApproximator;
|
||||
|
||||
public CodegenAnnotatingVisitor(@NotNull GenerationState state) {
|
||||
this.bindingTrace = state.getBindingTrace();
|
||||
@@ -103,8 +100,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
this.languageVersionSettings = state.getLanguageVersionSettings();
|
||||
this.classBuilderMode = state.getClassBuilderMode();
|
||||
this.delegatedPropertiesCodegenHelper = new DelegatedPropertiesCodegenHelper(state);
|
||||
this.jvmDefaultMode = state.getJvmDefaultMode();
|
||||
this.samTypeApproximator = new SamTypeApproximator(state.getModule().getBuiltIns(), state.getLanguageVersionSettings());
|
||||
jvmDefaultMode = state.getJvmDefaultMode();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -859,20 +855,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SamType createSamType(KotlinType kotlinType) {
|
||||
if (!JavaSingleAbstractMethodUtils.isSamType(kotlinType)) return null;
|
||||
return new SamType(kotlinType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SamType createSamTypeByValueParameter(ValueParameterDescriptor valueParameterDescriptor) {
|
||||
KotlinType kotlinSamType = samTypeApproximator.getSamTypeForValueParameter(valueParameterDescriptor);
|
||||
if (kotlinSamType == null) return null;
|
||||
if (!JavaSingleAbstractMethodUtils.isSamType(kotlinSamType)) return null;
|
||||
return new SamType(kotlinSamType);
|
||||
}
|
||||
|
||||
private void writeSamValueForValueParameters(
|
||||
@NotNull Collection<ValueParameterDescriptor> valueParametersWithSAMConversion,
|
||||
@Nullable List<ResolvedValueArgument> valueArguments
|
||||
@@ -880,7 +862,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
if (valueArguments == null) return;
|
||||
|
||||
for (ValueParameterDescriptor valueParameter : valueParametersWithSAMConversion) {
|
||||
SamType samType = createSamTypeByValueParameter(valueParameter);
|
||||
SamType samType = JvmSamTypeFactory.INSTANCE.createByValueParameter(valueParameter);
|
||||
if (samType == null) continue;
|
||||
|
||||
ResolvedValueArgument resolvedValueArgument = valueArguments.get(valueParameter.getIndex());
|
||||
@@ -892,7 +874,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
}
|
||||
|
||||
private void recordSamTypeOnArgumentExpression(ValueParameterDescriptor valueParameter, ValueArgument valueArgument) {
|
||||
SamType samType = createSamTypeByValueParameter(valueParameter);
|
||||
SamType samType = JvmSamTypeFactory.INSTANCE.createByValueParameter(valueParameter);
|
||||
if (samType == null) return;
|
||||
|
||||
recordSamTypeOnArgumentExpression(samType, valueArgument);
|
||||
@@ -973,7 +955,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
KtExpression argumentExpression = argument.getArgumentExpression();
|
||||
bindingTrace.record(SAM_CONSTRUCTOR_TO_ARGUMENT, expression, argumentExpression);
|
||||
|
||||
SamType samType = createSamType(callableDescriptor.getReturnType());
|
||||
//noinspection ConstantConditions
|
||||
SamType samType = JvmSamTypeFactory.INSTANCE.create(callableDescriptor.getReturnType());
|
||||
bindingTrace.record(SAM_VALUE, argumentExpression, samType);
|
||||
}
|
||||
|
||||
@@ -990,7 +973,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
FunctionDescriptor original = SamCodegenUtil.getOriginalIfSamAdapter((FunctionDescriptor) operationDescriptor);
|
||||
if (original == null) return;
|
||||
|
||||
SamType samType = createSamTypeByValueParameter(original.getValueParameters().get(0));
|
||||
SamType samType = JvmSamTypeFactory.INSTANCE.createByValueParameter(original.getValueParameters().get(0));
|
||||
if (samType == null) return;
|
||||
|
||||
IElementType token = expression.getOperationToken();
|
||||
@@ -1019,7 +1002,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
List<KtExpression> indexExpressions = expression.getIndexExpressions();
|
||||
List<ValueParameterDescriptor> parameters = original.getValueParameters();
|
||||
for (ValueParameterDescriptor valueParameter : parameters) {
|
||||
SamType samType = createSamTypeByValueParameter(valueParameter);
|
||||
SamType samType = JvmSamTypeFactory.INSTANCE.createByValueParameter(valueParameter);
|
||||
if (samType == null) continue;
|
||||
|
||||
if (isSetter && valueParameter.getIndex() == parameters.size() - 1) {
|
||||
|
||||
@@ -393,12 +393,13 @@ fun TypeSystemCommonBackendContext.extractReificationArgument(initialType: Kotli
|
||||
while (type.isArrayOrNullableArray()) {
|
||||
arrayDepth++
|
||||
val argument = type.getArgument(0)
|
||||
if (argument.isStarProjection()) return null
|
||||
type = argument.getType()
|
||||
type =
|
||||
if (argument.isStarProjection()) nullableAnyType()
|
||||
else argument.getType()
|
||||
}
|
||||
|
||||
val typeParameter = type.typeConstructor().getTypeParameterClassifier() ?: return null
|
||||
if (!typeParameter.isReified()) return null
|
||||
|
||||
return Pair(typeParameter, ReificationArgument(typeParameter.getName().asString(), isNullable, arrayDepth))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.CAPTURES_CROSSINLINE_LAMBDA
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE
|
||||
@@ -599,6 +601,21 @@ class CoroutineCodegenForLambda private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun isCapturedSuspendLambda(closure: CalculatedClosure, name: String, bindingContext: BindingContext): Boolean {
|
||||
for ((param, value) in closure.captureVariables) {
|
||||
if (param !is ValueParameterDescriptor) continue
|
||||
if (value.fieldName != name) continue
|
||||
return param.type.isSuspendFunctionTypeOrSubtype
|
||||
}
|
||||
val classDescriptor = closure.capturedOuterClassDescriptor ?: return false
|
||||
return isCapturedSuspendLambda(classDescriptor, name, bindingContext)
|
||||
}
|
||||
|
||||
fun isCapturedSuspendLambda(classDescriptor: ClassDescriptor, name: String, bindingContext: BindingContext): Boolean {
|
||||
val closure = bindingContext[CLOSURE, classDescriptor] ?: return false
|
||||
return isCapturedSuspendLambda(closure, name, bindingContext)
|
||||
}
|
||||
|
||||
private class AddEndLabelMethodVisitor(
|
||||
delegate: MethodVisitor,
|
||||
access: Int,
|
||||
|
||||
@@ -650,7 +650,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
|
||||
val livenessFrames = analyzeLiveness(methodNode)
|
||||
|
||||
// References shall be cleaned up after unspill (during spill in next suspension point) to prevent memory leaks,
|
||||
// References shall be cleaned up after uspill (during spill in next suspension point) to prevent memory leaks,
|
||||
val referencesToSpillBySuspensionPointIndex = arrayListOf<List<ReferenceToSpill>>()
|
||||
// while primitives shall not
|
||||
val primitivesToSpillBySuspensionPointIndex = arrayListOf<List<PrimitiveToSpill>>()
|
||||
@@ -759,35 +759,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
referencesToCleanBySuspensionPointIndex += currentSpilledReferencesCount to predSpilledReferencesCount
|
||||
}
|
||||
|
||||
// Calculate debug metadata mapping before modifying method node to make it easier to locate
|
||||
// locals alive across suspension points.
|
||||
|
||||
fun calculateSpilledVariableAndField(
|
||||
suspension: SuspensionPoint,
|
||||
slot: Int,
|
||||
spillableVariable: SpillableVariable?
|
||||
): SpilledVariableAndField? {
|
||||
if (spillableVariable == null) return null
|
||||
val name = localVariableName(methodNode, slot, suspension.suspensionCallBegin.index()) ?: return null
|
||||
return SpilledVariableAndField(spillableVariable.fieldName, name)
|
||||
}
|
||||
|
||||
val spilledToVariableMapping = arrayListOf<List<SpilledVariableAndField>>()
|
||||
for (suspensionPointIndex in suspensionPoints.indices) {
|
||||
val suspension = suspensionPoints[suspensionPointIndex]
|
||||
|
||||
val spilledToVariable = arrayListOf<SpilledVariableAndField>()
|
||||
|
||||
referencesToSpillBySuspensionPointIndex[suspensionPointIndex].mapNotNullTo(spilledToVariable) { (slot, spillableVariable) ->
|
||||
calculateSpilledVariableAndField(suspension, slot, spillableVariable)
|
||||
}
|
||||
primitivesToSpillBySuspensionPointIndex[suspensionPointIndex].mapNotNullTo(spilledToVariable) { (slot, spillableVariable) ->
|
||||
calculateSpilledVariableAndField(suspension, slot, spillableVariable)
|
||||
}
|
||||
|
||||
spilledToVariableMapping += spilledToVariable
|
||||
}
|
||||
|
||||
// Mutate method node
|
||||
|
||||
fun generateSpillAndUnspill(suspension: SuspensionPoint, slot: Int, spillableVariable: SpillableVariable?) {
|
||||
@@ -801,22 +772,6 @@ class CoroutineTransformerMethodVisitor(
|
||||
return
|
||||
}
|
||||
|
||||
// Find and remove the local variable node, if any, in the local variable table corresponding to the slot that is spilled.
|
||||
var local: LocalVariableNode? = null
|
||||
val localRestart = LabelNode().linkWithLabel()
|
||||
val iterator = methodNode.localVariables.listIterator()
|
||||
while (iterator.hasNext()) {
|
||||
val node = iterator.next()
|
||||
if (node.index == slot &&
|
||||
methodNode.instructions.indexOf(node.start) <= methodNode.instructions.indexOf(suspension.suspensionCallBegin) &&
|
||||
methodNode.instructions.indexOf(node.end) > methodNode.instructions.indexOf(suspension.tryCatchBlockEndLabelAfterSuspensionCall)
|
||||
) {
|
||||
local = node
|
||||
iterator.remove()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
with(instructions) {
|
||||
// store variable before suspension call
|
||||
insertBefore(suspension.suspensionCallBegin, withInstructionAdapter {
|
||||
@@ -840,31 +795,8 @@ class CoroutineTransformerMethodVisitor(
|
||||
)
|
||||
StackValue.coerce(spillableVariable.normalizedType, spillableVariable.type, this)
|
||||
store(slot, spillableVariable.type)
|
||||
if (local != null) {
|
||||
visitLabel(localRestart.label)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Split the local variable range for the local so that it is visible until the next state label, but is
|
||||
// not visible until it has been unspilled from the continuation on the reentry path.
|
||||
if (local != null) {
|
||||
val previousEnd = local.end
|
||||
local.end = suspension.stateLabel
|
||||
// Add the local back, but end it at the next state label.
|
||||
methodNode.localVariables.add(local)
|
||||
// Add a new entry that starts after the local variable is restored from the continuation.
|
||||
methodNode.localVariables.add(
|
||||
LocalVariableNode(
|
||||
local.name,
|
||||
local.desc,
|
||||
local.signature,
|
||||
localRestart,
|
||||
previousEnd,
|
||||
local.index
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanUpField(suspension: SuspensionPoint, fieldIndex: Int) {
|
||||
@@ -907,6 +839,33 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate debug metadata mapping
|
||||
|
||||
fun calculateSpilledVariableAndField(
|
||||
suspension: SuspensionPoint,
|
||||
slot: Int,
|
||||
spillableVariable: SpillableVariable?
|
||||
): SpilledVariableAndField? {
|
||||
if (spillableVariable == null) return null
|
||||
val name = localVariableName(methodNode, slot, suspension.suspensionCallEnd.next.index()) ?: return null
|
||||
return SpilledVariableAndField(spillableVariable.fieldName, name)
|
||||
}
|
||||
|
||||
val spilledToVariableMapping = arrayListOf<List<SpilledVariableAndField>>()
|
||||
for (suspensionPointIndex in suspensionPoints.indices) {
|
||||
val suspension = suspensionPoints[suspensionPointIndex]
|
||||
|
||||
val spilledToVariable = arrayListOf<SpilledVariableAndField>()
|
||||
|
||||
referencesToSpillBySuspensionPointIndex[suspensionPointIndex].mapNotNullTo(spilledToVariable) { (slot, spillableVariable) ->
|
||||
calculateSpilledVariableAndField(suspension, slot, spillableVariable)
|
||||
}
|
||||
primitivesToSpillBySuspensionPointIndex[suspensionPointIndex].mapNotNullTo(spilledToVariable) { (slot, spillableVariable) ->
|
||||
calculateSpilledVariableAndField(suspension, slot, spillableVariable)
|
||||
}
|
||||
|
||||
spilledToVariableMapping += spilledToVariable
|
||||
}
|
||||
return spilledToVariableMapping
|
||||
}
|
||||
|
||||
@@ -942,6 +901,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
suspendMarkerVarIndex: Int,
|
||||
suspendPointLineNumber: LineNumberNode?
|
||||
): LabelNode {
|
||||
val stateLabel = LabelNode().linkWithLabel()
|
||||
val continuationLabelAfterLoadedResult = LabelNode()
|
||||
val suspendElementLineNumber = lineNumber
|
||||
var nextLineNumberNode = nextDefinitelyHitLineNumber(suspension)
|
||||
@@ -969,7 +929,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE)
|
||||
areturn(AsmTypes.OBJECT_TYPE)
|
||||
// Mark place for continuation
|
||||
visitLabel(suspension.stateLabel.label)
|
||||
visitLabel(stateLabel.label)
|
||||
})
|
||||
|
||||
// After suspension point there is always three nodes: L1, NOP, L2
|
||||
@@ -1025,7 +985,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
return suspension.stateLabel
|
||||
return stateLabel
|
||||
}
|
||||
|
||||
// Find the next line number instruction that is defintely hit. That is, a line number
|
||||
@@ -1194,7 +1154,6 @@ internal class SuspensionPoint(
|
||||
) {
|
||||
lateinit var tryCatchBlocksContinuationLabel: LabelNode
|
||||
|
||||
val stateLabel = LabelNode().linkWithLabel()
|
||||
val unboxInlineClassInstructions: List<AbstractInsnNode> = findUnboxInlineClassInstructions()
|
||||
|
||||
private fun findUnboxInlineClassInstructions(): List<AbstractInsnNode> {
|
||||
@@ -1276,13 +1235,16 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
fun isAlive(insnIndex: Int, variableIndex: Int): Boolean =
|
||||
liveness[insnIndex].isAlive(variableIndex)
|
||||
|
||||
fun nextLabel(node: AbstractInsnNode?): LabelNode? {
|
||||
var current = node
|
||||
while (current != null) {
|
||||
if (current is LabelNode) return current
|
||||
current = current.next
|
||||
}
|
||||
return null
|
||||
fun nextSuspensionPointEndLabel(insn: AbstractInsnNode): LabelNode {
|
||||
val suspensionPoint =
|
||||
InsnSequence(insn, method.instructions.last).firstOrNull { isAfterSuspendMarker(it) } ?: method.instructions.last
|
||||
return suspensionPoint as? LabelNode ?: suspensionPoint.findNextOrNull { it is LabelNode } as LabelNode
|
||||
}
|
||||
|
||||
fun nextSuspensionPointStartLabel(insn: AbstractInsnNode): LabelNode {
|
||||
val suspensionPoint =
|
||||
InsnSequence(insn, method.instructions.last).firstOrNull { isBeforeSuspendMarker(it) } ?: method.instructions.last
|
||||
return suspensionPoint as? LabelNode ?: suspensionPoint.findPreviousOrNull { it is LabelNode } as LabelNode
|
||||
}
|
||||
|
||||
fun min(a: LabelNode, b: LabelNode): LabelNode =
|
||||
@@ -1291,6 +1253,9 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
fun max(a: LabelNode, b: LabelNode): LabelNode =
|
||||
if (method.instructions.indexOf(a) < method.instructions.indexOf(b)) b else a
|
||||
|
||||
fun containsSuspensionPoint(a: LabelNode, b: LabelNode): Boolean =
|
||||
InsnSequence(min(a, b), max(a, b)).none { isBeforeSuspendMarker(it) }
|
||||
|
||||
val oldLvt = arrayListOf<LocalVariableNode>()
|
||||
for (record in method.localVariables) {
|
||||
oldLvt += record
|
||||
@@ -1311,40 +1276,35 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
// No variable in LVT -> do not add one
|
||||
val lvtRecord = oldLvt.findRecord(insnIndex, variableIndex) ?: continue
|
||||
if (lvtRecord.name == CONTINUATION_VARIABLE_NAME) continue
|
||||
// End the local when it is no longer live. Since it is not live, we will not spill and unspill it across
|
||||
// suspension points. It is tempting to keep it alive until the next suspension point to leave it visible in
|
||||
// the debugger for as long as possible. However, in the case of loops, the resumption after suspension can
|
||||
// have a backwards edge targeting instruction between the point of death and the next suspension point.
|
||||
//
|
||||
// For example, code such as the following:
|
||||
//
|
||||
// listOf<String>.forEach {
|
||||
// yield(it)
|
||||
// }
|
||||
//
|
||||
// Generates code of this form with a back edge after resumption that will lead to invalid locals tables
|
||||
// if the local range is extended to the next suspension point.
|
||||
//
|
||||
// iterator = iterable.iterator()
|
||||
// L1: (iterable dies here)
|
||||
// load iterator.next if there
|
||||
// yield suspension point
|
||||
//
|
||||
// L2: (resumption point)
|
||||
// restore live variables (not including iterable)
|
||||
// goto L1 (iterator not restored here, so we cannot not have iterator live at L1)
|
||||
val endLabel = nextLabel(insn.next)?.let { min(lvtRecord.end, it) } ?: lvtRecord.end
|
||||
// Extend lvt record to the next suspension point
|
||||
val endLabel = min(lvtRecord.end, nextSuspensionPointEndLabel(insn))
|
||||
// startLabel can be null in case of parameters
|
||||
@Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start
|
||||
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
if (lvtRecord !in oldLvtNodeToLatestNewLvtNode) {
|
||||
method.localVariables.add(node)
|
||||
// Attempt to extend existing local variable node corresponding to the record in
|
||||
// the original local variable table.
|
||||
val recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
||||
if (recordToExtend != null && containsSuspensionPoint(recordToExtend.end, startLabel)) {
|
||||
recordToExtend.end = endLabel
|
||||
} else {
|
||||
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||
if (lvtRecord !in oldLvtNodeToLatestNewLvtNode) {
|
||||
method.localVariables.add(node)
|
||||
}
|
||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = node
|
||||
}
|
||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val deadVariables = arrayListOf<Int>()
|
||||
outer@for (variableIndex in start until method.maxLocals) {
|
||||
if (oldLvt.none { it.index == variableIndex }) continue
|
||||
for (insnIndex in 0 until (method.instructions.size() - 1)) {
|
||||
if (isAlive(insnIndex, variableIndex)) continue@outer
|
||||
}
|
||||
deadVariables += variableIndex
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1360,5 +1320,19 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
||||
method.localVariables.add(variable)
|
||||
continue
|
||||
}
|
||||
|
||||
// Shrink LVT records of dead variables to the next suspension point
|
||||
if (variable.index in deadVariables) {
|
||||
method.localVariables.add(
|
||||
LocalVariableNode(
|
||||
variable.name,
|
||||
variable.desc,
|
||||
variable.signature,
|
||||
variable.start,
|
||||
min(variable.end, nextSuspensionPointStartLabel(variable.start)),
|
||||
variable.index
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.isUnitInstance
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeAll
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
@@ -85,7 +85,7 @@ private class UnitSourceInterpreter(private val localVariables: Set<Int>) : Basi
|
||||
}
|
||||
|
||||
fun run(internalClassName: String, methodNode: MethodNode): Array<Frame<BasicValue>?> {
|
||||
val frames = FastMethodAnalyzer<BasicValue>(internalClassName, methodNode, this).analyze()
|
||||
val frames = MethodAnalyzer<BasicValue>(internalClassName, methodNode, this).analyze()
|
||||
// The ASM analyzer does not visit POP instructions, so we do so here.
|
||||
for ((insn, frame) in methodNode.instructions.asSequence().zip(frames.asSequence())) {
|
||||
if (frame != null && insn.opcode == Opcodes.POP) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.codegen.coroutines
|
||||
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
@@ -130,11 +130,11 @@ internal fun performSpilledVariableFieldTypesAnalysis(
|
||||
thisName: String
|
||||
): Array<out Frame<BasicValue>?> {
|
||||
val interpreter = IntLikeCoerceInterpreter()
|
||||
FastMethodAnalyzer(thisName, methodNode, interpreter).analyze()
|
||||
MethodAnalyzer(thisName, methodNode, interpreter).analyze()
|
||||
for ((insn, type) in interpreter.needsToBeCoerced) {
|
||||
methodNode.instructions.insert(insn, withInstructionAdapter { coerceInt(type, this) })
|
||||
}
|
||||
return FastMethodAnalyzer(thisName, methodNode, OptimizationBasicInterpreter()).analyze()
|
||||
return MethodAnalyzer(thisName, methodNode, OptimizationBasicInterpreter()).analyze()
|
||||
}
|
||||
|
||||
private fun coerceInt(to: Type, v: InstructionAdapter) {
|
||||
|
||||
@@ -189,7 +189,7 @@ fun ResolvedCall<*>.replaceSuspensionFunctionWithRealDescriptor(
|
||||
Pair(it, typeArguments[candidateDescriptor.typeParameters[it.index]]!!.asTypeProjection())
|
||||
}.toMap()
|
||||
|
||||
newCall.setSubstitutor(
|
||||
newCall.setResultingSubstitutor(
|
||||
TypeConstructorSubstitution.createByParametersMap(newTypeArguments).buildSubstitutor()
|
||||
)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.coroutines.DEBUG_METADATA_ANNOTATION_ASM_TYPE
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isResumeImplMethodName
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.CoroutineTransformer
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable
|
||||
@@ -26,7 +27,6 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import java.util.*
|
||||
|
||||
@@ -272,7 +272,7 @@ class AnonymousObjectTransformer(
|
||||
// Since $$forInline functions are not generated if retransformation is the last one (i.e. call site is not inline)
|
||||
// link to the function in OUTERCLASS field becomes invalid. However, since $$forInline function always has no-inline
|
||||
// companion without the suffix, use it.
|
||||
visitor.visitOuterClass(info.ownerClassName, info.method.name.removeSuffix(FOR_INLINE_SUFFIX), info.method.descriptor)
|
||||
visitor.visitOuterClass(info.ownerClassName, info.functionName?.removeSuffix(FOR_INLINE_SUFFIX), info.functionDesc)
|
||||
}
|
||||
|
||||
private fun inlineMethodAndUpdateGlobalResult(
|
||||
@@ -314,10 +314,11 @@ class AnonymousObjectTransformer(
|
||||
SourceMapCopier(sourceMapper, sourceMap),
|
||||
InlineCallSiteInfo(
|
||||
transformationInfo.oldClassName,
|
||||
Method(sourceNode.name, if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc),
|
||||
sourceNode.name,
|
||||
if (isConstructor) transformationInfo.newConstructorDescriptor else sourceNode.desc,
|
||||
inliningContext.callSiteInfo.isInlineOrInsideInline,
|
||||
inliningContext.callSiteInfo.file,
|
||||
inliningContext.callSiteInfo.lineNumber
|
||||
isSuspendFunctionOrLambda(sourceNode),
|
||||
inliningContext.root.sourceCompilerForInline.inlineCallSiteInfo.lineNumber
|
||||
), null
|
||||
)
|
||||
|
||||
@@ -327,6 +328,12 @@ class AnonymousObjectTransformer(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isSuspendFunctionOrLambda(sourceNode: MethodNode): Boolean =
|
||||
(sourceNode.desc.endsWith(";Lkotlin/coroutines/Continuation;)Ljava/lang/Object;") ||
|
||||
sourceNode.desc.endsWith(";Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object;")) &&
|
||||
(CoroutineTransformer.findFakeContinuationConstructorClassName(sourceNode) != null ||
|
||||
languageVersionSettings.isResumeImplMethodName(sourceNode.name.removeSuffix(FOR_INLINE_SUFFIX)))
|
||||
|
||||
private fun generateConstructorAndFields(
|
||||
classBuilder: ClassBuilder,
|
||||
allCapturedBuilder: ParametersBuilder,
|
||||
@@ -540,7 +547,7 @@ class AnonymousObjectTransformer(
|
||||
capturedParamBuilder.addCapturedParam(desc, desc.fieldName, !capturedOuterThisTypes.add(desc.type.className))
|
||||
else
|
||||
capturedParamBuilder.addCapturedParam(desc, addUniqueField(desc.fieldName + INLINE_TRANSFORMATION_SUFFIX), false)
|
||||
if (desc.isSuspend) {
|
||||
if (info is ExpressionLambda && info.isCapturedSuspend(desc)) {
|
||||
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||
}
|
||||
val composed = StackValue.field(
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class CapturedParamDesc(containingLambdaType: Type, val fieldName: String, val type: Type, val isSuspend: Boolean = false) {
|
||||
val containingLambdaName: String = containingLambdaType.internalName
|
||||
class CapturedParamDesc(private val containingLambdaType: Type, val fieldName: String, val type: Type) {
|
||||
|
||||
val containingLambdaName: String
|
||||
get() = containingLambdaType.internalName
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
data class MethodId(val ownerInternalName: String, val method: Method)
|
||||
|
||||
class InlineCache {
|
||||
val classBytes: SLRUMap<String, ByteArray> = SLRUMap(30, 20)
|
||||
val classBytes: SLRUMap<ClassId, ByteArray> = SLRUMap(30, 20)
|
||||
val methodNodeById: SLRUMap<MethodId, SMAPAndMethodNode> = SLRUMap(60, 50)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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
|
||||
|
||||
class InlineCallSiteInfo(
|
||||
val ownerClassName: String,
|
||||
val functionName: String?,
|
||||
val functionDesc: String?,
|
||||
val isInlineOrInsideInline: Boolean,
|
||||
val isSuspend: Boolean,
|
||||
val lineNumber: Int
|
||||
)
|
||||
@@ -6,83 +6,213 @@
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.codegen.context.ClosureContext
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructors
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.Position
|
||||
import org.jetbrains.kotlin.incremental.components.ScopeKind
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.inline.isInlineOnly
|
||||
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.TypeSystemCommonBackendContext
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
|
||||
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
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.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import kotlin.math.max
|
||||
|
||||
abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
protected val codegen: T,
|
||||
protected val state: GenerationState,
|
||||
protected val functionDescriptor: FunctionDescriptor,
|
||||
protected val methodOwner: Type,
|
||||
protected val jvmSignature: JvmMethodSignature,
|
||||
private val typeParameterMappings: TypeParameterMappings<*>,
|
||||
protected val sourceCompiler: SourceCompilerForInline,
|
||||
private val reifiedTypeInliner: ReifiedTypeInliner<*>
|
||||
) {
|
||||
init {
|
||||
assert(InlineUtil.isInline(functionDescriptor)) {
|
||||
"InlineCodegen can inline only inline functions: $functionDescriptor"
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement AS_FUNCTION inline strategy
|
||||
private val asFunctionInline = false
|
||||
|
||||
private val initialFrameSize = codegen.frameMap.currentSize
|
||||
|
||||
private val isSameModule: Boolean
|
||||
|
||||
protected val invocationParamBuilder = ParametersBuilder.newBuilder()
|
||||
|
||||
protected val expressionMap = linkedMapOf<Int, FunctionalArgument>()
|
||||
|
||||
var activeLambda: LambdaInfo? = null
|
||||
protected set
|
||||
|
||||
private val sourceMapper = sourceCompiler.lazySourceMapper
|
||||
|
||||
protected var delayedHiddenWriting: Function0<Unit>? = null
|
||||
|
||||
protected val maskValues = ArrayList<Int>()
|
||||
protected var maskStartIndex = -1
|
||||
protected var methodHandleInDefaultMethodIndex = -1
|
||||
|
||||
protected fun generateStub(text: String, codegen: BaseExpressionCodegen) {
|
||||
leaveTemps()
|
||||
AsmUtil.genThrow(codegen.visitor, "java/lang/UnsupportedOperationException", "Call is part of inline cycle: $text")
|
||||
}
|
||||
init {
|
||||
isSameModule = sourceCompiler.isCallInsideSameModuleAsDeclared(functionDescriptor)
|
||||
|
||||
fun performInline(registerLineNumberAfterwards: Boolean, isInlineOnly: Boolean) {
|
||||
var nodeAndSmap: SMAPAndMethodNode? = null
|
||||
try {
|
||||
nodeAndSmap = sourceCompiler.compileInlineFunction(jvmSignature).apply {
|
||||
node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
if (functionDescriptor !is FictitiousArrayConstructor) {
|
||||
//track changes for property accessor and @JvmName inline functions/property accessors
|
||||
if (jvmSignature.asmMethod.name != functionDescriptor.name.asString()) {
|
||||
trackLookup(functionDescriptor)
|
||||
}
|
||||
val result = inlineCall(nodeAndSmap, isInlineOnly)
|
||||
leaveTemps()
|
||||
codegen.propagateChildReifiedTypeParametersUsages(result.reifiedTypeParametersUsages)
|
||||
codegen.markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards)
|
||||
state.factory.removeClasses(result.calcClassesToRemove())
|
||||
} catch (e: CompilationException) {
|
||||
throw e
|
||||
} catch (e: InlineException) {
|
||||
throw CompilationException(
|
||||
"Couldn't inline method call: ${sourceCompiler.callElementText}",
|
||||
e, sourceCompiler.callElement as? PsiElement
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw CompilationException(
|
||||
"Couldn't inline method call: ${sourceCompiler.callElementText}\nMethod: ${nodeAndSmap?.node?.nodeText}",
|
||||
e, sourceCompiler.callElement as? PsiElement
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, isInlineOnly: Boolean): InlineResult {
|
||||
|
||||
protected fun throwCompilationException(
|
||||
nodeAndSmap: SMAPAndMethodNode?, e: Exception, generateNodeText: Boolean
|
||||
): CompilationException {
|
||||
val contextDescriptor = sourceCompiler.compilationContextDescriptor
|
||||
val element = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor)
|
||||
val node = nodeAndSmap?.node
|
||||
throw CompilationException(
|
||||
"Couldn't inline method call '" + functionDescriptor.name + "' into\n" +
|
||||
DescriptorRenderer.DEBUG_TEXT.render(contextDescriptor) + "\n" +
|
||||
(element?.text ?: "<no source>") +
|
||||
if (generateNodeText) "\nCause: " + node.nodeText else "",
|
||||
e, sourceCompiler.callElement as? PsiElement
|
||||
)
|
||||
}
|
||||
|
||||
protected fun generateStub(text: String, codegen: BaseExpressionCodegen) {
|
||||
leaveTemps()
|
||||
AsmUtil.genThrow(codegen.v, "java/lang/UnsupportedOperationException", "Call is part of inline cycle: $text")
|
||||
}
|
||||
|
||||
protected fun endCall(result: InlineResult, registerLineNumberAfterwards: Boolean) {
|
||||
leaveTemps()
|
||||
|
||||
codegen.propagateChildReifiedTypeParametersUsages(result.reifiedTypeParametersUsages)
|
||||
|
||||
state.factory.removeClasses(result.calcClassesToRemove())
|
||||
|
||||
codegen.markLineNumberAfterInlineIfNeeded(registerLineNumberAfterwards)
|
||||
}
|
||||
|
||||
fun performInline(
|
||||
typeArguments: List<TypeParameterMarker>?,
|
||||
inlineDefaultLambdas: Boolean,
|
||||
mapDefaultSignature: Boolean,
|
||||
typeSystem: TypeSystemCommonBackendContext,
|
||||
registerLineNumberAfterwards: Boolean,
|
||||
) {
|
||||
var nodeAndSmap: SMAPAndMethodNode? = null
|
||||
try {
|
||||
nodeAndSmap = createInlineMethodNode(mapDefaultSignature, typeArguments, typeSystem)
|
||||
endCall(inlineCall(nodeAndSmap, inlineDefaultLambdas), registerLineNumberAfterwards)
|
||||
} catch (e: CompilationException) {
|
||||
throw e
|
||||
} catch (e: InlineException) {
|
||||
throw throwCompilationException(nodeAndSmap, e, false)
|
||||
} catch (e: Exception) {
|
||||
throw throwCompilationException(nodeAndSmap, e, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun canSkipStackSpillingOnInline(methodNode: MethodNode): Boolean {
|
||||
// Stack spilling before inline function 'f' call is required if:
|
||||
// - 'f' is a suspend function
|
||||
// - 'f' has try-catch blocks
|
||||
// - 'f' has loops
|
||||
//
|
||||
// Instead of checking for loops precisely, we just check if there are any backward jumps -
|
||||
// that is, a jump from instruction #i to instruction #j where j < i
|
||||
|
||||
if (functionDescriptor.isSuspend) return false
|
||||
if (methodNode.tryCatchBlocks.isNotEmpty()) return false
|
||||
|
||||
fun isBackwardJump(fromIndex: Int, toLabel: LabelNode) =
|
||||
methodNode.instructions.indexOf(toLabel) < fromIndex
|
||||
|
||||
val insns = methodNode.instructions.toArray()
|
||||
for (i in insns.indices) {
|
||||
val insn = insns[i]
|
||||
when (insn) {
|
||||
is JumpInsnNode ->
|
||||
if (isBackwardJump(i, insn.label)) return false
|
||||
|
||||
is LookupSwitchInsnNode -> {
|
||||
insn.dflt?.let {
|
||||
if (isBackwardJump(i, it)) return false
|
||||
}
|
||||
if (insn.labels.any { isBackwardJump(i, it) }) return false
|
||||
}
|
||||
|
||||
is TableSwitchInsnNode -> {
|
||||
insn.dflt?.let {
|
||||
if (isBackwardJump(i, it)) return false
|
||||
}
|
||||
if (insn.labels.any { isBackwardJump(i, it) }) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun continuationValue(): StackValue {
|
||||
assert(codegen is ExpressionCodegen) { "Expected ExpressionCodegen in coroutineContext inlining" }
|
||||
codegen as ExpressionCodegen
|
||||
|
||||
val parentContext = codegen.context.parentContext
|
||||
return if (parentContext is ClosureContext) {
|
||||
val originalSuspendLambdaDescriptor =
|
||||
parentContext.originalSuspendLambdaDescriptor ?: error("No original lambda descriptor found")
|
||||
codegen.genCoroutineInstanceForSuspendLambda(originalSuspendLambdaDescriptor)
|
||||
?: error("No stack value for coroutine instance of lambda found")
|
||||
} else
|
||||
codegen.getContinuationParameterFromEnclosingSuspendFunctionDescriptor(codegen.context.functionDescriptor)
|
||||
?: error("No stack value for continuation parameter of suspend function")
|
||||
}
|
||||
|
||||
private fun inlineCall(nodeAndSmap: SMAPAndMethodNode, inlineDefaultLambda: Boolean): InlineResult {
|
||||
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
|
||||
val node = nodeAndSmap.node
|
||||
if (maskStartIndex != -1) {
|
||||
if (inlineDefaultLambda) {
|
||||
for (lambda in extractDefaultLambdas(node)) {
|
||||
invocationParamBuilder.buildParameters().getParameterByDeclarationSlot(lambda.offset).functionalArgument = lambda
|
||||
val prev = expressionMap.put(lambda.offset, lambda)
|
||||
assert(prev == null) { "Lambda with offset ${lambda.offset} already exists: $prev" }
|
||||
if (lambda.needReification) {
|
||||
lambda.reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(lambda.node.node))
|
||||
}
|
||||
rememberCapturedForDefaultLambda(lambda)
|
||||
}
|
||||
}
|
||||
|
||||
val reificationResult = reifiedTypeInliner.reifyInstructions(node)
|
||||
generateClosuresBodies()
|
||||
|
||||
//through generation captured parameters will be added to invocationParamBuilder
|
||||
putClosureParametersOnStack()
|
||||
|
||||
val parameters = invocationParamBuilder.buildParameters()
|
||||
|
||||
@@ -91,14 +221,13 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
sourceCompiler, sourceCompiler.inlineCallSiteInfo, reifiedTypeInliner, typeParameterMappings
|
||||
)
|
||||
|
||||
val sourceMapper = sourceCompiler.sourceMapper
|
||||
val sourceInfo = sourceMapper.sourceInfo!!
|
||||
val callSite = SourcePosition(codegen.lastLineNumber, sourceInfo.sourceFileName!!, sourceInfo.pathOrCleanFQN)
|
||||
val inliner = MethodInliner(
|
||||
node, parameters, info, FieldRemapper(null, null, parameters), sourceCompiler.isCallInsideSameModuleAsCallee,
|
||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||
"Method inlining " + sourceCompiler.callElementText,
|
||||
SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP, callSite),
|
||||
info.callSiteInfo, if (isInlineOnly) InlineOnlySmapSkipper(codegen) else null,
|
||||
info.callSiteInfo, if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||
!isInlinedToInlineFunInKotlinRuntime()
|
||||
) //with captured
|
||||
|
||||
@@ -113,47 +242,43 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
|
||||
val infos = MethodInliner.processReturns(adapter, sourceCompiler.getContextLabels(), null)
|
||||
generateAndInsertFinallyBlocks(
|
||||
adapter, infos, (remapper.remap(parameters.argsSizeOnStack).value as StackValue.Local).index
|
||||
adapter, infos, (remapper.remap(parameters.argsSizeOnStack + 1).value as StackValue.Local).index
|
||||
)
|
||||
if (!sourceCompiler.isFinallyMarkerRequired) {
|
||||
if (!sourceCompiler.isFinallyMarkerRequired()) {
|
||||
removeFinallyMarkers(adapter)
|
||||
}
|
||||
|
||||
// In case `codegen.visitor` is `<clinit>`, initializer for the `$assertionsDisabled` field
|
||||
// In case `codegen.v` is `<clinit>`, initializer for the `$assertionsDisabled` field
|
||||
// needs to be inserted before the code that actually uses it.
|
||||
if (info.generateAssertField) {
|
||||
generateAssertField()
|
||||
}
|
||||
generateAssertFieldIfNeeded(info)
|
||||
|
||||
val shouldSpillStack = node.requiresEmptyStackOnEntry()
|
||||
val shouldSpillStack = !canSkipStackSpillingOnInline(node)
|
||||
if (shouldSpillStack) {
|
||||
addInlineMarker(codegen.visitor, true)
|
||||
addInlineMarker(codegen.v, true)
|
||||
}
|
||||
adapter.accept(MethodBodyVisitor(codegen.visitor))
|
||||
adapter.accept(MethodBodyVisitor(codegen.v))
|
||||
if (shouldSpillStack) {
|
||||
addInlineMarker(codegen.visitor, false)
|
||||
addInlineMarker(codegen.v, false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
abstract fun extractDefaultLambdas(node: MethodNode): List<DefaultLambda>
|
||||
|
||||
protected inline fun <T> extractDefaultLambdas(
|
||||
node: MethodNode, parameters: Map<Int, T>, block: ExtractedDefaultLambda.(T) -> DefaultLambda
|
||||
): List<DefaultLambda> = expandMaskConditionsAndUpdateVariableNodes(
|
||||
node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex, parameters.keys
|
||||
).map {
|
||||
it.block(parameters[it.offset]!!)
|
||||
}
|
||||
abstract fun descriptorIsDeserialized(memberDescriptor: CallableMemberDescriptor): Boolean
|
||||
|
||||
private fun generateAndInsertFinallyBlocks(
|
||||
fun generateAndInsertFinallyBlocks(
|
||||
intoNode: MethodNode,
|
||||
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
|
||||
offsetForFinallyLocalVar: Int
|
||||
) {
|
||||
if (!sourceCompiler.hasFinallyBlocks()) return
|
||||
|
||||
val extensionPoints = insertPoints.associateBy { it.beforeIns }
|
||||
val extensionPoints = HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>()
|
||||
for (insertPoint in insertPoints) {
|
||||
extensionPoints.put(insertPoint.beforeIns, insertPoint)
|
||||
}
|
||||
|
||||
val processor = DefaultProcessor(intoNode, offsetForFinallyLocalVar)
|
||||
|
||||
var curFinallyDepth = 0
|
||||
@@ -167,35 +292,49 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
|
||||
val extension = extensionPoints[curInstr]
|
||||
if (extension != null) {
|
||||
var nextFreeLocalIndex = processor.nextFreeLocalIndex
|
||||
for (local in processor.localVarsMetaInfo.currentIntervals) {
|
||||
val size = Type.getType(local.node.desc).size
|
||||
nextFreeLocalIndex = max(offsetForFinallyLocalVar + local.node.index + size, nextFreeLocalIndex)
|
||||
}
|
||||
|
||||
val start = Label()
|
||||
|
||||
val finallyNode = createEmptyMethodNode()
|
||||
finallyNode.visitLabel(start)
|
||||
val mark = codegen.frameMap.skipTo(nextFreeLocalIndex)
|
||||
sourceCompiler.generateFinallyBlocks(
|
||||
finallyNode, curFinallyDepth, extension.returnType, extension.finallyIntervalEnd.label, extension.jumpTarget
|
||||
|
||||
val finallyCodegen =
|
||||
sourceCompiler.createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode, curFinallyDepth)
|
||||
|
||||
val frameMap = finallyCodegen.frameMap
|
||||
val mark = frameMap.mark()
|
||||
var marker = -1
|
||||
val intervals = processor.localVarsMetaInfo.currentIntervals
|
||||
for (interval in intervals) {
|
||||
marker = max(interval.node.index + 1, marker)
|
||||
}
|
||||
while (frameMap.currentSize < max(processor.nextFreeLocalIndex, offsetForFinallyLocalVar + marker)) {
|
||||
frameMap.enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
sourceCompiler.generateFinallyBlocksIfNeeded(
|
||||
finallyCodegen, extension.returnType, extension.finallyIntervalEnd.label, extension.jumpTarget
|
||||
)
|
||||
mark.dropTo()
|
||||
|
||||
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||
insertNodeBefore(finallyNode, intoNode, curInstr)
|
||||
|
||||
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
|
||||
processor.tryBlocksMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
processor.localVarsMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
|
||||
//processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
|
||||
|
||||
mark.dropTo()
|
||||
}
|
||||
|
||||
curInstr = curInstr.next
|
||||
}
|
||||
|
||||
processor.substituteTryBlockNodes(intoNode)
|
||||
processor.substituteLocalVarTable(intoNode)
|
||||
|
||||
//processor.substituteLocalVarTable(intoNode);
|
||||
}
|
||||
|
||||
protected abstract fun generateAssertField()
|
||||
protected abstract fun generateAssertFieldIfNeeded(info: RootInliningContext)
|
||||
|
||||
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
|
||||
val codegen = this.codegen as? ExpressionCodegen ?: return false
|
||||
@@ -208,50 +347,90 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
}
|
||||
}
|
||||
|
||||
protected fun rememberClosure(parameterType: Type, index: Int, lambdaInfo: LambdaInfo) {
|
||||
val closureInfo = invocationParamBuilder.addNextValueParameter(parameterType, true, null, index)
|
||||
closureInfo.functionalArgument = lambdaInfo
|
||||
expressionMap[closureInfo.index] = lambdaInfo
|
||||
}
|
||||
|
||||
protected fun putCapturedToLocalVal(stackValue: StackValue, capturedParam: CapturedParamDesc, kotlinType: KotlinType?) {
|
||||
val info = invocationParamBuilder.addCapturedParam(capturedParam, capturedParam.fieldName, false)
|
||||
if (stackValue.isLocalWithNoBoxing(JvmKotlinType(info.type, kotlinType))) {
|
||||
info.remapValue = stackValue
|
||||
} else {
|
||||
stackValue.put(info.type, kotlinType, codegen.visitor)
|
||||
val local = StackValue.local(codegen.frameMap.enterTemp(info.type), info.type)
|
||||
local.store(StackValue.onStack(info.type), codegen.visitor)
|
||||
info.remapValue = local
|
||||
info.isSynthetic = true
|
||||
}
|
||||
}
|
||||
|
||||
protected fun putArgumentToLocalVal(jvmKotlinType: JvmKotlinType, stackValue: StackValue, parameterIndex: Int, kind: ValueKind) {
|
||||
if (kind === ValueKind.DEFAULT_MASK || kind === ValueKind.METHOD_HANDLE_IN_DEFAULT) {
|
||||
return processDefaultMaskOrMethodHandler(stackValue, kind)
|
||||
}
|
||||
|
||||
val info = invocationParamBuilder.addNextValueParameter(jvmKotlinType.type, false, null, parameterIndex)
|
||||
info.functionalArgument = when (kind) {
|
||||
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND ->
|
||||
NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER ->
|
||||
NonInlineableArgumentForInlineableSuspendParameter
|
||||
else -> null
|
||||
}
|
||||
when {
|
||||
kind === ValueKind.DEFAULT_PARAMETER ->
|
||||
codegen.frameMap.enterTemp(info.type) // the inline function will put the value into this slot
|
||||
stackValue.isLocalWithNoBoxing(jvmKotlinType) ->
|
||||
info.remapValue = stackValue
|
||||
else -> {
|
||||
stackValue.put(info.type, jvmKotlinType.kotlinType, codegen.visitor)
|
||||
codegen.visitor.store(codegen.frameMap.enterTemp(info.type), info.type)
|
||||
private fun generateClosuresBodies() {
|
||||
for (info in expressionMap.values) {
|
||||
if (info is LambdaInfo) {
|
||||
info.generateLambdaBody(sourceCompiler, reifiedTypeInliner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun putArgumentOrCapturedToLocalVal(
|
||||
jvmKotlinType: JvmKotlinType,
|
||||
stackValue: StackValue,
|
||||
capturedParamIndex: Int,
|
||||
parameterIndex: Int,
|
||||
kind: ValueKind
|
||||
) {
|
||||
val isDefaultParameter = kind === ValueKind.DEFAULT_PARAMETER
|
||||
val jvmType = jvmKotlinType.type
|
||||
val kotlinType = jvmKotlinType.kotlinType
|
||||
if (!isDefaultParameter && shouldPutGeneralValue(jvmType, kotlinType, stackValue)) {
|
||||
stackValue.put(jvmType, kotlinType, codegen.v)
|
||||
}
|
||||
|
||||
if (!asFunctionInline && Type.VOID_TYPE !== jvmType) {
|
||||
//TODO remap only inlinable closure => otherwise we could get a lot of problem
|
||||
val couldBeRemapped = !shouldPutGeneralValue(jvmType, kotlinType, stackValue) && kind !== ValueKind.DEFAULT_PARAMETER
|
||||
val remappedValue = if (couldBeRemapped) stackValue else null
|
||||
|
||||
val info: ParameterInfo
|
||||
if (capturedParamIndex >= 0) {
|
||||
val capturedParamInfoInLambda = activeLambda!!.capturedVars[capturedParamIndex]
|
||||
info = invocationParamBuilder.addCapturedParam(capturedParamInfoInLambda, capturedParamInfoInLambda.fieldName, false)
|
||||
info.remapValue = remappedValue
|
||||
} else {
|
||||
info = invocationParamBuilder.addNextValueParameter(jvmType, false, remappedValue, parameterIndex)
|
||||
info.functionalArgument = when (kind) {
|
||||
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_PARAMETER_CALLED_IN_SUSPEND ->
|
||||
NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||
ValueKind.NON_INLINEABLE_ARGUMENT_FOR_INLINE_SUSPEND_PARAMETER -> NonInlineableArgumentForInlineableSuspendParameter
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
recordParameterValueInLocalVal(
|
||||
false,
|
||||
isDefaultParameter || kind === ValueKind.DEFAULT_LAMBDA_CAPTURED_PARAMETER,
|
||||
info
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun recordParameterValueInLocalVal(
|
||||
delayedWritingToLocals: Boolean,
|
||||
skipStore: Boolean,
|
||||
vararg infos: ParameterInfo
|
||||
): Function0<Unit>? {
|
||||
val index = IntArray(infos.size) { i ->
|
||||
if (!infos[i].isSkippedOrRemapped) {
|
||||
codegen.frameMap.enterTemp(infos[i].type)
|
||||
} else -1
|
||||
}
|
||||
|
||||
val possibleLazyTask = {
|
||||
for (i in infos.indices.reversed()) {
|
||||
val info = infos[i]
|
||||
if (!info.isSkippedOrRemapped) {
|
||||
val type = info.type
|
||||
val local = StackValue.local(index[i], type)
|
||||
if (!skipStore) {
|
||||
local.store(StackValue.onStack(info.typeOnStack), codegen.v)
|
||||
}
|
||||
if (info is CapturedParamInfo) {
|
||||
info.remapValue = local
|
||||
info.isSynthetic = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (delayedWritingToLocals) return possibleLazyTask
|
||||
possibleLazyTask()
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
private fun leaveTemps() {
|
||||
invocationParamBuilder.listAllParams().asReversed().forEach { param ->
|
||||
if (!param.isSkippedOrRemapped || CapturedParamInfo.isSynthetic(param)) {
|
||||
@@ -260,19 +439,45 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun rememberCapturedForDefaultLambda(defaultLambda: DefaultLambda) {
|
||||
for (captured in defaultLambda.capturedVars) {
|
||||
val info = invocationParamBuilder.addCapturedParam(captured, captured.fieldName, false)
|
||||
info.remapValue = StackValue.local(codegen.frameMap.enterTemp(info.type), info.type)
|
||||
info.isSynthetic = true
|
||||
private fun putClosureParametersOnStack() {
|
||||
for (next in expressionMap.values) {
|
||||
//closure parameters for bounded callable references are generated inplace
|
||||
if (next is LambdaInfo) {
|
||||
if (next is ExpressionLambda && next.isBoundCallableReference) continue
|
||||
putClosureParametersOnStack(next, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processDefaultMaskOrMethodHandler(value: StackValue, kind: ValueKind) {
|
||||
assert(value is StackValue.Constant) { "Additional default method argument should be constant, but $value" }
|
||||
abstract protected fun putClosureParametersOnStack(next: LambdaInfo, functionReferenceReceiver: StackValue?)
|
||||
|
||||
protected fun rememberCapturedForDefaultLambda(defaultLambda: DefaultLambda) {
|
||||
for ((paramIndex, captured) in defaultLambda.capturedVars.withIndex()) {
|
||||
putArgumentOrCapturedToLocalVal(
|
||||
JvmKotlinType(captured.type),
|
||||
//HACK: actually parameter would be placed on stack in default function
|
||||
// also see ValueKind.DEFAULT_LAMBDA_CAPTURED_PARAMETER check
|
||||
StackValue.onStack(captured.type),
|
||||
paramIndex,
|
||||
paramIndex,
|
||||
ValueKind.DEFAULT_LAMBDA_CAPTURED_PARAMETER
|
||||
)
|
||||
|
||||
defaultLambda.parameterOffsetsInDefault.add(invocationParamBuilder.nextParameterOffset)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected fun processDefaultMaskOrMethodHandler(value: StackValue, kind: ValueKind): Boolean {
|
||||
if (kind !== ValueKind.DEFAULT_MASK && kind !== ValueKind.METHOD_HANDLE_IN_DEFAULT) {
|
||||
return false
|
||||
}
|
||||
assert(value is StackValue.Constant) {
|
||||
"Additional default method argument should be constant, but " + value
|
||||
}
|
||||
val constantValue = (value as StackValue.Constant).value
|
||||
if (kind === ValueKind.DEFAULT_MASK) {
|
||||
assert(constantValue is Int) { "Mask should be of Integer type, but $constantValue" }
|
||||
assert(constantValue is Int) { "Mask should be of Integer type, but " + constantValue }
|
||||
maskValues.add(constantValue as Int)
|
||||
if (maskStartIndex == -1) {
|
||||
maskStartIndex = invocationParamBuilder.listAllParams().sumOf {
|
||||
@@ -283,40 +488,228 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
assert(constantValue == null) { "Additional method handle for default argument should be null, but " + constantValue!! }
|
||||
methodHandleInDefaultMethodIndex = maskStartIndex + maskValues.size
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun trackLookup(functionOrAccessor: FunctionDescriptor) {
|
||||
val functionOrAccessorName = jvmSignature.asmMethod.name
|
||||
val lookupTracker = state.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER) ?: return
|
||||
val location = sourceCompiler.lookupLocation.location ?: return
|
||||
val position = if (lookupTracker.requiresPosition) location.position else Position.NO_POSITION
|
||||
val classOrPackageFragment = functionOrAccessor.containingDeclaration
|
||||
lookupTracker.record(
|
||||
location.filePath,
|
||||
position,
|
||||
DescriptorUtils.getFqName(classOrPackageFragment).asString(),
|
||||
ScopeKind.CLASSIFIER,
|
||||
functionOrAccessorName
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
internal fun createInlineMethodNode(
|
||||
callDefault: Boolean,
|
||||
typeArguments: List<TypeParameterMarker>?,
|
||||
typeSystem: TypeSystemCommonBackendContext
|
||||
): SMAPAndMethodNode {
|
||||
val intrinsic = generateInlineIntrinsic(state, functionDescriptor, typeArguments, typeSystem)
|
||||
if (intrinsic != null) {
|
||||
return SMAPAndMethodNode(intrinsic, createDefaultFakeSMAP())
|
||||
}
|
||||
|
||||
var asmMethod = mapMethod(callDefault)
|
||||
if (asmMethod.name.contains("-") &&
|
||||
!state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_INLINE_CLASSES_MANGLING_SCHEME) &&
|
||||
classFileContainsMethod(functionDescriptor, state, asmMethod) == false
|
||||
) {
|
||||
state.typeMapper.useOldManglingRulesForFunctionAcceptingInlineClass = true
|
||||
asmMethod = mapMethod(callDefault)
|
||||
state.typeMapper.useOldManglingRulesForFunctionAcceptingInlineClass = false
|
||||
}
|
||||
|
||||
val directMember = getDirectMemberAndCallableFromObject(functionDescriptor)
|
||||
if (!isBuiltInArrayIntrinsic(functionDescriptor) && !descriptorIsDeserialized(directMember)) {
|
||||
val node = sourceCompiler.doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, callDefault, asmMethod)
|
||||
node.node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
return node
|
||||
}
|
||||
|
||||
return getCompiledMethodNodeInner(functionDescriptor, directMember, asmMethod, methodOwner, state, jvmSignature)
|
||||
}
|
||||
|
||||
private fun mapMethod(callDefault: Boolean): Method =
|
||||
if (callDefault) state.typeMapper.mapDefaultMethod(functionDescriptor, sourceCompiler.contextKind)
|
||||
else mangleSuspendInlineFunctionAsmMethodIfNeeded(functionDescriptor, jvmSignature.asmMethod)
|
||||
|
||||
companion object {
|
||||
private fun StackValue.isLocalWithNoBoxing(expected: JvmKotlinType): Boolean =
|
||||
isPrimitive(expected.type) == isPrimitive(type) &&
|
||||
!StackValue.requiresInlineClassBoxingOrUnboxing(type, kotlinType, expected.type, expected.kotlinType) &&
|
||||
(this is StackValue.Local || isCapturedInlineParameter())
|
||||
|
||||
private fun StackValue.isCapturedInlineParameter(): Boolean {
|
||||
val field = if (this is StackValue.FieldForSharedVar) receiver else this
|
||||
return field is StackValue.Field && field.descriptor is ParameterDescriptor &&
|
||||
InlineUtil.isInlineParameter(field.descriptor) &&
|
||||
InlineUtil.isInline(field.descriptor.containingDeclaration)
|
||||
internal fun createSpecialInlineMethodNodeFromBinaries(functionDescriptor: FunctionDescriptor, state: GenerationState): MethodNode {
|
||||
val directMember = getDirectMemberAndCallableFromObject(functionDescriptor)
|
||||
assert(directMember is DescriptorWithContainerSource) {
|
||||
"Function is not in binaries: $functionDescriptor"
|
||||
}
|
||||
assert(directMember is FunctionDescriptor && directMember.isOperator) {
|
||||
"Operator function expected: $directMember"
|
||||
}
|
||||
|
||||
val methodOwner = state.typeMapper.mapImplementationOwner(functionDescriptor)
|
||||
val jvmSignature = state.typeMapper.mapSignatureWithGeneric(functionDescriptor, OwnerKind.IMPLEMENTATION)
|
||||
|
||||
val asmMethod = mangleSuspendInlineFunctionAsmMethodIfNeeded(functionDescriptor, jvmSignature.asmMethod)
|
||||
|
||||
return getCompiledMethodNodeInner(functionDescriptor, directMember, asmMethod, methodOwner, state, jvmSignature).node
|
||||
}
|
||||
|
||||
// Stack spilling before inline function call is required if the inlined bytecode has:
|
||||
// 1. try-catch blocks - otherwise the stack spilling before and after them will not be correct;
|
||||
// 2. suspension points - again, the stack spilling around them is otherwise wrong;
|
||||
// 3. loops - OpenJDK cannot JIT-optimize between loop iterations if the stack is not empty.
|
||||
// Instead of checking for loops precisely, we just check if there are any backward jumps -
|
||||
// that is, a jump from instruction #i to instruction #j where j < i.
|
||||
private fun MethodNode.requiresEmptyStackOnEntry(): Boolean = tryCatchBlocks.isNotEmpty() ||
|
||||
instructions.toArray().any { isBeforeSuspendMarker(it) || isBeforeInlineSuspendMarker(it) || isBackwardsJump(it) }
|
||||
private fun getCompiledMethodNodeInner(
|
||||
functionDescriptor: FunctionDescriptor,
|
||||
directMember: CallableMemberDescriptor,
|
||||
asmMethod: Method,
|
||||
methodOwner: Type,
|
||||
state: GenerationState,
|
||||
jvmSignature: JvmMethodSignature
|
||||
): SMAPAndMethodNode {
|
||||
val methodId = MethodId(methodOwner.internalName, asmMethod)
|
||||
|
||||
private fun MethodNode.isBackwardsJump(insn: AbstractInsnNode): Boolean = when (insn) {
|
||||
is JumpInsnNode -> isBackwardsJump(insn, insn.label)
|
||||
is LookupSwitchInsnNode ->
|
||||
insn.dflt?.let { to -> isBackwardsJump(insn, to) } == true || insn.labels.any { to -> isBackwardsJump(insn, to) }
|
||||
is TableSwitchInsnNode ->
|
||||
insn.dflt?.let { to -> isBackwardsJump(insn, to) } == true || insn.labels.any { to -> isBackwardsJump(insn, to) }
|
||||
else -> false
|
||||
val resultInCache = state.inlineCache.methodNodeById.getOrPut(methodId) {
|
||||
val result = doCreateMethodNodeFromCompiled(directMember, state, asmMethod)
|
||||
?: if (functionDescriptor.isSuspend)
|
||||
doCreateMethodNodeFromCompiled(directMember, state, jvmSignature.asmMethod)
|
||||
else
|
||||
null
|
||||
result ?:
|
||||
throw IllegalStateException("Couldn't obtain compiled function body for $functionDescriptor")
|
||||
}
|
||||
|
||||
return SMAPAndMethodNode(cloneMethodNode(resultInCache.node), resultInCache.classSMAP)
|
||||
}
|
||||
|
||||
private fun MethodNode.isBackwardsJump(from: AbstractInsnNode, to: LabelNode): Boolean =
|
||||
instructions.indexOf(to) < instructions.indexOf(from)
|
||||
private fun createDefaultFakeSMAP() = SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1)
|
||||
|
||||
// For suspend inline functions we generate two methods:
|
||||
// 1) normal one: with state machine to call directly
|
||||
// 2) for inliner: with mangled name and without state machine
|
||||
private fun mangleSuspendInlineFunctionAsmMethodIfNeeded(functionDescriptor: FunctionDescriptor, asmMethod: Method): Method {
|
||||
if (!functionDescriptor.isSuspend) return asmMethod
|
||||
return Method("${asmMethod.name}$FOR_INLINE_SUFFIX", asmMethod.descriptor)
|
||||
}
|
||||
|
||||
private fun getDirectMemberAndCallableFromObject(functionDescriptor: FunctionDescriptor): CallableMemberDescriptor {
|
||||
val directMember = JvmCodegenUtil.getDirectMember(functionDescriptor)
|
||||
return (directMember as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: directMember
|
||||
}
|
||||
|
||||
private fun doCreateMethodNodeFromCompiled(
|
||||
callableDescriptor: CallableMemberDescriptor,
|
||||
state: GenerationState,
|
||||
asmMethod: Method
|
||||
): SMAPAndMethodNode? {
|
||||
if (isBuiltInArrayIntrinsic(callableDescriptor)) {
|
||||
val body = when {
|
||||
callableDescriptor is FictitiousArrayConstructor -> IntrinsicArrayConstructors.generateArrayConstructorBody(asmMethod)
|
||||
callableDescriptor.name.asString() == "emptyArray" -> IntrinsicArrayConstructors.generateEmptyArrayBody(asmMethod)
|
||||
callableDescriptor.name.asString() == "arrayOf" -> IntrinsicArrayConstructors.generateArrayOfBody(asmMethod)
|
||||
else -> throw UnsupportedOperationException("Not an array intrinsic: $callableDescriptor")
|
||||
}
|
||||
return SMAPAndMethodNode(body, SMAP(listOf()))
|
||||
}
|
||||
|
||||
assert(callableDescriptor is DescriptorWithContainerSource) { "Not a deserialized function or proper: $callableDescriptor" }
|
||||
|
||||
val containingClasses =
|
||||
KotlinTypeMapper.getContainingClassesForDeserializedCallable(callableDescriptor as DescriptorWithContainerSource)
|
||||
|
||||
val containerId = containingClasses.implClassId
|
||||
|
||||
val bytes = state.inlineCache.classBytes.getOrPut(containerId) {
|
||||
findVirtualFile(state, containerId)?.contentsToByteArray()
|
||||
?: throw IllegalStateException("Couldn't find declaration file for $containerId")
|
||||
}
|
||||
|
||||
val classType = AsmUtil.asmTypeByClassId(containerId)
|
||||
val methodNode = getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, classType)
|
||||
if (methodNode == null && requiresFunctionNameManglingForReturnType(callableDescriptor)) {
|
||||
val nameWithoutManglingSuffix = asmMethod.name.stripManglingSuffixOrNull()
|
||||
if (nameWithoutManglingSuffix != null) {
|
||||
val methodWithoutMangling = getMethodNode(bytes, nameWithoutManglingSuffix, asmMethod.descriptor, classType)
|
||||
if (methodWithoutMangling != null) return methodWithoutMangling
|
||||
}
|
||||
return getMethodNode(bytes, "$nameWithoutManglingSuffix-impl", asmMethod.descriptor, classType)
|
||||
}
|
||||
|
||||
return methodNode
|
||||
}
|
||||
|
||||
private fun String.stripManglingSuffixOrNull(): String? {
|
||||
val dashIndex = indexOf('-')
|
||||
return if (dashIndex < 0) null else substring(0, dashIndex)
|
||||
}
|
||||
|
||||
private fun isBuiltInArrayIntrinsic(callableDescriptor: CallableMemberDescriptor): Boolean {
|
||||
if (callableDescriptor is FictitiousArrayConstructor) return true
|
||||
val name = callableDescriptor.name.asString()
|
||||
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration.let { container ->
|
||||
container is PackageFragmentDescriptor && container.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME
|
||||
}
|
||||
}
|
||||
|
||||
/*descriptor is null for captured vars*/
|
||||
private fun shouldPutGeneralValue(type: Type, kotlinType: KotlinType?, stackValue: StackValue): Boolean {
|
||||
//remap only inline functions (and maybe non primitives)
|
||||
//TODO - clean assertion and remapping logic
|
||||
|
||||
// don't remap boxing/unboxing primitives
|
||||
if (isPrimitive(type) != isPrimitive(stackValue.type)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// don't remap boxing/unboxing inline classes
|
||||
if (StackValue.requiresInlineClassBoxingOrUnboxing(stackValue.type, stackValue.kotlinType, type, kotlinType)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (stackValue is StackValue.Local) {
|
||||
return false
|
||||
}
|
||||
|
||||
var field = stackValue
|
||||
if (stackValue is StackValue.FieldForSharedVar) {
|
||||
field = stackValue.receiver
|
||||
}
|
||||
|
||||
//check that value corresponds to captured inlining parameter
|
||||
if (field is StackValue.Field) {
|
||||
val varDescriptor = field.descriptor
|
||||
//check that variable is inline function parameter
|
||||
return !(varDescriptor is ParameterDescriptor &&
|
||||
InlineUtil.isInlineParameter(varDescriptor) &&
|
||||
InlineUtil.isInline(varDescriptor.containingDeclaration))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
fun getDeclarationLabels(lambdaOrFun: PsiElement?, descriptor: DeclarationDescriptor): Set<String> {
|
||||
val result = HashSet<String>()
|
||||
|
||||
if (lambdaOrFun != null) {
|
||||
val label = LabelResolver.getLabelNameIfAny(lambdaOrFun)
|
||||
if (label != null) {
|
||||
result.add(label.asString())
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFunctionLiteral(descriptor)) {
|
||||
if (!descriptor.name.isSpecial) {
|
||||
result.add(descriptor.name.asString())
|
||||
}
|
||||
result.add(FIRST_FUN_LABEL)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val BaseExpressionCodegen.v: InstructionAdapter
|
||||
get() = visitor
|
||||
|
||||
@@ -15,13 +15,15 @@ import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class InlineCodegenForDefaultBody(
|
||||
private val function: FunctionDescriptor,
|
||||
codegen: ExpressionCodegen,
|
||||
val state: GenerationState,
|
||||
private val methodOwner: Type,
|
||||
private val jvmSignature: JvmMethodSignature,
|
||||
private val sourceCompilerForInline: PsiSourceCompilerForInline
|
||||
private val sourceCompilerForInline: SourceCompilerForInline
|
||||
) : CallGenerator {
|
||||
private val sourceMapper: SourceMapper = codegen.parentCodegen.orCreateSourceMapper
|
||||
|
||||
@@ -37,22 +39,32 @@ class InlineCodegenForDefaultBody(
|
||||
}
|
||||
|
||||
override fun genCallInner(callableMethod: Callable, resolvedCall: ResolvedCall<*>?, callDefault: Boolean, codegen: ExpressionCodegen) {
|
||||
assert(!callDefault) { "inlining default stub into another default stub" }
|
||||
val (node, smap) = sourceCompilerForInline.compileInlineFunction(jvmSignature)
|
||||
val childSourceMapper = SourceMapCopier(sourceMapper, smap)
|
||||
val nodeAndSmap = PsiInlineCodegen(
|
||||
codegen, state, function, methodOwner, jvmSignature, TypeParameterMappings(), sourceCompilerForInline
|
||||
).createInlineMethodNode(
|
||||
callDefault, null, codegen.typeSystem
|
||||
)
|
||||
val childSourceMapper = SourceMapCopier(sourceMapper, nodeAndSmap.classSMAP)
|
||||
|
||||
val node = nodeAndSmap.node
|
||||
val transformedMethod = MethodNode(
|
||||
node.access,
|
||||
node.name,
|
||||
node.desc,
|
||||
node.signature,
|
||||
node.exceptions.toTypedArray()
|
||||
)
|
||||
|
||||
val argsSize =
|
||||
(Type.getArgumentsAndReturnSizes(jvmSignature.asmMethod.descriptor) ushr 2) - if (callableMethod.isStaticCall()) 1 else 0
|
||||
// `$default` is only for Kotlin use so it has no `$$forInline` version - this *is* what the inliner will use.
|
||||
node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
node.accept(object : MethodBodyVisitor(codegen.visitor) {
|
||||
// The LVT was not generated at all, so move the start of parameters to the start of the method.
|
||||
override fun visitLocalVariable(name: String, desc: String, signature: String?, start: Label, end: Label, index: Int) =
|
||||
super.visitLocalVariable(name, desc, signature, if (index < argsSize) methodStartLabel else start, end, index)
|
||||
|
||||
override fun visitLineNumber(line: Int, start: Label) =
|
||||
super.visitLineNumber(childSourceMapper.mapLineNumber(line), start)
|
||||
node.accept(object : InlineAdapter(transformedMethod, 0, childSourceMapper) {
|
||||
override fun visitLocalVariable(name: String, desc: String, signature: String?, start: Label, end: Label, index: Int) {
|
||||
val startLabel = if (index < argsSize) methodStartLabel else start
|
||||
super.visitLocalVariable(name, desc, signature, startLabel, end, index)
|
||||
}
|
||||
})
|
||||
|
||||
transformedMethod.accept(MethodBodyVisitor(codegen.visitor))
|
||||
}
|
||||
|
||||
override fun genValueAndPut(
|
||||
@@ -73,7 +85,7 @@ class InlineCodegenForDefaultBody(
|
||||
throw UnsupportedOperationException("Shouldn't be called")
|
||||
}
|
||||
|
||||
override fun processHiddenParameters() {
|
||||
override fun processAndPutHiddenParameters(justProcess: Boolean) {
|
||||
throw UnsupportedOperationException("Shouldn't be called")
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,40 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil
|
||||
import org.jetbrains.kotlin.codegen.PropertyReferenceCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.*
|
||||
import org.jetbrains.kotlin.codegen.binding.MutableClosure
|
||||
import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor
|
||||
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isCapturedSuspendLambda
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.coroutines.isSuspendLambda
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
interface FunctionalArgument
|
||||
|
||||
abstract class LambdaInfo : FunctionalArgument {
|
||||
abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgument {
|
||||
|
||||
abstract val isBoundCallableReference: Boolean
|
||||
|
||||
abstract val isSuspend: Boolean
|
||||
@@ -25,9 +47,7 @@ abstract class LambdaInfo : FunctionalArgument {
|
||||
|
||||
abstract val invokeMethod: Method
|
||||
|
||||
abstract val invokeMethodParameters: List<KotlinType?>
|
||||
|
||||
abstract val invokeMethodReturnType: KotlinType?
|
||||
abstract val invokeMethodDescriptor: FunctionDescriptor
|
||||
|
||||
abstract val capturedVars: List<CapturedParamDesc>
|
||||
|
||||
@@ -36,7 +56,7 @@ abstract class LambdaInfo : FunctionalArgument {
|
||||
|
||||
lateinit var node: SMAPAndMethodNode
|
||||
|
||||
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
|
||||
abstract fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>)
|
||||
|
||||
open val hasDispatchReceiver = true
|
||||
|
||||
@@ -47,7 +67,7 @@ abstract class LambdaInfo : FunctionalArgument {
|
||||
val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, ""))
|
||||
?: error("Captured field not found: " + info.containingLambdaName + "." + info.fieldName)
|
||||
val recapturedParamInfo = builder.addCapturedParam(field, info.fieldName)
|
||||
if (info.isSuspend) {
|
||||
if (this is ExpressionLambda && isCapturedSuspend(info)) {
|
||||
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||
}
|
||||
}
|
||||
@@ -55,9 +75,14 @@ abstract class LambdaInfo : FunctionalArgument {
|
||||
return builder.buildParameters()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun LambdaInfo.capturedParamDesc(fieldName: String, fieldType: Type, isSuspend: Boolean): CapturedParamDesc {
|
||||
return CapturedParamDesc(lambdaClassType, fieldName, fieldType, isSuspend)
|
||||
fun LambdaInfo.getCapturedParamInfo(descriptor: EnclosedValueDescriptor): CapturedParamDesc {
|
||||
return capturedParamDesc(descriptor.fieldName, descriptor.type)
|
||||
}
|
||||
|
||||
fun LambdaInfo.capturedParamDesc(fieldName: String, fieldType: Type): CapturedParamDesc {
|
||||
return CapturedParamDesc(lambdaClassType, fieldName, fieldType)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,76 +90,115 @@ abstract class LambdaInfo : FunctionalArgument {
|
||||
object NonInlineableArgumentForInlineableParameterCalledInSuspend : FunctionalArgument
|
||||
object NonInlineableArgumentForInlineableSuspendParameter : FunctionalArgument
|
||||
|
||||
abstract class ExpressionLambda : LambdaInfo() {
|
||||
fun generateLambdaBody(sourceCompiler: SourceCompilerForInline) {
|
||||
node = sourceCompiler.generateLambdaBody(this, reifiedTypeParametersUsages)
|
||||
node.node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
|
||||
class PsiDefaultLambda(
|
||||
lambdaClassType: Type,
|
||||
capturedArgs: Array<Type>,
|
||||
parameterDescriptor: ValueParameterDescriptor,
|
||||
offset: Int,
|
||||
needReification: Boolean
|
||||
) : DefaultLambda(lambdaClassType, capturedArgs, parameterDescriptor, offset, needReification) {
|
||||
override fun mapAsmSignature(sourceCompiler: SourceCompilerForInline): Method {
|
||||
return sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod
|
||||
}
|
||||
|
||||
override fun findInvokeMethodDescriptor(): FunctionDescriptor =
|
||||
parameterDescriptor.type.memberScope
|
||||
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
|
||||
.single()
|
||||
}
|
||||
|
||||
abstract class DefaultLambda(
|
||||
final override val lambdaClassType: Type,
|
||||
capturedArgs: Array<Type>,
|
||||
override val lambdaClassType: Type,
|
||||
private val capturedArgs: Array<Type>,
|
||||
val parameterDescriptor: ValueParameterDescriptor,
|
||||
val offset: Int,
|
||||
val needReification: Boolean,
|
||||
sourceCompiler: SourceCompilerForInline
|
||||
) : LambdaInfo() {
|
||||
final override val isSuspend
|
||||
get() = false // TODO: it should probably be true sometimes, but it never was
|
||||
final override val isBoundCallableReference: Boolean
|
||||
final override val capturedVars: List<CapturedParamDesc>
|
||||
val needReification: Boolean
|
||||
) : LambdaInfo(parameterDescriptor.isCrossinline) {
|
||||
|
||||
final override val invokeMethod: Method
|
||||
get() = Method(node.node.name, node.node.desc)
|
||||
final override var isBoundCallableReference by Delegates.notNull<Boolean>()
|
||||
private set
|
||||
|
||||
val originalBoundReceiverType: Type?
|
||||
val parameterOffsetsInDefault: MutableList<Int> = arrayListOf()
|
||||
|
||||
protected val isPropertyReference: Boolean
|
||||
protected val isFunctionReference: Boolean
|
||||
final override lateinit var invokeMethod: Method
|
||||
private set
|
||||
|
||||
init {
|
||||
val classBytes = loadClass(sourceCompiler)
|
||||
val superName = ClassReader(classBytes).superName
|
||||
isPropertyReference = superName in PROPERTY_REFERENCE_SUPER_CLASSES
|
||||
isFunctionReference = superName == FUNCTION_REFERENCE.internalName || superName == FUNCTION_REFERENCE_IMPL.internalName
|
||||
override lateinit var invokeMethodDescriptor: FunctionDescriptor
|
||||
|
||||
val constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs)
|
||||
val constructor = getMethodNode(classBytes, "<init>", constructorDescriptor, lambdaClassType)?.node
|
||||
assert(constructor != null || capturedArgs.isEmpty()) {
|
||||
"Can't find non-default constructor <init>$constructorDescriptor for default lambda $lambdaClassType"
|
||||
final override lateinit var capturedVars: List<CapturedParamDesc>
|
||||
private set
|
||||
|
||||
var originalBoundReceiverType: Type? = null
|
||||
private set
|
||||
|
||||
override val isSuspend = parameterDescriptor.isSuspendLambda
|
||||
|
||||
override fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>) {
|
||||
val classBytes = loadClassBytesByInternalName(sourceCompiler.state, lambdaClassType.internalName)
|
||||
val classReader = ClassReader(classBytes)
|
||||
var isPropertyReference = false
|
||||
var isFunctionReference = false
|
||||
classReader.accept(object : ClassVisitor(Opcodes.API_VERSION) {
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
isPropertyReference = superName in PROPERTY_REFERENCE_SUPER_CLASSES
|
||||
isFunctionReference = superName == FUNCTION_REFERENCE.internalName || superName == FUNCTION_REFERENCE_IMPL.internalName
|
||||
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
}, ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES or ClassReader.SKIP_DEBUG)
|
||||
|
||||
invokeMethodDescriptor = findInvokeMethodDescriptor().let {
|
||||
//property reference generates erased 'get' method
|
||||
if (isPropertyReference) it.original else it
|
||||
}
|
||||
// This only works for primitives but not inline classes, since information about the Kotlin type of the bound
|
||||
// receiver is not present anywhere. This is why with JVM_IR the constructor argument of bound references
|
||||
// is already `Object`, and this field is never used.
|
||||
originalBoundReceiverType =
|
||||
capturedArgs.singleOrNull()?.takeIf { (isFunctionReference || isPropertyReference) && AsmUtil.isPrimitive(it) }
|
||||
|
||||
val descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs)
|
||||
val constructor = getMethodNode(classBytes, "<init>", descriptor, lambdaClassType)?.node
|
||||
|
||||
assert(constructor != null || capturedArgs.isEmpty()) {
|
||||
"Can't find non-default constructor <init>$descriptor for default lambda $lambdaClassType"
|
||||
}
|
||||
|
||||
capturedVars =
|
||||
if (isFunctionReference || isPropertyReference)
|
||||
capturedArgs.singleOrNull()?.let {
|
||||
listOf(capturedParamDesc(AsmUtil.RECEIVER_PARAMETER_NAME, AsmUtil.boxType(it), isSuspend = false))
|
||||
constructor?.desc?.let { Type.getArgumentTypes(it) }?.singleOrNull()?.let {
|
||||
originalBoundReceiverType = it
|
||||
listOf(capturedParamDesc(AsmUtil.RECEIVER_PARAMETER_NAME, it.boxReceiverForBoundReference()))
|
||||
} ?: emptyList()
|
||||
else
|
||||
constructor?.findCapturedFieldAssignmentInstructions()?.map { fieldNode ->
|
||||
capturedParamDesc(fieldNode.name, Type.getType(fieldNode.desc), isSuspend = false)
|
||||
capturedParamDesc(fieldNode.name, Type.getType(fieldNode.desc))
|
||||
}?.toList() ?: emptyList()
|
||||
|
||||
isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun loadClass(sourceCompiler: SourceCompilerForInline): ByteArray =
|
||||
sourceCompiler.state.inlineCache.classBytes.getOrPut(lambdaClassType.internalName) {
|
||||
loadClassBytesByInternalName(sourceCompiler.state, lambdaClassType.internalName)
|
||||
val methodName = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString()
|
||||
|
||||
val signature = mapAsmSignature(sourceCompiler)
|
||||
|
||||
node = getMethodNode(classBytes, methodName, signature.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||
?: error("Can't find method '$methodName$signature' in '${classReader.className}'")
|
||||
|
||||
invokeMethod = Method(node.node.name, node.node.desc)
|
||||
|
||||
if (needReification) {
|
||||
//nested classes could also require reification
|
||||
reifiedTypeInliner.reifyInstructions(node.node)
|
||||
}
|
||||
|
||||
// Returns whether the loaded invoke is erased, i.e. the name equals the fallback and all types are `Object`.
|
||||
protected fun loadInvoke(sourceCompiler: SourceCompilerForInline, erasedName: String, actualMethod: Method): Boolean {
|
||||
val classBytes = loadClass(sourceCompiler)
|
||||
// TODO: `signatureAmbiguity = true` ignores the argument types from `invokeMethod` and only looks at the count.
|
||||
node = getMethodNode(classBytes, actualMethod.name, actualMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||
?: getMethodNode(classBytes, erasedName, actualMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||
?: error("Can't find method '$actualMethod' in '${lambdaClassType.internalName}'")
|
||||
return invokeMethod.run { name == erasedName && returnType == OBJECT_TYPE && argumentTypes.all { it == OBJECT_TYPE } }
|
||||
}
|
||||
|
||||
protected abstract fun mapAsmSignature(sourceCompiler: SourceCompilerForInline): Method
|
||||
|
||||
protected abstract fun findInvokeMethodDescriptor(): FunctionDescriptor
|
||||
|
||||
private companion object {
|
||||
val PROPERTY_REFERENCE_SUPER_CLASSES =
|
||||
listOf(
|
||||
@@ -144,3 +208,131 @@ abstract class DefaultLambda(
|
||||
.mapTo(HashSet(), Type::getInternalName)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Type.boxReceiverForBoundReference() =
|
||||
AsmUtil.boxType(this)
|
||||
|
||||
internal fun Type.boxReceiverForBoundReference(kotlinType: KotlinType, typeMapper: KotlinTypeMapper) =
|
||||
DescriptorAsmUtil.boxType(this, kotlinType, typeMapper)
|
||||
|
||||
abstract class ExpressionLambda(isCrossInline: Boolean) : LambdaInfo(isCrossInline) {
|
||||
override fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>) {
|
||||
node = sourceCompiler.generateLambdaBody(this)
|
||||
node.node.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||
}
|
||||
|
||||
abstract fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor
|
||||
abstract fun isCapturedSuspend(desc: CapturedParamDesc): Boolean
|
||||
}
|
||||
|
||||
class PsiExpressionLambda(
|
||||
expression: KtExpression,
|
||||
private val typeMapper: KotlinTypeMapper,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
isCrossInline: Boolean,
|
||||
override val isBoundCallableReference: Boolean
|
||||
) : ExpressionLambda(isCrossInline) {
|
||||
|
||||
override val lambdaClassType: Type
|
||||
|
||||
override val invokeMethod: Method
|
||||
|
||||
override val invokeMethodDescriptor: FunctionDescriptor
|
||||
|
||||
val classDescriptor: ClassDescriptor
|
||||
|
||||
val propertyReferenceInfo: PropertyReferenceInfo?
|
||||
|
||||
val functionWithBodyOrCallableReference: KtExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression
|
||||
|
||||
override val returnLabels: Map<String, Label?>
|
||||
|
||||
override val isSuspend: Boolean
|
||||
|
||||
var closure: CalculatedClosure
|
||||
private set
|
||||
|
||||
init {
|
||||
val bindingContext = typeMapper.bindingContext
|
||||
val function =
|
||||
bindingContext.get<PsiElement, SimpleFunctionDescriptor>(BindingContext.FUNCTION, functionWithBodyOrCallableReference)
|
||||
if (function == null && expression is KtCallableReferenceExpression) {
|
||||
val variableDescriptor =
|
||||
bindingContext.get(BindingContext.VARIABLE, functionWithBodyOrCallableReference) as? VariableDescriptorWithAccessors
|
||||
?: throw AssertionError("Reference expression not resolved to variable descriptor with accessors: ${expression.getText()}")
|
||||
classDescriptor = bindingContext.get(CLASS_FOR_CALLABLE, variableDescriptor)
|
||||
?: throw IllegalStateException("Class for callable not found: $variableDescriptor\n${expression.text}")
|
||||
lambdaClassType = typeMapper.mapClass(classDescriptor)
|
||||
val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor)
|
||||
invokeMethodDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor)
|
||||
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext)
|
||||
propertyReferenceInfo = PropertyReferenceInfo(
|
||||
resolvedCall.resultingDescriptor as VariableDescriptor, getFunction
|
||||
)
|
||||
} else {
|
||||
propertyReferenceInfo = null
|
||||
invokeMethodDescriptor = function ?: throw AssertionError("Function is not resolved to descriptor: " + expression.text)
|
||||
classDescriptor = bindingContext.get(CLASS_FOR_CALLABLE, invokeMethodDescriptor)
|
||||
?: throw IllegalStateException("Class for invoke method not found: $invokeMethodDescriptor\n${expression.text}")
|
||||
lambdaClassType = asmTypeForAnonymousClass(bindingContext, invokeMethodDescriptor)
|
||||
}
|
||||
|
||||
bindingContext.get<ClassDescriptor, MutableClosure>(CLOSURE, classDescriptor).let {
|
||||
assert(it != null) { "Closure for lambda should be not null " + expression.text }
|
||||
closure = it!!
|
||||
}
|
||||
|
||||
returnLabels = InlineCodegen.getDeclarationLabels(expression, invokeMethodDescriptor).associateWith { null }
|
||||
invokeMethod = typeMapper.mapAsmMethod(invokeMethodDescriptor)
|
||||
isSuspend = invokeMethodDescriptor.isSuspend
|
||||
}
|
||||
|
||||
override val capturedVars: List<CapturedParamDesc> by lazy {
|
||||
arrayListOf<CapturedParamDesc>().apply {
|
||||
val captureThis = closure.capturedOuterClassDescriptor
|
||||
if (captureThis != null) {
|
||||
val kotlinType = captureThis.defaultType
|
||||
val type = typeMapper.mapType(kotlinType)
|
||||
val descriptor = EnclosedValueDescriptor(
|
||||
AsmUtil.CAPTURED_THIS_FIELD, null,
|
||||
StackValue.field(type, lambdaClassType, AsmUtil.CAPTURED_THIS_FIELD, false, StackValue.LOCAL_0),
|
||||
type, kotlinType
|
||||
)
|
||||
add(getCapturedParamInfo(descriptor))
|
||||
}
|
||||
|
||||
val capturedReceiver = closure.capturedReceiverFromOuterContext
|
||||
if (capturedReceiver != null) {
|
||||
val type = typeMapper.mapType(capturedReceiver).let {
|
||||
if (isBoundCallableReference) it.boxReceiverForBoundReference() else it
|
||||
}
|
||||
|
||||
val fieldName = closure.getCapturedReceiverFieldName(typeMapper.bindingContext, languageVersionSettings)
|
||||
val descriptor = EnclosedValueDescriptor(
|
||||
fieldName, null,
|
||||
StackValue.field(type, capturedReceiver, lambdaClassType, fieldName, false, StackValue.LOCAL_0),
|
||||
type, capturedReceiver
|
||||
)
|
||||
add(getCapturedParamInfo(descriptor))
|
||||
}
|
||||
|
||||
closure.captureVariables.values.forEach { descriptor ->
|
||||
add(getCapturedParamInfo(descriptor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val isPropertyReference: Boolean
|
||||
get() = propertyReferenceInfo != null
|
||||
|
||||
override fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor {
|
||||
return getOrCreateJvmSuspendFunctionView(
|
||||
invokeMethodDescriptor,
|
||||
languageVersionSettings.isReleaseCoroutines(),
|
||||
typeMapper.bindingContext
|
||||
)
|
||||
}
|
||||
|
||||
override fun isCapturedSuspend(desc: CapturedParamDesc): Boolean =
|
||||
isCapturedSuspendLambda(closure, desc.fieldName, typeMapper.bindingContext)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.codegen.*
|
||||
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.isSuspendLambdaCapturedByOuterObjectOrLambda
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.markNoinlineLambdaIfSuspend
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.surroundInvokesWithSuspendMarkersIfNeeded
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
@@ -22,9 +23,13 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
@@ -228,34 +233,52 @@ class MethodInliner(
|
||||
return
|
||||
}
|
||||
|
||||
val nullableAnyType = inliningContext.state.module.builtIns.nullableAnyType
|
||||
val expectedParameters = info.invokeMethod.argumentTypes
|
||||
val expectedKotlinParameters = info.invokeMethodParameters
|
||||
val argumentCount = Type.getArgumentTypes(desc).size.let {
|
||||
if (!inliningContext.root.state.isIrBackend && info.isSuspend && it < expectedParameters.size) {
|
||||
// Inlining suspend lambda into a function that takes a non-suspend lambda.
|
||||
// In the IR backend, this cannot happen as inline lambdas are not lowered.
|
||||
// in case of inlining suspend lambda reference as ordinary parameter of inline function:
|
||||
// suspend fun foo (...) ...
|
||||
// inline fun inlineMe(c: (...) -> ...) ...
|
||||
// builder {
|
||||
// inlineMe(::foo)
|
||||
// }
|
||||
// we should create additional parameter for continuation.
|
||||
var coroutineDesc = desc
|
||||
val actualInvokeDescriptor: FunctionDescriptor
|
||||
if (info.isSuspend) {
|
||||
actualInvokeDescriptor = (info as ExpressionLambda).getInlineSuspendLambdaViewDescriptor()
|
||||
val parametersSize = actualInvokeDescriptor.valueParameters.size +
|
||||
(if (actualInvokeDescriptor.extensionReceiverParameter != null) 1 else 0)
|
||||
// And here we expect invoke(...Ljava/lang/Object;) be replaced with invoke(...Lkotlin/coroutines/Continuation;)
|
||||
// if this does not happen, insert fake continuation, since we could not have one yet.
|
||||
val argumentTypes = Type.getArgumentTypes(desc)
|
||||
if (argumentTypes.size != parametersSize &&
|
||||
// But do not add it in IR. In IR we already have lowered lambdas with additional parameter, while in Old BE we don't.
|
||||
!inliningContext.root.state.isIrBackend
|
||||
) {
|
||||
addFakeContinuationMarker(this)
|
||||
it + 1
|
||||
} else it
|
||||
}
|
||||
assert(argumentCount == expectedParameters.size && argumentCount == expectedKotlinParameters.size) {
|
||||
"inconsistent lambda arguments: $argumentCount on stack, ${expectedParameters.size} expected, " +
|
||||
"${expectedKotlinParameters.size} Kotlin types"
|
||||
coroutineDesc = Type.getMethodDescriptor(Type.getReturnType(desc), *argumentTypes, AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
} else {
|
||||
actualInvokeDescriptor = info.invokeMethodDescriptor
|
||||
}
|
||||
|
||||
var valueParamShift = max(nextLocalIndex, markerShift) + expectedParameters.sumOf { it.size }
|
||||
for (index in argumentCount - 1 downTo 0) {
|
||||
val type = expectedParameters[index]
|
||||
StackValue.coerce(AsmTypes.OBJECT_TYPE, nullableAnyType, type, expectedKotlinParameters[index], this)
|
||||
valueParamShift -= type.size
|
||||
store(valueParamShift, type)
|
||||
}
|
||||
if (expectedParameters.isEmpty()) {
|
||||
nop() // add something for a line number to bind onto
|
||||
val valueParameters =
|
||||
listOfNotNull(actualInvokeDescriptor.extensionReceiverParameter) + actualInvokeDescriptor.valueParameters
|
||||
|
||||
val erasedInvokeFunction = ClosureCodegen.getErasedInvokeFunction(actualInvokeDescriptor)
|
||||
val invokeParameters = erasedInvokeFunction.valueParameters
|
||||
|
||||
val valueParamShift = max(nextLocalIndex, markerShift)//NB: don't inline cause it changes
|
||||
val parameterTypesFromDesc = info.invokeMethod.argumentTypes
|
||||
putStackValuesIntoLocalsForLambdaOnInvoke(
|
||||
listOf(*parameterTypesFromDesc), valueParameters, invokeParameters, valueParamShift, this, coroutineDesc
|
||||
)
|
||||
|
||||
if (parameterTypesFromDesc.isEmpty()) {
|
||||
// There won't be no parameters processing and line call can be left without actual instructions.
|
||||
// Note: if function is called on the line with other instructions like 1 + foo(), 'nop' will still be generated.
|
||||
visitInsn(Opcodes.NOP)
|
||||
}
|
||||
|
||||
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info.node.node, sourceMapper.parent)
|
||||
inlineOnlySmapSkipper?.onInlineLambdaStart(remappingMethodAdapter, info, sourceMapper.parent)
|
||||
addInlineMarker(this, true)
|
||||
val lambdaParameters = info.addAllParameters(nodeRemapper)
|
||||
|
||||
@@ -280,9 +303,10 @@ class MethodInliner(
|
||||
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
|
||||
result.mergeWithNotChangeInfo(lambdaResult)
|
||||
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
|
||||
result.reifiedTypeParametersUsages.mergeAll(info.reifiedTypeParametersUsages)
|
||||
|
||||
StackValue.coerce(info.invokeMethod.returnType, info.invokeMethodReturnType, OBJECT_TYPE, nullableAnyType, this)
|
||||
StackValue
|
||||
.onStack(info.invokeMethod.returnType, info.invokeMethodDescriptor.returnType)
|
||||
.put(OBJECT_TYPE, erasedInvokeFunction.returnType, this)
|
||||
setLambdaInlining(false)
|
||||
addInlineMarker(this, false)
|
||||
inlineOnlySmapSkipper?.onInlineLambdaEnd(remappingMethodAdapter)
|
||||
@@ -423,10 +447,10 @@ class MethodInliner(
|
||||
if (DEFAULT_LAMBDA_FAKE_CALL == owner) {
|
||||
val index = name.substringAfter(DEFAULT_LAMBDA_FAKE_CALL).toInt()
|
||||
val lambda = getFunctionalArgumentIfExists(index) as DefaultLambda
|
||||
for (captured in lambda.capturedVars.asReversed()) {
|
||||
lambda.originalBoundReceiverType?.let {
|
||||
// The receiver is the only captured value; it needs to be boxed.
|
||||
StackValue.onStack(it).put(captured.type, InstructionAdapter(this))
|
||||
lambda.parameterOffsetsInDefault.zip(lambda.capturedVars).asReversed().forEach { (_, captured) ->
|
||||
val originalBoundReceiverType = lambda.originalBoundReceiverType
|
||||
if (lambda.isBoundCallableReference && AsmUtil.isPrimitive(originalBoundReceiverType)) {
|
||||
StackValue.onStack(originalBoundReceiverType!!).put(captured.type, InstructionAdapter(this))
|
||||
}
|
||||
super.visitFieldInsn(
|
||||
Opcodes.PUTSTATIC,
|
||||
@@ -558,17 +582,21 @@ class MethodInliner(
|
||||
)
|
||||
} else if (fieldInsnNode.isCheckAssertionsStatus()) {
|
||||
fieldInsnNode.owner = inlineCallSiteInfo.ownerClassName
|
||||
when {
|
||||
// In inline function itself:
|
||||
inliningContext.parent == null -> inliningContext
|
||||
// In method of regenerated object - field should already exist:
|
||||
inliningContext.parent is RegeneratedClassContext -> inliningContext.parent
|
||||
// In lambda inlined into the root function:
|
||||
inliningContext.parent.parent == null -> inliningContext.parent
|
||||
// In lambda inlined into a method of a regenerated object:
|
||||
else -> inliningContext.parent.parent as? RegeneratedClassContext
|
||||
?: throw AssertionError("couldn't find class for \$assertionsDisabled (context = $inliningContext)")
|
||||
}.generateAssertField = true
|
||||
if (inliningContext.isInliningLambda) {
|
||||
if (inliningContext.lambdaInfo!!.isCrossInline) {
|
||||
assert(inliningContext.parent?.parent is RegeneratedClassContext) {
|
||||
"$inliningContext grandparent shall be RegeneratedClassContext but got ${inliningContext.parent?.parent}"
|
||||
}
|
||||
inliningContext.parent!!.parent!!.generateAssertField = true
|
||||
} else {
|
||||
assert(inliningContext.parent != null) {
|
||||
"$inliningContext parent shall not be null"
|
||||
}
|
||||
inliningContext.parent!!.generateAssertField = true
|
||||
}
|
||||
} else {
|
||||
inliningContext.generateAssertField = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,7 +847,7 @@ class MethodInliner(
|
||||
getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`)
|
||||
insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) ->
|
||||
findCapturedField(insnNode, nodeRemapper).functionalArgument
|
||||
insnNode is FieldInsnNode && inliningContext.root.sourceCompilerForInline.isSuspendLambdaCapturedByOuterObjectOrLambda(insnNode.name) ->
|
||||
insnNode is FieldInsnNode && insnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext) ->
|
||||
NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||
else ->
|
||||
null
|
||||
@@ -1045,6 +1073,46 @@ class MethodInliner(
|
||||
}
|
||||
}
|
||||
|
||||
private fun putStackValuesIntoLocalsForLambdaOnInvoke(
|
||||
directOrder: List<Type>,
|
||||
directOrderOfArguments: List<ParameterDescriptor>,
|
||||
directOrderOfInvokeParameters: List<ValueParameterDescriptor>,
|
||||
shift: Int,
|
||||
iv: InstructionAdapter,
|
||||
descriptor: String
|
||||
) {
|
||||
val actualParams = Type.getArgumentTypes(descriptor)
|
||||
assert(actualParams.size == directOrder.size) {
|
||||
"Number of expected and actual parameters should be equal, but ${actualParams.size} != ${directOrder.size}!"
|
||||
}
|
||||
|
||||
var currentShift = shift + directOrder.sumOf { it.size }
|
||||
|
||||
val safeToUseArgumentKotlinType =
|
||||
directOrder.size == directOrderOfArguments.size && directOrderOfArguments.size == directOrderOfInvokeParameters.size
|
||||
|
||||
for (index in directOrder.lastIndex downTo 0) {
|
||||
val type = directOrder[index]
|
||||
currentShift -= type.size
|
||||
val typeOnStack = actualParams[index]
|
||||
|
||||
val argumentKotlinType: KotlinType?
|
||||
val invokeParameterKotlinType: KotlinType?
|
||||
if (safeToUseArgumentKotlinType) {
|
||||
argumentKotlinType = directOrderOfArguments[index].type
|
||||
invokeParameterKotlinType = directOrderOfInvokeParameters[index].type
|
||||
} else {
|
||||
argumentKotlinType = null
|
||||
invokeParameterKotlinType = null
|
||||
}
|
||||
|
||||
if (typeOnStack != type || invokeParameterKotlinType != argumentKotlinType) {
|
||||
StackValue.onStack(typeOnStack, invokeParameterKotlinType).put(type, argumentKotlinType, iv)
|
||||
}
|
||||
iv.store(currentShift, type)
|
||||
}
|
||||
}
|
||||
|
||||
//process local and global returns (local substituted with goto end-label global kept unchanged)
|
||||
@JvmStatic
|
||||
fun processReturns(
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
@@ -137,7 +136,7 @@ internal class Aload0Interpreter(private val node: MethodNode) : BasicInterprete
|
||||
internal fun AbstractInsnNode.isAload0() = opcode == Opcodes.ALOAD && (this as VarInsnNode).`var` == 0
|
||||
|
||||
internal fun analyzeMethodNodeWithInterpreter(node: MethodNode, interpreter: BasicInterpreter): Array<out Frame<BasicValue>?> {
|
||||
val analyzer = object : FastMethodAnalyzer<BasicValue>("fake", node, interpreter) {
|
||||
val analyzer = object : Analyzer<BasicValue>(interpreter) {
|
||||
override fun newFrame(nLocals: Int, nStack: Int): Frame<BasicValue> {
|
||||
|
||||
return object : Frame<BasicValue>(nLocals, nStack) {
|
||||
@@ -152,7 +151,7 @@ internal fun analyzeMethodNodeWithInterpreter(node: MethodNode, interpreter: Bas
|
||||
}
|
||||
|
||||
try {
|
||||
return analyzer.analyze()
|
||||
return analyzer.analyze("fake", node)
|
||||
} catch (e: AnalyzerException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ abstract class ObjectTransformer<out T : TransformationInfo>(@JvmField val trans
|
||||
val classBuilder = state.factory.newVisitor(
|
||||
JvmDeclarationOrigin.NO_ORIGIN,
|
||||
Type.getObjectType(transformationInfo.newClassName),
|
||||
inliningContext.callSiteInfo.file!!
|
||||
inliningContext.root.sourceCompilerForInline.callsiteFile!!
|
||||
)
|
||||
|
||||
return RemappingClassBuilder(
|
||||
|
||||
@@ -5,55 +5,54 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.getMethodAsmFlags
|
||||
import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtIfExpression
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinableParameterExpression
|
||||
import org.jetbrains.kotlin.resolve.inline.isInlineOnly
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class PsiInlineCodegen(
|
||||
codegen: ExpressionCodegen,
|
||||
state: GenerationState,
|
||||
private val functionDescriptor: FunctionDescriptor,
|
||||
function: FunctionDescriptor,
|
||||
methodOwner: Type,
|
||||
signature: JvmMethodSignature,
|
||||
typeParameterMappings: TypeParameterMappings<KotlinType>,
|
||||
sourceCompiler: SourceCompilerForInline,
|
||||
private val methodOwner: Type,
|
||||
private val actualDispatchReceiver: Type,
|
||||
reportErrorsOn: KtElement,
|
||||
private val actualDispatchReceiver: Type = methodOwner
|
||||
) : InlineCodegen<ExpressionCodegen>(
|
||||
codegen, state, signature, typeParameterMappings, sourceCompiler,
|
||||
codegen, state, function, methodOwner, signature, typeParameterMappings, sourceCompiler,
|
||||
ReifiedTypeInliner(
|
||||
typeParameterMappings, PsiInlineIntrinsicsSupport(state, reportErrorsOn), codegen.typeSystem,
|
||||
typeParameterMappings, PsiInlineIntrinsicsSupport(state), codegen.typeSystem,
|
||||
state.languageVersionSettings, state.unifiedNullChecks
|
||||
),
|
||||
), CallGenerator {
|
||||
override fun generateAssertField() =
|
||||
codegen.parentCodegen.generateAssertField()
|
||||
override fun generateAssertFieldIfNeeded(info: RootInliningContext) {
|
||||
if (info.generateAssertField) {
|
||||
codegen.parentCodegen.generateAssertField()
|
||||
}
|
||||
}
|
||||
|
||||
override fun genCallInner(
|
||||
callableMethod: Callable,
|
||||
@@ -61,26 +60,13 @@ class PsiInlineCodegen(
|
||||
callDefault: Boolean,
|
||||
codegen: ExpressionCodegen
|
||||
) {
|
||||
(sourceCompiler as PsiSourceCompilerForInline).callDefault = callDefault
|
||||
assert(hiddenParameters.isEmpty()) { "putHiddenParamsIntoLocals() should be called after processHiddenParameters()" }
|
||||
if (!state.globalInlineContext.enterIntoInlining(functionDescriptor, resolvedCall?.call?.callElement)) {
|
||||
generateStub(resolvedCall?.call?.callElement?.text ?: "<no source>", codegen)
|
||||
return
|
||||
}
|
||||
try {
|
||||
val registerLineNumber = registerLineNumberAfterwards(resolvedCall)
|
||||
for (info in expressionMap.values) {
|
||||
if (info is PsiExpressionLambda) {
|
||||
// Can't be done immediately in `rememberClosure` for some reason:
|
||||
info.generateLambdaBody(sourceCompiler)
|
||||
// Requires `generateLambdaBody` first if the closure is non-empty (for bound callable references,
|
||||
// or indeed any callable references, it *is* empty, so this was done in `rememberClosure`):
|
||||
if (!info.isBoundCallableReference) {
|
||||
putClosureParametersOnStack(info, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
performInline(registerLineNumber, functionDescriptor.isInlineOnly())
|
||||
performInline(resolvedCall?.typeArguments?.keys?.toList(), callDefault, callDefault, codegen.typeSystem, registerLineNumber)
|
||||
} finally {
|
||||
state.globalInlineContext.exitFromInlining()
|
||||
}
|
||||
@@ -92,30 +78,38 @@ class PsiInlineCodegen(
|
||||
return parentIfCondition.isAncestor(callElement, false)
|
||||
}
|
||||
|
||||
private val hiddenParameters = mutableListOf<Pair<ParameterInfo, Int>>()
|
||||
|
||||
override fun processHiddenParameters() {
|
||||
val contextKind = (sourceCompiler as PsiSourceCompilerForInline).context.contextKind
|
||||
if (getMethodAsmFlags(functionDescriptor, contextKind, state) and Opcodes.ACC_STATIC == 0) {
|
||||
hiddenParameters += invocationParamBuilder.addNextParameter(methodOwner, false, actualDispatchReceiver) to
|
||||
codegen.frameMap.enterTemp(methodOwner)
|
||||
override fun processAndPutHiddenParameters(justProcess: Boolean) {
|
||||
if (getMethodAsmFlags(functionDescriptor, sourceCompiler.contextKind, state) and Opcodes.ACC_STATIC == 0) {
|
||||
invocationParamBuilder.addNextParameter(methodOwner, false, actualDispatchReceiver)
|
||||
}
|
||||
|
||||
for (param in jvmSignature.valueParameters) {
|
||||
if (param.kind == JvmMethodParameterKind.VALUE) {
|
||||
break
|
||||
}
|
||||
hiddenParameters += invocationParamBuilder.addNextParameter(param.asmType, false) to
|
||||
codegen.frameMap.enterTemp(param.asmType)
|
||||
invocationParamBuilder.addNextParameter(param.asmType, false)
|
||||
}
|
||||
|
||||
invocationParamBuilder.markValueParametersStart()
|
||||
val hiddenParameters = invocationParamBuilder.buildParameters().parameters
|
||||
|
||||
delayedHiddenWriting = recordParameterValueInLocalVal(justProcess, false, *hiddenParameters.toTypedArray())
|
||||
}
|
||||
|
||||
override fun putHiddenParamsIntoLocals() {
|
||||
for (i in hiddenParameters.indices.reversed()) {
|
||||
val (param, offset) = hiddenParameters[i]
|
||||
StackValue.local(offset, param.type).store(StackValue.onStack(param.typeOnStack), codegen.visitor)
|
||||
override fun putClosureParametersOnStack(next: LambdaInfo, functionReferenceReceiver: StackValue?) {
|
||||
activeLambda = next
|
||||
when (next) {
|
||||
is PsiExpressionLambda -> codegen.pushClosureOnStack(next.classDescriptor, true, this, functionReferenceReceiver)
|
||||
is DefaultLambda -> rememberCapturedForDefaultLambda(next)
|
||||
else -> throw RuntimeException("Unknown lambda: $next")
|
||||
}
|
||||
hiddenParameters.clear()
|
||||
activeLambda = null
|
||||
}
|
||||
|
||||
private fun getBoundCallableReferenceReceiver(argumentExpression: KtExpression): ReceiverValue? {
|
||||
val deparenthesized = KtPsiUtil.deparenthesize(argumentExpression) as? KtCallableReferenceExpression ?: return null
|
||||
val resolvedCall = deparenthesized.callableReference.getResolvedCallWithAssert(state.bindingContext)
|
||||
return JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)
|
||||
}
|
||||
|
||||
/*lambda or callable reference*/
|
||||
@@ -138,7 +132,23 @@ class PsiInlineCodegen(
|
||||
}
|
||||
|
||||
if (isInliningParameter(argumentExpression, valueParameterDescriptor)) {
|
||||
rememberClosure(argumentExpression, parameterType.type, valueParameterDescriptor)
|
||||
val lambdaInfo = rememberClosure(argumentExpression, parameterType.type, valueParameterDescriptor)
|
||||
|
||||
val receiverValue = getBoundCallableReferenceReceiver(argumentExpression)
|
||||
if (receiverValue != null) {
|
||||
val receiver = codegen.generateReceiverValue(receiverValue, false)
|
||||
val receiverKotlinType = receiver.kotlinType
|
||||
val boxedReceiver =
|
||||
if (receiverKotlinType != null)
|
||||
receiver.type.boxReceiverForBoundReference(receiverKotlinType, state.typeMapper)
|
||||
else
|
||||
receiver.type.boxReceiverForBoundReference()
|
||||
|
||||
putClosureParametersOnStack(
|
||||
lambdaInfo,
|
||||
StackValue.coercion(receiver, boxedReceiver, receiverKotlinType)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val value = codegen.gen(argumentExpression)
|
||||
val kind = when {
|
||||
@@ -157,176 +167,50 @@ class PsiInlineCodegen(
|
||||
private fun isCallSiteIsSuspend(descriptor: ValueParameterDescriptor): Boolean =
|
||||
state.bindingContext[CodegenBinding.CALL_SITE_IS_SUSPEND_FOR_CROSSINLINE_LAMBDA, descriptor] == true
|
||||
|
||||
private fun rememberClosure(expression: KtExpression, type: Type, parameter: ValueParameterDescriptor) {
|
||||
private fun rememberClosure(expression: KtExpression, type: Type, parameter: ValueParameterDescriptor): LambdaInfo {
|
||||
val ktLambda = KtPsiUtil.deparenthesize(expression)
|
||||
assert(isInlinableParameterExpression(ktLambda)) { "Couldn't find inline expression in ${expression.text}" }
|
||||
|
||||
val boundReceiver = if (ktLambda is KtCallableReferenceExpression) {
|
||||
val resolvedCall = ktLambda.callableReference.getResolvedCallWithAssert(state.bindingContext)
|
||||
JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)
|
||||
} else null
|
||||
|
||||
val lambda = PsiExpressionLambda(ktLambda!!, state, parameter.isCrossinline, boundReceiver != null)
|
||||
rememberClosure(type, parameter.index, lambda)
|
||||
if (boundReceiver != null) {
|
||||
// Has to be done immediately to preserve evaluation order.
|
||||
val receiver = codegen.generateReceiverValue(boundReceiver, false)
|
||||
val receiverKotlinType = receiver.kotlinType
|
||||
val boxedReceiver =
|
||||
if (receiverKotlinType != null)
|
||||
DescriptorAsmUtil.boxType(receiver.type, receiverKotlinType, state.typeMapper)
|
||||
else
|
||||
AsmUtil.boxType(receiver.type)
|
||||
val receiverValue = StackValue.coercion(receiver, boxedReceiver, receiverKotlinType)
|
||||
putClosureParametersOnStack(lambda, receiverValue)
|
||||
return PsiExpressionLambda(
|
||||
ktLambda!!, state.typeMapper, state.languageVersionSettings,
|
||||
parameter.isCrossinline, getBoundCallableReferenceReceiver(expression) != null
|
||||
).also { lambda ->
|
||||
val closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.index)
|
||||
closureInfo.functionalArgument = lambda
|
||||
expressionMap[closureInfo.index] = lambda
|
||||
}
|
||||
}
|
||||
|
||||
var activeLambda: PsiExpressionLambda? = null
|
||||
private set
|
||||
override fun putValueIfNeeded(parameterType: JvmKotlinType, value: StackValue, kind: ValueKind, parameterIndex: Int) {
|
||||
if (processDefaultMaskOrMethodHandler(value, kind)) return
|
||||
|
||||
private fun putClosureParametersOnStack(next: PsiExpressionLambda, receiverValue: StackValue?) {
|
||||
activeLambda = next
|
||||
codegen.pushClosureOnStack(next.classDescriptor, true, this, receiverValue)
|
||||
activeLambda = null
|
||||
assert(maskValues.isEmpty()) { "Additional default call arguments should be last ones, but $value" }
|
||||
|
||||
putArgumentOrCapturedToLocalVal(parameterType, value, -1, parameterIndex, kind)
|
||||
}
|
||||
|
||||
override fun putValueIfNeeded(parameterType: JvmKotlinType, value: StackValue, kind: ValueKind, parameterIndex: Int) =
|
||||
putArgumentToLocalVal(parameterType, value, parameterIndex, kind)
|
||||
|
||||
override fun putCapturedValueOnStack(stackValue: StackValue, valueType: Type, paramIndex: Int) =
|
||||
putCapturedToLocalVal(stackValue, activeLambda!!.capturedVars[paramIndex], stackValue.kotlinType)
|
||||
override fun putCapturedValueOnStack(stackValue: StackValue, valueType: Type, paramIndex: Int) {
|
||||
putArgumentOrCapturedToLocalVal(
|
||||
JvmKotlinType(stackValue.type, stackValue.kotlinType), stackValue, paramIndex, paramIndex, ValueKind.CAPTURED
|
||||
)
|
||||
}
|
||||
|
||||
override fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>) = Unit
|
||||
|
||||
override fun extractDefaultLambdas(node: MethodNode): List<DefaultLambda> =
|
||||
extractDefaultLambdas(node, extractDefaultLambdaOffsetAndDescriptor(jvmSignature, functionDescriptor)) { parameter ->
|
||||
PsiDefaultLambda(type, capturedArgs, parameter, offset, needReification, sourceCompiler)
|
||||
}
|
||||
}
|
||||
|
||||
private val FunctionDescriptor.explicitParameters
|
||||
get() = listOfNotNull(extensionReceiverParameter) + valueParameters
|
||||
|
||||
class PsiExpressionLambda(
|
||||
expression: KtExpression,
|
||||
private val state: GenerationState,
|
||||
val isCrossInline: Boolean,
|
||||
override val isBoundCallableReference: Boolean
|
||||
) : ExpressionLambda() {
|
||||
override val lambdaClassType: Type
|
||||
|
||||
override val invokeMethod: Method
|
||||
|
||||
val invokeMethodDescriptor: FunctionDescriptor
|
||||
|
||||
override val invokeMethodParameters: List<KotlinType?>
|
||||
get() {
|
||||
val actualInvokeDescriptor = if (isSuspend)
|
||||
getOrCreateJvmSuspendFunctionView(invokeMethodDescriptor, state)
|
||||
else
|
||||
invokeMethodDescriptor
|
||||
return actualInvokeDescriptor.explicitParameters.map { it.returnType }
|
||||
}
|
||||
|
||||
override val invokeMethodReturnType: KotlinType?
|
||||
get() = invokeMethodDescriptor.returnType
|
||||
|
||||
val classDescriptor: ClassDescriptor
|
||||
|
||||
val propertyReferenceInfo: PropertyReferenceInfo?
|
||||
|
||||
val functionWithBodyOrCallableReference: KtExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression
|
||||
|
||||
override val returnLabels: Map<String, Label?>
|
||||
|
||||
override val isSuspend: Boolean
|
||||
|
||||
val closure: CalculatedClosure
|
||||
|
||||
init {
|
||||
val bindingContext = state.bindingContext
|
||||
val function = bindingContext.get(BindingContext.FUNCTION, functionWithBodyOrCallableReference)
|
||||
if (function == null && expression is KtCallableReferenceExpression) {
|
||||
val variableDescriptor =
|
||||
bindingContext.get(BindingContext.VARIABLE, functionWithBodyOrCallableReference) as? VariableDescriptorWithAccessors
|
||||
?: throw AssertionError("Reference expression not resolved to variable descriptor with accessors: ${expression.getText()}")
|
||||
classDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, variableDescriptor)
|
||||
?: throw IllegalStateException("Class for callable not found: $variableDescriptor\n${expression.text}")
|
||||
lambdaClassType = state.typeMapper.mapClass(classDescriptor)
|
||||
val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor)
|
||||
invokeMethodDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor)
|
||||
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext)
|
||||
propertyReferenceInfo = PropertyReferenceInfo(resolvedCall.resultingDescriptor as VariableDescriptor, getFunction)
|
||||
} else {
|
||||
propertyReferenceInfo = null
|
||||
invokeMethodDescriptor = function ?: throw AssertionError("Function is not resolved to descriptor: " + expression.text)
|
||||
classDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, invokeMethodDescriptor)
|
||||
?: throw IllegalStateException("Class for invoke method not found: $invokeMethodDescriptor\n${expression.text}")
|
||||
lambdaClassType = CodegenBinding.asmTypeForAnonymousClass(bindingContext, invokeMethodDescriptor)
|
||||
}
|
||||
|
||||
closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor)
|
||||
?: throw AssertionError("null closure for lambda ${expression.text}")
|
||||
returnLabels = getDeclarationLabels(expression, invokeMethodDescriptor).associateWith { null }
|
||||
invokeMethod = state.typeMapper.mapAsmMethod(invokeMethodDescriptor)
|
||||
isSuspend = invokeMethodDescriptor.isSuspend
|
||||
override fun putHiddenParamsIntoLocals() {
|
||||
assert(delayedHiddenWriting != null) { "processAndPutHiddenParameters(true) should be called before putHiddenParamsIntoLocals" }
|
||||
delayedHiddenWriting!!.invoke()
|
||||
delayedHiddenWriting = null
|
||||
}
|
||||
|
||||
// This can only be computed after generating the body, hence `lazy`.
|
||||
override val capturedVars: List<CapturedParamDesc> by lazy {
|
||||
arrayListOf<CapturedParamDesc>().apply {
|
||||
val captureThis = closure.capturedOuterClassDescriptor
|
||||
if (captureThis != null) {
|
||||
add(capturedParamDesc(AsmUtil.CAPTURED_THIS_FIELD, state.typeMapper.mapType(captureThis.defaultType), isSuspend = false))
|
||||
}
|
||||
|
||||
val capturedReceiver = closure.capturedReceiverFromOuterContext
|
||||
if (capturedReceiver != null) {
|
||||
val fieldName = closure.getCapturedReceiverFieldName(state.typeMapper.bindingContext, state.languageVersionSettings)
|
||||
val type = if (isBoundCallableReference)
|
||||
state.typeMapper.mapType(capturedReceiver, null, TypeMappingMode.GENERIC_ARGUMENT)
|
||||
else
|
||||
state.typeMapper.mapType(capturedReceiver)
|
||||
add(capturedParamDesc(fieldName, type, isSuspend = false))
|
||||
}
|
||||
|
||||
closure.captureVariables.forEach { (parameter, value) ->
|
||||
val isSuspend = parameter is ValueParameterDescriptor && parameter.type.isSuspendFunctionTypeOrSubtype
|
||||
add(capturedParamDesc(value.fieldName, value.type, isSuspend))
|
||||
}
|
||||
}
|
||||
override fun extractDefaultLambdas(node: MethodNode): List<DefaultLambda> {
|
||||
return expandMaskConditionsAndUpdateVariableNodes(
|
||||
node, maskStartIndex, maskValues, methodHandleInDefaultMethodIndex,
|
||||
extractDefaultLambdaOffsetAndDescriptor(jvmSignature, functionDescriptor),
|
||||
::PsiDefaultLambda
|
||||
)
|
||||
}
|
||||
|
||||
val isPropertyReference: Boolean
|
||||
get() = propertyReferenceInfo != null
|
||||
}
|
||||
|
||||
class PsiDefaultLambda(
|
||||
lambdaClassType: Type,
|
||||
capturedArgs: Array<Type>,
|
||||
parameterDescriptor: ValueParameterDescriptor,
|
||||
offset: Int,
|
||||
needReification: Boolean,
|
||||
sourceCompiler: SourceCompilerForInline
|
||||
) : DefaultLambda(lambdaClassType, capturedArgs, offset, needReification, sourceCompiler) {
|
||||
private val invokeMethodDescriptor: FunctionDescriptor
|
||||
|
||||
override val invokeMethodParameters: List<KotlinType?>
|
||||
get() = invokeMethodDescriptor.explicitParameters.map { it.returnType }
|
||||
|
||||
override val invokeMethodReturnType: KotlinType?
|
||||
get() = invokeMethodDescriptor.returnType
|
||||
|
||||
init {
|
||||
val name = if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE
|
||||
val descriptor = parameterDescriptor.type.memberScope
|
||||
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
|
||||
.single()
|
||||
.let { if (parameterDescriptor.type.isSuspendFunctionType) getOrCreateJvmSuspendFunctionView(it, sourceCompiler.state) else it }
|
||||
// This is technically wrong as it always uses `invoke`, but `loadInvoke` will fall back to `get` which is never mangled...
|
||||
val asmMethod = sourceCompiler.state.typeMapper.mapAsmMethod(descriptor)
|
||||
val invokeIsErased = loadInvoke(sourceCompiler, name.asString(), asmMethod)
|
||||
invokeMethodDescriptor = if (invokeIsErased) descriptor.original else descriptor
|
||||
}
|
||||
override fun descriptorIsDeserialized(memberDescriptor: CallableMemberDescriptor): Boolean =
|
||||
memberDescriptor is DescriptorWithContainerSource
|
||||
}
|
||||
|
||||
@@ -5,19 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
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.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.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
|
||||
@@ -25,10 +19,7 @@ import org.jetbrains.org.objectweb.asm.Type.INT_TYPE
|
||||
import org.jetbrains.org.objectweb.asm.Type.VOID_TYPE
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class PsiInlineIntrinsicsSupport(
|
||||
override val state: GenerationState,
|
||||
private val reportErrorsOn: KtElement,
|
||||
) : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
|
||||
class PsiInlineIntrinsicsSupport(private val state: GenerationState) : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
|
||||
override fun putClassInstance(v: InstructionAdapter, type: KotlinType) {
|
||||
DescriptorAsmUtil.putJavaLangClassInstance(v, state.typeMapper.mapType(type), type, state.typeMapper)
|
||||
}
|
||||
@@ -65,18 +56,5 @@ class PsiInlineIntrinsicsSupport(
|
||||
)
|
||||
}
|
||||
|
||||
override fun isMutableCollectionType(type: KotlinType): Boolean {
|
||||
val classifier = type.constructor.declarationDescriptor
|
||||
return classifier is ClassDescriptor && JavaToKotlinClassMap.isMutable(classifier.fqNameUnsafe)
|
||||
}
|
||||
|
||||
override fun toKotlinType(type: KotlinType): KotlinType = type
|
||||
|
||||
override fun reportSuspendTypeUnsupported() {
|
||||
state.diagnostics.report(TYPEOF_SUSPEND_TYPE.on(reportErrorsOn))
|
||||
}
|
||||
|
||||
override fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name) {
|
||||
state.diagnostics.report(TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND.on(reportErrorsOn, typeParameterName.asString()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,443 +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 com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionTypeOrSubtype
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.context.*
|
||||
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.*
|
||||
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
|
||||
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.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class PsiSourceCompilerForInline(
|
||||
private val codegen: ExpressionCodegen,
|
||||
override val callElement: KtElement,
|
||||
private val functionDescriptor: FunctionDescriptor
|
||||
) : SourceCompilerForInline {
|
||||
override val state
|
||||
get() = codegen.state
|
||||
|
||||
private val additionalInnerClasses = mutableListOf<ClassDescriptor>()
|
||||
|
||||
val context = getContext(
|
||||
functionDescriptor,
|
||||
functionDescriptor,
|
||||
codegen.state,
|
||||
DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile,
|
||||
additionalInnerClasses
|
||||
) as MethodContext
|
||||
|
||||
override val callElementText: String by lazy { callElement.text }
|
||||
|
||||
override val inlineCallSiteInfo: InlineCallSiteInfo
|
||||
get() {
|
||||
var context = codegen.getContext()
|
||||
var parentCodegen = codegen.parentCodegen
|
||||
while (context is InlineLambdaContext) {
|
||||
val closureContext = context.getParentContext()
|
||||
assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" }
|
||||
assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" }
|
||||
context = closureContext.parentContext as MethodContext
|
||||
assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" }
|
||||
parentCodegen = (parentCodegen as FakeMemberCodegen).delegate
|
||||
}
|
||||
|
||||
val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
|
||||
return InlineCallSiteInfo(
|
||||
parentCodegen.className,
|
||||
signature.asmMethod,
|
||||
context.functionDescriptor.isInlineOrInsideInline(),
|
||||
callElement.containingFile,
|
||||
CodegenUtil.getLineNumberForElement(callElement, false) ?: 0
|
||||
)
|
||||
}
|
||||
|
||||
override val sourceMapper
|
||||
get() = codegen.parentCodegen.orCreateSourceMapper
|
||||
|
||||
override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
|
||||
require(lambdaInfo is PsiExpressionLambda)
|
||||
val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor
|
||||
val jvmMethodSignature = state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
|
||||
val asmMethod = jvmMethodSignature.asmMethod
|
||||
val methodNode = MethodNode(
|
||||
Opcodes.API_VERSION, DescriptorAsmUtil.getMethodAsmFlags(invokeMethodDescriptor, OwnerKind.IMPLEMENTATION, state),
|
||||
asmMethod.name, asmMethod.descriptor, null, null
|
||||
)
|
||||
val adapter = wrapWithMaxLocalCalc(methodNode)
|
||||
val closureContext = when {
|
||||
lambdaInfo.isPropertyReference ->
|
||||
codegen.getContext().intoAnonymousClass(lambdaInfo.classDescriptor, codegen, OwnerKind.IMPLEMENTATION)
|
||||
invokeMethodDescriptor.isSuspend ->
|
||||
codegen.getContext().intoCoroutineClosure(
|
||||
getOrCreateJvmSuspendFunctionView(invokeMethodDescriptor, state), invokeMethodDescriptor, codegen, state.typeMapper
|
||||
)
|
||||
else -> codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, state.typeMapper)
|
||||
}
|
||||
val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, lambdaInfo.isCrossInline, lambdaInfo.isPropertyReference)
|
||||
val smap = generateMethodBody(
|
||||
adapter, invokeMethodDescriptor, context,
|
||||
lambdaInfo.functionWithBodyOrCallableReference,
|
||||
jvmMethodSignature, lambdaInfo
|
||||
)
|
||||
adapter.visitMaxs(-1, -1)
|
||||
return SMAPAndMethodNode(methodNode, smap)
|
||||
}
|
||||
|
||||
private fun generateMethodBody(
|
||||
adapter: MethodVisitor,
|
||||
descriptor: FunctionDescriptor,
|
||||
context: MethodContext,
|
||||
expression: KtExpression,
|
||||
jvmMethodSignature: JvmMethodSignature,
|
||||
lambdaInfo: PsiExpressionLambda?
|
||||
): SMAP {
|
||||
val isLambda = lambdaInfo != null
|
||||
|
||||
// Wrapping for preventing marking actual parent codegen as containing reified markers
|
||||
val parentCodegen = FakeMemberCodegen(
|
||||
codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>,
|
||||
if (isLambda)
|
||||
codegen.parentCodegen.className
|
||||
else
|
||||
state.typeMapper.mapImplementationOwner(descriptor).internalName,
|
||||
if (isLambda) emptyList() else additionalInnerClasses,
|
||||
isLambda
|
||||
)
|
||||
|
||||
val strategy = when (expression) {
|
||||
is KtCallableReferenceExpression -> {
|
||||
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext)
|
||||
val receiverKotlinType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type
|
||||
val receiverType = receiverKotlinType?.let(state.typeMapper::mapType)
|
||||
val boundReceiverJvmKotlinType = receiverType?.let { JvmKotlinType(receiverType, receiverKotlinType) }
|
||||
|
||||
if (isLambda && lambdaInfo!!.isPropertyReference) {
|
||||
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
|
||||
val info = lambdaInfo.propertyReferenceInfo
|
||||
PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
|
||||
true, info!!.getFunction, info.target, asmType,
|
||||
boundReceiverJvmKotlinType,
|
||||
lambdaInfo.functionWithBodyOrCallableReference, state, true
|
||||
)
|
||||
} else {
|
||||
FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, boundReceiverJvmKotlinType, null, true)
|
||||
}
|
||||
}
|
||||
is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
|
||||
else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody)
|
||||
}
|
||||
|
||||
FunctionCodegen.generateMethodBody(
|
||||
adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode,
|
||||
state.languageVersionSettings.isReleaseCoroutines()
|
||||
)
|
||||
|
||||
if (isLambda) {
|
||||
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
|
||||
}
|
||||
|
||||
return SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class FakeMemberCodegen(
|
||||
val delegate: MemberCodegen<*>,
|
||||
declaration: KtElement,
|
||||
codegenContext: FieldOwnerContext<*>,
|
||||
private val className: String,
|
||||
private val parentAsInnerClasses: List<ClassDescriptor>,
|
||||
private val isInlineLambdaCodegen: Boolean
|
||||
) : MemberCodegen<KtPureElement>(delegate as MemberCodegen<KtPureElement>, declaration, codegenContext) {
|
||||
|
||||
override fun generateDeclaration() {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun generateBody() {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun generateKotlinMetadataAnnotation() {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun getInlineNameGenerator(): NameGenerator {
|
||||
return delegate.inlineNameGenerator
|
||||
}
|
||||
|
||||
override //TODO: obtain name from context
|
||||
fun getClassName(): String {
|
||||
return className
|
||||
}
|
||||
|
||||
override fun addParentsToInnerClassesIfNeeded(innerClasses: MutableCollection<ClassDescriptor>) {
|
||||
if (isInlineLambdaCodegen) {
|
||||
super.addParentsToInnerClassesIfNeeded(innerClasses)
|
||||
} else {
|
||||
innerClasses.addAll(parentAsInnerClasses)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateAssertField() {
|
||||
delegate.generateAssertField()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDirectMemberAndCallableFromObject(): CallableMemberDescriptor {
|
||||
val directMember = JvmCodegenUtil.getDirectMember(functionDescriptor)
|
||||
return (directMember as? ImportedFromObjectCallableDescriptor<*>)?.callableFromObject ?: directMember
|
||||
}
|
||||
|
||||
internal var callDefault: Boolean = false
|
||||
|
||||
private fun mapDefault(): Method {
|
||||
// This is all available in the `Callable` passed to `PsiInlineCodegen.genCallInner`, but it's not forwarded through the inliner...
|
||||
var result = state.typeMapper.mapDefaultMethod(functionDescriptor, context.contextKind)
|
||||
if (result.name.contains("-") &&
|
||||
!state.configuration.getBoolean(JVMConfigurationKeys.USE_OLD_INLINE_CLASSES_MANGLING_SCHEME) &&
|
||||
classFileContainsMethod(functionDescriptor, state, result) == false
|
||||
) {
|
||||
state.typeMapper.useOldManglingRulesForFunctionAcceptingInlineClass = true
|
||||
result = state.typeMapper.mapDefaultMethod(functionDescriptor, context.contextKind)
|
||||
state.typeMapper.useOldManglingRulesForFunctionAcceptingInlineClass = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun compileInlineFunction(jvmSignature: JvmMethodSignature): SMAPAndMethodNode {
|
||||
generateInlineIntrinsic(state.languageVersionSettings, functionDescriptor, jvmSignature.asmMethod, codegen.typeSystem)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
val asmMethod = if (callDefault) mapDefault() else jvmSignature.asmMethod
|
||||
if (asmMethod.name != functionDescriptor.name.asString()) {
|
||||
KotlinLookupLocation(callElement).location?.let {
|
||||
state.trackLookup(DescriptorUtils.getFqNameSafe(functionDescriptor.containingDeclaration), asmMethod.name, it)
|
||||
}
|
||||
}
|
||||
|
||||
val directMember = getDirectMemberAndCallableFromObject()
|
||||
if (directMember is DescriptorWithContainerSource) {
|
||||
val containerId = KotlinTypeMapper.getContainingClassesForDeserializedCallable(directMember).implClassId
|
||||
val isMangled = requiresFunctionNameManglingForReturnType(functionDescriptor)
|
||||
return loadCompiledInlineFunction(containerId, asmMethod, functionDescriptor.isSuspend, isMangled, state)
|
||||
}
|
||||
|
||||
val element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor) as? KtDeclarationWithBody
|
||||
?: throw IllegalStateException("Couldn't find declaration for function $functionDescriptor")
|
||||
|
||||
val node = MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
DescriptorAsmUtil.getMethodAsmFlags(functionDescriptor, context.contextKind, state) or
|
||||
if (callDefault) Opcodes.ACC_STATIC else 0,
|
||||
asmMethod.name,
|
||||
asmMethod.descriptor, null, null
|
||||
)
|
||||
|
||||
//for maxLocals calculation
|
||||
val maxCalcAdapter = wrapWithMaxLocalCalc(node)
|
||||
val smap = if (callDefault) {
|
||||
val implementationOwner = state.typeMapper.mapImplementationOwner(functionDescriptor)
|
||||
val parentCodegen = FakeMemberCodegen(
|
||||
codegen.parentCodegen, element, context.parentContext as FieldOwnerContext<*>,
|
||||
implementationOwner.internalName,
|
||||
additionalInnerClasses,
|
||||
false
|
||||
)
|
||||
if (element !is KtNamedFunction) {
|
||||
throw IllegalStateException("Property accessors with default parameters not supported $functionDescriptor")
|
||||
}
|
||||
FunctionCodegen.generateDefaultImplBody(
|
||||
context, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||
element as KtNamedFunction?, parentCodegen, asmMethod
|
||||
)
|
||||
SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
} else {
|
||||
generateMethodBody(maxCalcAdapter, functionDescriptor, context, element, jvmSignature, null)
|
||||
}
|
||||
maxCalcAdapter.visitMaxs(-1, -1)
|
||||
maxCalcAdapter.visitEnd()
|
||||
|
||||
return SMAPAndMethodNode(node, smap)
|
||||
}
|
||||
|
||||
override fun hasFinallyBlocks() = codegen.hasFinallyBlocks()
|
||||
|
||||
override fun generateFinallyBlocks(finallyNode: MethodNode, curFinallyDepth: Int, returnType: Type, afterReturnLabel: Label, target: Label?) {
|
||||
// TODO use the target label for non-local break/continue
|
||||
ExpressionCodegen(
|
||||
finallyNode, codegen.frameMap, codegen.returnType,
|
||||
codegen.getContext(), codegen.state, codegen.parentCodegen
|
||||
).also {
|
||||
it.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)
|
||||
}.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel)
|
||||
}
|
||||
|
||||
override val isCallInsideSameModuleAsCallee: Boolean
|
||||
get() = JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory)
|
||||
|
||||
override val isFinallyMarkerRequired: Boolean
|
||||
get() = isFinallyMarkerRequired(codegen.getContext())
|
||||
|
||||
override fun isSuspendLambdaCapturedByOuterObjectOrLambda(name: String): Boolean {
|
||||
// We cannot find the lambda in captured parameters: it came from object outside of the our reach:
|
||||
// this can happen when the lambda capture by non-transformed closure:
|
||||
// inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() }
|
||||
// inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() }
|
||||
// Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0)
|
||||
// Thus, transformed lambda from inlineMe, inlineMe3$$inlined$inlineMe2$1 contains the following bytecode
|
||||
// ALOAD 0
|
||||
// GETFIELD inlineMe2$1$invokeSuspend$$inlined$inlineMe$1.this$0 : LScratchKt$inlineMe2$1;
|
||||
// GETFIELD inlineMe2$1.$c : Lkotlin/jvm/functions/Function1;
|
||||
// Since inlineMe2's lambda is outside of reach of the inliner, find crossinline parameter from compilation context:
|
||||
var container: DeclarationDescriptor = codegen.getContext().functionDescriptor
|
||||
while (container !is ClassDescriptor) {
|
||||
container = container.containingDeclaration ?: return false
|
||||
}
|
||||
var classDescriptor: ClassDescriptor? = container
|
||||
while (classDescriptor != null) {
|
||||
val closure = state.bindingContext[CodegenBinding.CLOSURE, classDescriptor] ?: return false
|
||||
for ((param, value) in closure.captureVariables) {
|
||||
if (param is ValueParameterDescriptor && value.fieldName == name) {
|
||||
return param.type.isSuspendFunctionTypeOrSubtype
|
||||
}
|
||||
}
|
||||
classDescriptor = closure.capturedOuterClassDescriptor
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getContextLabels(): Map<String, Label?> {
|
||||
val context = codegen.getContext()
|
||||
val parentContext = context.parentContext
|
||||
val descriptor = if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) {
|
||||
parentContext.originalSuspendLambdaDescriptor!!
|
||||
} else context.contextDescriptor
|
||||
|
||||
val labels = getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor)
|
||||
return labels.associateWith { null } // TODO add break/continue labels
|
||||
}
|
||||
|
||||
override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) {
|
||||
org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor(callElement, state, stackTraceElement)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getContext(
|
||||
descriptor: DeclarationDescriptor,
|
||||
innerDescriptor: DeclarationDescriptor,
|
||||
state: GenerationState,
|
||||
sourceFile: KtFile?,
|
||||
additionalInners: MutableList<ClassDescriptor>
|
||||
): CodegenContext<*> {
|
||||
if (descriptor is PackageFragmentDescriptor) {
|
||||
//no inners
|
||||
return PackageContext(descriptor, state.rootContext, null, sourceFile)
|
||||
}
|
||||
|
||||
val container = descriptor.containingDeclaration ?: error("No container for descriptor: $descriptor")
|
||||
val containerContext = getContext(
|
||||
container,
|
||||
descriptor,
|
||||
state,
|
||||
sourceFile,
|
||||
additionalInners
|
||||
)
|
||||
|
||||
return when (descriptor) {
|
||||
is ScriptDescriptor -> {
|
||||
val earlierScripts = state.scriptSpecific.earlierScriptsForReplInterpreter
|
||||
containerContext.intoScript(
|
||||
descriptor,
|
||||
earlierScripts ?: emptyList(),
|
||||
descriptor as ClassDescriptor, state.typeMapper
|
||||
)
|
||||
}
|
||||
is ClassDescriptor -> {
|
||||
val kind =
|
||||
when {
|
||||
DescriptorUtils.isInterface(descriptor) &&
|
||||
innerDescriptor !is ClassDescriptor &&
|
||||
!innerDescriptor.isCallableMemberCompiledToJvmDefault(state.jvmDefaultMode) ->
|
||||
OwnerKind.DEFAULT_IMPLS
|
||||
else ->
|
||||
OwnerKind.IMPLEMENTATION
|
||||
}
|
||||
|
||||
additionalInners.addIfNotNull(
|
||||
InnerClassConsumer.classForInnerClassRecord(descriptor, kind == OwnerKind.DEFAULT_IMPLS)
|
||||
)
|
||||
|
||||
if (descriptor.isInlineClass()) {
|
||||
containerContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION, state)
|
||||
.intoClass(descriptor, OwnerKind.ERASED_INLINE_CLASS, state)
|
||||
} else {
|
||||
containerContext.intoClass(descriptor, kind, state)
|
||||
}
|
||||
}
|
||||
is FunctionDescriptor -> {
|
||||
containerContext.intoFunction(descriptor)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalStateException("Couldn't build context for $descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.isInlineOrInsideInline(): Boolean =
|
||||
if (this is FunctionDescriptor && isInline) true
|
||||
else containingDeclaration?.isInlineOrInsideInline() == true
|
||||
|
||||
fun getDeclarationLabels(lambdaOrFun: PsiElement?, descriptor: DeclarationDescriptor): Set<String> {
|
||||
val result = HashSet<String>()
|
||||
|
||||
if (lambdaOrFun != null) {
|
||||
val label = LabelResolver.getLabelNameIfAny(lambdaOrFun)
|
||||
if (label != null) {
|
||||
result.add(label.asString())
|
||||
}
|
||||
}
|
||||
|
||||
if (!ExpressionTypingUtils.isFunctionLiteral(descriptor)) {
|
||||
if (!descriptor.name.isSpecial) {
|
||||
result.add(descriptor.name.asString())
|
||||
}
|
||||
result.add(FIRST_FUN_LABEL)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -20,11 +20,9 @@ import org.jetbrains.kotlin.codegen.generateAsCast
|
||||
import org.jetbrains.kotlin.codegen.generateIsCheck
|
||||
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
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
@@ -65,18 +63,11 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
|
||||
}
|
||||
|
||||
interface IntrinsicsSupport<KT : KotlinTypeMarker> {
|
||||
val state: GenerationState
|
||||
|
||||
fun putClassInstance(v: InstructionAdapter, type: KT)
|
||||
|
||||
fun generateTypeParameterContainer(v: InstructionAdapter, typeParameter: TypeParameterMarker)
|
||||
|
||||
fun isMutableCollectionType(type: KT): Boolean
|
||||
|
||||
fun toKotlinType(type: KT): KotlinType
|
||||
|
||||
fun reportSuspendTypeUnsupported()
|
||||
fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -6,110 +6,428 @@
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.context.*
|
||||
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.incremental.components.LocationInfo
|
||||
import org.jetbrains.kotlin.incremental.components.Position
|
||||
import org.jetbrains.kotlin.incremental.components.ScopeKind
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
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.utils.addIfNotNull
|
||||
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.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class InlineCallSiteInfo(
|
||||
val ownerClassName: String,
|
||||
val method: Method,
|
||||
val isInlineOrInsideInline: Boolean,
|
||||
val file: PsiFile?,
|
||||
val lineNumber: Int
|
||||
)
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
interface SourceCompilerForInline {
|
||||
val state: GenerationState
|
||||
|
||||
val callElement: Any
|
||||
|
||||
val lookupLocation: LookupLocation
|
||||
|
||||
val callElementText: String
|
||||
|
||||
val callsiteFile: PsiFile?
|
||||
|
||||
val contextKind: OwnerKind
|
||||
|
||||
val inlineCallSiteInfo: InlineCallSiteInfo
|
||||
|
||||
val sourceMapper: SourceMapper
|
||||
val lazySourceMapper: SourceMapper
|
||||
|
||||
fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode
|
||||
fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode
|
||||
|
||||
fun compileInlineFunction(jvmSignature: JvmMethodSignature): SMAPAndMethodNode
|
||||
fun doCreateMethodNodeFromSource(
|
||||
callableDescriptor: FunctionDescriptor,
|
||||
jvmSignature: JvmMethodSignature,
|
||||
callDefault: Boolean,
|
||||
asmMethod: Method
|
||||
): SMAPAndMethodNode
|
||||
|
||||
fun hasFinallyBlocks(): Boolean
|
||||
|
||||
fun generateFinallyBlocks(finallyNode: MethodNode, curFinallyDepth: Int, returnType: Type, afterReturnLabel: Label, target: Label?)
|
||||
fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(
|
||||
finallyNode: MethodNode,
|
||||
curFinallyDepth: Int
|
||||
): BaseExpressionCodegen
|
||||
|
||||
val isCallInsideSameModuleAsCallee: Boolean
|
||||
fun generateFinallyBlocksIfNeeded(codegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label, target: Label?)
|
||||
|
||||
val isFinallyMarkerRequired: Boolean
|
||||
fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean
|
||||
|
||||
fun isSuspendLambdaCapturedByOuterObjectOrLambda(name: String): Boolean
|
||||
fun isFinallyMarkerRequired(): Boolean
|
||||
|
||||
val compilationContextDescriptor: DeclarationDescriptor
|
||||
|
||||
val compilationContextFunctionDescriptor: FunctionDescriptor
|
||||
|
||||
fun getContextLabels(): Map<String, Label?>
|
||||
|
||||
fun reportSuspensionPointInsideMonitor(stackTraceElement: String)
|
||||
}
|
||||
|
||||
fun GenerationState.trackLookup(container: FqName, functionName: String, location: LocationInfo) {
|
||||
val lookupTracker = configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER) ?: return
|
||||
lookupTracker.record(
|
||||
location.filePath,
|
||||
if (lookupTracker.requiresPosition) location.position else Position.NO_POSITION,
|
||||
container.asString(),
|
||||
ScopeKind.CLASSIFIER,
|
||||
functionName
|
||||
)
|
||||
}
|
||||
|
||||
fun loadCompiledInlineFunction(
|
||||
containerId: ClassId,
|
||||
asmMethod: Method,
|
||||
isSuspend: Boolean,
|
||||
isMangled: Boolean,
|
||||
state: GenerationState
|
||||
): SMAPAndMethodNode {
|
||||
val containerType = AsmUtil.asmTypeByClassId(containerId)
|
||||
val bytes = state.inlineCache.classBytes.getOrPut(containerType.internalName) {
|
||||
findVirtualFile(state, containerId)?.contentsToByteArray()
|
||||
?: throw IllegalStateException("Couldn't find declaration file for $containerId")
|
||||
}
|
||||
val resultInCache = state.inlineCache.methodNodeById.getOrPut(MethodId(containerType.descriptor, asmMethod)) {
|
||||
getMethodNode(containerType, bytes, asmMethod.name, asmMethod.descriptor, isSuspend, isMangled)
|
||||
}
|
||||
return SMAPAndMethodNode(cloneMethodNode(resultInCache.node), resultInCache.classSMAP)
|
||||
}
|
||||
class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, override val callElement: KtElement) :
|
||||
SourceCompilerForInline {
|
||||
|
||||
override val state = codegen.state
|
||||
|
||||
private var context by Delegates.notNull<CodegenContext<*>>()
|
||||
|
||||
private var additionalInnerClasses = mutableListOf<ClassDescriptor>()
|
||||
|
||||
override val lookupLocation = KotlinLookupLocation(callElement)
|
||||
|
||||
|
||||
override val callElementText: String by lazy {
|
||||
callElement.text
|
||||
}
|
||||
|
||||
override val callsiteFile by lazy {
|
||||
callElement.containingFile
|
||||
}
|
||||
|
||||
override val contextKind
|
||||
get () = context.contextKind
|
||||
|
||||
override val inlineCallSiteInfo: InlineCallSiteInfo
|
||||
get() {
|
||||
var context = codegen.getContext()
|
||||
var parentCodegen = codegen.parentCodegen
|
||||
while (context is InlineLambdaContext) {
|
||||
val closureContext = context.getParentContext()
|
||||
assert(closureContext is ClosureContext) { "Parent context of inline lambda should be closure context" }
|
||||
assert(closureContext.parentContext is MethodContext) { "Closure context should appear in method context" }
|
||||
context = closureContext.parentContext as MethodContext
|
||||
assert(parentCodegen is FakeMemberCodegen) { "Parent codegen of inlined lambda should be FakeMemberCodegen" }
|
||||
parentCodegen = (parentCodegen as FakeMemberCodegen).delegate
|
||||
}
|
||||
|
||||
val signature = codegen.state.typeMapper.mapSignatureSkipGeneric(context.functionDescriptor, context.contextKind)
|
||||
return InlineCallSiteInfo(
|
||||
parentCodegen.className,
|
||||
signature.asmMethod.name,
|
||||
signature.asmMethod.descriptor,
|
||||
compilationContextFunctionDescriptor.isInlineOrInsideInline(),
|
||||
compilationContextFunctionDescriptor.isSuspend,
|
||||
CodegenUtil.getLineNumberForElement(callElement, false) ?: 0
|
||||
)
|
||||
}
|
||||
|
||||
override val lazySourceMapper
|
||||
get() = codegen.parentCodegen.orCreateSourceMapper
|
||||
|
||||
override fun generateLambdaBody(lambdaInfo: ExpressionLambda): SMAPAndMethodNode {
|
||||
lambdaInfo as? PsiExpressionLambda ?: error("TODO")
|
||||
val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor
|
||||
val jvmMethodSignature = state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
|
||||
val asmMethod = jvmMethodSignature.asmMethod
|
||||
val methodNode = MethodNode(
|
||||
Opcodes.API_VERSION, DescriptorAsmUtil.getMethodAsmFlags(invokeMethodDescriptor, OwnerKind.IMPLEMENTATION, state),
|
||||
asmMethod.name, asmMethod.descriptor, null, null
|
||||
)
|
||||
val adapter = wrapWithMaxLocalCalc(methodNode)
|
||||
val closureContext = when {
|
||||
lambdaInfo.isPropertyReference ->
|
||||
codegen.getContext().intoAnonymousClass(lambdaInfo.classDescriptor, codegen, OwnerKind.IMPLEMENTATION)
|
||||
invokeMethodDescriptor.isSuspend ->
|
||||
codegen.getContext().intoCoroutineClosure(
|
||||
getOrCreateJvmSuspendFunctionView(invokeMethodDescriptor, state), invokeMethodDescriptor, codegen, state.typeMapper
|
||||
)
|
||||
else -> codegen.getContext().intoClosure(invokeMethodDescriptor, codegen, state.typeMapper)
|
||||
}
|
||||
val context = closureContext.intoInlinedLambda(invokeMethodDescriptor, lambdaInfo.isCrossInline, lambdaInfo.isPropertyReference)
|
||||
val smap = generateMethodBody(
|
||||
adapter, invokeMethodDescriptor, context,
|
||||
lambdaInfo.functionWithBodyOrCallableReference,
|
||||
jvmMethodSignature, lambdaInfo
|
||||
)
|
||||
adapter.visitMaxs(-1, -1)
|
||||
return SMAPAndMethodNode(methodNode, smap)
|
||||
}
|
||||
|
||||
private fun generateMethodBody(
|
||||
adapter: MethodVisitor,
|
||||
descriptor: FunctionDescriptor,
|
||||
context: MethodContext,
|
||||
expression: KtExpression,
|
||||
jvmMethodSignature: JvmMethodSignature,
|
||||
lambdaInfo: PsiExpressionLambda?
|
||||
): SMAP {
|
||||
val isLambda = lambdaInfo != null
|
||||
|
||||
// Wrapping for preventing marking actual parent codegen as containing reified markers
|
||||
val parentCodegen = FakeMemberCodegen(
|
||||
codegen.parentCodegen, expression, context.parentContext as FieldOwnerContext<*>,
|
||||
if (isLambda)
|
||||
codegen.parentCodegen.className
|
||||
else
|
||||
state.typeMapper.mapImplementationOwner(descriptor).internalName,
|
||||
if (isLambda) emptyList<ClassDescriptor>() else additionalInnerClasses,
|
||||
isLambda
|
||||
)
|
||||
|
||||
val strategy = when (expression) {
|
||||
is KtCallableReferenceExpression -> {
|
||||
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext)
|
||||
val receiverKotlinType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type
|
||||
val receiverType = receiverKotlinType?.let(state.typeMapper::mapType)
|
||||
val boundReceiverJvmKotlinType = receiverType?.let { JvmKotlinType(receiverType, receiverKotlinType) }
|
||||
|
||||
if (isLambda && lambdaInfo!!.isPropertyReference) {
|
||||
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
|
||||
val info = lambdaInfo.propertyReferenceInfo
|
||||
PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
|
||||
true, info!!.getFunction, info.target, asmType,
|
||||
boundReceiverJvmKotlinType,
|
||||
lambdaInfo.functionWithBodyOrCallableReference, state, true
|
||||
)
|
||||
} else {
|
||||
FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, boundReceiverJvmKotlinType, null, true)
|
||||
}
|
||||
}
|
||||
is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
|
||||
else -> FunctionGenerationStrategy.FunctionDefault(state, expression as KtDeclarationWithBody)
|
||||
}
|
||||
|
||||
FunctionCodegen.generateMethodBody(
|
||||
adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen, state.jvmDefaultMode,
|
||||
state.languageVersionSettings.isReleaseCoroutines()
|
||||
)
|
||||
|
||||
if (isLambda) {
|
||||
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.reifiedTypeParametersUsages)
|
||||
}
|
||||
|
||||
return SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class FakeMemberCodegen(
|
||||
internal val delegate: MemberCodegen<*>,
|
||||
declaration: KtElement,
|
||||
codegenContext: FieldOwnerContext<*>,
|
||||
private val className: String,
|
||||
private val parentAsInnerClasses: List<ClassDescriptor>,
|
||||
private val isInlineLambdaCodegen: Boolean
|
||||
) : MemberCodegen<KtPureElement>(delegate as MemberCodegen<KtPureElement>, declaration, codegenContext) {
|
||||
|
||||
override fun generateDeclaration() {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun generateBody() {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun generateKotlinMetadataAnnotation() {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun getInlineNameGenerator(): NameGenerator {
|
||||
return delegate.inlineNameGenerator
|
||||
}
|
||||
|
||||
override //TODO: obtain name from context
|
||||
fun getClassName(): String {
|
||||
return className
|
||||
}
|
||||
|
||||
override fun addParentsToInnerClassesIfNeeded(innerClasses: MutableCollection<ClassDescriptor>) {
|
||||
if (isInlineLambdaCodegen) {
|
||||
super.addParentsToInnerClassesIfNeeded(innerClasses)
|
||||
} else {
|
||||
innerClasses.addAll(parentAsInnerClasses)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateAssertField() {
|
||||
delegate.generateAssertField()
|
||||
}
|
||||
}
|
||||
|
||||
override fun doCreateMethodNodeFromSource(
|
||||
callableDescriptor: FunctionDescriptor,
|
||||
jvmSignature: JvmMethodSignature,
|
||||
callDefault: Boolean,
|
||||
asmMethod: Method
|
||||
): SMAPAndMethodNode {
|
||||
val element = DescriptorToSourceUtils.descriptorToDeclaration(callableDescriptor)
|
||||
|
||||
if (!(element is KtNamedFunction || element is KtPropertyAccessor)) {
|
||||
throw IllegalStateException("Couldn't find declaration for function $callableDescriptor")
|
||||
}
|
||||
val inliningFunction = element as KtDeclarationWithBody?
|
||||
|
||||
val node = MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
DescriptorAsmUtil.getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0,
|
||||
asmMethod.name,
|
||||
asmMethod.descriptor, null, null
|
||||
)
|
||||
|
||||
//for maxLocals calculation
|
||||
val maxCalcAdapter = wrapWithMaxLocalCalc(node)
|
||||
val parentContext = context.parentContext ?: error("Context has no parent: " + context)
|
||||
val methodContext = parentContext.intoFunction(callableDescriptor)
|
||||
|
||||
val smap = if (callDefault) {
|
||||
val implementationOwner = state.typeMapper.mapImplementationOwner(callableDescriptor)
|
||||
val parentCodegen = FakeMemberCodegen(
|
||||
codegen.parentCodegen, inliningFunction!!, methodContext.parentContext as FieldOwnerContext<*>,
|
||||
implementationOwner.internalName,
|
||||
additionalInnerClasses,
|
||||
false
|
||||
)
|
||||
if (element !is KtNamedFunction) {
|
||||
throw IllegalStateException("Property accessors with default parameters not supported $callableDescriptor")
|
||||
}
|
||||
FunctionCodegen.generateDefaultImplBody(
|
||||
methodContext, callableDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||
inliningFunction as KtNamedFunction?, parentCodegen, asmMethod
|
||||
)
|
||||
SMAP(parentCodegen.orCreateSourceMapper.resultMappings)
|
||||
} else {
|
||||
generateMethodBody(maxCalcAdapter, callableDescriptor, methodContext, inliningFunction!!, jvmSignature, null)
|
||||
}
|
||||
maxCalcAdapter.visitMaxs(-1, -1)
|
||||
maxCalcAdapter.visitEnd()
|
||||
|
||||
return SMAPAndMethodNode(node, smap)
|
||||
}
|
||||
|
||||
override fun hasFinallyBlocks() = codegen.hasFinallyBlocks()
|
||||
|
||||
override fun generateFinallyBlocksIfNeeded(codegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label, target: Label?) {
|
||||
// TODO use the target label for non-local break/continue
|
||||
require(codegen is ExpressionCodegen)
|
||||
codegen.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel)
|
||||
}
|
||||
|
||||
override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) =
|
||||
ExpressionCodegen(
|
||||
finallyNode, codegen.frameMap, codegen.returnType,
|
||||
codegen.getContext(), codegen.state, codegen.parentCodegen
|
||||
).also {
|
||||
it.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)
|
||||
}
|
||||
|
||||
override fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean {
|
||||
return JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory)
|
||||
}
|
||||
|
||||
override fun isFinallyMarkerRequired(): Boolean = isFinallyMarkerRequired(codegen.getContext())
|
||||
|
||||
|
||||
override val compilationContextDescriptor
|
||||
get() = codegen.getContext().contextDescriptor
|
||||
|
||||
override val compilationContextFunctionDescriptor
|
||||
get() = codegen.getContext().functionDescriptor
|
||||
|
||||
override fun getContextLabels(): Map<String, Label?> {
|
||||
val context = codegen.getContext()
|
||||
val parentContext = context.parentContext
|
||||
val descriptor = if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) {
|
||||
parentContext.originalSuspendLambdaDescriptor!!
|
||||
} else context.contextDescriptor
|
||||
|
||||
val labels = InlineCodegen.getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor)
|
||||
return labels.associateWith { null } // TODO add break/continue labels
|
||||
}
|
||||
|
||||
fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) {
|
||||
context = getContext(
|
||||
functionDescriptor,
|
||||
functionDescriptor,
|
||||
state,
|
||||
DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)?.containingFile as? KtFile,
|
||||
additionalInnerClasses
|
||||
)
|
||||
}
|
||||
|
||||
override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) {
|
||||
org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor(callElement, state, stackTraceElement)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getContext(
|
||||
descriptor: DeclarationDescriptor,
|
||||
innerDescriptor: DeclarationDescriptor,
|
||||
state: GenerationState,
|
||||
sourceFile: KtFile?,
|
||||
additionalInners: MutableList<ClassDescriptor>
|
||||
): CodegenContext<*> {
|
||||
if (descriptor is PackageFragmentDescriptor) {
|
||||
//no inners
|
||||
return PackageContext(descriptor, state.rootContext, null, sourceFile)
|
||||
}
|
||||
|
||||
val container = descriptor.containingDeclaration ?: error("No container for descriptor: $descriptor")
|
||||
val containerContext = getContext(
|
||||
container,
|
||||
descriptor,
|
||||
state,
|
||||
sourceFile,
|
||||
additionalInners
|
||||
)
|
||||
|
||||
return when (descriptor) {
|
||||
is ScriptDescriptor -> {
|
||||
val earlierScripts = state.scriptSpecific.earlierScriptsForReplInterpreter
|
||||
containerContext.intoScript(
|
||||
descriptor,
|
||||
earlierScripts ?: emptyList(),
|
||||
descriptor as ClassDescriptor, state.typeMapper
|
||||
)
|
||||
}
|
||||
is ClassDescriptor -> {
|
||||
val kind =
|
||||
when {
|
||||
DescriptorUtils.isInterface(descriptor) &&
|
||||
innerDescriptor !is ClassDescriptor &&
|
||||
!innerDescriptor.isCallableMemberCompiledToJvmDefault(state.jvmDefaultMode) ->
|
||||
OwnerKind.DEFAULT_IMPLS
|
||||
else ->
|
||||
OwnerKind.IMPLEMENTATION
|
||||
}
|
||||
|
||||
additionalInners.addIfNotNull(
|
||||
InnerClassConsumer.classForInnerClassRecord(descriptor, kind == OwnerKind.DEFAULT_IMPLS)
|
||||
)
|
||||
|
||||
if (descriptor.isInlineClass()) {
|
||||
containerContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION, state)
|
||||
.intoClass(descriptor, OwnerKind.ERASED_INLINE_CLASS, state)
|
||||
} else {
|
||||
containerContext.intoClass(descriptor, kind, state)
|
||||
}
|
||||
}
|
||||
is FunctionDescriptor -> {
|
||||
containerContext.intoFunction(descriptor)
|
||||
}
|
||||
else -> {
|
||||
throw IllegalStateException("Couldn't build context for $descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMethodNode(
|
||||
owner: Type,
|
||||
bytes: ByteArray,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
isSuspend: Boolean,
|
||||
isMangled: Boolean
|
||||
): SMAPAndMethodNode {
|
||||
getMethodNode(owner, bytes, name, descriptor, isSuspend)?.let { return it }
|
||||
if (isMangled) {
|
||||
// Compatibility with old inline class ABI versions.
|
||||
val dashIndex = name.indexOf('-')
|
||||
val nameWithoutManglingSuffix = if (dashIndex > 0) name.substring(0, dashIndex) else name
|
||||
if (nameWithoutManglingSuffix != name) {
|
||||
getMethodNode(owner, bytes, nameWithoutManglingSuffix, descriptor, isSuspend)?.let { return it }
|
||||
}
|
||||
getMethodNode(owner, bytes, "$nameWithoutManglingSuffix-impl", descriptor, isSuspend)?.let { return it }
|
||||
}
|
||||
throw IllegalStateException("couldn't find inline method $owner.$name$descriptor")
|
||||
}
|
||||
|
||||
// If an `inline suspend fun` has a state machine, it should have a `$$forInline` version without one.
|
||||
private fun getMethodNode(owner: Type, bytes: ByteArray, name: String, descriptor: String, isSuspend: Boolean) =
|
||||
(if (isSuspend) getMethodNode(bytes, name + FOR_INLINE_SUFFIX, descriptor, owner) else null)
|
||||
?: getMethodNode(bytes, name, descriptor, owner)
|
||||
fun DeclarationDescriptor.isInlineOrInsideInline(): Boolean =
|
||||
if (this is FunctionDescriptor && isInline) true
|
||||
else containingDeclaration?.isInlineOrInsideInline() == true
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
* Copyright 2010-2019 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
|
||||
package org.jetbrains.kotlin.codegen.inline.coroutines
|
||||
|
||||
import com.intellij.util.ArrayUtil
|
||||
import jdk.internal.org.objectweb.asm.Type
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.coroutines.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
@@ -13,11 +14,12 @@ 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.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
@@ -91,8 +93,8 @@ class CoroutineTransformer(
|
||||
obtainClassBuilderForCoroutineState = { classBuilder },
|
||||
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 ?: "",
|
||||
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
|
||||
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
@@ -125,8 +127,8 @@ class CoroutineTransformer(
|
||||
createNewMethodFrom(node, name), node.access, name, node.desc, null, null,
|
||||
obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! },
|
||||
reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) },
|
||||
lineNumber = inliningContext.callSiteInfo.lineNumber,
|
||||
sourceFile = inliningContext.callSiteInfo.file?.name ?: "",
|
||||
lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber,
|
||||
sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "",
|
||||
languageVersionSettings = state.languageVersionSettings,
|
||||
shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization,
|
||||
containingClassInternalName = classBuilder.thisName,
|
||||
@@ -233,6 +235,24 @@ fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode) {
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot find the lambda in captured parameters: it came from object outside of the our reach:
|
||||
// this can happen when the lambda capture by non-transformed closure:
|
||||
// inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() }
|
||||
// inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() }
|
||||
// Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0)
|
||||
// Thus, transformed lambda from inlineMe, inlineMe3$$inlined$inlineMe2$1 contains the following bytecode
|
||||
// ALOAD 0
|
||||
// GETFIELD inlineMe2$1$invokeSuspend$$inlined$inlineMe$1.this$0 : LScratchKt$inlineMe2$1;
|
||||
// GETFIELD inlineMe2$1.$c : Lkotlin/jvm/functions/Function1;
|
||||
// Since inlineMe2's lambda is outside of reach of the inliner, find crossinline parameter from compilation context:
|
||||
fun FieldInsnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext: InliningContext): Boolean {
|
||||
var container: DeclarationDescriptor = inliningContext.root.sourceCompilerForInline.compilationContextFunctionDescriptor
|
||||
while (container !is ClassDescriptor) {
|
||||
container = container.containingDeclaration ?: return false
|
||||
}
|
||||
return isCapturedSuspendLambda(container, name, inliningContext.state.bindingContext)
|
||||
}
|
||||
|
||||
// Interpreter, that keeps track of captured functional arguments
|
||||
private class PossibleLambdaLoad(val insn: AbstractInsnNode) : BasicValue(AsmTypes.OBJECT_TYPE)
|
||||
|
||||
@@ -267,4 +287,4 @@ private class CapturedLambdaInterpreter : BasicInterpreter(Opcodes.API_VERSION)
|
||||
|
||||
override fun merge(v: BasicValue?, w: BasicValue?): BasicValue? =
|
||||
if (v is PossibleLambdaLoad && w is PossibleLambdaLoad && v.insn == w.insn) v else super.merge(v, w)
|
||||
}
|
||||
}
|
||||
@@ -60,15 +60,15 @@ fun extractDefaultLambdaOffsetAndDescriptor(
|
||||
}
|
||||
}
|
||||
|
||||
class ExtractedDefaultLambda(val type: Type, val capturedArgs: Array<Type>, val offset: Int, val needReification: Boolean)
|
||||
|
||||
fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
fun <T, R : DefaultLambda> expandMaskConditionsAndUpdateVariableNodes(
|
||||
node: MethodNode,
|
||||
maskStartIndex: Int,
|
||||
masks: List<Int>,
|
||||
methodHandlerIndex: Int,
|
||||
validOffsets: Collection<Int>
|
||||
): List<ExtractedDefaultLambda> {
|
||||
defaultLambdas: Map<Int, T>,
|
||||
lambdaConstructor: (Type, Array<Type>, T, Int, Boolean) -> R
|
||||
): List<R> {
|
||||
fun isMaskIndex(varIndex: Int): Boolean {
|
||||
return maskStartIndex <= varIndex && varIndex < maskStartIndex + masks.size
|
||||
}
|
||||
@@ -111,8 +111,7 @@ fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
val toDelete = linkedSetOf<AbstractInsnNode>()
|
||||
val toInsert = arrayListOf<Pair<AbstractInsnNode, AbstractInsnNode>>()
|
||||
|
||||
val extractable = conditions.filter { it.expandNotDelete && it.varIndex in validOffsets }
|
||||
val defaultLambdasInfo = extractDefaultLambdasInfo(extractable, toDelete, toInsert)
|
||||
val defaultLambdasInfo = extractDefaultLambdasInfo(conditions, defaultLambdas, toDelete, toInsert, lambdaConstructor)
|
||||
|
||||
val indexToVarNode = node.localVariables?.filter { it.index < maskStartIndex }?.associateBy { it.index } ?: emptyMap()
|
||||
conditions.forEach {
|
||||
@@ -132,7 +131,7 @@ fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
}
|
||||
|
||||
node.localVariables.removeIf {
|
||||
(it.start in toDelete && it.end in toDelete) || validOffsets.contains(it.index)
|
||||
(it.start in toDelete && it.end in toDelete) || defaultLambdas.contains(it.index)
|
||||
}
|
||||
|
||||
node.remove(toDelete)
|
||||
@@ -140,12 +139,16 @@ fun expandMaskConditionsAndUpdateVariableNodes(
|
||||
return defaultLambdasInfo
|
||||
}
|
||||
|
||||
private fun extractDefaultLambdasInfo(
|
||||
|
||||
private fun <T, R : DefaultLambda> extractDefaultLambdasInfo(
|
||||
conditions: List<Condition>,
|
||||
defaultLambdas: Map<Int, T>,
|
||||
toDelete: MutableCollection<AbstractInsnNode>,
|
||||
toInsert: MutableList<Pair<AbstractInsnNode, AbstractInsnNode>>
|
||||
): List<ExtractedDefaultLambda> {
|
||||
return conditions.map {
|
||||
toInsert: MutableList<Pair<AbstractInsnNode, AbstractInsnNode>>,
|
||||
lambdaConstructor: (Type, Array<Type>, T, Int, Boolean) -> R
|
||||
): List<R> {
|
||||
val defaultLambdaConditions = conditions.filter { it.expandNotDelete && defaultLambdas.contains(it.varIndex) }
|
||||
return defaultLambdaConditions.map {
|
||||
val varAssignmentInstruction = it.varInsNode!!
|
||||
var instanceInstuction = varAssignmentInstruction.previous
|
||||
if (instanceInstuction is TypeInsnNode && instanceInstuction.opcode == Opcodes.CHECKCAST) {
|
||||
@@ -187,7 +190,7 @@ private fun extractDefaultLambdasInfo(
|
||||
|
||||
toInsert.add(varAssignmentInstruction to defaultLambdaFakeCallStub(argTypes, it.varIndex))
|
||||
|
||||
ExtractedDefaultLambda(owner, argTypes, it.varIndex, needReification)
|
||||
lambdaConstructor(owner, argTypes, defaultLambdas[it.varIndex]!!, it.varIndex, needReification)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -347,42 +347,8 @@ internal val AbstractInsnNode?.insnText: String
|
||||
return sw.toString().trim()
|
||||
}
|
||||
|
||||
fun AbstractInsnNode?.insnText(insnList: InsnList): String {
|
||||
if (this == null) return "<null>"
|
||||
|
||||
fun AbstractInsnNode.indexOf() =
|
||||
insnList.indexOf(this)
|
||||
|
||||
fun LabelNode.labelText() =
|
||||
"L#${this.indexOf()}"
|
||||
|
||||
return when (this) {
|
||||
is LabelNode ->
|
||||
labelText()
|
||||
is JumpInsnNode ->
|
||||
"$insnOpcodeText ${label.labelText()}"
|
||||
is LookupSwitchInsnNode ->
|
||||
"$insnOpcodeText " +
|
||||
this.keys.zip(this.labels).joinToString(prefix = "[", postfix = "]") { (key, label) -> "$key:${label.labelText()}" }
|
||||
is TableSwitchInsnNode ->
|
||||
"$insnOpcodeText " +
|
||||
(min..max).zip(this.labels).joinToString(prefix = "[", postfix = "]") { (key, label) -> "$key:${label.labelText()}" }
|
||||
else ->
|
||||
insnText
|
||||
}
|
||||
}
|
||||
|
||||
internal val AbstractInsnNode?.insnOpcodeText: String
|
||||
get() = when (this) {
|
||||
null -> "null"
|
||||
is LabelNode -> "LABEL"
|
||||
is LineNumberNode -> "LINENUMBER"
|
||||
is FrameNode -> "FRAME"
|
||||
else -> Printer.OPCODES[opcode]
|
||||
}
|
||||
|
||||
internal fun TryCatchBlockNode.text(insns: InsnList): String =
|
||||
"[${insns.indexOf(start)} .. ${insns.indexOf(end)} -> ${insns.indexOf(handler)}]"
|
||||
get() = if (this == null) "null" else Printer.OPCODES[opcode]
|
||||
|
||||
internal fun loadClassBytesByInternalName(state: GenerationState, internalName: String): ByteArray {
|
||||
//try to find just compiled classes then in dependencies
|
||||
@@ -613,8 +579,8 @@ class InlineOnlySmapSkipper(codegen: BaseExpressionCodegen) {
|
||||
const val LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER = 1
|
||||
}
|
||||
|
||||
fun onInlineLambdaStart(mv: MethodVisitor, lambda: MethodNode, smap: SourceMapper) {
|
||||
val firstLine = lambda.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
|
||||
fun onInlineLambdaStart(mv: MethodVisitor, info: LambdaInfo, smap: SourceMapper) {
|
||||
val firstLine = info.node.node.instructions.asSequence().mapNotNull { it as? LineNumberNode }.firstOrNull()?.line ?: -1
|
||||
if (callLineNumber >= 0 && firstLine == callLineNumber) {
|
||||
// We want the debugger to be able to break both on the inline call itself, plus on each
|
||||
// invocation of the inline lambda passed to it. For that to happen there needs to be at least
|
||||
|
||||
@@ -14,9 +14,8 @@ import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForCoroutineConte
|
||||
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
|
||||
import org.jetbrains.kotlin.codegen.isBuiltinAlwaysEnabledAssert
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.TypeOfChecker
|
||||
@@ -27,42 +26,31 @@ import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
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.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
fun generateInlineIntrinsicForIr(languageVersionSettings: LanguageVersionSettings, descriptor: FunctionDescriptor): SMAPAndMethodNode? =
|
||||
when {
|
||||
// TODO: implement these as codegen intrinsics (see IrIntrinsicMethods)
|
||||
internal fun generateInlineIntrinsic(
|
||||
state: GenerationState,
|
||||
descriptor: FunctionDescriptor,
|
||||
typeParameters: List<TypeParameterMarker>?,
|
||||
typeSystem: TypeSystemCommonBackendContext
|
||||
): MethodNode? {
|
||||
val languageVersionSettings = state.languageVersionSettings
|
||||
|
||||
return when {
|
||||
isSpecialEnumMethod(descriptor) ->
|
||||
createSpecialEnumMethodBody(descriptor.name.asString(), typeParameters!!.single(), typeSystem)
|
||||
TypeOfChecker.isTypeOf(descriptor) ->
|
||||
typeSystem.createTypeOfMethodBody(typeParameters!!.single())
|
||||
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(languageVersionSettings, descriptor) ?: when {
|
||||
isSpecialEnumMethod(descriptor) ->
|
||||
createSpecialEnumMethodBody(descriptor.name.asString(), descriptor.original.typeParameters.single(), typeSystem)
|
||||
TypeOfChecker.isTypeOf(descriptor) ->
|
||||
typeSystem.createTypeOfMethodBody(descriptor.original.typeParameters.single())
|
||||
descriptor.isBuiltinAlwaysEnabledAssert() ->
|
||||
createMethodNodeForAlwaysEnabledAssert(descriptor)
|
||||
descriptor is FictitiousArrayConstructor ->
|
||||
IntrinsicArrayConstructors.generateArrayConstructorBody(asmMethod)
|
||||
IntrinsicArrayConstructors.isArrayOf(descriptor) ->
|
||||
IntrinsicArrayConstructors.generateArrayOfBody(asmMethod)
|
||||
IntrinsicArrayConstructors.isEmptyArray(descriptor) ->
|
||||
IntrinsicArrayConstructors.generateEmptyArrayBody(asmMethod)
|
||||
else -> null
|
||||
}?.let { SMAPAndMethodNode(it, SMAP(listOf())) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSpecialEnumMethod(descriptor: FunctionDescriptor): Boolean {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
@@ -38,15 +37,15 @@ private fun TypeSystemCommonBackendContext.putTypeOfReifiedTypeParameter(
|
||||
v.aconst(null)
|
||||
}
|
||||
|
||||
fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
|
||||
internal fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
|
||||
v: InstructionAdapter, type: KT, intrinsicsSupport: ReifiedTypeInliner.IntrinsicsSupport<KT>
|
||||
) {
|
||||
val typeParameter = type.typeConstructor().getTypeParameterClassifier()
|
||||
if (typeParameter != null) {
|
||||
if (!doesTypeContainTypeParametersWithRecursiveBounds(type)) {
|
||||
intrinsicsSupport.reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameter.getName())
|
||||
v.aconst(null)
|
||||
return
|
||||
throw UnsupportedOperationException(
|
||||
"Non-reified type parameters with recursive bounds are not supported yet: ${typeParameter.getName()}"
|
||||
)
|
||||
}
|
||||
|
||||
generateNonReifiedTypeParameter(v, typeParameter, intrinsicsSupport)
|
||||
@@ -90,27 +89,6 @@ fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateTypeOf(
|
||||
}
|
||||
|
||||
v.invokestatic(REFLECTION, methodName, signature, false)
|
||||
|
||||
if (intrinsicsSupport.toKotlinType(type).isSuspendFunctionType) {
|
||||
intrinsicsSupport.reportSuspendTypeUnsupported()
|
||||
}
|
||||
|
||||
if (intrinsicsSupport.state.stableTypeOf) {
|
||||
if (intrinsicsSupport.isMutableCollectionType(type)) {
|
||||
v.invokestatic(REFLECTION, "mutableCollectionType", Type.getMethodDescriptor(K_TYPE, K_TYPE), false)
|
||||
} else if (type.typeConstructor().isNothingConstructor()) {
|
||||
v.invokestatic(REFLECTION, "nothingType", Type.getMethodDescriptor(K_TYPE, K_TYPE), false)
|
||||
}
|
||||
|
||||
if (type.isFlexible()) {
|
||||
// If this is a flexible type, we've just generated its lower bound and have it on the stack.
|
||||
// Let's generate the upper bound now and call the method that takes lower and upper bound and constructs a flexible KType.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
generateTypeOf(v, type.upperBoundIfFlexible() as KT, intrinsicsSupport)
|
||||
|
||||
v.invokestatic(REFLECTION, "platformType", Type.getMethodDescriptor(K_TYPE, K_TYPE, K_TYPE), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateNonReifiedTypeParameter(
|
||||
@@ -125,7 +103,7 @@ private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateNonRe
|
||||
TypeVariance.OUT -> KVariance.OUT
|
||||
}
|
||||
v.getstatic(K_VARIANCE.internalName, variance.name, K_VARIANCE.descriptor)
|
||||
v.iconst(if (typeParameter.isReified()) 1 else 0)
|
||||
v.aconst(typeParameter.isReified())
|
||||
v.invokestatic(
|
||||
REFLECTION, "typeParameter",
|
||||
Type.getMethodDescriptor(K_TYPE_PARAMETER, OBJECT_TYPE, JAVA_STRING_TYPE, K_VARIANCE, Type.BOOLEAN_TYPE),
|
||||
@@ -141,11 +119,11 @@ private fun <KT : KotlinTypeMarker> TypeSystemCommonBackendContext.generateNonRe
|
||||
if (bounds.size == 1) {
|
||||
generateTypeOf(v, bounds.single(), intrinsicsSupport)
|
||||
} else {
|
||||
v.iconst(bounds.size)
|
||||
v.aconst(bounds.size)
|
||||
v.newarray(K_TYPE)
|
||||
for ((i, bound) in bounds.withIndex()) {
|
||||
v.dup()
|
||||
v.iconst(i)
|
||||
v.aconst(i)
|
||||
generateTypeOf(v, bound, intrinsicsSupport)
|
||||
v.astore(K_TYPE)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||
import org.jetbrains.kotlin.resolve.underlyingRepresentation
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -24,8 +26,9 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
|
||||
fun KotlinType.isInlineClassWithUnderlyingTypeAnyOrAnyN(): Boolean {
|
||||
val classDescriptor = constructor.declarationDescriptor
|
||||
return classDescriptor is ClassDescriptor && classDescriptor.inlineClassRepresentation?.underlyingType?.isAnyOrNullableAny() == true
|
||||
if (!isInlineClassType()) return false
|
||||
val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
return classDescriptor.underlyingRepresentation()?.type?.isAnyOrNullableAny() == true
|
||||
}
|
||||
|
||||
fun CallableDescriptor.isGenericParameter(): Boolean {
|
||||
@@ -76,4 +79,4 @@ fun classFileContainsMethod(classId: ClassId, state: GenerationState, method: Me
|
||||
}
|
||||
}, ClassReader.SKIP_FRAMES)
|
||||
return found
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -34,15 +30,6 @@ import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
internal object IntrinsicArrayConstructors {
|
||||
fun isArrayOf(descriptor: FunctionDescriptor): Boolean =
|
||||
descriptor.name.asString() == "arrayOf" && descriptor.containingDeclaration.isBuiltInsPackage
|
||||
|
||||
fun isEmptyArray(descriptor: FunctionDescriptor): Boolean =
|
||||
descriptor.name.asString() == "emptyArray" && descriptor.containingDeclaration.isBuiltInsPackage
|
||||
|
||||
private val DeclarationDescriptor.isBuiltInsPackage: Boolean
|
||||
get() = this is PackageFragmentDescriptor && fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME
|
||||
|
||||
fun generateArrayConstructorBody(method: Method): MethodNode {
|
||||
val node = MethodNode(
|
||||
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter
|
||||
import org.jetbrains.kotlin.psi.KtClassLiteralExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.DOUBLE_COLON_LHS
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.codegen.optimization
|
||||
import org.jetbrains.kotlin.codegen.inline.insnText
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeAll
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
@@ -32,28 +31,15 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
||||
private val deadCodeElimination = DeadCodeEliminationMethodTransformer()
|
||||
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
if (!methodNode.hasOptimizableConditions()) {
|
||||
return
|
||||
}
|
||||
do {
|
||||
val changes = ConstantConditionsOptimization(internalClassName, methodNode).run()
|
||||
if (changes) deadCodeElimination.transform(internalClassName, methodNode)
|
||||
} while (changes)
|
||||
}
|
||||
|
||||
private fun MethodNode.hasOptimizableConditions(): Boolean {
|
||||
val insns = instructions.toArray()
|
||||
return insns.any { it.isIntJump() } && insns.any { it.isIntConst() }
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isIntConst() =
|
||||
opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5 || opcode == Opcodes.BIPUSH || opcode == Opcodes.SIPUSH ||
|
||||
(opcode == Opcodes.LDC && this is LdcInsnNode && cst is Int)
|
||||
|
||||
private fun AbstractInsnNode.isIntJump() =
|
||||
opcode in Opcodes.IFEQ..Opcodes.IFLE || opcode in Opcodes.IF_ICMPEQ..Opcodes.IF_ICMPLE
|
||||
|
||||
private class ConstantConditionsOptimization(val internalClassName: String, val methodNode: MethodNode) {
|
||||
fun run(): Boolean {
|
||||
val actions = collectRewriteActions()
|
||||
@@ -63,21 +49,11 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
||||
|
||||
private fun collectRewriteActions(): List<() -> Unit> =
|
||||
arrayListOf<() -> Unit>().also { actions ->
|
||||
val deadCode = ArrayList<AbstractInsnNode>()
|
||||
|
||||
val frames = analyze(internalClassName, methodNode, ConstantPropagationInterpreter())
|
||||
val insns = methodNode.instructions.toArray()
|
||||
|
||||
for (i in frames.indices) {
|
||||
val insn = insns[i]
|
||||
val frame = frames[i]
|
||||
|
||||
if (frame == null && insn !is LabelNode) {
|
||||
deadCode.add(insn)
|
||||
continue
|
||||
}
|
||||
|
||||
if (insn !is JumpInsnNode) continue
|
||||
val frame = frames[i] ?: continue
|
||||
val insn = insns[i] as? JumpInsnNode ?: continue
|
||||
when (insn.opcode) {
|
||||
in Opcodes.IFEQ..Opcodes.IFLE ->
|
||||
tryRewriteComparisonWithZero(insn, frame, actions)
|
||||
@@ -85,12 +61,6 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
|
||||
tryRewriteBinaryComparison(insn, frame, actions)
|
||||
}
|
||||
}
|
||||
|
||||
if (deadCode.isNotEmpty()) {
|
||||
actions.add {
|
||||
methodNode.instructions.removeAll(deadCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryRewriteComparisonWithZero(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user