mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-17 00:21:29 +00:00
Compare commits
3 Commits
entry_poin
...
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/
|
||||
|
||||
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>
|
||||
|
||||
672
ChangeLog.md
672
ChangeLog.md
@@ -1,677 +1,5 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 1.5.10
|
||||
|
||||
### Compiler
|
||||
|
||||
#### Fixes
|
||||
|
||||
- [`KT-41078`](https://youtrack.jetbrains.com/issue/KT-41078) Incorrect type substitution in contracts with type parameters
|
||||
- [`KT-44770`](https://youtrack.jetbrains.com/issue/KT-44770) JVM / IR: "IllegalArgumentException: Unrecognized Type: [null]" Jackson doesn't recognize type
|
||||
- [`KT-45084`](https://youtrack.jetbrains.com/issue/KT-45084) JVM IR: "NoSuchElementException: Sequence contains no element matching the predicate" when inline class is passed to lambda with >22 parameters
|
||||
- [`KT-45779`](https://youtrack.jetbrains.com/issue/KT-45779) JVM / IR: java.lang.NoSuchMethodError: 'int java.lang.Integer.plus(int)' caused by function reference
|
||||
- [`KT-45941`](https://youtrack.jetbrains.com/issue/KT-45941) JVM IR: local functions use generic type parameters of the outer class in the bytecode, which breaks Bytebuddy and MockK
|
||||
- [`KT-46149`](https://youtrack.jetbrains.com/issue/KT-46149) Generate synthetic classes for SAM adapters with erased instead of generic supertype
|
||||
- [`KT-46189`](https://youtrack.jetbrains.com/issue/KT-46189) JVM IR: tailrec function with capturing lambda in default parameter value leads to NoSuchMethodError at runtime
|
||||
- [`KT-46214`](https://youtrack.jetbrains.com/issue/KT-46214) JVM / IR: "IllegalStateException: No mapping for symbol: VALUE_PARAMETER INSTANCE_RECEIVER" on a suspend function in an inner class
|
||||
- [`KT-46238`](https://youtrack.jetbrains.com/issue/KT-46238) JVM IR: BootstrapMethodError in JDK 11+ on intersection type passed in arguments of SAM adapter where SAM interface's type parameter has a non-trivial upper bound
|
||||
- [`KT-46259`](https://youtrack.jetbrains.com/issue/KT-46259) JVM IR: local function for adapted function reference is not declared as ACC_SYNTHETIC
|
||||
- [`KT-46284`](https://youtrack.jetbrains.com/issue/KT-46284) JVM IR: "Unbound private symbol IrClassSymbol" on class reference to script class
|
||||
- [`KT-46402`](https://youtrack.jetbrains.com/issue/KT-46402) IllegalAccessError: "CapturedLambdaInterpreter (in unnamed module @0x71b06418) cannot access class jdk.internal.org.objectweb.asm.Type" caused by inline function with a suspend parameter in Maven project
|
||||
- [`KT-46408`](https://youtrack.jetbrains.com/issue/KT-46408) JVM IR: BootstrapMethodError due to missing bridge for subclass of generic Java interface
|
||||
- [`KT-46426`](https://youtrack.jetbrains.com/issue/KT-46426) JVM IR: Corrupted .class file when passing Array constructor reference as (inline) lambda
|
||||
- [`KT-46455`](https://youtrack.jetbrains.com/issue/KT-46455) OOM on parsing invalid code with string interpolation
|
||||
- [`KT-46503`](https://youtrack.jetbrains.com/issue/KT-46503) JVM IR: "AssertionError: Unexpected variance in super type argument: out @1"
|
||||
- [`KT-46505`](https://youtrack.jetbrains.com/issue/KT-46505) JVM IR: NullPointerException caused by a callable reference with nullable inline value class parameter
|
||||
- [`KT-46512`](https://youtrack.jetbrains.com/issue/KT-46512) JVM / IR: NoSuchMethodError on SAM conversion of a function reference
|
||||
- [`KT-46515`](https://youtrack.jetbrains.com/issue/KT-46515) IndexOutOfBoundsException: "Empty list doesn't contain element at index 0." on bad variable name in 1.5.0
|
||||
- [`KT-46516`](https://youtrack.jetbrains.com/issue/KT-46516) JVM IR: "AnalyzerException: Expected I, but found R" on subclassing AbstractMutableList<Int>
|
||||
- [`KT-46524`](https://youtrack.jetbrains.com/issue/KT-46524) Cannot use unsigned literals with api-version < 1.5 even with opt-in
|
||||
- [`KT-46537`](https://youtrack.jetbrains.com/issue/KT-46537) JVM / IR: "IllegalStateException: No noarg super constructor for CLASS" caused by "No-arg" plugin with annotation on child class
|
||||
- [`KT-46540`](https://youtrack.jetbrains.com/issue/KT-46540) JVM / IR: AnalyzerException: Expected an object reference, but found J caused by java.function.Supplier
|
||||
- [`KT-46554`](https://youtrack.jetbrains.com/issue/KT-46554) JVM IR: "IllegalStateException: No mapping for symbol: VAR IR_TEMPORARY_VARIABLE" with value class constructor delegation call
|
||||
- [`KT-46555`](https://youtrack.jetbrains.com/issue/KT-46555) JVM IR: IllegalAccessError when using Java method reference
|
||||
- [`KT-46562`](https://youtrack.jetbrains.com/issue/KT-46562) Kotlin 1.5.0 generates non-serializable lambdas when they should be serializable
|
||||
- [`KT-46568`](https://youtrack.jetbrains.com/issue/KT-46568) JVM IR: "AssertionError: IrCall expected inside JvmStatic wrapper" on compiling protected static function with return type Nothing inside companion object of abstract class
|
||||
- [`KT-46574`](https://youtrack.jetbrains.com/issue/KT-46574) JVM / IR: ClassCastException caused by runBlocking awaiting call while returning Kotlin.Result type.
|
||||
- [`KT-46579`](https://youtrack.jetbrains.com/issue/KT-46579) JVM IR: "IllegalArgumentException: Sequence contains more than one matching element" for Java enum with overloaded values() static method
|
||||
- [`KT-46584`](https://youtrack.jetbrains.com/issue/KT-46584) JVM IR: Intrinsics.needClassReification (UnsupportedOperationException thrown). Property delegate provider crossinline lambda inlining/reification issue
|
||||
- [`KT-46751`](https://youtrack.jetbrains.com/issue/KT-46751) JVM / IR:"ClassCastException: java.lang.String cannot be cast to java.lang.Void" in extension function in Kotlin 1.5
|
||||
|
||||
### IDE
|
||||
|
||||
- [`KT-45981`](https://youtrack.jetbrains.com/issue/KT-45981) failed to analyze: java.lang.AssertionError: diagnostic callback has been already registered: Code analysis get stuck in AS 2020.3.1.14 & Kotlin v1.5.0-M2
|
||||
- [`KT-46622`](https://youtrack.jetbrains.com/issue/KT-46622) 60+ second freezes with Kotlin plugin 1.5.0: GetModuleInfoKt.findJvmStdlibAcrossDependencies
|
||||
|
||||
### IDE. Gradle Integration
|
||||
|
||||
- [`KT-46417`](https://youtrack.jetbrains.com/issue/KT-46417) [UNRESOLVED_REFERENCE] For project to project dependencies of native platform test source sets
|
||||
|
||||
### Libraries
|
||||
|
||||
- [`KT-46280`](https://youtrack.jetbrains.com/issue/KT-46280) JvmRecord annotation missing constructor in common
|
||||
|
||||
### Middle-end. IR
|
||||
|
||||
- [`KT-44013`](https://youtrack.jetbrains.com/issue/KT-44013) NPE: When calling constructor of a function type while inheriting from it, despite it's an interface
|
||||
|
||||
### Tools. Android Extensions
|
||||
|
||||
- [`KT-46590`](https://youtrack.jetbrains.com/issue/KT-46590) Kotlin Android Extensions 1.5.0 generates bad writeToParcel() method for nullable Array types
|
||||
|
||||
### Tools. Gradle
|
||||
|
||||
- [`KT-41142`](https://youtrack.jetbrains.com/issue/KT-41142) Kotlin version conflict when using Kotlin Gradle plugins in pre-compiled script plugin
|
||||
- [`KT-46353`](https://youtrack.jetbrains.com/issue/KT-46353) Optimizations disabled in Gradle 7 for KAPT when generating sources
|
||||
- [`KT-46368`](https://youtrack.jetbrains.com/issue/KT-46368) Memory leak with 1.5.0-RC when building with Gradle
|
||||
- [`KT-46689`](https://youtrack.jetbrains.com/issue/KT-46689) Track -Xuse-old-backend flag usage
|
||||
|
||||
### Tools. Gradle. JS
|
||||
|
||||
- [`KT-46006`](https://youtrack.jetbrains.com/issue/KT-46006) KJS \ Gradle: Task without declaring an explicit or implicit dependency on `jsGenerateExternalsIntegrated` in Gradle 7
|
||||
- [`KT-46162`](https://youtrack.jetbrains.com/issue/KT-46162) KJS: Exported items unavailable on dev server
|
||||
- [`KT-46331`](https://youtrack.jetbrains.com/issue/KT-46331) KJS: With `kotlin.js.webpack.major.version=4` browserXRun tasks fail
|
||||
|
||||
### Tools. Parcelize
|
||||
|
||||
- [`KT-46567`](https://youtrack.jetbrains.com/issue/KT-46567) Kotlin 1.5.0 parcelize compilation fails in new backend when using TypeParceller with nested generics
|
||||
|
||||
### Tools. kapt
|
||||
|
||||
- [`KT-45532`](https://youtrack.jetbrains.com/issue/KT-45532) Do not create Kapt stubs directory during configuration time
|
||||
|
||||
|
||||
## 1.5.0
|
||||
|
||||
### Backend. Native
|
||||
|
||||
- [`KT-42053`](https://youtrack.jetbrains.com/issue/KT-42053) Support compiler caches for linux_x64
|
||||
- [`KT-43690`](https://youtrack.jetbrains.com/issue/KT-43690) Support compiler caches for ios_arm64
|
||||
|
||||
### Backend. IR
|
||||
|
||||
- [`KT-42684`](https://youtrack.jetbrains.com/issue/KT-42684) StackOverflowError on recursive inline arguments in inline fun
|
||||
|
||||
### Compiler
|
||||
|
||||
#### New Features
|
||||
|
||||
- [`KT-28791`](https://youtrack.jetbrains.com/issue/KT-28791) Kotlin serialization with inline classes
|
||||
- [`KT-30222`](https://youtrack.jetbrains.com/issue/KT-30222) Support JVM target version selection in Kotlin bytecode tool window
|
||||
- [`KT-41884`](https://youtrack.jetbrains.com/issue/KT-41884) Support 'file' target for JvmSynthetic annotation
|
||||
- [`KT-43677`](https://youtrack.jetbrains.com/issue/KT-43677) Support for Java records
|
||||
- [`KT-43920`](https://youtrack.jetbrains.com/issue/KT-43920) Support loading binary Java annotations on fields
|
||||
- [`KT-44278`](https://youtrack.jetbrains.com/issue/KT-44278) Generate SAM-converted lambdas and function references using 'invokedynamic' on JDK 1.8+
|
||||
- [`KT-44650`](https://youtrack.jetbrains.com/issue/KT-44650) Deprecate JVM target version 1.6
|
||||
- [`KT-44787`](https://youtrack.jetbrains.com/issue/KT-44787) Suspend functions in fun interfaces
|
||||
- [`KT-44865`](https://youtrack.jetbrains.com/issue/KT-44865) Allow to declare protected constructors in sealed classes
|
||||
- [`KT-44869`](https://youtrack.jetbrains.com/issue/KT-44869) Compiling sealed interface with version less than 1.5: error message from future could be provided
|
||||
|
||||
|
||||
#### Performance Improvements
|
||||
|
||||
- [`KT-6336`](https://youtrack.jetbrains.com/issue/KT-6336) Optimize generation of local functions
|
||||
- [`KT-7307`](https://youtrack.jetbrains.com/issue/KT-7307) Optimize infix call of String.plus
|
||||
- [`KT-18692`](https://youtrack.jetbrains.com/issue/KT-18692) Optimize '<optimizable_range> step x' for-in loop
|
||||
- [`KT-19978`](https://youtrack.jetbrains.com/issue/KT-19978) Inefficient bytecode generated for function references undergoing Java SAM conversion
|
||||
- [`KT-23565`](https://youtrack.jetbrains.com/issue/KT-23565) OperationsMapGenerated.kt generates unreasonable amount of bytecode
|
||||
- [`KT-23825`](https://youtrack.jetbrains.com/issue/KT-23825) Tail suspend call utilizing elvis operator does not take advantage of suspend tail call optimization
|
||||
- [`KT-23826`](https://youtrack.jetbrains.com/issue/KT-23826) A suspend function on the right side of a returned || condition is not tail call optimized
|
||||
- [`KT-25348`](https://youtrack.jetbrains.com/issue/KT-25348) No compile time unsigned integer conversion when using hex literal
|
||||
- [`KT-26060`](https://youtrack.jetbrains.com/issue/KT-26060) Support a compiler mode to compile lambda expressions using `invokedynamic` instruction
|
||||
- [`KT-26590`](https://youtrack.jetbrains.com/issue/KT-26590) Do not generate create method for suspend lambdas if its arity >= 2
|
||||
- [`KT-27427`](https://youtrack.jetbrains.com/issue/KT-27427) Optimize nullable check introduced with 'as' cast
|
||||
- [`KT-28246`](https://youtrack.jetbrains.com/issue/KT-28246) Redundant boxing/unboxing isn't eliminated by the compiler in case of inline classes and javaClass intrinsic
|
||||
- [`KT-30605`](https://youtrack.jetbrains.com/issue/KT-30605) Constant folding doesn't evaluate inv() function
|
||||
- [`KT-36845`](https://youtrack.jetbrains.com/issue/KT-36845) Generate enum-based TABLESWITCH/LOOKUPSWITCH on a value with smart cast to enum in JVM_IR
|
||||
- [`KT-39585`](https://youtrack.jetbrains.com/issue/KT-39585) JVM BE generates redundant accessor calls when accessing static final field lifted from companion
|
||||
- [`KT-40886`](https://youtrack.jetbrains.com/issue/KT-40886) Old JVM BE unspills ACONST_NULL from continuation
|
||||
- [`KT-42621`](https://youtrack.jetbrains.com/issue/KT-42621) Kotlin binary size considerably larger for code extensively using stream API
|
||||
- [`KT-44153`](https://youtrack.jetbrains.com/issue/KT-44153) NI: Low Memory and IntelliJ hangs when quotes in split() are missed
|
||||
- [`KT-45410`](https://youtrack.jetbrains.com/issue/KT-45410) JVM / IR: Extreme performance regression on arithmetic operations inside a loop
|
||||
|
||||
#### Fixes
|
||||
|
||||
- [`KT-6007`](https://youtrack.jetbrains.com/issue/KT-6007) Support changed return type of inlined generic function when lambda returns anonymous object
|
||||
- [`KT-6055`](https://youtrack.jetbrains.com/issue/KT-6055) Failed invoke plus assign on array element accessed via several args through local get/set convention extensions
|
||||
- [`KT-6879`](https://youtrack.jetbrains.com/issue/KT-6879) CompilationException when local classes hierarchy is placed within other local or inner declaration
|
||||
- [`KT-8120`](https://youtrack.jetbrains.com/issue/KT-8120) NoSuchMethodError on local class constructor call inside a local class
|
||||
- [`KT-8199`](https://youtrack.jetbrains.com/issue/KT-8199) "Cannot pop operand off an empty stack" for local class using a captured variable as default value for constructor parameter
|
||||
- [`KT-10835`](https://youtrack.jetbrains.com/issue/KT-10835) "AssertionError: Non-outer parameter incorrectly mapped to outer" when inlining object literal extending inner class
|
||||
- [`KT-12790`](https://youtrack.jetbrains.com/issue/KT-12790) Don't generate synthetic accessors for private inline function/properties
|
||||
- [`KT-13213`](https://youtrack.jetbrains.com/issue/KT-13213) IllegalArgumentException in ByteVector.putUTF8 on attempt to compile file with moderately long string literal
|
||||
- [`KT-14628`](https://youtrack.jetbrains.com/issue/KT-14628) "UnsupportedOperationException: Don't know how to generate outer expression" for nested class inheriting from inner class with a companion object
|
||||
- [`KT-14833`](https://youtrack.jetbrains.com/issue/KT-14833) JVM internal error: Augment assignment and increment are not supported for local delegated properties and inline properties
|
||||
- [`KT-15403`](https://youtrack.jetbrains.com/issue/KT-15403) Suspend operator get wrong code generated by BE (NoSuchMethodError)
|
||||
- [`KT-15404`](https://youtrack.jetbrains.com/issue/KT-15404) Suspend operator set wrong code generated
|
||||
- [`KT-16084`](https://youtrack.jetbrains.com/issue/KT-16084) Proguard can't find enclosing class of let closure inside apply closure
|
||||
- [`KT-16151`](https://youtrack.jetbrains.com/issue/KT-16151) Internal compiler error when using plusAssign operator with mutable map
|
||||
- [`KT-16221`](https://youtrack.jetbrains.com/issue/KT-16221) Support in/!in suspend operators
|
||||
- [`KT-16282`](https://youtrack.jetbrains.com/issue/KT-16282) "Cannot pop operand off an empty stack" for plusAssign with default parameters in setter operator
|
||||
- [`KT-16445`](https://youtrack.jetbrains.com/issue/KT-16445) `java.lang.VerifyError: Bad type on operand stack` when delegating an interface through a private reified function inside an object
|
||||
- [`KT-16520`](https://youtrack.jetbrains.com/issue/KT-16520) Invalid bytecode semantics for set call by convention with default parameters
|
||||
- [`KT-16567`](https://youtrack.jetbrains.com/issue/KT-16567) Inliner creates redundant objects on source inlining
|
||||
- [`KT-16752`](https://youtrack.jetbrains.com/issue/KT-16752) Delegating function interface to function reference does not work
|
||||
- [`KT-17554`](https://youtrack.jetbrains.com/issue/KT-17554) Incorrect cast to Unit generated on annotated when-expression with a single-branch if inside
|
||||
- [`KT-17738`](https://youtrack.jetbrains.com/issue/KT-17738) Java cannot extend class implementing kotlin.collections.Map
|
||||
- [`KT-17753`](https://youtrack.jetbrains.com/issue/KT-17753) Strange behavior of if and return statements
|
||||
- [`KT-18583`](https://youtrack.jetbrains.com/issue/KT-18583) "ISE: Recursive call in a lazy value" for generic sealed class with nested subclass in a `when(this)` with inferred return type
|
||||
- [`KT-19861`](https://youtrack.jetbrains.com/issue/KT-19861) "IllegalStateException: Label wasn't found during iterating through instructions" for `plusAssign` with safe call
|
||||
- [`KT-20306`](https://youtrack.jetbrains.com/issue/KT-20306) Make 'when' over an 'expect' enum class non-exhaustive
|
||||
- [`KT-20869`](https://youtrack.jetbrains.com/issue/KT-20869) kotlin.jvm.internal.DefaultConstructorMarker should be public
|
||||
- [`KT-20996`](https://youtrack.jetbrains.com/issue/KT-20996) IllegalStateException: Cannot get FQ name of local class: class <no name provided> in metadata serialization for common code
|
||||
- [`KT-21014`](https://youtrack.jetbrains.com/issue/KT-21014) Incorrect bytecode generated for 'PrimitiveArray::size'
|
||||
- [`KT-21092`](https://youtrack.jetbrains.com/issue/KT-21092) Reference `javaClass` for generic property: "couldn't transform method node: get()"
|
||||
- [`KT-21778`](https://youtrack.jetbrains.com/issue/KT-21778) "IllegalStateException: Couldn't build context" for inline function inside an anonymous object
|
||||
- [`KT-21900`](https://youtrack.jetbrains.com/issue/KT-21900) VerifyError on equals on generic primitive type
|
||||
- [`KT-22098`](https://youtrack.jetbrains.com/issue/KT-22098) "UnsupportedOperationException: Don't know how to generate outer expression" on extension function call inside lambda in anonymous object super constructor call
|
||||
- [`KT-22488`](https://youtrack.jetbrains.com/issue/KT-22488) Bad line numbers generated for '&&' expression
|
||||
- [`KT-22972`](https://youtrack.jetbrains.com/issue/KT-22972) A compiler bug(?) in Number class descendants
|
||||
- [`KT-23619`](https://youtrack.jetbrains.com/issue/KT-23619) Transform stateless singleton lambda during inline
|
||||
- [`KT-23881`](https://youtrack.jetbrains.com/issue/KT-23881) Declaration of lambda in inlined apply block holds reference to superfluous references causing leak
|
||||
- [`KT-24135`](https://youtrack.jetbrains.com/issue/KT-24135) Calling invoke on crossinline suspend lambda leads to no state-machine
|
||||
- [`KT-24193`](https://youtrack.jetbrains.com/issue/KT-24193) NoClassDefFoundError: java/lang/Cloneable$DefaultImpls on inheritance from Cloneable through an interface
|
||||
- [`KT-24305`](https://youtrack.jetbrains.com/issue/KT-24305) ClassNotFoundException when using Java reflection on local class in an inlined lambda
|
||||
- [`KT-24564`](https://youtrack.jetbrains.com/issue/KT-24564) Custom operator fun set on ByteArray resolves properly but is miscompiled
|
||||
- [`KT-25400`](https://youtrack.jetbrains.com/issue/KT-25400) "NoClassDefFoundError: kotlin/KotlinPackage" with Turkish system locale on macOS
|
||||
- [`KT-26130`](https://youtrack.jetbrains.com/issue/KT-26130) Incorrect method signature for a generic function with inline class as a type parameter upper bound
|
||||
- [`KT-26360`](https://youtrack.jetbrains.com/issue/KT-26360) "Method from super interface has a different signature" for Interface that extends both interfaces with and without @JvmDefault
|
||||
- [`KT-26473`](https://youtrack.jetbrains.com/issue/KT-26473) Error on compiling inline class with calls of super methods equals(), hashCode(), toString()
|
||||
- [`KT-26474`](https://youtrack.jetbrains.com/issue/KT-26474) VE “Bad type on operand stack” at runtime on calling toString() method of inline class with calls of super methods (toString(), equals(), hashCode()) inside
|
||||
- [`KT-26592`](https://youtrack.jetbrains.com/issue/KT-26592) Do not generate private suspend functions as synthetic package-private
|
||||
- [`KT-27449`](https://youtrack.jetbrains.com/issue/KT-27449) NoSuchMethodError for local suspend function with suspend lambda parameter with default value
|
||||
- [`KT-27469`](https://youtrack.jetbrains.com/issue/KT-27469) "Cannot pop operand off an empty stack" for compound assignment (plusAssign) with a `vararg` operator get
|
||||
- [`KT-27825`](https://youtrack.jetbrains.com/issue/KT-27825) Gradually prohibit non-abstract classes containing abstract members invisible from that classes (internal/package-private)
|
||||
- [`KT-27830`](https://youtrack.jetbrains.com/issue/KT-27830) "Incompatible stack heights" with suspend inline function in do while loop that executes suspend lambda
|
||||
- [`KT-28042`](https://youtrack.jetbrains.com/issue/KT-28042) "Cannot pop operand off an empty stack" for a bound callable reference of lambda inside inline function
|
||||
- [`KT-28166`](https://youtrack.jetbrains.com/issue/KT-28166) "Argument 1: expected I, but found R" for generic method with generic parameter or receiver with inline class upper bound
|
||||
- [`KT-28331`](https://youtrack.jetbrains.com/issue/KT-28331) Consider generating accessors for lateinit properties to avoid assertion on each call
|
||||
- [`KT-28573`](https://youtrack.jetbrains.com/issue/KT-28573) Inliner does not update references to transformed object
|
||||
- [`KT-29331`](https://youtrack.jetbrains.com/issue/KT-29331) "AnalyzerException: Argument 1: expected R, but found I" with local generic extension property called on `Int` receiver
|
||||
- [`KT-29595`](https://youtrack.jetbrains.com/issue/KT-29595) NoClassDefFoundError with inline reified function with lambda argument returning anonymous object
|
||||
- [`KT-29802`](https://youtrack.jetbrains.com/issue/KT-29802) Incorrect reification when the same type parameter name is used for different reified types
|
||||
- [`KT-30041`](https://youtrack.jetbrains.com/issue/KT-30041) "AnalyzerException: Expected an object reference, but found ." on nested suspend function calls outer suspend function
|
||||
- [`KT-30066`](https://youtrack.jetbrains.com/issue/KT-30066) Consider adding annotations to ConeKotlinType
|
||||
- [`KT-30280`](https://youtrack.jetbrains.com/issue/KT-30280) Inline class class literal gets unwrapped in annotation arguments
|
||||
- [`KT-30402`](https://youtrack.jetbrains.com/issue/KT-30402) Constant folding works incorrectly with unsigned arithmetics
|
||||
- [`KT-30548`](https://youtrack.jetbrains.com/issue/KT-30548) "java.lang.IndexOutOfBoundsException: Cannot pop operand off an empty stack" while compiling access to a private lateinit companion field
|
||||
- [`KT-30629`](https://youtrack.jetbrains.com/issue/KT-30629) `java.lang.VerifyError: Bad type on operand stack` when using a function reference to a generic property
|
||||
- [`KT-30933`](https://youtrack.jetbrains.com/issue/KT-30933) Inline function produces IllegalAccessError on property reference from different package
|
||||
- [`KT-31136`](https://youtrack.jetbrains.com/issue/KT-31136) "AnalyzerException: Argument 1: expected R, but found I" on x::javaClass when x is inline class object built around primitive type
|
||||
- [`KT-31227`](https://youtrack.jetbrains.com/issue/KT-31227) Prohibit using array based on non-reified type parameters as reified type arguments on JVM
|
||||
- [`KT-31592`](https://youtrack.jetbrains.com/issue/KT-31592) NoSuchMethodException when inlining public function accessing a protected static Java class member
|
||||
- [`KT-31727`](https://youtrack.jetbrains.com/issue/KT-31727) Object expression captures all variables used in constructor
|
||||
- [`KT-32023`](https://youtrack.jetbrains.com/issue/KT-32023) "AnalyzerException: Expected I, but found R" with inline suspend function used with callable reference
|
||||
- [`KT-32115`](https://youtrack.jetbrains.com/issue/KT-32115) NPE during initialization of enum class with delegated property
|
||||
- [`KT-32153`](https://youtrack.jetbrains.com/issue/KT-32153) "AnalyzerException: Expected an object reference, but found ." with recursive suspend local function
|
||||
- [`KT-32351`](https://youtrack.jetbrains.com/issue/KT-32351) ClassNotFoundException for anonymous object implementing interface inside a lambda with data class and inline methods
|
||||
- [`KT-32384`](https://youtrack.jetbrains.com/issue/KT-32384) Safe cast to generic type argument with inline class upper-bound throws NPE instead of ClassCastException
|
||||
- [`KT-32579`](https://youtrack.jetbrains.com/issue/KT-32579) java.lang.VerifyError: Bad type on operand stack on calling inner class of inherited class in super class when casting to inherited class
|
||||
- [`KT-32749`](https://youtrack.jetbrains.com/issue/KT-32749) "VerifyError: Call to wrong <init> method" with inline function and accessing class field from anonymous object
|
||||
- [`KT-32793`](https://youtrack.jetbrains.com/issue/KT-32793) Generated code crashes by ClassCastException with local suspend function and inline class
|
||||
- [`KT-32812`](https://youtrack.jetbrains.com/issue/KT-32812) "AnalyzerException: Argument 1: expected R, but found I" invoking function with default parameter inherited by inline class
|
||||
- [`KT-32821`](https://youtrack.jetbrains.com/issue/KT-32821) Missing unboxing of inline class for complex hierarchy of suspend calls
|
||||
- [`KT-33155`](https://youtrack.jetbrains.com/issue/KT-33155) ClassNotFoundException for qualified this in anonymous object and as a result of inline function call
|
||||
- [`KT-33173`](https://youtrack.jetbrains.com/issue/KT-33173) Internal error: "AnalyzerException: Expected I, but found R" for supercall inside inline lambda from HashSet.remove implementation
|
||||
- [`KT-33577`](https://youtrack.jetbrains.com/issue/KT-33577) NoSuchFieldError with nested anonymous objects accessing outer instance property
|
||||
- [`KT-33836`](https://youtrack.jetbrains.com/issue/KT-33836) Wrong code generated for a local tailrec suspend function.
|
||||
- [`KT-33873`](https://youtrack.jetbrains.com/issue/KT-33873) ClassCastException invoking UByte setter function via reflection
|
||||
- [`KT-34018`](https://youtrack.jetbrains.com/issue/KT-34018) "Cannot pop operand off an empty stack" with inline lambda with callable reference
|
||||
- [`KT-34186`](https://youtrack.jetbrains.com/issue/KT-34186) JDK11: class file contains malformed variable arity method for vararg sealed class constructor
|
||||
- [`KT-34202`](https://youtrack.jetbrains.com/issue/KT-34202) IllegalAccessError on callable reference of function from multifile facade from standard library
|
||||
- [`KT-34255`](https://youtrack.jetbrains.com/issue/KT-34255) @JvmStatic tailrec function: "Cannot pop operand off an empty stack"
|
||||
- [`KT-34507`](https://youtrack.jetbrains.com/issue/KT-34507) Incorrect generated code for mutable collection stub methods in case of presence of functions with similar signature
|
||||
- [`KT-34665`](https://youtrack.jetbrains.com/issue/KT-34665) Possible index overflow in optimized "for" loop over withIndex()
|
||||
- [`KT-34754`](https://youtrack.jetbrains.com/issue/KT-34754) Flow builder: "AnalyzerException: Expected an object reference, but found ." with recursive suspend local function
|
||||
- [`KT-34816`](https://youtrack.jetbrains.com/issue/KT-34816) "AnalyzerException: Expected an object reference, but found I" on "this" in inline class member extension suspend function
|
||||
- [`KT-34841`](https://youtrack.jetbrains.com/issue/KT-34841) ClassNotFoundException when invoke param function inside anonymous object method
|
||||
- [`KT-35008`](https://youtrack.jetbrains.com/issue/KT-35008) "AnalyzerException: Expected an object reference, but found I" in inline class companion calling private constructor
|
||||
- [`KT-35166`](https://youtrack.jetbrains.com/issue/KT-35166) `NoSuchMethodError` at runtime with local property delegate on anonymous object referencing another anonymous object
|
||||
- [`KT-35224`](https://youtrack.jetbrains.com/issue/KT-35224) It's possible to pass non-spread arrays after arguments with SAM-conversion
|
||||
- [`KT-35301`](https://youtrack.jetbrains.com/issue/KT-35301) MethodInliner fails with "AssertionError: <init> call doesn't correspond to object transformation info" for qualified this in SAM constructor used as parameter of anonymous object inside inline lambda
|
||||
- [`KT-35419`](https://youtrack.jetbrains.com/issue/KT-35419) `Failed to generate expression: KtNamedFunction` for local suspend tailrec function with receiver
|
||||
- [`KT-35511`](https://youtrack.jetbrains.com/issue/KT-35511) VerifyError: "Bad type on operand stack" after reification
|
||||
- [`KT-35553`](https://youtrack.jetbrains.com/issue/KT-35553) Kotlin compiler generates methods that always have line number 1 for Inline Classes
|
||||
- [`KT-35725`](https://youtrack.jetbrains.com/issue/KT-35725) "AssertionError: Couldn't find a context for a super-call" for `super` member call in property initializer of companion object
|
||||
- [`KT-36420`](https://youtrack.jetbrains.com/issue/KT-36420) ClassCastException with inline class Foo extending generic Comparable<Foo>
|
||||
- [`KT-36713`](https://youtrack.jetbrains.com/issue/KT-36713) AnalyzerException: "Incompatible stack heights" with suspend and inline suspend functions
|
||||
- [`KT-36794`](https://youtrack.jetbrains.com/issue/KT-36794) Move $assertionsDisabled field to the top-level class
|
||||
- [`KT-36853`](https://youtrack.jetbrains.com/issue/KT-36853) IR: UninitializedPropertyAccessException on tailrec with object expression in default argument
|
||||
- [`KT-36875`](https://youtrack.jetbrains.com/issue/KT-36875) "RuntimeException: Trying to access skipped parameter" on synthetic local variable access from inline function
|
||||
- [`KT-36916`](https://youtrack.jetbrains.com/issue/KT-36916) AnalyzerException: Argument 1: expected I, but found R when using inline class with rxjava
|
||||
- [`KT-36957`](https://youtrack.jetbrains.com/issue/KT-36957) Exception during codegen: cannot pop operand off an empty stack (Nothing variable in string interpolation)
|
||||
- [`KT-36984`](https://youtrack.jetbrains.com/issue/KT-36984) SAM adapter classes should be generated as anonymous inner classes in JVM_IR
|
||||
- [`KT-37704`](https://youtrack.jetbrains.com/issue/KT-37704) Incorrect SMAP syntax
|
||||
- [`KT-37716`](https://youtrack.jetbrains.com/issue/KT-37716) "AssertionError: <init> call doesn't correspond to object transformation info" with inline reified type parameter, anonymous object and lambda in constructor call
|
||||
- [`KT-37972`](https://youtrack.jetbrains.com/issue/KT-37972) IllegalAccessError on initializing property reference for a property declared in JvmMultifileClass with -Xmultifile-parts-inherit
|
||||
- [`KT-38100`](https://youtrack.jetbrains.com/issue/KT-38100) Support local delegated properties (not inlined) in new JVM default modes
|
||||
- [`KT-38833`](https://youtrack.jetbrains.com/issue/KT-38833) JVM: java.lang.ClassCastException when loop variable is nullable in for loop over unsigned progression
|
||||
- [`KT-38849`](https://youtrack.jetbrains.com/issue/KT-38849) Read-only variable initialized in non-inline lambda using contract callsInPlace EXACTLY_ONCE is not captured correctly in nested lambdas
|
||||
- [`KT-38869`](https://youtrack.jetbrains.com/issue/KT-38869) JVM BE produces invalid bytecode when inheriting from AbstractList and declaring methods that look like MutableList implementors (but they aren't)
|
||||
- [`KT-38965`](https://youtrack.jetbrains.com/issue/KT-38965) "UnsupportedOperationException: Don't know how to generate outer expression: Closure" with reference to local variable in block argument of anonymous object `by` delegation
|
||||
- [`KT-39289`](https://youtrack.jetbrains.com/issue/KT-39289) CCE in if-else inside annotated 'if' statement
|
||||
- [`KT-39425`](https://youtrack.jetbrains.com/issue/KT-39425) AbstractMethodError: "Receiver class does not define or inherit an implementation of the resolved method" using classes with complex Java and Kotlin inheritance hierarchies.
|
||||
- [`KT-39434`](https://youtrack.jetbrains.com/issue/KT-39434) IllegalAccessError with local delegated property in lambda in inlined function
|
||||
- [`KT-39687`](https://youtrack.jetbrains.com/issue/KT-39687) "Couldn't find captured this" when more than 3 inline functions are nested
|
||||
- [`KT-39784`](https://youtrack.jetbrains.com/issue/KT-39784) "IndexOutOfBoundsException: Cannot pop operand off an empty stack" caused by JvmOverloads annotation inside an inline class
|
||||
- [`KT-40165`](https://youtrack.jetbrains.com/issue/KT-40165) ClassCastException caused by SAM conversion used on a functional interface with suspended function
|
||||
- [`KT-40179`](https://youtrack.jetbrains.com/issue/KT-40179) "VerifyError: Bad type on operand stack" with parent class `get` extension function and child class `set` extension function which used inside child class `plusAssign` extension function
|
||||
- [`KT-40277`](https://youtrack.jetbrains.com/issue/KT-40277) Fix generic types in special bridge methods
|
||||
- [`KT-40308`](https://youtrack.jetbrains.com/issue/KT-40308) NoSuchFieldError for multiple delegated extension properties with the same name in a companion object
|
||||
- [`KT-40338`](https://youtrack.jetbrains.com/issue/KT-40338) NoSuchFieldError on property without backing field that is called as function reference
|
||||
- [`KT-40392`](https://youtrack.jetbrains.com/issue/KT-40392) Deprecate JvmDefault annotation and old -Xjvm-default modes
|
||||
- [`KT-40396`](https://youtrack.jetbrains.com/issue/KT-40396) NI: Exceptions when ambiguous type argument and generic invoke
|
||||
- [`KT-40510`](https://youtrack.jetbrains.com/issue/KT-40510) "AssertionError: DELEGATION slice must override something" for ByteBuffer delegation
|
||||
- [`KT-40601`](https://youtrack.jetbrains.com/issue/KT-40601) VerifyError: "interface method reference is in an indirect superinterface" when calling @JvmDefault suspend method
|
||||
- [`KT-40809`](https://youtrack.jetbrains.com/issue/KT-40809) "Couldn't find captured field" compiler error with local function with recursive call through method reference
|
||||
- [`KT-41056`](https://youtrack.jetbrains.com/issue/KT-41056) Increase stub version due to new "contract" keyword
|
||||
- [`KT-41105`](https://youtrack.jetbrains.com/issue/KT-41105) IllegalStateException: 'Couldn't find declaration file <class name>' with inline delegate declared in another file
|
||||
- [`KT-41165`](https://youtrack.jetbrains.com/issue/KT-41165) "IllegalStateException: Concrete fake override public final fun" when an enum class inherits an interface with a variable 'name' or 'ordinal'
|
||||
- [`KT-41222`](https://youtrack.jetbrains.com/issue/KT-41222) "IllegalStateException: Concrete fake override public final fun" when a class property is inherited as merged 'var' from 'val' and 'var' from parent abstract class and interface properties
|
||||
- [`KT-41255`](https://youtrack.jetbrains.com/issue/KT-41255) JDK 11: "VerifyError: Bad type on operand stack" with long function body with annotated `when` expression
|
||||
- [`KT-41427`](https://youtrack.jetbrains.com/issue/KT-41427) NoSuchMethodError caused by implementation by delegation to function reference
|
||||
- [`KT-41508`](https://youtrack.jetbrains.com/issue/KT-41508) ClassNotFoundException caused by object with overridden function inside a lambda with safe cast receiver
|
||||
- [`KT-41750`](https://youtrack.jetbrains.com/issue/KT-41750) Inline classes: ClassCastExceptionError when calling .withIndex() on Iterator over Array
|
||||
- [`KT-41758`](https://youtrack.jetbrains.com/issue/KT-41758) Deprecate kotlin.Metadata.bytecodeVersion and avoid using it in the compiler
|
||||
- [`KT-41770`](https://youtrack.jetbrains.com/issue/KT-41770) AssertionError: "Asm parameter types should be the same length as Kotlin parameter types" cause by fun interface
|
||||
- [`KT-41874`](https://youtrack.jetbrains.com/issue/KT-41874) "IllegalStateException: Couldn't obtain compiled function body" on extension delegated property with inline operator getValue in a different file
|
||||
- [`KT-41917`](https://youtrack.jetbrains.com/issue/KT-41917) [FIR] Incorrect calculating property type for override from intersection scope
|
||||
- [`KT-42012`](https://youtrack.jetbrains.com/issue/KT-42012) IllegalAccess to protected field instead of getter
|
||||
- [`KT-42017`](https://youtrack.jetbrains.com/issue/KT-42017) "AssertionError: Unsigned type expected: UInt?" during codegen when a variable of nullable unsigned type is checking for presence in the range
|
||||
- [`KT-42032`](https://youtrack.jetbrains.com/issue/KT-42032) "AnalyzerException: Expected I, but found R" while using Flow.reduce() with suspend function reference
|
||||
- [`KT-42034`](https://youtrack.jetbrains.com/issue/KT-42034) ArrayIndexOutOfBoundsException in PopBackwardPropagationTransformer on external override of function in inline class
|
||||
- [`KT-42064`](https://youtrack.jetbrains.com/issue/KT-42064) "Parameter specified as non-null is null" with default value of the parameter in operator fun
|
||||
- [`KT-42069`](https://youtrack.jetbrains.com/issue/KT-42069) JVM IR: -Xreport-output-files doesn't report any source files for META-INF/*.kotlin_module files
|
||||
- [`KT-42083`](https://youtrack.jetbrains.com/issue/KT-42083) AbstractMethodError when 'remove' with irrelevant generic parameter but matching JVM signature is present in Kotlin collection class
|
||||
- [`KT-42092`](https://youtrack.jetbrains.com/issue/KT-42092) JVM / IR: "AnalyzerException: Argument 1: expected R, but found J" when trying to add to ArrayList the result of a function applied to int
|
||||
- [`KT-42175`](https://youtrack.jetbrains.com/issue/KT-42175) Psi2ir: "AssertionError: Undefined parameter referenced: <this>" on augmented assignment on this in a BuilderInference lambda
|
||||
- [`KT-42179`](https://youtrack.jetbrains.com/issue/KT-42179) Platform declaration clash when extending abstract Java class implementing 'java.util.Collection' by abstract Kotlin class implementing Kotlin Set or List
|
||||
- [`KT-42321`](https://youtrack.jetbrains.com/issue/KT-42321) JVM IR: do not cast integer value based on the type of a literal receiver of an operator call
|
||||
- [`KT-42337`](https://youtrack.jetbrains.com/issue/KT-42337) NoSuchMethodError in JVM backend with inheritance of private functions in the interface
|
||||
- [`KT-42404`](https://youtrack.jetbrains.com/issue/KT-42404) "Supertypes of the following classes cannot be resolved" in Rider project
|
||||
- [`KT-42472`](https://youtrack.jetbrains.com/issue/KT-42472) No TYPE_INFERENCE_UPPER_BOUND_VIOLATED for Delegated Properties do not check types (in Kotlin 1.4.10)
|
||||
- [`KT-42487`](https://youtrack.jetbrains.com/issue/KT-42487) "IndexOutOfBoundsException: Cannot pop operand off an empty stack" caused by USELESS_IS_CHECK of Double type
|
||||
- [`KT-42533`](https://youtrack.jetbrains.com/issue/KT-42533) `(N until MIN_VALUE).reversed()` should be an empty progression in for loops
|
||||
- [`KT-42588`](https://youtrack.jetbrains.com/issue/KT-42588) "IllegalStateException: Concrete fake override public open fun" caused by `val` override with `var` with delegation.
|
||||
- [`KT-42634`](https://youtrack.jetbrains.com/issue/KT-42634) Different bridges and abstract stubs behavior in abstract class implementing Map<K, String> in JVM and JVM_IR
|
||||
- [`KT-42635`](https://youtrack.jetbrains.com/issue/KT-42635) ClassCastException with inline class in for loop
|
||||
- [`KT-42662`](https://youtrack.jetbrains.com/issue/KT-42662) AbstractMethodError when using partially specialized generic Map class
|
||||
- [`KT-42694`](https://youtrack.jetbrains.com/issue/KT-42694) @get:Synchronized causes the JVM getter method not to be generated
|
||||
- [`KT-42753`](https://youtrack.jetbrains.com/issue/KT-42753) "VerifyError: Bad invokespecial instruction: interface method reference is in an indirect superinterface" with `jvm-default=all`
|
||||
- [`KT-42879`](https://youtrack.jetbrains.com/issue/KT-42879) JVM: Declaration clash in fun interface implementation returning an inline class
|
||||
- [`KT-42900`](https://youtrack.jetbrains.com/issue/KT-42900) "VerifyError: Bad return type" incorrect bytecode when a property and an extension property in inline class have the same names
|
||||
- [`KT-42946`](https://youtrack.jetbrains.com/issue/KT-42946) FIR2IR: Fix super-calls to Java overrides of special built-in
|
||||
- [`KT-42971`](https://youtrack.jetbrains.com/issue/KT-42971) JVM: "AssertionError: Unsigned type expected: T" with UInt loop range
|
||||
- [`KT-42990`](https://youtrack.jetbrains.com/issue/KT-42990) "AssertionError: Next value after NEW should be one generated by DUP" caused by extension properties with accessors annotataed as @JvmStatic
|
||||
- [`KT-43034`](https://youtrack.jetbrains.com/issue/KT-43034) AssertionError: Compiler fails with complicated tailrec + inline case
|
||||
- [`KT-43048`](https://youtrack.jetbrains.com/issue/KT-43048) JVM_IR: Implement coroutines state clearing
|
||||
- [`KT-43050`](https://youtrack.jetbrains.com/issue/KT-43050) JVM IR: incorrect mangling for method with type parameter with inline class bound in the signature
|
||||
- [`KT-43059`](https://youtrack.jetbrains.com/issue/KT-43059) Different bridges behavior in class implementing Map<String, String> in JVM and JVM_IR
|
||||
- [`KT-43063`](https://youtrack.jetbrains.com/issue/KT-43063) Redundant DefaultImpls delegate is generated in old JVM backend on explicit "duplicate" inheritance from interface
|
||||
- [`KT-43069`](https://youtrack.jetbrains.com/issue/KT-43069) JVM: incorrect generic signature for method with implicit return type Nothing overriding a method from Collection
|
||||
- [`KT-43099`](https://youtrack.jetbrains.com/issue/KT-43099) Tailrec call in not tail-call position leads to internal compiler error
|
||||
- [`KT-43106`](https://youtrack.jetbrains.com/issue/KT-43106) JVM: custom `remove` in Iterator subclass results in a synthetic bridge
|
||||
- [`KT-43120`](https://youtrack.jetbrains.com/issue/KT-43120) JVM: "Expected an object reference, but found ." caused by function which is passed as reference to suspend parameter
|
||||
- [`KT-43167`](https://youtrack.jetbrains.com/issue/KT-43167) JVM IR, serialization: "No mapping for symbol: VALUE_PARAMETER INSTANCE_RECEIVER" with data class containing property defined in body
|
||||
- [`KT-43255`](https://youtrack.jetbrains.com/issue/KT-43255) Verify error when inheriting from an abstract class implementing Collection with stub-like method in superclass
|
||||
- [`KT-43303`](https://youtrack.jetbrains.com/issue/KT-43303) NI: False negative TYPE_INFERENCE_UPPER_BOUND_VIOLATED when inferred type argument is not a subtype of type parameter upper bound
|
||||
- [`KT-43333`](https://youtrack.jetbrains.com/issue/KT-43333) AbstractMethodError when calling 'toArray' from Java on a Kotlin Collection with custom internal 'toArray'
|
||||
- [`KT-43334`](https://youtrack.jetbrains.com/issue/KT-43334) AbstractMethodError when calling 'remove' from Java on a Kotlin Collection with custom internal 'remove'
|
||||
- [`KT-43342`](https://youtrack.jetbrains.com/issue/KT-43342) [FIR2IR] No getter or backing field found for delegated member call
|
||||
- [`KT-43347`](https://youtrack.jetbrains.com/issue/KT-43347) [FIR] Synthetic setter with unmatched parameter type isn't found
|
||||
- [`KT-43401`](https://youtrack.jetbrains.com/issue/KT-43401) JVM_IR. Additional `synchronized` flag on JvmOverloads-generated adapter for Synchronized function
|
||||
- [`KT-43405`](https://youtrack.jetbrains.com/issue/KT-43405) Turkish locale, Linux Mint: "NoSuchMethodError: 'int[] kotlin.jvm.internal.Intrinsics$Kotlin.intArrayOf(int[])'" with `intArrayOf` function call
|
||||
- [`KT-43460`](https://youtrack.jetbrains.com/issue/KT-43460) JVM: redundant private setter is generated in case of multifile facade
|
||||
- [`KT-43473`](https://youtrack.jetbrains.com/issue/KT-43473) "VerifyError: Bad type on operand stack" caused by operator `get` with optional argument in superclass when called via square brackets on subclass
|
||||
- [`KT-43518`](https://youtrack.jetbrains.com/issue/KT-43518) JVM_IR. Additional `strictfp` flag on JvmOverloads-generated adapter for Strictfp function
|
||||
- [`KT-43569`](https://youtrack.jetbrains.com/issue/KT-43569) FIR: inapplicable candidate(s): kotlin/collections/set
|
||||
- [`KT-43616`](https://youtrack.jetbrains.com/issue/KT-43616) [FIR] Nullable type parameter-based type after merge in if
|
||||
- [`KT-43669`](https://youtrack.jetbrains.com/issue/KT-43669) FIR: No real overrides for FUN IR_EXTERNAL_DECLARATION_STUB
|
||||
- [`KT-43682`](https://youtrack.jetbrains.com/issue/KT-43682) Inline extension method of a multifile library inline class not found
|
||||
- [`KT-43687`](https://youtrack.jetbrains.com/issue/KT-43687) FIR: UnusedChecker does not take annotation arguments into account
|
||||
- [`KT-43688`](https://youtrack.jetbrains.com/issue/KT-43688) FIR: unused checker doesn't handle invokes properly
|
||||
- [`KT-43749`](https://youtrack.jetbrains.com/issue/KT-43749) "UnsupportedOperationException: Don't know how to generate outer expression: Closure" caused by Flow and collect method with function reference as a parameter
|
||||
- [`KT-43812`](https://youtrack.jetbrains.com/issue/KT-43812) JVM IR: SAM wrapper class with generic supertype mentions missing type parameter in the signature
|
||||
- [`KT-43832`](https://youtrack.jetbrains.com/issue/KT-43832) JVM IR: missing bridges for inheritance of class from interface in a complex generic diamond hierarchy
|
||||
- [`KT-43851`](https://youtrack.jetbrains.com/issue/KT-43851) JVM IR: function call returning object instance is removed during constant propagation
|
||||
- [`KT-43864`](https://youtrack.jetbrains.com/issue/KT-43864) JVM: "Assertion error after mandatory stack transformations: incorrect bytecode" with lateinit property of type T, which has a primitive type upperbound
|
||||
- [`KT-43887`](https://youtrack.jetbrains.com/issue/KT-43887) Problem with FunctionReferenceLowering$FunctionReferenceBuilder in kotlin native
|
||||
- [`KT-43912`](https://youtrack.jetbrains.com/issue/KT-43912) JVM internal error: Augment assignment and increment are not supported for local delegated properties and inline properties
|
||||
- [`KT-43915`](https://youtrack.jetbrains.com/issue/KT-43915) Back-end (JVM) Internal error: wrong bytecode generated for default method
|
||||
- [`KT-43938`](https://youtrack.jetbrains.com/issue/KT-43938) NSME when calling 'kotlin.Number' methods on instance of Java class extending Kolin abstract class extending 'kotlin.Number'
|
||||
- [`KT-43942`](https://youtrack.jetbrains.com/issue/KT-43942) org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Failed to generate function
|
||||
- [`KT-43949`](https://youtrack.jetbrains.com/issue/KT-43949) FIR: unresolved callable reference as lambda return
|
||||
- [`KT-43983`](https://youtrack.jetbrains.com/issue/KT-43983) IllegalStateException: "Couldn't obtain compiled function body for public final suspend inline fun" after moving inline extension function to library
|
||||
- [`KT-43984`](https://youtrack.jetbrains.com/issue/KT-43984) FIR: recursion in overridden symbols
|
||||
- [`KT-43984`](https://youtrack.jetbrains.com/issue/KT-43984) FIR: recursion in overridden symbols
|
||||
- [`KT-44010`](https://youtrack.jetbrains.com/issue/KT-44010) FIR: Inapplicable constructor due to an unresolved reference
|
||||
- [`KT-44030`](https://youtrack.jetbrains.com/issue/KT-44030) FIR2IR: uncached type parameters in delegated property
|
||||
- [`KT-44032`](https://youtrack.jetbrains.com/issue/KT-44032) FIR2IR: uncached type parameters in Java field
|
||||
- [`KT-44050`](https://youtrack.jetbrains.com/issue/KT-44050) FIR: anonymous object as IR parent
|
||||
- [`KT-44054`](https://youtrack.jetbrains.com/issue/KT-44054) FIR2IR: incorrect IR origin for substituted override function
|
||||
- [`KT-44058`](https://youtrack.jetbrains.com/issue/KT-44058) CompilationException: open suspend fun with @JvmStatic in open class companion
|
||||
- [`KT-44069`](https://youtrack.jetbrains.com/issue/KT-44069) please remove deprecated usages
|
||||
- [`KT-44066`](https://youtrack.jetbrains.com/issue/KT-44066) FIR Java: override ambiguity with vararg value type
|
||||
- [`KT-44114`](https://youtrack.jetbrains.com/issue/KT-44114) CompilationException when inlining a extension suspend function declared in interface companion with 'this' reference to extension receiver
|
||||
- [`KT-44131`](https://youtrack.jetbrains.com/issue/KT-44131) "UnsupportedOperationException: Don't know how to generate outer expression: Closure" when using suspend lambda and a function reference
|
||||
- [`KT-44140`](https://youtrack.jetbrains.com/issue/KT-44140) JVM IR: compilation of kotlin.Result crashes with IOOBE while generating toString-impl
|
||||
- [`KT-44141`](https://youtrack.jetbrains.com/issue/KT-44141) JVM IR: "ISE: There should be underlying type for inline class type" on usage of type parameter with Result upper bound inside a lambda
|
||||
- [`KT-44192`](https://youtrack.jetbrains.com/issue/KT-44192) Allow a greater number of constants in an enum class
|
||||
- [`KT-44202`](https://youtrack.jetbrains.com/issue/KT-44202) "ClassCastException" when getting delegated property with inline class and Any/Any? type
|
||||
- [`KT-44210`](https://youtrack.jetbrains.com/issue/KT-44210) KJS / IR: "AssertionError: Undefined parameter referenced: <this> defined" caused by plus assign operators in build blocks
|
||||
- [`KT-44233`](https://youtrack.jetbrains.com/issue/KT-44233) [IR] Collection Stub generation not correctly considering java.util Collection iterators
|
||||
- [`KT-44234`](https://youtrack.jetbrains.com/issue/KT-44234) Private companion property with explicit setter generates invalid bytecode
|
||||
- [`KT-44269`](https://youtrack.jetbrains.com/issue/KT-44269) "[TAILREC_ON_VIRTUAL_MEMBER_ERROR] Tailrec is not allowed on open members" with Spring annotation and private tailrec function
|
||||
- [`KT-44284`](https://youtrack.jetbrains.com/issue/KT-44284) Make Kotlin binaries publicly unavailable (set KotlinCompilerVersion.IS_PRE_RELEASE = true)
|
||||
- [`KT-44316`](https://youtrack.jetbrains.com/issue/KT-44316) ReenteringLazyValueComputationException when analyzing complex lazy delegate
|
||||
- [`KT-44347`](https://youtrack.jetbrains.com/issue/KT-44347) Back-end (JVM) Internal error: Couldn't transform method node for suspend function with wrong local for Continuation
|
||||
- [`KT-44368`](https://youtrack.jetbrains.com/issue/KT-44368) "IllegalStateException: Error type encountered" when inlining 'invoke' operator without enough information on type variable
|
||||
- [`KT-44412`](https://youtrack.jetbrains.com/issue/KT-44412) JVM IR backend fails to compile break in condition of do while
|
||||
- [`KT-44420`](https://youtrack.jetbrains.com/issue/KT-44420) False NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATION with 1.4.30-RC
|
||||
- [`KT-44429`](https://youtrack.jetbrains.com/issue/KT-44429) JVM IR: unnecessary integer unboxing leads to NPE when using mockito-kotlin
|
||||
- [`KT-44439`](https://youtrack.jetbrains.com/issue/KT-44439) Type inference of generic types failing java interop
|
||||
- [`KT-44440`](https://youtrack.jetbrains.com/issue/KT-44440) Too many Nothings in inferred type
|
||||
- [`KT-44471`](https://youtrack.jetbrains.com/issue/KT-44471) Fix failing script tests after switching to 1.5
|
||||
- [`KT-44474`](https://youtrack.jetbrains.com/issue/KT-44474) Compiler expects sealed type inheritors from platform specific source-sets in when expression in common source-set
|
||||
- [`KT-44483`](https://youtrack.jetbrains.com/issue/KT-44483) JVM IR: CCE on calling generic vararg function reference with Array expected type
|
||||
- [`KT-44527`](https://youtrack.jetbrains.com/issue/KT-44527) Suspend function with kotlin.Result: ClassCastException class kotlin.Result cannot be cast to class ...
|
||||
- [`KT-44529`](https://youtrack.jetbrains.com/issue/KT-44529) Inline class calls wrong iterator method in for loop
|
||||
- [`KT-44533`](https://youtrack.jetbrains.com/issue/KT-44533) JVM IR: ClassFormatError on synthetic $suspendImpl method generated in interface for a @JvmDefault function
|
||||
- [`KT-44540`](https://youtrack.jetbrains.com/issue/KT-44540) Regression in 1.4.30 in intellij-community: type mismatch for generic function call with generic Java class
|
||||
- [`KT-44546`](https://youtrack.jetbrains.com/issue/KT-44546) NI: changed variable fixation order (that can lead to changed resolution)
|
||||
- [`KT-44550`](https://youtrack.jetbrains.com/issue/KT-44550) KotlinBinaryClassCache leaks Kotlin plugin classloader on plugin unload
|
||||
- [`KT-44563`](https://youtrack.jetbrains.com/issue/KT-44563) Type Inference loosing type annotations in lambda type expectation for function calls with block parameters
|
||||
- [`KT-44583`](https://youtrack.jetbrains.com/issue/KT-44583) "Supertypes of the following classes cannot be resolved" error message gives no context
|
||||
- [`KT-44627`](https://youtrack.jetbrains.com/issue/KT-44627) JVM IR: ACCIDENTAL_OVERRIDE when overriding a generic field where the type parameter has a primitive bound
|
||||
- [`KT-44631`](https://youtrack.jetbrains.com/issue/KT-44631) "IndexOutOfBoundsException: Cannot pop operand off an empty stack" caused by a default param in inner class constructor which uses method or field from receiver
|
||||
- [`KT-44647`](https://youtrack.jetbrains.com/issue/KT-44647) "IllegalAccessError: class TestKt tried to access private method" with String Builder `get` and `inc` operator
|
||||
- [`KT-44651`](https://youtrack.jetbrains.com/issue/KT-44651) JVM / IR: "IllegalStateException: Illegal type substitutor" with if-else inside class constructor argument inside another if-else
|
||||
- [`KT-44660`](https://youtrack.jetbrains.com/issue/KT-44660) Internal inline functions in companion object with inline class return type fails compilation
|
||||
- [`KT-44671`](https://youtrack.jetbrains.com/issue/KT-44671) JVM_IR: ClassCastException: Result$Failure cannot be cast to Result with multithreaded coroutines
|
||||
- [`KT-44703`](https://youtrack.jetbrains.com/issue/KT-44703) JVM / IR: "IllegalStateException: Unhandled special name in mangledNameFor" caused by a reference to inline class inside interface's companion with lazy initialization
|
||||
- [`KT-44712`](https://youtrack.jetbrains.com/issue/KT-44712) JVM / IR: Behavior change after enabling with Mockito
|
||||
- [`KT-44714`](https://youtrack.jetbrains.com/issue/KT-44714) Debugger / Coroutines: Local variables are trimmed out too aggressively
|
||||
- [`KT-44722`](https://youtrack.jetbrains.com/issue/KT-44722) JVM IR: ClassCastException with inline class, let and bound function reference
|
||||
- [`KT-44726`](https://youtrack.jetbrains.com/issue/KT-44726) JVM IR: Incorrect KType nullability for platform type reified as non-null
|
||||
- [`KT-44781`](https://youtrack.jetbrains.com/issue/KT-44781) JVM IR: java.lang.NoSuchFieldError: $noName_0 when calling a crossinline lambda within a suspending lambda
|
||||
- [`KT-44798`](https://youtrack.jetbrains.com/issue/KT-44798) JVM IR: Inherited platform declarations clash for class implementing both List and Set
|
||||
- [`KT-44801`](https://youtrack.jetbrains.com/issue/KT-44801) 1.4.30 JVM IR: Unbound symbols not allowed with anonymous object
|
||||
- [`KT-44803`](https://youtrack.jetbrains.com/issue/KT-44803) FIR bootstrap: incorrect nullability is set for type alias-based type
|
||||
- [`KT-44827`](https://youtrack.jetbrains.com/issue/KT-44827) Non-existing outer class is written in anonymous class for SAM wrapper in inline lambda with capture
|
||||
- [`KT-44837`](https://youtrack.jetbrains.com/issue/KT-44837) JVM / IR: ClassCastException with Result object when it is used by a generic method in a suspend call
|
||||
- [`KT-44875`](https://youtrack.jetbrains.com/issue/KT-44875) JVM_IR. `hashCode` call is generated on interface target in fun interface equality
|
||||
- [`KT-44878`](https://youtrack.jetbrains.com/issue/KT-44878) JVM_IR: "IllegalStateException: Unexpected types" when checking non-nullable variable is `in` range between nullable ones with smart-cast
|
||||
- [`KT-44926`](https://youtrack.jetbrains.com/issue/KT-44926) MPP: Actual typealias to compiled inline class incompatible with expect inline class
|
||||
- [`KT-44947`](https://youtrack.jetbrains.com/issue/KT-44947) Sealed interfaces: Sealed fun interface leads to "NoWhenBranchMatchedException"
|
||||
- [`KT-44993`](https://youtrack.jetbrains.com/issue/KT-44993) JVM IR: VerifyError on getfield with Kotlin generic field and elvis operator
|
||||
- [`KT-45008`](https://youtrack.jetbrains.com/issue/KT-45008) JVM IR: hashCode is generated as invokeinterface if smart cast to interface is present
|
||||
- [`KT-45011`](https://youtrack.jetbrains.com/issue/KT-45011) JVM / IR: "AssertionError: Unbound symbols not allowed"
|
||||
- [`KT-45022`](https://youtrack.jetbrains.com/issue/KT-45022) IR: "AssertionError: Undefined variable referenced" from psi2ir caused by plusAssign operator of object
|
||||
- [`KT-45064`](https://youtrack.jetbrains.com/issue/KT-45064) JVM IR: "java.lang.AssertionError: SyntheticAccessorLowering should not attempt to modify other files!" with member reference to property in another file with private setter
|
||||
- [`KT-45067`](https://youtrack.jetbrains.com/issue/KT-45067) "IllegalArgumentException: Wildcard mast have a bound for annotation of WILDCARD_BOUND position" with BEAM SDK 2.27
|
||||
- [`KT-45069`](https://youtrack.jetbrains.com/issue/KT-45069) JVM / IR: New SAM conversions mode fails when converting from Unit to Any
|
||||
- [`KT-45131`](https://youtrack.jetbrains.com/issue/KT-45131) JVM / IR: "RuntimeException: Lambda, SAM or anonymous object should have only one constructor" caused by inline class that type cast to reified type parameter inside lambda in inline function
|
||||
- [`KT-45139`](https://youtrack.jetbrains.com/issue/KT-45139) Inline class: AssertionError: Expected top level inline class
|
||||
- [`KT-45166`](https://youtrack.jetbrains.com/issue/KT-45166) JVM / IR: "AbstractMethodError: Receiver class does not define or inherit an implementation of the resolved method of interface" caused by interface with suspend function
|
||||
- [`KT-45187`](https://youtrack.jetbrains.com/issue/KT-45187) JVM / IR: ClassCastException caused by substituting generic type of vararg parameter with java.lang.Void
|
||||
- [`KT-45195`](https://youtrack.jetbrains.com/issue/KT-45195) JVM IR: annotation methods are generated as default interface methods if `allopen` is used
|
||||
- [`KT-45243`](https://youtrack.jetbrains.com/issue/KT-45243) "IllegalStateException: Lambdas shouldn't be visited by ESExpressionVisitor" caused by lambda inside `kotlin.test.assertNotNull`
|
||||
- [`KT-45259`](https://youtrack.jetbrains.com/issue/KT-45259) JVM: ClassCastException caused by Result as lambda parameter type
|
||||
- [`KT-45292`](https://youtrack.jetbrains.com/issue/KT-45292) AssertionError with recursive inline extension property
|
||||
- [`KT-45300`](https://youtrack.jetbrains.com/issue/KT-45300) Deprecate super calls in public-api inline functions
|
||||
- [`KT-45409`](https://youtrack.jetbrains.com/issue/KT-45409) Rename jspecify annotations’ package and default not null annotation
|
||||
- [`KT-45446`](https://youtrack.jetbrains.com/issue/KT-45446) JVM / IR: NullPointerException caused by unreachable code and comparison
|
||||
- [`KT-45721`](https://youtrack.jetbrains.com/issue/KT-45721) JVM / IR: "Unbound symbols not allowed" caused by class reference in sequence lambda
|
||||
- [`KT-45853`](https://youtrack.jetbrains.com/issue/KT-45853) JVM / IR: "Accidental override" caused by inheriting Throwable.getCause from Java interface
|
||||
- [`KT-45861`](https://youtrack.jetbrains.com/issue/KT-45861) Turning warnings into errors for calls with type parameters annotated by @OnlyInputTypes
|
||||
- [`KT-45865`](https://youtrack.jetbrains.com/issue/KT-45865) JVM IR: "VerifyError: Bad type on operand stack" with `enumValueOf` on a value from a list of strings
|
||||
- [`KT-45868`](https://youtrack.jetbrains.com/issue/KT-45868) JVM IR: ClassCastException with SAM function in init block when SAM is generated via invokedynamic
|
||||
- [`KT-45920`](https://youtrack.jetbrains.com/issue/KT-45920) JVM IR: "Accidental override" on redefining `get()` in custom Map class
|
||||
- [`KT-45934`](https://youtrack.jetbrains.com/issue/KT-45934) JVM IR: "java.lang.IllegalStateException: Function has no body" for class implementing interface by delegation
|
||||
- [`KT-45945`](https://youtrack.jetbrains.com/issue/KT-45945) JVM / IR: "AssertionError: Unexpected variance in super type argument" with contravariance and intersection types
|
||||
- [`KT-45963`](https://youtrack.jetbrains.com/issue/KT-45963) JVM / IR: "AbstractMethodError: Receiver class does not define or inherit an implementation of the resolved method" in Dokka tests
|
||||
- [`KT-45967`](https://youtrack.jetbrains.com/issue/KT-45967) JVM IR: "IllegalAccessError" with invokedynamic to Java SAM over callable reference to private function
|
||||
- [`KT-45982`](https://youtrack.jetbrains.com/issue/KT-45982) Wrong subtyping result on captured types with postponed type variables
|
||||
- [`KT-46007`](https://youtrack.jetbrains.com/issue/KT-46007) JVM / IR: "ClassCastException: kotlin.Unit cannot be cast to java.lang.String" caused by default suspend function in interface
|
||||
- [`KT-46060`](https://youtrack.jetbrains.com/issue/KT-46060) JVM IR: NullPointerException from RangeContainsLowering when `contains` is a @JvmStatic function in object
|
||||
- [`KT-46069`](https://youtrack.jetbrains.com/issue/KT-46069) JVM IR: unbound type parameter on generic bound adapted function reference
|
||||
- [`KT-46092`](https://youtrack.jetbrains.com/issue/KT-46092) JVM IR: AssertionError "Array type expected: @[FlexibleNullability] kotlin.CharArray?" on super call to Java constructor with primitive vararg
|
||||
- [`KT-46104`](https://youtrack.jetbrains.com/issue/KT-46104) The message on inline -> value class migration should not say that inline classes are deprecated
|
||||
- [`KT-46131`](https://youtrack.jetbrains.com/issue/KT-46131) Kotlin 1.5.0-RC errors when reading class file
|
||||
- [`KT-46160`](https://youtrack.jetbrains.com/issue/KT-46160) JVM IR: IllegalAccessException at runtime for member reference to JvmMultifileClass member from stdlib
|
||||
- [`KT-46186`](https://youtrack.jetbrains.com/issue/KT-46186) Type inference regression in Kotlin 1.5 with constrained generic return types
|
||||
|
||||
### Docs & Examples
|
||||
|
||||
- [`KT-45884`](https://youtrack.jetbrains.com/issue/KT-45884) Incorrect description for JVM `toUpperCase` method
|
||||
|
||||
### IDE
|
||||
|
||||
- [`KT-33233`](https://youtrack.jetbrains.com/issue/KT-33233) Use dependency of library to build built-ins in IDE, instead of loading them from the current classloader
|
||||
- [`KT-34023`](https://youtrack.jetbrains.com/issue/KT-34023) kotlin.KotlinNullPointerException at org.jetbrains.kotlin.backend.common.FunctionsFromAnyGenerator.getPrimaryConstructorProperties(FunctionsFromAnyGenerator.kt:66)
|
||||
- [`KT-35947`](https://youtrack.jetbrains.com/issue/KT-35947) KFunctionN.call is unresolved in IDE in Kotlin/JVM project
|
||||
- [`KT-37702`](https://youtrack.jetbrains.com/issue/KT-37702) Code analysis speed: on-the-fly analysis diagnostics reporting
|
||||
- [`KT-41048`](https://youtrack.jetbrains.com/issue/KT-41048) [FIR-IDE] Properly implement methods in KtFirPackageScope
|
||||
- [`KT-41671`](https://youtrack.jetbrains.com/issue/KT-41671) Missing nullability information in properties using type inference from get()
|
||||
- [`KT-43824`](https://youtrack.jetbrains.com/issue/KT-43824) KtLightClassForSourceDeclaration#isInheritor works in a different way than java implementation
|
||||
- [`KT-44128`](https://youtrack.jetbrains.com/issue/KT-44128) IDE: Kotlin JVM record has incorrect property accessors as seen from Java
|
||||
- [`KT-44487`](https://youtrack.jetbrains.com/issue/KT-44487) MPP, IDE: No error in IDE when sealed class inheritor from common source-set is not used in exhaustive when expression in platform source-set
|
||||
- [`KT-45254`](https://youtrack.jetbrains.com/issue/KT-45254) Highlighting for files with certain errors appears only on second opening
|
||||
- [`KT-46097`](https://youtrack.jetbrains.com/issue/KT-46097) Light classes: Incomplete nullability information for a getter method of a kotlin property defined in private constructor
|
||||
|
||||
### IDE. Decompiler, Indexing, Stubs
|
||||
|
||||
- [`KT-43699`](https://youtrack.jetbrains.com/issue/KT-43699) IDE: Unresolved extension method from Java code for simple class with typealias and generics (IllegalStateException: Unknown type parameter)
|
||||
- [`KT-44756`](https://youtrack.jetbrains.com/issue/KT-44756) Infinite "UpToDateStubIndexMismatch: PSI and index do not match." with IDEA 2021.1 EAP upon attempt to open "org.gradle.configurationcache" even they seem to be the same
|
||||
|
||||
### IDE. Gradle Integration
|
||||
|
||||
- [`KT-37127`](https://youtrack.jetbrains.com/issue/KT-37127) Implement precise importing of platforms of root source sets (commonMain/commonTest) when hierarchical multiplatform support is enabled
|
||||
- [`KT-42048`](https://youtrack.jetbrains.com/issue/KT-42048) KJS / Gradle integration: Could not determine the dependencies of task ':webApp:testPackageJson' in Android Studio 4.2 Canary 11
|
||||
|
||||
### IDE. Gradle. Script
|
||||
|
||||
- [`KT-46215`](https://youtrack.jetbrains.com/issue/KT-46215) Dead lock on closing project during the import in IJ211 through ScriptDefinitionsManager
|
||||
|
||||
### IDE. Inspections and Intentions
|
||||
|
||||
- [`KT-23824`](https://youtrack.jetbrains.com/issue/KT-23824) Return lifted out of if condition causes suspend tail call optimization to no longer apply
|
||||
- [`KT-38155`](https://youtrack.jetbrains.com/issue/KT-38155) Lift assignment out of 'if' produces type mismatch without manually adding a semicolon
|
||||
- [`KT-44821`](https://youtrack.jetbrains.com/issue/KT-44821) IDE: False positive NO_ELSE_IN_WHEN caused by sealed class and when in another module
|
||||
- [`KT-46088`](https://youtrack.jetbrains.com/issue/KT-46088) [IDEA] Incorrect behavior of replace inline class with value class intention
|
||||
|
||||
### IDE. Misc
|
||||
|
||||
- [`KT-44675`](https://youtrack.jetbrains.com/issue/KT-44675) Incorrect reference to resource into 202 plugin
|
||||
|
||||
### IDE. Refactorings
|
||||
|
||||
- [`KT-44079`](https://youtrack.jetbrains.com/issue/KT-44079) Sealed Interfaces: Move refactoring should warn about violation of hierarchy restrictions
|
||||
- [`KT-44839`](https://youtrack.jetbrains.com/issue/KT-44839) Sealed interfaces: move refactoring warnings works with "more freedom for sealed classes" rules for language level < 1.5
|
||||
|
||||
### IDE. Script
|
||||
|
||||
- [`KT-43288`](https://youtrack.jetbrains.com/issue/KT-43288) Allow push notifications about script configuration /dependencies changes via the `ScriptDefinitionsProvider` EP
|
||||
|
||||
### JavaScript
|
||||
|
||||
- [`KT-39272`](https://youtrack.jetbrains.com/issue/KT-39272) KJS / IR: Can't use javascript keywords as JsName
|
||||
- [`KT-41650`](https://youtrack.jetbrains.com/issue/KT-41650) JS IR BE: `default` should be a reserved identifier
|
||||
- [`KT-42176`](https://youtrack.jetbrains.com/issue/KT-42176) KJS / IR: Interface default method in sub-interface not resolved correctly from extension on super-interface
|
||||
- [`KT-44103`](https://youtrack.jetbrains.com/issue/KT-44103) [JSIR] TypeError when bumping from 1.4.20 to 1.4.30-M1
|
||||
- [`KT-44180`](https://youtrack.jetbrains.com/issue/KT-44180) KJS / IR: NPE in ConstTransformer of compileDevelopmentExecutableKotlinJs/compileProductionExecutableKotlinJs tasks
|
||||
- [`KT-44415`](https://youtrack.jetbrains.com/issue/KT-44415) Kotlin/JS with IR and kotlin-react: "too much recursion" error in runtime in browser
|
||||
- [`KT-44433`](https://youtrack.jetbrains.com/issue/KT-44433) KJS IR: support function interfaces with suspend member
|
||||
- [`KT-44469`](https://youtrack.jetbrains.com/issue/KT-44469) KJS / IR: Incorrect export functions with bridges
|
||||
- [`KT-44718`](https://youtrack.jetbrains.com/issue/KT-44718) MPP/ KJS: "IllegalStateException: Unsupported operation" with serialization plugin and incremental compilation
|
||||
- [`KT-44796`](https://youtrack.jetbrains.com/issue/KT-44796) KJS / IR: default parameter of function with @JsName leads to "RangeError: Maximum call stack size exceeded"
|
||||
- [`KT-45059`](https://youtrack.jetbrains.com/issue/KT-45059) KJS / IR: Add possibility for runtime diagnostics of DCE result
|
||||
|
||||
### Libraries
|
||||
|
||||
- [`KT-12109`](https://youtrack.jetbrains.com/issue/KT-12109) Add stdlib method that combines mapNotNull() and first/firstOrNull()
|
||||
- [`KT-25571`](https://youtrack.jetbrains.com/issue/KT-25571) Make random implementations serializable
|
||||
- [`KT-26234`](https://youtrack.jetbrains.com/issue/KT-26234) Floored division and remainder function for numeric types
|
||||
- [`KT-32996`](https://youtrack.jetbrains.com/issue/KT-32996) kotlin.test: add assertContentEquals for comparing content of arrays, iterables, sequences
|
||||
- [`KT-39177`](https://youtrack.jetbrains.com/issue/KT-39177) Make CharCategory available in common multiplatform code
|
||||
- [`KT-40225`](https://youtrack.jetbrains.com/issue/KT-40225) Support adding kotlin-test as a single dependency, as it should be with a multiplatform library
|
||||
- [`KT-42071`](https://youtrack.jetbrains.com/issue/KT-42071) Strict version of String.toBoolean()
|
||||
- [`KT-42720`](https://youtrack.jetbrains.com/issue/KT-42720) Kotlin ArrayDeque on JVM: provide optimized toArray method
|
||||
- [`KT-42840`](https://youtrack.jetbrains.com/issue/KT-42840) Commonize and generalize String.contentEquals that is currently JVM-only
|
||||
- [`KT-43772`](https://youtrack.jetbrains.com/issue/KT-43772) Kotlin/Native unfinished workers detected.
|
||||
- [`KT-44168`](https://youtrack.jetbrains.com/issue/KT-44168) Prevent storing NaN and negative zero in kotlin.time.Duration
|
||||
- [`KT-44369`](https://youtrack.jetbrains.com/issue/KT-44369) Commonize Char.titlecaseChar() and Char.titlecase() that are currently JVM-only
|
||||
- [`KT-44783`](https://youtrack.jetbrains.com/issue/KT-44783) Add IS_VALUE flag for value classes to kotlinx-metadata-jvm
|
||||
- [`KT-44815`](https://youtrack.jetbrains.com/issue/KT-44815) Remove kotlin-annotations-android and JVM compiler support for @ParameterName/@DefaultValue/@DefaultNull
|
||||
- [`KT-45213`](https://youtrack.jetbrains.com/issue/KT-45213) Update Unicode version used in K/N for Char and String case conversion functions
|
||||
|
||||
### Middle-end. IR
|
||||
|
||||
- [`KT-43831`](https://youtrack.jetbrains.com/issue/KT-43831) Compilation failed, IrSimpleFunctionPublicSymbolImpl is already bound
|
||||
- [`KT-44100`](https://youtrack.jetbrains.com/issue/KT-44100) KJS / IR: Top level declarations added in IR plugin are not referenceable from other modules
|
||||
- [`KT-45170`](https://youtrack.jetbrains.com/issue/KT-45170) IR: "AssertionError: Single expression value for GET_OBJECT" caused by inc operator of field inside scope function inside object
|
||||
|
||||
### Native
|
||||
|
||||
- [`KT-42446`](https://youtrack.jetbrains.com/issue/KT-42446) Native: SIGSEGV in Kotlin_Array_get on linuxArm64
|
||||
- [`KT-43502`](https://youtrack.jetbrains.com/issue/KT-43502) [K/N] relocation R_X86_64_PC32 cannot be used against symbol __environ; recompile with -fPIC
|
||||
- [`KT-44295`](https://youtrack.jetbrains.com/issue/KT-44295) 1.4.21 Kotlin native ndk compiler crash
|
||||
- [`KT-44774`](https://youtrack.jetbrains.com/issue/KT-44774) ld fails with CALL16 reloc at 0x48f00 not against global symbol (Linux MIPS)
|
||||
- [`KT-44746`](https://youtrack.jetbrains.com/issue/KT-44746) Different hashCode() results for Kotlin/Native stdlib
|
||||
|
||||
### Native. C and ObjC Import
|
||||
|
||||
- [`KT-44824`](https://youtrack.jetbrains.com/issue/KT-44824) cinterop tool no longer appends .klib to produced klibs
|
||||
|
||||
### Native. C Export
|
||||
|
||||
- [`KT-36639`](https://youtrack.jetbrains.com/issue/KT-36639) MPP: Build ios "staticLib" or "sharedLib" binary failed if interface contains member extension function
|
||||
- [`KT-41725`](https://youtrack.jetbrains.com/issue/KT-41725) Dynamic library doesn't load on raspberrypi
|
||||
|
||||
### Native. ObjC Export
|
||||
|
||||
- [`KT-44549`](https://youtrack.jetbrains.com/issue/KT-44549) In the Xcode debug session, call stack is missing a frame when the iOS app fails
|
||||
|
||||
### Native. Platforms
|
||||
|
||||
- [`KT-45094`](https://youtrack.jetbrains.com/issue/KT-45094) Fail to compile Kotlin Native sources under Oracle Linux 7
|
||||
|
||||
### Reflection
|
||||
|
||||
- [`KT-44594`](https://youtrack.jetbrains.com/issue/KT-44594) Avoid using unnecessary array types reflection in kotlin-reflect
|
||||
- [`KT-44782`](https://youtrack.jetbrains.com/issue/KT-44782) Add KClass.isValue to kotlin-reflect
|
||||
|
||||
### Tools. Ant
|
||||
|
||||
- [`KT-16227`](https://youtrack.jetbrains.com/issue/KT-16227) Ant task: do not include runtime by default if destination is a jar
|
||||
- [`KT-44293`](https://youtrack.jetbrains.com/issue/KT-44293) Support fork mode in kotlinc Ant task
|
||||
|
||||
### Tools. CLI
|
||||
|
||||
- [`KT-17344`](https://youtrack.jetbrains.com/issue/KT-17344) Include kotlin-reflect to resulting jar if "-include-runtime" is specified
|
||||
- [`KT-43220`](https://youtrack.jetbrains.com/issue/KT-43220) -include-runtime should add .kotlin_builtins to the output
|
||||
- [`KT-43704`](https://youtrack.jetbrains.com/issue/KT-43704) Illegal reflective access by com.intellij.util.ReflectionUtil to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
|
||||
- [`KT-44078`](https://youtrack.jetbrains.com/issue/KT-44078) Do not include module-info.class of kotlin-stdlib.jar to the resulting jar with -include-runtime
|
||||
- [`KT-44232`](https://youtrack.jetbrains.com/issue/KT-44232) CLI: do not pass -noverify to java process starting from JDK 13
|
||||
- [`KT-45566`](https://youtrack.jetbrains.com/issue/KT-45566) JDK 16 - e: java.lang.NoClassDefFoundError: Could not initialize class org.jetbrains.kotlin.com.intellij.pom.java.LanguageLevel
|
||||
|
||||
### Tools. CLI. Native
|
||||
|
||||
- [`KT-43874`](https://youtrack.jetbrains.com/issue/KT-43874) Native / CLI: provide a way to show difference between Jvm and Native compilers
|
||||
|
||||
### Tools. Compiler Plugins
|
||||
|
||||
- [`KT-45783`](https://youtrack.jetbrains.com/issue/KT-45783) Serialization: "AnalyzerException: Expected an object reference, but found I" caused by `JvmInline` and `Serializable` annotations
|
||||
|
||||
### Tools. Gradle
|
||||
|
||||
- [`KT-31027`](https://youtrack.jetbrains.com/issue/KT-31027) java.lang.NoSuchMethodError: No static method hashCode(Z)I in class Ljava/lang/Boolean; or its super classes (declaration of 'java.lang.Boolean' appears in /system/framework/core-libart.jar)
|
||||
- [`KT-43605`](https://youtrack.jetbrains.com/issue/KT-43605) Kotlin Gradle Plugin 1.4.20 undeclared system property reads cause problems with Gradle configuration cache enabled
|
||||
- [`KT-44204`](https://youtrack.jetbrains.com/issue/KT-44204) Kotlin Gradle Plugin 1.4.21 makes impossible to use ANTLR in other plugins
|
||||
- [`KT-44361`](https://youtrack.jetbrains.com/issue/KT-44361) Gradle: deprecate options includeRuntime, noStdlib, noReflect
|
||||
- [`KT-44462`](https://youtrack.jetbrains.com/issue/KT-44462) Kotlin Gradle plugin creates `compile` configuration with Gradle 7.0
|
||||
- [`KT-44834`](https://youtrack.jetbrains.com/issue/KT-44834) Gradle Kotlin DSL: Add `languageSettings` configuration lambda without `apply` call
|
||||
- [`KT-44949`](https://youtrack.jetbrains.com/issue/KT-44949) Compatibility with Gradle 7.0
|
||||
- [`KT-44957`](https://youtrack.jetbrains.com/issue/KT-44957) gradle - target.compilations seems to be deprecated
|
||||
- [`KT-45340`](https://youtrack.jetbrains.com/issue/KT-45340) Update minimal supported version of Kotlin Gradle Plugin to 6.1
|
||||
|
||||
### Tools. Gradle. JS
|
||||
|
||||
- [`KT-43237`](https://youtrack.jetbrains.com/issue/KT-43237) KJS: `-jsLegacy` Naming Convention is incompatible with NPM
|
||||
- [`KT-43869`](https://youtrack.jetbrains.com/issue/KT-43869) Error in webpack configuration not displayed
|
||||
- [`KT-44614`](https://youtrack.jetbrains.com/issue/KT-44614) Update Node.JS and Yarn versions
|
||||
- [`KT-44616`](https://youtrack.jetbrains.com/issue/KT-44616) Kotlin/JS: IR backend with React: "Uncaught TypeError: _this__0 is undefined" runtime error in browser
|
||||
- [`KT-45574`](https://youtrack.jetbrains.com/issue/KT-45574) Sync Kotlin/JS compile tasks into one folder (build/js/packages/<package>/kotlin)
|
||||
|
||||
### Tools. Gradle. Multiplatform
|
||||
|
||||
- [`KT-42098`](https://youtrack.jetbrains.com/issue/KT-42098) Commonizer is re-launched for every included Gradle build
|
||||
- [`KT-43116`](https://youtrack.jetbrains.com/issue/KT-43116) Merge together MultiplatformHighlightingTest and MultiplatformAnalysisTest
|
||||
- [`KT-44322`](https://youtrack.jetbrains.com/issue/KT-44322) KotlinTargetComponent maintenance for -sources.jar
|
||||
- [`KT-44900`](https://youtrack.jetbrains.com/issue/KT-44900) Support gradle configuration cache with kotlin.multiplatform plugin
|
||||
|
||||
### Tools. Gradle. Native
|
||||
|
||||
- [`KT-46122`](https://youtrack.jetbrains.com/issue/KT-46122) kotlinx-serialization and kotlinx-datetime can't be built with 1.5.0-RC
|
||||
|
||||
### Tools. JPS
|
||||
|
||||
- [`KT-13631`](https://youtrack.jetbrains.com/issue/KT-13631) Compilation fails on Turkish locale because of locale-sensitive uppercasing
|
||||
- [`KT-44644`](https://youtrack.jetbrains.com/issue/KT-44644) Mark all `@JvmMultifileClass` parts compiled in the previous round as dirty in the JPS plugin, similarly to how it’s done in the Gradle plugin
|
||||
|
||||
### Tools. Scripts
|
||||
|
||||
- [`KT-45194`](https://youtrack.jetbrains.com/issue/KT-45194) KT: Generate Kotlin Entities script: it doesn't work
|
||||
- [`KT-44580`](https://youtrack.jetbrains.com/issue/KT-44580) Scripts: Unable to set new file annotation hooks after first snippet compilation
|
||||
|
||||
### Tools. kapt
|
||||
|
||||
- [`KT-43686`](https://youtrack.jetbrains.com/issue/KT-43686) KaptWithoutKotlincTask should use `@CompileClasspath` for `kotlinStdlibClasspath` for cache relocateability.
|
||||
- [`KT-44130`](https://youtrack.jetbrains.com/issue/KT-44130) KAPT changes field order in 1.4.30-M1
|
||||
- [`KT-44909`](https://youtrack.jetbrains.com/issue/KT-44909) Kapt: ReenteringLazyValueComputationException without stacktrace caused by `when` expression with sealed class function without explicit return type
|
||||
- [`KT-45168`](https://youtrack.jetbrains.com/issue/KT-45168) KAPT: Java stubs generated for Kotlin files generated by annotation processors
|
||||
|
||||
|
||||
## 1.4.32
|
||||
|
||||
### IDE
|
||||
|
||||
- [`KT-43824`](https://youtrack.jetbrains.com/issue/KT-43824) KtLightClassForSourceDeclaration#isInheritor works in a different way than java implementation
|
||||
- [`KT-45287`](https://youtrack.jetbrains.com/issue/KT-45287) LightClasses: `KtLightSimpleModifierList` is no more a parent of `KtLightAnnotationForSourceEntry`
|
||||
- [`KT-45291`](https://youtrack.jetbrains.com/issue/KT-45291) LightClasses: can't get annotations for constructor val-parameter
|
||||
- [`KT-45417`](https://youtrack.jetbrains.com/issue/KT-45417) ULC leakage of primitive type annotations
|
||||
|
||||
### Tools. CLI
|
||||
|
||||
- [`KT-44758`](https://youtrack.jetbrains.com/issue/KT-44758) kotlin-compiler-embeddable dependency includes unshaded `fastutil` package
|
||||
- [`KT-45007`](https://youtrack.jetbrains.com/issue/KT-45007) Concurrent Kotlin script compilation/execution results in NullPointerException in KeyedExtensionCollector.getPoint()
|
||||
|
||||
## 1.4.31
|
||||
|
||||
### Compiler
|
||||
|
||||
- [`KT-39776`](https://youtrack.jetbrains.com/issue/KT-39776) 2020.3+: Unresolved reference to Kotlin stdlib function
|
||||
|
||||
### IDE. Gradle Integration
|
||||
|
||||
- [`KT-44845`](https://youtrack.jetbrains.com/issue/KT-44845) After update to Kotlin 1.4.30 all external dependencies is unresolved in IDE with kotlin.mpp.enableGranularSourceSetsMetadata=true
|
||||
|
||||
### IDE. Gradle. Script
|
||||
|
||||
- [`KTIJ-11137`](https://youtrack.jetbrains.com/issue/KTIJ-1137) build.gradle.kts: Fatal error during save/load standalone scripts settings
|
||||
- [`KTIJ-898`](https://youtrack.jetbrains.com/issue/KTIJ-898) Unable to import with Kotlin DSL buildscript - NullPointerException in KotlinDslScriptModelProcessorKt.toListOfScriptModels
|
||||
|
||||
### IDE. Multiplatform
|
||||
|
||||
- [`KTIJ-1200`](https://youtrack.jetbrains.com/issue/KTIJ-1200) KotlinIconProviderKt.addExpectActualMarker takes up to 180+ seconds
|
||||
|
||||
### IDE
|
||||
|
||||
#### Fixes
|
||||
|
||||
- [`KT-44697`](https://youtrack.jetbrains.com/issue/KT-44697) New JVM IR backend notification - narrow its triggering to Kotlin projects
|
||||
- [`KT-44523`](https://youtrack.jetbrains.com/issue/KT-44523) IDE notification for trying new JVM backend
|
||||
- [`KTIJ-696`](https://youtrack.jetbrains.com/issue/KTIJ-696) Freeze during startup of IDEA with intellij project with Kotlin (211-1.4.10-release-IJ1440)
|
||||
|
||||
## 1.4.30
|
||||
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -381,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()
|
||||
@@ -399,7 +376,6 @@ private class InlineFunctionsMap(
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(sourceFile: File) {
|
||||
storage.remove(pathConverter.toPath(sourceFile))
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
188
build.gradle.kts
188
build.gradle.kts
@@ -14,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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +27,18 @@ buildscript {
|
||||
dependencies {
|
||||
bootstrapCompilerClasspath(kotlin("compiler-embeddable", bootstrapKotlinVersion))
|
||||
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.30")
|
||||
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
|
||||
@@ -88,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",
|
||||
@@ -107,7 +111,9 @@ 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 {
|
||||
@@ -177,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
|
||||
@@ -186,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.5.30-dev-1916"
|
||||
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)
|
||||
@@ -200,11 +207,15 @@ extra["intellijSeparateSdks"] = intellijSeparateSdks
|
||||
|
||||
extra["IntellijCoreDependencies"] =
|
||||
listOf(
|
||||
"asm-all-8.0.1",
|
||||
when {
|
||||
Platform[202].orHigher() -> "asm-all-8.0.1"
|
||||
else -> "asm-all-7.0.1"
|
||||
},
|
||||
"guava",
|
||||
"jdom",
|
||||
"jna",
|
||||
"log4j",
|
||||
if (Platform[201].orHigher()) null else "picocontainer",
|
||||
"snappy-in-java",
|
||||
"streamex",
|
||||
"trove4j"
|
||||
@@ -287,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",
|
||||
@@ -307,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",
|
||||
@@ -316,49 +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",
|
||||
":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
|
||||
@@ -403,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")
|
||||
}
|
||||
@@ -422,29 +397,6 @@ val defaultJavaHome = jdkPath(if (Platform[203].orHigher()) "11" else defaultJvm
|
||||
val ignoreTestFailures by extra(project.kotlinBuildProperties.ignoreTestFailures)
|
||||
|
||||
allprojects {
|
||||
val mirrorRepo: String? = findProperty("maven.repository.mirror")?.toString()
|
||||
|
||||
repositories {
|
||||
kotlinBuildLocalRepo(project)
|
||||
mirrorRepo?.let(::maven)
|
||||
|
||||
internalBootstrapRepo?.let(::maven)
|
||||
bootstrapKotlinRepo?.let(::maven)
|
||||
maven(protobufRepo)
|
||||
|
||||
maven(intellijRepo)
|
||||
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-dependencies")
|
||||
maven("https://dl.google.com/dl/android/maven2")
|
||||
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
|
||||
|
||||
jcenter()
|
||||
}
|
||||
|
||||
if (path.startsWith(":kotlin-ide.")) {
|
||||
return@allprojects
|
||||
}
|
||||
|
||||
configurations.maybeCreate("embedded").apply {
|
||||
isCanBeConsumed = false
|
||||
@@ -473,6 +425,25 @@ allprojects {
|
||||
// therefore it is disabled by default
|
||||
// buildDir = File(commonBuildDir, project.name)
|
||||
|
||||
val mirrorRepo: String? = findProperty("maven.repository.mirror")?.toString()
|
||||
|
||||
repositories {
|
||||
kotlinBuildLocalRepo(project)
|
||||
mirrorRepo?.let(::maven)
|
||||
|
||||
internalBootstrapRepo?.let(::maven)
|
||||
bootstrapKotlinRepo?.let(::maven)
|
||||
maven(protobufRepo)
|
||||
|
||||
maven(intellijRepo)
|
||||
|
||||
mavenCentral()
|
||||
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-dependencies")
|
||||
maven("https://dl.google.com/dl/android/maven2")
|
||||
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
|
||||
|
||||
jcenter()
|
||||
}
|
||||
|
||||
configureJvmProject(javaHome!!, jvmTarget!!)
|
||||
|
||||
@@ -548,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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,8 +539,6 @@ 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")
|
||||
@@ -608,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")
|
||||
}
|
||||
}
|
||||
@@ -878,12 +844,11 @@ tasks {
|
||||
":idea:idea-fir:test",
|
||||
":idea:idea-frontend-api:test",
|
||||
":idea:idea-frontend-fir:test",
|
||||
":idea:idea-frontend-fir:idea-fir-low-level-api:test",
|
||||
":plugins:uast-kotlin-fir:test",
|
||||
":idea:idea-fir-fe10-binding:test"
|
||||
":idea:idea-frontend-fir:idea-fir-low-level-api:test"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
register("android-ide-tests") {
|
||||
dependsOn("dist")
|
||||
dependsOn(
|
||||
@@ -929,6 +894,7 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
register("kaptIdeTest") {
|
||||
dependsOn(":kotlin-annotation-processing:test")
|
||||
dependsOn(":kotlin-annotation-processing-base:test")
|
||||
@@ -942,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")
|
||||
@@ -968,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"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1026,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?
|
||||
@@ -1111,7 +1113,7 @@ fun Project.configureJvmProject(javaHome: String, javaVersion: String) {
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jdkHome = javaHome.takeUnless { kotlinBuildProperties.suppressJdkHomeWarning }
|
||||
kotlinOptions.jdkHome = javaHome
|
||||
kotlinOptions.jvmTarget = javaVersion
|
||||
kotlinOptions.freeCompilerArgs += "-Xjvm-default=compatibility"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.30")
|
||||
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.30")
|
||||
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")}")
|
||||
|
||||
@@ -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,5 +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.suppressJdkHomeWarning: Boolean get() = getBoolean("kotlin.suppress.jdkHome.warning", false)
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -229,6 +229,23 @@ fun Project.publish(moduleMetadata: Boolean = false, configure: MavenPublication
|
||||
publication.configure()
|
||||
}
|
||||
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.idePluginDependency(block: () -> Unit) {
|
||||
val shouldActivate = rootProject.findProperty("publish.ide.plugin.dependencies")?.toString()?.toBoolean() == true
|
||||
if (shouldActivate) {
|
||||
@@ -236,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>()
|
||||
|
||||
@@ -309,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()
|
||||
@@ -334,7 +314,7 @@ fun Project.publishTestJar(projects: List<String>) {
|
||||
|
||||
sourcesJar {
|
||||
from {
|
||||
projects.map { project(it).testSourceSet.allSource }
|
||||
project(projectName).testSourceSet.allSource
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,11 @@ import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.ModuleDependency
|
||||
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.kotlin.dsl.accessors.runtime.addDependencyTo
|
||||
import org.gradle.kotlin.dsl.closureOf
|
||||
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,87 +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 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,
|
||||
|
||||
@@ -93,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")
|
||||
}
|
||||
|
||||
publish()
|
||||
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)
|
||||
}
|
||||
@@ -117,13 +117,8 @@ fun MutableCollection<JdkId>.discoverJdksOnMacOS(project: Project) {
|
||||
for (rex in macOsJavaHomeOutRegexes) {
|
||||
val matchResult = rex.matchEntire(line)
|
||||
if (matchResult != null) {
|
||||
val jdkHomeDir = File(matchResult.groupValues[3])
|
||||
// Filter out JRE installed at /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/
|
||||
// and shown by the java_home tool
|
||||
if (!jdkHomeDir.path.contains("JavaAppletPlugin.plugin")) {
|
||||
addIfBetter(project, matchResult.groupValues[1], matchResult.groupValues[0], jdkHomeDir)
|
||||
break
|
||||
}
|
||||
addIfBetter(project, matchResult.groupValues[1], matchResult.groupValues[0], File(matchResult.groupValues[3]))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -164,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
|
||||
|
||||
@@ -27,9 +27,17 @@ dependencies {
|
||||
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())
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ interface TypeMappingContext<Writer : JvmDescriptorTypeWriter<Type>> {
|
||||
val typeContext: TypeSystemCommonBackendContextForTypeMapping
|
||||
|
||||
fun getClassInternalName(typeConstructor: TypeConstructorMarker): String
|
||||
fun getScriptInternalName(typeConstructor: TypeConstructorMarker): String
|
||||
fun Writer.writeGenericType(type: SimpleTypeMarker, asmType: Type, mode: TypeMappingMode)
|
||||
}
|
||||
|
||||
@@ -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,16 +6,14 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.inline.loadCompiledInlineFunction
|
||||
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
|
||||
@@ -67,14 +65,13 @@ 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
|
||||
val methodNode = InlineCodegen.createSpecialInlineMethodNodeFromBinaries(calleeDescriptor, state)
|
||||
|
||||
return isMetadataParameterUsedInCompiledMethodBody(metadataParameterIndex, methodNode)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,8 +1251,8 @@ 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(),
|
||||
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,15 +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));
|
||||
return new InlineCodegenForDefaultBody(functionDescriptor, this, state, methodOwner, signature, sourceCompiler);
|
||||
}
|
||||
else {
|
||||
return new PsiInlineCodegen(this, state, functionDescriptor, methodOwner, signature, typeParameterMappings, sourceCompiler,
|
||||
typeMapper.mapOwner(descriptor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3556,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);
|
||||
@@ -4929,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 {
|
||||
@@ -5233,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);
|
||||
@@ -5290,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;
|
||||
}
|
||||
@@ -5542,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,45 +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.
|
||||
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
|
||||
@@ -165,32 +292,46 @@ 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 generateAssertFieldIfNeeded(info: RootInliningContext)
|
||||
@@ -206,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)) {
|
||||
@@ -258,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 {
|
||||
@@ -281,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,14 +5,35 @@
|
||||
|
||||
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
|
||||
|
||||
@@ -26,9 +47,7 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
||||
|
||||
abstract val invokeMethod: Method
|
||||
|
||||
abstract val invokeMethodParameters: List<KotlinType?>
|
||||
|
||||
abstract val invokeMethodReturnType: KotlinType?
|
||||
abstract val invokeMethodDescriptor: FunctionDescriptor
|
||||
|
||||
abstract val capturedVars: List<CapturedParamDesc>
|
||||
|
||||
@@ -37,7 +56,7 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
||||
|
||||
lateinit var node: SMAPAndMethodNode
|
||||
|
||||
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
|
||||
abstract fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>)
|
||||
|
||||
open val hasDispatchReceiver = true
|
||||
|
||||
@@ -48,7 +67,7 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -56,9 +75,14 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,77 +90,115 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
||||
object NonInlineableArgumentForInlineableParameterCalledInSuspend : FunctionalArgument
|
||||
object NonInlineableArgumentForInlineableSuspendParameter : FunctionalArgument
|
||||
|
||||
abstract class ExpressionLambda(isCrossInline: Boolean) : LambdaInfo(isCrossInline) {
|
||||
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>,
|
||||
isCrossinline: Boolean,
|
||||
override val lambdaClassType: Type,
|
||||
private val capturedArgs: Array<Type>,
|
||||
val parameterDescriptor: ValueParameterDescriptor,
|
||||
val offset: Int,
|
||||
val needReification: Boolean,
|
||||
sourceCompiler: SourceCompilerForInline
|
||||
) : LambdaInfo(isCrossinline) {
|
||||
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(
|
||||
@@ -146,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,
|
||||
@@ -823,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
|
||||
@@ -1049,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(
|
||||
|
||||
@@ -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,47 +5,44 @@
|
||||
|
||||
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
|
||||
private val actualDispatchReceiver: Type = methodOwner
|
||||
) : InlineCodegen<ExpressionCodegen>(
|
||||
codegen, state, signature, typeParameterMappings, sourceCompiler,
|
||||
codegen, state, function, methodOwner, signature, typeParameterMappings, sourceCompiler,
|
||||
ReifiedTypeInliner(
|
||||
typeParameterMappings, PsiInlineIntrinsicsSupport(state), codegen.typeSystem,
|
||||
state.languageVersionSettings, state.unifiedNullChecks
|
||||
@@ -63,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()
|
||||
}
|
||||
@@ -94,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*/
|
||||
@@ -140,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 {
|
||||
@@ -159,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: LambdaInfo? = 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,
|
||||
isCrossInline: Boolean,
|
||||
override val isBoundCallableReference: Boolean
|
||||
) : ExpressionLambda(isCrossInline) {
|
||||
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, parameterDescriptor.isCrossinline, 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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -579,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 {
|
||||
|
||||
@@ -37,7 +37,7 @@ 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()
|
||||
@@ -103,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),
|
||||
@@ -119,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,10 +31,12 @@ 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) {
|
||||
do {
|
||||
val changes = ConstantConditionsOptimization(internalClassName, methodNode).run()
|
||||
if (changes) deadCodeElimination.transform(internalClassName, methodNode)
|
||||
} while (changes)
|
||||
}
|
||||
|
||||
@@ -48,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)
|
||||
@@ -70,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>) {
|
||||
|
||||
@@ -17,9 +17,8 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.remove
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InstructionLivenessAnalyzer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||
@@ -28,14 +27,21 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class DeadCodeEliminationMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
val liveness = InstructionLivenessAnalyzer(methodNode).analyze()
|
||||
transformWithResult(internalClassName, methodNode)
|
||||
}
|
||||
|
||||
fun transformWithResult(internalClassName: String, methodNode: MethodNode): Result {
|
||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
||||
return removeDeadCodeByFrames(methodNode, frames)
|
||||
}
|
||||
|
||||
fun removeDeadCodeByFrames(methodNode: MethodNode, frames: Array<out Any?>): Result {
|
||||
val insnsToRemove = ArrayList<AbstractInsnNode>()
|
||||
|
||||
val insns = methodNode.instructions.toArray()
|
||||
for (i in insns.indices) {
|
||||
val insn = insns[i]
|
||||
if (shouldRemove(insn, i, liveness)) {
|
||||
if (shouldRemove(insn, i, frames)) {
|
||||
insnsToRemove.add(insn)
|
||||
}
|
||||
}
|
||||
@@ -45,31 +51,46 @@ class DeadCodeEliminationMethodTransformer : MethodTransformer() {
|
||||
// Remove empty try-catch blocks to make sure we don't break data flow analysis invariants by dead code elimination.
|
||||
methodNode.removeEmptyCatchBlocks()
|
||||
|
||||
methodNode.removeUnusedLocalVariables()
|
||||
return Result(insnsToRemove.toSet())
|
||||
}
|
||||
|
||||
private fun shouldRemove(insn: AbstractInsnNode, index: Int, liveness: BooleanArray): Boolean {
|
||||
if (insn !is LineNumberNode) return !liveness[index]
|
||||
private fun shouldRemove(insn: AbstractInsnNode, index: Int, frames: Array<out Any?>): Boolean =
|
||||
when (insn) {
|
||||
is LabelNode ->
|
||||
// Do not remove label nodes because they can be referred by try/catch blocks or local variables table
|
||||
false
|
||||
is LineNumberNode ->
|
||||
isDeadLineNumber(insn, index, frames)
|
||||
else ->
|
||||
frames[index] == null
|
||||
}
|
||||
|
||||
private fun isDeadLineNumber(insn: LineNumberNode, index: Int, frames: Array<out Any?>): Boolean {
|
||||
// Line number node is "dead" if the corresponding line number interval
|
||||
// contains at least one "dead" meaningful instruction and no "live" meaningful instructions.
|
||||
var finger: AbstractInsnNode = insn
|
||||
var fingerIndex = index
|
||||
var hasDeadInsn = false
|
||||
while (true) {
|
||||
loop@ while (true) {
|
||||
finger = finger.next ?: break
|
||||
fingerIndex++
|
||||
when (finger) {
|
||||
is LabelNode ->
|
||||
continue
|
||||
continue@loop
|
||||
is LineNumberNode ->
|
||||
if (finger.line != insn.line) return hasDeadInsn
|
||||
else -> {
|
||||
if (liveness[fingerIndex]) return false
|
||||
if (frames[fingerIndex] != null) return false
|
||||
hasDeadInsn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
class Result(private val removedNodes: Set<AbstractInsnNode>) {
|
||||
fun hasRemovedAnything() = removedNodes.isNotEmpty()
|
||||
fun isRemoved(node: AbstractInsnNode) = removedNodes.contains(node)
|
||||
fun isAlive(node: AbstractInsnNode) = !isRemoved(node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,12 @@ package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.coroutines.RELEASE_COROUTINES_VERSION_SETTINGS
|
||||
import org.jetbrains.kotlin.codegen.coroutines.coroutinesJvmInternalPackageFqName
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.codegen.topLevelClassInternalName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
@@ -178,7 +174,7 @@ fun AbstractInsnNode.isUnboxing(state: GenerationState) =
|
||||
isPrimitiveUnboxing() || isJavaLangClassUnboxing() || isInlineClassUnboxing(state)
|
||||
|
||||
fun AbstractInsnNode.isBoxing(state: GenerationState) =
|
||||
isPrimitiveBoxing() || isJavaLangClassBoxing() || isInlineClassBoxing(state) || isCoroutinePrimitiveBoxing()
|
||||
isPrimitiveBoxing() || isJavaLangClassBoxing() || isInlineClassBoxing(state)
|
||||
|
||||
fun AbstractInsnNode.isPrimitiveUnboxing() =
|
||||
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
|
||||
@@ -215,19 +211,6 @@ fun AbstractInsnNode.isPrimitiveBoxing() =
|
||||
isBoxingMethodDescriptor()
|
||||
}
|
||||
|
||||
private val BOXING_CLASS_INTERNAL_NAME =
|
||||
RELEASE_COROUTINES_VERSION_SETTINGS.coroutinesJvmInternalPackageFqName().child(Name.identifier("Boxing")).topLevelClassInternalName()
|
||||
|
||||
private fun isJvmPrimitiveName(name: String) = JvmPrimitiveType.values().any { it.javaKeywordName == name }
|
||||
|
||||
fun AbstractInsnNode.isCoroutinePrimitiveBoxing(): Boolean {
|
||||
return isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner == BOXING_CLASS_INTERNAL_NAME &&
|
||||
name.startsWith("box") &&
|
||||
isJvmPrimitiveName(name.substring(3).lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
|
||||
val ownerType = Type.getObjectType(owner)
|
||||
return desc == Type.getMethodDescriptor(ownerType, AsmUtil.unboxType(ownerType))
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.debugText
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isLoadOperation
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peekWords
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
@@ -29,66 +32,91 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
|
||||
import java.util.*
|
||||
|
||||
private typealias Transformation = (AbstractInsnNode) -> Unit
|
||||
|
||||
class PopBackwardPropagationTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
if (!OptimizationMethodVisitor.canBeOptimizedUsingSourceInterpreter(methodNode)) return
|
||||
if (methodNode.instructions.any { it.isPop() || it.isPurePush() }) {
|
||||
Transformer(methodNode).transform()
|
||||
}
|
||||
Transformer(methodNode).transform()
|
||||
}
|
||||
|
||||
private class Transformer(val methodNode: MethodNode) {
|
||||
private val REPLACE_WITH_NOP: Transformation = { insnList.set(it, InsnNode(Opcodes.NOP)) }
|
||||
private val REPLACE_WITH_POP1: Transformation = { insnList.set(it, InsnNode(Opcodes.POP)) }
|
||||
private val REPLACE_WITH_POP2: Transformation = { insnList.set(it, InsnNode(Opcodes.POP2)) }
|
||||
private val INSERT_POP1_AFTER: Transformation = { insnList.insert(it, InsnNode(Opcodes.POP)) }
|
||||
private val INSERT_POP2_AFTER: Transformation = { insnList.insert(it, InsnNode(Opcodes.POP2)) }
|
||||
private interface Transformation {
|
||||
fun apply(insn: AbstractInsnNode)
|
||||
}
|
||||
|
||||
private inline fun Transformation(crossinline body: (AbstractInsnNode) -> Unit): Transformation =
|
||||
object : Transformation {
|
||||
override fun apply(insn: AbstractInsnNode) {
|
||||
body(insn)
|
||||
}
|
||||
}
|
||||
|
||||
private val REPLACE_WITH_NOP = Transformation { insnList.set(it, createRemovableNopInsn()) }
|
||||
private val REPLACE_WITH_POP1 = Transformation { insnList.set(it, InsnNode(Opcodes.POP)) }
|
||||
private val REPLACE_WITH_POP2 = Transformation { insnList.set(it, InsnNode(Opcodes.POP2)) }
|
||||
private val INSERT_POP1_AFTER = Transformation { insnList.insert(it, InsnNode(Opcodes.POP)) }
|
||||
private val INSERT_POP2_AFTER = Transformation { insnList.insert(it, InsnNode(Opcodes.POP2)) }
|
||||
|
||||
private val insnList = methodNode.instructions
|
||||
|
||||
private val insns = insnList.toArray()
|
||||
|
||||
private val dontTouchInsnIndices = BitSet(insns.size)
|
||||
private val transformations = hashMapOf<AbstractInsnNode, Transformation>()
|
||||
private val removableNops = hashSetOf<InsnNode>()
|
||||
|
||||
private val frames by lazy { analyzeMethodBody() }
|
||||
|
||||
fun transform() {
|
||||
val frames = Analyzer(HazardsTrackingInterpreter()).analyze("fake", methodNode)
|
||||
for ((i, insn) in insns.withIndex()) {
|
||||
val frame = frames[i] ?: continue
|
||||
when (insn.opcode) {
|
||||
Opcodes.POP ->
|
||||
frame.top()?.let { input ->
|
||||
// If this POP won't be removed, other POPs that touch the same values have to stay as well.
|
||||
if (input.insns.any { it.shouldKeep() } || input.longerWhenFusedWithPop()) {
|
||||
input.insns.markAsDontTouch()
|
||||
}
|
||||
}
|
||||
Opcodes.POP2 -> frame.peekWords(2)?.forEach { it.insns.markAsDontTouch() }
|
||||
Opcodes.DUP_X1 -> frame.peekWords(1, 1)?.forEach { it.insns.markAsDontTouch() }
|
||||
Opcodes.DUP2_X1 -> frame.peekWords(2, 1)?.forEach { it.insns.markAsDontTouch() }
|
||||
Opcodes.DUP_X2 -> frame.peekWords(1, 2)?.forEach { it.insns.markAsDontTouch() }
|
||||
Opcodes.DUP2_X2 -> frame.peekWords(2, 2)?.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
}
|
||||
if (insns.none { it.isPop() || it.isPurePush() }) return
|
||||
|
||||
val transformations = hashMapOf<AbstractInsnNode, Transformation>()
|
||||
for ((i, insn) in insns.withIndex()) {
|
||||
computeTransformations()
|
||||
for ((insn, transformation) in transformations.entries) {
|
||||
transformation.apply(insn)
|
||||
}
|
||||
postprocessNops()
|
||||
}
|
||||
|
||||
private fun analyzeMethodBody(): Array<out Frame<SourceValue>?> {
|
||||
val frames = Analyzer<SourceValue>(HazardsTrackingInterpreter()).analyze("fake", methodNode)
|
||||
|
||||
postprocessStackHazards(frames)
|
||||
|
||||
return frames
|
||||
}
|
||||
|
||||
private fun postprocessStackHazards(frames: Array<out Frame<SourceValue>?>) {
|
||||
val insns = methodNode.instructions.toArray()
|
||||
for (i in frames.indices) {
|
||||
val frame = frames[i] ?: continue
|
||||
if (insn.opcode == Opcodes.POP) {
|
||||
val input = frame.top() ?: continue
|
||||
if (input.insns.none { it.shouldKeep() }) {
|
||||
transformations[insn] = REPLACE_WITH_NOP
|
||||
input.insns.forEach {
|
||||
if (it !in transformations) {
|
||||
transformations[it] = it.combineWithPop(frames, input.size)
|
||||
}
|
||||
}
|
||||
val insn = insns[i]
|
||||
|
||||
when (insn.opcode) {
|
||||
Opcodes.POP2 -> {
|
||||
val top2 = frame.peekWords(2) ?: throwIncorrectBytecode(insn, frame)
|
||||
top2.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP_X1 -> {
|
||||
val top2 = frame.peekWords(1, 1) ?: throwIncorrectBytecode(insn, frame)
|
||||
top2.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP2_X1 -> {
|
||||
val top3 = frame.peekWords(2, 1) ?: throwIncorrectBytecode(insn, frame)
|
||||
top3.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP_X2 -> {
|
||||
val top3 = frame.peekWords(1, 2) ?: throwIncorrectBytecode(insn, frame)
|
||||
top3.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
Opcodes.DUP2_X2 -> {
|
||||
val top4 = frame.peekWords(2, 2) ?: throwIncorrectBytecode(insn, frame)
|
||||
top4.forEach { it.insns.markAsDontTouch() }
|
||||
}
|
||||
}
|
||||
}
|
||||
for ((insn, transformation) in transformations.entries) {
|
||||
transformation(insn)
|
||||
}
|
||||
}
|
||||
|
||||
private fun throwIncorrectBytecode(insn: AbstractInsnNode?, frame: Frame<SourceValue>): Nothing {
|
||||
throw AssertionError("Incorrect bytecode at ${methodNode.instructions.indexOf(insn)}: ${insn.debugText} $frame")
|
||||
}
|
||||
|
||||
private inner class HazardsTrackingInterpreter : SourceInterpreter(Opcodes.API_VERSION) {
|
||||
@@ -105,7 +133,9 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
||||
}
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: SourceValue): SourceValue {
|
||||
value.insns.markAsDontTouch()
|
||||
if (insn.opcode != Opcodes.CHECKCAST && !insn.isPrimitiveTypeConversion()) {
|
||||
value.insns.markAsDontTouch()
|
||||
}
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
|
||||
@@ -134,48 +164,153 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun SourceValue.longerWhenFusedWithPop() = insns.fold(0) { x, insn ->
|
||||
when {
|
||||
insn.isPurePush() -> x - 1
|
||||
insn.isPrimitiveBoxing() || insn.isPrimitiveTypeConversion() -> x
|
||||
else -> x + 1
|
||||
}
|
||||
} > 0
|
||||
|
||||
private fun AbstractInsnNode.combineWithPop(frames: Array<out Frame<SourceValue>?>, resultSize: Int): Transformation =
|
||||
private fun computeTransformations() {
|
||||
transformations.clear()
|
||||
|
||||
for (i in insns.indices) {
|
||||
if (frames[i] == null) continue
|
||||
val insn = insns[i]
|
||||
|
||||
if (insn.opcode == Opcodes.POP) {
|
||||
propagatePopBackwards(insn, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun propagatePopBackwards(insn: AbstractInsnNode, poppedValueSize: Int) {
|
||||
if (transformations.containsKey(insn)) return
|
||||
|
||||
when {
|
||||
isPurePush() -> REPLACE_WITH_NOP
|
||||
isPrimitiveBoxing() || isPrimitiveTypeConversion() -> {
|
||||
val index = insnList.indexOf(this)
|
||||
val frame = frames[index] ?: throw AssertionError("dead instruction #$index used by non-dead instruction")
|
||||
val input = frame.top() ?: throw AssertionError("coercion instruction at #$index has no input")
|
||||
when (input.size) {
|
||||
1 -> REPLACE_WITH_POP1
|
||||
2 -> REPLACE_WITH_POP2
|
||||
else -> throw AssertionError("Unexpected pop value size: ${input.size}")
|
||||
insn.opcode == Opcodes.POP -> {
|
||||
val inputTop = getInputTop(insn)
|
||||
val sources = inputTop.insns
|
||||
if (sources.all { !isDontTouch(it) } && sources.any { isTransformablePopOperand(it) }) {
|
||||
transformations[insn] = replaceWithNopTransformation()
|
||||
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
||||
}
|
||||
}
|
||||
else ->
|
||||
when (resultSize) {
|
||||
1 -> INSERT_POP1_AFTER
|
||||
2 -> INSERT_POP2_AFTER
|
||||
else -> throw AssertionError("Unexpected pop value size: $resultSize")
|
||||
|
||||
insn.opcode == Opcodes.CHECKCAST -> {
|
||||
val inputTop = getInputTop(insn)
|
||||
val sources = inputTop.insns
|
||||
val resultType = (insn as TypeInsnNode).desc
|
||||
if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) {
|
||||
transformations[insn] = replaceWithNopTransformation()
|
||||
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
||||
} else {
|
||||
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
|
||||
}
|
||||
}
|
||||
|
||||
insn.isPrimitiveBoxing() -> {
|
||||
val boxedValueSize = getInputTop(insn).size
|
||||
transformations[insn] = replaceWithPopTransformation(boxedValueSize)
|
||||
}
|
||||
|
||||
insn.isPurePush() -> {
|
||||
transformations[insn] = replaceWithNopTransformation()
|
||||
}
|
||||
|
||||
insn.isPrimitiveTypeConversion() -> {
|
||||
val inputTop = getInputTop(insn)
|
||||
val sources = inputTop.insns
|
||||
if (sources.all { !isDontTouch(it) }) {
|
||||
transformations[insn] = replaceWithNopTransformation()
|
||||
sources.forEach { propagatePopBackwards(it, inputTop.size) }
|
||||
} else {
|
||||
transformations[insn] = replaceWithPopTransformation(poppedValueSize)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun postprocessNops() {
|
||||
var node: AbstractInsnNode? = insnList.first
|
||||
var hasRemovableNops = false
|
||||
while (node != null) {
|
||||
node = node.next
|
||||
val begin = node ?: break
|
||||
while (node != null && node !is LabelNode) {
|
||||
if (node in removableNops) {
|
||||
hasRemovableNops = true
|
||||
}
|
||||
node = node.next
|
||||
}
|
||||
val end = node
|
||||
if (hasRemovableNops) {
|
||||
removeUnneededNopsInRange(begin, end)
|
||||
}
|
||||
hasRemovableNops = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeUnneededNopsInRange(begin: AbstractInsnNode, end: AbstractInsnNode?) {
|
||||
var node: AbstractInsnNode? = begin
|
||||
var keepNop = true
|
||||
while (node != null && node != end) {
|
||||
if (node in removableNops && !keepNop) {
|
||||
node = insnList.removeNodeGetNext(node)
|
||||
} else {
|
||||
if (node.isMeaningful) keepNop = false
|
||||
node = node.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceWithPopTransformation(size: Int): Transformation =
|
||||
when (size) {
|
||||
1 -> REPLACE_WITH_POP1
|
||||
2 -> REPLACE_WITH_POP2
|
||||
else -> throw AssertionError("Unexpected pop value size: $size")
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.shouldKeep() =
|
||||
dontTouchInsnIndices[insnList.indexOf(this)]
|
||||
private fun insertPopAfterTransformation(size: Int): Transformation =
|
||||
when (size) {
|
||||
1 -> INSERT_POP1_AFTER
|
||||
2 -> INSERT_POP2_AFTER
|
||||
else -> throw AssertionError("Unexpected pop value size: $size")
|
||||
}
|
||||
|
||||
private fun replaceWithNopTransformation(): Transformation =
|
||||
REPLACE_WITH_NOP
|
||||
|
||||
private fun createRemovableNopInsn() =
|
||||
InsnNode(Opcodes.NOP).apply { removableNops.add(this) }
|
||||
|
||||
private fun getInputTop(insn: AbstractInsnNode): SourceValue {
|
||||
val i = insnList.indexOf(insn)
|
||||
val frame = frames[i] ?: throw AssertionError("Unexpected dead instruction #$i")
|
||||
return frame.top() ?: throw AssertionError("Instruction #$i has empty stack on input")
|
||||
}
|
||||
|
||||
private fun isTransformableCheckcastOperand(it: AbstractInsnNode, resultType: String) =
|
||||
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
|
||||
|
||||
private fun isTransformablePopOperand(insn: AbstractInsnNode) =
|
||||
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isPurePush()
|
||||
|
||||
private fun isDontTouch(insn: AbstractInsnNode) =
|
||||
dontTouchInsnIndices[insnList.indexOf(insn)]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isPurePush() =
|
||||
isLoadOperation() || opcode in Opcodes.ACONST_NULL..Opcodes.LDC + 2 || isUnitInstance()
|
||||
isLoadOperation() ||
|
||||
opcode in Opcodes.ACONST_NULL..Opcodes.LDC + 2 ||
|
||||
isUnitInstance()
|
||||
|
||||
fun AbstractInsnNode.isPop() =
|
||||
opcode == Opcodes.POP || opcode == Opcodes.POP2
|
||||
|
||||
fun AbstractInsnNode.isUnitInstance() =
|
||||
opcode == Opcodes.GETSTATIC && this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
||||
opcode == Opcodes.GETSTATIC &&
|
||||
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
||||
|
||||
fun AbstractInsnNode.isPrimitiveTypeConversion() =
|
||||
opcode in Opcodes.I2L..Opcodes.I2S
|
||||
|
||||
@@ -19,12 +19,14 @@ package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.findPreviousOrNull
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class StackPeepholeOptimizationsTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
while (true) {
|
||||
if (!transformOnce(methodNode)) break
|
||||
while (transformOnce(methodNode)) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,26 +115,20 @@ class StackPeepholeOptimizationsTransformer : MethodTransformer() {
|
||||
opcode == Opcodes.DUP
|
||||
|
||||
private fun AbstractInsnNode.isPurePushOfSize1(): Boolean =
|
||||
!isLdcOfSize2() && (
|
||||
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
|
||||
opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
|
||||
opcode == Opcodes.FLOAD ||
|
||||
opcode == Opcodes.ALOAD ||
|
||||
isUnitInstance()
|
||||
)
|
||||
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
|
||||
opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
|
||||
opcode == Opcodes.FLOAD ||
|
||||
opcode == Opcodes.ALOAD ||
|
||||
isUnitInstance()
|
||||
|
||||
private fun AbstractInsnNode.isEliminatedByPop2() =
|
||||
isPurePushOfSize2() ||
|
||||
opcode == Opcodes.DUP2
|
||||
|
||||
private fun AbstractInsnNode.isPurePushOfSize2(): Boolean =
|
||||
isLdcOfSize2() ||
|
||||
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
|
||||
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
|
||||
opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 ||
|
||||
opcode == Opcodes.LLOAD ||
|
||||
opcode == Opcodes.DLOAD
|
||||
|
||||
private fun AbstractInsnNode.isLdcOfSize2(): Boolean =
|
||||
opcode == Opcodes.LDC && this is LdcInsnNode && (this.cst is Double || this.cst is Long)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,148 +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.optimization.common
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class InstructionLivenessAnalyzer(val method: MethodNode) {
|
||||
private val instructions = method.instructions
|
||||
private val nInsns = instructions.size()
|
||||
|
||||
private val isLive = BooleanArray(nInsns)
|
||||
|
||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
||||
private val queued = BooleanArray(nInsns)
|
||||
private val queue = IntArray(nInsns)
|
||||
private var top = 0
|
||||
|
||||
private val AbstractInsnNode.indexOf get() = instructions.indexOf(this)
|
||||
|
||||
fun analyze(): BooleanArray {
|
||||
if (nInsns == 0) return isLive
|
||||
checkAssertions()
|
||||
computeExceptionHandlersForEachInsn(method)
|
||||
initControlFlowAnalysis()
|
||||
traverseCfg()
|
||||
|
||||
// We consider labels referenced by LVs and TCBs always implicitly reachable
|
||||
// (so that they don't get accidentally removed, producing corrupted class files),
|
||||
// and let the client code decide what to do with redundant LVs and TCBs.
|
||||
localVariableAndTryCatchBlockLabelsAreAlwaysLive()
|
||||
|
||||
// Last label in a method is always implicitly reachable (preserving our implicit invariants).
|
||||
if (instructions.last is LabelNode) {
|
||||
isLive[instructions.last.indexOf] = true
|
||||
}
|
||||
|
||||
return isLive
|
||||
}
|
||||
|
||||
private fun traverseCfg() {
|
||||
while (top > 0) {
|
||||
val insn = queue[--top]
|
||||
val insnNode = method.instructions[insn]
|
||||
val insnOpcode = insnNode.opcode
|
||||
|
||||
when (insnNode.type) {
|
||||
AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME ->
|
||||
visitOpInsn(insn)
|
||||
AbstractInsnNode.JUMP_INSN ->
|
||||
visitJumpInsnNode(insnNode as JumpInsnNode, insn, insnOpcode)
|
||||
AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode)
|
||||
AbstractInsnNode.TABLESWITCH_INSN ->
|
||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode)
|
||||
else -> {
|
||||
if (insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN)) {
|
||||
visitOpInsn(insn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handlers[insn]?.forEach { tcb ->
|
||||
visitControlFlowEdge(tcb.handler.indexOf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun localVariableAndTryCatchBlockLabelsAreAlwaysLive() {
|
||||
for (localVariable in method.localVariables) {
|
||||
isLive[localVariable.start.indexOf] = true
|
||||
isLive[localVariable.end.indexOf] = true
|
||||
}
|
||||
|
||||
for (tcb in method.tryCatchBlocks) {
|
||||
isLive[tcb.start.indexOf] = true
|
||||
isLive[tcb.end.indexOf] = true
|
||||
isLive[tcb.handler.indexOf] = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAssertions() {
|
||||
if (instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
||||
throw AssertionError("Subroutines are deprecated since Java 6")
|
||||
}
|
||||
|
||||
private fun visitOpInsn(insn: Int) {
|
||||
visitControlFlowEdge(insn + 1)
|
||||
}
|
||||
|
||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode) {
|
||||
var jump = insnNode.dflt.indexOf
|
||||
visitControlFlowEdge(jump)
|
||||
for (label in insnNode.labels) {
|
||||
jump = instructions.indexOf(label)
|
||||
visitControlFlowEdge(jump)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode) {
|
||||
var jump = insnNode.dflt.indexOf
|
||||
visitControlFlowEdge(jump)
|
||||
for (label in insnNode.labels) {
|
||||
jump = label.indexOf
|
||||
visitControlFlowEdge(jump)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, insn: Int, insnOpcode: Int) {
|
||||
if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) {
|
||||
visitControlFlowEdge(insn + 1)
|
||||
}
|
||||
val jump = insnNode.label.indexOf
|
||||
visitControlFlowEdge(jump)
|
||||
}
|
||||
|
||||
private fun initControlFlowAnalysis() {
|
||||
visitControlFlowEdge(0)
|
||||
}
|
||||
|
||||
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
||||
for (tcb in m.tryCatchBlocks) {
|
||||
val begin = tcb.start.indexOf
|
||||
val end = tcb.end.indexOf
|
||||
for (j in begin until end) {
|
||||
var insnHandlers = handlers[j]
|
||||
if (insnHandlers == null) {
|
||||
insnHandlers = ArrayList<TryCatchBlockNode>()
|
||||
handlers[j] = insnHandlers
|
||||
}
|
||||
insnHandlers.add(tcb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitControlFlowEdge(insn: Int) {
|
||||
val changes = !isLive[insn]
|
||||
isLive[insn] = true
|
||||
if (changes && !queued[insn]) {
|
||||
queued[insn] = true
|
||||
queue[top++] = insn
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,7 +62,7 @@ import java.util.*
|
||||
* @author Dmitry Petrov
|
||||
*/
|
||||
open class MethodAnalyzer<V : Value>(
|
||||
private val owner: String,
|
||||
val owner: String,
|
||||
val method: MethodNode,
|
||||
protected val interpreter: Interpreter<V>
|
||||
) {
|
||||
|
||||
@@ -39,9 +39,9 @@ import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class RedundantNullCheckMethodTransformer(private val generationState: GenerationState) : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
do {
|
||||
val changes = TransformerPass(internalClassName, methodNode, generationState).run()
|
||||
} while (changes)
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
while (TransformerPass(internalClassName, methodNode, generationState).run()) {
|
||||
}
|
||||
}
|
||||
|
||||
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode, val generationState: GenerationState) {
|
||||
@@ -170,14 +170,14 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
}
|
||||
|
||||
private fun collectVariableDependentChecks() {
|
||||
for (insn in methodNode.instructions) {
|
||||
insnLoop@ for (insn in methodNode.instructions) {
|
||||
when {
|
||||
insn.isInstanceOfOrNullCheck() -> {
|
||||
val previous = insn.previous ?: continue
|
||||
val previous = insn.previous ?: continue@insnLoop
|
||||
if (previous.opcode == Opcodes.ALOAD) {
|
||||
addDependentCheck(insn, previous as VarInsnNode)
|
||||
} else if (previous.opcode == Opcodes.DUP) {
|
||||
val previous2 = previous.previous ?: continue
|
||||
val previous2 = previous.previous ?: continue@insnLoop
|
||||
if (previous2.opcode == Opcodes.ALOAD) {
|
||||
addDependentCheck(insn, previous2 as VarInsnNode)
|
||||
}
|
||||
@@ -185,36 +185,36 @@ class RedundantNullCheckMethodTransformer(private val generationState: Generatio
|
||||
}
|
||||
|
||||
insn.isCheckNotNull() -> {
|
||||
val previous = insn.previous ?: continue
|
||||
val previous = insn.previous ?: continue@insnLoop
|
||||
val aLoadInsn = if (previous.opcode == Opcodes.DUP) {
|
||||
previous.previous ?: continue
|
||||
previous.previous ?: continue@insnLoop
|
||||
} else previous
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) continue
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) continue@insnLoop
|
||||
addDependentCheck(insn, aLoadInsn as VarInsnNode)
|
||||
}
|
||||
|
||||
insn.isCheckParameterIsNotNull() -> {
|
||||
val ldcInsn = insn.previous ?: continue
|
||||
if (ldcInsn.opcode != Opcodes.LDC) continue
|
||||
val aLoadInsn = ldcInsn.previous ?: continue
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) continue
|
||||
val ldcInsn = insn.previous ?: continue@insnLoop
|
||||
if (ldcInsn.opcode != Opcodes.LDC) continue@insnLoop
|
||||
val aLoadInsn = ldcInsn.previous ?: continue@insnLoop
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) continue@insnLoop
|
||||
addDependentCheck(insn, aLoadInsn as VarInsnNode)
|
||||
}
|
||||
|
||||
insn.isCheckExpressionValueIsNotNull() -> {
|
||||
val ldcInsn = insn.previous ?: continue
|
||||
if (ldcInsn.opcode != Opcodes.LDC) continue
|
||||
val ldcInsn = insn.previous ?: continue@insnLoop
|
||||
if (ldcInsn.opcode != Opcodes.LDC) continue@insnLoop
|
||||
var aLoadInsn: VarInsnNode? = null
|
||||
val insn1 = ldcInsn.previous ?: continue
|
||||
val insn1 = ldcInsn.previous ?: continue@insnLoop
|
||||
if (insn1.opcode == Opcodes.ALOAD) {
|
||||
aLoadInsn = insn1 as VarInsnNode
|
||||
} else if (insn1.opcode == Opcodes.DUP) {
|
||||
val insn2 = insn1.previous ?: continue
|
||||
val insn2 = insn1.previous ?: continue@insnLoop
|
||||
if (insn2.opcode == Opcodes.ALOAD) {
|
||||
aLoadInsn = insn2 as VarInsnNode
|
||||
}
|
||||
}
|
||||
if (aLoadInsn == null) continue
|
||||
if (aLoadInsn == null) continue@insnLoop
|
||||
addDependentCheck(insn, aLoadInsn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,4 @@ class JvmCodegenStringTable @JvmOverloads constructor(
|
||||
ClassId(fqName.parent(), FqName.topLevel(fqName.shortName()), true)
|
||||
}
|
||||
}
|
||||
|
||||
override val isLocalClassIdReplacementKeptGeneric: Boolean
|
||||
get() = true
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ dependencies {
|
||||
compile(project(":compiler:fir:fir2ir"))
|
||||
compile(project(":compiler:fir:fir2ir:jvm-backend"))
|
||||
compile(project(":compiler:fir:checkers"))
|
||||
compile(project(":compiler:fir:checkers:checkers.jvm"))
|
||||
compile(project(":kotlin-util-klib"))
|
||||
compile(project(":kotlin-util-io"))
|
||||
|
||||
|
||||
@@ -36,13 +36,6 @@ enum class CompilerSystemProperties(val property: String, val alwaysDirectAccess
|
||||
KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY("kotlin.environment.keepalive"),
|
||||
COMPILE_DAEMON_CUSTOM_RUN_FILES_PATH_FOR_TESTS("kotlin.daemon.custom.run.files.path.for.tests"),
|
||||
KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"),
|
||||
|
||||
KOTLIN_STAT_ENABLED_PROPERTY("kotlin.plugin.stat.enabled"),
|
||||
KOTLIN_STAT_ENDPOINT_PROPERTY("kotlin.plugin.stat.endpoint"),
|
||||
KOTLIN_STAT_USER_PROPERTY("kotlin.plugin.stat.user"),
|
||||
KOTLIN_STAT_PASSWORD_PROPERTY("kotlin.plugin.stat.password"),
|
||||
KOTLIN_STAT_LABEl_PROPERTY("kotlin.plugin.stat.label"),
|
||||
|
||||
OS_NAME("os.name", alwaysDirectAccess = true),
|
||||
TMP_DIR("java.io.tmpdir"),
|
||||
USER_HOME("user.home", alwaysDirectAccess = true),
|
||||
|
||||
@@ -384,7 +384,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
"Unknown value for parameter -Xexplicit-api: '$explicitApi'. Value should be one of ${ExplicitApiMode.availableValues()}"
|
||||
)
|
||||
put(AnalysisFlags.extendedCompilerChecks, extendedCompilerChecks)
|
||||
put(AnalysisFlags.allowKotlinPackage, allowKotlinPackage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,18 +151,9 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var irModuleName: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@Argument(value = "-Xir-legacy-property-access", description = "Force property access via JS properties (requires -Xir-export-all)")
|
||||
var irLegacyPropertyAccess: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xir-base-class-in-metadata", description = "Write base class into metadata")
|
||||
var irBaseClassInMetadata: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xir-per-module", description = "Splits generated .js per-module")
|
||||
var irPerModule: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xir-per-module-output-name", description = "Adds a custom output name to the splitted js files")
|
||||
var irPerModuleOutputName: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@Argument(
|
||||
value = "-Xinclude",
|
||||
valueDescription = "<path>",
|
||||
@@ -170,16 +161,6 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var includes: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@Argument(
|
||||
value = "-Xcache-directories",
|
||||
valueDescription = "<path>",
|
||||
description = "A path to cache directories"
|
||||
)
|
||||
var cacheDirectories: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@Argument(value = "-Xir-build-cache", description = "Use compiler to build cache")
|
||||
var irBuildCache: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xgenerate-dts",
|
||||
description = "Generate TypeScript declarations .d.ts file alongside JS file. Available in IR backend only."
|
||||
@@ -231,7 +212,7 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
|
||||
}
|
||||
|
||||
fun K2JSCompilerArguments.isPreIrBackendDisabled(): Boolean =
|
||||
irOnly || irProduceJs || irProduceKlibFile || irBuildCache
|
||||
irOnly || irProduceJs || irProduceKlibFile
|
||||
|
||||
fun K2JSCompilerArguments.isIrBackendEnabled(): Boolean =
|
||||
irProduceKlibDir || irProduceJs || irProduceKlibFile || wasm || irBuildCache
|
||||
irProduceKlibDir || irProduceJs || irProduceKlibFile || wasm
|
||||
@@ -30,11 +30,6 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-include-runtime", description = "Include Kotlin runtime into the resulting JAR")
|
||||
var includeRuntime: Boolean by FreezableVar(false)
|
||||
|
||||
@DeprecatedOption(
|
||||
message = "This option is not working well with Gradle caching and will be removed in the future.",
|
||||
removeAfter = "1.7",
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@GradleOption(DefaultValues.StringNullDefault::class)
|
||||
@Argument(
|
||||
value = "-jdk-home",
|
||||
|
||||
@@ -38,10 +38,7 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalNextRoundChecker
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
|
||||
import org.jetbrains.kotlin.ir.backend.js.MainModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.compile
|
||||
import org.jetbrains.kotlin.ir.backend.js.generateKLib
|
||||
import org.jetbrains.kotlin.ir.backend.js.jsPhases
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrFactory
|
||||
import org.jetbrains.kotlin.js.config.*
|
||||
@@ -164,10 +161,9 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
|
||||
val outputFile = File(outputFilePath)
|
||||
|
||||
val moduleName = arguments.irModuleName ?: FileUtil.getNameWithoutExtension(outputFile)
|
||||
configurationJs.put(
|
||||
CommonConfigurationKeys.MODULE_NAME,
|
||||
moduleName
|
||||
arguments.irModuleName ?: FileUtil.getNameWithoutExtension(outputFile)
|
||||
)
|
||||
|
||||
// TODO: in this method at least 3 different compiler configurations are used (original, env.configuration, jsConfig.configuration)
|
||||
@@ -184,6 +180,17 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
// TODO: Handle non-empty main call arguments
|
||||
val mainCallArguments = if (K2JsArgumentConstants.NO_CALL == arguments.main) null else emptyList<String>()
|
||||
|
||||
val resolvedLibraries = jsResolveLibraries(
|
||||
libraries,
|
||||
configuration[JSConfigurationKeys.REPOSITORIES] ?: emptyList(),
|
||||
messageCollectorLogger(configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] ?: error("Could not find message collector"))
|
||||
)
|
||||
|
||||
val friendAbsolutePaths = friendLibraries.map { File(it).absolutePath }
|
||||
val friendDependencies = resolvedLibraries.getFullList().filter {
|
||||
it.libraryFile.absolutePath in friendAbsolutePaths
|
||||
}
|
||||
|
||||
if (arguments.irProduceKlibDir || arguments.irProduceKlibFile) {
|
||||
if (arguments.irProduceKlibFile) {
|
||||
require(outputFile.extension == KLIB_FILE_EXTENSION) { "Please set up .klib file as output" }
|
||||
@@ -194,12 +201,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
files = sourcesFiles,
|
||||
analyzer = AnalyzerWithCompilerReport(config.configuration),
|
||||
configuration = config.configuration,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
allDependencies = resolvedLibraries,
|
||||
friendDependencies = friendDependencies,
|
||||
irFactory = PersistentIrFactory(), // TODO IrFactoryImpl?
|
||||
outputKlibPath = outputFile.path,
|
||||
nopack = arguments.irProduceKlibDir,
|
||||
jsOutputName = arguments.irPerModuleOutputName,
|
||||
nopack = arguments.irProduceKlibDir
|
||||
)
|
||||
}
|
||||
|
||||
@@ -212,10 +218,9 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
if (sourcesFiles.isNotEmpty()) {
|
||||
messageCollector.report(ERROR, "Source files are not supported when -Xinclude is present")
|
||||
}
|
||||
val includesPath = File(includes).canonicalPath
|
||||
val mainLibPath = libraries.find { File(it).canonicalPath == includesPath }
|
||||
?: error("No library with name $includes ($includesPath) found")
|
||||
MainModule.Klib(mainLibPath)
|
||||
val allLibraries = resolvedLibraries.getFullList()
|
||||
val mainLib = allLibraries.find { it.libraryFile.absolutePath == File(includes).absolutePath }!!
|
||||
MainModule.Klib(mainLib)
|
||||
} else {
|
||||
MainModule.SourceFiles(sourcesFiles)
|
||||
}
|
||||
@@ -228,8 +233,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
config.configuration,
|
||||
PhaseConfig(wasmPhases),
|
||||
IrFactoryImpl,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
allDependencies = resolvedLibraries,
|
||||
friendDependencies = friendDependencies,
|
||||
exportedDeclarations = setOf(FqName("main"))
|
||||
)
|
||||
val outputWasmFile = outputFile.withReplacedExtensionOrNull(outputFile.extension, "wasm")!!
|
||||
@@ -255,8 +260,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
config.configuration,
|
||||
phaseConfig,
|
||||
if (arguments.irDceDriven) PersistentIrFactory() else IrFactoryImpl,
|
||||
dependencies = libraries,
|
||||
friendDependencies = friendLibraries,
|
||||
allDependencies = resolvedLibraries,
|
||||
friendDependencies = friendDependencies,
|
||||
mainArguments = mainCallArguments,
|
||||
generateFullJs = !arguments.irDce,
|
||||
generateDceJs = arguments.irDce,
|
||||
@@ -268,10 +273,9 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
multiModule = arguments.irPerModule,
|
||||
relativeRequirePath = true,
|
||||
propertyLazyInitialization = arguments.irPropertyLazyInitialization,
|
||||
legacyPropertyAccess = arguments.irLegacyPropertyAccess,
|
||||
baseClassIntoMetadata = arguments.irBaseClassInMetadata,
|
||||
)
|
||||
|
||||
|
||||
val jsCode = if (arguments.irDce && !arguments.irDceDriven) compiledModule.dceJsCode!! else compiledModule.jsCode!!
|
||||
outputFile.writeText(jsCode.mainModule)
|
||||
jsCode.dependencies.forEach { (name, content) ->
|
||||
@@ -442,6 +446,17 @@ fun DceRuntimeDiagnostic.Companion.resolve(
|
||||
}
|
||||
}
|
||||
|
||||
fun messageCollectorLogger(collector: MessageCollector) = object : Logger {
|
||||
override fun warning(message: String) = collector.report(STRONG_WARNING, message)
|
||||
override fun error(message: String) = collector.report(ERROR, message)
|
||||
override fun log(message: String) = collector.report(LOGGING, message)
|
||||
override fun fatal(message: String): Nothing {
|
||||
collector.report(ERROR, message)
|
||||
(collector as? GroupingMessageCollector)?.flush()
|
||||
kotlin.error(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadPluginsForTests(configuration: CompilerConfiguration): ExitCode {
|
||||
var pluginClasspaths: Iterable<String> = emptyList()
|
||||
val kotlinPaths = PathUtil.kotlinPathsForCompiler
|
||||
|
||||
@@ -53,7 +53,6 @@ public class Usage {
|
||||
appendln(sb, "Advanced options are non-standard and may be changed or removed without any notice.");
|
||||
}
|
||||
else {
|
||||
renderOptionJUsage(sb);
|
||||
renderArgfileUsage(sb);
|
||||
}
|
||||
|
||||
@@ -104,15 +103,6 @@ public class Usage {
|
||||
appendln(sb, argument.description().replace("\n", "\n" + StringsKt.repeat(" ", OPTION_NAME_PADDING_WIDTH)));
|
||||
}
|
||||
|
||||
private static void renderOptionJUsage(@NotNull StringBuilder sb) {
|
||||
int descriptionStart = sb.length() + OPTION_NAME_PADDING_WIDTH;
|
||||
sb.append(" -J<option>");
|
||||
while (sb.length() < descriptionStart) {
|
||||
sb.append(" ");
|
||||
}
|
||||
appendln(sb, "Pass an option directly to JVM");
|
||||
}
|
||||
|
||||
private static void renderArgfileUsage(@NotNull StringBuilder sb) {
|
||||
int descriptionStart = sb.length() + OPTION_NAME_PADDING_WIDTH;
|
||||
sb.append(" ");
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.light.LightJavaModule
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.cli.common.config.ContentRoot
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
@@ -38,7 +37,6 @@ import org.jetbrains.kotlin.cli.jvm.modules.CliJavaModuleFinder
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.JavaModuleGraph
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.isValidJavaFqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
|
||||
@@ -54,13 +52,10 @@ class ClasspathRootsResolver(
|
||||
private val contentRootToVirtualFile: (JvmContentRoot) -> VirtualFile?,
|
||||
private val javaModuleFinder: CliJavaModuleFinder,
|
||||
private val requireStdlibModule: Boolean,
|
||||
private val outputDirectory: VirtualFile?,
|
||||
private val javaFileManager: KotlinCliJavaFileManager
|
||||
private val outputDirectory: VirtualFile?
|
||||
) {
|
||||
val javaModuleGraph = JavaModuleGraph(javaModuleFinder)
|
||||
|
||||
private val searchScope = GlobalSearchScope.allScope(psiManager.project)
|
||||
|
||||
data class RootsAndModules(val roots: List<JavaRoot>, val modules: List<JavaModule>)
|
||||
|
||||
private data class RootWithPrefix(val root: VirtualFile, val packagePrefix: String?)
|
||||
@@ -165,7 +160,7 @@ class ClasspathRootsResolver(
|
||||
}
|
||||
|
||||
if (moduleInfoFile != null) {
|
||||
val moduleInfo = JavaModuleInfo.read(moduleInfoFile, javaFileManager, searchScope) ?: return null
|
||||
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
|
||||
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class CliKotlinAsJavaSupport(
|
||||
return findFacadeFilesInPackage(packageFqName, scope)
|
||||
.groupBy { it.javaFileFacadeFqName }
|
||||
.mapNotNull { (facadeClassFqName, _) ->
|
||||
KtLightClassForFacadeImpl.createForFacade(psiManager, facadeClassFqName, scope)
|
||||
KtLightClassForFacade.createForFacade(psiManager, facadeClassFqName, scope)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class CliKotlinAsJavaSupport(
|
||||
.orEmpty()
|
||||
|
||||
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||
return listOfNotNull(KtLightClassForFacadeImpl.createForFacade(psiManager, facadeFqName, scope))
|
||||
return listOfNotNull(KtLightClassForFacade.createForFacade(psiManager, facadeFqName, scope))
|
||||
}
|
||||
|
||||
override fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||
|
||||
@@ -35,10 +35,6 @@ class CliVirtualFileFinder(
|
||||
override fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? =
|
||||
findBinaryClass(classId, classId.relativeClassName.asString().replace('.', '$') + ".class")
|
||||
|
||||
override fun findSourceOrBinaryVirtualFile(classId: ClassId) =
|
||||
findBinaryClass(classId, classId.relativeClassName.asString().replace('.', '$') + ".class")
|
||||
?: findSourceClass(classId, classId.relativeClassName.asString() + ".java")
|
||||
|
||||
override fun findMetadata(classId: ClassId): InputStream? {
|
||||
assert(!classId.isNestedClass) { "Nested classes are not supported here: $classId" }
|
||||
|
||||
@@ -65,11 +61,8 @@ class CliVirtualFileFinder(
|
||||
return findBinaryClass(classId, BuiltInSerializerProtocol.getBuiltInsFileName(packageFqName))?.inputStream
|
||||
}
|
||||
|
||||
private fun findClass(classId: ClassId, fileName: String, rootType: Set<JavaRoot.RootType>) =
|
||||
index.findClass(classId, acceptedRootTypes = rootType) { dir, _ ->
|
||||
private fun findBinaryClass(classId: ClassId, fileName: String): VirtualFile? =
|
||||
index.findClass(classId, acceptedRootTypes = JavaRoot.OnlyBinary) { dir, _ ->
|
||||
dir.findChild(fileName)?.takeIf(VirtualFile::isValid)
|
||||
}?.takeIf { it in scope }
|
||||
|
||||
private fun findBinaryClass(classId: ClassId, fileName: String) = findClass(classId, fileName, JavaRoot.OnlyBinary)
|
||||
private fun findSourceClass(classId: ClassId, fileName: String) = findClass(classId, fileName, JavaRoot.OnlySource)
|
||||
}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensionsImpl
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||
import org.jetbrains.kotlin.backend.jvm.jvmPhases
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.CodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendExtension
|
||||
import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers
|
||||
import org.jetbrains.kotlin.fir.session.FirSessionFactory
|
||||
import org.jetbrains.kotlin.fir.session.FirSessionFactory.createSessionWithDependencies
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackagePartProvider
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.SimpleGenericDiagnostics
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
|
||||
import java.io.File
|
||||
|
||||
object FirKotlinToJvmBytecodeCompiler {
|
||||
fun compileModulesUsingFrontendIR(
|
||||
environment: KotlinCoreEnvironment,
|
||||
buildFile: File?,
|
||||
chunk: List<Module>,
|
||||
extendedAnalysisMode: Boolean
|
||||
): Boolean {
|
||||
val project = environment.project
|
||||
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
|
||||
|
||||
environment.messageCollector.report(
|
||||
STRONG_WARNING,
|
||||
"ATTENTION!\n This build uses in-dev FIR: \n -Xuse-fir"
|
||||
)
|
||||
|
||||
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
|
||||
|
||||
val projectConfiguration = environment.configuration
|
||||
val localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
|
||||
val outputs = newLinkedHashMapWithExpectedSize<Module, GenerationState>(chunk.size)
|
||||
val targetIds = environment.configuration.get(JVMConfigurationKeys.MODULES)?.map(::TargetId)
|
||||
val incrementalComponents = environment.configuration.get(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS)
|
||||
for (module in chunk) {
|
||||
performanceManager?.notifyAnalysisStarted()
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
val ktFiles = module.getSourceFiles(environment, localFileSystem, chunk.size > 1, buildFile)
|
||||
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
|
||||
|
||||
val syntaxErrors = ktFiles.fold(false) { errorsFound, ktFile ->
|
||||
AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, environment.messageCollector).isHasErrors or errorsFound
|
||||
}
|
||||
|
||||
val moduleConfiguration = projectConfiguration.applyModuleProperties(module, buildFile)
|
||||
|
||||
val sourceScope = GlobalSearchScope.filesWithoutLibrariesScope(project, ktFiles.map { it.virtualFile })
|
||||
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project))
|
||||
|
||||
var librariesScope = ProjectScope.getLibrariesScope(project)
|
||||
|
||||
val providerAndScopeForIncrementalCompilation = run {
|
||||
if (targetIds == null || incrementalComponents == null) return@run null
|
||||
val fileSystem = environment.projectEnvironment.environment.localFileSystem
|
||||
val directoryWithIncrementalPartsFromPreviousCompilation =
|
||||
moduleConfiguration[JVMConfigurationKeys.OUTPUT_DIRECTORY]
|
||||
?: return@run null
|
||||
val previouslyCompiledFiles = directoryWithIncrementalPartsFromPreviousCompilation.walk()
|
||||
.filter { it.extension == "class" }
|
||||
.mapNotNull { fileSystem.findFileByIoFile(it) }
|
||||
.toList()
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: return@run null
|
||||
val packagePartProvider = IncrementalPackagePartProvider(
|
||||
environment.createPackagePartProvider(sourceScope),
|
||||
targetIds.map(incrementalComponents::getIncrementalCache)
|
||||
)
|
||||
val incrementalCompilationScope = GlobalSearchScope.filesWithoutLibrariesScope(
|
||||
project,
|
||||
previouslyCompiledFiles
|
||||
)
|
||||
librariesScope = librariesScope.intersectWith(GlobalSearchScope.notScope(incrementalCompilationScope))
|
||||
FirSessionFactory.ProviderAndScopeForIncrementalCompilation(packagePartProvider, incrementalCompilationScope)
|
||||
}
|
||||
|
||||
val languageVersionSettings = moduleConfiguration.languageVersionSettings
|
||||
val session = createSessionWithDependencies(
|
||||
Name.identifier(module.getModuleName()),
|
||||
JvmPlatforms.unspecifiedJvmPlatform,
|
||||
JvmPlatformAnalyzerServices,
|
||||
externalSessionProvider = null,
|
||||
project,
|
||||
languageVersionSettings,
|
||||
sourceScope,
|
||||
librariesScope,
|
||||
lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER),
|
||||
providerAndScopeForIncrementalCompilation,
|
||||
getPackagePartProvider = { environment.createPackagePartProvider(it) },
|
||||
dependenciesConfigurator = {
|
||||
dependencies(moduleConfiguration.jvmClasspathRoots.map { it.toPath() })
|
||||
dependencies(moduleConfiguration.jvmModularRoots.map { it.toPath() })
|
||||
friendDependencies(moduleConfiguration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList())
|
||||
},
|
||||
sessionConfigurator = {
|
||||
if (extendedAnalysisMode) {
|
||||
registerExtendedCommonCheckers()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val firAnalyzerFacade = FirAnalyzerFacade(session, languageVersionSettings, ktFiles)
|
||||
|
||||
firAnalyzerFacade.runResolution()
|
||||
val firDiagnostics = firAnalyzerFacade.runCheckers().values.flatten()
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(
|
||||
SimpleGenericDiagnostics(firDiagnostics),
|
||||
environment.messageCollector
|
||||
)
|
||||
performanceManager?.notifyAnalysisFinished()
|
||||
|
||||
if (syntaxErrors || firDiagnostics.any { it.severity == Severity.ERROR }) {
|
||||
return false
|
||||
}
|
||||
|
||||
performanceManager?.notifyGenerationStarted()
|
||||
|
||||
performanceManager?.notifyIRTranslationStarted()
|
||||
val extensions = JvmGeneratorExtensionsImpl()
|
||||
val (moduleFragment, symbolTable, components) = firAnalyzerFacade.convertToIr(extensions)
|
||||
|
||||
performanceManager?.notifyIRTranslationFinished()
|
||||
|
||||
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
|
||||
|
||||
val codegenFactory = JvmIrCodegenFactory(moduleConfiguration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases))
|
||||
|
||||
// Create and initialize the module and its dependencies
|
||||
val container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
project, ktFiles, NoScopeRecordCliBindingTrace(), environment.configuration, environment::createPackagePartProvider,
|
||||
::FileBasedDeclarationProviderFactory, CompilerEnvironment,
|
||||
TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles), emptyList()
|
||||
)
|
||||
|
||||
val generationState = GenerationState.Builder(
|
||||
environment.project, ClassBuilderFactories.BINARIES,
|
||||
container.get(), dummyBindingContext, ktFiles,
|
||||
moduleConfiguration
|
||||
).codegenFactory(
|
||||
codegenFactory
|
||||
).withModule(
|
||||
module
|
||||
).onIndependentPartCompilationEnd(
|
||||
createOutputFilesFlushingCallbackIfPossible(moduleConfiguration)
|
||||
).isIrBackend(
|
||||
true
|
||||
).jvmBackendClassResolver(
|
||||
FirJvmBackendClassResolver(components)
|
||||
).build()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
performanceManager?.notifyIRLoweringStarted()
|
||||
generationState.beforeCompile()
|
||||
codegenFactory.generateModuleInFrontendIRMode(
|
||||
generationState, moduleFragment, symbolTable, extensions, FirJvmBackendExtension(session, components)
|
||||
) {
|
||||
performanceManager?.notifyIRLoweringFinished()
|
||||
performanceManager?.notifyIRGenerationStarted()
|
||||
}
|
||||
CodegenFactory.doCheckCancelled(generationState)
|
||||
generationState.factory.done()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(
|
||||
FilteredJvmDiagnostics(
|
||||
generationState.collectedExtraJvmDiagnostics,
|
||||
dummyBindingContext.diagnostics
|
||||
),
|
||||
environment.messageCollector
|
||||
)
|
||||
|
||||
performanceManager?.notifyIRGenerationFinished()
|
||||
performanceManager?.notifyGenerationFinished()
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
outputs[module] = generationState
|
||||
|
||||
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
|
||||
Disposer.dispose(environment.project)
|
||||
}
|
||||
|
||||
val mainClassFqName: FqName? =
|
||||
if (chunk.size == 1 && projectConfiguration.get(JVMConfigurationKeys.OUTPUT_JAR) != null)
|
||||
TODO(".jar output is not yet supported for -Xuse-fir: KT-42868")
|
||||
else null
|
||||
|
||||
return writeOutputs(environment, projectConfiguration, chunk, outputs, mainClassFqName)
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ
|
||||
private val binaryCache: MutableMap<ClassId, JavaClass?> = THashMap()
|
||||
private val signatureParsingComponent = BinaryClassSignatureParser()
|
||||
|
||||
fun findClass(classId: ClassId, searchScope: GlobalSearchScope) = findClass(JavaClassFinder.Request(classId), searchScope)
|
||||
fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? = findClass(JavaClassFinder.Request(classId), searchScope)
|
||||
|
||||
override fun findClass(request: JavaClassFinder.Request, searchScope: GlobalSearchScope): JavaClass? {
|
||||
val (classId, classFileContentFromRequest, outerClassFromRequest) = request
|
||||
|
||||
@@ -190,17 +190,11 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
sourceFiles.sortBy { it.virtualFile.path }
|
||||
|
||||
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val javaModuleFinder = CliJavaModuleFinder(
|
||||
jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
},
|
||||
javaFileManager,
|
||||
project
|
||||
)
|
||||
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
})
|
||||
|
||||
val outputDirectory =
|
||||
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||
@@ -213,8 +207,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder,
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||
outputDirectory?.let(this::findLocalFile),
|
||||
javaFileManager
|
||||
outputDirectory?.let(this::findLocalFile)
|
||||
)
|
||||
|
||||
val (initialRoots, javaModules) =
|
||||
@@ -238,7 +231,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
updateClasspathFromRootsIndex(this)
|
||||
}
|
||||
|
||||
javaFileManager.initialize(
|
||||
(ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl).initialize(
|
||||
rootsIndex,
|
||||
packagePartProviders,
|
||||
SingleJavaFileRootsIndex(singleJavaFileRoots),
|
||||
@@ -247,7 +240,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList(), project)
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList())
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
|
||||
@@ -78,7 +78,10 @@ import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.config.APPEND_JAVA_SOURCE_ROOTS_HANDLER_KEY
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.extensions.*
|
||||
import org.jetbrains.kotlin.extensions.internal.CandidateInterceptor
|
||||
import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptor
|
||||
@@ -190,16 +193,11 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
sourceFiles.sortBy { it.virtualFile.path }
|
||||
|
||||
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val javaModuleFinder = CliJavaModuleFinder(
|
||||
jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
},
|
||||
javaFileManager
|
||||
)
|
||||
val javaModuleFinder = CliJavaModuleFinder(jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
})
|
||||
|
||||
val outputDirectory =
|
||||
configuration.get(JVMConfigurationKeys.MODULES)?.singleOrNull()?.getOutputDirectory()
|
||||
@@ -212,8 +210,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
this::contentRootToVirtualFile,
|
||||
javaModuleFinder,
|
||||
!configuration.getBoolean(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE),
|
||||
outputDirectory?.let(this::findLocalFile),
|
||||
javaFileManager
|
||||
outputDirectory?.let(this::findLocalFile)
|
||||
)
|
||||
|
||||
val (initialRoots, javaModules) =
|
||||
@@ -237,7 +234,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
updateClasspathFromRootsIndex(this)
|
||||
}
|
||||
|
||||
javaFileManager.initialize(
|
||||
(ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl).initialize(
|
||||
rootsIndex,
|
||||
packagePartProviders,
|
||||
SingleJavaFileRootsIndex(singleJavaFileRoots),
|
||||
@@ -246,7 +243,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
project.registerService(
|
||||
JavaModuleResolver::class.java,
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList(), project)
|
||||
CliJavaModuleResolver(classpathRootsResolver.javaModuleGraph, javaModules, javaModuleFinder.systemModules.toList())
|
||||
)
|
||||
|
||||
val finderFactory = CliVirtualFileFinderFactory(rootsIndex)
|
||||
|
||||
@@ -17,42 +17,140 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.asJava.FilteredJvmDiagnostics
|
||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensionsImpl
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
|
||||
import org.jetbrains.kotlin.backend.jvm.jvmPhases
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
|
||||
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAll
|
||||
import org.jetbrains.kotlin.cli.common.toLogger
|
||||
import org.jetbrains.kotlin.cli.jvm.config.*
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.CodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback
|
||||
import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.container.get
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
|
||||
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendExtension
|
||||
import org.jetbrains.kotlin.fir.checkers.registerExtendedCommonCheckers
|
||||
import org.jetbrains.kotlin.fir.createSessionWithDependencies
|
||||
import org.jetbrains.kotlin.ir.backend.jvm.jvmResolveLibraries
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
|
||||
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackagePartProvider
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.CompilerEnvironment
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.SimpleGenericDiagnostics
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
|
||||
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
|
||||
import java.io.File
|
||||
import kotlin.collections.set
|
||||
|
||||
object KotlinToJVMBytecodeCompiler {
|
||||
private fun writeOutput(
|
||||
configuration: CompilerConfiguration,
|
||||
outputFiles: OutputFileCollection,
|
||||
mainClassFqName: FqName?
|
||||
) {
|
||||
val reportOutputFiles = configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES)
|
||||
val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR)
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
|
||||
if (jarPath != null) {
|
||||
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
|
||||
val noReflect = configuration.get(JVMConfigurationKeys.NO_REFLECT, false)
|
||||
val resetJarTimestamps = !configuration.get(JVMConfigurationKeys.NO_RESET_JAR_TIMESTAMPS, false)
|
||||
CompileEnvironmentUtil.writeToJar(jarPath, includeRuntime, noReflect, resetJarTimestamps, mainClassFqName, outputFiles, messageCollector)
|
||||
if (reportOutputFiles) {
|
||||
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
|
||||
messageCollector.report(OUTPUT, message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".")
|
||||
outputFiles.writeAll(outputDir, messageCollector, reportOutputFiles)
|
||||
}
|
||||
|
||||
private fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback {
|
||||
if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) {
|
||||
return GenerationStateEventCallback.DO_NOTHING
|
||||
}
|
||||
return GenerationStateEventCallback { state ->
|
||||
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
|
||||
writeOutput(configuration, currentOutput, null)
|
||||
if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) {
|
||||
state.factory.releaseGeneratedOutput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Module.getSourceFiles(
|
||||
environment: KotlinCoreEnvironment,
|
||||
localFileSystem: VirtualFileSystem,
|
||||
multiModuleChunk: Boolean,
|
||||
buildFile: File?
|
||||
): List<KtFile> {
|
||||
return if (multiModuleChunk) {
|
||||
// filter out source files from other modules
|
||||
assert(buildFile != null) { "Compiling multiple modules, but build file is null" }
|
||||
val (moduleSourceDirs, moduleSourceFiles) =
|
||||
getBuildFilePaths(buildFile, getSourceFiles())
|
||||
.mapNotNull(localFileSystem::findFileByPath)
|
||||
.partition(VirtualFile::isDirectory)
|
||||
|
||||
environment.getSourceFiles().filter { file ->
|
||||
val virtualFile = file.virtualFile
|
||||
virtualFile in moduleSourceFiles || moduleSourceDirs.any { dir ->
|
||||
VfsUtilCore.isAncestor(dir, virtualFile, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
environment.getSourceFiles()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.applyModuleProperties(module: Module, buildFile: File?): CompilerConfiguration {
|
||||
return copy().apply {
|
||||
if (buildFile != null) {
|
||||
fun checkKeyIsNull(key: CompilerConfigurationKey<*>, name: String) {
|
||||
assert(get(key) == null) { "$name should be null, when buildFile is used" }
|
||||
}
|
||||
|
||||
checkKeyIsNull(JVMConfigurationKeys.OUTPUT_DIRECTORY, "OUTPUT_DIRECTORY")
|
||||
checkKeyIsNull(JVMConfigurationKeys.OUTPUT_JAR, "OUTPUT_JAR")
|
||||
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun compileModules(
|
||||
environment: KotlinCoreEnvironment,
|
||||
buildFile: File?,
|
||||
@@ -84,7 +182,7 @@ object KotlinToJVMBytecodeCompiler {
|
||||
val projectConfiguration = environment.configuration
|
||||
if (projectConfiguration.getBoolean(CommonConfigurationKeys.USE_FIR)) {
|
||||
val extendedAnalysisMode = projectConfiguration.getBoolean(CommonConfigurationKeys.USE_FIR_EXTENDED_CHECKERS)
|
||||
return FirKotlinToJvmBytecodeCompiler.compileModulesUsingFrontendIR(environment, buildFile, chunk, extendedAnalysisMode)
|
||||
return compileModulesUsingFrontendIR(environment, buildFile, chunk, extendedAnalysisMode)
|
||||
}
|
||||
|
||||
val result = repeatAnalysisIfNeeded(analyze(environment), environment)
|
||||
@@ -116,6 +214,40 @@ object KotlinToJVMBytecodeCompiler {
|
||||
return writeOutputs(environment, projectConfiguration, chunk, outputs, mainClassFqName)
|
||||
}
|
||||
|
||||
private fun writeOutputs(
|
||||
environment: KotlinCoreEnvironment,
|
||||
projectConfiguration: CompilerConfiguration,
|
||||
chunk: List<Module>,
|
||||
outputs: Map<Module, GenerationState>,
|
||||
mainClassFqName: FqName?
|
||||
): Boolean {
|
||||
try {
|
||||
for ((_, state) in outputs) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
writeOutput(state.configuration, state.factory, mainClassFqName)
|
||||
}
|
||||
} finally {
|
||||
outputs.values.forEach(GenerationState::destroy)
|
||||
}
|
||||
|
||||
if (projectConfiguration.getBoolean(JVMConfigurationKeys.COMPILE_JAVA)) {
|
||||
val singleModule = chunk.singleOrNull()
|
||||
if (singleModule != null) {
|
||||
return JavacWrapper.getInstance(environment.project).use {
|
||||
it.compile(File(singleModule.getOutputDirectory()))
|
||||
}
|
||||
} else {
|
||||
projectConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
||||
WARNING,
|
||||
"A chunk contains multiple modules (${chunk.joinToString { it.getModuleName() }}). " +
|
||||
"-Xuse-javac option couldn't be used to compile java files"
|
||||
)
|
||||
JavacWrapper.getInstance(environment.project).close()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun configureSourceRoots(configuration: CompilerConfiguration, chunk: List<Module>, buildFile: File? = null) {
|
||||
for (module in chunk) {
|
||||
@@ -163,6 +295,162 @@ object KotlinToJVMBytecodeCompiler {
|
||||
configuration.addAll(JVMConfigurationKeys.MODULES, chunk)
|
||||
}
|
||||
|
||||
private fun compileModulesUsingFrontendIR(
|
||||
environment: KotlinCoreEnvironment,
|
||||
buildFile: File?,
|
||||
chunk: List<Module>,
|
||||
extendedAnalysisMode: Boolean
|
||||
): Boolean {
|
||||
val project = environment.project
|
||||
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
|
||||
|
||||
environment.messageCollector.report(
|
||||
STRONG_WARNING,
|
||||
"ATTENTION!\n This build uses in-dev FIR: \n -Xuse-fir"
|
||||
)
|
||||
|
||||
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
|
||||
|
||||
val projectConfiguration = environment.configuration
|
||||
val localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
|
||||
val outputs = newLinkedHashMapWithExpectedSize<Module, GenerationState>(chunk.size)
|
||||
val targetIds = environment.configuration.get(JVMConfigurationKeys.MODULES)?.map(::TargetId)
|
||||
val incrementalComponents = environment.configuration.get(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS)
|
||||
for (module in chunk) {
|
||||
performanceManager?.notifyAnalysisStarted()
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
val ktFiles = module.getSourceFiles(environment, localFileSystem, chunk.size > 1, buildFile)
|
||||
if (!checkKotlinPackageUsage(environment, ktFiles)) return false
|
||||
|
||||
val syntaxErrors = ktFiles.fold(false) { errorsFound, ktFile ->
|
||||
AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, environment.messageCollector).isHasErrors or errorsFound
|
||||
}
|
||||
|
||||
val moduleConfiguration = projectConfiguration.applyModuleProperties(module, buildFile)
|
||||
|
||||
val sourceScope = GlobalSearchScope.filesScope(project, ktFiles.map { it.virtualFile })
|
||||
.uniteWith(TopDownAnalyzerFacadeForJVM.AllJavaSourcesInProjectScope(project))
|
||||
|
||||
val librariesScope = ProjectScope.getLibrariesScope(project)
|
||||
|
||||
val languageVersionSettings = moduleConfiguration.languageVersionSettings
|
||||
val session = createSessionWithDependencies(
|
||||
module,
|
||||
project,
|
||||
languageVersionSettings,
|
||||
sourceScope,
|
||||
librariesScope,
|
||||
lookupTracker = environment.configuration.get(CommonConfigurationKeys.LOOKUP_TRACKER),
|
||||
getPackagePartProvider = { environment.createPackagePartProvider(it) },
|
||||
getAdditionalModulePackagePartProvider = {
|
||||
if (targetIds == null || incrementalComponents == null) null
|
||||
else IncrementalPackagePartProvider(
|
||||
environment.createPackagePartProvider(it),
|
||||
targetIds.map(incrementalComponents::getIncrementalCache)
|
||||
)
|
||||
}
|
||||
) {
|
||||
if (extendedAnalysisMode) {
|
||||
registerExtendedCommonCheckers()
|
||||
}
|
||||
}
|
||||
|
||||
val firAnalyzerFacade = FirAnalyzerFacade(session, languageVersionSettings, ktFiles)
|
||||
|
||||
firAnalyzerFacade.runResolution()
|
||||
val firDiagnostics = firAnalyzerFacade.runCheckers().values.flatten()
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(
|
||||
SimpleGenericDiagnostics(firDiagnostics),
|
||||
environment.messageCollector
|
||||
)
|
||||
performanceManager?.notifyAnalysisFinished()
|
||||
|
||||
if (syntaxErrors || firDiagnostics.any { it.severity == Severity.ERROR }) {
|
||||
return false
|
||||
}
|
||||
|
||||
performanceManager?.notifyGenerationStarted()
|
||||
|
||||
performanceManager?.notifyIRTranslationStarted()
|
||||
val extensions = JvmGeneratorExtensionsImpl()
|
||||
val (moduleFragment, symbolTable, components) = firAnalyzerFacade.convertToIr(extensions)
|
||||
|
||||
performanceManager?.notifyIRTranslationFinished()
|
||||
|
||||
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
|
||||
|
||||
val codegenFactory = JvmIrCodegenFactory(moduleConfiguration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases))
|
||||
|
||||
// Create and initialize the module and its dependencies
|
||||
val container = TopDownAnalyzerFacadeForJVM.createContainer(
|
||||
project, ktFiles, NoScopeRecordCliBindingTrace(), environment.configuration, environment::createPackagePartProvider,
|
||||
::FileBasedDeclarationProviderFactory, CompilerEnvironment,
|
||||
TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles), emptyList()
|
||||
)
|
||||
|
||||
val generationState = GenerationState.Builder(
|
||||
environment.project, ClassBuilderFactories.BINARIES,
|
||||
container.get<ModuleDescriptor>(), dummyBindingContext, ktFiles,
|
||||
moduleConfiguration
|
||||
).codegenFactory(
|
||||
codegenFactory
|
||||
).withModule(
|
||||
module
|
||||
).onIndependentPartCompilationEnd(
|
||||
createOutputFilesFlushingCallbackIfPossible(moduleConfiguration)
|
||||
).isIrBackend(
|
||||
true
|
||||
).jvmBackendClassResolver(
|
||||
FirJvmBackendClassResolver(components)
|
||||
).build()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
performanceManager?.notifyIRLoweringStarted()
|
||||
generationState.beforeCompile()
|
||||
codegenFactory.generateModuleInFrontendIRMode(
|
||||
generationState, moduleFragment, symbolTable, extensions, FirJvmBackendExtension(session, components)
|
||||
) {
|
||||
performanceManager?.notifyIRLoweringFinished()
|
||||
performanceManager?.notifyIRGenerationStarted()
|
||||
}
|
||||
CodegenFactory.doCheckCancelled(generationState)
|
||||
generationState.factory.done()
|
||||
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
|
||||
AnalyzerWithCompilerReport.reportDiagnostics(
|
||||
FilteredJvmDiagnostics(
|
||||
generationState.collectedExtraJvmDiagnostics,
|
||||
dummyBindingContext.diagnostics
|
||||
),
|
||||
environment.messageCollector
|
||||
)
|
||||
|
||||
performanceManager?.notifyIRGenerationFinished()
|
||||
performanceManager?.notifyGenerationFinished()
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
outputs[module] = generationState
|
||||
|
||||
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
|
||||
Disposer.dispose(environment.project)
|
||||
}
|
||||
|
||||
val mainClassFqName: FqName? =
|
||||
if (chunk.size == 1 && projectConfiguration.get(JVMConfigurationKeys.OUTPUT_JAR) != null)
|
||||
TODO(".jar output is not yet supported for -Xuse-fir: KT-42868")
|
||||
else null
|
||||
|
||||
return writeOutputs(environment, projectConfiguration, chunk, outputs, mainClassFqName)
|
||||
}
|
||||
|
||||
private fun getBuildFilePaths(buildFile: File?, sourceFilePaths: List<String>): List<String> =
|
||||
if (buildFile == null) sourceFilePaths
|
||||
else sourceFilePaths.map { path ->
|
||||
(File(path).takeIf(File::isAbsolute) ?: buildFile.resolveSibling(path)).absolutePath
|
||||
}
|
||||
|
||||
fun compileBunchOfSources(environment: KotlinCoreEnvironment): Boolean {
|
||||
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
|
||||
|
||||
@@ -289,6 +577,15 @@ object KotlinToJVMBytecodeCompiler {
|
||||
override fun toString() = "All files under: $directories"
|
||||
}
|
||||
|
||||
private fun GenerationState.Builder.withModule(module: Module?) =
|
||||
apply {
|
||||
if (module != null) {
|
||||
targetId(TargetId(module))
|
||||
moduleName(module.getModuleName())
|
||||
outDirectory(File(module.getOutputDirectory()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun generate(
|
||||
environment: KotlinCoreEnvironment,
|
||||
configuration: CompilerConfiguration,
|
||||
@@ -335,4 +632,7 @@ object KotlinToJVMBytecodeCompiler {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
return generationState
|
||||
}
|
||||
|
||||
private val KotlinCoreEnvironment.messageCollector: MessageCollector
|
||||
get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
}
|
||||
|
||||
@@ -209,8 +209,7 @@ object TopDownAnalyzerFacadeForJVM {
|
||||
moduleClassResolver.compiledCodeResolver.packageFragmentProvider,
|
||||
dependenciesContainer.get<JvmBuiltInsPackageFragmentProvider>(),
|
||||
dependenciesContainer.get<OptionalAnnotationPackageFragmentProvider>()
|
||||
),
|
||||
"CompositeProvider@TopDownAnalyzerForJvm for dependencies ${dependenciesContext.module}"
|
||||
)
|
||||
)
|
||||
)
|
||||
dependenciesContext.module
|
||||
@@ -284,8 +283,7 @@ object TopDownAnalyzerFacadeForJVM {
|
||||
listOf(
|
||||
container.get<KotlinCodeAnalyzer>().packageFragmentProvider,
|
||||
container.get<OptionalAnnotationPackageFragmentProvider>()
|
||||
) + additionalProviders,
|
||||
"CompositeProvider@TopDownAnalzyerForJvm for $module"
|
||||
) + additionalProviders
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAll
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationStateEventCallback
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.javac.JavacWrapper
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.modules.TargetId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
|
||||
fun Module.getSourceFiles(
|
||||
environment: KotlinCoreEnvironment,
|
||||
localFileSystem: VirtualFileSystem,
|
||||
multiModuleChunk: Boolean,
|
||||
buildFile: File?
|
||||
): List<KtFile> {
|
||||
return if (multiModuleChunk) {
|
||||
// filter out source files from other modules
|
||||
assert(buildFile != null) { "Compiling multiple modules, but build file is null" }
|
||||
val (moduleSourceDirs, moduleSourceFiles) =
|
||||
getBuildFilePaths(buildFile, getSourceFiles())
|
||||
.mapNotNull(localFileSystem::findFileByPath)
|
||||
.partition(VirtualFile::isDirectory)
|
||||
|
||||
environment.getSourceFiles().filter { file ->
|
||||
val virtualFile = file.virtualFile
|
||||
virtualFile in moduleSourceFiles || moduleSourceDirs.any { dir ->
|
||||
VfsUtilCore.isAncestor(dir, virtualFile, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
environment.getSourceFiles()
|
||||
}
|
||||
}
|
||||
|
||||
fun getBuildFilePaths(buildFile: File?, sourceFilePaths: List<String>): List<String> =
|
||||
if (buildFile == null) sourceFilePaths
|
||||
else sourceFilePaths.map { path ->
|
||||
(File(path).takeIf(File::isAbsolute) ?: buildFile.resolveSibling(path)).absolutePath
|
||||
}
|
||||
|
||||
fun GenerationState.Builder.withModule(module: Module?) =
|
||||
apply {
|
||||
if (module != null) {
|
||||
targetId(TargetId(module))
|
||||
moduleName(module.getModuleName())
|
||||
outDirectory(File(module.getOutputDirectory()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun createOutputFilesFlushingCallbackIfPossible(configuration: CompilerConfiguration): GenerationStateEventCallback {
|
||||
if (configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) == null) {
|
||||
return GenerationStateEventCallback.DO_NOTHING
|
||||
}
|
||||
return GenerationStateEventCallback { state ->
|
||||
val currentOutput = SimpleOutputFileCollection(state.factory.currentOutput)
|
||||
writeOutput(configuration, currentOutput, null)
|
||||
if (!configuration.get(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, false)) {
|
||||
state.factory.releaseGeneratedOutput()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun writeOutput(
|
||||
configuration: CompilerConfiguration,
|
||||
outputFiles: OutputFileCollection,
|
||||
mainClassFqName: FqName?
|
||||
) {
|
||||
val reportOutputFiles = configuration.getBoolean(CommonConfigurationKeys.REPORT_OUTPUT_FILES)
|
||||
val jarPath = configuration.get(JVMConfigurationKeys.OUTPUT_JAR)
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
|
||||
if (jarPath != null) {
|
||||
val includeRuntime = configuration.get(JVMConfigurationKeys.INCLUDE_RUNTIME, false)
|
||||
val noReflect = configuration.get(JVMConfigurationKeys.NO_REFLECT, false)
|
||||
val resetJarTimestamps = !configuration.get(JVMConfigurationKeys.NO_RESET_JAR_TIMESTAMPS, false)
|
||||
CompileEnvironmentUtil.writeToJar(
|
||||
jarPath,
|
||||
includeRuntime,
|
||||
noReflect,
|
||||
resetJarTimestamps,
|
||||
mainClassFqName,
|
||||
outputFiles,
|
||||
messageCollector
|
||||
)
|
||||
if (reportOutputFiles) {
|
||||
val message = OutputMessageUtil.formatOutputMessage(outputFiles.asList().flatMap { it.sourceFiles }.distinct(), jarPath)
|
||||
messageCollector.report(CompilerMessageSeverity.OUTPUT, message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val outputDir = configuration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY) ?: File(".")
|
||||
outputFiles.writeAll(outputDir, messageCollector, reportOutputFiles)
|
||||
}
|
||||
|
||||
fun writeOutputs(
|
||||
environment: KotlinCoreEnvironment,
|
||||
projectConfiguration: CompilerConfiguration,
|
||||
chunk: List<Module>,
|
||||
outputs: Map<Module, GenerationState>,
|
||||
mainClassFqName: FqName?
|
||||
): Boolean {
|
||||
try {
|
||||
for ((_, state) in outputs) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
|
||||
writeOutput(state.configuration, state.factory, mainClassFqName)
|
||||
}
|
||||
} finally {
|
||||
outputs.values.forEach(GenerationState::destroy)
|
||||
}
|
||||
|
||||
if (projectConfiguration.getBoolean(JVMConfigurationKeys.COMPILE_JAVA)) {
|
||||
val singleModule = chunk.singleOrNull()
|
||||
if (singleModule != null) {
|
||||
return JavacWrapper.getInstance(environment.project).use {
|
||||
it.compile(File(singleModule.getOutputDirectory()))
|
||||
}
|
||||
} else {
|
||||
projectConfiguration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
||||
CompilerMessageSeverity.WARNING,
|
||||
"A chunk contains multiple modules (${chunk.joinToString { it.getModuleName() }}). " +
|
||||
"-Xuse-javac option couldn't be used to compile java files"
|
||||
)
|
||||
JavacWrapper.getInstance(environment.project).close()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -15,13 +15,10 @@ import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.extensions.PreprocessedFileCreator
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.modules.Module
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
|
||||
import java.io.File
|
||||
@@ -82,22 +79,4 @@ fun createSourceFilesFromSourceRoots(
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
val KotlinCoreEnvironment.messageCollector: MessageCollector
|
||||
get() = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
fun CompilerConfiguration.applyModuleProperties(module: Module, buildFile: File?): CompilerConfiguration {
|
||||
return copy().apply {
|
||||
if (buildFile != null) {
|
||||
fun checkKeyIsNull(key: CompilerConfigurationKey<*>, name: String) {
|
||||
assert(get(key) == null) { "$name should be null, when buildFile is used" }
|
||||
}
|
||||
|
||||
checkKeyIsNull(JVMConfigurationKeys.OUTPUT_DIRECTORY, "OUTPUT_DIRECTORY")
|
||||
checkKeyIsNull(JVMConfigurationKeys.OUTPUT_JAR, "OUTPUT_JAR")
|
||||
put(JVMConfigurationKeys.OUTPUT_DIRECTORY, File(module.getOutputDirectory()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,9 +49,6 @@ fun CompilerConfiguration.addJvmSdkRoots(files: List<File>) {
|
||||
val CompilerConfiguration.jvmClasspathRoots: List<File>
|
||||
get() = getList(CLIConfigurationKeys.CONTENT_ROOTS).filterIsInstance<JvmClasspathRoot>().map(JvmContentRoot::file)
|
||||
|
||||
val CompilerConfiguration.jvmModularRoots: List<File>
|
||||
get() = getList(CLIConfigurationKeys.CONTENT_ROOTS).filterIsInstance<JvmModulePathRoot>().map(JvmContentRoot::file)
|
||||
|
||||
@JvmOverloads
|
||||
fun CompilerConfiguration.addJavaSourceRoot(file: File, packagePrefix: String? = null) {
|
||||
add(CLIConfigurationKeys.CONTENT_ROOTS, JavaSourceRoot(file, packagePrefix))
|
||||
|
||||
@@ -45,7 +45,6 @@ data class JavaRoot(val file: VirtualFile, val type: RootType, val prefixFqName:
|
||||
|
||||
companion object RootTypes {
|
||||
val OnlyBinary: Set<RootType> = EnumSet.of(RootType.BINARY)
|
||||
val OnlySource: Set<RootType> = EnumSet.of(RootType.SOURCE)
|
||||
val SourceAndBinary: Set<RootType> = EnumSet.of(RootType.BINARY, RootType.SOURCE)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,25 +16,16 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
|
||||
class CliJavaModuleFinder(
|
||||
jrtFileSystemRoot: VirtualFile?,
|
||||
private val javaFileManager: KotlinCliJavaFileManager,
|
||||
project: Project
|
||||
) : JavaModuleFinder {
|
||||
class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
|
||||
private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
|
||||
private val userModules = linkedMapOf<String, JavaModule>()
|
||||
|
||||
private val allScope = GlobalSearchScope.allScope(project)
|
||||
|
||||
fun addUserModule(module: JavaModule) {
|
||||
userModules.putIfAbsent(module.name, module)
|
||||
}
|
||||
@@ -50,7 +41,7 @@ class CliJavaModuleFinder(
|
||||
|
||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
||||
val moduleInfo = JavaModuleInfo.read(file, javaFileManager, allScope) ?: return null
|
||||
val moduleInfo = JavaModuleInfo.read(file) ?: return null
|
||||
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,24 +18,18 @@ package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import com.intellij.ide.highlighter.JavaClassFileType
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaAnnotation
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class CliJavaModuleResolver(
|
||||
private val moduleGraph: JavaModuleGraph,
|
||||
private val userModules: List<JavaModule>,
|
||||
private val systemModules: List<JavaModule.Explicit>,
|
||||
private val project: Project
|
||||
private val systemModules: List<JavaModule.Explicit>
|
||||
) : JavaModuleResolver {
|
||||
init {
|
||||
assert(userModules.count(JavaModule::isSourceModule) <= 1) {
|
||||
@@ -43,14 +37,6 @@ class CliJavaModuleResolver(
|
||||
}
|
||||
}
|
||||
|
||||
private val virtualFileFinder by lazy { VirtualFileFinder.getInstance(project) }
|
||||
|
||||
override fun getAnnotationsForModuleOwnerOfClass(classId: ClassId): List<JavaAnnotation>? {
|
||||
val virtualFile = virtualFileFinder.findSourceOrBinaryVirtualFile(classId) ?: return null
|
||||
|
||||
return (findJavaModule(virtualFile) as? JavaModule.Explicit)?.moduleInfo?.annotations
|
||||
}
|
||||
|
||||
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
|
||||
|
||||
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
||||
@@ -91,8 +77,4 @@ class CliJavaModuleResolver(
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MODULE_ANNOTATIONS_CACHE_SIZE = 10000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,4 @@ object AnalysisFlags {
|
||||
|
||||
@JvmStatic
|
||||
val extendedCompilerChecks by AnalysisFlag.Delegates.Boolean
|
||||
|
||||
@JvmStatic
|
||||
val allowKotlinPackage by AnalysisFlag.Delegates.Boolean
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user