mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-30 15:51:53 +00:00
Compare commits
326 Commits
abannykh/v
...
master_161
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ede11fbdd0 | ||
|
|
823a371fa9 | ||
|
|
ec68471f82 | ||
|
|
9f5e6f5f9c | ||
|
|
6b9c4c1526 | ||
|
|
198fa9733a | ||
|
|
34de3283f7 | ||
|
|
28a2abea4e | ||
|
|
61cb263af6 | ||
|
|
745665dfe7 | ||
|
|
119f81cda3 | ||
|
|
622e8ceb8f | ||
|
|
b862999394 | ||
|
|
df76ad533e | ||
|
|
97fd653235 | ||
|
|
4a32239a88 | ||
|
|
b7c35d4545 | ||
|
|
e24a20360a | ||
|
|
8291f07464 | ||
|
|
6bee3f8aff | ||
|
|
127e2cba50 | ||
|
|
e6130042a7 | ||
|
|
bd49deef41 | ||
|
|
581ae0fd86 | ||
|
|
2bb965607c | ||
|
|
a23fe719c8 | ||
|
|
6b74b8cf0b | ||
|
|
a3fd53b843 | ||
|
|
e1afe868e8 | ||
|
|
97bb1a92e3 | ||
|
|
12d7461cdd | ||
|
|
233cbf1069 | ||
|
|
5988728417 | ||
|
|
89c2b98629 | ||
|
|
d0dff3638c | ||
|
|
cb3804c7e4 | ||
|
|
97187fa5d2 | ||
|
|
b336ad43c5 | ||
|
|
8c75426545 | ||
|
|
06dbc029d8 | ||
|
|
cf9369264e | ||
|
|
7def1618a8 | ||
|
|
c0d6987a3a | ||
|
|
0e4c3ec202 | ||
|
|
88a394e892 | ||
|
|
fa06965ed6 | ||
|
|
cf9d7a0470 | ||
|
|
040f5f88f2 | ||
|
|
5dc5ca551f | ||
|
|
ee36abd73a | ||
|
|
5c55b9fbbe | ||
|
|
ce434585e3 | ||
|
|
f8e5065845 | ||
|
|
f1c0d5316f | ||
|
|
eedcc19209 | ||
|
|
04591bb938 | ||
|
|
6d595e30c2 | ||
|
|
d62db8dc6b | ||
|
|
a645dc109a | ||
|
|
c73e58516b | ||
|
|
ab0d939626 | ||
|
|
feae5079ed | ||
|
|
ba185d7616 | ||
|
|
48cae0e480 | ||
|
|
8054020f61 | ||
|
|
d846d05527 | ||
|
|
d34b73befb | ||
|
|
d94da5af40 | ||
|
|
fa58f1b4d7 | ||
|
|
abf206a134 | ||
|
|
fee29c47c8 | ||
|
|
3e7357a5d7 | ||
|
|
daef8a0eed | ||
|
|
ed9e94c632 | ||
|
|
d01aaeb65c | ||
|
|
00e84fb483 | ||
|
|
22fb9ec5e1 | ||
|
|
15b063d236 | ||
|
|
dd2d9c1dc2 | ||
|
|
71161e218b | ||
|
|
0a0e628068 | ||
|
|
ac368ac182 | ||
|
|
955fe9e1e6 | ||
|
|
dbcd141a46 | ||
|
|
8794005234 | ||
|
|
bbe3b3cabe | ||
|
|
85420d1ffd | ||
|
|
1441aea2ea | ||
|
|
6924ddeace | ||
|
|
6f6a595fef | ||
|
|
d7c1993194 | ||
|
|
babb3b557d | ||
|
|
50d0f5bde6 | ||
|
|
91e8d9e211 | ||
|
|
e7753c31db | ||
|
|
38047240d3 | ||
|
|
5c4ba53f42 | ||
|
|
4906ddfc29 | ||
|
|
bbab0f11ca | ||
|
|
045a23ae10 | ||
|
|
b121bf8802 | ||
|
|
d0cc1635db | ||
|
|
1375267996 | ||
|
|
e37800d056 | ||
|
|
10ea2883f7 | ||
|
|
d58d75c6ef | ||
|
|
56201a6dc4 | ||
|
|
7a240b63c7 | ||
|
|
34e131c928 | ||
|
|
32826c1686 | ||
|
|
cf7048dd0f | ||
|
|
a879cb0cfd | ||
|
|
ac530ac49c | ||
|
|
f5d4dd33da | ||
|
|
573c6ab5d4 | ||
|
|
d2cd5d46fa | ||
|
|
dcc98e3839 | ||
|
|
78ffe47bf8 | ||
|
|
bd88919411 | ||
|
|
465a424af4 | ||
|
|
b8ebc087e2 | ||
|
|
087551ad61 | ||
|
|
b8563f7fcf | ||
|
|
cab80812ef | ||
|
|
631f58f27f | ||
|
|
2c692de98f | ||
|
|
19db4869e6 | ||
|
|
b6974a88c5 | ||
|
|
3a14a5c461 | ||
|
|
831467891c | ||
|
|
f6734e74e1 | ||
|
|
23698f93e0 | ||
|
|
6b6d7a5030 | ||
|
|
e8749e639c | ||
|
|
8c91dc579a | ||
|
|
9bbea47f93 | ||
|
|
e5a128ab2e | ||
|
|
73b879ea89 | ||
|
|
e037e9de39 | ||
|
|
8d1d76cdae | ||
|
|
da53317357 | ||
|
|
0568bc3ef1 | ||
|
|
fd80c0d1d1 | ||
|
|
05ef705609 | ||
|
|
006062499c | ||
|
|
774aa720b4 | ||
|
|
a0a8beee82 | ||
|
|
ce3b455f57 | ||
|
|
c46164481a | ||
|
|
a087ea559f | ||
|
|
ec403bfdbc | ||
|
|
3c09a26e16 | ||
|
|
3fc106572e | ||
|
|
eda43c8b45 | ||
|
|
29e5ad5abe | ||
|
|
8a3fa2e4e5 | ||
|
|
a5e4e0284e | ||
|
|
d21d362f0f | ||
|
|
39055229a1 | ||
|
|
830bf62d94 | ||
|
|
4e98394c38 | ||
|
|
688802de51 | ||
|
|
e6ee933b27 | ||
|
|
6d9b519bb2 | ||
|
|
0a7a73d4be | ||
|
|
9120ccc054 | ||
|
|
d886cd7d06 | ||
|
|
2719016539 | ||
|
|
b240ae791c | ||
|
|
52d11eb22b | ||
|
|
82d7a269ed | ||
|
|
394c68c326 | ||
|
|
530214fcee | ||
|
|
be90f7d331 | ||
|
|
2b21280ba9 | ||
|
|
8761ef6694 | ||
|
|
7173e56393 | ||
|
|
f2fea9a04a | ||
|
|
5e80d80797 | ||
|
|
32bdb6becb | ||
|
|
faa0dff649 | ||
|
|
cc20c66bfc | ||
|
|
e8640b441d | ||
|
|
811b8978c2 | ||
|
|
c5ee28da05 | ||
|
|
26537cd8fc | ||
|
|
0e583aa929 | ||
|
|
278cc71c4a | ||
|
|
641a9a7153 | ||
|
|
19ea18a340 | ||
|
|
01a9d9a284 | ||
|
|
8c3936a0ee | ||
|
|
3bf7223448 | ||
|
|
27bf51c73f | ||
|
|
6a68eb218f | ||
|
|
c9df227fef | ||
|
|
73a2c8c436 | ||
|
|
5d461ec6df | ||
|
|
4261880340 | ||
|
|
999ef51653 | ||
|
|
cbccb68948 | ||
|
|
9f2ce3c521 | ||
|
|
d188de3086 | ||
|
|
5e8afd26e1 | ||
|
|
f950ff4b8f | ||
|
|
9a2c9ed30e | ||
|
|
954c1d853d | ||
|
|
a2f7808ab1 | ||
|
|
263cf85c5c | ||
|
|
11caa03427 | ||
|
|
80063b6f91 | ||
|
|
b83b534374 | ||
|
|
c038d3e9a3 | ||
|
|
646f50dd66 | ||
|
|
1296c5444b | ||
|
|
61e8848aa2 | ||
|
|
a04e6de047 | ||
|
|
75ae42121b | ||
|
|
578dd1dc42 | ||
|
|
d096f1d381 | ||
|
|
cb61c358ea | ||
|
|
1bbbc1ca1c | ||
|
|
68fab55251 | ||
|
|
6bc6c1b6cc | ||
|
|
4ba8268a29 | ||
|
|
e4683a1e9f | ||
|
|
8c32719f3d | ||
|
|
b3aeddac85 | ||
|
|
8e8f83656f | ||
|
|
ee6aae7219 | ||
|
|
ef38761dc2 | ||
|
|
a6ca2906d8 | ||
|
|
723c9be5a0 | ||
|
|
8567db10b5 | ||
|
|
18fb70b32f | ||
|
|
268f7b715c | ||
|
|
50e7973fc0 | ||
|
|
243f718193 | ||
|
|
4637dcde33 | ||
|
|
ff9fe85507 | ||
|
|
5e4459f41d | ||
|
|
0111c4d581 | ||
|
|
7c22113c34 | ||
|
|
c952e26cbb | ||
|
|
1bad04db50 | ||
|
|
e3391175d9 | ||
|
|
4c6b9b695c | ||
|
|
fcffd190d0 | ||
|
|
73e94ffde0 | ||
|
|
6605ba80e7 | ||
|
|
0432e2e947 | ||
|
|
d9710ea4ff | ||
|
|
8965bb8977 | ||
|
|
1376c8f8cf | ||
|
|
a907ec92b5 | ||
|
|
39010ab847 | ||
|
|
25c1828288 | ||
|
|
de8dd37e44 | ||
|
|
f120865350 | ||
|
|
297eb952bc | ||
|
|
b52b90c182 | ||
|
|
4e91dadfab | ||
|
|
bafa0ec1ee | ||
|
|
5a00a97cf1 | ||
|
|
3cb8f1ab20 | ||
|
|
a36e457c12 | ||
|
|
2b1b1fb0d4 | ||
|
|
abb5bc6aba | ||
|
|
ee0874a26d | ||
|
|
67699bf17e | ||
|
|
57877bb007 | ||
|
|
a03ed6f742 | ||
|
|
ffe3453937 | ||
|
|
3060ecc066 | ||
|
|
634d9834de | ||
|
|
d573962259 | ||
|
|
e2dcec62d3 | ||
|
|
0e5603f644 | ||
|
|
c2e5fc5215 | ||
|
|
39d0cd7237 | ||
|
|
6fb83c2ba3 | ||
|
|
415c3d57af | ||
|
|
9e5ecc11b7 | ||
|
|
f636ab21f8 | ||
|
|
97fbbc74e6 | ||
|
|
dc1d92855d | ||
|
|
c92f118e5e | ||
|
|
9baaf607a3 | ||
|
|
c226707a80 | ||
|
|
ab3681e164 | ||
|
|
3ef612f05a | ||
|
|
6dd75f697a | ||
|
|
593fbadc98 | ||
|
|
060095d39f | ||
|
|
2506bb6673 | ||
|
|
af7de9a0c5 | ||
|
|
000da2f6d0 | ||
|
|
78b4cbdb69 | ||
|
|
0c0e0aab09 | ||
|
|
322379e6ae | ||
|
|
1d86bd5610 | ||
|
|
adc937c57d | ||
|
|
8d425a6f94 | ||
|
|
4d47c0fd63 | ||
|
|
3ad4f18e1a | ||
|
|
875fdef917 | ||
|
|
318014e4ab | ||
|
|
91cd590395 | ||
|
|
a7fc32c8da | ||
|
|
1e0ae04aba | ||
|
|
b5a8ffaddc | ||
|
|
dd61a5b2c6 | ||
|
|
63b16e14d8 | ||
|
|
885f397cdd | ||
|
|
6046b25f56 | ||
|
|
045a12ddc6 | ||
|
|
e66c2621af | ||
|
|
d0134f2c64 | ||
|
|
e2e57e5b6d | ||
|
|
0f1f354ba6 | ||
|
|
abbbdb5771 | ||
|
|
559da842c0 | ||
|
|
e19c1b5364 | ||
|
|
12b48f86e7 | ||
|
|
794cc1e3be | ||
|
|
86994f81c3 |
1
.idea/artifacts/KotlinJpsPlugin.xml
generated
1
.idea/artifacts/KotlinJpsPlugin.xml
generated
@@ -11,7 +11,6 @@
|
||||
<element id="directory" name="META-INF">
|
||||
<element id="dir-copy" path="$PROJECT_DIR$/jps-plugin/src/META-INF" />
|
||||
</element>
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/dependencies/cli-parser-1.1.2.jar" path-in-jar="/" />
|
||||
<element id="module-output" name="cli-common" />
|
||||
<element id="module-output" name="idea-jps-common" />
|
||||
<element id="module-output" name="jps-plugin" />
|
||||
|
||||
7
.idea/dictionaries/4u7.xml
generated
Normal file
7
.idea/dictionaries/4u7.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="4u7">
|
||||
<words>
|
||||
<w>foldable</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
11
.idea/libraries/cli_parser.xml
generated
11
.idea/libraries/cli_parser.xml
generated
@@ -1,11 +0,0 @@
|
||||
<component name="libraryTable">
|
||||
<library name="cli-parser">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/dependencies/cli-parser-1.1.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/dependencies/cli-parser-1.1.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
37
ChangeLog.md
37
ChangeLog.md
@@ -3,6 +3,42 @@
|
||||
<!-- Find: ([^\`/\[])(KT-\d+) -->
|
||||
<!-- Replace: $1[`$2`](https://youtrack.jetbrains.com/issue/$2) -->
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### IDE
|
||||
- [`KT-16714`](https://youtrack.jetbrains.com/issue/KT-16714) J2K: Write access is allowed from event dispatch thread only
|
||||
|
||||
### Compiler
|
||||
- [`KT-16801`](https://youtrack.jetbrains.com/issue/KT-16801) Accessors of `@PublishedApi` property gets mangled
|
||||
- [`KT-16673`](https://youtrack.jetbrains.com/issue/KT-16673) Potentially problematic code causes exception when work with SAM adapters
|
||||
|
||||
### Libraries
|
||||
- [`KT-16557`](https://youtrack.jetbrains.com/issue/KT-16557) Correct `SinceKotlin(1.1)` for all declarations in `kotlin.reflect.full`
|
||||
|
||||
## 1.1.1-RC
|
||||
|
||||
### IDE
|
||||
- [`KT-16481`](https://youtrack.jetbrains.com/issue/KT-16481) Kotlin debugger & bytecode fail on select statement blocks (IllegalStateException: More than one package fragment)
|
||||
|
||||
### Gradle support
|
||||
- [`KT-15783`](https://youtrack.jetbrains.com/issue/KT-15783) Gradle builds don't use incremental compilation due to an error: "Could not connect to kotlin daemon"
|
||||
- [`KT-16434`](https://youtrack.jetbrains.com/issue/KT-16434) Gradle plugin does not compile androidTest sources when Jack is enabled
|
||||
- [`KT-16546`](https://youtrack.jetbrains.com/issue/KT-16546) Enable incremental compilation in gradle by default
|
||||
|
||||
### Compiler
|
||||
- [`KT-16184`](https://youtrack.jetbrains.com/issue/KT-16184) AbstractMethodError in Kapt3ComponentRegistrar while compiling from IntelliJ using Kotlin 1.1.0
|
||||
- [`KT-16578`](https://youtrack.jetbrains.com/issue/KT-16578) Fix substitutor for synthetic SAM adapters
|
||||
- [`KT-16581`](https://youtrack.jetbrains.com/issue/KT-16581) VerifyError when calling default value parameter with jvm-target 1.8
|
||||
- [`KT-16583`](https://youtrack.jetbrains.com/issue/KT-16583) Cannot access private file-level variables inside a class init within the same file if a secondary constructor is present
|
||||
- [`KT-16587`](https://youtrack.jetbrains.com/issue/KT-16587) AbstractMethodError: Delegates not generated correctly for private interfaces
|
||||
- [`KT-16598`](https://youtrack.jetbrains.com/issue/KT-16598) Incorrect error: The feature "bound callable references" is only available since language version 1.1
|
||||
- [`KT-16621`](https://youtrack.jetbrains.com/issue/KT-16621) Kotlin compiler doesn't report an error if a class implements Annotation interface but doesn't implement annotationType method
|
||||
- [`KT-16441`](https://youtrack.jetbrains.com/issue/KT-16441) `NoSuchFieldError: $$delegatedProperties` when delegating through `provideDelegate` in companion object
|
||||
|
||||
### JavaScript support
|
||||
- Prohibit function types with receiver as parameter types of external declarations
|
||||
- Remove extension receiver for function parameters in `jQuery` declarations
|
||||
|
||||
## 1.1
|
||||
|
||||
### Compiler exceptions
|
||||
@@ -12,6 +48,7 @@
|
||||
|
||||
### Standard library
|
||||
- [`KT-6561`](https://youtrack.jetbrains.com/issue/KT-6561) Drop java.util.Collections package from js stdlib
|
||||
- `javaClass` extension property is no more deprecated due to migration problems
|
||||
|
||||
### IDE
|
||||
- [`KT-16329`](https://youtrack.jetbrains.com/issue/KT-16329) Inspection "Calls to staic methods in Java interfaces..." always reports warning undependent of jvm-target
|
||||
|
||||
@@ -29,8 +29,8 @@ internal object KotlinAntTaskUtil {
|
||||
|
||||
private val libPath: File by lazy {
|
||||
// Find path of kotlin-ant.jar in the filesystem and find kotlin-compiler.jar in the same directory
|
||||
val resourcePath = "/" + javaClass.name.replace('.', '/') + ".class"
|
||||
val jarConnection = javaClass.getResource(resourcePath).openConnection() as? JarURLConnection
|
||||
val resourcePath = "/" + this::class.java.name.replace('.', '/') + ".class"
|
||||
val jarConnection = this::class.java.getResource(resourcePath).openConnection() as? JarURLConnection
|
||||
?: throw UnsupportedOperationException("Kotlin compiler Ant task should be loaded from the JAR file")
|
||||
val antTaskJarPath = File(jarConnection.jarFileURL.toURI())
|
||||
|
||||
@@ -54,7 +54,7 @@ internal object KotlinAntTaskUtil {
|
||||
val cached = classLoaderRef.get()
|
||||
if (cached != null) return cached
|
||||
|
||||
val myLoader = javaClass.classLoader
|
||||
val myLoader = this::class.java.classLoader
|
||||
if (myLoader !is AntClassLoader) return myLoader
|
||||
|
||||
val classLoader = ClassPreloadingUtils.preloadClasses(listOf(compilerJar), Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE, myLoader, null)
|
||||
|
||||
@@ -19,9 +19,9 @@ package org.jetbrains.kotlin.compilerRunner;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ComparatorUtil;
|
||||
import com.sampullara.cli.Argument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -43,7 +43,6 @@ import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.serialization.deserialization.supertypes
|
||||
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
@@ -272,7 +271,7 @@ open class IncrementalCacheImpl<Target>(
|
||||
ProtoBuf.Class::getPropertyList
|
||||
) + classData.classProto.enumEntryList.map { classData.nameResolver.getString(it.name) }
|
||||
|
||||
val companionObjectChanged = createChangeInfo(classFqName.parent(), classFqName.shortName().asString().singletonOrEmptyList())
|
||||
val companionObjectChanged = createChangeInfo(classFqName.parent(), listOfNotNull(classFqName.shortName().asString()))
|
||||
val companionObjectMembersChanged = createChangeInfo(classFqName, memberNames)
|
||||
|
||||
listOf(companionObjectMembersChanged, companionObjectChanged)
|
||||
@@ -780,7 +779,7 @@ sealed class ChangeInfo(val fqName: FqName) {
|
||||
protected open fun toStringProperties(): String = "fqName = $fqName"
|
||||
|
||||
override fun toString(): String {
|
||||
return this.javaClass.simpleName + "(${toStringProperties()})"
|
||||
return this::class.java.simpleName + "(${toStringProperties()})"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,5 +50,5 @@ class LocalFileKotlinClass private constructor(
|
||||
|
||||
override fun hashCode(): Int = file.hashCode()
|
||||
override fun equals(other: Any?): Boolean = other is LocalFileKotlinClass && file == other.file
|
||||
override fun toString(): String = "$javaClass: $file"
|
||||
override fun toString(): String = "${this::class.java}: $file"
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import java.util.*
|
||||
|
||||
|
||||
fun Iterable<File>.javaSourceRoots(roots: Iterable<File>): Iterable<File> =
|
||||
filter { it.isJavaFile() }
|
||||
filter(File::isJavaFile)
|
||||
.map { findSrcDirRoot(it, roots) }
|
||||
.filterNotNull()
|
||||
|
||||
@@ -145,7 +145,7 @@ fun LookupStorage.update(
|
||||
filesToCompile: Iterable<File>,
|
||||
removedFiles: Iterable<File>
|
||||
) {
|
||||
if (lookupTracker !is LookupTrackerImpl) throw AssertionError("Lookup tracker is expected to be LookupTrackerImpl, got ${lookupTracker.javaClass}")
|
||||
if (lookupTracker !is LookupTrackerImpl) throw AssertionError("Lookup tracker is expected to be LookupTrackerImpl, got ${lookupTracker::class.java}")
|
||||
|
||||
removeLookupsFrom(filesToCompile.asSequence() + removedFiles.asSequence())
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ abstract class BasicMap<K : Comparable<K>, V>(
|
||||
fun dump(): String {
|
||||
return with(StringBuilder()) {
|
||||
with(Printer(this)) {
|
||||
println(this@BasicMap.javaClass.simpleName)
|
||||
println(this@BasicMap::class.java.simpleName)
|
||||
pushIndent()
|
||||
|
||||
for (key in storage.keys.sorted()) {
|
||||
|
||||
@@ -131,7 +131,7 @@ object ConstantsMapExternalizer : DataExternalizer<Map<String, Any>> {
|
||||
output.writeByte(Kind.STRING.ordinal)
|
||||
IOUtil.writeString(value, output)
|
||||
}
|
||||
else -> throw IllegalStateException("Unexpected constant class: ${value.javaClass}")
|
||||
else -> throw IllegalStateException("Unexpected constant class: ${value::class.java}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ private fun classFileToString(classFile: File): String {
|
||||
|
||||
out.write("\n------ string table types proto -----\n${DebugJvmProtoBuf.StringTableTypes.parseDelimitedFrom(input)}")
|
||||
|
||||
if (!classHeader!!.metadataVersion.isCompatible()) {
|
||||
if (!classHeader.metadataVersion.isCompatible()) {
|
||||
error("Incompatible class ($classHeader): $classFile")
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,8 @@ fun getModificationsToPerform(
|
||||
|
||||
val rules = mapOf<String, (String, File) -> Modification>(
|
||||
newSuffix to { path, file -> ModifyContent(path, file) },
|
||||
touchSuffix to { path, file -> TouchFile(path, touchPolicy) },
|
||||
deleteSuffix to { path, file -> DeleteFile(path) }
|
||||
touchSuffix to { path, _ -> TouchFile(path, touchPolicy) },
|
||||
deleteSuffix to { path, _ -> DeleteFile(path) }
|
||||
)
|
||||
|
||||
val modifications = ArrayList<Modification>()
|
||||
@@ -130,7 +130,7 @@ fun getModificationsToPerform(
|
||||
abstract class Modification(val path: String) {
|
||||
abstract fun perform(workDir: File, mapping: MutableMap<File, File>)
|
||||
|
||||
override fun toString(): String = "${javaClass.simpleName} $path"
|
||||
override fun toString(): String = "${this::class.java.simpleName} $path"
|
||||
}
|
||||
|
||||
class ModifyContent(path: String, val dataFile: File) : Modification(path) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
17
build.xml
17
build.xml
@@ -1,4 +1,4 @@
|
||||
<project name="Kotlin" default="dist" xmlns:if="ant:if" xmlns:unless="ant:unless">
|
||||
<project xmlns:if="ant:if" xmlns:unless="ant:unless" name="Kotlin" default="dist">
|
||||
<import file="common.xml" optional="false"/>
|
||||
<property file="resources/kotlinManifest.properties"/>
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
<fileset dir="${basedir}/lib" includes="**/*.jar"/>
|
||||
<fileset dir="${dependencies}" includes="jansi.jar"/>
|
||||
<fileset dir="${dependencies}" includes="jline.jar"/>
|
||||
<fileset dir="${dependencies}" includes="cli-parser-1.1.2.jar"/>
|
||||
<fileset dir="${basedir}/ideaSDK/jps" includes="jps-model.jar"/>
|
||||
</path>
|
||||
|
||||
@@ -406,9 +405,12 @@
|
||||
byline="true" encoding="UTF-8" />
|
||||
</target>
|
||||
|
||||
<target name="js-stdlib">
|
||||
<property environment="env"/>
|
||||
<target name="js-stdlib-preprocess">
|
||||
<kotlin-pp src="libraries/stdlib/src" output="${intermediate-sources}/stdlib/js" profile="JS" />
|
||||
</target>
|
||||
|
||||
<target name="js-stdlib" depends="js-stdlib-preprocess">
|
||||
<property environment="env"/>
|
||||
<cleandir dir="${js.stdlib.output.dir}"/>
|
||||
|
||||
<!-- We don't want descriptors for built-ins to be serialized, so we compile these files separately. -->
|
||||
@@ -474,7 +476,7 @@
|
||||
<copy file="${kotlin-home}/lib/kotlin-stdlib-js.jar" tofile="${kotlin-home}/lib/kotlin-jslib.jar" />
|
||||
</target>
|
||||
|
||||
<target name="pack-js-stdlib-sources">
|
||||
<target name="pack-js-stdlib-sources" depends="js-stdlib-preprocess">
|
||||
<jar destfile="${kotlin-home}/lib/kotlin-stdlib-js-sources.jar" duplicate="fail">
|
||||
<resources refid="js.lib.files" />
|
||||
<fileset refid="kotlin.builtin.files" />
|
||||
@@ -586,7 +588,6 @@
|
||||
<zipfileset src="${idea.sdk}/lib/oromatcher.jar"/>
|
||||
<zipfileset src="${idea.sdk}/jps/jps-model.jar"/>
|
||||
<zipfileset src="${dependencies}/jline.jar"/>
|
||||
<zipfileset src="${dependencies}/cli-parser-1.1.2.jar"/>
|
||||
<zipfileset src="${protobuf.jar}"/>
|
||||
|
||||
<manifest>
|
||||
@@ -1032,13 +1033,13 @@
|
||||
<sequential>
|
||||
<java classname="org.jetbrains.kotlin.preloading.Preloader" failonerror="true" fork="true" maxmemory="${max.heap.size.for.forked.jvm}">
|
||||
<classpath>
|
||||
<pathelement location="${kotlin-home}/lib/kotlin-preloader.jar"/>
|
||||
<pathelement location="${bootstrap.compiler.home}/lib/kotlin-preloader.jar"/>
|
||||
</classpath>
|
||||
<assertions>
|
||||
<enable/>
|
||||
</assertions>
|
||||
<arg value="-cp"/>
|
||||
<arg value="${kotlin-home}/lib/kotlin-compiler.jar"/>
|
||||
<arg value="${bootstrap.compiler.home}/lib/kotlin-compiler.jar"/>
|
||||
<arg value="org.jetbrains.kotlin.preprocessor.PreprocessorCLI"/>
|
||||
<arg value="@{src}"/>
|
||||
<arg value="@{output}"/>
|
||||
|
||||
@@ -91,7 +91,7 @@ object CodegenUtil {
|
||||
else if (traitMember is PropertyDescriptor) {
|
||||
for (traitAccessor in traitMember.accessors) {
|
||||
for (inheritedAccessor in (copy as PropertyDescriptor).accessors) {
|
||||
if (inheritedAccessor.javaClass == traitAccessor.javaClass) { // same accessor kind
|
||||
if (inheritedAccessor::class.java == traitAccessor::class.java) { // same accessor kind
|
||||
result.put(traitAccessor, inheritedAccessor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,5 +103,5 @@ abstract class DataClassMethodGenerator(private val declaration: KtClassOrObject
|
||||
.map { bindingContext.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it)!! }
|
||||
|
||||
private val primaryConstructorParameters: List<KtParameter>
|
||||
get() = (declaration as? KtClass)?.getPrimaryConstructorParameters().orEmpty()
|
||||
get() = (declaration as? KtClass)?.primaryConstructorParameters.orEmpty()
|
||||
}
|
||||
|
||||
@@ -77,9 +77,13 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtPureClassOrObject
|
||||
}
|
||||
}
|
||||
|
||||
generatePrimaryConstructorProperties();
|
||||
generateConstructors();
|
||||
generateDefaultImplsIfNeeded();
|
||||
boolean generateNonClassMembers = shouldGenerateNonClassMembers();
|
||||
|
||||
if (generateNonClassMembers) {
|
||||
generatePrimaryConstructorProperties();
|
||||
generateConstructors();
|
||||
generateDefaultImplsIfNeeded();
|
||||
}
|
||||
|
||||
// Generate _declared_ companions
|
||||
for (KtObjectDeclaration companion : companions) {
|
||||
@@ -92,23 +96,30 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtPureClassOrObject
|
||||
genSyntheticClassOrObject((SyntheticClassOrObjectDescriptor) companionObjectDescriptor);
|
||||
}
|
||||
|
||||
if (!DescriptorUtils.isInterface(descriptor)) {
|
||||
for (DeclarationDescriptor memberDescriptor : DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().getMemberScope())) {
|
||||
if (memberDescriptor instanceof CallableMemberDescriptor) {
|
||||
CallableMemberDescriptor member = (CallableMemberDescriptor) memberDescriptor;
|
||||
if (!member.getKind().isReal() && ImplKt.findInterfaceImplementation(member) == null) {
|
||||
if (member instanceof FunctionDescriptor) {
|
||||
functionCodegen.generateBridges((FunctionDescriptor) member);
|
||||
if (generateNonClassMembers) {
|
||||
generateBridges();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateBridges() {
|
||||
if (DescriptorUtils.isInterface(descriptor)) {
|
||||
return;
|
||||
}
|
||||
for (DeclarationDescriptor memberDescriptor : DescriptorUtils.getAllDescriptors(descriptor.getDefaultType().getMemberScope())) {
|
||||
if (memberDescriptor instanceof CallableMemberDescriptor) {
|
||||
CallableMemberDescriptor member = (CallableMemberDescriptor) memberDescriptor;
|
||||
if (!member.getKind().isReal() && ImplKt.findInterfaceImplementation(member) == null) {
|
||||
if (member instanceof FunctionDescriptor) {
|
||||
functionCodegen.generateBridges((FunctionDescriptor) member);
|
||||
}
|
||||
else if (member instanceof PropertyDescriptor) {
|
||||
PropertyGetterDescriptor getter = ((PropertyDescriptor) member).getGetter();
|
||||
if (getter != null) {
|
||||
functionCodegen.generateBridges(getter);
|
||||
}
|
||||
else if (member instanceof PropertyDescriptor) {
|
||||
PropertyGetterDescriptor getter = ((PropertyDescriptor) member).getGetter();
|
||||
if (getter != null) {
|
||||
functionCodegen.generateBridges(getter);
|
||||
}
|
||||
PropertySetterDescriptor setter = ((PropertyDescriptor) member).getSetter();
|
||||
if (setter != null) {
|
||||
functionCodegen.generateBridges(setter);
|
||||
}
|
||||
PropertySetterDescriptor setter = ((PropertyDescriptor) member).getSetter();
|
||||
if (setter != null) {
|
||||
functionCodegen.generateBridges(setter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +127,11 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtPureClassOrObject
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldGenerateNonClassMembers() {
|
||||
return !(myClass instanceof KtClassOrObject) ||
|
||||
state.getGenerateDeclaredClassFilter().shouldGenerateClassMembers((KtClassOrObject) myClass);
|
||||
}
|
||||
|
||||
protected void generateConstructors() {
|
||||
|
||||
}
|
||||
@@ -130,7 +146,9 @@ public abstract class ClassBodyCodegen extends MemberCodegen<KtPureClassOrObject
|
||||
|
||||
protected void generateDeclaration(KtDeclaration declaration) {
|
||||
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
|
||||
genSimpleMember(declaration);
|
||||
if (shouldGenerateNonClassMembers()) {
|
||||
genSimpleMember(declaration);
|
||||
}
|
||||
}
|
||||
else if (declaration instanceof KtClassOrObject) {
|
||||
if (declaration instanceof KtEnumEntry && !enumEntryNeedSubclass(bindingContext, (KtEnumEntry) declaration)) {
|
||||
|
||||
@@ -82,7 +82,7 @@ public class ClassFileFactory implements OutputFileCollection {
|
||||
return answer;
|
||||
}
|
||||
|
||||
void done() {
|
||||
public void done() {
|
||||
if (!isDone) {
|
||||
isDone = true;
|
||||
writeModuleMappings();
|
||||
|
||||
@@ -54,7 +54,7 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
contextKind: OwnerKind,
|
||||
classOrObject: KtPureClassOrObject
|
||||
) {
|
||||
val methodElement = classOrObject.getPrimaryConstructor() ?: classOrObject
|
||||
val methodElement = classOrObject.primaryConstructor ?: classOrObject
|
||||
|
||||
if (generateOverloadsIfNeeded(methodElement, constructorDescriptor, constructorDescriptor, contextKind, classBuilder, memberCodegen)) {
|
||||
return
|
||||
@@ -135,7 +135,7 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (functionDescriptor.modality == Modality.FINAL && functionDescriptor !is ConstructorDescriptor) Opcodes.ACC_FINAL else 0) or
|
||||
(if (remainingParameters.lastOrNull()?.varargElementType != null) Opcodes.ACC_VARARGS else 0)
|
||||
val signature = typeMapper.mapSignature(functionDescriptor, contextKind, remainingParameters, false)
|
||||
val signature = typeMapper.mapSignatureWithCustomParameters(functionDescriptor, contextKind, remainingParameters, false)
|
||||
val mv = classBuilder.newMethod(OtherOrigin(methodElement, functionDescriptor), flags,
|
||||
signature.asmMethod.name,
|
||||
signature.asmMethod.descriptor,
|
||||
@@ -251,7 +251,7 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
val classDescriptor = constructorDescriptor.constructedClass
|
||||
if (classDescriptor.kind != ClassKind.CLASS) return false
|
||||
|
||||
if (classOrObject.isLocal()) return false
|
||||
if (classOrObject.isLocal) return false
|
||||
|
||||
if (CodegenBinding.canHaveOuter(state.bindingContext, classDescriptor)) return false
|
||||
|
||||
@@ -265,6 +265,6 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) {
|
||||
}
|
||||
|
||||
private fun hasSecondaryConstructorsWithNoParameters(klass: KtClass) =
|
||||
klass.getSecondaryConstructors().any { it.valueParameters.isEmpty() }
|
||||
klass.secondaryConstructors.any { it.valueParameters.isEmpty() }
|
||||
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ abstract class DelegatingClassBuilderFactory(
|
||||
abstract override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder
|
||||
|
||||
override fun asBytes(builder: ClassBuilder?): ByteArray? {
|
||||
return delegate.asBytes((builder as DelegatingClassBuilder).getDelegate())
|
||||
return delegate.asBytes((builder as DelegatingClassBuilder).delegate)
|
||||
}
|
||||
|
||||
override fun asText(builder: ClassBuilder?): String? {
|
||||
return delegate.asText((builder as DelegatingClassBuilder).getDelegate())
|
||||
return delegate.asText((builder as DelegatingClassBuilder).delegate)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1919,11 +1919,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return asmType(varType);
|
||||
}
|
||||
|
||||
private static boolean isSharedVarType(@NotNull Type type) {
|
||||
return type.getSort() == Type.OBJECT && type.getInternalName().startsWith(REF_TYPE_PREFIX);
|
||||
}
|
||||
|
||||
|
||||
private void putDescriptorIntoFrameMap(@NotNull KtElement statement) {
|
||||
if (statement instanceof KtDestructuringDeclaration) {
|
||||
KtDestructuringDeclaration multiDeclaration = (KtDestructuringDeclaration) statement;
|
||||
@@ -4320,6 +4315,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
private static ReceiverValue getConstructorReceiver(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
CallableDescriptor constructor = resolvedCall.getResultingDescriptor();
|
||||
if (constructor.getExtensionReceiverParameter() != null) {
|
||||
// see comment on `withDispatchReceiver` parameter in
|
||||
// org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptorImpl.Companion.createIfAvailable
|
||||
assert constructor instanceof TypeAliasConstructorDescriptor :
|
||||
"Only type alias constructor can have an extension receiver: " + constructor;
|
||||
return resolvedCall.getExtensionReceiver();
|
||||
|
||||
@@ -88,6 +88,7 @@ import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.
|
||||
import static org.jetbrains.kotlin.types.Variance.INVARIANT;
|
||||
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isLocalFunction;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
import static org.jetbrains.org.objectweb.asm.Type.getObjectType;
|
||||
|
||||
public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
private static final String ENUM_VALUES_FIELD_NAME = "$VALUES";
|
||||
@@ -113,7 +114,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
boolean isLocal
|
||||
) {
|
||||
super(aClass, context, v, state, parentCodegen);
|
||||
this.classAsmType = typeMapper.mapClass(descriptor);
|
||||
this.classAsmType = getObjectType(typeMapper.classInternalName(descriptor));
|
||||
this.isLocal = isLocal;
|
||||
delegationFieldsInfo = getDelegationFieldsInfo(myClass.getSuperTypeListEntries());
|
||||
}
|
||||
@@ -127,43 +128,35 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
boolean isAbstract = false;
|
||||
boolean isInterface = false;
|
||||
boolean isFinal = false;
|
||||
boolean isStatic;
|
||||
boolean isAnnotation = false;
|
||||
boolean isEnum = false;
|
||||
|
||||
ClassKind kind = descriptor.getKind();
|
||||
|
||||
if (kind == ClassKind.OBJECT) {
|
||||
isStatic = isCompanionObject(descriptor);
|
||||
isFinal = true;
|
||||
Modality modality = descriptor.getModality();
|
||||
|
||||
if (modality == Modality.ABSTRACT || modality == Modality.SEALED) {
|
||||
isAbstract = true;
|
||||
}
|
||||
else {
|
||||
Modality modality = descriptor.getModality();
|
||||
|
||||
if (modality == Modality.ABSTRACT || modality == Modality.SEALED) {
|
||||
isAbstract = true;
|
||||
}
|
||||
if (kind == ClassKind.INTERFACE) {
|
||||
isAbstract = true;
|
||||
isInterface = true;
|
||||
}
|
||||
else if (kind == ClassKind.ANNOTATION_CLASS) {
|
||||
isAbstract = true;
|
||||
isInterface = true;
|
||||
isAnnotation = true;
|
||||
}
|
||||
else if (kind == ClassKind.ENUM_CLASS) {
|
||||
isAbstract = hasAbstractMembers(descriptor);
|
||||
isEnum = true;
|
||||
}
|
||||
|
||||
if (kind == ClassKind.INTERFACE) {
|
||||
isAbstract = true;
|
||||
isInterface = true;
|
||||
}
|
||||
else if (kind == ClassKind.ANNOTATION_CLASS) {
|
||||
isAbstract = true;
|
||||
isInterface = true;
|
||||
isAnnotation = true;
|
||||
}
|
||||
else if (kind == ClassKind.ENUM_CLASS) {
|
||||
isAbstract = hasAbstractMembers(descriptor);
|
||||
isEnum = true;
|
||||
}
|
||||
|
||||
if (modality != Modality.OPEN && !isAbstract) {
|
||||
// Light-class mode: Do not make enum classes final since PsiClass corresponding to enum is expected to be inheritable from
|
||||
isFinal = !(kind == ClassKind.ENUM_CLASS && !state.getClassBuilderMode().generateBodies);
|
||||
}
|
||||
|
||||
isStatic = !descriptor.isInner();
|
||||
if (modality != Modality.OPEN && !isAbstract) {
|
||||
isFinal = kind == ClassKind.OBJECT ||
|
||||
// Light-class mode: Do not make enum classes final since PsiClass corresponding to enum is expected to be inheritable from
|
||||
!(kind == ClassKind.ENUM_CLASS && !state.getClassBuilderMode().generateBodies);
|
||||
}
|
||||
|
||||
int access = 0;
|
||||
@@ -175,7 +168,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
// Thus we must write full accessibility flags on inner classes in this mode
|
||||
access |= getVisibilityAccessFlag(descriptor);
|
||||
// Same for STATIC
|
||||
if (isStatic) {
|
||||
if (!descriptor.isInner()) {
|
||||
access |= ACC_STATIC;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +325,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
for (String kotlinMarkerInterface : kotlinMarkerInterfaces) {
|
||||
sw.writeInterface();
|
||||
sw.writeAsmType(Type.getObjectType(kotlinMarkerInterface));
|
||||
sw.writeAsmType(getObjectType(kotlinMarkerInterface));
|
||||
sw.writeInterfaceEnd();
|
||||
}
|
||||
|
||||
@@ -1207,9 +1200,9 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
}
|
||||
else if (descriptor instanceof VariableDescriptor) {
|
||||
if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
|
||||
ClassDescriptor classDescriptor =
|
||||
(ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration();
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
if (containingDeclaration instanceof ConstructorDescriptor) {
|
||||
ClassDescriptor classDescriptor = ((ConstructorDescriptor) containingDeclaration).getConstructedClass();
|
||||
if (classDescriptor == ImplementationBodyCodegen.this.descriptor) return;
|
||||
}
|
||||
lookupInContext(descriptor);
|
||||
|
||||
@@ -127,7 +127,10 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
|
||||
generateBody();
|
||||
|
||||
generateSyntheticParts();
|
||||
if (!(element instanceof KtClassOrObject) ||
|
||||
state.getGenerateDeclaredClassFilter().shouldGenerateClassMembers((KtClassOrObject) element)) {
|
||||
generateSyntheticParts();
|
||||
}
|
||||
|
||||
if (state.getClassBuilderMode().generateMetadata) {
|
||||
generateKotlinMetadataAnnotation();
|
||||
@@ -342,17 +345,19 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
|
||||
}
|
||||
|
||||
private void writeInnerClass(@NotNull ClassDescriptor innerClass) {
|
||||
writeInnerClass(innerClass, typeMapper, v);
|
||||
if (!ErrorUtils.isError(innerClass)) {
|
||||
writeInnerClass(innerClass, typeMapper, v);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeInnerClass(@NotNull ClassDescriptor innerClass, @NotNull KotlinTypeMapper typeMapper, @NotNull ClassBuilder v) {
|
||||
DeclarationDescriptor containing = innerClass.getContainingDeclaration();
|
||||
String outerClassInternalName = null;
|
||||
if (containing instanceof ClassDescriptor) {
|
||||
outerClassInternalName = typeMapper.mapClass((ClassDescriptor) containing).getInternalName();
|
||||
outerClassInternalName = typeMapper.classInternalName((ClassDescriptor) containing);
|
||||
}
|
||||
String innerName = innerClass.getName().isSpecial() ? null : innerClass.getName().asString();
|
||||
String innerClassInternalName = typeMapper.mapClass(innerClass).getInternalName();
|
||||
String innerClassInternalName = typeMapper.classInternalName(innerClass);
|
||||
v.visitInnerClass(innerClassInternalName, outerClassInternalName, innerName, calculateInnerClassAccessFlags(innerClass));
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ class MultifileClassPartCodegen(
|
||||
}
|
||||
|
||||
val serializer = DescriptorSerializer.createTopLevel(JvmSerializerExtension(v.serializationBindings, state))
|
||||
val packageProto = serializer.packagePartProto(members).build()
|
||||
val packageProto = serializer.packagePartProto(packageFragment.fqName, members).build()
|
||||
|
||||
val extraFlags = if (shouldGeneratePartHierarchy) JvmAnnotationNames.METADATA_MULTIFILE_PARTS_INHERIT_FLAG else 0
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
|
||||
final DescriptorSerializer serializer =
|
||||
DescriptorSerializer.createTopLevel(new JvmSerializerExtension(v.getSerializationBindings(), state));
|
||||
final ProtoBuf.Package packageProto = serializer.packagePartProto(members).build();
|
||||
final ProtoBuf.Package packageProto = serializer.packagePartProto(element.getPackageFqName(), members).build();
|
||||
|
||||
WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.FILE_FACADE, 0, new Function1<AnnotationVisitor, Unit>() {
|
||||
@Override
|
||||
|
||||
@@ -57,7 +57,6 @@ import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getDeprecatedAccessFlag;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getVisibilityForBackingField;
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPropertyWithBackingFieldCopyInOuterClass;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConstOrHasJvmFieldAnnotation;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface;
|
||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.FIELD_FOR_PROPERTY;
|
||||
@@ -189,7 +188,7 @@ public class PropertyCodegen {
|
||||
if (isCompanionObject(descriptor.getContainingDeclaration())) return true;
|
||||
|
||||
// Non-const properties from multifile classes have accessors regardless of visibility
|
||||
if (!descriptor.isConst() && JvmFileClassUtilKt.isInsideJvmMultifileClassFile(declaration)) return true;
|
||||
if (isNonConstTopLevelPropertyInMultifileClass(declaration, descriptor)) return true;
|
||||
|
||||
// Private class properties have accessors only in cases when those accessors are non-trivial
|
||||
if (Visibilities.isPrivate(descriptor.getVisibility())) {
|
||||
@@ -199,6 +198,15 @@ public class PropertyCodegen {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isNonConstTopLevelPropertyInMultifileClass(
|
||||
@NotNull KtProperty declaration,
|
||||
@NotNull PropertyDescriptor descriptor
|
||||
) {
|
||||
return !descriptor.isConst() &&
|
||||
descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor &&
|
||||
JvmFileClassUtilKt.isInsideJvmMultifileClassFile(declaration);
|
||||
}
|
||||
|
||||
private static boolean areAccessorsNeededForPrimaryConstructorProperty(
|
||||
@NotNull PropertyDescriptor descriptor
|
||||
) {
|
||||
|
||||
@@ -34,7 +34,7 @@ data class SourceInfo(val source: String, val pathOrCleanFQN: String, val linesI
|
||||
val isTopLevel = element is KtFile || (element is KtNamedFunction && element.getParent() is KtFile)
|
||||
val cleanedClassFqName = if (!isTopLevel) internalClassName else internalClassName.substringBefore('$')
|
||||
|
||||
return SourceInfo(element.getContainingKtFile().name, cleanedClassFqName, lineNumbers!!)
|
||||
return SourceInfo(element.containingKtFile.name, cleanedClassFqName, lineNumbers!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,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.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;
|
||||
|
||||
@@ -281,60 +282,29 @@ public abstract class StackValue {
|
||||
}
|
||||
|
||||
private static void box(Type type, Type toType, InstructionAdapter v) {
|
||||
if (type == Type.BYTE_TYPE || toType.getInternalName().equals(NULLABLE_BYTE_TYPE_NAME) && type == Type.INT_TYPE) {
|
||||
v.cast(type, Type.BYTE_TYPE);
|
||||
v.invokestatic(NULLABLE_BYTE_TYPE_NAME, "valueOf", "(B)L" + NULLABLE_BYTE_TYPE_NAME + ";", false);
|
||||
}
|
||||
else if (type == Type.SHORT_TYPE || toType.getInternalName().equals(NULLABLE_SHORT_TYPE_NAME) && type == Type.INT_TYPE) {
|
||||
v.cast(type, Type.SHORT_TYPE);
|
||||
v.invokestatic(NULLABLE_SHORT_TYPE_NAME, "valueOf", "(S)L" + NULLABLE_SHORT_TYPE_NAME + ";", false);
|
||||
}
|
||||
else if (type == Type.LONG_TYPE || toType.getInternalName().equals(NULLABLE_LONG_TYPE_NAME) && type == Type.INT_TYPE) {
|
||||
v.cast(type, Type.LONG_TYPE);
|
||||
v.invokestatic(NULLABLE_LONG_TYPE_NAME, "valueOf", "(J)L" + NULLABLE_LONG_TYPE_NAME + ";", false);
|
||||
}
|
||||
else if (type == Type.INT_TYPE) {
|
||||
v.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
|
||||
}
|
||||
else if (type == Type.BOOLEAN_TYPE) {
|
||||
v.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
|
||||
}
|
||||
else if (type == Type.CHAR_TYPE) {
|
||||
v.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
|
||||
}
|
||||
else if (type == Type.FLOAT_TYPE) {
|
||||
v.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
|
||||
}
|
||||
else if (type == Type.DOUBLE_TYPE) {
|
||||
v.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
|
||||
if (type == Type.INT_TYPE) {
|
||||
if (toType.getInternalName().equals(NULLABLE_BYTE_TYPE_NAME)) {
|
||||
type = Type.BYTE_TYPE;
|
||||
}
|
||||
else if (toType.getInternalName().equals(NULLABLE_SHORT_TYPE_NAME)) {
|
||||
type = Type.SHORT_TYPE;
|
||||
}
|
||||
else if (toType.getInternalName().equals(NULLABLE_LONG_TYPE_NAME)) {
|
||||
type = Type.LONG_TYPE;
|
||||
}
|
||||
v.cast(Type.INT_TYPE, type);
|
||||
}
|
||||
|
||||
Type boxedType = AsmUtil.boxType(type);
|
||||
if (boxedType == type) return;
|
||||
|
||||
v.invokestatic(boxedType.getInternalName(), "valueOf", Type.getMethodDescriptor(boxedType, type), false);
|
||||
coerce(boxedType, toType, v);
|
||||
}
|
||||
|
||||
private static void unbox(Type type, InstructionAdapter v) {
|
||||
if (type == Type.INT_TYPE) {
|
||||
v.invokevirtual("java/lang/Number", "intValue", "()I", false);
|
||||
}
|
||||
else if (type == Type.BOOLEAN_TYPE) {
|
||||
v.invokevirtual("java/lang/Boolean", "booleanValue", "()Z", false);
|
||||
}
|
||||
else if (type == Type.CHAR_TYPE) {
|
||||
v.invokevirtual("java/lang/Character", "charValue", "()C", false);
|
||||
}
|
||||
else if (type == Type.SHORT_TYPE) {
|
||||
v.invokevirtual("java/lang/Number", "shortValue", "()S", false);
|
||||
}
|
||||
else if (type == Type.LONG_TYPE) {
|
||||
v.invokevirtual("java/lang/Number", "longValue", "()J", false);
|
||||
}
|
||||
else if (type == Type.BYTE_TYPE) {
|
||||
v.invokevirtual("java/lang/Number", "byteValue", "()B", false);
|
||||
}
|
||||
else if (type == Type.FLOAT_TYPE) {
|
||||
v.invokevirtual("java/lang/Number", "floatValue", "()F", false);
|
||||
}
|
||||
else if (type == Type.DOUBLE_TYPE) {
|
||||
v.invokevirtual("java/lang/Number", "doubleValue", "()D", false);
|
||||
}
|
||||
private static void unbox(Type methodOwner, Type type, InstructionAdapter v) {
|
||||
assert isPrimitive(type) : "Unboxing should be performed to primitive type, but " + type.getClassName();
|
||||
v.invokevirtual(methodOwner.getInternalName(), type.getClassName() + "Value", "()" + type.getDescriptor(), false);
|
||||
}
|
||||
|
||||
protected void coerceTo(@NotNull Type toType, @NotNull InstructionAdapter v) {
|
||||
@@ -372,23 +342,12 @@ public abstract class StackValue {
|
||||
}
|
||||
}
|
||||
else if (toType.getSort() == Type.ARRAY) {
|
||||
if (fromType.getSort() == Type.ARRAY &&
|
||||
fromType.getElementType().equals(AsmTypes.JAVA_CLASS_TYPE) && toType.equals(K_CLASS_ARRAY_TYPE)) {
|
||||
wrapJavaClassesIntoKClasses(v);
|
||||
}
|
||||
else {
|
||||
v.checkcast(toType);
|
||||
}
|
||||
v.checkcast(toType);
|
||||
}
|
||||
else if (toType.getSort() == Type.OBJECT) {
|
||||
if (fromType.getSort() == Type.OBJECT || fromType.getSort() == Type.ARRAY) {
|
||||
if (!toType.equals(OBJECT_TYPE)) {
|
||||
if (fromType.equals(AsmTypes.JAVA_CLASS_TYPE) && toType.equals(AsmTypes.K_CLASS_TYPE)) {
|
||||
wrapJavaClassIntoKClass(v);
|
||||
}
|
||||
else {
|
||||
v.checkcast(toType);
|
||||
}
|
||||
v.checkcast(toType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -396,20 +355,29 @@ public abstract class StackValue {
|
||||
}
|
||||
}
|
||||
else if (fromType.getSort() == Type.OBJECT) {
|
||||
//toType is primitive here
|
||||
Type unboxedType = unboxPrimitiveTypeOrNull(fromType);
|
||||
if (unboxedType != null) {
|
||||
unbox(unboxedType, v);
|
||||
unbox(fromType, unboxedType, v);
|
||||
coerce(unboxedType, toType, v);
|
||||
}
|
||||
else {
|
||||
Type numberType = getType(Number.class);
|
||||
if (toType.getSort() == Type.BOOLEAN || (toType.getSort() == Type.CHAR && !numberType.equals(fromType))) {
|
||||
coerce(fromType, boxType(toType), v);
|
||||
else if (toType.getSort() == Type.BOOLEAN) {
|
||||
coerce(fromType, BOOLEAN_WRAPPER_TYPE, v);
|
||||
unbox(BOOLEAN_WRAPPER_TYPE, Type.BOOLEAN_TYPE, v);
|
||||
}
|
||||
else if (toType.getSort() == Type.CHAR) {
|
||||
if (fromType.equals(NUMBER_TYPE)) {
|
||||
unbox(NUMBER_TYPE, Type.INT_TYPE, v);
|
||||
v.visitInsn(Opcodes.I2C);
|
||||
}
|
||||
else {
|
||||
coerce(fromType, numberType, v);
|
||||
coerce(fromType, CHARACTER_WRAPPER_TYPE, v);
|
||||
unbox(CHARACTER_WRAPPER_TYPE, Type.CHAR_TYPE, v);
|
||||
}
|
||||
unbox(toType, v);
|
||||
}
|
||||
else {
|
||||
coerce(fromType, NUMBER_TYPE, v);
|
||||
unbox(NUMBER_TYPE, toType, v);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1211,7 +1179,20 @@ public abstract class StackValue {
|
||||
else {
|
||||
getter.genInvokeInstruction(v);
|
||||
}
|
||||
coerce(getter.getReturnType(), type, v);
|
||||
|
||||
Type typeOfValueOnStack = getter.getReturnType();
|
||||
if (DescriptorUtils.isAnnotationClass(descriptor.getContainingDeclaration())) {
|
||||
if (this.type.equals(K_CLASS_TYPE)) {
|
||||
wrapJavaClassIntoKClass(v);
|
||||
typeOfValueOnStack = K_CLASS_TYPE;
|
||||
}
|
||||
else if (this.type.equals(K_CLASS_ARRAY_TYPE)) {
|
||||
wrapJavaClassesIntoKClasses(v);
|
||||
typeOfValueOnStack = K_CLASS_ARRAY_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
coerce(typeOfValueOnStack, type, v);
|
||||
|
||||
KotlinType returnType = descriptor.getReturnType();
|
||||
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||
@@ -1378,9 +1359,7 @@ public abstract class StackValue {
|
||||
default:
|
||||
PrimitiveType primitiveType = AsmUtil.asmPrimitiveTypeToLangPrimitiveType(type);
|
||||
if (primitiveType == null) throw new UnsupportedOperationException();
|
||||
|
||||
String typeName = primitiveType.getTypeName().getIdentifier();
|
||||
return Type.getObjectType(REF_TYPE_PREFIX + typeName + "Ref");
|
||||
return sharedTypeForPrimitive(primitiveType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeAsSequence
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
import java.util.*
|
||||
|
||||
class BridgeForBuiltinSpecial<out Signature : Any>(
|
||||
@@ -76,7 +75,7 @@ object BuiltinSpecialBridgesUtil {
|
||||
else null
|
||||
|
||||
val commonBridges = reachableDeclarations.mapTo(LinkedHashSet<Signature>(), signatureByDescriptor)
|
||||
commonBridges.removeAll(specialBridgesSignaturesInSuperClass + specialBridge?.from.singletonOrEmptyList())
|
||||
commonBridges.removeAll(specialBridgesSignaturesInSuperClass + listOfNotNull(specialBridge?.from))
|
||||
|
||||
if (fake) {
|
||||
for (overridden in function.overriddenDescriptors.map { it.original }) {
|
||||
|
||||
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
@@ -289,8 +288,8 @@ class CoroutineCodegen private constructor(
|
||||
}
|
||||
|
||||
private fun allFunctionParameters() =
|
||||
originalSuspendFunctionDescriptor.extensionReceiverParameter.singletonOrEmptyList() +
|
||||
originalSuspendFunctionDescriptor.valueParameters.orEmpty()
|
||||
listOfNotNull(originalSuspendFunctionDescriptor.extensionReceiverParameter) +
|
||||
originalSuspendFunctionDescriptor.valueParameters.orEmpty()
|
||||
|
||||
private fun ParameterDescriptor.getFieldInfoForCoroutineLambdaParameter() =
|
||||
createHiddenFieldInfo(type, COROUTINE_LAMBDA_PARAMETER_PREFIX + (this.safeAs<ValueParameterDescriptor>()?.index ?: ""))
|
||||
|
||||
@@ -294,7 +294,7 @@ class CoroutineTransformerMethodVisitor(
|
||||
get() {
|
||||
assert(suspensionCallEnd.next is LabelNode) {
|
||||
"Next instruction after ${this} should be a label, but " +
|
||||
"${suspensionCallEnd.next.javaClass}/${suspensionCallEnd.next.opcode} was found"
|
||||
"${suspensionCallEnd.next::class.java}/${suspensionCallEnd.next.opcode} was found"
|
||||
}
|
||||
|
||||
return suspensionCallEnd.next as LabelNode
|
||||
@@ -421,7 +421,7 @@ private fun InstructionAdapter.generateResumeWithExceptionCheck() {
|
||||
|
||||
private fun Type.fieldNameForVar(index: Int) = descriptor.first() + "$" + index
|
||||
|
||||
private fun withInstructionAdapter(block: InstructionAdapter.() -> Unit): InsnList {
|
||||
inline fun withInstructionAdapter(block: InstructionAdapter.() -> Unit): InsnList {
|
||||
val tmpMethodNode = MethodNode()
|
||||
|
||||
InstructionAdapter(tmpMethodNode).apply(block)
|
||||
|
||||
@@ -217,7 +217,7 @@ private class VarExpectedTypeFrame(maxLocals: Int) : VarFrame<VarExpectedTypeFra
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
if (other == null || other::class.java != this::class.java) return false
|
||||
|
||||
other as VarExpectedTypeFrame
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
||||
createClassReader().accept(new ClassVisitor(InlineCodegenUtil.API, classBuilder.getVisitor()) {
|
||||
@Override
|
||||
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
|
||||
InlineCodegenUtil.assertVersionNotGreaterThanGeneratedOne(version, name, inliningContext.state);
|
||||
classBuilder.defineClass(null, version, access, name, signature, superName, interfaces);
|
||||
if(CoroutineCodegenUtilKt.COROUTINE_IMPL_ASM_TYPE.getInternalName().equals(superName)) {
|
||||
inliningContext.setContinuation(true);
|
||||
@@ -271,7 +270,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
||||
//TODO for inline method make public class
|
||||
transformationInfo.setNewConstructorDescriptor(constructorDescriptor);
|
||||
MethodVisitor constructorVisitor = classBuilder.newMethod(
|
||||
NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY
|
||||
NO_ORIGIN, constructor.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY
|
||||
);
|
||||
|
||||
final Label newBodyStartLabel = new Label();
|
||||
@@ -312,7 +311,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
||||
}
|
||||
|
||||
MethodNode intermediateMethodNode =
|
||||
new MethodNode(AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
new MethodNode(constructor.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor, constructorInlineBuilder, true);
|
||||
InlineCodegenUtil.removeFinallyMarkers(intermediateMethodNode);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
@@ -202,7 +203,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
MethodNode node = nodeAndSmap != null ? nodeAndSmap.getNode() : null;
|
||||
throw new CompilationException(
|
||||
"Couldn't inline method call '" + functionDescriptor.getName() + "' into\n" +
|
||||
contextDescriptor + "\n" +
|
||||
DescriptorRenderer.DEBUG_TEXT.render(contextDescriptor) + "\n" +
|
||||
(element != null ? element.getText() : "<no source>") +
|
||||
(generateNodeText ? ("\nCause: " + InlineCodegenUtil.getNodeText(node)) : ""),
|
||||
e, callElement
|
||||
@@ -321,7 +322,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
}
|
||||
});
|
||||
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), classId, state);
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), classId);
|
||||
}
|
||||
|
||||
assert callableDescriptor instanceof DeserializedCallableMemberDescriptor : "Not a deserialized function or proper: " + callableDescriptor;
|
||||
@@ -348,7 +349,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
});
|
||||
|
||||
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId, state);
|
||||
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder;
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||
import org.jetbrains.org.objectweb.asm.util.Printer;
|
||||
import org.jetbrains.org.objectweb.asm.util.Textifier;
|
||||
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
|
||||
|
||||
@@ -92,8 +93,7 @@ public class InlineCodegenUtil {
|
||||
byte[] classData,
|
||||
final String methodName,
|
||||
final String methodDescriptor,
|
||||
ClassId classId,
|
||||
final @NotNull GenerationState state
|
||||
ClassId classId
|
||||
) {
|
||||
ClassReader cr = new ClassReader(classData);
|
||||
final MethodNode[] node = new MethodNode[1];
|
||||
@@ -103,10 +103,6 @@ public class InlineCodegenUtil {
|
||||
lines[1] = Integer.MIN_VALUE;
|
||||
//noinspection PointlessBitwiseExpression
|
||||
cr.accept(new ClassVisitor(API) {
|
||||
@Override
|
||||
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
|
||||
assertVersionNotGreaterThanGeneratedOne(version, name, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
@@ -151,16 +147,6 @@ public class InlineCodegenUtil {
|
||||
return new SMAPAndMethodNode(node[0], smap);
|
||||
}
|
||||
|
||||
public static void assertVersionNotGreaterThanGeneratedOne(int version, String internalName, @NotNull GenerationState state) {
|
||||
// TODO: report a proper diagnostic
|
||||
if (version > state.getClassFileVersion() && !"true".equals(System.getProperty("kotlin.skip.bytecode.version.check"))) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot inline bytecode of class " + internalName + " which has version " + version + ". " +
|
||||
"This compiler can only inline Java 1.6 bytecode (version " + Opcodes.V1_6 + ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initDefaultSourceMappingIfNeeded(
|
||||
@NotNull CodegenContext context, @NotNull MemberCodegen codegen, @NotNull GenerationState state
|
||||
) {
|
||||
@@ -179,7 +165,7 @@ public class InlineCodegenUtil {
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile findVirtualFile(@NotNull GenerationState state, @NotNull ClassId classId) {
|
||||
return JvmVirtualFileFinder.SERVICE.getInstance(state.getProject()).findVirtualFileWithHeader(classId);
|
||||
return VirtualFileFinder.SERVICE.getInstance(state.getProject()).findVirtualFileWithHeader(classId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -405,6 +391,11 @@ public class InlineCodegenUtil {
|
||||
return sw.toString().trim();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getInsnOpcodeText(@Nullable AbstractInsnNode node) {
|
||||
return node == null ? "null" : Printer.OPCODES[node.getOpcode()];
|
||||
}
|
||||
|
||||
@NotNull
|
||||
/* package */ static ClassReader buildClassReaderByInternalName(@NotNull GenerationState state, @NotNull String internalName) {
|
||||
//try to find just compiled classes then in dependencies
|
||||
|
||||
@@ -252,6 +252,7 @@ public class MethodInliner {
|
||||
//TODO add skipped this and receiver
|
||||
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info, invokeCall.finallyDepthShift);
|
||||
result.mergeWithNotChangeInfo(lambdaResult);
|
||||
result.getReifiedTypeParametersUsages().mergeAll(lambdaResult.getReifiedTypeParametersUsages());
|
||||
|
||||
//return value boxing/unboxing
|
||||
Method bridge = typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor()));
|
||||
@@ -486,6 +487,11 @@ public class MethodInliner {
|
||||
);
|
||||
awaitClassReification = false;
|
||||
}
|
||||
else if (inliningContext.isInliningLambda && ReifiedTypeInliner.Companion.isOperationReifiedMarker(cur)) {
|
||||
ReificationArgument reificationArgument = ReifiedTypeInlinerKt.getReificationArgument((MethodInsnNode) cur);
|
||||
String parameterName = reificationArgument.getParameterName();
|
||||
result.getReifiedTypeParametersUsages().addUsedReifiedParameter(parameterName);
|
||||
}
|
||||
}
|
||||
else if (cur.getOpcode() == Opcodes.GETSTATIC) {
|
||||
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
|
||||
|
||||
@@ -66,7 +66,6 @@ class WhenMappingTransformer(
|
||||
val fieldNode = transformationInfo.fieldNode
|
||||
classReader.accept(object : ClassVisitor(InlineCodegenUtil.API, classBuilder.visitor) {
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
|
||||
InlineCodegenUtil.assertVersionNotGreaterThanGeneratedOne(version, name, state)
|
||||
classBuilder.defineClass(null, version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,5 @@ internal class Parameters(val parameters: List<ParameterInfo>) : Iterable<Parame
|
||||
}
|
||||
|
||||
val capturedTypes: List<Type>
|
||||
get() = captured.map {
|
||||
it.getType()
|
||||
}
|
||||
get() = captured.map(CapturedParamInfo::getType)
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
|
||||
const val REIFIED_OPERATION_MARKER_METHOD_NAME = "reifiedOperationMarker"
|
||||
const val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME = "needClassReification"
|
||||
|
||||
private fun isOperationReifiedMarker(insn: AbstractInsnNode) =
|
||||
fun isOperationReifiedMarker(insn: AbstractInsnNode) =
|
||||
isReifiedMarker(insn) { it == REIFIED_OPERATION_MARKER_METHOD_NAME }
|
||||
|
||||
private fun isReifiedMarker(insn: AbstractInsnNode, namePredicate: (String) -> Boolean): Boolean {
|
||||
@@ -247,7 +247,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
|
||||
}
|
||||
}
|
||||
|
||||
private val MethodInsnNode.reificationArgument: ReificationArgument?
|
||||
val MethodInsnNode.reificationArgument: ReificationArgument?
|
||||
get() {
|
||||
val prev = previous!!
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class SMAPBuilder(
|
||||
private fun generateDebugStrata(realMappings: List<FileMapping>): String {
|
||||
val combinedMapping = FileMapping(source, path)
|
||||
realMappings.forEach { fileMapping ->
|
||||
fileMapping.lineMappings.filter { it.callSiteMarker != null }.forEach { (source, dest, range, callSiteMarker) ->
|
||||
fileMapping.lineMappings.filter { it.callSiteMarker != null }.forEach { (_, dest, range, callSiteMarker) ->
|
||||
combinedMapping.addRangeMapping(RangeMapping(
|
||||
callSiteMarker!!.lineNumber, dest, range
|
||||
))
|
||||
|
||||
@@ -28,7 +28,7 @@ internal val classId: ClassId =
|
||||
ClassId.topLevel(FqName("org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt"))
|
||||
|
||||
internal val bytecode: ByteArray by lazy {
|
||||
val stream = object {}.javaClass.classLoader.getResourceAsStream("${classId.asString()}.class")
|
||||
val stream = object {}::class.java.classLoader.getResourceAsStream("${classId.asString()}.class")
|
||||
stream.readBytes().apply {
|
||||
stream.close()
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ object JavaClassProperty : IntrinsicPropertyGetter() {
|
||||
}
|
||||
|
||||
override fun toCallable(fd: FunctionDescriptor, isSuper: Boolean, resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): Callable {
|
||||
val classType = codegen.getState().typeMapper.mapType(resolvedCall.call.dispatchReceiver!!.type)
|
||||
val classType = codegen.state.typeMapper.mapType(resolvedCall.call.dispatchReceiver!!.type)
|
||||
return object : IntrinsicCallable(getType(Class::class.java), listOf(), classType, null) {
|
||||
override fun invokeIntrinsic(v: InstructionAdapter) {
|
||||
if (isPrimitive(classType)) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
class NewArray : IntrinsicMethod() {
|
||||
override fun toCallable(fd: FunctionDescriptor, isSuper: Boolean, resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): Callable {
|
||||
val jetType = resolvedCall.resultingDescriptor.returnType!!
|
||||
val type = codegen.getState().typeMapper.mapType(jetType)
|
||||
val type = codegen.state.typeMapper.mapType(jetType)
|
||||
return object : IntrinsicCallable(type, listOf(Type.INT_TYPE), null, null) {
|
||||
override fun invokeIntrinsic(v: InstructionAdapter) {
|
||||
codegen.newArrayInstruction(jetType)
|
||||
|
||||
@@ -29,9 +29,13 @@ class DeadCodeEliminationMethodTransformer : MethodTransformer() {
|
||||
}
|
||||
|
||||
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 removedNodes = HashSet<AbstractInsnNode>()
|
||||
|
||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
||||
val insnList = methodNode.instructions
|
||||
val insnsArray = insnList.toArray()
|
||||
|
||||
@@ -51,6 +55,7 @@ class DeadCodeEliminationMethodTransformer : MethodTransformer() {
|
||||
}
|
||||
|
||||
class Result(val removedNodes: Set<AbstractInsnNode>) {
|
||||
fun hasRemovedAnything() = removedNodes.isNotEmpty()
|
||||
fun isRemoved(node: AbstractInsnNode) = removedNodes.contains(node)
|
||||
fun isAlive(node: AbstractInsnNode) = !isRemoved(node)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor;
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantBoxingMethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantCoercionToUnitTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.RedundantNullCheckMethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.captured.CapturedVarsOptimizationMethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.RedundantNullCheckV2MethodTransformer;
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
@@ -33,7 +34,9 @@ public class OptimizationMethodVisitor extends TransformationMethodVisitor {
|
||||
private static final MethodTransformer MANDATORY_METHOD_TRANSFORMER = new FixStackWithLabelNormalizationMethodTransformer();
|
||||
|
||||
private static final MethodTransformer[] OPTIMIZATION_TRANSFORMERS = new MethodTransformer[] {
|
||||
new RedundantNullCheckMethodTransformer(),
|
||||
new CapturedVarsOptimizationMethodTransformer(),
|
||||
new RedundantNullCheckV2MethodTransformer(),
|
||||
new RedundantCheckCastEliminationMethodTransformer(),
|
||||
new RedundantBoxingMethodTransformer(),
|
||||
new RedundantCoercionToUnitTransformer(),
|
||||
new DeadCodeEliminationMethodTransformer(),
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.popReferenceValueBefore
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
val insns = methodNode.instructions.toArray()
|
||||
if (!insns.any { it.opcode == Opcodes.CHECKCAST }) return
|
||||
|
||||
val redundantCheckCasts = ArrayList<TypeInsnNode>()
|
||||
|
||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
||||
for (i in insns.indices) {
|
||||
val valueType = frames[i]?.top()?.type ?: continue
|
||||
val insn = insns[i]
|
||||
if (ReifiedTypeInliner.isOperationReifiedMarker(insn.previous)) continue
|
||||
|
||||
if (insn is TypeInsnNode) {
|
||||
val insnType = Type.getObjectType(insn.desc)
|
||||
if (!isTrivialSubtype(insnType, valueType)) continue
|
||||
|
||||
if (insn.opcode == Opcodes.CHECKCAST) {
|
||||
redundantCheckCasts.add(insn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redundantCheckCasts.forEach {
|
||||
methodNode.instructions.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isTrivialSubtype(superType: Type, subType: Type) =
|
||||
superType == subType
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.util.Pair
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import java.util.*
|
||||
@@ -66,7 +65,7 @@ class BoxedValueDescriptor(
|
||||
var isSafeToRemove = true; private set
|
||||
val unboxedType: Type = getUnboxedType(boxedType)
|
||||
|
||||
fun getAssociatedInsns() = associatedInsns.toReadOnlyList()
|
||||
fun getAssociatedInsns() = associatedInsns.toList()
|
||||
|
||||
fun addInsn(insnNode: AbstractInsnNode) {
|
||||
associatedInsns.add(insnNode)
|
||||
|
||||
@@ -35,14 +35,12 @@ import java.util.*
|
||||
open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() {
|
||||
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
|
||||
|
||||
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue {
|
||||
val index = insnList.indexOf(insn)
|
||||
return boxingPlaces.getOrPut(index) {
|
||||
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
|
||||
onNewBoxedValue(boxedBasicValue)
|
||||
boxedBasicValue
|
||||
}
|
||||
}
|
||||
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue =
|
||||
boxingPlaces.getOrPut(insnList.indexOf(insn)) {
|
||||
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
|
||||
onNewBoxedValue(boxedBasicValue)
|
||||
boxedBasicValue
|
||||
}
|
||||
|
||||
protected fun checkUsedValue(value: BasicValue) {
|
||||
if (value is TaintedBoxedValue) {
|
||||
@@ -66,14 +64,17 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
||||
onUnboxing(insn, firstArg, value.type)
|
||||
value
|
||||
}
|
||||
insn.isIteratorMethodCallOfProgression(values) -> {
|
||||
ProgressionIteratorBasicValue(getValuesTypeOfProgressionClass(firstArg.type.internalName))
|
||||
}
|
||||
insn.isIteratorMethodCallOfProgression(values) ->
|
||||
ProgressionIteratorBasicValue.byProgressionClassType(firstArg.type)
|
||||
insn.isNextMethodCallOfProgressionIterator(values) -> {
|
||||
val progressionIterator = firstArg as? ProgressionIteratorBasicValue
|
||||
?: throw AssertionError("firstArg should be progression iterator")
|
||||
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
|
||||
}
|
||||
insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values) -> {
|
||||
onAreEqual(insn, values[0] as BoxedBasicValue, values[1] as BoxedBasicValue)
|
||||
value
|
||||
}
|
||||
else -> {
|
||||
// N-ary operation should be a method call or multinewarray.
|
||||
// Arguments for multinewarray could be only numeric,
|
||||
@@ -100,7 +101,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
||||
protected open fun isExactValue(value: BasicValue) =
|
||||
value is ProgressionIteratorBasicValue ||
|
||||
value is CleanBoxedValue ||
|
||||
value.type != null && isProgressionClass(value.type.internalName)
|
||||
value.type != null && isProgressionClass(value.type)
|
||||
|
||||
override fun merge(v: BasicValue, w: BasicValue) =
|
||||
when {
|
||||
@@ -125,6 +126,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
||||
|
||||
protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
|
||||
protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {}
|
||||
protected open fun onAreEqual(insn: AbstractInsnNode, value1: BoxedBasicValue, value2: BoxedBasicValue) {}
|
||||
protected open fun onMethodCallWithBoxedValue(value: BoxedBasicValue) {}
|
||||
protected open fun onMergeFail(value: BoxedBasicValue) {}
|
||||
protected open fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) {}
|
||||
@@ -190,23 +192,44 @@ private fun AbstractInsnNode.isJavaLangClassBoxing() =
|
||||
desc == JLCLASS_TO_KCLASS
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
||||
values[0] is ProgressionIteratorBasicValue &&
|
||||
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
||||
values.firstOrNull() is ProgressionIteratorBasicValue &&
|
||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||
name == "next"
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
|
||||
fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
|
||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||
val firstArgType = values[0].type
|
||||
val firstArgType = values.firstOrNull()?.type
|
||||
firstArgType != null &&
|
||||
isProgressionClass(firstArgType.internalName) &&
|
||||
isProgressionClass(firstArgType) &&
|
||||
name == "iterator"
|
||||
}
|
||||
|
||||
private fun isProgressionClass(internalClassName: String) =
|
||||
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName))
|
||||
fun isProgressionClass(type: Type) =
|
||||
RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(type.internalName))
|
||||
|
||||
private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) =
|
||||
RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))
|
||||
?.typeName?.asString() ?: error("type should be not null")
|
||||
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||
isAreEqualIntrinsic() && run {
|
||||
if (values.size != 2) return false
|
||||
|
||||
val (v1, v2) = values
|
||||
if (v1 !is BoxedBasicValue || v2 !is BoxedBasicValue) return false
|
||||
|
||||
val d1 = v1.descriptor
|
||||
val d2 = v2.descriptor
|
||||
d1.unboxedType == d2.unboxedType
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isAreEqualIntrinsic() =
|
||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
name == "areEqual" &&
|
||||
owner == "kotlin/jvm/internal/Intrinsics" &&
|
||||
desc == "(Ljava/lang/Object;Ljava/lang/Object;)Z"
|
||||
}
|
||||
|
||||
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean =
|
||||
!values.any {
|
||||
val unboxedType = getUnboxedType(it.type)
|
||||
unboxedType == Type.DOUBLE_TYPE || unboxedType == Type.FLOAT_TYPE
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnList
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
class NullabilityInterpreter(insns: InsnList) : BoxingInterpreter(insns) {
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue) = makeNotNullIfNeeded(insn, super.unaryOperation(insn, value))
|
||||
|
||||
override fun newOperation(insn: AbstractInsnNode) = makeNotNullIfNeeded(insn, super.newOperation(insn))
|
||||
|
||||
override fun isExactValue(value: BasicValue) = super.isExactValue(value) || value is NotNullBasicValue
|
||||
|
||||
override fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?) =
|
||||
NotNullBasicValue(type)
|
||||
}
|
||||
|
||||
private fun makeNotNullIfNeeded(insn: AbstractInsnNode, value: BasicValue?): BasicValue? =
|
||||
when (insn.opcode) {
|
||||
Opcodes.ANEWARRAY, Opcodes.NEWARRAY, Opcodes.LDC, Opcodes.NEW ->
|
||||
if (value?.type?.sort == Type.OBJECT || value?.type?.sort == Type.ARRAY)
|
||||
NotNullBasicValue(value.type)
|
||||
else
|
||||
value
|
||||
|
||||
else -> value
|
||||
}
|
||||
|
||||
class NotNullBasicValue(type: Type?) : StrictBasicValue(type) {
|
||||
override fun equals(other: Any?): Boolean = other is NotNullBasicValue
|
||||
// We do not differ not-nullable values, so we should always return the same hashCode
|
||||
// Actually it doesn't really matter because analyzer is not supposed to store values in hashtables
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
@@ -18,17 +18,18 @@ package org.jetbrains.kotlin.codegen.optimization.boxing;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType;
|
||||
import org.jetbrains.kotlin.codegen.RangeCodegenUtil;
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IteratorNext;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||
private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE;
|
||||
|
||||
static {
|
||||
ImmutableMap.Builder<String, Type> builder = ImmutableMap.builder();
|
||||
for (PrimitiveType primitiveType : RangeCodegenUtil.supportedRangeTypes()) {
|
||||
@@ -37,6 +38,15 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||
VALUES_TYPENAME_TO_TYPE = builder.build();
|
||||
}
|
||||
|
||||
private static final ImmutableMap<PrimitiveType, ProgressionIteratorBasicValue> ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE;
|
||||
static {
|
||||
ImmutableMap.Builder<PrimitiveType, ProgressionIteratorBasicValue> builder = ImmutableMap.builder();
|
||||
for (PrimitiveType elementType : RangeCodegenUtil.supportedRangeTypes()) {
|
||||
builder.put(elementType, new ProgressionIteratorBasicValue(elementType.getTypeName().asString()));
|
||||
}
|
||||
ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE = builder.build();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Type getValuesType(@NotNull String valuesTypeName) {
|
||||
Type type = VALUES_TYPENAME_TO_TYPE.get(valuesTypeName);
|
||||
@@ -47,12 +57,20 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
|
||||
private final Type valuesPrimitiveType;
|
||||
private final String valuesPrimitiveTypeName;
|
||||
|
||||
public ProgressionIteratorBasicValue(@NotNull String valuesPrimitiveTypeName) {
|
||||
private ProgressionIteratorBasicValue(@NotNull String valuesPrimitiveTypeName) {
|
||||
super(IteratorNext.Companion.getPrimitiveIteratorType(Name.identifier(valuesPrimitiveTypeName)));
|
||||
this.valuesPrimitiveType = getValuesType(valuesPrimitiveTypeName);
|
||||
this.valuesPrimitiveTypeName = valuesPrimitiveTypeName;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static ProgressionIteratorBasicValue byProgressionClassType(@NotNull Type progressionClassType) {
|
||||
FqName classFqName = new FqName(progressionClassType.getClassName());
|
||||
PrimitiveType elementType = RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(classFqName);
|
||||
return ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE.get(elementType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Type getValuesPrimitiveType() {
|
||||
return valuesPrimitiveType;
|
||||
|
||||
@@ -84,6 +84,13 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAreEqual(insn: AbstractInsnNode, value1: BoxedBasicValue, value2: BoxedBasicValue) {
|
||||
val descriptor1 = value1.descriptor
|
||||
val descriptor2 = value2.descriptor
|
||||
candidatesBoxedValues.merge(descriptor1, descriptor2)
|
||||
descriptor1.addInsn(insn)
|
||||
}
|
||||
|
||||
override fun onMethodCallWithBoxedValue(value: BoxedBasicValue) {
|
||||
markValueAsDirty(value)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue;
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
@@ -48,7 +50,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
|
||||
adaptLocalVariableTableForBoxedValues(node, frames);
|
||||
|
||||
applyVariablesRemapping(node, buildVariablesRemapping(valuesToOptimize, node));
|
||||
UtilKt.remapLocalVariables(node, buildVariablesRemapping(valuesToOptimize, node));
|
||||
|
||||
adaptInstructionsForBoxedValues(node, valuesToOptimize);
|
||||
}
|
||||
@@ -221,21 +223,6 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
return remapping;
|
||||
}
|
||||
|
||||
private static void applyVariablesRemapping(@NotNull MethodNode node, @NotNull int[] remapping) {
|
||||
for (AbstractInsnNode insn : node.instructions.toArray()) {
|
||||
if (insn instanceof VarInsnNode) {
|
||||
((VarInsnNode) insn).var = remapping[((VarInsnNode) insn).var];
|
||||
}
|
||||
if (insn instanceof IincInsnNode) {
|
||||
((IincInsnNode) insn).var = remapping[((IincInsnNode) insn).var];
|
||||
}
|
||||
}
|
||||
|
||||
for (LocalVariableNode localVariableNode : node.localVariables) {
|
||||
localVariableNode.index = remapping[localVariableNode.index];
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptInstructionsForBoxedValues(
|
||||
@NotNull MethodNode node,
|
||||
@NotNull RedundantBoxedValuesCollection values
|
||||
@@ -341,9 +328,68 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
|
||||
);
|
||||
node.instructions.set(insn, new InsnNode(Opcodes.ICONST_1));
|
||||
break;
|
||||
case Opcodes.INVOKESTATIC:
|
||||
if (BoxingInterpreterKt.isAreEqualIntrinsic(insn)) {
|
||||
adaptAreEqualIntrinsic(node, insn, value);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// fall-through to default
|
||||
}
|
||||
default:
|
||||
// CHECKCAST or unboxing-method call
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsic(@NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedValueDescriptor value) {
|
||||
Type unboxedType = value.getUnboxedType();
|
||||
|
||||
switch (unboxedType.getSort()) {
|
||||
case Type.BOOLEAN:
|
||||
case Type.BYTE:
|
||||
case Type.SHORT:
|
||||
case Type.INT:
|
||||
case Type.CHAR:
|
||||
adaptAreEqualIntrinsicForInt(node, insn);
|
||||
break;
|
||||
case Type.LONG:
|
||||
adaptAreEqualIntrinsicForLong(node, insn);
|
||||
break;
|
||||
case Type.OBJECT:
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unexpected unboxed type kind: " + unboxedType);
|
||||
}
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsicForInt(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
LabelNode lNotEqual = new LabelNode(new Label());
|
||||
LabelNode lDone = new LabelNode(new Label());
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.IF_ICMPNE, lNotEqual));
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_1));
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.GOTO, lDone));
|
||||
node.instructions.insertBefore(insn, lNotEqual);
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_0));
|
||||
node.instructions.insertBefore(insn, lDone);
|
||||
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
|
||||
private static void adaptAreEqualIntrinsicForLong(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.LCMP));
|
||||
ifEqual1Else0(node, insn);
|
||||
node.instructions.remove(insn);
|
||||
}
|
||||
|
||||
private static void ifEqual1Else0(@NotNull MethodNode node, @NotNull AbstractInsnNode insn) {
|
||||
LabelNode lNotEqual = new LabelNode(new Label());
|
||||
LabelNode lDone = new LabelNode(new Label());
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.IFNE, lNotEqual));
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_1));
|
||||
node.instructions.insertBefore(insn, new JumpInsnNode(Opcodes.GOTO, lDone));
|
||||
node.instructions.insertBefore(insn, lNotEqual);
|
||||
node.instructions.insertBefore(insn, new InsnNode(Opcodes.ICONST_0));
|
||||
node.instructions.insertBefore(insn, lDone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
||||
private val frames by lazy { analyzeMethodBody() }
|
||||
|
||||
fun transform() {
|
||||
if (!insns.any { it.isUnitOrNull() }) return
|
||||
if (!insns.any { it.isUnitInstanceOrNull() }) return
|
||||
|
||||
computeTransformations()
|
||||
for ((insn, transformation) in transformations.entries) {
|
||||
@@ -158,7 +158,7 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
||||
transformations[insn] = replaceWithPopTransformation(boxedValueSize)
|
||||
}
|
||||
|
||||
insn.isUnitOrNull() -> {
|
||||
insn.isUnitInstanceOrNull() -> {
|
||||
transformations[insn] = replaceWithNopTransformation()
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
||||
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
|
||||
|
||||
private fun isTransformablePopOperand(insn: AbstractInsnNode) =
|
||||
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isUnitOrNull()
|
||||
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isUnitInstanceOrNull()
|
||||
|
||||
private fun isDontTouch(insn: AbstractInsnNode) =
|
||||
dontTouchInsnIndices[insnList.indexOf(insn)]
|
||||
@@ -240,6 +240,9 @@ class RedundantCoercionToUnitTransformer : MethodTransformer() {
|
||||
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isUnitOrNull() =
|
||||
opcode == Opcodes.ACONST_NULL ||
|
||||
opcode == Opcodes.GETSTATIC && this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
||||
fun AbstractInsnNode.isUnitInstanceOrNull() =
|
||||
opcode == Opcodes.ACONST_NULL || isUnitInstance()
|
||||
|
||||
fun AbstractInsnNode.isUnitInstance() =
|
||||
opcode == Opcodes.GETSTATIC &&
|
||||
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.boxing;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.analysis.BasicValue;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RedundantNullCheckMethodTransformer extends MethodTransformer {
|
||||
|
||||
@Override
|
||||
public void transform(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
|
||||
while (removeRedundantNullCheckPass(internalClassName, methodNode)) {
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean removeRedundantNullCheckPass(@NotNull String internalClassName, @NotNull MethodNode methodNode) {
|
||||
InsnList insnList = methodNode.instructions;
|
||||
Frame<BasicValue>[] frames = analyze(
|
||||
internalClassName, methodNode,
|
||||
new NullabilityInterpreter(insnList)
|
||||
);
|
||||
|
||||
List<AbstractInsnNode> insnsToOptimize = new ArrayList<AbstractInsnNode>();
|
||||
|
||||
for (int i = 0; i < insnList.size(); i++) {
|
||||
Frame<BasicValue> frame = frames[i];
|
||||
AbstractInsnNode insn = insnList.get(i);
|
||||
|
||||
if ((insn.getOpcode() == Opcodes.IFNULL || insn.getOpcode() == Opcodes.IFNONNULL) &&
|
||||
frame != null && frame.getStack(frame.getStackSize() - 1) instanceof NotNullBasicValue) {
|
||||
insnsToOptimize.add(insn);
|
||||
}
|
||||
}
|
||||
|
||||
for (AbstractInsnNode insn : insnsToOptimize) {
|
||||
if (insn.getPrevious() != null && insn.getPrevious().getOpcode() == Opcodes.DUP) {
|
||||
insnList.remove(insn.getPrevious());
|
||||
}
|
||||
else {
|
||||
insnList.insertBefore(insn, new InsnNode(Opcodes.POP));
|
||||
}
|
||||
|
||||
assert insn.getOpcode() == Opcodes.IFNULL
|
||||
|| insn.getOpcode() == Opcodes.IFNONNULL : "only IFNULL/IFNONNULL are supported";
|
||||
|
||||
if (insn.getOpcode() == Opcodes.IFNULL) {
|
||||
insnList.remove(insn);
|
||||
}
|
||||
else {
|
||||
insnList.set(
|
||||
insn,
|
||||
new JumpInsnNode(Opcodes.GOTO, ((JumpInsnNode) insn).label)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return insnsToOptimize.size() > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.captured
|
||||
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
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.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
|
||||
class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
Transformer(internalClassName, methodNode).run()
|
||||
}
|
||||
|
||||
// Tracks proper usages of objects corresponding to captured variables.
|
||||
//
|
||||
// The 'kotlin.jvm.internal.Ref.*' instance can be replaced with a local variable,
|
||||
// if all of the following conditions are satisfied:
|
||||
// * It is created inside a current method.
|
||||
// * The only permitted operations on it are:
|
||||
// - store to a local variable
|
||||
// - ALOAD, ASTORE
|
||||
// - DUP, POP
|
||||
// - GETFIELD <owner>.element, PUTFIELD <owner>.element
|
||||
// * There's a corresponding local variable definition,
|
||||
// and all ALOAD/ASTORE instructions operate on that particular local variable.
|
||||
// * Its 'element' field is initialized at start of local variable visibility range.
|
||||
//
|
||||
// Note that for code that doesn't create Ref objects explicitly these conditions are true,
|
||||
// unless the Ref object escapes to a local class constructor (including local classes for lambdas).
|
||||
//
|
||||
private class CapturedVarDescriptor(val newInsn: TypeInsnNode, val refType: Type, val valueType: Type) : ReferenceValueDescriptor {
|
||||
var hazard = false
|
||||
|
||||
var initCallInsn: MethodInsnNode? = null
|
||||
var localVar: LocalVariableNode? = null
|
||||
var localVarIndex = -1
|
||||
val astoreInsns: MutableCollection<VarInsnNode> = LinkedHashSet()
|
||||
val aloadInsns: MutableCollection<VarInsnNode> = LinkedHashSet()
|
||||
val stackInsns: MutableCollection<AbstractInsnNode> = LinkedHashSet()
|
||||
val getFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
||||
val putFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
||||
|
||||
fun canRewrite(): Boolean =
|
||||
!hazard &&
|
||||
initCallInsn != null &&
|
||||
localVar != null &&
|
||||
localVarIndex >= 0
|
||||
|
||||
override fun onUseAsTainted() {
|
||||
hazard = true
|
||||
}
|
||||
}
|
||||
|
||||
private class Transformer(private val internalClassName: String, private val methodNode: MethodNode) {
|
||||
private val refValues = ArrayList<CapturedVarDescriptor>()
|
||||
private val refValuesByNewInsn = LinkedHashMap<TypeInsnNode, CapturedVarDescriptor>()
|
||||
private val insns = methodNode.instructions.toArray()
|
||||
private lateinit var frames: Array<out Frame<BasicValue>?>
|
||||
|
||||
val hasRewritableRefValues: Boolean
|
||||
get() = refValues.isNotEmpty()
|
||||
|
||||
fun run() {
|
||||
createRefValues()
|
||||
if (!hasRewritableRefValues) return
|
||||
|
||||
analyze()
|
||||
if (!hasRewritableRefValues) return
|
||||
|
||||
rewrite()
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.getIndex() = methodNode.instructions.indexOf(this)
|
||||
|
||||
private fun createRefValues() {
|
||||
for (insn in insns) {
|
||||
if (insn.opcode == Opcodes.NEW && insn is TypeInsnNode) {
|
||||
val type = Type.getObjectType(insn.desc)
|
||||
if (AsmTypes.isSharedVarType(type)) {
|
||||
val valueType = REF_TYPE_TO_ELEMENT_TYPE[type.internalName] ?: continue
|
||||
val refValue = CapturedVarDescriptor(insn, type, valueType)
|
||||
refValues.add(refValue)
|
||||
refValuesByNewInsn[insn] = refValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class Interpreter : ReferenceTrackingInterpreter() {
|
||||
override fun newOperation(insn: AbstractInsnNode): BasicValue =
|
||||
refValuesByNewInsn[insn]?.let { descriptor ->
|
||||
ProperTrackedReferenceValue(descriptor.refType, descriptor)
|
||||
}
|
||||
?: super.newOperation(insn)
|
||||
|
||||
override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) {
|
||||
for (descriptor in value.descriptors) {
|
||||
if (descriptor !is CapturedVarDescriptor) throw AssertionError("Unexpected descriptor: $descriptor")
|
||||
when {
|
||||
insn.opcode == Opcodes.ALOAD ->
|
||||
descriptor.aloadInsns.add(insn as VarInsnNode)
|
||||
insn.opcode == Opcodes.ASTORE ->
|
||||
descriptor.astoreInsns.add(insn as VarInsnNode)
|
||||
insn.opcode == Opcodes.GETFIELD && insn is FieldInsnNode && insn.name == REF_ELEMENT_FIELD && position == 0 ->
|
||||
descriptor.getFieldInsns.add(insn)
|
||||
insn.opcode == Opcodes.PUTFIELD && insn is FieldInsnNode && insn.name == REF_ELEMENT_FIELD && position == 0 ->
|
||||
descriptor.putFieldInsns.add(insn)
|
||||
insn.opcode == Opcodes.INVOKESPECIAL && insn is MethodInsnNode && insn.name == INIT_METHOD_NAME && position == 0 ->
|
||||
if (descriptor.initCallInsn != null && descriptor.initCallInsn != insn)
|
||||
descriptor.hazard = true
|
||||
else
|
||||
descriptor.initCallInsn = insn
|
||||
insn.opcode == Opcodes.DUP ->
|
||||
descriptor.stackInsns.add(insn)
|
||||
else ->
|
||||
descriptor.hazard = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun analyze() {
|
||||
frames = MethodTransformer.analyze(internalClassName, methodNode, Interpreter())
|
||||
trackPops()
|
||||
assignLocalVars()
|
||||
|
||||
refValues.removeAll { !it.canRewrite() }
|
||||
}
|
||||
|
||||
private fun trackPops() {
|
||||
for (i in insns.indices) {
|
||||
val frame = frames[i] ?: continue
|
||||
val insn = insns[i]
|
||||
|
||||
when (insn.opcode) {
|
||||
Opcodes.POP -> {
|
||||
frame.top()?.getCapturedVarOrNull()?.run { stackInsns.add(insn) }
|
||||
}
|
||||
Opcodes.POP2 -> {
|
||||
val top = frame.top()
|
||||
if (top?.size == 1) {
|
||||
top.getCapturedVarOrNull()?.hazard = true
|
||||
frame.peek(1)?.getCapturedVarOrNull()?.hazard = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BasicValue.getCapturedVarOrNull() =
|
||||
safeAs<ProperTrackedReferenceValue>()?.descriptor?.safeAs<CapturedVarDescriptor>()
|
||||
|
||||
private fun assignLocalVars() {
|
||||
for (localVar in methodNode.localVariables) {
|
||||
val type = Type.getType(localVar.desc)
|
||||
if (!AsmTypes.isSharedVarType(type)) continue
|
||||
|
||||
val startFrame = frames[localVar.start.getIndex()] ?: continue
|
||||
|
||||
val refValue = startFrame.getLocal(localVar.index) as? ProperTrackedReferenceValue ?: continue
|
||||
val descriptor = refValue.descriptor as? CapturedVarDescriptor ?: continue
|
||||
|
||||
if (descriptor.hazard) continue
|
||||
|
||||
if (descriptor.localVar == null) {
|
||||
descriptor.localVar = localVar
|
||||
}
|
||||
else {
|
||||
descriptor.hazard = true
|
||||
}
|
||||
}
|
||||
|
||||
for (refValue in refValues) {
|
||||
if (refValue.hazard) continue
|
||||
val localVar = refValue.localVar ?: continue
|
||||
|
||||
if (refValue.valueType.size != 1) {
|
||||
refValue.localVarIndex = methodNode.maxLocals
|
||||
methodNode.maxLocals += 2
|
||||
localVar.index = refValue.localVarIndex
|
||||
}
|
||||
else {
|
||||
refValue.localVarIndex = localVar.index
|
||||
}
|
||||
|
||||
val startIndex = localVar.start.getIndex()
|
||||
val initFieldInsns = refValue.putFieldInsns.filter { it.getIndex() < startIndex }
|
||||
if (initFieldInsns.size != 1) {
|
||||
refValue.hazard = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rewrite() {
|
||||
for (refValue in refValues) {
|
||||
if (!refValue.canRewrite()) continue
|
||||
|
||||
rewriteRefValue(refValue)
|
||||
}
|
||||
|
||||
methodNode.removeEmptyCatchBlocks()
|
||||
methodNode.removeUnusedLocalVariables()
|
||||
}
|
||||
|
||||
private fun rewriteRefValue(capturedVar: CapturedVarDescriptor) {
|
||||
methodNode.instructions.run {
|
||||
capturedVar.localVar!!.let {
|
||||
it.signature = null
|
||||
it.desc = capturedVar.valueType.descriptor
|
||||
}
|
||||
|
||||
remove(capturedVar.newInsn)
|
||||
remove(capturedVar.initCallInsn!!)
|
||||
capturedVar.stackInsns.forEach { remove(it) }
|
||||
capturedVar.aloadInsns.forEach { remove(it) }
|
||||
capturedVar.astoreInsns.forEach { remove(it) }
|
||||
|
||||
capturedVar.getFieldInsns.forEach {
|
||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ILOAD), capturedVar.localVarIndex))
|
||||
}
|
||||
|
||||
capturedVar.putFieldInsns.forEach {
|
||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal const val REF_ELEMENT_FIELD = "element"
|
||||
internal const val INIT_METHOD_NAME = "<init>"
|
||||
|
||||
internal val REF_TYPE_TO_ELEMENT_TYPE = HashMap<String, Type>().apply {
|
||||
put(AsmTypes.OBJECT_REF_TYPE.internalName, AsmTypes.OBJECT_TYPE)
|
||||
PrimitiveType.values().forEach {
|
||||
put(AsmTypes.sharedTypeForPrimitive(it).internalName, AsmTypes.valueTypeForPrimitive(it))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.captured
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() {
|
||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||
when {
|
||||
v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue ->
|
||||
if (v.descriptor == w.descriptor)
|
||||
v
|
||||
else
|
||||
TaintedTrackedReferenceValue(
|
||||
getTaintedValueType(v.type, w.type),
|
||||
setOf(v.descriptor, w.descriptor)
|
||||
)
|
||||
|
||||
v is TrackedReferenceValue || w is TrackedReferenceValue ->
|
||||
TaintedTrackedReferenceValue(
|
||||
getTaintedValueType(v.type, w.type),
|
||||
v.referenceValueDescriptors + w.referenceValueDescriptors
|
||||
)
|
||||
|
||||
else ->
|
||||
super.merge(v, w)
|
||||
}
|
||||
|
||||
private val BasicValue.referenceValueDescriptors: Set<ReferenceValueDescriptor>
|
||||
get() = if (this is TrackedReferenceValue) this.descriptors else emptySet()
|
||||
|
||||
private fun getTaintedValueType(type1: Type?, type2: Type?): Type =
|
||||
when {
|
||||
type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE
|
||||
type1 == type2 -> type1
|
||||
else -> AsmTypes.OBJECT_TYPE
|
||||
}
|
||||
|
||||
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
|
||||
if (value is TrackedReferenceValue) {
|
||||
checkRefValuesUsages(insn, listOf(value))
|
||||
value
|
||||
}
|
||||
else {
|
||||
super.copyOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
|
||||
checkRefValuesUsages(insn, listOf(value))
|
||||
return super.unaryOperation(insn, value)
|
||||
}
|
||||
|
||||
override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? {
|
||||
checkRefValuesUsages(insn, listOf(value1, value2))
|
||||
return super.binaryOperation(insn, value1, value2)
|
||||
}
|
||||
|
||||
override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? {
|
||||
checkRefValuesUsages(insn, listOf(value1, value2, value3))
|
||||
return super.ternaryOperation(insn, value1, value2, value3)
|
||||
}
|
||||
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: List<BasicValue>): BasicValue? {
|
||||
checkRefValuesUsages(insn, values)
|
||||
return super.naryOperation(insn, values)
|
||||
}
|
||||
|
||||
protected open fun checkRefValuesUsages(insn: AbstractInsnNode, values: List<BasicValue>) {
|
||||
values.forEach { value ->
|
||||
if (value is TaintedTrackedReferenceValue) {
|
||||
value.descriptors.forEach { it.onUseAsTainted() }
|
||||
}
|
||||
}
|
||||
|
||||
values.forEachIndexed { pos, value ->
|
||||
if (value is TrackedReferenceValue) {
|
||||
processRefValueUsage(value, insn, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.captured
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
interface ReferenceValueDescriptor {
|
||||
fun onUseAsTainted()
|
||||
}
|
||||
|
||||
abstract class TrackedReferenceValue(type: Type): StrictBasicValue(type) {
|
||||
abstract val descriptors: Set<ReferenceValueDescriptor>
|
||||
}
|
||||
|
||||
class ProperTrackedReferenceValue(type: Type, val descriptor: ReferenceValueDescriptor) : TrackedReferenceValue(type) {
|
||||
override val descriptors: Set<ReferenceValueDescriptor>
|
||||
get() = setOf(descriptor)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other === this ||
|
||||
other is ProperTrackedReferenceValue && other.descriptor == this.descriptor
|
||||
|
||||
override fun hashCode(): Int =
|
||||
descriptor.hashCode()
|
||||
|
||||
override fun toString(): String =
|
||||
"[$descriptor]"
|
||||
}
|
||||
|
||||
class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other === this ||
|
||||
other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors
|
||||
|
||||
override fun hashCode(): Int =
|
||||
descriptors.hashCode()
|
||||
|
||||
override fun toString(): String =
|
||||
"!$descriptors"
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.common;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
|
||||
import org.jetbrains.org.objectweb.asm.Handle;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -136,7 +137,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
case NEW:
|
||||
return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
|
||||
default:
|
||||
throw new Error("Internal error.");
|
||||
throw new IllegalArgumentException("Unexpected instruction: " + InlineCodegenUtil.getInsnOpcodeText(insn));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +222,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
case PUTFIELD:
|
||||
return null;
|
||||
default:
|
||||
throw new Error("Internal error.");
|
||||
throw new IllegalArgumentException("Unexpected instruction: " + InlineCodegenUtil.getInsnOpcodeText(insn));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +341,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
|
||||
case IFNONNULL:
|
||||
return null;
|
||||
default:
|
||||
throw new Error("Internal error.");
|
||||
throw new IllegalArgumentException("Unexpected instruction: " + InlineCodegenUtil.getInsnOpcodeText(insn));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ open class StrictBasicValue(type: Type?) : BasicValue(type) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
if (other == null || other::class.java != this::class.java) return false
|
||||
if (!super.equals(other)) return false
|
||||
|
||||
other as StrictBasicValue
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package org.jetbrains.kotlin.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
val AbstractInsnNode.isMeaningful: Boolean get() =
|
||||
@@ -50,9 +52,7 @@ fun MethodNode.prepareForEmitting() {
|
||||
|
||||
// local variables with live ranges starting after last meaningful instruction lead to VerifyError
|
||||
localVariables = localVariables.filter { lv ->
|
||||
InsnSequence(lv.start, lv.end).any { insn ->
|
||||
insn.isMeaningful
|
||||
}
|
||||
InsnSequence(lv.start, lv.end).any(AbstractInsnNode::isMeaningful)
|
||||
}
|
||||
|
||||
// We should remove linenumbers after last meaningful instruction
|
||||
@@ -71,10 +71,64 @@ fun MethodNode.prepareForEmitting() {
|
||||
|
||||
fun MethodNode.removeEmptyCatchBlocks() {
|
||||
tryCatchBlocks = tryCatchBlocks.filter { tcb ->
|
||||
InsnSequence(tcb.start, tcb.end).any { insn ->
|
||||
insn.isMeaningful
|
||||
InsnSequence(tcb.start, tcb.end).any(AbstractInsnNode::isMeaningful)
|
||||
}
|
||||
}
|
||||
|
||||
fun MethodNode.removeUnusedLocalVariables() {
|
||||
val used = BooleanArray(maxLocals) { false }
|
||||
for (insn in instructions) {
|
||||
when (insn) {
|
||||
is VarInsnNode -> {
|
||||
val varIndex = insn.`var`
|
||||
used[varIndex] = true
|
||||
if (insn.isSize2LoadStoreOperation()) {
|
||||
used[varIndex + 1] = true
|
||||
}
|
||||
}
|
||||
is IincInsnNode ->
|
||||
used[insn.`var`] = true
|
||||
}
|
||||
}
|
||||
for (localVar in localVariables) {
|
||||
val varIndex = localVar.index
|
||||
used[varIndex] = true
|
||||
val type = Type.getType(localVar.desc)
|
||||
if (type.size == 2) {
|
||||
used[varIndex + 1] = true
|
||||
}
|
||||
}
|
||||
|
||||
if (used.all { it }) return
|
||||
|
||||
val remapping = IntArray(maxLocals) { 0 }
|
||||
var lastUnused = 0
|
||||
for (i in remapping.indices) {
|
||||
remapping[i] = lastUnused
|
||||
if (used[i]) {
|
||||
lastUnused++
|
||||
}
|
||||
}
|
||||
|
||||
remapLocalVariables(remapping)
|
||||
}
|
||||
|
||||
private fun VarInsnNode.isSize2LoadStoreOperation() =
|
||||
opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE
|
||||
|
||||
fun MethodNode.remapLocalVariables(remapping: IntArray) {
|
||||
for (insn in instructions.toArray()) {
|
||||
when (insn) {
|
||||
is VarInsnNode ->
|
||||
insn.`var` = remapping[insn.`var`]
|
||||
is IincInsnNode ->
|
||||
insn.`var` = remapping[insn.`var`]
|
||||
}
|
||||
}
|
||||
|
||||
for (localVariableNode in localVariables) {
|
||||
localVariableNode.index = remapping[localVariableNode.index]
|
||||
}
|
||||
}
|
||||
|
||||
inline fun AbstractInsnNode.findNextOrNull(predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
|
||||
@@ -119,9 +173,16 @@ val AbstractInsnNode.intConstant: Int? get() =
|
||||
|
||||
fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
|
||||
|
||||
fun AbstractInsnNode.isStoreOperation(): Boolean = getOpcode() in Opcodes.ISTORE..Opcodes.ASTORE
|
||||
fun AbstractInsnNode.isLoadOperation(): Boolean = getOpcode() in Opcodes.ILOAD..Opcodes.ALOAD
|
||||
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
|
||||
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
|
||||
|
||||
val AbstractInsnNode?.insnText get() = InlineCodegenUtil.getInsnText(this)
|
||||
val AbstractInsnNode?.debugText get() =
|
||||
if (this == null) "<null>" else "${this.javaClass.simpleName}: $insnText"
|
||||
if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText"
|
||||
|
||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean =
|
||||
takeInsnIf(opcode, condition) != null
|
||||
|
||||
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
|
||||
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
|
||||
|
||||
|
||||
@@ -58,16 +58,9 @@ internal class FixStackAnalyzer(
|
||||
for (marker in context.fakeAlwaysFalseIfeqMarkers) {
|
||||
val next = marker.next
|
||||
if (next is JumpInsnNode) {
|
||||
val nop = InsnNode(Opcodes.NOP)
|
||||
expectedStackNode[next.label] = nop
|
||||
method.instructions.insert(next, nop)
|
||||
method.instructions.remove(marker)
|
||||
method.instructions.remove(next)
|
||||
context.nodesToRemoveOnCleanup.add(nop)
|
||||
expectedStackNode[next.label] = marker
|
||||
}
|
||||
}
|
||||
|
||||
context.fakeAlwaysFalseIfeqMarkers.clear()
|
||||
}
|
||||
|
||||
private val analyzer = InternalAnalyzer(owner, method, context)
|
||||
|
||||
@@ -39,8 +39,6 @@ internal class FixStackContext(val methodNode: MethodNode) {
|
||||
val openingInlineMethodMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
|
||||
var consistentInlineMarkers: Boolean = true; private set
|
||||
|
||||
val nodesToRemoveOnCleanup = arrayListOf<AbstractInsnNode>()
|
||||
|
||||
init {
|
||||
saveStackMarkerForRestoreMarker = insertTryCatchBlocksMarkers(methodNode)
|
||||
isThereAnyTryCatch = saveStackMarkerForRestoreMarker.isNotEmpty()
|
||||
|
||||
@@ -39,31 +39,51 @@ class FixStackMethodTransformer : MethodTransformer() {
|
||||
}
|
||||
|
||||
if (context.isAnalysisRequired()) {
|
||||
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
|
||||
analyzer.analyze()
|
||||
|
||||
methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize
|
||||
|
||||
val actions = arrayListOf<() -> Unit>()
|
||||
|
||||
transformBreakContinueGotos(methodNode, context, actions, analyzer)
|
||||
|
||||
transformSaveRestoreStackMarkers(methodNode, context, actions, analyzer)
|
||||
|
||||
actions.forEach { it() }
|
||||
analyzeAndTransformBreakContinueGotos(context, internalClassName, methodNode)
|
||||
removeAlwaysFalseIfeqMarkers(context, methodNode)
|
||||
analyzeAndTransformSaveRestoreStack(context, internalClassName, methodNode)
|
||||
}
|
||||
|
||||
context.fakeAlwaysTrueIfeqMarkers.forEach { marker ->
|
||||
replaceAlwaysTrueIfeqWithGoto(methodNode, marker)
|
||||
}
|
||||
removeAlwaysTrueIfeqMarkers(context, methodNode)
|
||||
removeAlwaysFalseIfeqMarkers(context, methodNode)
|
||||
}
|
||||
|
||||
private fun analyzeAndTransformBreakContinueGotos(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
|
||||
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
|
||||
analyzer.analyze()
|
||||
|
||||
methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize
|
||||
|
||||
val actions = arrayListOf<() -> Unit>()
|
||||
|
||||
transformBreakContinueGotos(methodNode, context, actions, analyzer)
|
||||
|
||||
actions.forEach { it() }
|
||||
}
|
||||
|
||||
private fun analyzeAndTransformSaveRestoreStack(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
|
||||
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
|
||||
analyzer.analyze()
|
||||
|
||||
val actions = arrayListOf<() -> Unit>()
|
||||
|
||||
transformSaveRestoreStackMarkers(methodNode, context, actions, analyzer)
|
||||
|
||||
actions.forEach { it() }
|
||||
}
|
||||
|
||||
private fun removeAlwaysFalseIfeqMarkers(context: FixStackContext, methodNode: MethodNode) {
|
||||
context.fakeAlwaysFalseIfeqMarkers.forEach { marker ->
|
||||
removeAlwaysFalseIfeq(methodNode, marker)
|
||||
}
|
||||
context.fakeAlwaysFalseIfeqMarkers.clear()
|
||||
}
|
||||
|
||||
context.nodesToRemoveOnCleanup.forEach {
|
||||
methodNode.instructions.remove(it)
|
||||
private fun removeAlwaysTrueIfeqMarkers(context: FixStackContext, methodNode: MethodNode) {
|
||||
context.fakeAlwaysTrueIfeqMarkers.forEach { marker ->
|
||||
replaceAlwaysTrueIfeqWithGoto(methodNode, marker)
|
||||
}
|
||||
context.fakeAlwaysTrueIfeqMarkers.clear()
|
||||
}
|
||||
|
||||
private fun transformBreakContinueGotos(
|
||||
|
||||
@@ -26,7 +26,7 @@ fun <V : Value> Frame<V>.top(): V? =
|
||||
peek(0)
|
||||
|
||||
fun <V : Value> Frame<V>.peek(offset: Int): V? =
|
||||
if (stackSize >= offset) getStack(stackSize - offset - 1) else null
|
||||
if (stackSize > offset) getStack(stackSize - offset - 1) else null
|
||||
|
||||
class SavedStackDescriptor(
|
||||
val savedValues: List<BasicValue>,
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.nullCheck
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.*
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
class NullabilityInterpreter : OptimizationBasicInterpreter() {
|
||||
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
||||
val defaultResult = super.newOperation(insn)
|
||||
val resultType = defaultResult?.type
|
||||
|
||||
return when {
|
||||
insn.opcode == Opcodes.ACONST_NULL ->
|
||||
NullBasicValue
|
||||
insn.opcode == Opcodes.NEW ->
|
||||
NotNullBasicValue(resultType)
|
||||
insn.opcode == Opcodes.LDC && resultType.isReferenceType() ->
|
||||
NotNullBasicValue(resultType)
|
||||
insn.isUnitInstance() ->
|
||||
NotNullBasicValue(resultType)
|
||||
else ->
|
||||
defaultResult
|
||||
}
|
||||
}
|
||||
|
||||
private fun Type?.isReferenceType() =
|
||||
this?.sort.let { it == Type.OBJECT || it == Type.ARRAY }
|
||||
|
||||
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
|
||||
val defaultResult = super.unaryOperation(insn, value)
|
||||
val resultType = defaultResult?.type
|
||||
|
||||
return when {
|
||||
insn.opcode == Opcodes.CHECKCAST ->
|
||||
value
|
||||
insn.opcode == Opcodes.NEWARRAY || insn.opcode == Opcodes.ANEWARRAY ->
|
||||
NotNullBasicValue(resultType)
|
||||
else ->
|
||||
defaultResult
|
||||
}
|
||||
}
|
||||
|
||||
override fun naryOperation(insn: AbstractInsnNode, values: List<BasicValue>): BasicValue? {
|
||||
val defaultResult = super.naryOperation(insn, values)
|
||||
val resultType = defaultResult?.type
|
||||
|
||||
return when {
|
||||
insn.isBoxing() ->
|
||||
NotNullBasicValue(resultType)
|
||||
insn.isIteratorMethodCallOfProgression(values) ->
|
||||
ProgressionIteratorBasicValue.byProgressionClassType(values[0].type)
|
||||
insn.isNextMethodCallOfProgressionIterator(values) ->
|
||||
NotNullBasicValue(resultType)
|
||||
else ->
|
||||
defaultResult
|
||||
}
|
||||
}
|
||||
|
||||
override fun merge(v: BasicValue, w: BasicValue): BasicValue =
|
||||
when {
|
||||
v is NullBasicValue && w is NullBasicValue ->
|
||||
NullBasicValue
|
||||
v is NullBasicValue || w is NullBasicValue ->
|
||||
StrictBasicValue.REFERENCE_VALUE
|
||||
v is ProgressionIteratorBasicValue && w is ProgressionIteratorBasicValue ->
|
||||
mergeNotNullValuesOfSameKind(v, w)
|
||||
v is ProgressionIteratorBasicValue && w is NotNullBasicValue ->
|
||||
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
||||
w is ProgressionIteratorBasicValue && v is NotNullBasicValue ->
|
||||
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
||||
v is NotNullBasicValue && w is NotNullBasicValue ->
|
||||
mergeNotNullValuesOfSameKind(v, w)
|
||||
else ->
|
||||
super.merge(v, w)
|
||||
}
|
||||
|
||||
private fun mergeNotNullValuesOfSameKind(v: StrictBasicValue, w: StrictBasicValue) =
|
||||
if (v.type == w.type) v else NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun TypeInsnNode.getObjectType(): Type =
|
||||
Type.getObjectType(desc)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.nullCheck
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.ProgressionIteratorBasicValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.InsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
private val deadCodeElimination = DeadCodeEliminationMethodTransformer()
|
||||
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
while (runSingleNullCheckEliminationPass(internalClassName, methodNode)) {
|
||||
deadCodeElimination.transform(internalClassName, methodNode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAlwaysFalse(opcode: Int, nullability: Nullability) =
|
||||
(opcode == Opcodes.IFNULL && nullability == Nullability.NOT_NULL) ||
|
||||
(opcode == Opcodes.IFNONNULL && nullability == Nullability.NULL)
|
||||
|
||||
private fun isAlwaysTrue(opcode: Int, nullability: Nullability) =
|
||||
(opcode == Opcodes.IFNULL && nullability == Nullability.NULL) ||
|
||||
(opcode == Opcodes.IFNONNULL && nullability == Nullability.NOT_NULL)
|
||||
|
||||
|
||||
private fun runSingleNullCheckEliminationPass(internalClassName: String, methodNode: MethodNode): Boolean {
|
||||
val insnList = methodNode.instructions
|
||||
val instructions = insnList.toArray()
|
||||
|
||||
val nullCheckIfs = instructions.mapNotNullTo(SmartList<JumpInsnNode>()) {
|
||||
it.safeAs<JumpInsnNode>()?.takeIf {
|
||||
it.opcode == Opcodes.IFNULL ||
|
||||
it.opcode == Opcodes.IFNONNULL
|
||||
}
|
||||
}
|
||||
if (nullCheckIfs.isEmpty()) return false
|
||||
|
||||
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
|
||||
|
||||
val redundantNullCheckIfs = nullCheckIfs.mapNotNull { insn ->
|
||||
frames[instructions.indexOf(insn)]?.top()?.let { top ->
|
||||
val nullability = top.getNullability()
|
||||
if (nullability == Nullability.NULLABLE)
|
||||
null
|
||||
else
|
||||
Pair(insn, nullability)
|
||||
}
|
||||
}
|
||||
if (redundantNullCheckIfs.isEmpty()) return false
|
||||
|
||||
for ((insn, nullability) in redundantNullCheckIfs) {
|
||||
val previous = insn.previous
|
||||
when (previous?.opcode) {
|
||||
Opcodes.ALOAD, Opcodes.DUP ->
|
||||
insnList.remove(previous)
|
||||
else ->
|
||||
insnList.insert(previous, InsnNode(Opcodes.POP))
|
||||
}
|
||||
|
||||
when {
|
||||
isAlwaysTrue(insn.opcode, nullability) ->
|
||||
insnList.set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
|
||||
isAlwaysFalse(insn.opcode, nullability) ->
|
||||
insnList.remove(insn)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.nullCheck
|
||||
|
||||
import org.jetbrains.kotlin.codegen.coroutines.withInstructionAdapter
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isInsn
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
|
||||
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.tree.*
|
||||
|
||||
class RedundantNullCheckV2MethodTransformer : MethodTransformer() {
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
while (TransformerPass(internalClassName, methodNode).run()) {}
|
||||
}
|
||||
|
||||
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode) {
|
||||
private var changes = false
|
||||
|
||||
private fun AbstractInsnNode.getIndex() =
|
||||
methodNode.instructions.indexOf(this)
|
||||
|
||||
fun run(): Boolean {
|
||||
val checkedReferenceTypes = analyzeTypesAndRemoveDeadCode()
|
||||
eliminateRedundantChecks(checkedReferenceTypes)
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
private fun analyzeTypesAndRemoveDeadCode(): Map<AbstractInsnNode, Type> {
|
||||
val insns = methodNode.instructions.toArray()
|
||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
||||
|
||||
val checkedReferenceTypes = HashMap<AbstractInsnNode, Type>()
|
||||
for (i in insns.indices) {
|
||||
val insn = insns[i]
|
||||
val frame = frames[i]
|
||||
if (insn.isInstanceOfOrNullCheck()) {
|
||||
checkedReferenceTypes[insn] = frame?.top()?.type ?: continue
|
||||
}
|
||||
else if (insn.isCheckParameterNotNull()) {
|
||||
checkedReferenceTypes[insn] = frame?.peek(1)?.type ?: continue
|
||||
}
|
||||
}
|
||||
|
||||
val dceResult = DeadCodeEliminationMethodTransformer().removeDeadCodeByFrames(methodNode, frames)
|
||||
if (dceResult.hasRemovedAnything()) {
|
||||
changes = true
|
||||
}
|
||||
|
||||
return checkedReferenceTypes
|
||||
}
|
||||
|
||||
private fun eliminateRedundantChecks(checkedReferenceTypes: Map<AbstractInsnNode, Type>) {
|
||||
val nullabilityAssumptions = injectNullabilityAssumptions(checkedReferenceTypes)
|
||||
|
||||
val nullabilityMap = analyzeNullabilities()
|
||||
|
||||
nullabilityAssumptions.revert()
|
||||
|
||||
transformTrivialChecks(nullabilityMap)
|
||||
}
|
||||
|
||||
private fun injectNullabilityAssumptions(checkedReferenceTypes: Map<AbstractInsnNode, Type>) =
|
||||
NullabilityAssumptionsBuilder(checkedReferenceTypes).injectNullabilityAssumptions()
|
||||
|
||||
private fun analyzeNullabilities(): Map<AbstractInsnNode, Nullability> {
|
||||
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
|
||||
val insns = methodNode.instructions.toArray()
|
||||
val nullabilityMap = HashMap<AbstractInsnNode, Nullability>()
|
||||
for (i in insns.indices) {
|
||||
val nullability = frames[i]?.top()?.getNullability() ?: continue
|
||||
if (nullability == Nullability.NULLABLE) continue
|
||||
|
||||
val insn = insns[i]
|
||||
if (insn.isInstanceOfOrNullCheck()) {
|
||||
nullabilityMap[insn] = nullability
|
||||
}
|
||||
}
|
||||
return nullabilityMap
|
||||
}
|
||||
|
||||
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, Nullability>) {
|
||||
for ((insn, nullability) in nullabilityMap) {
|
||||
when (insn.opcode) {
|
||||
Opcodes.IFNULL -> transformTrivialNullJump(insn as JumpInsnNode, nullability == Nullability.NULL)
|
||||
Opcodes.IFNONNULL -> transformTrivialNullJump(insn as JumpInsnNode, nullability == Nullability.NOT_NULL)
|
||||
Opcodes.INSTANCEOF -> transformInstanceOf(insn, nullability)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformTrivialNullJump(insn: JumpInsnNode, alwaysTrue: Boolean) {
|
||||
changes = true
|
||||
|
||||
methodNode.instructions.run {
|
||||
popReferenceValueBefore(insn)
|
||||
if (alwaysTrue) {
|
||||
set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
|
||||
}
|
||||
else {
|
||||
remove(insn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformInstanceOf(insn: AbstractInsnNode, nullability: Nullability) {
|
||||
if (nullability != Nullability.NULL) return
|
||||
if (ReifiedTypeInliner.isOperationReifiedMarker(insn.previous)) return
|
||||
|
||||
changes = true
|
||||
|
||||
val nextOpcode = insn.next?.opcode
|
||||
if (nextOpcode == Opcodes.IFEQ || nextOpcode == Opcodes.IFNE)
|
||||
transformNullInstanceOfWithJump(insn)
|
||||
else
|
||||
transformNullInstanceOf(insn)
|
||||
}
|
||||
|
||||
private fun transformNullInstanceOf(insn: AbstractInsnNode) {
|
||||
methodNode.instructions.run {
|
||||
popReferenceValueBefore(insn)
|
||||
set(insn, InsnNode(Opcodes.ICONST_0))
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformNullInstanceOfWithJump(insn: AbstractInsnNode) {
|
||||
methodNode.instructions.run {
|
||||
popReferenceValueBefore(insn)
|
||||
val jump = insn.next.assertedCast<JumpInsnNode> { "JumpInsnNode expected" }
|
||||
remove(insn)
|
||||
if (jump.opcode == Opcodes.IFEQ) {
|
||||
set(jump, JumpInsnNode(Opcodes.GOTO, jump.label))
|
||||
}
|
||||
else {
|
||||
remove(jump)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class NullabilityAssumptionsBuilder(val checkedReferenceTypes: Map<AbstractInsnNode, Type>) {
|
||||
|
||||
private val checksDependingOnVariable = HashMap<Int, MutableList<AbstractInsnNode>>()
|
||||
|
||||
fun injectNullabilityAssumptions(): NullabilityAssumptions {
|
||||
collectVariableDependentChecks()
|
||||
return injectAssumptions()
|
||||
}
|
||||
|
||||
private fun collectVariableDependentChecks() {
|
||||
for (insn in methodNode.instructions) {
|
||||
if (insn.isInstanceOfOrNullCheck()) {
|
||||
val previous = insn.previous ?: continue
|
||||
if (previous.opcode == Opcodes.ALOAD) {
|
||||
addDependentCheck(insn, previous as VarInsnNode)
|
||||
}
|
||||
else if (previous.opcode == Opcodes.DUP) {
|
||||
val previous2 = previous.previous ?: continue
|
||||
if (previous2.opcode == Opcodes.ALOAD) {
|
||||
addDependentCheck(insn, previous2 as VarInsnNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (insn.isCheckParameterNotNull()) {
|
||||
val ldcInsn = insn.previous ?: continue
|
||||
if (ldcInsn.opcode != Opcodes.LDC) continue
|
||||
val aLoadInsn = ldcInsn.previous ?: continue
|
||||
if (aLoadInsn.opcode != Opcodes.ALOAD) continue
|
||||
addDependentCheck(insn, aLoadInsn as VarInsnNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDependentCheck(insn: AbstractInsnNode, aLoadInsn: VarInsnNode) {
|
||||
checksDependingOnVariable.getOrPut(aLoadInsn.`var`) {
|
||||
SmartList<AbstractInsnNode>()
|
||||
}.add(insn)
|
||||
}
|
||||
|
||||
private fun injectAssumptions(): NullabilityAssumptions {
|
||||
val nullabilityAssumptions = NullabilityAssumptions()
|
||||
for ((varIndex, dependentChecks) in checksDependingOnVariable) {
|
||||
for (checkInsn in dependentChecks) {
|
||||
val varType = checkedReferenceTypes[checkInsn]
|
||||
?: throw AssertionError("No var type @${checkInsn.getIndex()}")
|
||||
nullabilityAssumptions.injectAssumptionsForCheck(varIndex, checkInsn, varType)
|
||||
}
|
||||
}
|
||||
return nullabilityAssumptions
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForCheck(varIndex: Int, insn: AbstractInsnNode, varType: Type) {
|
||||
when (insn.opcode) {
|
||||
Opcodes.IFNULL,
|
||||
Opcodes.IFNONNULL ->
|
||||
injectAssumptionsForNullCheck(varIndex, insn as JumpInsnNode, varType)
|
||||
Opcodes.INVOKESTATIC -> {
|
||||
assert(insn.isCheckParameterNotNull()) { "Expected non-null parameter check @${insn.getIndex()}"}
|
||||
injectAssumptionsForParameterNotNullCheck(varIndex, insn, varType)
|
||||
}
|
||||
Opcodes.INSTANCEOF ->
|
||||
injectAssumptionsForInstanceOfCheck(varIndex, insn, varType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForNullCheck(varIndex: Int, insn: JumpInsnNode, varType: Type) {
|
||||
// ALOAD v
|
||||
// IFNULL L
|
||||
// <...> -- v is not null here
|
||||
// L:
|
||||
// <...> -- v is null here
|
||||
|
||||
val jumpsIfNull = insn.opcode == Opcodes.IFNULL
|
||||
val originalLabel = insn.label
|
||||
originalLabels[insn] = originalLabel
|
||||
insn.label = synthetic(LabelNode(Label()))
|
||||
|
||||
val insertAfterNull = if (jumpsIfNull) insn.label else insn
|
||||
val insertAfterNonNull = if (jumpsIfNull) insn else insn.label
|
||||
|
||||
methodNode.instructions.run {
|
||||
add(insn.label)
|
||||
|
||||
insert(insertAfterNull, listOfSynthetics {
|
||||
aconst(null)
|
||||
store(varIndex, varType)
|
||||
if (jumpsIfNull) {
|
||||
goTo(originalLabel.label)
|
||||
}
|
||||
})
|
||||
|
||||
insert(insertAfterNonNull, listOfSynthetics {
|
||||
anew(varType)
|
||||
store(varIndex, varType)
|
||||
if (!jumpsIfNull) {
|
||||
goTo(originalLabel.label)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForParameterNotNullCheck(varIndex: Int, insn: AbstractInsnNode, varType: Type) {
|
||||
// ALOAD v
|
||||
// LDC param_name
|
||||
// INVOKESTATIC checkParameterIsNotNull
|
||||
// <...> -- v is not null here (otherwise an exception was thrown)
|
||||
|
||||
methodNode.instructions.insert(insn, listOfSynthetics {
|
||||
anew(varType)
|
||||
store(varIndex, varType)
|
||||
})
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForInstanceOfCheck(varIndex: Int, insn: AbstractInsnNode, varType: Type) {
|
||||
// ALOAD v
|
||||
// INSTANCEOF T
|
||||
// IFEQ L
|
||||
// <...> -- v is not null here (because it is an instance of T)
|
||||
// L:
|
||||
// <...> -- v is something else here (maybe null)
|
||||
|
||||
val next = insn.next ?: return
|
||||
if (next.opcode != Opcodes.IFEQ && next.opcode != Opcodes.IFNE) return
|
||||
if (next !is JumpInsnNode) return
|
||||
val jumpsIfInstance = next.opcode == Opcodes.IFNE
|
||||
|
||||
val originalLabel: LabelNode?
|
||||
val insertAfterNotNull: AbstractInsnNode
|
||||
if (jumpsIfInstance) {
|
||||
originalLabel = next.label
|
||||
originalLabels[next] = next.label
|
||||
val newLabel = synthetic(LabelNode(Label()))
|
||||
methodNode.instructions.add(newLabel)
|
||||
next.label = newLabel
|
||||
insertAfterNotNull = newLabel
|
||||
}
|
||||
else {
|
||||
originalLabel = null
|
||||
insertAfterNotNull = next
|
||||
}
|
||||
|
||||
methodNode.instructions.run {
|
||||
insert(insertAfterNotNull, listOfSynthetics {
|
||||
anew(varType)
|
||||
store(varIndex, varType)
|
||||
if (originalLabel != null) {
|
||||
goTo(originalLabel.label)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inner class NullabilityAssumptions {
|
||||
val originalLabels = HashMap<JumpInsnNode, LabelNode>()
|
||||
val syntheticInstructions = ArrayList<AbstractInsnNode>()
|
||||
|
||||
fun <T : AbstractInsnNode> synthetic(insn: T): T {
|
||||
syntheticInstructions.add(insn)
|
||||
return insn
|
||||
}
|
||||
|
||||
inline fun listOfSynthetics(block: InstructionAdapter.() -> Unit): InsnList {
|
||||
val insnList = withInstructionAdapter(block)
|
||||
for (insn in insnList) {
|
||||
synthetic(insn)
|
||||
}
|
||||
return insnList
|
||||
}
|
||||
|
||||
fun revert() {
|
||||
methodNode.instructions.run {
|
||||
syntheticInstructions.forEach { remove(it) }
|
||||
}
|
||||
for ((jumpInsn, originalLabel) in originalLabels) {
|
||||
jumpInsn.label = originalLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun AbstractInsnNode.isInstanceOfOrNullCheck() =
|
||||
opcode == Opcodes.INSTANCEOF || opcode == Opcodes.IFNULL || opcode == Opcodes.IFNONNULL
|
||||
|
||||
internal fun AbstractInsnNode.isCheckParameterNotNull() =
|
||||
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
||||
owner == "kotlin/jvm/internal/Intrinsics" &&
|
||||
name == "checkParameterIsNotNull" &&
|
||||
desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
|
||||
}
|
||||
|
||||
internal fun InsnList.popReferenceValueBefore(insn: AbstractInsnNode) {
|
||||
val prev = insn.previous
|
||||
when (prev?.opcode) {
|
||||
Opcodes.ACONST_NULL,
|
||||
Opcodes.DUP,
|
||||
Opcodes.ALOAD ->
|
||||
remove(prev)
|
||||
else ->
|
||||
insertBefore(insn, InsnNode(Opcodes.POP))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.optimization.nullCheck
|
||||
|
||||
import org.jetbrains.kotlin.codegen.optimization.boxing.ProgressionIteratorBasicValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
|
||||
class NotNullBasicValue(type: Type?) : StrictBasicValue(type) {
|
||||
override fun equals(other: Any?): Boolean = other is NotNullBasicValue
|
||||
// We do not differ not-nullable values, so we should always return the same hashCode
|
||||
// Actually it doesn't really matter because analyzer is not supposed to store values in hashtables
|
||||
override fun hashCode() = 0
|
||||
|
||||
companion object {
|
||||
val NOT_NULL_REFERENCE_VALUE = NotNullBasicValue(StrictBasicValue.REFERENCE_VALUE.type)
|
||||
}
|
||||
}
|
||||
|
||||
object NullBasicValue : StrictBasicValue(AsmTypes.OBJECT_TYPE)
|
||||
|
||||
enum class Nullability {
|
||||
NULL, NOT_NULL, NULLABLE;
|
||||
fun isNull() = this == NULL
|
||||
fun isNotNull() = this == NOT_NULL
|
||||
}
|
||||
|
||||
fun BasicValue.getNullability(): Nullability =
|
||||
when (this) {
|
||||
is NullBasicValue -> Nullability.NULL
|
||||
is NotNullBasicValue -> Nullability.NOT_NULL
|
||||
is ProgressionIteratorBasicValue -> Nullability.NOT_NULL
|
||||
else -> Nullability.NULLABLE
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.load.java.lazy.types.RawTypeImpl;
|
||||
import org.jetbrains.kotlin.load.kotlin.JavaFlexibleTypeDeserializer;
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeSignatureMappingKt;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.serialization.AnnotationSerializer;
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtension;
|
||||
@@ -77,7 +78,7 @@ public class JvmSerializerExtension extends SerializerExtension {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializePackage(@NotNull ProtoBuf.Package.Builder proto) {
|
||||
public void serializePackage(@NotNull FqName packageFqName, @NotNull ProtoBuf.Package.Builder proto) {
|
||||
if (!moduleName.equals(JvmAbi.DEFAULT_MODULE_NAME)) {
|
||||
proto.setExtension(JvmProtoBuf.packageModuleName, stringTable.getStringIndex(moduleName));
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class JvmStringTable(private val typeMapper: KotlinTypeMapper) : StringTable {
|
||||
|
||||
val lastRecord = records.lastOrNull()
|
||||
if (lastRecord != null && lastRecord.isTrivial()) {
|
||||
lastRecord.setRange(lastRecord.range + 1)
|
||||
lastRecord.range = lastRecord.range + 1
|
||||
}
|
||||
else records.add(Record.newBuilder())
|
||||
}
|
||||
@@ -93,12 +93,12 @@ class JvmStringTable(private val typeMapper: KotlinTypeMapper) : StringTable {
|
||||
else {
|
||||
val predefinedIndex = JvmNameResolver.getPredefinedStringIndex(string)
|
||||
if (predefinedIndex != null) {
|
||||
record.setPredefinedIndex(predefinedIndex)
|
||||
record.predefinedIndex = predefinedIndex
|
||||
// TODO: move all records with predefined names to the end and do not write associated strings for them (since they are ignored)
|
||||
strings.add("")
|
||||
}
|
||||
else {
|
||||
record.setOperation(Record.Operation.DESC_TO_CLASS_ID)
|
||||
record.operation = Record.Operation.DESC_TO_CLASS_ID
|
||||
strings.add("L${string.replace('.', '$')};")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import java.io.File
|
||||
|
||||
class GenerationState @JvmOverloads constructor(
|
||||
@@ -70,8 +69,9 @@ class GenerationState @JvmOverloads constructor(
|
||||
abstract class GenerateClassFilter {
|
||||
abstract fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean
|
||||
abstract fun shouldGenerateClass(processingClassOrObject: KtClassOrObject): Boolean
|
||||
abstract fun shouldGeneratePackagePart(jetFile: KtFile): Boolean
|
||||
abstract fun shouldGeneratePackagePart(ktFile: KtFile): Boolean
|
||||
abstract fun shouldGenerateScript(script: KtScript): Boolean
|
||||
open fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject) = shouldGenerateClass(processingClassOrObject)
|
||||
|
||||
companion object {
|
||||
@JvmField val GENERATE_ALL: GenerateClassFilter = object : GenerateClassFilter() {
|
||||
@@ -81,7 +81,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
|
||||
override fun shouldGenerateScript(script: KtScript): Boolean = true
|
||||
|
||||
override fun shouldGeneratePackagePart(jetFile: KtFile): Boolean = true
|
||||
override fun shouldGeneratePackagePart(ktFile: KtFile): Boolean = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,8 @@ class GenerationState @JvmOverloads constructor(
|
||||
val incrementalCacheForThisTarget: IncrementalCache?
|
||||
val packagesWithObsoleteParts: Set<FqName>
|
||||
val obsoleteMultifileClasses: List<FqName>
|
||||
val deserializationConfiguration: DeserializationConfiguration = CompilerDeserializationConfiguration(configuration)
|
||||
val deserializationConfiguration: DeserializationConfiguration =
|
||||
CompilerDeserializationConfiguration(configuration.languageVersionSettings)
|
||||
|
||||
init {
|
||||
val icComponents = configuration.get(JVMConfigurationKeys.INCREMENTAL_COMPILATION_COMPONENTS)
|
||||
@@ -113,7 +114,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val extraJvmDiagnosticsTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "For extra diagnostics in ${this.javaClass}", false)
|
||||
val extraJvmDiagnosticsTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "For extra diagnostics in ${this::class.java}", false)
|
||||
private val interceptedBuilderFactory: ClassBuilderFactory
|
||||
private var used = false
|
||||
|
||||
@@ -123,7 +124,8 @@ class GenerationState @JvmOverloads constructor(
|
||||
extraJvmDiagnosticsTrace.bindingContext.diagnostics
|
||||
}
|
||||
|
||||
val isJvm8Target: Boolean = configuration.get(JVMConfigurationKeys.JVM_TARGET) == JvmTarget.JVM_1_8
|
||||
val target = configuration.get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT
|
||||
val isJvm8Target: Boolean = target == JvmTarget.JVM_1_8
|
||||
val isJvm8TargetWithDefaults: Boolean = isJvm8Target && configuration.getBoolean(JVMConfigurationKeys.JVM8_TARGET_WITH_DEFAULTS)
|
||||
val generateDefaultImplsForJvm8: Boolean = configuration.getBoolean(JVMConfigurationKeys.INTERFACE_COMPATIBILITY)
|
||||
|
||||
@@ -162,7 +164,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
|
||||
val rootContext: CodegenContext<*> = RootContext(this)
|
||||
|
||||
val classFileVersion: Int = if (isJvm8Target) Opcodes.V1_8 else Opcodes.V1_6
|
||||
val classFileVersion: Int = target.bytecodeVersion
|
||||
|
||||
val generateParametersMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.PARAMETERS_METADATA)
|
||||
|
||||
|
||||
@@ -192,6 +192,10 @@ public class KotlinTypeMapper {
|
||||
return asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
|
||||
}
|
||||
|
||||
if (descriptor instanceof ConstructorDescriptor) {
|
||||
return mapClass(((ConstructorDescriptor) descriptor).getConstructedClass());
|
||||
}
|
||||
|
||||
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
||||
if (container instanceof PackageFragmentDescriptor) {
|
||||
String packageMemberOwner = internalNameForPackageMemberOwner((CallableMemberDescriptor) descriptor, publicFacade);
|
||||
@@ -702,15 +706,14 @@ public class KotlinTypeMapper {
|
||||
|
||||
@NotNull
|
||||
public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall) {
|
||||
if (descriptor instanceof TypeAliasConstructorDescriptor) {
|
||||
return mapToCallableMethod(((TypeAliasConstructorDescriptor) descriptor).getUnderlyingConstructorDescriptor(), superCall);
|
||||
}
|
||||
|
||||
if (descriptor instanceof ClassConstructorDescriptor) {
|
||||
JvmMethodSignature method = mapSignatureSkipGeneric(descriptor);
|
||||
Type owner = mapClass(((ClassConstructorDescriptor) descriptor).getContainingDeclaration());
|
||||
String defaultImplDesc = mapDefaultMethod(descriptor, OwnerKind.IMPLEMENTATION).getDescriptor();
|
||||
return new CallableMethod(owner, owner, defaultImplDesc, method, INVOKESPECIAL, null, null, null, false);
|
||||
if (descriptor instanceof ConstructorDescriptor) {
|
||||
JvmMethodSignature method = mapSignatureSkipGeneric(descriptor.getOriginal());
|
||||
Type owner = mapOwner(descriptor);
|
||||
String defaultImplDesc = mapDefaultMethod(descriptor.getOriginal(), OwnerKind.IMPLEMENTATION).getDescriptor();
|
||||
return new CallableMethod(
|
||||
owner, owner, defaultImplDesc, method, INVOKESPECIAL,
|
||||
null, null, null, false
|
||||
);
|
||||
}
|
||||
|
||||
if (descriptor instanceof LocalVariableAccessorDescriptor) {
|
||||
@@ -958,7 +961,7 @@ public class KotlinTypeMapper {
|
||||
|
||||
if (!(descriptor instanceof ConstructorDescriptor) &&
|
||||
descriptor.getVisibility() == Visibilities.INTERNAL &&
|
||||
!descriptor.getAnnotations().hasAnnotation(KotlinBuiltIns.FQ_NAMES.publishedApi)) {
|
||||
!DescriptorUtilsKt.isPublishedApi(descriptor)) {
|
||||
return name + "$" + NameUtils.sanitizeAsJavaIdentifier(moduleName);
|
||||
}
|
||||
|
||||
@@ -1027,31 +1030,28 @@ public class KotlinTypeMapper {
|
||||
}
|
||||
}
|
||||
|
||||
if (f instanceof TypeAliasConstructorDescriptor) {
|
||||
return mapSignature(((TypeAliasConstructorDescriptor) f).getUnderlyingConstructorDescriptor(), kind, skipGenericSignature);
|
||||
}
|
||||
|
||||
if (f instanceof FunctionImportedFromObject) {
|
||||
return mapSignature(((FunctionImportedFromObject) f).getCallableFromObject(), kind, skipGenericSignature);
|
||||
}
|
||||
|
||||
if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(f)) {
|
||||
return mapSignature(CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(f), kind, skipGenericSignature);
|
||||
}
|
||||
|
||||
if (f instanceof ConstructorDescriptor) {
|
||||
return mapSignature(f, kind, f.getOriginal().getValueParameters(), skipGenericSignature);
|
||||
}
|
||||
|
||||
return mapSignature(f, kind, f.getValueParameters(), skipGenericSignature);
|
||||
return mapSignatureWithCustomParameters(f, kind, f.getValueParameters(), skipGenericSignature);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JvmMethodGenericSignature mapSignature(
|
||||
public JvmMethodGenericSignature mapSignatureWithCustomParameters(
|
||||
@NotNull FunctionDescriptor f,
|
||||
@NotNull OwnerKind kind,
|
||||
@NotNull List<ValueParameterDescriptor> valueParameters,
|
||||
boolean skipGenericSignature
|
||||
) {
|
||||
if (f instanceof FunctionImportedFromObject) {
|
||||
return mapSignature(((FunctionImportedFromObject) f).getCallableFromObject(), kind, skipGenericSignature);
|
||||
}
|
||||
else if (f instanceof TypeAliasConstructorDescriptor) {
|
||||
return mapSignature(((TypeAliasConstructorDescriptor) f).getUnderlyingConstructorDescriptor(), kind, valueParameters, skipGenericSignature);
|
||||
}
|
||||
|
||||
checkOwnerCompatibility(f);
|
||||
|
||||
JvmSignatureWriter sw = skipGenericSignature || f instanceof AccessorForCallableDescriptor
|
||||
@@ -1422,7 +1422,7 @@ public class KotlinTypeMapper {
|
||||
List<ResolvedValueArgument> valueArguments = superCall.getValueArgumentsByIndex();
|
||||
assert valueArguments != null : "Failed to arrange value arguments by index: " + superDescriptor;
|
||||
|
||||
List<JvmMethodParameterSignature> parameters = mapSignatureSkipGeneric(superDescriptor).getValueParameters();
|
||||
List<JvmMethodParameterSignature> parameters = mapSignatureSkipGeneric(superDescriptor.getOriginal()).getValueParameters();
|
||||
|
||||
int params = parameters.size();
|
||||
int args = valueArguments.size();
|
||||
@@ -1504,4 +1504,13 @@ public class KotlinTypeMapper {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String classInternalName(@NotNull ClassDescriptor classDescriptor) {
|
||||
Type recordedType = typeMappingConfiguration.getPredefinedTypeForClass(classDescriptor);
|
||||
if (recordedType != null) {
|
||||
return recordedType.getInternalName();
|
||||
}
|
||||
return TypeSignatureMappingKt.computeInternalName(classDescriptor, typeMappingConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.builtins.BuiltInsSerializerExtension
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment.Companion.DOT_METADATA_FILE_EXTENSION
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmPackageTable
|
||||
@@ -142,11 +141,11 @@ open class MetadataSerializer(private val dependOnOldBuiltIns: Boolean) {
|
||||
protected inner class PackageSerializer(
|
||||
private val classes: Collection<DeclarationDescriptor>,
|
||||
private val members: Collection<DeclarationDescriptor>,
|
||||
packageFqName: FqName,
|
||||
private val packageFqName: FqName,
|
||||
private val destFile: File
|
||||
) {
|
||||
private val proto = BuiltInsProtoBuf.BuiltIns.newBuilder()
|
||||
private val extension = BuiltInsSerializerExtension(packageFqName)
|
||||
private val proto = ProtoBuf.PackageFragment.newBuilder()
|
||||
private val extension = BuiltInsSerializerExtension()
|
||||
|
||||
fun run() {
|
||||
serializeClasses(classes)
|
||||
@@ -171,7 +170,7 @@ open class MetadataSerializer(private val dependOnOldBuiltIns: Boolean) {
|
||||
}
|
||||
|
||||
private fun serializeMembers(members: Collection<DeclarationDescriptor>) {
|
||||
proto.`package` = DescriptorSerializer.createTopLevel(extension).packagePartProto(members).build()
|
||||
proto.`package` = DescriptorSerializer.createTopLevel(extension).packagePartProto(packageFqName, members).build()
|
||||
}
|
||||
|
||||
private fun serializeStringTable() {
|
||||
|
||||
@@ -17,16 +17,8 @@
|
||||
package org.jetbrains.kotlin.serialization.builtins
|
||||
|
||||
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.KotlinSerializerExtensionBase
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
|
||||
class BuiltInsSerializerExtension(
|
||||
private val packageFqName: FqName
|
||||
) : KotlinSerializerExtensionBase(BuiltInSerializerProtocol) {
|
||||
class BuiltInsSerializerExtension : KotlinSerializerExtensionBase(BuiltInSerializerProtocol) {
|
||||
override fun shouldUseTypeTable(): Boolean = true
|
||||
|
||||
override fun serializePackage(proto: ProtoBuf.Package.Builder) {
|
||||
proto.setExtension(BuiltInsProtoBuf.packageFqName, stringTable.getPackageFqNameIndex(packageFqName))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ found top-level declarations to <destination dir> (*.kotlin_builtins files)"""
|
||||
|
||||
val destDir = File(args[0])
|
||||
|
||||
val srcDirs = args.drop(1).map { File(it) }
|
||||
val srcDirs = args.drop(1).map(::File)
|
||||
assert(srcDirs.isNotEmpty()) { "At least one source directory should be specified" }
|
||||
|
||||
val missing = srcDirs.filterNot { it.exists() }
|
||||
val missing = srcDirs.filterNot(File::exists)
|
||||
assert(missing.isEmpty()) { "These source directories are missing: $missing" }
|
||||
|
||||
try {
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="intellij-core" level="project" />
|
||||
<orderEntry type="library" exported="" name="cli-parser" level="project" />
|
||||
<orderEntry type="library" name="jps" level="project" />
|
||||
<orderEntry type="module" module-name="descriptor.loader.java" />
|
||||
<orderEntry type="module" module-name="frontend.java" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -17,8 +17,8 @@
|
||||
package org.jetbrains.kotlin.cli.common.arguments;
|
||||
|
||||
import com.intellij.util.SmartList;
|
||||
import com.sampullara.cli.Argument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
@@ -79,6 +79,9 @@ public abstract class CommonCompilerArguments implements Serializable {
|
||||
@Argument(value = "Xno-check-impl", description = "Do not check presence of 'impl' modifier in multi-platform projects")
|
||||
public boolean noCheckImpl;
|
||||
|
||||
@Argument(value = "Xskip-java-check", description = "Do not warn when running the compiler under Java 6 or 7")
|
||||
public boolean noJavaVersionWarning;
|
||||
|
||||
@Argument(value = "Xcoroutines=warn")
|
||||
public boolean coroutinesWarn;
|
||||
|
||||
@@ -96,6 +99,15 @@ public abstract class CommonCompilerArguments implements Serializable {
|
||||
|
||||
public List<String> unknownExtraFlags = new SmartList<String>();
|
||||
|
||||
@NotNull
|
||||
public static CommonCompilerArguments createDefaultInstance() {
|
||||
DummyImpl arguments = new DummyImpl();
|
||||
arguments.coroutinesEnable = false;
|
||||
arguments.coroutinesWarn = true;
|
||||
arguments.coroutinesError = false;
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String executableScriptFileName() {
|
||||
return "kotlinc";
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.arguments;
|
||||
|
||||
import com.sampullara.cli.Argument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument;
|
||||
|
||||
import static org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.CALL;
|
||||
import static org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.NO_CALL;
|
||||
@@ -71,6 +71,13 @@ public class K2JSCompilerArguments extends CommonCompilerArguments {
|
||||
@ValueDescription("<path>")
|
||||
public String outputPostfix;
|
||||
|
||||
@NotNull
|
||||
public static K2JSCompilerArguments createDefaultInstance() {
|
||||
K2JSCompilerArguments arguments = new K2JSCompilerArguments();
|
||||
arguments.moduleKind = K2JsArgumentConstants.MODULE_PLAIN;
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String executableScriptFileName() {
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.arguments;
|
||||
|
||||
import com.sampullara.cli.Argument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument;
|
||||
import org.jetbrains.kotlin.config.JvmTarget;
|
||||
|
||||
public class K2JVMCompilerArguments extends CommonCompilerArguments {
|
||||
public static final long serialVersionUID = 0L;
|
||||
@@ -113,10 +114,16 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
|
||||
// Paths to output directories for friend modules.
|
||||
public String[] friendPaths;
|
||||
|
||||
@NotNull
|
||||
public static K2JVMCompilerArguments createDefaultInstance() {
|
||||
K2JVMCompilerArguments arguments = new K2JVMCompilerArguments();
|
||||
arguments.jvmTarget = JvmTarget.DEFAULT.getDescription();
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String executableScriptFileName() {
|
||||
return "kotlinc-jvm";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.arguments;
|
||||
|
||||
import com.sampullara.cli.Argument;
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument;
|
||||
|
||||
public class K2MetadataCompilerArguments extends CommonCompilerArguments {
|
||||
public static final long serialVersionUID = 0L;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.jetbrains.kotlin.cli.common.arguments
|
||||
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil
|
||||
import com.sampullara.cli.Args
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Args
|
||||
import java.lang.reflect.Field
|
||||
import java.lang.reflect.Modifier
|
||||
import java.util.*
|
||||
@@ -37,23 +37,23 @@ import java.util.*
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Any> copyBean(bean: T) = copyFields(bean, bean.javaClass.newInstance(), true, collectFieldsToCopy(bean.javaClass, false))
|
||||
fun <T : Any> copyBean(bean: T) = copyFields(bean, bean::class.java.newInstance(), true, collectFieldsToCopy(bean::class.java, false))
|
||||
|
||||
fun <From : Any, To : From> mergeBeans(from: From, to: To): To {
|
||||
// TODO: rewrite when updated version of com.intellij.util.xmlb is available on TeamCity
|
||||
return copyFields(from, XmlSerializerUtil.createCopy(to), false, collectFieldsToCopy(from.javaClass, false))
|
||||
return copyFields(from, XmlSerializerUtil.createCopy(to), false, collectFieldsToCopy(from::class.java, false))
|
||||
}
|
||||
|
||||
fun <From : Any, To : Any> copyInheritedFields(from: From, to: To) = copyFields(from, to, true, collectFieldsToCopy(from.javaClass, true))
|
||||
fun <From : Any, To : Any> copyInheritedFields(from: From, to: To) = copyFields(from, to, true, collectFieldsToCopy(from::class.java, true))
|
||||
|
||||
fun <From : Any, To : Any> copyFieldsSatisfying(from: From, to: To, predicate: (Field) -> Boolean) =
|
||||
copyFields(from, to, true, collectFieldsToCopy(from.javaClass, false).filter(predicate))
|
||||
copyFields(from, to, true, collectFieldsToCopy(from::class.java, false).filter(predicate))
|
||||
|
||||
private fun <From : Any, To : Any> copyFields(from: From, to: To, deepCopyWhenNeeded: Boolean, fieldsToCopy: List<Field>): To {
|
||||
if (from == to) return to
|
||||
|
||||
for (fromField in fieldsToCopy) {
|
||||
val toField = to.javaClass.getField(fromField.name)
|
||||
val toField = to::class.java.getField(fromField.name)
|
||||
val fromValue = fromField.get(from)
|
||||
toField.set(to, if (deepCopyWhenNeeded) fromValue?.copyValueIfNeeded() else fromValue)
|
||||
}
|
||||
@@ -72,14 +72,14 @@ private fun Any.copyValueIfNeeded(): Any {
|
||||
is DoubleArray -> Arrays.copyOf(this, size)
|
||||
is BooleanArray -> Arrays.copyOf(this, size)
|
||||
|
||||
is Array<*> -> java.lang.reflect.Array.newInstance(javaClass.componentType, size).apply {
|
||||
is Array<*> -> java.lang.reflect.Array.newInstance(this::class.java.componentType, size).apply {
|
||||
this as Array<Any?>
|
||||
(this@copyValueIfNeeded as Array<Any?>).forEachIndexed { i, value -> this[i] = value?.copyValueIfNeeded() }
|
||||
}
|
||||
|
||||
is MutableCollection<*> -> (this as Collection<Any?>).mapTo(javaClass.newInstance() as MutableCollection<Any?>) { it?.copyValueIfNeeded() }
|
||||
is MutableCollection<*> -> (this as Collection<Any?>).mapTo(this::class.java.newInstance() as MutableCollection<Any?>) { it?.copyValueIfNeeded() }
|
||||
|
||||
is MutableMap<*, *> -> (javaClass.newInstance() as MutableMap<Any?, Any?>).apply {
|
||||
is MutableMap<*, *> -> (this::class.java.newInstance() as MutableMap<Any?, Any?>).apply {
|
||||
for ((k, v) in this@copyValueIfNeeded.entries) {
|
||||
put(k?.copyValueIfNeeded(), v?.copyValueIfNeeded())
|
||||
}
|
||||
@@ -89,7 +89,7 @@ private fun Any.copyValueIfNeeded(): Any {
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectFieldsToCopy(clazz: Class<*>, inheritedOnly: Boolean): List<Field> {
|
||||
fun collectFieldsToCopy(clazz: Class<*>, inheritedOnly: Boolean): List<Field> {
|
||||
val fromFields = ArrayList<Field>()
|
||||
|
||||
var currentClass: Class<*>? = if (inheritedOnly) clazz.superclass else clazz
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Sam Pullara. All Rights Reserved.
|
||||
* You may modify and redistribute as long as this attribution remains.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli;
|
||||
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class Args {
|
||||
|
||||
/**
|
||||
* {@link ValueCreator} building object using a one arg constructor taking a {@link String} object as parameter
|
||||
*/
|
||||
public static final ValueCreator FROM_STRING_CONSTRUCTOR = new ValueCreator() {
|
||||
public Object createValue(Class<?> type, String value) {
|
||||
Object v = null;
|
||||
try {
|
||||
Constructor<?> init = type.getDeclaredConstructor(String.class);
|
||||
v = init.newInstance(value);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// ignore
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Failed to convert " + value + " to type " + type.getName(), e);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
};
|
||||
public static final ValueCreator ENUM_CREATOR = new ValueCreator() {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Object createValue(Class type, String value) {
|
||||
if (Enum.class.isAssignableFrom(type)) {
|
||||
return Enum.valueOf(type, value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
private static final List<ValueCreator> DEFAULT_VALUE_CREATORS = Arrays.asList(Args.FROM_STRING_CONSTRUCTOR, Args.ENUM_CREATOR);
|
||||
private static List<ValueCreator> valueCreators = new ArrayList<Args.ValueCreator>(DEFAULT_VALUE_CREATORS);
|
||||
|
||||
/**
|
||||
* A convenience method for parsing and automatically producing error messages.
|
||||
*
|
||||
* @param target Either an instance or a class
|
||||
* @param args The arguments you want to parse and populate
|
||||
* @return The list of arguments that were not consumed
|
||||
*/
|
||||
public static List<String> parseOrExit(Object target, String[] args) {
|
||||
try {
|
||||
return parse(target, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println(e.getMessage());
|
||||
Args.usage(target);
|
||||
System.exit(1);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> parse(Object target, String[] args) {
|
||||
return parse(target, args, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a set of arguments and populate the target with the appropriate values.
|
||||
*
|
||||
* @param target
|
||||
* Either an instance or a class
|
||||
* @param args
|
||||
* The arguments you want to parse and populate
|
||||
* @param failOnExtraFlags
|
||||
* Throw an IllegalArgumentException if extra flags are present
|
||||
* @return The list of arguments that were not consumed
|
||||
*/
|
||||
public static List<String> parse(Object target, String[] args, boolean failOnExtraFlags) {
|
||||
List<String> arguments = new ArrayList<String>();
|
||||
arguments.addAll(Arrays.asList(args));
|
||||
Class<?> clazz;
|
||||
if (target instanceof Class) {
|
||||
clazz = (Class) target;
|
||||
} else {
|
||||
clazz = target.getClass();
|
||||
try {
|
||||
BeanInfo info = Introspector.getBeanInfo(clazz);
|
||||
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
|
||||
processProperty(target, pd, arguments);
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
// If its not a JavaBean we ignore it
|
||||
}
|
||||
}
|
||||
|
||||
// Check fields of 'target' class and its superclasses
|
||||
for (Class<?> currentClazz = clazz; currentClazz != null; currentClazz = currentClazz.getSuperclass()) {
|
||||
for (Field field : currentClazz.getDeclaredFields()) {
|
||||
processField(target, field, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
if (failOnExtraFlags) {
|
||||
for (String argument : arguments) {
|
||||
if (argument.startsWith("-")) {
|
||||
throw new IllegalArgumentException("Invalid argument: " + argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private static void processField(Object target, Field field, List<String> arguments) {
|
||||
Argument argument = field.getAnnotation(Argument.class);
|
||||
if (argument != null) {
|
||||
boolean set = false;
|
||||
for (Iterator<String> i = arguments.iterator(); i.hasNext(); ) {
|
||||
String arg = i.next();
|
||||
String prefix = argument.prefix();
|
||||
String delimiter = argument.delimiter();
|
||||
if (arg.startsWith(prefix)) {
|
||||
Object value;
|
||||
String name = getName(argument, field);
|
||||
String alias = getAlias(argument);
|
||||
arg = arg.substring(prefix.length());
|
||||
Class<?> type = field.getType();
|
||||
if (arg.equals(name) || (alias != null && arg.equals(alias))) {
|
||||
i.remove();
|
||||
value = consumeArgumentValue(type, argument, i);
|
||||
if (!set) {
|
||||
setField(type, field, target, value, delimiter);
|
||||
} else {
|
||||
addArgument(type, field, target, value, delimiter);
|
||||
}
|
||||
set = true;
|
||||
}
|
||||
if (set && !type.isArray()) break;
|
||||
}
|
||||
}
|
||||
if (!set && argument.required()) {
|
||||
String name = getName(argument, field);
|
||||
throw new IllegalArgumentException("You must set argument " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addArgument(Class type, Field field, Object target, Object value, String delimiter) {
|
||||
try {
|
||||
Object[] os = (Object[]) field.get(target);
|
||||
Object[] vs = (Object[]) getValue(type, value, delimiter);
|
||||
Object[] s = (Object[]) Array.newInstance(type.getComponentType(), os.length + vs.length);
|
||||
System.arraycopy(os, 0, s, 0, os.length);
|
||||
System.arraycopy(vs, 0, s, os.length, vs.length);
|
||||
field.set(target, s);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException("Could not set field " + field, iae);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addPropertyArgument(Class type, PropertyDescriptor property, Object target, Object value, String delimiter) {
|
||||
try {
|
||||
Object[] os = (Object[]) property.getReadMethod().invoke(target);
|
||||
Object[] vs = (Object[]) getValue(type, value, delimiter);
|
||||
Object[] s = (Object[]) Array.newInstance(type.getComponentType(), os.length + vs.length);
|
||||
System.arraycopy(os, 0, s, 0, os.length);
|
||||
System.arraycopy(vs, 0, s, os.length, vs.length);
|
||||
property.getWriteMethod().invoke(target, (Object) s);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException("Could not set property " + property, iae);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException("Failed to validate argument " + value + " for " + property);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processProperty(Object target, PropertyDescriptor property, List<String> arguments) {
|
||||
Method writeMethod = property.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
Argument argument = writeMethod.getAnnotation(Argument.class);
|
||||
if (argument != null) {
|
||||
boolean set = false;
|
||||
for (Iterator<String> i = arguments.iterator(); i.hasNext(); ) {
|
||||
String arg = i.next();
|
||||
String prefix = argument.prefix();
|
||||
String delimiter = argument.delimiter();
|
||||
if (arg.startsWith(prefix)) {
|
||||
Object value;
|
||||
String name = getName(argument, property);
|
||||
String alias = getAlias(argument);
|
||||
arg = arg.substring(prefix.length());
|
||||
Class<?> type = property.getPropertyType();
|
||||
if (arg.equals(name) || (alias != null && arg.equals(alias))) {
|
||||
i.remove();
|
||||
value = consumeArgumentValue(type, argument, i);
|
||||
if (!set) {
|
||||
setProperty(type, property, target, value, delimiter);
|
||||
} else {
|
||||
addPropertyArgument(type, property, target, value, delimiter);
|
||||
}
|
||||
set = true;
|
||||
}
|
||||
if (set && !type.isArray()) break;
|
||||
}
|
||||
}
|
||||
if (!set && argument.required()) {
|
||||
String name = getName(argument, property);
|
||||
throw new IllegalArgumentException("You must set argument " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate usage information based on the target annotations.
|
||||
*
|
||||
* @param target An instance or class.
|
||||
*/
|
||||
public static void usage(Object target) {
|
||||
usage(System.err, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate usage information based on the target annotations.
|
||||
*
|
||||
* @param errStream A {@link java.io.PrintStream} to print the usage information to.
|
||||
* @param target An instance or class.
|
||||
*/
|
||||
public static void usage(PrintStream errStream, Object target) {
|
||||
Class<?> clazz;
|
||||
if (target instanceof Class) {
|
||||
clazz = (Class) target;
|
||||
} else {
|
||||
clazz = target.getClass();
|
||||
}
|
||||
errStream.println("Usage: " + clazz.getName());
|
||||
for (Class<?> currentClazz = clazz; currentClazz != null; currentClazz = currentClazz.getSuperclass()) {
|
||||
for (Field field : currentClazz.getDeclaredFields()) {
|
||||
fieldUsage(errStream, target, field);
|
||||
}
|
||||
}
|
||||
try {
|
||||
BeanInfo info = Introspector.getBeanInfo(clazz);
|
||||
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
|
||||
propertyUsage(errStream, target, pd);
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
// If its not a JavaBean we ignore it
|
||||
}
|
||||
}
|
||||
|
||||
private static void fieldUsage(PrintStream errStream, Object target, Field field) {
|
||||
Argument argument = field.getAnnotation(Argument.class);
|
||||
if (argument != null) {
|
||||
String name = getName(argument, field);
|
||||
String alias = getAlias(argument);
|
||||
String prefix = argument.prefix();
|
||||
String delimiter = argument.delimiter();
|
||||
String description = argument.description();
|
||||
makeAccessible(field);
|
||||
try {
|
||||
Object defaultValue = field.get(target);
|
||||
Class<?> type = field.getType();
|
||||
propertyUsage(errStream, prefix, name, alias, type, delimiter, description, defaultValue);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("Could not use thie field " + field + " as an argument field", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void propertyUsage(PrintStream errStream, Object target, PropertyDescriptor field) {
|
||||
Method writeMethod = field.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
Argument argument = writeMethod.getAnnotation(Argument.class);
|
||||
if (argument != null) {
|
||||
String name = getName(argument, field);
|
||||
String alias = getAlias(argument);
|
||||
String prefix = argument.prefix();
|
||||
String delimiter = argument.delimiter();
|
||||
String description = argument.description();
|
||||
try {
|
||||
Method readMethod = field.getReadMethod();
|
||||
Object defaultValue;
|
||||
if (readMethod == null) {
|
||||
defaultValue = null;
|
||||
} else {
|
||||
defaultValue = readMethod.invoke(target, (Object[]) null);
|
||||
}
|
||||
Class<?> type = field.getPropertyType();
|
||||
propertyUsage(errStream, prefix, name, alias, type, delimiter, description, defaultValue);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("Could not use thie field " + field + " as an argument field", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException("Could not get default value for " + field, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void propertyUsage(PrintStream errStream, String prefix, String name, String alias, Class<?> type, String delimiter, String description, Object defaultValue) {
|
||||
StringBuilder sb = new StringBuilder(" ");
|
||||
sb.append(prefix);
|
||||
sb.append(name);
|
||||
if (alias != null) {
|
||||
sb.append(" (");
|
||||
sb.append(prefix);
|
||||
sb.append(alias);
|
||||
sb.append(")");
|
||||
}
|
||||
if (type == Boolean.TYPE || type == Boolean.class) {
|
||||
sb.append(" [flag] ");
|
||||
sb.append(description);
|
||||
} else {
|
||||
sb.append(" [");
|
||||
if (type.isArray()) {
|
||||
String typeName = getTypeName(type.getComponentType());
|
||||
sb.append(typeName);
|
||||
sb.append("[");
|
||||
sb.append(delimiter);
|
||||
sb.append("]");
|
||||
} else {
|
||||
String typeName = getTypeName(type);
|
||||
sb.append(typeName);
|
||||
}
|
||||
sb.append("] ");
|
||||
sb.append(description);
|
||||
if (defaultValue != null) {
|
||||
sb.append(" (");
|
||||
if (type.isArray()) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
int len = Array.getLength(defaultValue);
|
||||
for (int i = 0; i < len; i++) {
|
||||
list.add(Array.get(defaultValue, i));
|
||||
}
|
||||
sb.append(list);
|
||||
} else {
|
||||
sb.append(defaultValue);
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
|
||||
}
|
||||
errStream.println(sb);
|
||||
}
|
||||
|
||||
private static String getTypeName(Class<?> type) {
|
||||
String typeName = type.getName();
|
||||
int beginIndex = typeName.lastIndexOf(".");
|
||||
typeName = typeName.substring(beginIndex + 1);
|
||||
return typeName;
|
||||
}
|
||||
|
||||
static String getName(Argument argument, PropertyDescriptor property) {
|
||||
String name = argument.value();
|
||||
if (name.equals("")) {
|
||||
name = property.getName();
|
||||
}
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
private static Object consumeArgumentValue(Class<?> type, Argument argument, Iterator<String> i) {
|
||||
Object value;
|
||||
if (type == Boolean.TYPE || type == Boolean.class) {
|
||||
value = true;
|
||||
} else {
|
||||
if (i.hasNext()) {
|
||||
value = i.next();
|
||||
i.remove();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Must have a value for non-boolean argument " + argument.value());
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void setProperty(Class<?> type, PropertyDescriptor property, Object target, Object value, String delimiter) {
|
||||
try {
|
||||
value = getValue(type, value, delimiter);
|
||||
property.getWriteMethod().invoke(target, value);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException("Could not set property " + property, iae);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalArgumentException("Failed to validate argument " + value + " for " + property);
|
||||
}
|
||||
}
|
||||
|
||||
static String getAlias(Argument argument) {
|
||||
String alias = argument.alias();
|
||||
if (alias.equals("")) {
|
||||
alias = null;
|
||||
}
|
||||
return alias;
|
||||
}
|
||||
|
||||
static String getName(Argument argument, Field field) {
|
||||
String name = argument.value();
|
||||
if (name.equals("")) {
|
||||
name = field.getName();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static void setField(Class<?> type, Field field, Object target, Object value, String delimiter) {
|
||||
makeAccessible(field);
|
||||
try {
|
||||
value = getValue(type, value, delimiter);
|
||||
field.set(target, value);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new IllegalArgumentException("Could not set field " + field, iae);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("Could not find constructor in class " + type.getName() + " that takes a string", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getValue(Class<?> type, Object value, String delimiter) throws NoSuchMethodException {
|
||||
if (type != String.class && type != Boolean.class && type != Boolean.TYPE) {
|
||||
String string = (String) value;
|
||||
if (type.isArray()) {
|
||||
String[] strings = string.split(delimiter);
|
||||
type = type.getComponentType();
|
||||
if (type == String.class) {
|
||||
value = strings;
|
||||
} else {
|
||||
Object[] array = (Object[]) Array.newInstance(type, strings.length);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = createValue(type, strings[i]);
|
||||
}
|
||||
value = array;
|
||||
}
|
||||
} else {
|
||||
value = createValue(type, string);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Object createValue(Class<?> type, String valueAsString) throws NoSuchMethodException {
|
||||
for (ValueCreator valueCreator : valueCreators) {
|
||||
Object createdValue = valueCreator.createValue(type, valueAsString);
|
||||
if (createdValue != null) {
|
||||
return createdValue;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.format("cannot instanciate any %s object using %s value", type.toString(), valueAsString));
|
||||
}
|
||||
|
||||
private static void makeAccessible(AccessibleObject ao) {
|
||||
if (ao instanceof Member) {
|
||||
Member member = (Member) ao;
|
||||
if (!Modifier.isPublic(member.getModifiers())) {
|
||||
ao.setAccessible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ValueCreator} object able to create object assignable from given type,
|
||||
* using a static one arg method which name is the the given one taking a String object as parameter
|
||||
*
|
||||
* @param compatibleType the base assignable for which this object will try to invoke the given method
|
||||
* @param methodName the name of the one arg method taking a String as parameter that will be used to built a new value
|
||||
* @return null if the object could not be created, the value otherwise
|
||||
*/
|
||||
public static ValueCreator byStaticMethodInvocation(final Class<?> compatibleType, final String methodName) {
|
||||
return new ValueCreator() {
|
||||
public Object createValue(Class<?> type, String value) {
|
||||
Object v = null;
|
||||
if (compatibleType.isAssignableFrom(type)) {
|
||||
try {
|
||||
Method m = type.getMethod(methodName, String.class);
|
||||
return m.invoke(null, value);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// ignore
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(String.format("could not invoke %s#%s to create an obejct from %s", type.toString(), methodName, value));
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows external extension of the valiue creators.
|
||||
*
|
||||
* @param vc another value creator to take into account for trying to create values
|
||||
*/
|
||||
public static void registerValueCreator(ValueCreator vc) {
|
||||
valueCreators.add(vc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup of registered ValueCreators (mainly for tests)
|
||||
*/
|
||||
public static void resetValueCreators() {
|
||||
valueCreators.clear();
|
||||
valueCreators.addAll(DEFAULT_VALUE_CREATORS);
|
||||
}
|
||||
|
||||
public static interface ValueCreator {
|
||||
/**
|
||||
* Creates a value object of the given type using the given string value representation;
|
||||
*
|
||||
* @param type the type to create an instance of
|
||||
* @param value the string represented value of the object to create
|
||||
* @return null if the object could not be created, the value otherwise
|
||||
*/
|
||||
public Object createValue(Class<?> type, String value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Sam Pullara. All Rights Reserved.
|
||||
* You may modify and redistribute as long as this attribution remains.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Argument {
|
||||
/**
|
||||
* This is the actual command line argument itself
|
||||
*/
|
||||
String value() default "";
|
||||
/**
|
||||
* If this is true, then the argument must be set or the parse will fail
|
||||
*/
|
||||
boolean required() default false;
|
||||
|
||||
/**
|
||||
* This is the prefix expected for the argument
|
||||
*/
|
||||
String prefix() default "-";
|
||||
/**
|
||||
* Each argument can have an alias
|
||||
*/
|
||||
String alias() default "";
|
||||
/**
|
||||
* A description of the argument that will appear in the usage method
|
||||
*/
|
||||
String description() default "";
|
||||
|
||||
/**
|
||||
* A delimiter for arguments that are multi-valued.
|
||||
*/
|
||||
String delimiter() default ",";
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* <p/>
|
||||
* User: samp
|
||||
* Date: Nov 11, 2007
|
||||
* Time: 3:42:27 PM
|
||||
*/
|
||||
public class PropertiesArgs {
|
||||
/**
|
||||
* Parse properties instead of String arguments. Any additional arguments need to be passed some other way.
|
||||
* This is often used in a second pass when the property filename is passed on the command line. Because of
|
||||
* required properties you must be careful to set them all in the property file.
|
||||
*
|
||||
* @param target Either an instance or a class
|
||||
* @param arguments The properties that contain the arguments
|
||||
*/
|
||||
public static void parse(Object target, Properties arguments) {
|
||||
Class clazz;
|
||||
if (target instanceof Class) {
|
||||
clazz = (Class) target;
|
||||
} else {
|
||||
clazz = target.getClass();
|
||||
}
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
processField(target, field, arguments);
|
||||
}
|
||||
try {
|
||||
BeanInfo info = Introspector.getBeanInfo(clazz);
|
||||
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
|
||||
processProperty(target, pd, arguments);
|
||||
}
|
||||
} catch (IntrospectionException e) {
|
||||
// If its not a JavaBean we ignore it
|
||||
}
|
||||
}
|
||||
|
||||
private static void processField(Object target, Field field, Properties arguments) {
|
||||
Argument argument = field.getAnnotation(Argument.class);
|
||||
if (argument != null) {
|
||||
String name = Args.getName(argument, field);
|
||||
String alias = Args.getAlias(argument);
|
||||
Class type = field.getType();
|
||||
Object value = arguments.get(name);
|
||||
if (value == null && alias != null) {
|
||||
value = arguments.get(alias);
|
||||
}
|
||||
if (value != null) {
|
||||
if (type == Boolean.TYPE || type == Boolean.class) {
|
||||
value = true;
|
||||
}
|
||||
Args.setField(type, field, target, value, argument.delimiter());
|
||||
} else {
|
||||
if (argument.required()) {
|
||||
throw new IllegalArgumentException("You must set argument " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processProperty(Object target, PropertyDescriptor property, Properties arguments) {
|
||||
Method writeMethod = property.getWriteMethod();
|
||||
if (writeMethod != null) {
|
||||
Argument argument = writeMethod.getAnnotation(Argument.class);
|
||||
if (argument != null) {
|
||||
String name = Args.getName(argument, property);
|
||||
String alias = Args.getAlias(argument);
|
||||
Object value = arguments.get(name);
|
||||
if (value == null && alias != null) {
|
||||
value = arguments.get(alias);
|
||||
}
|
||||
if (value != null) {
|
||||
Class type = property.getPropertyType();
|
||||
if (type == Boolean.TYPE || type == Boolean.class) {
|
||||
value = true;
|
||||
}
|
||||
Args.setProperty(type, property, target, value, argument.delimiter());
|
||||
} else {
|
||||
if (argument.required()) {
|
||||
throw new IllegalArgumentException("You must set argument " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
val evalState = state.asState(GenericReplEvaluatorState::class.java)
|
||||
return evalState.history.map { it.item }.filter { it.instance != null }.reversed().ensureNotEmpty("no script ").let { history ->
|
||||
if (receiverInstance != null) {
|
||||
val receiverKlass = receiverClass ?: receiverInstance.javaClass.kotlin
|
||||
val receiverKlass = receiverClass ?: receiverInstance::class.java.kotlin
|
||||
val receiverInHistory = history.find { it.instance == receiverInstance } ?:
|
||||
EvalClassWithInstanceAndLoader(receiverKlass, receiverInstance, receiverKlass.java.classLoader, history.first().invokeWrapper)
|
||||
listOf(receiverInHistory) + history.filterNot { it == receiverInHistory }
|
||||
@@ -54,7 +54,7 @@ interface KotlinJsr223JvmInvocableScriptEngine : Invocable {
|
||||
override fun invokeMethod(thiz: Any?, name: String?, vararg args: Any?): Any? {
|
||||
if (name == null) throw java.lang.NullPointerException("method name cannot be null")
|
||||
if (thiz == null) throw IllegalArgumentException("cannot invoke method on the null object")
|
||||
return invokeImpl(prioritizedHistory(thiz.javaClass.kotlin, thiz), name, args)
|
||||
return invokeImpl(prioritizedHistory(thiz::class.java.kotlin, thiz), name, args)
|
||||
}
|
||||
|
||||
private fun invokeImpl(prioritizedCallOrder: List<EvalClassWithInstanceAndLoader>, name: String, args: Array<out Any?>): Any? {
|
||||
|
||||
@@ -26,7 +26,7 @@ fun makeScriptBaseName(codeLine: ReplCodeLine) =
|
||||
fun renderReplStackTrace(cause: Throwable, startFromMethodName: String): String {
|
||||
val newTrace = arrayListOf<StackTraceElement>()
|
||||
var skip = true
|
||||
for ((_, element) in cause.stackTrace.withIndex().reversed()) {
|
||||
for (element in cause.stackTrace.reversed()) {
|
||||
if ("${element.className}.${element.methodName}" == startFromMethodName) {
|
||||
skip = false
|
||||
}
|
||||
|
||||
@@ -40,8 +40,9 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat
|
||||
import org.jetbrains.kotlin.utils.StringsKt;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.jetbrains.kotlin.cli.common.ExitCode.*;
|
||||
@@ -155,6 +156,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
}
|
||||
|
||||
reportUnknownExtraFlags(messageCollector, arguments);
|
||||
reportUnsupportedJavaVersion(messageCollector, arguments);
|
||||
|
||||
GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector);
|
||||
|
||||
@@ -232,8 +234,6 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
configuration.put(CLIConfigurationKeys.COMPILER_JAR_LOCATOR, locator);
|
||||
}
|
||||
|
||||
configuration.put(CommonConfigurationKeys.SKIP_METADATA_VERSION_CHECK, arguments.skipMetadataVersionCheck);
|
||||
|
||||
setupLanguageVersionSettings(configuration, arguments);
|
||||
}
|
||||
|
||||
@@ -247,11 +247,15 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
// If only "-api-version" is specified, language version is assumed to be the latest
|
||||
languageVersion = LanguageVersion.LATEST;
|
||||
}
|
||||
|
||||
if (apiVersion == null) {
|
||||
// If only "-language-version" is specified, API version is assumed to be equal to the language version
|
||||
// (API version cannot be greater than the language version)
|
||||
apiVersion = languageVersion;
|
||||
}
|
||||
else {
|
||||
configuration.put(CLIConfigurationKeys.IS_API_VERSION_EXPLICIT, true);
|
||||
}
|
||||
|
||||
if (apiVersion.compareTo(languageVersion) > 0) {
|
||||
configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY).report(
|
||||
@@ -262,40 +266,33 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
);
|
||||
}
|
||||
|
||||
List<LanguageFeature> extraLanguageFeatures = new ArrayList<LanguageFeature>(0);
|
||||
Map<LanguageFeature, LanguageFeature.State> extraLanguageFeatures = new HashMap<LanguageFeature, LanguageFeature.State>(0);
|
||||
if (arguments.multiPlatform) {
|
||||
extraLanguageFeatures.add(LanguageFeature.MultiPlatformProjects);
|
||||
}
|
||||
if (arguments.noCheckImpl) {
|
||||
extraLanguageFeatures.add(LanguageFeature.MultiPlatformDoNotCheckImpl);
|
||||
extraLanguageFeatures.put(LanguageFeature.MultiPlatformProjects, LanguageFeature.State.ENABLED);
|
||||
}
|
||||
|
||||
LanguageFeature coroutinesApplicabilityLevel = chooseCoroutinesApplicabilityLevel(configuration, arguments);
|
||||
if (coroutinesApplicabilityLevel != null) {
|
||||
extraLanguageFeatures.add(coroutinesApplicabilityLevel);
|
||||
LanguageFeature.State coroutinesState = chooseCoroutinesApplicabilityLevel(configuration, arguments);
|
||||
if (coroutinesState != null) {
|
||||
extraLanguageFeatures.put(LanguageFeature.Coroutines, coroutinesState);
|
||||
}
|
||||
|
||||
CommonConfigurationKeysKt.setLanguageVersionSettings(
|
||||
configuration,
|
||||
new LanguageVersionSettingsImpl(
|
||||
languageVersion,
|
||||
ApiVersion.createByLanguageVersion(apiVersion),
|
||||
extraLanguageFeatures,
|
||||
arguments.apiVersion != null
|
||||
)
|
||||
);
|
||||
LanguageVersionSettingsImpl settings =
|
||||
new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion), extraLanguageFeatures);
|
||||
settings.switchFlag(AnalysisFlags.getSkipMetadataVersionCheck(), arguments.skipMetadataVersionCheck);
|
||||
settings.switchFlag(AnalysisFlags.getMultiPlatformDoNotCheckImpl(), arguments.noCheckImpl);
|
||||
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, settings);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LanguageFeature chooseCoroutinesApplicabilityLevel(
|
||||
private static LanguageFeature.State chooseCoroutinesApplicabilityLevel(
|
||||
@NotNull CompilerConfiguration configuration,
|
||||
@NotNull CommonCompilerArguments arguments
|
||||
) {
|
||||
if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) {
|
||||
return LanguageFeature.ErrorOnCoroutines;
|
||||
return LanguageFeature.State.ENABLED_WITH_ERROR;
|
||||
}
|
||||
else if (arguments.coroutinesEnable && !arguments.coroutinesWarn && !arguments.coroutinesError) {
|
||||
return LanguageFeature.DoNotWarnOnCoroutines;
|
||||
return LanguageFeature.State.ENABLED;
|
||||
}
|
||||
else if (!arguments.coroutinesEnable && !arguments.coroutinesError) {
|
||||
return null;
|
||||
@@ -350,6 +347,16 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
||||
}
|
||||
}
|
||||
|
||||
private void reportUnsupportedJavaVersion(MessageCollector collector, A arguments) {
|
||||
if (!SystemInfo.isJavaVersionAtLeast("1.8") && !arguments.noJavaVersionWarning) {
|
||||
collector.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Running the Kotlin compiler under Java 6 or 7 is unsupported and will no longer be possible in a future update.",
|
||||
CompilerMessageLocation.NO_LOCATION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract ExitCode doExecute(
|
||||
@NotNull A arguments,
|
||||
|
||||
@@ -29,6 +29,8 @@ public class CLIConfigurationKeys {
|
||||
CompilerConfigurationKey.create("allow kotlin package");
|
||||
public static final CompilerConfigurationKey<Boolean> REPORT_PERF =
|
||||
CompilerConfigurationKey.create("report performance information");
|
||||
public static final CompilerConfigurationKey<Boolean> IS_API_VERSION_EXPLICIT =
|
||||
CompilerConfigurationKey.create("is API version explicit");
|
||||
|
||||
// Used in Eclipse plugin (see KotlinCLICompiler)
|
||||
public static final CompilerConfigurationKey<CompilerJarLocator> COMPILER_JAR_LOCATOR =
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common;
|
||||
|
||||
import com.sampullara.cli.Argument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.ValueDescription;
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@@ -67,7 +67,7 @@ class ModuleVisibilityHelperImpl : ModuleVisibilityHelper {
|
||||
private fun findModule(descriptor: DeclarationDescriptor, modules: Collection<Module>): Module? {
|
||||
val sourceElement = getSourceElement(descriptor)
|
||||
if (sourceElement is KotlinSourceElement) {
|
||||
return modules.singleOrNull() ?: modules.firstOrNull { sourceElement.psi.getContainingKtFile().virtualFile.path in it.getSourceFiles() }
|
||||
return modules.singleOrNull() ?: modules.firstOrNull { sourceElement.psi.containingKtFile.virtualFile.path in it.getSourceFiles() }
|
||||
}
|
||||
else {
|
||||
return modules.firstOrNull { module ->
|
||||
|
||||
@@ -34,14 +34,14 @@ fun OutputFileCollection.writeAll(outputDir: File, report: (file: OutputFile, so
|
||||
}
|
||||
}
|
||||
|
||||
private val REPORT_NOTHING = { file: OutputFile, sources: List<File>, output: File -> }
|
||||
private val REPORT_NOTHING: (OutputFile, List<File>, File) -> Unit = { _, _, _ -> }
|
||||
|
||||
fun OutputFileCollection.writeAllTo(outputDir: File) {
|
||||
writeAll(outputDir, REPORT_NOTHING)
|
||||
}
|
||||
|
||||
fun OutputFileCollection.writeAll(outputDir: File, messageCollector: MessageCollector) {
|
||||
writeAll(outputDir) { file, sources, output ->
|
||||
writeAll(outputDir) { _, sources, output ->
|
||||
messageCollector.report(CompilerMessageSeverity.OUTPUT, OutputMessageUtil.formatOutputMessage(sources, output), CompilerMessageLocation.NO_LOCATION)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.jvm
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
@@ -120,19 +121,16 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
val actualApi = ApiVersion.parse(actualRuntimeVersion.toString())
|
||||
if (actualApi != null) {
|
||||
val inferredApiVersion =
|
||||
if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit)
|
||||
if (configuration.getBoolean(CLIConfigurationKeys.IS_API_VERSION_EXPLICIT))
|
||||
languageVersionSettings.apiVersion
|
||||
else
|
||||
// "minOf" is needed in case when API version was inferred from language version and it's older than actualApi.
|
||||
// For example, in "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0
|
||||
minOf(languageVersionSettings.apiVersion, actualApi)
|
||||
|
||||
// "minOf" is needed in case when API version was inferred from language version and it's older than actualApi.
|
||||
// For example, in "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0
|
||||
val newSettings = LanguageVersionSettingsImpl(
|
||||
languageVersionSettings.languageVersion,
|
||||
inferredApiVersion,
|
||||
languageVersionSettings.additionalFeatures,
|
||||
isApiVersionExplicit = false
|
||||
)
|
||||
val newSettings = object : LanguageVersionSettings by languageVersionSettings {
|
||||
override val apiVersion: ApiVersion get() = inferredApiVersion
|
||||
}
|
||||
|
||||
messageCollector.issue(null, "Old runtime has been found in the classpath. " +
|
||||
"Initial language version settings: $languageVersionSettings. " +
|
||||
|
||||
@@ -35,7 +35,7 @@ object PluginCliParser {
|
||||
?.map { File(it).toURI().toURL() }
|
||||
?.toTypedArray()
|
||||
?: arrayOf<URL>(),
|
||||
javaClass.classLoader
|
||||
this::class.java.classLoader
|
||||
)
|
||||
|
||||
val componentRegistrars = ServiceLoader.load(ComponentRegistrar::class.java, classLoader).toMutableList()
|
||||
@@ -50,7 +50,7 @@ object PluginCliParser {
|
||||
configuration: CompilerConfiguration,
|
||||
classLoader: ClassLoader
|
||||
) {
|
||||
val optionValuesByPlugin = arguments.pluginOptions?.map { parsePluginOption(it) }?.groupBy {
|
||||
val optionValuesByPlugin = arguments.pluginOptions?.map(::parsePluginOption)?.groupBy {
|
||||
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
|
||||
it.pluginId
|
||||
} ?: mapOf()
|
||||
|
||||
@@ -25,8 +25,9 @@ import com.intellij.util.Function
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.LightClassBuilder
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
|
||||
import org.jetbrains.kotlin.asJava.builder.*
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
||||
@@ -44,7 +45,6 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/**
|
||||
@@ -76,10 +76,20 @@ class CliLightClassGenerationSupport(project: Project) : LightClassGenerationSup
|
||||
trace.setKotlinCodeAnalyzer(codeAnalyzer)
|
||||
}
|
||||
|
||||
override fun getContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
|
||||
override fun createDataHolderForClass(
|
||||
classOrObject: KtClassOrObject, builder: LightClassBuilder
|
||||
): LightClassDataHolder.ForClass {
|
||||
//force resolve companion for light class generation
|
||||
bindingContext.get(BindingContext.CLASS, classOrObject)?.companionObjectDescriptor
|
||||
return LightClassConstructionContext(bindingContext, module)
|
||||
|
||||
val (stub, bindingContext, diagnostics) = builder(getContext())
|
||||
|
||||
bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassDataHolder
|
||||
|
||||
return LightClassDataHolderImpl(
|
||||
stub,
|
||||
diagnostics
|
||||
)
|
||||
}
|
||||
|
||||
private fun getContext(): LightClassConstructionContext {
|
||||
@@ -147,7 +157,7 @@ class CliLightClassGenerationSupport(project: Project) : LightClassGenerationSup
|
||||
val filesForFacade = findFilesForFacade(facadeFqName, scope)
|
||||
if (filesForFacade.isEmpty()) return emptyList()
|
||||
|
||||
return emptyOrSingletonList<PsiClass>(
|
||||
return listOfNotNull<PsiClass>(
|
||||
KtLightClassForFacade.createForFacade(psiManager, facadeFqName, scope, filesForFacade))
|
||||
}
|
||||
|
||||
@@ -164,8 +174,9 @@ class CliLightClassGenerationSupport(project: Project) : LightClassGenerationSup
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContextForFacade(files: Collection<KtFile>): LightClassConstructionContext {
|
||||
return getContext()
|
||||
override fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade {
|
||||
val (stub, _, diagnostics) = builder(getContext())
|
||||
return LightClassDataHolderImpl(stub, diagnostics)
|
||||
}
|
||||
|
||||
override fun createTrace(): BindingTraceContext {
|
||||
|
||||
@@ -21,18 +21,17 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClassFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import java.io.InputStream
|
||||
|
||||
class JvmCliVirtualFileFinder(
|
||||
class CliVirtualFileFinder(
|
||||
private val index: JvmDependenciesIndex,
|
||||
private val scope: GlobalSearchScope
|
||||
) : VirtualFileKotlinClassFinder() {
|
||||
) : VirtualFileFinder() {
|
||||
override fun findVirtualFileWithHeader(classId: ClassId): VirtualFile? =
|
||||
findBinaryClass(classId, classId.relativeClassName.asString().replace('.', '$') + ".class")
|
||||
|
||||
@@ -61,6 +60,6 @@ class JvmCliVirtualFileFinder(
|
||||
|
||||
private fun findBinaryClass(classId: ClassId, fileName: String): VirtualFile? =
|
||||
index.findClass(classId, acceptedRootTypes = JavaRoot.OnlyBinary) { dir, _ ->
|
||||
dir.findChild(fileName)?.check(VirtualFile::isValid)
|
||||
}?.check { it in scope }
|
||||
dir.findChild(fileName)?.takeIf(VirtualFile::isValid)
|
||||
}?.takeIf { it in scope }
|
||||
}
|
||||
@@ -18,10 +18,10 @@ package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinderFactory
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory
|
||||
|
||||
// TODO: create different JvmDependenciesIndex instances for different sets of source roots to improve performance
|
||||
class JvmCliVirtualFileFinderFactory(private val index: JvmDependenciesIndex) : JvmVirtualFileFinderFactory {
|
||||
override fun create(scope: GlobalSearchScope): JvmVirtualFileFinder = JvmCliVirtualFileFinder(index, scope)
|
||||
class CliVirtualFileFinderFactory(private val index: JvmDependenciesIndex) : VirtualFileFinderFactory {
|
||||
override fun create(scope: GlobalSearchScope): VirtualFileFinder = CliVirtualFileFinder(index, scope)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.load.kotlin.ModuleMapping
|
||||
import org.jetbrains.kotlin.load.kotlin.PackageParts
|
||||
@@ -33,7 +34,7 @@ class JvmPackagePartProvider(
|
||||
) : PackagePartProvider {
|
||||
private data class ModuleMappingInfo(val root: VirtualFile, val mapping: ModuleMapping)
|
||||
|
||||
private val deserializationConfiguration = CompilerDeserializationConfiguration(env.configuration)
|
||||
private val deserializationConfiguration = CompilerDeserializationConfiguration(env.configuration.languageVersionSettings)
|
||||
|
||||
private val notLoadedRoots by lazy(LazyThreadSafetyMode.NONE) {
|
||||
env.configuration.getList(JVMConfigurationKeys.CONTENT_ROOTS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user