mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-05-05 15:53:13 +00:00
Compare commits
107 Commits
master_bas
...
build-docs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70d275e8a4 | ||
|
|
61426063de | ||
|
|
7adc1abb2e | ||
|
|
7c9a1694ac | ||
|
|
0ecb78eba8 | ||
|
|
f55601a84d | ||
|
|
f6e291e642 | ||
|
|
a0f995827c | ||
|
|
ea9868bd4c | ||
|
|
c9e884edd5 | ||
|
|
e0e5656f12 | ||
|
|
7971a396f0 | ||
|
|
12c0d3b45a | ||
|
|
33ab5fe64a | ||
|
|
f0521a5bd4 | ||
|
|
3638611bf6 | ||
|
|
148d4e459f | ||
|
|
0110bff083 | ||
|
|
84d71446f2 | ||
|
|
235f60e56f | ||
|
|
962cc51c2e | ||
|
|
25483ca286 | ||
|
|
d63b7ade7e | ||
|
|
1a898e1f46 | ||
|
|
9674dc6279 | ||
|
|
f7906ed7ee | ||
|
|
461ebbc4cd | ||
|
|
c4ceb5653a | ||
|
|
a9f3940e47 | ||
|
|
c12be9b54b | ||
|
|
2472d1c1fd | ||
|
|
47fe7aefcb | ||
|
|
ad975b517e | ||
|
|
31189f8270 | ||
|
|
c8c4835d17 | ||
|
|
c43fe9b9ff | ||
|
|
88c952902d | ||
|
|
1b6f495014 | ||
|
|
3f9a068e5c | ||
|
|
bdd5310dce | ||
|
|
61e570279a | ||
|
|
c61034191e | ||
|
|
0bf6a37384 | ||
|
|
3e1482a869 | ||
|
|
6757212e0f | ||
|
|
70f3e1a53e | ||
|
|
cba7c848c2 | ||
|
|
63fbab77dd | ||
|
|
74567aaa43 | ||
|
|
caa010f0ff | ||
|
|
5f0b6a5fb6 | ||
|
|
0341227552 | ||
|
|
14099e8a66 | ||
|
|
480768c841 | ||
|
|
82b033b85d | ||
|
|
6c73373f64 | ||
|
|
1596622f60 | ||
|
|
49d040886f | ||
|
|
8a41561b36 | ||
|
|
ea33a0aa6c | ||
|
|
1729f175c1 | ||
|
|
83eb6dd000 | ||
|
|
fe60fb4a44 | ||
|
|
0c95783829 | ||
|
|
5b04b84e71 | ||
|
|
15f9fa7b03 | ||
|
|
52093f3fad | ||
|
|
c3a6cb3ab4 | ||
|
|
ee36a19db5 | ||
|
|
8e5026e6b1 | ||
|
|
31f08c6ba5 | ||
|
|
0a3f4b5be3 | ||
|
|
4e51e04e3c | ||
|
|
5740163c92 | ||
|
|
0331627757 | ||
|
|
ac85c9d728 | ||
|
|
95e8aa6c41 | ||
|
|
0132a54d22 | ||
|
|
512c2da560 | ||
|
|
0eb713920b | ||
|
|
67c25aa6ab | ||
|
|
ca0c2690a9 | ||
|
|
ac8ce070e8 | ||
|
|
0eeb47d8ff | ||
|
|
2ddea32b37 | ||
|
|
e5ae916fe5 | ||
|
|
f6be73cb3d | ||
|
|
ea142d884f | ||
|
|
080b4dccc9 | ||
|
|
dc40c34152 | ||
|
|
97d648731a | ||
|
|
97b9e1c444 | ||
|
|
71020fb690 | ||
|
|
83bdf55dda | ||
|
|
6528ced4b5 | ||
|
|
264107081f | ||
|
|
0f2ed5e570 | ||
|
|
8a664a0ac2 | ||
|
|
7cfc3ea46f | ||
|
|
a30d946351 | ||
|
|
0425afb061 | ||
|
|
9ceb9e729a | ||
|
|
5cf9937423 | ||
|
|
40dfee6d81 | ||
|
|
c4d2e0f519 | ||
|
|
8bef2ec503 | ||
|
|
fc0d0f2c48 |
1
.idea/codeStyles/Project.xml
generated
1
.idea/codeStyles/Project.xml
generated
@@ -227,6 +227,7 @@
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION" value="true" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
|
||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -6,7 +6,6 @@
|
||||
</properties>
|
||||
</component>
|
||||
<component name="EntryPointsManager">
|
||||
<entry_points version="2.0" />
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="javax.inject.Inject" />
|
||||
</list>
|
||||
|
||||
19
.idea/modules/kotlin.iml
generated
Normal file
19
.idea/modules/kotlin.iml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="kotlin" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="org.jetbrains.kotlin" external.system.module.version="1.1-SNAPSHOT" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../..">
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../dependencies" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../ideaSDK/androidSDK" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../ideaSDK/config" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../ideaSDK/config-idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../ideaSDK/system" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../ideaSDK/system-idea" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
2
.idea/scopes/IDE.xml
generated
2
.idea/scopes/IDE.xml
generated
@@ -1,3 +1,3 @@
|
||||
<component name="DependencyValidationManager">
|
||||
<scope name="IDE" pattern="file[idea_main]:*/||file[idea]:testData/*/||file[idea-formatter_main]:*/||file[ide-common_main]:*/||file[idea-android_main]:*/||file[idea-android-output-parser_main]:*/||file[idea]:idea-completion/testData/*/||file[idea-core_main]:*/||file[idea-gradle_main]:*/||file[idea-jps-common_main]:*/||file[idea-jvm_main]:*/||file[idea]:idea-live-templates/src/*/||file[idea]:idea-live-templates/testData/*/||file[idea-maven_main]:*/||file[idea-repl]:*/||file[idea-test-framework_main]:*/||file[kotlin-gradle-tooling_main]:*/" />
|
||||
<scope name="IDE" pattern="file[idea]:src/*/||file[idea]:testData/*/||file[ide-common]:src/*/||file[idea-analysis]:src/*/||file[idea-android]:src/*/||file[idea-completion]:src/*/||file[idea-completion]:testData/*/||file[idea-core]:src/*/||file[idea-jps-common]:/*/||file[idea-live-templates]:/*/||file[idea-live-templates]:testData/*/||file[idea-repl]:/*/||src[idea-android-output-parser]:*..*" />
|
||||
</component>
|
||||
207
ChangeLog.md
207
ChangeLog.md
@@ -3,6 +3,213 @@
|
||||
<!-- Find: ([^\`/\[])(KT-\d+) -->
|
||||
<!-- Replace: $1[`$2`](https://youtrack.jetbrains.com/issue/$2) -->
|
||||
|
||||
## 1.1.60
|
||||
|
||||
### Android
|
||||
|
||||
#### New Features
|
||||
|
||||
- [`KT-20051`](https://youtrack.jetbrains.com/issue/KT-20051) Quickfixes to support @Parcelize
|
||||
#### Fixes
|
||||
|
||||
- [`KT-19747`](https://youtrack.jetbrains.com/issue/KT-19747) Android extensions + Parcelable: VerifyError in case of RawValue annotation on a type when it's unknown how to parcel it
|
||||
- [`KT-19899`](https://youtrack.jetbrains.com/issue/KT-19899) Parcelize: Building with ProGuard enabled
|
||||
- [`KT-19988`](https://youtrack.jetbrains.com/issue/KT-19988) [Android Extensions] inner class LayoutContainer causes NoSuchMethodError
|
||||
- [`KT-20002`](https://youtrack.jetbrains.com/issue/KT-20002) Parcelize explodes on LongArray
|
||||
- [`KT-20019`](https://youtrack.jetbrains.com/issue/KT-20019) Parcelize does not propogate flags argument when writing nested Parcelable
|
||||
- [`KT-20020`](https://youtrack.jetbrains.com/issue/KT-20020) Parcelize does not use primitive array read/write methods on Parcel
|
||||
- [`KT-20021`](https://youtrack.jetbrains.com/issue/KT-20021) Parcelize does not serialize Parcelable enum as Parcelable
|
||||
- [`KT-20022`](https://youtrack.jetbrains.com/issue/KT-20022) Parcelize should dispatch directly to java.lang.Enum when writing an enum.
|
||||
- [`KT-20034`](https://youtrack.jetbrains.com/issue/KT-20034) Application installation failed (INSTALL_FAILED_DEXOPT) in Android 4.3 devices if I use Parcelize
|
||||
- [`KT-20057`](https://youtrack.jetbrains.com/issue/KT-20057) Parcelize should use specialized write/create methods where available.
|
||||
- [`KT-20062`](https://youtrack.jetbrains.com/issue/KT-20062) Parceler should allow otherwise un-parcelable property types in enclosing class.
|
||||
|
||||
### Compiler
|
||||
|
||||
#### Performance Improvements
|
||||
|
||||
- [`KT-20462`](https://youtrack.jetbrains.com/issue/KT-20462) Don't create an array copy for '*<array-constructor-fun>(...)'
|
||||
#### Fixes
|
||||
|
||||
- [`KT-14697`](https://youtrack.jetbrains.com/issue/KT-14697) Use-site targeted annotation is not correctly loaded from class file
|
||||
- [`KT-17680`](https://youtrack.jetbrains.com/issue/KT-17680) Android Studio and multiple tests in single file
|
||||
- [`KT-19251`](https://youtrack.jetbrains.com/issue/KT-19251) Stack spilling in constructor arguments breaks Quasar
|
||||
- [`KT-19592`](https://youtrack.jetbrains.com/issue/KT-19592) Apply JSR 305 default nullability qualifiers with to generic type arguments if they're applicable for TYPE_USE
|
||||
- [`KT-20016`](https://youtrack.jetbrains.com/issue/KT-20016) JSR 305: default nullability qualifiers are ignored in TYPE_USE and PARAMETER positions
|
||||
- [`KT-20131`](https://youtrack.jetbrains.com/issue/KT-20131) Support @NonNull(when = NEVER) nullability annotation
|
||||
- [`KT-20158`](https://youtrack.jetbrains.com/issue/KT-20158) Preserve flexibility for Java types annotated with @NonNull(when = UNKNOWN)
|
||||
- [`KT-20337`](https://youtrack.jetbrains.com/issue/KT-20337) No multifile class facade is generated for files with type aliases only
|
||||
- [`KT-20387`](https://youtrack.jetbrains.com/issue/KT-20387) Wrong argument generated for accessor call of a protected generic 'operator fun get/set' from base class with primitive type as type parameter
|
||||
- [`KT-20418`](https://youtrack.jetbrains.com/issue/KT-20418) Wrong code generated for literal long range with mixed integer literal ends
|
||||
- [`KT-20491`](https://youtrack.jetbrains.com/issue/KT-20491) Incorrect synthetic accessor generated for a generic base class function specialized with primitive type
|
||||
- [`KT-20651`](https://youtrack.jetbrains.com/issue/KT-20651) "Don't know how to generate outer expression" for enum-values with non-trivial self-closures
|
||||
- [`KT-20707`](https://youtrack.jetbrains.com/issue/KT-20707) Support when by enum in kotlin scripts
|
||||
- [`KT-20879`](https://youtrack.jetbrains.com/issue/KT-20879) Compiler problem in when-expressions
|
||||
|
||||
### IDE
|
||||
|
||||
#### New Features
|
||||
|
||||
- [`KT-14175`](https://youtrack.jetbrains.com/issue/KT-14175) Surround with try ... catch (... finally) doesn't work for expressions
|
||||
- [`KT-15769`](https://youtrack.jetbrains.com/issue/KT-15769) Join lines could "convert to expression body"
|
||||
- [`KT-19134`](https://youtrack.jetbrains.com/issue/KT-19134) IntelliJ Color Scheme editor - allow changing color of colons and double colons
|
||||
- [`KT-20308`](https://youtrack.jetbrains.com/issue/KT-20308) New Gradle with Kotlin DSL project wizard
|
||||
#### Fixes
|
||||
|
||||
- [`KT-15932`](https://youtrack.jetbrains.com/issue/KT-15932) Attempt to rename private property finds unrelated usages
|
||||
- [`KT-18996`](https://youtrack.jetbrains.com/issue/KT-18996) After Kotlin compiler settings change: 'Apply' button doesn't work
|
||||
- [`KT-19458`](https://youtrack.jetbrains.com/issue/KT-19458) Resolver for 'completion/highlighting in ScriptModuleInfo for build.gradle.kts / JVM' does not know how to resolve LibraryInfo
|
||||
- [`KT-19474`](https://youtrack.jetbrains.com/issue/KT-19474) Kotlin Gradle Script: highlighting fails on unresolved references
|
||||
- [`KT-19823`](https://youtrack.jetbrains.com/issue/KT-19823) Kotlin Gradle project import into IntelliJ: import kapt generated classes into classpath
|
||||
- [`KT-19958`](https://youtrack.jetbrains.com/issue/KT-19958) Android: kotlinOptions from build.gradle are not imported into facet
|
||||
- [`KT-19972`](https://youtrack.jetbrains.com/issue/KT-19972) AssertionError “Resolver for 'completion/highlighting in ModuleProductionSourceInfo(module=Module: 'kotlin-pure_main') for files dummy.kt for platform JVM' does not know how to resolve SdkInfo“ on copying Kotlin file with kotlin.* imports from other project
|
||||
- [`KT-20112`](https://youtrack.jetbrains.com/issue/KT-20112) maven dependency type test-jar with scope compile not
|
||||
- [`KT-20185`](https://youtrack.jetbrains.com/issue/KT-20185) Stub and PSI element type mismatch for "var nullableSuspend: (suspend (P) -> Unit)? = null"
|
||||
- [`KT-20199`](https://youtrack.jetbrains.com/issue/KT-20199) Cut action is not available during indexing
|
||||
- [`KT-20331`](https://youtrack.jetbrains.com/issue/KT-20331) Wrong EAP repository
|
||||
- [`KT-20346`](https://youtrack.jetbrains.com/issue/KT-20346) Can't build tests in common code due to missing org.jetbrains.kotlin:kotlin-test-js testCompile dependency in JS
|
||||
- [`KT-20419`](https://youtrack.jetbrains.com/issue/KT-20419) Android Studio plugin 1.1.50 show multiple gutter icon for the same item
|
||||
- [`KT-20519`](https://youtrack.jetbrains.com/issue/KT-20519) Error “Parameter specified as non-null is null: method ModuleGrouperKt.isQualifiedModuleNamesEnabled” on creating Gradle (Kotlin DSL) project from scratch
|
||||
- [`KT-20550`](https://youtrack.jetbrains.com/issue/KT-20550) Spring: "Navigate to autowired candidates" gutter action is missed (IDEA 2017.3)
|
||||
- [`KT-20566`](https://youtrack.jetbrains.com/issue/KT-20566) Spring: "Navigate to the spring beans declaration" gutter action for `@ComponentScan` is missed (IDEA 2017.3)
|
||||
- [`KT-20621`](https://youtrack.jetbrains.com/issue/KT-20621) Provide automatic migration from JetRunConfigurationType to KotlinRunConfigurationType
|
||||
- [`KT-20648`](https://youtrack.jetbrains.com/issue/KT-20648) Do we need a separate ProjectImportProvider for gradle kotlin dsl projects?
|
||||
- [`KT-20782`](https://youtrack.jetbrains.com/issue/KT-20782) Non-atomic trees update
|
||||
- [`KT-20789`](https://youtrack.jetbrains.com/issue/KT-20789) Can't navigate to inline call/inline use site when runner is delegated to Gradle
|
||||
- [`KT-20843`](https://youtrack.jetbrains.com/issue/KT-20843) Kotlin TypeDeclarationProvider may stop other declarations providers execution
|
||||
- [`KT-20929`](https://youtrack.jetbrains.com/issue/KT-20929) Import Project from Gradle wizard: the same page is shown twice
|
||||
|
||||
### IDE. Completion
|
||||
|
||||
- [`KT-16383`](https://youtrack.jetbrains.com/issue/KT-16383) IllegalStateException: Failed to create expression from text: '<init>' on choosing ByteArray from completion list
|
||||
- [`KT-18458`](https://youtrack.jetbrains.com/issue/KT-18458) Spring: code completion does not suggest bean names inside `@Qualifier` before function parameter
|
||||
- [`KT-20256`](https://youtrack.jetbrains.com/issue/KT-20256) java.lang.Throwable “Invalid range specified” on editing template inside string literal
|
||||
|
||||
### IDE. Inspections and Intentions
|
||||
|
||||
#### New Features
|
||||
|
||||
- [`KT-14695`](https://youtrack.jetbrains.com/issue/KT-14695) Simplify comparison intention produces meaningless statement for assert()
|
||||
- [`KT-17204`](https://youtrack.jetbrains.com/issue/KT-17204) Add `Assign to property quickfix`
|
||||
- [`KT-18220`](https://youtrack.jetbrains.com/issue/KT-18220) Add data modifier to a class quickfix
|
||||
- [`KT-18742`](https://youtrack.jetbrains.com/issue/KT-18742) Add quick-fix for CANNOT_CHECK_FOR_ERASED
|
||||
- [`KT-19735`](https://youtrack.jetbrains.com/issue/KT-19735) Add quickfix for type mismatch that converts Sequence/Array/List
|
||||
- [`KT-20259`](https://youtrack.jetbrains.com/issue/KT-20259) Show warning if arrays are compared by '!='
|
||||
#### Fixes
|
||||
|
||||
- [`KT-10546`](https://youtrack.jetbrains.com/issue/KT-10546) Wrong "Unused property" warning on using inline object syntax
|
||||
- [`KT-16394`](https://youtrack.jetbrains.com/issue/KT-16394) "Convert reference to lambda" generates wrong code
|
||||
- [`KT-16808`](https://youtrack.jetbrains.com/issue/KT-16808) Intention "Remove unnecessary parantheses" is erroneously proposed for elvis operator on LHS of `in` operator if RHS of elvis is return with value
|
||||
- [`KT-17437`](https://youtrack.jetbrains.com/issue/KT-17437) Class highlighted as unused even if Companion methods/fields really used
|
||||
- [`KT-19377`](https://youtrack.jetbrains.com/issue/KT-19377) Inspections are run for Kotlin Gradle DSL sources
|
||||
- [`KT-19420`](https://youtrack.jetbrains.com/issue/KT-19420) Kotlin Gradle script editor: suggestion to import required class from stdlib fails with AE: ResolverForProjectImpl.descriptorForModule()
|
||||
- [`KT-19626`](https://youtrack.jetbrains.com/issue/KT-19626) (Specify type explicitly) Descriptor was not found for VALUE_PARAMETER
|
||||
- [`KT-19674`](https://youtrack.jetbrains.com/issue/KT-19674) 'Convert property initializer to getter' intention fails on incompilable initializer with AssertionError at SpecifyTypeExplicitlyIntention$Companion.addTypeAnnotationWithTemplate()
|
||||
- [`KT-19782`](https://youtrack.jetbrains.com/issue/KT-19782) Surround with if else doesn't work for expressions
|
||||
- [`KT-20010`](https://youtrack.jetbrains.com/issue/KT-20010) 'Replace safe access expression with 'if' expression' IDEA Kotlin plugin intention may failed
|
||||
- [`KT-20104`](https://youtrack.jetbrains.com/issue/KT-20104) "Recursive property accessor" reports false positive when property reference is used in the assignment
|
||||
- [`KT-20218`](https://youtrack.jetbrains.com/issue/KT-20218) AE on calling intention “Convert to secondary constructor” for already referred argument
|
||||
- [`KT-20231`](https://youtrack.jetbrains.com/issue/KT-20231) False positive 'Redundant override' when delegated member hides super type override
|
||||
- [`KT-20261`](https://youtrack.jetbrains.com/issue/KT-20261) Incorrect "Redundant Unit return type" inspection for Nothing-typed expression
|
||||
- [`KT-20315`](https://youtrack.jetbrains.com/issue/KT-20315) "call chain on collection type may be simplified" generates code that does not compile
|
||||
- [`KT-20333`](https://youtrack.jetbrains.com/issue/KT-20333) Assignment can be lifted out of try is applied too broadly
|
||||
- [`KT-20366`](https://youtrack.jetbrains.com/issue/KT-20366) Code cleanup: some inspections are broken
|
||||
- [`KT-20369`](https://youtrack.jetbrains.com/issue/KT-20369) Inspection messages with INFORMATION highlight type are shown in Code Inspect
|
||||
- [`KT-20409`](https://youtrack.jetbrains.com/issue/KT-20409) useless warning "Remove curly braces" for Chinese character
|
||||
- [`KT-20417`](https://youtrack.jetbrains.com/issue/KT-20417) Converting property getter to block body doesn't insert explicit return type
|
||||
|
||||
### IDE. Refactorings
|
||||
|
||||
#### Performance Improvements
|
||||
|
||||
- [`KT-18823`](https://youtrack.jetbrains.com/issue/KT-18823) Move class to a separate file is very slow in 'kotlin' project
|
||||
- [`KT-20205`](https://youtrack.jetbrains.com/issue/KT-20205) Invoke MoveKotlinDeclarationsProcessor.findUsages() under progress
|
||||
#### Fixes
|
||||
|
||||
- [`KT-15840`](https://youtrack.jetbrains.com/issue/KT-15840) Introduce type alias: don't change not-nullable type with nullable typealias
|
||||
- [`KT-17949`](https://youtrack.jetbrains.com/issue/KT-17949) Rename private fun should not search it out of scope
|
||||
- [`KT-18196`](https://youtrack.jetbrains.com/issue/KT-18196) Refactor / Copy: the copy is formatted
|
||||
- [`KT-18594`](https://youtrack.jetbrains.com/issue/KT-18594) Refactor / Extract (Functional) Parameter are available for annotation arguments, but fail with AE: "Body element is not found"
|
||||
- [`KT-19439`](https://youtrack.jetbrains.com/issue/KT-19439) Kotlin introduce parameter causes exception
|
||||
- [`KT-19909`](https://youtrack.jetbrains.com/issue/KT-19909) copy a kotlin class removes imports and other modifications
|
||||
- [`KT-19949`](https://youtrack.jetbrains.com/issue/KT-19949) AssertionError „Resolver for 'project source roots and libraries for platform JVM' does not know how to resolve ModuleProductionSourceInfo“ through MoveConflictChecker.getModuleDescriptor() on copying Kotlin file from other project
|
||||
- [`KT-20092`](https://youtrack.jetbrains.com/issue/KT-20092) Refactor / Copy: copy of .kt file removes all the blank lines and 'hanging' comments
|
||||
- [`KT-20335`](https://youtrack.jetbrains.com/issue/KT-20335) Refactor → Extract Type Parameter: “AWT events are not allowed inside write action” after processing duplicates
|
||||
- [`KT-20402`](https://youtrack.jetbrains.com/issue/KT-20402) Throwable “PsiElement(IDENTIFIER) by KotlinInplaceParameterIntroducer” on calling Refactor → Extract Parameter for default values
|
||||
- [`KT-20403`](https://youtrack.jetbrains.com/issue/KT-20403) AE “Body element is not found” on calling Refactor → Extract Parameter for default values in constructor of class without body
|
||||
|
||||
### JavaScript
|
||||
|
||||
#### Fixes
|
||||
|
||||
- [`KT-8285`](https://youtrack.jetbrains.com/issue/KT-8285) JS: don't generate tmp when only need one component
|
||||
- [`KT-14549`](https://youtrack.jetbrains.com/issue/KT-14549) JS: Non-local returns from secondary constructors don't work
|
||||
- [`KT-15294`](https://youtrack.jetbrains.com/issue/KT-15294) JS: parse error in `js()` function
|
||||
- [`KT-17450`](https://youtrack.jetbrains.com/issue/KT-17450) PlatformDependent members of collections are compiled in JS
|
||||
- [`KT-18010`](https://youtrack.jetbrains.com/issue/KT-18010) JS: JsName annotation in interfaces can cause runtime exception
|
||||
- [`KT-18063`](https://youtrack.jetbrains.com/issue/KT-18063) Inlining does not work properly in JS for suspend functions from another module
|
||||
- [`KT-18548`](https://youtrack.jetbrains.com/issue/KT-18548) JS: wrong string interpolation with generic or Any parameters
|
||||
- [`KT-19794`](https://youtrack.jetbrains.com/issue/KT-19794) runtime crash with empty object (Javascript)
|
||||
- [`KT-19818`](https://youtrack.jetbrains.com/issue/KT-19818) JS: generate paths relative to .map file by default (unless "-source-map-prefix" is used)
|
||||
- [`KT-19906`](https://youtrack.jetbrains.com/issue/KT-19906) JS: rename compiler option "-source-map-source-roots" to avoid misleading since sourcemaps have field called "sourceRoot"
|
||||
- [`KT-20287`](https://youtrack.jetbrains.com/issue/KT-20287) Functions don't actually return Unit in Kotlin-JS -> unexpected null problems vs JDK version
|
||||
- [`KT-20451`](https://youtrack.jetbrains.com/issue/KT-20451) KotlinJs - interface function with default parameter, overridden by implementor, can't be found at runtime
|
||||
- [`KT-20650`](https://youtrack.jetbrains.com/issue/KT-20650) JS: compiler crashes in Java 9 with NoClassDefFoundError
|
||||
- [`KT-20653`](https://youtrack.jetbrains.com/issue/KT-20653) JS: compiler crashes in Java 9 with TranslationRuntimeException
|
||||
- [`KT-20820`](https://youtrack.jetbrains.com/issue/KT-20820) JS: IDEA project doesn't generate paths relative to .map
|
||||
|
||||
### Libraries
|
||||
|
||||
- [`KT-20596`](https://youtrack.jetbrains.com/issue/KT-20596) 'synchronized' does not allow non-local return in Kotlin JS
|
||||
- [`KT-20600`](https://youtrack.jetbrains.com/issue/KT-20600) Typo in POMs for kotlin-runtime
|
||||
|
||||
### Tools
|
||||
|
||||
- [`KT-19692`](https://youtrack.jetbrains.com/issue/KT-19692) kotlin-jpa plugin doesn't support @MappedSuperclass annotation
|
||||
- [`KT-20030`](https://youtrack.jetbrains.com/issue/KT-20030) Parcelize can directly reference writeToParcel and CREATOR for final, non-Parcelize Parcelable types in same compilation unit.
|
||||
- [`KT-19742`](https://youtrack.jetbrains.com/issue/KT-19742) [Android extensions] Calling clearFindViewByIdCache causes NPE
|
||||
- [`KT-19749`](https://youtrack.jetbrains.com/issue/KT-19749) Android extensions + Parcelable: NoSuchMethodError on attempt to pack into parcel a serializable object
|
||||
- [`KT-20026`](https://youtrack.jetbrains.com/issue/KT-20026) Parcelize overrides describeContents despite being already implemented.
|
||||
- [`KT-20027`](https://youtrack.jetbrains.com/issue/KT-20027) Parcelize uses wrong classloader when reading parcelable type.
|
||||
- [`KT-20029`](https://youtrack.jetbrains.com/issue/KT-20029) Parcelize should not directly reference parcel methods on types outside compilation unit
|
||||
- [`KT-20032`](https://youtrack.jetbrains.com/issue/KT-20032) Parcelize does not respect type nullability in case of Parcelize parcelables
|
||||
|
||||
### Tools. Gradle
|
||||
|
||||
- [`KT-3463`](https://youtrack.jetbrains.com/issue/KT-3463) Gradle plugin ignores kotlin compile options changes
|
||||
- [`KT-16299`](https://youtrack.jetbrains.com/issue/KT-16299) Gradle build does not recompile annotated classes on changing compiler's plugins configuration
|
||||
- [`KT-16764`](https://youtrack.jetbrains.com/issue/KT-16764) Kotlin Gradle plugin should replicate task dependencies of Java source directories
|
||||
- [`KT-17564`](https://youtrack.jetbrains.com/issue/KT-17564) Applying Kotlin's Gradle plugin results in src/main/java being listed twice in sourceSets.main.allSource
|
||||
- [`KT-17674`](https://youtrack.jetbrains.com/issue/KT-17674) Test code is not compiled incrementally when main is changed
|
||||
- [`KT-18765`](https://youtrack.jetbrains.com/issue/KT-18765) Move incremental compilation message from Gradle's warning to info logging level
|
||||
- [`KT-20036`](https://youtrack.jetbrains.com/issue/KT-20036) Gradle tasks up-to-date-ness
|
||||
|
||||
### Tools. J2K
|
||||
|
||||
- [`KT-19565`](https://youtrack.jetbrains.com/issue/KT-19565) Java code using Iterator#remove converted to red code
|
||||
- [`KT-19651`](https://youtrack.jetbrains.com/issue/KT-19651) Java class with static-only methods can contain 'protected' members
|
||||
|
||||
### Tools. JPS
|
||||
|
||||
- [`KT-20082`](https://youtrack.jetbrains.com/issue/KT-20082) Java 9: incremental build reports bogus error for reference to Kotlin source
|
||||
- [`KT-20671`](https://youtrack.jetbrains.com/issue/KT-20671) Kotlin plugin compiler exception when compiling under JDK9
|
||||
|
||||
### Tools. Maven
|
||||
|
||||
- [`KT-20064`](https://youtrack.jetbrains.com/issue/KT-20064) Maven + Java 9: compile task warns about module-info in the output path
|
||||
- [`KT-20400`](https://youtrack.jetbrains.com/issue/KT-20400) Do not output module name, version and related information by default in Maven builds
|
||||
|
||||
### Tools. REPL
|
||||
|
||||
- [`KT-20167`](https://youtrack.jetbrains.com/issue/KT-20167) JDK 9 `unresolved supertypes: Object` when working with Kotlin Scripting API
|
||||
|
||||
### Tools. kapt
|
||||
|
||||
- [`KT-17923`](https://youtrack.jetbrains.com/issue/KT-17923) Reference to Dagger generated class is highlighted red
|
||||
- [`KT-18923`](https://youtrack.jetbrains.com/issue/KT-18923) Kapt: Do not use the Kotlin error message collector to issue errors from kapt
|
||||
- [`KT-19097`](https://youtrack.jetbrains.com/issue/KT-19097) Request: Decent support of `kapt.kotlin.generated` on Intellij/Android Studio
|
||||
- [`KT-20877`](https://youtrack.jetbrains.com/issue/KT-20877) Butterknife: UninitializedPropertyAccessException: "lateinit property has not been initialized" for field annotated with `@BindView`.
|
||||
|
||||
## 1.1.50
|
||||
|
||||
### Android
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<import file="build.xml" optional="false"/>
|
||||
|
||||
<property name="build.number" value="snapshot"/>
|
||||
<property name="jdk16.home" value="${java.home}"/>
|
||||
<property name="fail.on.plugin.verifier.error" value="true"/>
|
||||
|
||||
<property name="version_substitute_dir" value="${basedir}/versions_temp/"/>
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.config.shouldWritePreReleaseFlag
|
||||
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
|
||||
@@ -59,23 +58,18 @@ data class JvmBuildMetaInfo(
|
||||
}
|
||||
}
|
||||
|
||||
fun JvmBuildMetaInfo(args: CommonCompilerArguments): JvmBuildMetaInfo {
|
||||
val languageVersion = args.languageVersion?.let((LanguageVersion)::fromVersionString) ?: LanguageVersion.LATEST_STABLE
|
||||
|
||||
return JvmBuildMetaInfo(
|
||||
isEAP = languageVersion.shouldWritePreReleaseFlag(),
|
||||
compilerBuildVersion = KotlinCompilerVersion.VERSION,
|
||||
languageVersionString = languageVersion.versionString,
|
||||
apiVersionString = args.apiVersion ?: ApiVersion.LATEST_STABLE.versionString,
|
||||
coroutinesEnable = args.coroutinesState == CommonCompilerArguments.ENABLE,
|
||||
coroutinesWarn = args.coroutinesState == CommonCompilerArguments.WARN,
|
||||
coroutinesError = args.coroutinesState == CommonCompilerArguments.ERROR,
|
||||
multiplatformEnable = args.multiPlatform,
|
||||
metadataVersionMajor = JvmMetadataVersion.INSTANCE.major,
|
||||
metadataVersionMinor = JvmMetadataVersion.INSTANCE.minor,
|
||||
metadataVersionPatch = JvmMetadataVersion.INSTANCE.patch,
|
||||
bytecodeVersionMajor = JvmBytecodeBinaryVersion.INSTANCE.major,
|
||||
bytecodeVersionMinor = JvmBytecodeBinaryVersion.INSTANCE.minor,
|
||||
bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch
|
||||
)
|
||||
}
|
||||
fun JvmBuildMetaInfo(args: CommonCompilerArguments): JvmBuildMetaInfo =
|
||||
JvmBuildMetaInfo(isEAP = KotlinCompilerVersion.isPreRelease(),
|
||||
compilerBuildVersion = KotlinCompilerVersion.VERSION,
|
||||
languageVersionString = args.languageVersion ?: LanguageVersion.LATEST_STABLE.versionString,
|
||||
apiVersionString = args.apiVersion ?: ApiVersion.LATEST_STABLE.versionString,
|
||||
coroutinesEnable = args.coroutinesState == CommonCompilerArguments.ENABLE,
|
||||
coroutinesWarn = args.coroutinesState == CommonCompilerArguments.WARN,
|
||||
coroutinesError = args.coroutinesState == CommonCompilerArguments.ERROR,
|
||||
multiplatformEnable = args.multiPlatform,
|
||||
metadataVersionMajor = JvmMetadataVersion.INSTANCE.major,
|
||||
metadataVersionMinor = JvmMetadataVersion.INSTANCE.minor,
|
||||
metadataVersionPatch = JvmMetadataVersion.INSTANCE.patch,
|
||||
bytecodeVersionMajor = JvmBytecodeBinaryVersion.INSTANCE.major,
|
||||
bytecodeVersionMinor = JvmBytecodeBinaryVersion.INSTANCE.minor,
|
||||
bytecodeVersionPatch = JvmBytecodeBinaryVersion.INSTANCE.patch)
|
||||
|
||||
@@ -287,7 +287,9 @@ open class IncrementalJvmCache(
|
||||
fun remove(className: JvmClassName, changesCollector: ChangesCollector) {
|
||||
val key = className.internalName
|
||||
val oldValue = storage[key] ?: return
|
||||
changesCollector.collectProtoChanges(oldData = oldValue.toProtoData(className.packageFqName), newData = null)
|
||||
if (key != MODULE_MAPPING_FILE_NAME) {
|
||||
changesCollector.collectProtoChanges(oldData = oldValue.toProtoData(className.packageFqName), newData = null)
|
||||
}
|
||||
storage.remove(key)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,9 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (!checkEquals(old.typeTable, new.typeTable)) return false
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirementTable() != new.hasVersionRequirementTable()) return false
|
||||
if (old.hasVersionRequirementTable()) {
|
||||
if (!checkEquals(old.versionRequirementTable, new.versionRequirementTable)) return false
|
||||
if (old.hasSinceKotlinInfoTable() != new.hasSinceKotlinInfoTable()) return false
|
||||
if (old.hasSinceKotlinInfoTable()) {
|
||||
if (!checkEquals(old.sinceKotlinInfoTable, new.sinceKotlinInfoTable)) return false
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.packageModuleName) != new.hasExtension(JvmProtoBuf.packageModuleName)) return false
|
||||
@@ -78,7 +78,7 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
PROPERTY_LIST,
|
||||
TYPE_ALIAS_LIST,
|
||||
TYPE_TABLE,
|
||||
VERSION_REQUIREMENT_TABLE,
|
||||
SINCE_KOTLIN_INFO_TABLE,
|
||||
JVM_EXT_PACKAGE_MODULE_NAME,
|
||||
JVM_EXT_PACKAGE_LOCAL_VARIABLE_LIST,
|
||||
JS_EXT_PACKAGE_FQ_NAME
|
||||
@@ -98,9 +98,9 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (!checkEquals(old.typeTable, new.typeTable)) result.add(ProtoBufPackageKind.TYPE_TABLE)
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirementTable() != new.hasVersionRequirementTable()) result.add(ProtoBufPackageKind.VERSION_REQUIREMENT_TABLE)
|
||||
if (old.hasVersionRequirementTable()) {
|
||||
if (!checkEquals(old.versionRequirementTable, new.versionRequirementTable)) result.add(ProtoBufPackageKind.VERSION_REQUIREMENT_TABLE)
|
||||
if (old.hasSinceKotlinInfoTable() != new.hasSinceKotlinInfoTable()) result.add(ProtoBufPackageKind.SINCE_KOTLIN_INFO_TABLE)
|
||||
if (old.hasSinceKotlinInfoTable()) {
|
||||
if (!checkEquals(old.sinceKotlinInfoTable, new.sinceKotlinInfoTable)) result.add(ProtoBufPackageKind.SINCE_KOTLIN_INFO_TABLE)
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.packageModuleName) != new.hasExtension(JvmProtoBuf.packageModuleName)) result.add(ProtoBufPackageKind.JVM_EXT_PACKAGE_MODULE_NAME)
|
||||
@@ -163,14 +163,14 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (!checkEquals(old.typeTable, new.typeTable)) return false
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirement() != new.hasVersionRequirement()) return false
|
||||
if (old.hasVersionRequirement()) {
|
||||
if (old.versionRequirement != new.versionRequirement) return false
|
||||
if (old.hasSinceKotlinInfo() != new.hasSinceKotlinInfo()) return false
|
||||
if (old.hasSinceKotlinInfo()) {
|
||||
if (old.sinceKotlinInfo != new.sinceKotlinInfo) return false
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirementTable() != new.hasVersionRequirementTable()) return false
|
||||
if (old.hasVersionRequirementTable()) {
|
||||
if (!checkEquals(old.versionRequirementTable, new.versionRequirementTable)) return false
|
||||
if (old.hasSinceKotlinInfoTable() != new.hasSinceKotlinInfoTable()) return false
|
||||
if (old.hasSinceKotlinInfoTable()) {
|
||||
if (!checkEquals(old.sinceKotlinInfoTable, new.sinceKotlinInfoTable)) return false
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.classModuleName) != new.hasExtension(JvmProtoBuf.classModuleName)) return false
|
||||
@@ -218,8 +218,8 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
ENUM_ENTRY_LIST,
|
||||
SEALED_SUBCLASS_FQ_NAME_LIST,
|
||||
TYPE_TABLE,
|
||||
VERSION_REQUIREMENT,
|
||||
VERSION_REQUIREMENT_TABLE,
|
||||
SINCE_KOTLIN_INFO,
|
||||
SINCE_KOTLIN_INFO_TABLE,
|
||||
JVM_EXT_CLASS_MODULE_NAME,
|
||||
JVM_EXT_CLASS_LOCAL_VARIABLE_LIST,
|
||||
JS_EXT_CLASS_ANNOTATION_LIST,
|
||||
@@ -266,14 +266,14 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (!checkEquals(old.typeTable, new.typeTable)) result.add(ProtoBufClassKind.TYPE_TABLE)
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirement() != new.hasVersionRequirement()) result.add(ProtoBufClassKind.VERSION_REQUIREMENT)
|
||||
if (old.hasVersionRequirement()) {
|
||||
if (old.versionRequirement != new.versionRequirement) result.add(ProtoBufClassKind.VERSION_REQUIREMENT)
|
||||
if (old.hasSinceKotlinInfo() != new.hasSinceKotlinInfo()) result.add(ProtoBufClassKind.SINCE_KOTLIN_INFO)
|
||||
if (old.hasSinceKotlinInfo()) {
|
||||
if (old.sinceKotlinInfo != new.sinceKotlinInfo) result.add(ProtoBufClassKind.SINCE_KOTLIN_INFO)
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirementTable() != new.hasVersionRequirementTable()) result.add(ProtoBufClassKind.VERSION_REQUIREMENT_TABLE)
|
||||
if (old.hasVersionRequirementTable()) {
|
||||
if (!checkEquals(old.versionRequirementTable, new.versionRequirementTable)) result.add(ProtoBufClassKind.VERSION_REQUIREMENT_TABLE)
|
||||
if (old.hasSinceKotlinInfoTable() != new.hasSinceKotlinInfoTable()) result.add(ProtoBufClassKind.SINCE_KOTLIN_INFO_TABLE)
|
||||
if (old.hasSinceKotlinInfoTable()) {
|
||||
if (!checkEquals(old.sinceKotlinInfoTable, new.sinceKotlinInfoTable)) result.add(ProtoBufClassKind.SINCE_KOTLIN_INFO_TABLE)
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.classModuleName) != new.hasExtension(JvmProtoBuf.classModuleName)) result.add(ProtoBufClassKind.JVM_EXT_CLASS_MODULE_NAME)
|
||||
@@ -349,14 +349,9 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (!checkEquals(old.typeTable, new.typeTable)) return false
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirement() != new.hasVersionRequirement()) return false
|
||||
if (old.hasVersionRequirement()) {
|
||||
if (old.versionRequirement != new.versionRequirement) return false
|
||||
}
|
||||
|
||||
if (old.hasContract() != new.hasContract()) return false
|
||||
if (old.hasContract()) {
|
||||
if (!checkEquals(old.contract, new.contract)) return false
|
||||
if (old.hasSinceKotlinInfo() != new.hasSinceKotlinInfo()) return false
|
||||
if (old.hasSinceKotlinInfo()) {
|
||||
if (old.sinceKotlinInfo != new.sinceKotlinInfo) return false
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.methodSignature) != new.hasExtension(JvmProtoBuf.methodSignature)) return false
|
||||
@@ -431,9 +426,9 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (old.setterFlags != new.setterFlags) return false
|
||||
}
|
||||
|
||||
if (old.hasVersionRequirement() != new.hasVersionRequirement()) return false
|
||||
if (old.hasVersionRequirement()) {
|
||||
if (old.versionRequirement != new.versionRequirement) return false
|
||||
if (old.hasSinceKotlinInfo() != new.hasSinceKotlinInfo()) return false
|
||||
if (old.hasSinceKotlinInfo()) {
|
||||
if (old.sinceKotlinInfo != new.sinceKotlinInfo) return false
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.propertySignature) != new.hasExtension(JvmProtoBuf.propertySignature)) return false
|
||||
@@ -495,9 +490,9 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
|
||||
if (!checkEqualsTypeAliasAnnotation(old, new)) return false
|
||||
|
||||
if (old.hasVersionRequirement() != new.hasVersionRequirement()) return false
|
||||
if (old.hasVersionRequirement()) {
|
||||
if (old.versionRequirement != new.versionRequirement) return false
|
||||
if (old.hasSinceKotlinInfo() != new.hasSinceKotlinInfo()) return false
|
||||
if (old.hasSinceKotlinInfo()) {
|
||||
if (old.sinceKotlinInfo != new.sinceKotlinInfo) return false
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -514,8 +509,8 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.VersionRequirementTable, new: ProtoBuf.VersionRequirementTable): Boolean {
|
||||
if (!checkEqualsVersionRequirementTableRequirement(old, new)) return false
|
||||
open fun checkEquals(old: ProtoBuf.SinceKotlinInfoTable, new: ProtoBuf.SinceKotlinInfoTable): Boolean {
|
||||
if (!checkEqualsSinceKotlinInfoTableInfo(old, new)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -662,9 +657,9 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
|
||||
if (!checkEqualsConstructorValueParameter(old, new)) return false
|
||||
|
||||
if (old.hasVersionRequirement() != new.hasVersionRequirement()) return false
|
||||
if (old.hasVersionRequirement()) {
|
||||
if (old.versionRequirement != new.versionRequirement) return false
|
||||
if (old.hasSinceKotlinInfo() != new.hasSinceKotlinInfo()) return false
|
||||
if (old.hasSinceKotlinInfo()) {
|
||||
if (old.sinceKotlinInfo != new.sinceKotlinInfo) return false
|
||||
}
|
||||
|
||||
if (old.hasExtension(JvmProtoBuf.constructorSignature) != new.hasExtension(JvmProtoBuf.constructorSignature)) return false
|
||||
@@ -750,12 +745,6 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Contract, new: ProtoBuf.Contract): Boolean {
|
||||
if (!checkEqualsContractEffect(old, new)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: JvmProtoBuf.JvmMethodSignature, new: JvmProtoBuf.JvmMethodSignature): Boolean {
|
||||
if (old.hasName() != new.hasName()) return false
|
||||
if (old.hasName()) {
|
||||
@@ -840,7 +829,7 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.VersionRequirement, new: ProtoBuf.VersionRequirement): Boolean {
|
||||
open fun checkEquals(old: ProtoBuf.SinceKotlinInfo, new: ProtoBuf.SinceKotlinInfo): Boolean {
|
||||
if (old.hasVersion() != new.hasVersion()) return false
|
||||
if (old.hasVersion()) {
|
||||
if (old.version != new.version) return false
|
||||
@@ -866,11 +855,6 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
if (!checkStringEquals(old.message, new.message)) return false
|
||||
}
|
||||
|
||||
if (old.hasVersionKind() != new.hasVersionKind()) return false
|
||||
if (old.hasVersionKind()) {
|
||||
if (old.versionKind != new.versionKind) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -901,27 +885,6 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Effect, new: ProtoBuf.Effect): Boolean {
|
||||
if (old.hasEffectType() != new.hasEffectType()) return false
|
||||
if (old.hasEffectType()) {
|
||||
if (old.effectType != new.effectType) return false
|
||||
}
|
||||
|
||||
if (!checkEqualsEffectEffectConstructorArgument(old, new)) return false
|
||||
|
||||
if (old.hasConclusionOfConditionalEffect() != new.hasConclusionOfConditionalEffect()) return false
|
||||
if (old.hasConclusionOfConditionalEffect()) {
|
||||
if (!checkEquals(old.conclusionOfConditionalEffect, new.conclusionOfConditionalEffect)) return false
|
||||
}
|
||||
|
||||
if (old.hasKind() != new.hasKind()) return false
|
||||
if (old.hasKind()) {
|
||||
if (old.kind != new.kind) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: JvmProtoBuf.JvmFieldSignature, new: JvmProtoBuf.JvmFieldSignature): Boolean {
|
||||
if (old.hasName() != new.hasName()) return false
|
||||
if (old.hasName()) {
|
||||
@@ -936,39 +899,6 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEquals(old: ProtoBuf.Expression, new: ProtoBuf.Expression): Boolean {
|
||||
if (old.hasFlags() != new.hasFlags()) return false
|
||||
if (old.hasFlags()) {
|
||||
if (old.flags != new.flags) return false
|
||||
}
|
||||
|
||||
if (old.hasValueParameterReference() != new.hasValueParameterReference()) return false
|
||||
if (old.hasValueParameterReference()) {
|
||||
if (old.valueParameterReference != new.valueParameterReference) return false
|
||||
}
|
||||
|
||||
if (old.hasConstantValue() != new.hasConstantValue()) return false
|
||||
if (old.hasConstantValue()) {
|
||||
if (old.constantValue != new.constantValue) return false
|
||||
}
|
||||
|
||||
if (old.hasIsInstanceType() != new.hasIsInstanceType()) return false
|
||||
if (old.hasIsInstanceType()) {
|
||||
if (!checkEquals(old.isInstanceType, new.isInstanceType)) return false
|
||||
}
|
||||
|
||||
if (old.hasIsInstanceTypeId() != new.hasIsInstanceTypeId()) return false
|
||||
if (old.hasIsInstanceTypeId()) {
|
||||
if (old.isInstanceTypeId != new.isInstanceTypeId) return false
|
||||
}
|
||||
|
||||
if (!checkEqualsExpressionAndArgument(old, new)) return false
|
||||
|
||||
if (!checkEqualsExpressionOrArgument(old, new)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsPackageFunction(old: ProtoBuf.Package, new: ProtoBuf.Package): Boolean {
|
||||
if (old.functionCount != new.functionCount) return false
|
||||
|
||||
@@ -1159,11 +1089,11 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsVersionRequirementTableRequirement(old: ProtoBuf.VersionRequirementTable, new: ProtoBuf.VersionRequirementTable): Boolean {
|
||||
if (old.requirementCount != new.requirementCount) return false
|
||||
open fun checkEqualsSinceKotlinInfoTableInfo(old: ProtoBuf.SinceKotlinInfoTable, new: ProtoBuf.SinceKotlinInfoTable): Boolean {
|
||||
if (old.infoCount != new.infoCount) return false
|
||||
|
||||
for(i in 0..old.requirementCount - 1) {
|
||||
if (!checkEquals(old.getRequirement(i), new.getRequirement(i))) return false
|
||||
for(i in 0..old.infoCount - 1) {
|
||||
if (!checkEquals(old.getInfo(i), new.getInfo(i))) return false
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -1219,16 +1149,6 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsContractEffect(old: ProtoBuf.Contract, new: ProtoBuf.Contract): Boolean {
|
||||
if (old.effectCount != new.effectCount) return false
|
||||
|
||||
for(i in 0..old.effectCount - 1) {
|
||||
if (!checkEquals(old.getEffect(i), new.getEffect(i))) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsAnnotationArgumentValueArrayElement(old: ProtoBuf.Annotation.Argument.Value, new: ProtoBuf.Annotation.Argument.Value): Boolean {
|
||||
if (old.arrayElementCount != new.arrayElementCount) return false
|
||||
|
||||
@@ -1239,36 +1159,6 @@ open class ProtoCompareGenerated(val oldNameResolver: NameResolver, val newNameR
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsEffectEffectConstructorArgument(old: ProtoBuf.Effect, new: ProtoBuf.Effect): Boolean {
|
||||
if (old.effectConstructorArgumentCount != new.effectConstructorArgumentCount) return false
|
||||
|
||||
for(i in 0..old.effectConstructorArgumentCount - 1) {
|
||||
if (!checkEquals(old.getEffectConstructorArgument(i), new.getEffectConstructorArgument(i))) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsExpressionAndArgument(old: ProtoBuf.Expression, new: ProtoBuf.Expression): Boolean {
|
||||
if (old.andArgumentCount != new.andArgumentCount) return false
|
||||
|
||||
for(i in 0..old.andArgumentCount - 1) {
|
||||
if (!checkEquals(old.getAndArgument(i), new.getAndArgument(i))) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
open fun checkEqualsExpressionOrArgument(old: ProtoBuf.Expression, new: ProtoBuf.Expression): Boolean {
|
||||
if (old.orArgumentCount != new.orArgumentCount) return false
|
||||
|
||||
for(i in 0..old.orArgumentCount - 1) {
|
||||
if (!checkEquals(old.getOrArgument(i), new.getOrArgument(i))) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun oldGetIndexOfString(index: Int): Int = getIndexOfString(index, oldStringIndexesMap, oldNameResolver)
|
||||
fun newGetIndexOfString(index: Int): Int = getIndexOfString(index, newStringIndexesMap, newNameResolver)
|
||||
|
||||
@@ -1319,8 +1209,8 @@ fun ProtoBuf.Package.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int)
|
||||
hashCode = 31 * hashCode + typeTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasVersionRequirementTable()) {
|
||||
hashCode = 31 * hashCode + versionRequirementTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
if (hasSinceKotlinInfoTable()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfoTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasExtension(JvmProtoBuf.packageModuleName)) {
|
||||
@@ -1395,12 +1285,12 @@ fun ProtoBuf.Class.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) ->
|
||||
hashCode = 31 * hashCode + typeTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasVersionRequirement()) {
|
||||
hashCode = 31 * hashCode + versionRequirement
|
||||
if (hasSinceKotlinInfo()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfo
|
||||
}
|
||||
|
||||
if (hasVersionRequirementTable()) {
|
||||
hashCode = 31 * hashCode + versionRequirementTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
if (hasSinceKotlinInfoTable()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfoTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasExtension(JvmProtoBuf.classModuleName)) {
|
||||
@@ -1463,12 +1353,8 @@ fun ProtoBuf.Function.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int)
|
||||
hashCode = 31 * hashCode + typeTable.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasVersionRequirement()) {
|
||||
hashCode = 31 * hashCode + versionRequirement
|
||||
}
|
||||
|
||||
if (hasContract()) {
|
||||
hashCode = 31 * hashCode + contract.hashCode(stringIndexes, fqNameIndexes)
|
||||
if (hasSinceKotlinInfo()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfo
|
||||
}
|
||||
|
||||
if (hasExtension(JvmProtoBuf.methodSignature)) {
|
||||
@@ -1531,8 +1417,8 @@ fun ProtoBuf.Property.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int)
|
||||
hashCode = 31 * hashCode + setterFlags
|
||||
}
|
||||
|
||||
if (hasVersionRequirement()) {
|
||||
hashCode = 31 * hashCode + versionRequirement
|
||||
if (hasSinceKotlinInfo()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfo
|
||||
}
|
||||
|
||||
if (hasExtension(JvmProtoBuf.propertySignature)) {
|
||||
@@ -1587,8 +1473,8 @@ fun ProtoBuf.TypeAlias.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int
|
||||
hashCode = 31 * hashCode + getAnnotation(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasVersionRequirement()) {
|
||||
hashCode = 31 * hashCode + versionRequirement
|
||||
if (hasSinceKotlinInfo()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfo
|
||||
}
|
||||
|
||||
return hashCode
|
||||
@@ -1608,11 +1494,11 @@ fun ProtoBuf.TypeTable.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.VersionRequirementTable.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
fun ProtoBuf.SinceKotlinInfoTable.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
for(i in 0..requirementCount - 1) {
|
||||
hashCode = 31 * hashCode + getRequirement(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
for(i in 0..infoCount - 1) {
|
||||
hashCode = 31 * hashCode + getInfo(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
return hashCode
|
||||
@@ -1737,8 +1623,8 @@ fun ProtoBuf.Constructor.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (I
|
||||
hashCode = 31 * hashCode + getValueParameter(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasVersionRequirement()) {
|
||||
hashCode = 31 * hashCode + versionRequirement
|
||||
if (hasSinceKotlinInfo()) {
|
||||
hashCode = 31 * hashCode + sinceKotlinInfo
|
||||
}
|
||||
|
||||
if (hasExtension(JvmProtoBuf.constructorSignature)) {
|
||||
@@ -1810,16 +1696,6 @@ fun ProtoBuf.ValueParameter.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes:
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Contract.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
for(i in 0..effectCount - 1) {
|
||||
hashCode = 31 * hashCode + getEffect(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun JvmProtoBuf.JvmMethodSignature.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
@@ -1898,7 +1774,7 @@ fun ProtoBuf.Annotation.Argument.Value.hashCode(stringIndexes: (Int) -> Int, fqN
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.VersionRequirement.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
fun ProtoBuf.SinceKotlinInfo.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
if (hasVersion()) {
|
||||
@@ -1921,10 +1797,6 @@ fun ProtoBuf.VersionRequirement.hashCode(stringIndexes: (Int) -> Int, fqNameInde
|
||||
hashCode = 31 * hashCode + stringIndexes(message)
|
||||
}
|
||||
|
||||
if (hasVersionKind()) {
|
||||
hashCode = 31 * hashCode + versionKind.hashCode()
|
||||
}
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
@@ -1956,28 +1828,6 @@ fun ProtoBuf.Annotation.Argument.hashCode(stringIndexes: (Int) -> Int, fqNameInd
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Effect.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
if (hasEffectType()) {
|
||||
hashCode = 31 * hashCode + effectType.hashCode()
|
||||
}
|
||||
|
||||
for(i in 0..effectConstructorArgumentCount - 1) {
|
||||
hashCode = 31 * hashCode + getEffectConstructorArgument(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasConclusionOfConditionalEffect()) {
|
||||
hashCode = 31 * hashCode + conclusionOfConditionalEffect.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasKind()) {
|
||||
hashCode = 31 * hashCode + kind.hashCode()
|
||||
}
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun JvmProtoBuf.JvmFieldSignature.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
@@ -1991,37 +1841,3 @@ fun JvmProtoBuf.JvmFieldSignature.hashCode(stringIndexes: (Int) -> Int, fqNameIn
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
fun ProtoBuf.Expression.hashCode(stringIndexes: (Int) -> Int, fqNameIndexes: (Int) -> Int): Int {
|
||||
var hashCode = 1
|
||||
|
||||
if (hasFlags()) {
|
||||
hashCode = 31 * hashCode + flags
|
||||
}
|
||||
|
||||
if (hasValueParameterReference()) {
|
||||
hashCode = 31 * hashCode + valueParameterReference
|
||||
}
|
||||
|
||||
if (hasConstantValue()) {
|
||||
hashCode = 31 * hashCode + constantValue.hashCode()
|
||||
}
|
||||
|
||||
if (hasIsInstanceType()) {
|
||||
hashCode = 31 * hashCode + isInstanceType.hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
if (hasIsInstanceTypeId()) {
|
||||
hashCode = 31 * hashCode + isInstanceTypeId
|
||||
}
|
||||
|
||||
for(i in 0..andArgumentCount - 1) {
|
||||
hashCode = 31 * hashCode + getAndArgument(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
for(i in 0..orArgumentCount - 1) {
|
||||
hashCode = 31 * hashCode + getOrArgument(i).hashCode(stringIndexes, fqNameIndexes)
|
||||
}
|
||||
|
||||
return hashCode
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ class DifferenceCalculatorForClass(
|
||||
ProtoBufClassKind.TYPE_TABLE -> {
|
||||
// TODO
|
||||
}
|
||||
ProtoCompareGenerated.ProtoBufClassKind.VERSION_REQUIREMENT,
|
||||
ProtoCompareGenerated.ProtoBufClassKind.VERSION_REQUIREMENT_TABLE -> {
|
||||
ProtoCompareGenerated.ProtoBufClassKind.SINCE_KOTLIN_INFO,
|
||||
ProtoCompareGenerated.ProtoBufClassKind.SINCE_KOTLIN_INFO_TABLE -> {
|
||||
// TODO
|
||||
}
|
||||
ProtoBufClassKind.FLAGS,
|
||||
@@ -281,7 +281,7 @@ class DifferenceCalculatorForPackageFacade(
|
||||
ProtoBufPackageKind.TYPE_ALIAS_LIST ->
|
||||
names.addAll(calcDifferenceForNonPrivateMembers(ProtoBuf.Package::getTypeAliasList))
|
||||
ProtoBufPackageKind.TYPE_TABLE,
|
||||
ProtoBufPackageKind.VERSION_REQUIREMENT_TABLE,
|
||||
ProtoBufPackageKind.SINCE_KOTLIN_INFO_TABLE,
|
||||
ProtoBufPackageKind.JVM_EXT_PACKAGE_MODULE_NAME,
|
||||
ProtoBufPackageKind.JS_EXT_PACKAGE_FQ_NAME-> {
|
||||
// TODO
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,13 @@ import org.gradle.api.Project
|
||||
import java.util.*
|
||||
import java.io.File
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
|
||||
buildscript {
|
||||
extra["defaultSnapshotVersion"] = "1.2-SNAPSHOT"
|
||||
extra["defaultSnapshotVersion"] = "1.1-SNAPSHOT"
|
||||
|
||||
kotlinBootstrapFrom(BootstrapOption.TeamCity("1.2.0-dev-92", onlySuccessBootstrap = false))
|
||||
kotlinBootstrapFrom(BootstrapOption.BintrayDev("1.1.60-eap-30"))
|
||||
|
||||
val repos = listOfNotNull(
|
||||
bootstrapKotlinRepo,
|
||||
@@ -35,7 +34,6 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
`build-scan`
|
||||
idea
|
||||
}
|
||||
|
||||
buildScan {
|
||||
@@ -54,7 +52,7 @@ val defaultSnapshotVersion: String by extra
|
||||
val buildNumber by extra(findProperty("build.number")?.toString() ?: defaultSnapshotVersion)
|
||||
val kotlinVersion by extra(findProperty("deployVersion")?.toString() ?: buildNumber)
|
||||
|
||||
val kotlinLanguageVersion by extra("1.2")
|
||||
val kotlinLanguageVersion by extra("1.1")
|
||||
|
||||
allprojects {
|
||||
group = "org.jetbrains.kotlin"
|
||||
@@ -117,7 +115,7 @@ extra["versions.ant"] = "1.8.2"
|
||||
extra["versions.android"] = "2.3.1"
|
||||
|
||||
extra["ideaCoreSdkJars"] = arrayOf("annotations", "asm-all", "guava", "intellij-core", "jdom", "jna", "log4j", "picocontainer",
|
||||
"snappy-in-java", "trove4j", "xpp3-1.1.4-min", "xstream")
|
||||
"snappy-in-java", "streamex", "trove4j", "xpp3-1.1.4-min", "xstream")
|
||||
|
||||
extra["compilerModules"] = arrayOf(":compiler:util",
|
||||
":compiler:container",
|
||||
@@ -155,8 +153,6 @@ val coreLibProjects = listOf(
|
||||
":kotlin-stdlib-js",
|
||||
":kotlin-stdlib-jre7",
|
||||
":kotlin-stdlib-jre8",
|
||||
":kotlin-stdlib-jdk7",
|
||||
":kotlin-stdlib-jdk8",
|
||||
":kotlin-test:kotlin-test-common",
|
||||
":kotlin-test:kotlin-test-jvm",
|
||||
":kotlin-test:kotlin-test-junit",
|
||||
@@ -217,7 +213,6 @@ fun Task.listConfigurationContents(configName: String) {
|
||||
|
||||
val defaultJvmTarget = "1.8"
|
||||
val defaultJavaHome = jdkPath(defaultJvmTarget!!)
|
||||
val ignoreTestFailures by extra(project.findProperty("ignoreTestFailures")?.toString()?.toBoolean() ?: project.hasProperty("teamcity"))
|
||||
|
||||
allprojects {
|
||||
|
||||
@@ -226,7 +221,7 @@ allprojects {
|
||||
|
||||
// There are problems with common build dir:
|
||||
// - some tests (in particular js and binary-compatibility-validator depend on the fixed (default) location
|
||||
// - idea seems unable to exclude common builddir from indexing
|
||||
// - idea siims unable to exclude common builddir from indexing
|
||||
// therefore it is disabled by default
|
||||
// buildDir = File(commonBuildDir, project.name)
|
||||
|
||||
@@ -234,29 +229,19 @@ allprojects {
|
||||
for (repo in (rootProject.extra["repos"] as List<String>)) {
|
||||
maven { setUrl(repo) }
|
||||
}
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
configureJvmProject(javaHome!!, jvmTarget!!)
|
||||
|
||||
val commonCompilerArgs = listOf("-Xallow-kotlin-package")
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
|
||||
kotlinOptions {
|
||||
languageVersion = kotlinLanguageVersion
|
||||
apiVersion = kotlinLanguageVersion
|
||||
freeCompilerArgs = commonCompilerArgs
|
||||
freeCompilerArgs = listOf("-Xallow-kotlin-package", "-Xnormalize-constructor-calls=enable")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = commonCompilerArgs + listOf("-Xnormalize-constructor-calls=enable")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(VerificationTask::class.java as Class<Task>) {
|
||||
(this as VerificationTask).ignoreFailures = ignoreTestFailures
|
||||
}
|
||||
|
||||
tasks.withType<Javadoc> {
|
||||
enabled = false
|
||||
}
|
||||
@@ -332,6 +317,11 @@ tasks {
|
||||
}
|
||||
}
|
||||
|
||||
"gradlePluginsTest" {
|
||||
// deprecated
|
||||
dependsOn("gradlePluginTest")
|
||||
}
|
||||
|
||||
"gradlePluginIntegrationTest" {
|
||||
dependsOn(":kotlin-gradle-plugin-integration-tests:check")
|
||||
}
|
||||
@@ -349,6 +339,7 @@ tasks {
|
||||
}
|
||||
|
||||
"jsCompilerTest" {
|
||||
dependsOn("dist")
|
||||
dependsOn(":js:js.tests:test")
|
||||
}
|
||||
|
||||
@@ -457,23 +448,6 @@ tasks {
|
||||
"check" { dependsOn("test") }
|
||||
}
|
||||
|
||||
configure<IdeaModel> {
|
||||
module {
|
||||
excludeDirs = files(
|
||||
project.buildDir,
|
||||
".gradle",
|
||||
"dependencies",
|
||||
"dist",
|
||||
"ideaSDK/bin",
|
||||
"ideaSDK/androidSDK",
|
||||
"ideaSDK/config",
|
||||
"ideaSDK/config-idea",
|
||||
"ideaSDK/system",
|
||||
"ideaSDK/system-idea"
|
||||
).toSet()
|
||||
}
|
||||
}
|
||||
|
||||
fun jdkPathIfFound(version: String): String? {
|
||||
val jdkName = "JDK_${version.replace(".", "")}"
|
||||
val jdkMajorVersion = JdkMajorVersion.valueOf(jdkName)
|
||||
|
||||
10
build.xml
10
build.xml
@@ -25,7 +25,7 @@
|
||||
<property environment="env"/>
|
||||
<property name="tools.jar" value="${env.JDK_18}/lib/tools.jar"/>
|
||||
|
||||
<property name="java.target" value="1.6"/>
|
||||
<property name="java.target" value="1.8"/>
|
||||
|
||||
<condition property="bootstrap.or.local.build" value="true" else="false">
|
||||
<or>
|
||||
@@ -387,19 +387,20 @@
|
||||
<zipgroupfileset dir="${basedir}/lib" includes="*.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/annotations.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/asm-all.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/guava-19.0.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/guava-21.0.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/intellij-core.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/jdom.jar" excludes="META-INF/jb/** META-INF/LICENSE"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/jna.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/log4j.jar" excludes="META-INF/jb/** META-INF/LICENSE"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/picocontainer.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/snappy-in-java-0.5.1.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/streamex-0.6.2.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/trove4j.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/xpp3-1.1.4-min.jar"/>
|
||||
<zipfileset src="${basedir}/ideaSDK/core/xstream-1.4.8.jar"/>
|
||||
<zipfileset src="${idea.sdk}/lib/jna-platform.jar"/>
|
||||
<zipfileset src="${idea.sdk}/lib/oromatcher.jar" excludes="META-INF/jb/** META-INF/LICENSE"/>
|
||||
<zipfileset src="${idea.sdk}/jps/jps-model.jar"/>
|
||||
<zipfileset src="${idea.sdk}/jps/jps-model.jar" excludes="META-INF/services/**"/>
|
||||
<zipfileset src="${dependencies}/jline3.jar"/>
|
||||
<zipfileset src="${dependencies}/jansi.jar"/>
|
||||
<zipfileset src="${dependencies}/javaslang-2.0.6.jar"/>
|
||||
@@ -1021,9 +1022,6 @@
|
||||
<copy file="${basedir}/libraries/stdlib/src/kotlin/jvm/JvmVersion.kt" todir="${output}/mock-runtime-src"/>
|
||||
<copy file="${basedir}/libraries/stdlib/src/kotlin/util/Standard.kt" todir="${output}/mock-runtime-src"/>
|
||||
<copy file="${basedir}/libraries/stdlib/src/kotlin/internal/Annotations.kt" todir="${output}/mock-runtime-src"/>
|
||||
<!-- Contracts -->
|
||||
<copy file="${basedir}/libraries/stdlib/src/kotlin/internal/contracts/ContractBuilder.kt" todir="${output}/mock-runtime-src"/>
|
||||
<copy file="${basedir}/libraries/stdlib/src/kotlin/internal/contracts/Effect.kt" todir="${output}/mock-runtime-src"/>
|
||||
|
||||
<new-kotlinc output="${output}/classes/mock-runtime" moduleName="kotlin-stdlib">
|
||||
<src>
|
||||
|
||||
@@ -33,14 +33,12 @@ repositories {
|
||||
extra["buildSrcKotlinRepo"]?.let {
|
||||
maven { setUrl(it) }
|
||||
}
|
||||
maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") // for dex-method-list
|
||||
// maven { setUrl("https://repo.gradle.org/gradle/libs-releases-local") }
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(files("../dependencies/native-platform-uberjar.jar"))
|
||||
compile("com.jakewharton.dex:dex-method-list:2.0.0-alpha")
|
||||
// compile("net.rubygrapefruit:native-platform:0.14")
|
||||
// TODO: adding the dep to the plugin breaks the build unexpectedly, resolve and uncomment
|
||||
// compile("org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.extra["bootstrap_kotlin_version"]}")
|
||||
|
||||
@@ -12,15 +12,14 @@ var Project.bootstrapKotlinRepo: String?
|
||||
private set(value) { this.extra["bootstrapKotlinRepo"] = value }
|
||||
|
||||
fun Project.kotlinBootstrapFrom(defaultSource: BootstrapOption) {
|
||||
val customVersion = project.findProperty("bootstrap.kotlin.version") as String?
|
||||
val customRepo = project.findProperty("bootstrap.kotlin.repo") as String?
|
||||
val teamCityVersion = project.findProperty("bootstrap.teamcity.kotlin.version") as String?
|
||||
val teamCityProject = project.findProperty("bootstrap.teamcity.project") as String?
|
||||
val bootstrapVersion = project.findProperty("bootstrap.kotlin.version") as String?
|
||||
val bootstrapRepo = project.findProperty("bootstrap.kotlin.repo") as String?
|
||||
val bootstrapTeamCityVersion = project.findProperty("bootstrap.teamcity.kotlin.version") as String?
|
||||
|
||||
val bootstrapSource = when {
|
||||
project.hasProperty("bootstrap.local") -> BootstrapOption.Local(project.findProperty("bootstrap.local.version") as String?, project.findProperty("bootstrap.local.path") as String?)
|
||||
teamCityVersion != null -> BootstrapOption.TeamCity(teamCityVersion, projectExtId = teamCityProject, onlySuccessBootstrap = false)
|
||||
customVersion != null -> BootstrapOption.Custom(kotlinVersion = customVersion, repo = customRepo)
|
||||
bootstrapTeamCityVersion != null -> BootstrapOption.TeamCity(bootstrapTeamCityVersion, onlySuccessBootstrap = false)
|
||||
bootstrapVersion != null -> BootstrapOption.Custom(kotlinVersion = bootstrapVersion, repo = bootstrapRepo)
|
||||
else -> defaultSource
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.project
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import java.io.File
|
||||
|
||||
|
||||
@@ -30,11 +29,8 @@ fun Project.preloadedDeps(vararg artifactBaseNames: String, baseDir: File = File
|
||||
throw GradleException("Invalid base directory $dir")
|
||||
}
|
||||
val matchingFiles = dir.listFiles { file -> artifactBaseNames.any { file.matchMaybeVersionedArtifact(it) } }
|
||||
if (matchingFiles == null || matchingFiles.size < artifactBaseNames.size) {
|
||||
throw GradleException("Not all matching artifacts '${artifactBaseNames.joinToString()}' found in the '$dir' " +
|
||||
"(missing: ${artifactBaseNames.filterNot { request -> matchingFiles.any { it.matchMaybeVersionedArtifact(request) } }.joinToString()};" +
|
||||
" found: ${matchingFiles?.joinToString { it.name }})")
|
||||
}
|
||||
if (matchingFiles == null || matchingFiles.size < artifactBaseNames.size)
|
||||
throw GradleException("Not all matching artifacts '${artifactBaseNames.joinToString()}' found in the '$dir' (found: ${matchingFiles?.joinToString { it.name }})")
|
||||
return files(*matchingFiles.map { it.canonicalPath }.toTypedArray())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import com.jakewharton.dex.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import java.io.File
|
||||
|
||||
open class DexMethodCount : DefaultTask() {
|
||||
|
||||
data class Counts(
|
||||
val total: Int,
|
||||
val totalOwnPackages: Int?,
|
||||
val totalOtherPackages: Int?,
|
||||
val byPackage: Map<String, Int>,
|
||||
val byClass: Map<String, Int>
|
||||
)
|
||||
|
||||
@InputFile
|
||||
lateinit var jarFile: File
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
var ownPackages: List<String>? = null
|
||||
|
||||
// ##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}' value='62362']
|
||||
@Input
|
||||
@Optional
|
||||
var teamCityStatistics: Boolean? = null
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
var artifactName: String? = null
|
||||
|
||||
private val artifactOrArchiveName get() = artifactName ?: project.name
|
||||
|
||||
fun from(jar: Jar) {
|
||||
jarFile = jar.archivePath
|
||||
artifactName = jar.baseName
|
||||
dependsOn(jar)
|
||||
}
|
||||
|
||||
lateinit var counts: Counts
|
||||
|
||||
@TaskAction
|
||||
fun invoke() {
|
||||
val methods = DexMethods.list(jarFile)
|
||||
|
||||
val counts = methods.getCounts().also { this.counts = it }
|
||||
|
||||
printTotals(counts)
|
||||
printTCStats(counts)
|
||||
outputDetails(counts)
|
||||
}
|
||||
|
||||
private fun List<DexMethod>.getCounts(): Counts {
|
||||
val byPackage = this.groupingBy { it.`package` }.eachCount()
|
||||
val byClass = this.groupingBy { it.declaringType }.eachCount()
|
||||
|
||||
val ownPackages = ownPackages?.map { it + '.' }
|
||||
val byOwnPackages = if (ownPackages != null) {
|
||||
this.partition { method -> ownPackages.any { method.declaringType.startsWith(it) }}.let {
|
||||
it.first.size to it.second.size
|
||||
}
|
||||
} else (null to null)
|
||||
|
||||
return Counts(total = this.size,
|
||||
totalOwnPackages = byOwnPackages.first,
|
||||
totalOtherPackages = byOwnPackages.second,
|
||||
byPackage = byPackage,
|
||||
byClass = byClass)
|
||||
}
|
||||
|
||||
private fun printTotals(counts: Counts) {
|
||||
logger.lifecycle("Artifact $artifactOrArchiveName, total methods: ${counts.total}")
|
||||
ownPackages?.let { packages ->
|
||||
logger.lifecycle("Artifact $artifactOrArchiveName, total methods from packages ${packages.joinToString { "$it.*" }}: ${counts.totalOwnPackages}")
|
||||
logger.lifecycle("Artifact $artifactOrArchiveName, total methods from other packages: ${counts.totalOtherPackages}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun printTCStats(counts: Counts) {
|
||||
if (teamCityStatistics ?: project.hasProperty("teamcity")) {
|
||||
println("##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}' value='${counts.total}']")
|
||||
counts.totalOwnPackages?.let { value ->
|
||||
println("##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}_OwnPackages' value='$value']")
|
||||
}
|
||||
counts.totalOtherPackages?.let { value ->
|
||||
println("##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}_OtherPackages' value='$value']")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputDetails(counts: Counts) {
|
||||
val detailFile = project.buildDir.resolve("$artifactOrArchiveName-method-count.txt")
|
||||
detailFile.printWriter().use { writer ->
|
||||
writer.println("${counts.total.padRight()}\tTotal methods")
|
||||
ownPackages?.let { packages ->
|
||||
writer.println("${counts.totalOwnPackages?.padRight()}\tTotal methods from packages ${packages.joinToString { "$it.*" }}")
|
||||
writer.println("${counts.totalOtherPackages?.padRight()}\tTotal methods from other packages")
|
||||
}
|
||||
writer.println()
|
||||
writer.println("Method count by package:")
|
||||
counts.byPackage.forEach { (name, count) ->
|
||||
writer.println("${count.padRight()}\t$name")
|
||||
}
|
||||
writer.println()
|
||||
writer.println("Method count by class:")
|
||||
counts.byClass.forEach { (name, count) ->
|
||||
writer.println("${count.padRight()}\t$name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private val DexMethod.`package`: String get() = declaringType.substringBeforeLast('.')
|
||||
private fun Int.padRight() = toString().padStart(5, ' ')
|
||||
@@ -1,15 +1,11 @@
|
||||
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.task
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import java.lang.Character.isLowerCase
|
||||
import java.lang.Character.isUpperCase
|
||||
|
||||
fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}): Test = getOrCreateTask(taskName) {
|
||||
doFirst {
|
||||
@@ -17,29 +13,22 @@ fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}):
|
||||
if (patterns.isEmpty() || patterns.any { '*' in it }) return@doFirst
|
||||
patterns.forEach { pattern ->
|
||||
val maybeMethodName = pattern.substringAfterLast('.')
|
||||
val maybeClassFqName = if (maybeMethodName.isFirstChar(::isLowerCase))
|
||||
val className = if (maybeMethodName.isNotEmpty() && maybeMethodName[0].isLowerCase())
|
||||
pattern.substringBeforeLast('.')
|
||||
else
|
||||
pattern
|
||||
|
||||
if (!maybeClassFqName.substringAfterLast('.').isFirstChar(::isUpperCase)) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val classFileNameWithoutExtension = maybeClassFqName.replace('.', '/')
|
||||
val classFileName = classFileNameWithoutExtension + ".class"
|
||||
val matchPattern = className.replace('.', '/') + ".class"
|
||||
|
||||
include {
|
||||
val path = it.path
|
||||
if (it.isDirectory) {
|
||||
classFileNameWithoutExtension.startsWith(path)
|
||||
matchPattern.startsWith(it.path)
|
||||
} else {
|
||||
path == classFileName || (path.endsWith(".class") && path.startsWith(classFileNameWithoutExtension + "$"))
|
||||
it.path.endsWith(matchPattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jvmArgs("-ea", "-XX:+HeapDumpOnOutOfMemoryError", "-Xmx1100m", "-XX:+UseCodeCacheFlushing", "-XX:ReservedCodeCacheSize=128m", "-Djna.nosys=true")
|
||||
maxHeapSize = "1100m"
|
||||
systemProperty("idea.is.unit.test", "true")
|
||||
@@ -47,10 +36,9 @@ fun Project.projectTest(taskName: String = "test", body: Test.() -> Unit = {}):
|
||||
environment("PROJECT_CLASSES_DIRS", the<JavaPluginConvention>().sourceSets.getByName("test").output.classesDirs.asPath)
|
||||
environment("PROJECT_BUILD_DIR", buildDir)
|
||||
systemProperty("jps.kotlin.home", rootProject.extra["distKotlinHomeDir"])
|
||||
ignoreFailures = System.getenv("kotlin_build_ignore_test_failures")?.let { it == "yes" } ?: false
|
||||
body()
|
||||
}
|
||||
|
||||
private inline fun String.isFirstChar(f: (Char) -> Boolean) = isNotEmpty() && f(first())
|
||||
|
||||
inline fun <reified T : Task> Project.getOrCreateTask(taskName: String, body: T.() -> Unit): T =
|
||||
(tasks.findByName(taskName)?.let { it as T } ?: task<T>(taskName)).apply { body() }
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.android.tests;
|
||||
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.android.tests.download.SDKDownloader;
|
||||
import org.jetbrains.kotlin.android.tests.emulator.Emulator;
|
||||
import org.jetbrains.kotlin.android.tests.gradle.GradleRunner;
|
||||
import org.jetbrains.kotlin.android.tests.run.PermissionManager;
|
||||
import org.junit.Assert;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CodegenTestsOnAndroidRunner {
|
||||
|
||||
private final PathManager pathManager;
|
||||
|
||||
public static TestSuite getTestSuite(PathManager pathManager) {
|
||||
return new CodegenTestsOnAndroidRunner(pathManager).generateTestSuite();
|
||||
}
|
||||
|
||||
private CodegenTestsOnAndroidRunner(PathManager pathManager) {
|
||||
this.pathManager = pathManager;
|
||||
}
|
||||
|
||||
private TestSuite generateTestSuite() {
|
||||
TestSuite suite = new TestSuite("MySuite");
|
||||
|
||||
String resultOutput = runTests();
|
||||
|
||||
String reportFolder = pathManager.getTmpFolder() + "/build/outputs/androidTest-results/connected";
|
||||
try {
|
||||
List<TestCase> testCases = parseSingleReportInFolder(reportFolder);
|
||||
for (TestCase aCase : testCases) {
|
||||
suite.addTest(aCase);
|
||||
}
|
||||
Assert.assertNotEquals("There is no test results in report", 0, testCases.size());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Can't parse test results in " + reportFolder +"\n" + resultOutput);
|
||||
}
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String runTests() {
|
||||
File rootForAndroidDependencies = new File(pathManager.getDependenciesRoot());
|
||||
if (!rootForAndroidDependencies.exists()) {
|
||||
rootForAndroidDependencies.mkdirs();
|
||||
}
|
||||
|
||||
SDKDownloader downloader = new SDKDownloader(pathManager);
|
||||
downloader.downloadAll();
|
||||
downloader.unzipAll();
|
||||
PermissionManager.setPermissions(pathManager);
|
||||
|
||||
Emulator emulator = new Emulator(pathManager, Emulator.ARM);
|
||||
GradleRunner gradleRunner = new GradleRunner(pathManager);
|
||||
gradleRunner.clean();
|
||||
gradleRunner.build();
|
||||
|
||||
emulator.createEmulator();
|
||||
|
||||
String platformPrefixProperty = System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, "Idea");
|
||||
try {
|
||||
emulator.startEmulator();
|
||||
|
||||
try {
|
||||
emulator.waitEmulatorStart();
|
||||
//runTestsViaAdb(emulator, gradleRunner);
|
||||
return gradleRunner.connectedDebugAndroidTest();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
emulator.stopEmulator();
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (platformPrefixProperty != null) {
|
||||
System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, platformPrefixProperty);
|
||||
}
|
||||
else {
|
||||
System.clearProperty(PlatformUtils.PLATFORM_PREFIX_KEY);
|
||||
}
|
||||
emulator.finishEmulatorProcesses();
|
||||
}
|
||||
}
|
||||
|
||||
private String runTestsViaAdb(Emulator emulator, GradleRunner gradleRunner) {
|
||||
gradleRunner.installDebugAndroidTest();
|
||||
String result = emulator.runTestsViaAdb();
|
||||
System.out.println(result);
|
||||
gradleRunner.uninstallDebugAndroidTest();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<TestCase> parseSingleReportInFolder(String reportFolder) throws
|
||||
IOException,
|
||||
SAXException,
|
||||
ParserConfigurationException {
|
||||
File folder = new File(reportFolder);
|
||||
File[] files = folder.listFiles();
|
||||
assert files != null;
|
||||
assert files.length == 1;
|
||||
File reportFile = files[0];
|
||||
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
Document doc = dBuilder.parse(reportFile);
|
||||
Element root = doc.getDocumentElement();
|
||||
NodeList testCases = root.getElementsByTagName("testcase");
|
||||
List<TestCase> result = new ArrayList(testCases.getLength());
|
||||
|
||||
for (int i = 0; i < testCases.getLength(); i++) {
|
||||
Element item = (Element) testCases.item(i);
|
||||
NodeList failure = item.getElementsByTagName("failure");
|
||||
String name = item.getAttribute("name");
|
||||
String clazz = item.getAttribute("classname");
|
||||
|
||||
if (failure.getLength() == 0) {
|
||||
result.add(new TestCase(name) {
|
||||
@Override
|
||||
protected void runTest() throws Throwable {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
result.add(new TestCase(name) {
|
||||
@Override
|
||||
protected void runTest() throws Throwable {
|
||||
Assert.fail(failure.item(0).getTextContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,189 +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.android.tests
|
||||
|
||||
import com.intellij.util.PlatformUtils
|
||||
import junit.framework.TestCase
|
||||
import junit.framework.TestSuite
|
||||
import org.jetbrains.kotlin.android.tests.download.SDKDownloader
|
||||
import org.jetbrains.kotlin.android.tests.emulator.Emulator
|
||||
import org.jetbrains.kotlin.android.tests.gradle.GradleRunner
|
||||
import org.jetbrains.kotlin.android.tests.run.PermissionManager
|
||||
import org.junit.Assert
|
||||
import org.w3c.dom.Element
|
||||
import org.xml.sax.SAXException
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.parsers.ParserConfigurationException
|
||||
|
||||
class CodegenTestsOnAndroidRunner private constructor(private val pathManager: PathManager) {
|
||||
|
||||
private fun runTestsInEmulator(): TestSuite {
|
||||
val rootSuite = TestSuite("Root")
|
||||
|
||||
downloadDependencies()
|
||||
val emulator = Emulator(pathManager, Emulator.ARM)
|
||||
emulator.createEmulator()
|
||||
|
||||
val gradleRunner = GradleRunner(pathManager)
|
||||
//old dex
|
||||
cleanAndBuildProject(gradleRunner)
|
||||
|
||||
try {
|
||||
emulator.startEmulator()
|
||||
|
||||
try {
|
||||
emulator.waitEmulatorStart()
|
||||
|
||||
runTestsOnEmulator(gradleRunner, TestSuite("Dex")).apply {
|
||||
rootSuite.addTest(this)
|
||||
}
|
||||
|
||||
enableD8(true)
|
||||
runTestsOnEmulator(gradleRunner, TestSuite("D8")).apply {
|
||||
(0 until this.countTestCases()).forEach {
|
||||
val testCase = testAt(it) as TestCase
|
||||
testCase.name += "_D8"
|
||||
}
|
||||
rootSuite.addTest(this)
|
||||
}
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
e.printStackTrace()
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
emulator.stopEmulator()
|
||||
}
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
e.printStackTrace()
|
||||
throw e
|
||||
}
|
||||
finally {
|
||||
emulator.finishEmulatorProcesses()
|
||||
}
|
||||
|
||||
return rootSuite
|
||||
}
|
||||
|
||||
private fun enableD8(enable: Boolean) {
|
||||
val file = File(pathManager.androidTmpFolder, "gradle.properties")
|
||||
val lines = file.readLines().map {
|
||||
if (it.startsWith("android.enableD8=")) {
|
||||
"android.enableD8=$enable"
|
||||
}
|
||||
else it
|
||||
}
|
||||
file.writeText(lines.joinToString("\n"))
|
||||
}
|
||||
|
||||
private fun processReport(suite: TestSuite, resultOutput: String) {
|
||||
val reportFolder = pathManager.tmpFolder + "/build/outputs/androidTest-results/connected"
|
||||
try {
|
||||
val testCases = parseSingleReportInFolder(reportFolder)
|
||||
testCases.forEach { aCase -> suite.addTest(aCase) }
|
||||
Assert.assertNotEquals("There is no test results in report", 0, testCases.size.toLong())
|
||||
}
|
||||
catch (e: Exception) {
|
||||
throw RuntimeException("Can't parse test results in " + reportFolder + "\n" + resultOutput)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun runTestsOnEmulator(gradleRunner: GradleRunner, suite: TestSuite): TestSuite {
|
||||
val platformPrefixProperty = System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, "Idea")
|
||||
try {
|
||||
val resultOutput = gradleRunner.connectedDebugAndroidTest()
|
||||
processReport(suite, resultOutput)
|
||||
return suite
|
||||
}
|
||||
finally {
|
||||
if (platformPrefixProperty != null) {
|
||||
System.setProperty(PlatformUtils.PLATFORM_PREFIX_KEY, platformPrefixProperty)
|
||||
}
|
||||
else {
|
||||
System.clearProperty(PlatformUtils.PLATFORM_PREFIX_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun downloadDependencies() {
|
||||
val rootForAndroidDependencies = File(pathManager.dependenciesRoot)
|
||||
if (!rootForAndroidDependencies.exists()) {
|
||||
rootForAndroidDependencies.mkdirs()
|
||||
}
|
||||
|
||||
val downloader = SDKDownloader(pathManager)
|
||||
downloader.downloadAll()
|
||||
downloader.unzipAll()
|
||||
PermissionManager.setPermissions(pathManager)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun runTestsInEmulator(pathManager: PathManager): TestSuite {
|
||||
return CodegenTestsOnAndroidRunner(pathManager).runTestsInEmulator()
|
||||
}
|
||||
|
||||
private fun cleanAndBuildProject(gradleRunner: GradleRunner) {
|
||||
gradleRunner.clean()
|
||||
gradleRunner.build()
|
||||
}
|
||||
|
||||
@Throws(IOException::class, SAXException::class, ParserConfigurationException::class)
|
||||
private fun parseSingleReportInFolder(reportFolder: String): List<TestCase> {
|
||||
val folder = File(reportFolder)
|
||||
val files = folder.listFiles()!!
|
||||
assert(files.size == 1)
|
||||
val reportFile = files[0]
|
||||
|
||||
val dbFactory = DocumentBuilderFactory.newInstance()
|
||||
val dBuilder = dbFactory.newDocumentBuilder()
|
||||
val doc = dBuilder.parse(reportFile)
|
||||
val root = doc.documentElement
|
||||
val testCases = root.getElementsByTagName("testcase")
|
||||
|
||||
return (0 until testCases.length).map { i ->
|
||||
val item = testCases.item(i) as Element
|
||||
val failure = item.getElementsByTagName("failure")
|
||||
val name = item.getAttribute("name")
|
||||
val clazz = item.getAttribute("classname")
|
||||
|
||||
if (failure.length == 0) {
|
||||
object : TestCase(name) {
|
||||
@Throws(Throwable::class)
|
||||
override fun runTest() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
object : TestCase(name) {
|
||||
@Throws(Throwable::class)
|
||||
override fun runTest() {
|
||||
Assert.fail(failure.item(0).textContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,10 +68,6 @@ public class PathManager {
|
||||
return tmpFolder + "/src";
|
||||
}
|
||||
|
||||
public String getAndroidTmpFolder() {
|
||||
return tmpFolder;
|
||||
}
|
||||
|
||||
public String getAndroidSdkRoot() {
|
||||
return getDependenciesRoot() + "/android-sdk";
|
||||
}
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
package org.jetbrains.kotlin.android.tests;
|
||||
|
||||
import org.jetbrains.kotlin.jps.build.BaseKotlinJpsBuildTestCase;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@Ignore
|
||||
public class AndroidJpsBuildTestCase extends BaseKotlinJpsBuildTestCase {
|
||||
private static final String PROJECT_NAME = "android-module";
|
||||
private static final String SDK_NAME = "Android_SDK";
|
||||
@@ -31,8 +29,8 @@ public class AndroidJpsBuildTestCase extends BaseKotlinJpsBuildTestCase {
|
||||
|
||||
public void doTest() {
|
||||
initProject();
|
||||
rebuildAll();
|
||||
makeAll().assertSuccessful();
|
||||
rebuildAllModules();
|
||||
buildAllModules().assertSuccessful();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,15 +20,12 @@ import com.google.common.io.Files;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import junit.framework.TestSuite;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.AllTests;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
@RunWith(AllTests.class)
|
||||
public class AndroidRunner {
|
||||
public class AndroidRunner extends TestSuite {
|
||||
|
||||
private static PathManager pathManager;
|
||||
|
||||
@@ -52,7 +49,7 @@ public class AndroidRunner {
|
||||
CodegenTestsOnAndroidGenerator.generate(pathManager);
|
||||
|
||||
System.out.println("Run tests on android...");
|
||||
TestSuite suite = CodegenTestsOnAndroidRunner.runTestsInEmulator(pathManager);
|
||||
TestSuite suite = CodegenTestsOnAndroidRunner.getTestSuite(pathManager);
|
||||
//AndroidJpsBuildTestCase indirectly depends on UsefulTestCase which compiled against java 8
|
||||
//TODO: Need add separate run configuration for AndroidJpsBuildTestCase
|
||||
//suite.addTest(new AndroidJpsBuildTestCase());
|
||||
|
||||
@@ -42,14 +42,12 @@ import org.jetbrains.kotlin.test.*;
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase;
|
||||
import org.jetbrains.kotlin.utils.Printer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Ignore
|
||||
public class CodegenTestsOnAndroidGenerator extends KtUsefulTestCase {
|
||||
|
||||
private final PathManager pathManager;
|
||||
|
||||
@@ -123,13 +123,6 @@ public class SpecialFiles {
|
||||
excludedFiles.add("closureOfInnerLocalClass.kt");
|
||||
excludedFiles.add("closureWithSelfInstantiation.kt");
|
||||
excludedFiles.add("quotedClassName.kt");
|
||||
|
||||
//wrong function resolution after package renaming
|
||||
excludedFiles.add("apiVersionAtLeast1.kt");
|
||||
|
||||
//special flags
|
||||
excludedFiles.add("inlineFunInConstructorCallWithEnabledNormalization.kt");
|
||||
excludedFiles.add("kt9532_lv10.kt");
|
||||
}
|
||||
|
||||
private SpecialFiles() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
@@ -51,7 +52,6 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
|
||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
|
||||
import org.jetbrains.kotlin.config.ApiVersion;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
@@ -74,12 +74,12 @@ import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
|
||||
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.constants.*;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmBindingContextSlices;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmConstantsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
|
||||
@@ -308,12 +308,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
RuntimeAssertionInfo runtimeAssertionInfo = null;
|
||||
if (selector instanceof KtExpression) {
|
||||
KtExpression expression = (KtExpression) selector;
|
||||
runtimeAssertionInfo = bindingContext.get(JvmBindingContextSlices.RUNTIME_ASSERTION_INFO, expression);
|
||||
if (runtimeAssertionInfo == null &&
|
||||
state.getLanguageVersionSettings().supportsFeature(LanguageFeature.StrictJavaNullabilityAssertions)) {
|
||||
runtimeAssertionInfo = bindingContext.get(JvmBindingContextSlices.BODY_RUNTIME_ASSERTION_INFO, expression);
|
||||
}
|
||||
runtimeAssertionInfo = bindingContext.get(JvmBindingContextSlices.RUNTIME_ASSERTION_INFO, (KtExpression) selector);
|
||||
}
|
||||
|
||||
if (BuiltinSpecialBridgesKt.isValueArgumentForCallToMethodWithTypeCheckBarrier(selector, bindingContext)) return stackValue;
|
||||
@@ -791,7 +786,49 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
boolean takeUpConstValsAsConst,
|
||||
boolean shouldInlineConstVals
|
||||
) {
|
||||
return JvmConstantsKt.getCompileTimeConstant(expression, bindingContext, takeUpConstValsAsConst, shouldInlineConstVals);
|
||||
CompileTimeConstant<?> compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, bindingContext);
|
||||
if (compileTimeValue == null || compileTimeValue.getUsesNonConstValAsConstant()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!shouldInlineConstVals && !takeUpConstValsAsConst && compileTimeValue.getUsesVariableAsConstant()) {
|
||||
Ref<Boolean> containsNonInlinedVals = new Ref<>(false);
|
||||
KtVisitor constantChecker = new KtVisitor() {
|
||||
@Override
|
||||
public Object visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression, Object data) {
|
||||
ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, bindingContext);
|
||||
if (resolvedCall != null) {
|
||||
CallableDescriptor callableDescriptor = resolvedCall.getResultingDescriptor();
|
||||
if (callableDescriptor instanceof PropertyDescriptor &&
|
||||
!JvmCodegenUtil.isInlinedJavaConstProperty((VariableDescriptor) callableDescriptor)) {
|
||||
containsNonInlinedVals.set(true);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitKtElement(@NotNull KtElement element, Object data) {
|
||||
if (!containsNonInlinedVals.get()) {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
expression.accept(constantChecker);
|
||||
|
||||
if (containsNonInlinedVals.get()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
KotlinType expectedType = bindingContext.getType(expression);
|
||||
if (expectedType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return compileTimeValue.toConstantValue(expectedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1723,12 +1760,22 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
private boolean shouldGenerateSingletonAsThisOrOuterFromContext(ClassDescriptor classDescriptor) {
|
||||
return isPossiblyUninitializedSingleton(classDescriptor) &&
|
||||
isInsideSingleton(classDescriptor) &&
|
||||
isThisInitialized(classDescriptor);
|
||||
}
|
||||
if (!isPossiblyUninitializedSingleton(classDescriptor)) return false;
|
||||
if (!isInsideSingleton(classDescriptor)) return false;
|
||||
|
||||
// We are inside a singleton class 'S' with possibly uninitialized static instance
|
||||
// (enum entry, interface companion object).
|
||||
// Such singleton can be referenced by name, or as an explicit or implicit 'this'.
|
||||
// For a given singleton class 'S' we either use 'this@S' from context (local or captured),
|
||||
// or 'S' as a static instance.
|
||||
//
|
||||
// Local or captured 'this@S' should be used if:
|
||||
// - we are in the constructor for 'S',
|
||||
// and corresponding instance is initialized by super or delegating constructor call;
|
||||
// - we are in any other member of 'S' or any of its inner classes.
|
||||
//
|
||||
// Otherwise, a static instance should be used.
|
||||
|
||||
private boolean isThisInitialized(ClassDescriptor classDescriptor) {
|
||||
CodegenContext context = this.context;
|
||||
while (context != null) {
|
||||
if (context instanceof ConstructorContext) {
|
||||
@@ -1738,9 +1785,24 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return constructorContext.isThisInitialized();
|
||||
}
|
||||
}
|
||||
else if (context instanceof ClassContext) {
|
||||
ClassDescriptor contextClass = ((ClassContext) context).getContextDescriptor();
|
||||
if (isInInnerClassesChainFor(contextClass, classDescriptor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context = context.getParentContext();
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isInInnerClassesChainFor(ClassDescriptor innerClass, ClassDescriptor outerClass) {
|
||||
if (innerClass == outerClass) return true;
|
||||
if (!innerClass.isInner()) return false;
|
||||
|
||||
DeclarationDescriptor containingDeclaration = innerClass.getContainingDeclaration();
|
||||
if (!(containingDeclaration instanceof ClassDescriptor)) return false;
|
||||
return isInInnerClassesChainFor((ClassDescriptor) containingDeclaration, outerClass);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -2790,7 +2852,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public StackValue generateCallableReferenceReceiver(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
private StackValue generateCallableReferenceReceiver(@NotNull ResolvedCall<?> resolvedCall) {
|
||||
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
|
||||
if (descriptor.getExtensionReceiverParameter() == null && descriptor.getDispatchReceiverParameter() == null) return null;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public class FieldInfo {
|
||||
}
|
||||
|
||||
if (isNonCompanionObject(classDescriptor) || CompanionObjectMapping.INSTANCE.isMappedIntrinsicCompanionObject(classDescriptor)) {
|
||||
return createSingletonViaInstance(classDescriptor, typeMapper, JvmAbi.INSTANCE_FIELD);
|
||||
return createSingletonViaInstance(classDescriptor, typeMapper);
|
||||
}
|
||||
|
||||
ClassDescriptor ownerDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
|
||||
@@ -46,11 +46,10 @@ public class FieldInfo {
|
||||
@NotNull
|
||||
public static FieldInfo createSingletonViaInstance(
|
||||
@NotNull ClassDescriptor classDescriptor,
|
||||
@NotNull KotlinTypeMapper typeMapper,
|
||||
@NotNull String name
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
) {
|
||||
Type type = typeMapper.mapType(classDescriptor);
|
||||
return new FieldInfo(type, type, name, true);
|
||||
return new FieldInfo(type, type, JvmAbi.INSTANCE_FIELD, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode;
|
||||
import org.jetbrains.kotlin.name.ClassId;
|
||||
@@ -76,7 +77,6 @@ import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray;
|
||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
|
||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
|
||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtils2Kt.initDefaultSourceMappingIfNeeded;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAbi.*;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull;
|
||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
|
||||
@@ -354,8 +354,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
protected void generateSyntheticPartsAfterBody() {
|
||||
generateFieldForSingleton();
|
||||
|
||||
initializeObjects();
|
||||
|
||||
generateCompanionObjectBackingFieldCopies();
|
||||
|
||||
generateTraitMethods();
|
||||
@@ -813,21 +811,21 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
|
||||
private void generateFieldForSingleton() {
|
||||
if (isCompanionObjectInInterfaceNotIntrinsic(descriptor)) {
|
||||
StackValue.Field field = StackValue.createSingletonViaInstance(descriptor, typeMapper, HIDDEN_INSTANCE_FIELD);
|
||||
//hidden instance in interface companion
|
||||
v.newField(JvmDeclarationOriginKt.OtherOrigin(descriptor),
|
||||
ACC_SYNTHETIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null);
|
||||
}
|
||||
|
||||
if (isEnumEntry(descriptor) || isCompanionObject(descriptor)) return;
|
||||
|
||||
if (isNonCompanionObject(descriptor)) {
|
||||
StackValue.Field field = StackValue.createSingletonViaInstance(descriptor, typeMapper, INSTANCE_FIELD);
|
||||
StackValue.Field field = StackValue.singletonViaInstance(descriptor, typeMapper);
|
||||
v.newField(JvmDeclarationOriginKt.OtherOriginFromPure(myClass),
|
||||
ACC_PUBLIC | ACC_STATIC | ACC_FINAL,
|
||||
field.name, field.type.getDescriptor(), null, null);
|
||||
|
||||
if (!state.getClassBuilderMode().generateBodies) return;
|
||||
// Invoke the object constructor but ignore the result because INSTANCE will be initialized in the first line of <init>
|
||||
InstructionAdapter v = createOrGetClInitCodegen().v;
|
||||
markLineNumberForElement(element.getPsiOrParent(), v);
|
||||
v.anew(classAsmType);
|
||||
v.invokespecial(classAsmType.getInternalName(), "<init>", "()V", false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -855,60 +853,6 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeObjects() {
|
||||
if (!DescriptorUtils.isObject(descriptor)) return;
|
||||
if (!state.getClassBuilderMode().generateBodies) return;
|
||||
|
||||
boolean isNonCompanionObject = isNonCompanionObject(descriptor);
|
||||
boolean isInterfaceCompanion = isCompanionObjectInInterfaceNotIntrinsic(descriptor);
|
||||
boolean isMappedIntrinsicCompanionObject = isMappedIntrinsicCompanionObject(descriptor);
|
||||
if (isNonCompanionObject || isInterfaceCompanion || isMappedIntrinsicCompanionObject) {
|
||||
ExpressionCodegen clInitCodegen = createOrGetClInitCodegen();
|
||||
InstructionAdapter v = clInitCodegen.v;
|
||||
markLineNumberForElement(element.getPsiOrParent(), v);
|
||||
v.anew(classAsmType);
|
||||
v.dup();
|
||||
v.invokespecial(classAsmType.getInternalName(), "<init>", "()V", false);
|
||||
|
||||
//local0 emulates this in object constructor
|
||||
int local0Index = clInitCodegen.getFrameMap().enterTemp(classAsmType);
|
||||
assert local0Index == 0 : "Local variable with index 0 in clInit should be used only for singleton instance keeping";
|
||||
StackValue.Local local0 = StackValue.local(0, classAsmType);
|
||||
local0.store(StackValue.onStack(classAsmType), clInitCodegen.v);
|
||||
StackValue.Field singleton =
|
||||
StackValue.createSingletonViaInstance(
|
||||
descriptor, typeMapper, isInterfaceCompanion ? HIDDEN_INSTANCE_FIELD : INSTANCE_FIELD
|
||||
);
|
||||
singleton.store(local0, clInitCodegen.v);
|
||||
|
||||
generateInitializers(clInitCodegen);
|
||||
|
||||
if (isInterfaceCompanion) {
|
||||
//initialize singleton instance in outer by hidden instance
|
||||
StackValue.singleton(descriptor, typeMapper).store(
|
||||
singleton, getParentCodegen().createOrGetClInitCodegen().v, true
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (isCompanionObjectWithBackingFieldsInOuter(descriptor)) {
|
||||
ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen();
|
||||
ExpressionCodegen parentClInitCodegen = parentCodegen.createOrGetClInitCodegen();
|
||||
InstructionAdapter parentVisitor = parentClInitCodegen.v;
|
||||
|
||||
FunctionDescriptor constructor = (FunctionDescriptor) parentCodegen.context.accessibleDescriptor(
|
||||
CollectionsKt.single(descriptor.getConstructors()), /* superCallExpression = */ null
|
||||
);
|
||||
parentCodegen.generateMethodCallTo(constructor, null, parentVisitor);
|
||||
StackValue instance = StackValue.onStack(parentCodegen.typeMapper.mapClass(descriptor));
|
||||
StackValue.singleton(descriptor, parentCodegen.typeMapper).store(instance, parentVisitor, true);
|
||||
|
||||
generateInitializers(parentClInitCodegen);
|
||||
}
|
||||
else {
|
||||
assert false : "Unknown object type: " + descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
private void generateCompanionObjectBackingFieldCopies() {
|
||||
if (companionObjectPropertiesToCopy == null) return;
|
||||
|
||||
@@ -956,6 +900,17 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
field.store(property, codegen.v);
|
||||
}
|
||||
|
||||
private void generateCompanionObjectInitializer(@NotNull ClassDescriptor companionObject) {
|
||||
ExpressionCodegen codegen = createOrGetClInitCodegen();
|
||||
|
||||
FunctionDescriptor constructor = (FunctionDescriptor) context.accessibleDescriptor(
|
||||
CollectionsKt.single(companionObject.getConstructors()), /* superCallExpression = */ null
|
||||
);
|
||||
generateMethodCallTo(constructor, null, codegen.v);
|
||||
StackValue instance = StackValue.onStack(typeMapper.mapClass(companionObject));
|
||||
StackValue.singleton(companionObject, typeMapper).store(instance, codegen.v, true);
|
||||
}
|
||||
|
||||
private void generatePrimaryConstructor(DelegationFieldsInfo delegationFieldsInfo) {
|
||||
if (isInterface(descriptor) || isAnnotationClass(descriptor)) return;
|
||||
|
||||
@@ -1023,6 +978,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor,
|
||||
getDelegationConstructorCall(bindingContext, constructorDescriptor));
|
||||
|
||||
if (isNonCompanionObject(descriptor)) {
|
||||
StackValue.singletonViaInstance(descriptor, typeMapper).store(StackValue.LOCAL_0, iv);
|
||||
}
|
||||
|
||||
for (KtSuperTypeListEntry specifier : myClass.getSuperTypeListEntries()) {
|
||||
if (specifier instanceof KtDelegatedSuperTypeEntry) {
|
||||
genCallToDelegatorByExpressionSpecifier(iv, codegen, (KtDelegatedSuperTypeEntry) specifier, fieldsInfo);
|
||||
@@ -1044,10 +1003,18 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
curParam++;
|
||||
}
|
||||
|
||||
//object initialization was moved to initializeObjects()
|
||||
if (!isObject(descriptor)) {
|
||||
if (isCompanionObject(descriptor)) {
|
||||
ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen();
|
||||
parentCodegen.generateCompanionObjectInitializer(descriptor);
|
||||
}
|
||||
|
||||
if (JvmAbi.isCompanionObjectWithBackingFieldsInOuter(descriptor)) {
|
||||
generateInitializers(((ImplementationBodyCodegen) getParentCodegen())::createOrGetClInitCodegen);
|
||||
}
|
||||
else {
|
||||
generateInitializers(codegen);
|
||||
}
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
}
|
||||
|
||||
@@ -1192,7 +1159,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
if (asmType == null) continue;
|
||||
|
||||
result.addField((KtDelegatedSuperTypeEntry) specifier, asmType, DELEGATE_SUPER_FIELD_PREFIX + n);
|
||||
result.addField((KtDelegatedSuperTypeEntry) specifier, asmType, JvmAbi.DELEGATE_SUPER_FIELD_PREFIX + n);
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmConstantsKt;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor;
|
||||
@@ -295,7 +294,8 @@ public class JvmCodegenUtil {
|
||||
}
|
||||
|
||||
public static boolean isInlinedJavaConstProperty(VariableDescriptor descriptor) {
|
||||
return descriptor instanceof JavaPropertyDescriptor && descriptor.isConst();
|
||||
if (!(descriptor instanceof JavaPropertyDescriptor)) return false;
|
||||
return descriptor.isConst();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -31,10 +31,8 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotated;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer;
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
@@ -43,7 +41,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.codegen.AsmUtil.writeAnnotationData;
|
||||
import static org.jetbrains.kotlin.load.java.JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME;
|
||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
@@ -107,12 +104,6 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
|
||||
|
||||
WriteAnnotationUtilKt.writeKotlinMetadata(v, state, KotlinClassHeader.Kind.FILE_FACADE, 0, av -> {
|
||||
writeAnnotationData(av, serializedPart.getFirst(), serializedPart.getSecond());
|
||||
|
||||
FqName kotlinPackageFqName = element.getPackageFqName();
|
||||
if (!kotlinPackageFqName.equals(JvmClassName.byInternalName(packagePartType.getInternalName()).getPackageFqName())) {
|
||||
av.visit(METADATA_PACKAGE_NAME_FIELD_NAME, kotlinPackageFqName.asString());
|
||||
}
|
||||
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -621,8 +621,8 @@ public abstract class StackValue {
|
||||
return field(FieldInfo.createForSingleton(classDescriptor, typeMapper), none());
|
||||
}
|
||||
|
||||
public static Field createSingletonViaInstance(@NotNull ClassDescriptor classDescriptor, @NotNull KotlinTypeMapper typeMapper, @NotNull String name) {
|
||||
return field(FieldInfo.createSingletonViaInstance(classDescriptor, typeMapper, name), none());
|
||||
public static Field singletonViaInstance(ClassDescriptor classDescriptor, KotlinTypeMapper typeMapper) {
|
||||
return field(FieldInfo.createSingletonViaInstance(classDescriptor, typeMapper), none());
|
||||
}
|
||||
|
||||
public static StackValue operation(Type type, Function1<InstructionAdapter, Unit> lambda) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
|
||||
import org.jetbrains.kotlin.codegen.when.SwitchCodegenProvider;
|
||||
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
@@ -57,7 +56,6 @@ import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.EnumValue;
|
||||
import org.jetbrains.kotlin.resolve.constants.NullValue;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionsOnDeclarationBodyChecker;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
@@ -88,8 +86,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
private final JvmRuntimeTypes runtimeTypes;
|
||||
private final TypeMappingConfiguration<Type> typeMappingConfiguration;
|
||||
private final SwitchCodegenProvider switchCodegenProvider;
|
||||
private final LanguageVersionSettings languageVersionSettings;
|
||||
private final ClassBuilderMode classBuilderMode;
|
||||
|
||||
public CodegenAnnotatingVisitor(@NotNull GenerationState state) {
|
||||
this.bindingTrace = state.getBindingTrace();
|
||||
@@ -98,8 +94,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
this.runtimeTypes = state.getJvmRuntimeTypes();
|
||||
this.typeMappingConfiguration = state.getTypeMapper().getTypeMappingConfiguration();
|
||||
this.switchCodegenProvider = new SwitchCodegenProvider(state);
|
||||
this.languageVersionSettings = state.getLanguageVersionSettings();
|
||||
this.classBuilderMode = state.getClassBuilderMode();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -417,8 +411,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
// working around a problem with shallow analysis
|
||||
if (descriptor == null) return;
|
||||
|
||||
checkRuntimeAsserionsOnDeclarationBody(property, descriptor);
|
||||
|
||||
if (descriptor instanceof LocalVariableDescriptor) {
|
||||
recordLocalVariablePropertyMetadata((LocalVariableDescriptor) descriptor);
|
||||
}
|
||||
@@ -455,14 +447,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
nameStack.pop();
|
||||
}
|
||||
|
||||
private void checkRuntimeAsserionsOnDeclarationBody(@NotNull KtDeclaration declaration, DeclarationDescriptor descriptor) {
|
||||
if (classBuilderMode.generateBodies) {
|
||||
// NB This is required only for bodies generation.
|
||||
// In light class generation can cause recursion in types resolution.
|
||||
RuntimeAssertionsOnDeclarationBodyChecker.check(declaration, descriptor, bindingTrace, languageVersionSettings);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Type getMetadataOwner(@NotNull KtProperty property) {
|
||||
for (int i = classStack.size() - 1; i >= 0; i--) {
|
||||
@@ -485,24 +469,12 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
return Type.getObjectType(JvmFileClassUtil.getFileClassInternalName(property.getContainingKtFile()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPropertyAccessor(@NotNull KtPropertyAccessor accessor) {
|
||||
PropertyAccessorDescriptor accessorDescriptor = bindingContext.get(PROPERTY_ACCESSOR, accessor);
|
||||
if (accessorDescriptor != null) {
|
||||
checkRuntimeAsserionsOnDeclarationBody(accessor, accessorDescriptor);
|
||||
}
|
||||
|
||||
super.visitPropertyAccessor(accessor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNamedFunction(@NotNull KtNamedFunction function) {
|
||||
FunctionDescriptor functionDescriptor = (FunctionDescriptor) bindingContext.get(DECLARATION_TO_DESCRIPTOR, function);
|
||||
// working around a problem with shallow analysis
|
||||
if (functionDescriptor == null) return;
|
||||
|
||||
checkRuntimeAsserionsOnDeclarationBody(function, functionDescriptor);
|
||||
|
||||
String nameForClassOrPackageMember = getNameForClassOrPackageMember(functionDescriptor);
|
||||
|
||||
if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend()) {
|
||||
|
||||
@@ -20,8 +20,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForSuspendCoroutineOrReturn
|
||||
import org.jetbrains.kotlin.codegen.coroutines.isBuiltInSuspendCoroutineOrReturnInJvm
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.bytecode
|
||||
@@ -30,9 +29,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.isInlineOnly
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
@@ -50,15 +47,13 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
|
||||
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
protected val codegen: T,
|
||||
@@ -184,46 +179,6 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun canSkipStackSpillingOnInline(methodNode: MethodNode): Boolean {
|
||||
// Stack spilling before inline function 'f' call is required if:
|
||||
// - 'f' is a suspend function
|
||||
// - 'f' has try-catch blocks
|
||||
// - 'f' has loops
|
||||
//
|
||||
// Instead of checking for loops precisely, we just check if there are any backward jumps -
|
||||
// that is, a jump from instruction #i to instruction #j where j < i
|
||||
|
||||
if (functionDescriptor.isSuspend) return false
|
||||
if (methodNode.tryCatchBlocks.isNotEmpty()) return false
|
||||
|
||||
fun isBackwardJump(fromIndex: Int, toLabel: LabelNode) =
|
||||
methodNode.instructions.indexOf(toLabel) < fromIndex
|
||||
|
||||
val insns = methodNode.instructions.toArray()
|
||||
for (i in insns.indices) {
|
||||
val insn = insns[i]
|
||||
when (insn) {
|
||||
is JumpInsnNode ->
|
||||
if (isBackwardJump(i, insn.label)) return false
|
||||
|
||||
is LookupSwitchInsnNode -> {
|
||||
insn.dflt?.let {
|
||||
if (isBackwardJump(i, it)) return false
|
||||
}
|
||||
if (insn.labels.any { isBackwardJump(i, it) }) return false
|
||||
}
|
||||
|
||||
is TableSwitchInsnNode -> {
|
||||
insn.dflt?.let {
|
||||
if (isBackwardJump(i, it)) return false
|
||||
}
|
||||
if (insn.labels.any { isBackwardJump(i, it) }) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun inlineCall(nodeAndSmap: SMAPAndMethodNode, callDefault: Boolean): InlineResult {
|
||||
assert(delayedHiddenWriting == null) { "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'" }
|
||||
@@ -246,11 +201,7 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
//through generation captured parameters will be added to invocationParamBuilder
|
||||
putClosureParametersOnStack()
|
||||
|
||||
val shouldSpillStack = !canSkipStackSpillingOnInline(node)
|
||||
|
||||
if (shouldSpillStack) {
|
||||
addInlineMarker(codegen.v, true)
|
||||
}
|
||||
addInlineMarker(codegen.v, true)
|
||||
|
||||
val parameters = invocationParamBuilder.buildParameters()
|
||||
|
||||
@@ -263,8 +214,7 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
node, parameters, info, FieldRemapper(null, null, parameters), isSameModule,
|
||||
"Method inlining " + sourceCompiler.callElementText,
|
||||
createNestedSourceMapper(nodeAndSmap, defaultSourceMapper), info.callSiteInfo,
|
||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null,
|
||||
!isInlinedToInlineFunInKotlinRuntime()
|
||||
if (functionDescriptor.isInlineOnly()) InlineOnlySmapSkipper(codegen) else null
|
||||
) //with captured
|
||||
|
||||
val remapper = LocalVarRemapper(parameters, initialFrameSize)
|
||||
@@ -289,23 +239,13 @@ abstract class InlineCodegen<out T: BaseExpressionCodegen>(
|
||||
|
||||
adapter.accept(MethodBodyVisitor(codegen.v))
|
||||
|
||||
if (shouldSpillStack) {
|
||||
addInlineMarker(codegen.v, false)
|
||||
}
|
||||
addInlineMarker(codegen.v, false)
|
||||
|
||||
defaultSourceMapper.callSiteMarker = null
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
|
||||
val codegen = this.codegen as? ExpressionCodegen ?: return false
|
||||
val caller = codegen.context.functionDescriptor
|
||||
if (!caller.isInline) return false
|
||||
val callerPackage = DescriptorUtils.getParentOfType(caller, PackageFragmentDescriptor::class.java) ?: return false
|
||||
return callerPackage.fqName.asString().startsWith("kotlin.")
|
||||
}
|
||||
|
||||
private fun generateClosuresBodies() {
|
||||
for (info in expressionMap.values) {
|
||||
info.generateLambdaBody(sourceCompiler, reifiedTypeInliner)
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.ClosureCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.ApiVersionCallsPreprocessingMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
@@ -47,8 +46,7 @@ class MethodInliner(
|
||||
private val errorPrefix: String,
|
||||
private val sourceMapper: SourceMapper,
|
||||
private val inlineCallSiteInfo: InlineCallSiteInfo,
|
||||
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper?, //non null only for root
|
||||
private val shouldPreprocessApiVersionCalls: Boolean = false
|
||||
private val inlineOnlySmapSkipper: InlineOnlySmapSkipper? //non null only for root
|
||||
) {
|
||||
private val typeMapper = inliningContext.state.typeMapper
|
||||
private val invokeCalls = ArrayList<InvokeCall>()
|
||||
@@ -396,9 +394,9 @@ class MethodInliner(
|
||||
): MethodNode {
|
||||
val processingNode = prepareNode(node, finallyDeepShift)
|
||||
|
||||
preprocessNodeBeforeInline(processingNode, labelOwner)
|
||||
normalizeLocalReturns(processingNode, labelOwner)
|
||||
|
||||
val sources = analyzeMethodNodeBeforeInline(processingNode)
|
||||
val sources = analyzeMethodNodeWithoutMandatoryTransformations(processingNode)
|
||||
|
||||
val toDelete = SmartSet.create<AbstractInsnNode>()
|
||||
val instructions = processingNode.instructions
|
||||
@@ -510,19 +508,7 @@ class MethodInliner(
|
||||
return processingNode
|
||||
}
|
||||
|
||||
private fun preprocessNodeBeforeInline(node: MethodNode, labelOwner: LabelOwner) {
|
||||
try {
|
||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
throw wrapException(e, node, "couldn't inline method call")
|
||||
}
|
||||
|
||||
if (shouldPreprocessApiVersionCalls) {
|
||||
val targetApiVersion = inliningContext.state.languageVersionSettings.apiVersion
|
||||
ApiVersionCallsPreprocessingMethodTransformer(targetApiVersion).transform("fake", node)
|
||||
}
|
||||
|
||||
private fun normalizeLocalReturns(node: MethodNode, labelOwner: LabelOwner) {
|
||||
val frames = analyzeMethodNodeBeforeInline(node)
|
||||
|
||||
val localReturnsNormalizer = LocalReturnsNormalizer()
|
||||
@@ -554,6 +540,17 @@ class MethodInliner(
|
||||
return info != null && info.shouldRegenerate(true)
|
||||
}
|
||||
|
||||
private fun analyzeMethodNodeBeforeInline(node: MethodNode): Array<Frame<SourceValue>?> {
|
||||
try {
|
||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
throw wrapException(e, node, "couldn't inline method call")
|
||||
}
|
||||
|
||||
return analyzeMethodNodeWithoutMandatoryTransformations(node)
|
||||
}
|
||||
|
||||
private fun buildConstructorInvocation(
|
||||
anonymousType: String,
|
||||
desc: String,
|
||||
@@ -732,7 +729,7 @@ class MethodInliner(
|
||||
)
|
||||
}
|
||||
|
||||
private fun analyzeMethodNodeBeforeInline(node: MethodNode): Array<Frame<SourceValue>?> {
|
||||
private fun analyzeMethodNodeWithoutMandatoryTransformations(node: MethodNode): Array<Frame<SourceValue>?> {
|
||||
val analyzer = object : Analyzer<SourceValue>(SourceInterpreter()) {
|
||||
override fun newFrame(nLocals: Int, nStack: Int): Frame<SourceValue> {
|
||||
return object : Frame<SourceValue>(nLocals, nStack) {
|
||||
|
||||
@@ -69,7 +69,7 @@ internal const val THIS = "this"
|
||||
internal const val THIS_0 = "this$0"
|
||||
internal const val FIRST_FUN_LABEL = "$$$$\$ROOT$$$$$"
|
||||
internal const val SPECIAL_TRANSFORMATION_NAME = "\$special"
|
||||
const val INLINE_TRANSFORMATION_SUFFIX = "\$inlined"
|
||||
internal const val INLINE_TRANSFORMATION_SUFFIX = "\$inlined"
|
||||
internal const val INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX
|
||||
internal const val INLINE_FUN_THIS_0_SUFFIX = "\$inline_fun"
|
||||
internal const val DEFAULT_LAMBDA_FAKE_CALL = "$$\$DEFAULT_LAMBDA_FAKE_CALL$$$"
|
||||
|
||||
@@ -36,9 +36,9 @@ object LateinitIsInitialized : IntrinsicPropertyGetter() {
|
||||
private fun getStackValue(resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): StackValue? {
|
||||
val expression =
|
||||
(resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression as? KtCallableReferenceExpression ?: return null
|
||||
val referenceResolvedCall = expression.callableReference.getResolvedCallWithAssert(codegen.bindingContext)
|
||||
|
||||
val receiver = codegen.generateCallableReferenceReceiver(referenceResolvedCall) ?: StackValue.none()
|
||||
// TODO: support properties imported from objects as soon as KT-18982 is fixed
|
||||
val receiver = expression.receiverExpression?.let(codegen::gen) ?: StackValue.none()
|
||||
|
||||
val target = expression.callableReference.getResolvedCallWithAssert(codegen.bindingContext).resultingDescriptor
|
||||
return codegen.intermediateValueForProperty(target as PropertyDescriptor, true, false, null, false, receiver, null, true)
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* 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.optimization.boxing.isMethodInsnWith
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.MavenComparableVersion
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion: ApiVersion) : MethodTransformer() {
|
||||
private val constantConditionElimination = ConstantConditionEliminationMethodTransformer()
|
||||
|
||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||
var hasFoldedCalls = false
|
||||
|
||||
for (insn in methodNode.instructions.toArray()) {
|
||||
if (!insn.isApiVersionIsAtLeastCall()) continue
|
||||
|
||||
val prev3 = insn.previous ?: continue
|
||||
val minor = prev3.getIntConstValue() ?: continue
|
||||
|
||||
val prev2 = prev3.previous ?: continue
|
||||
val major = prev2.getIntConstValue() ?: continue
|
||||
|
||||
val prev1 = prev2.previous ?: continue
|
||||
val epic = prev1.getIntConstValue() ?: continue
|
||||
|
||||
hasFoldedCalls = true
|
||||
|
||||
val atLeastVersion = MavenComparableVersion("$epic.$major.$minor")
|
||||
|
||||
val replacementInsn =
|
||||
if (targetApiVersion.version >= atLeastVersion)
|
||||
InsnNode(Opcodes.ICONST_1)
|
||||
else
|
||||
InsnNode(Opcodes.ICONST_0)
|
||||
|
||||
methodNode.instructions.run {
|
||||
remove(prev1)
|
||||
remove(prev2)
|
||||
remove(prev3)
|
||||
set(insn, replacementInsn)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFoldedCalls) {
|
||||
constantConditionElimination.transform(internalClassName, methodNode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean =
|
||||
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||
owner.startsWith("kotlin/internal") &&
|
||||
name == "apiVersionIsAtLeast" &&
|
||||
desc == "(III)Z"
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.getIntConstValue(): Int? =
|
||||
when (this) {
|
||||
is InsnNode ->
|
||||
if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5)
|
||||
opcode - Opcodes.ICONST_0
|
||||
else
|
||||
null
|
||||
|
||||
is IntInsnNode ->
|
||||
when (opcode) {
|
||||
Opcodes.BIPUSH -> operand
|
||||
Opcodes.SIPUSH -> operand
|
||||
else -> null
|
||||
}
|
||||
|
||||
is LdcInsnNode -> cst as? Int
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ 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.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.isPseudo
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
@@ -74,8 +72,6 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() {
|
||||
ProgressionIteratorBasicValue.byProgressionClassType(values[0].type)
|
||||
insn.isNextMethodCallOfProgressionIterator(values) ->
|
||||
NotNullBasicValue(resultType)
|
||||
insn.isPseudo(PseudoInsn.AS_NOT_NULL) ->
|
||||
NotNullBasicValue(values[0].type)
|
||||
else ->
|
||||
defaultResult
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ 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.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.debugText
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.isInsn
|
||||
@@ -26,12 +28,12 @@ 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.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.asNotNull
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.isPseudo
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
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.*
|
||||
|
||||
@@ -44,19 +46,59 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
private var changes = false
|
||||
|
||||
fun run(): Boolean {
|
||||
if (methodNode.instructions.toArray().none { it.isOptimizable() }) return false
|
||||
val checkedReferenceTypes = analyzeTypesAndRemoveDeadCode()
|
||||
eliminateRedundantChecks(checkedReferenceTypes)
|
||||
|
||||
val nullabilityAssumptions = NullabilityAssumptionsBuilder().injectNullabilityAssumptions()
|
||||
return changes
|
||||
}
|
||||
|
||||
private fun analyzeTypesAndRemoveDeadCode(): Map<AbstractInsnNode, Type> {
|
||||
val insns = methodNode.instructions.toArray()
|
||||
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
|
||||
|
||||
val relevantReferenceTypes = HashMap<AbstractInsnNode, Type>()
|
||||
insnLoop@ for (i in insns.indices) {
|
||||
val insn = insns[i]
|
||||
val frame = frames[i] ?: continue
|
||||
when {
|
||||
insn.isInstanceOfOrNullCheck() ->
|
||||
relevantReferenceTypes[insn] = frame.top()?.type ?: continue@insnLoop
|
||||
insn.isCheckParameterIsNotNull() ||
|
||||
insn.isCheckExpressionValueIsNotNull() ->
|
||||
relevantReferenceTypes[insn] = frame.peek(1)?.type ?: continue@insnLoop
|
||||
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) -> {
|
||||
val previous = insn.previous ?: continue@insnLoop
|
||||
if (previous.opcode != Opcodes.ASTORE) continue@insnLoop
|
||||
val previousFrame = frames[i - 1] ?: continue@insnLoop
|
||||
relevantReferenceTypes[insn] = previousFrame.top()?.type ?: continue@insnLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dceResult = DeadCodeEliminationMethodTransformer().removeDeadCodeByFrames(methodNode, frames)
|
||||
if (dceResult.hasRemovedAnything()) {
|
||||
changes = true
|
||||
}
|
||||
|
||||
return relevantReferenceTypes
|
||||
}
|
||||
|
||||
private fun eliminateRedundantChecks(
|
||||
checkedReferenceTypes: Map<AbstractInsnNode, Type>
|
||||
) {
|
||||
val nullabilityAssumptions = injectNullabilityAssumptions(checkedReferenceTypes)
|
||||
|
||||
val nullabilityMap = analyzeNullabilities()
|
||||
|
||||
nullabilityAssumptions.revert()
|
||||
|
||||
transformTrivialChecks(nullabilityMap)
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
private fun injectNullabilityAssumptions(
|
||||
checkedReferenceTypes: Map<AbstractInsnNode, Type>
|
||||
) = NullabilityAssumptionsBuilder(checkedReferenceTypes).injectNullabilityAssumptions()
|
||||
|
||||
private fun analyzeNullabilities(): Map<AbstractInsnNode, StrictBasicValue> {
|
||||
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
|
||||
val insns = methodNode.instructions.toArray()
|
||||
@@ -78,12 +120,6 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
return nullabilityMap
|
||||
}
|
||||
|
||||
private fun AbstractInsnNode.isOptimizable() =
|
||||
opcode == Opcodes.IFNULL ||
|
||||
opcode == Opcodes.IFNONNULL ||
|
||||
opcode == Opcodes.INSTANCEOF ||
|
||||
isCheckExpressionValueIsNotNull()
|
||||
|
||||
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) {
|
||||
for ((insn, value) in nullabilityMap) {
|
||||
val nullability = value.getNullability()
|
||||
@@ -144,7 +180,9 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
}
|
||||
}
|
||||
|
||||
private inner class NullabilityAssumptionsBuilder {
|
||||
private inner class NullabilityAssumptionsBuilder(
|
||||
val relatedReferenceTypes: Map<AbstractInsnNode, Type>
|
||||
) {
|
||||
|
||||
private val checksDependingOnVariable = HashMap<Int, MutableList<AbstractInsnNode>>()
|
||||
|
||||
@@ -208,7 +246,9 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
val nullabilityAssumptions = NullabilityAssumptions()
|
||||
for ((varIndex, dependentChecks) in checksDependingOnVariable) {
|
||||
for (checkInsn in dependentChecks) {
|
||||
nullabilityAssumptions.injectAssumptionsForInsn(varIndex, checkInsn)
|
||||
val varType = relatedReferenceTypes[checkInsn]
|
||||
?: AsmTypes.OBJECT_TYPE
|
||||
nullabilityAssumptions.injectAssumptionsForInsn(varIndex, checkInsn, varType)
|
||||
}
|
||||
}
|
||||
for (insn in methodNode.instructions) {
|
||||
@@ -219,28 +259,28 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
return nullabilityAssumptions
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForInsn(varIndex: Int, insn: AbstractInsnNode) {
|
||||
private fun NullabilityAssumptions.injectAssumptionsForInsn(varIndex: Int, insn: AbstractInsnNode, varType: Type) {
|
||||
when (insn.opcode) {
|
||||
Opcodes.IFNULL,
|
||||
Opcodes.IFNONNULL ->
|
||||
injectAssumptionsForNullCheck(varIndex, insn as JumpInsnNode)
|
||||
injectAssumptionsForNullCheck(varIndex, insn as JumpInsnNode, varType)
|
||||
Opcodes.INVOKESTATIC -> {
|
||||
when {
|
||||
insn.isCheckParameterIsNotNull() ||
|
||||
insn.isCheckExpressionValueIsNotNull() ->
|
||||
injectAssumptionsForNotNullAssertion(varIndex, insn)
|
||||
injectAssumptionsForNotNullAssertion(varIndex, insn, varType)
|
||||
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) ->
|
||||
injectCodeForStoreNotNull(insn)
|
||||
injectCodeForStoreNotNull(insn, varType)
|
||||
else ->
|
||||
throw AssertionError("Expected non-null assertion: ${insn.debugText}")
|
||||
}
|
||||
}
|
||||
Opcodes.INSTANCEOF ->
|
||||
injectAssumptionsForInstanceOfCheck(varIndex, insn)
|
||||
injectAssumptionsForInstanceOfCheck(varIndex, insn, varType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForNullCheck(varIndex: Int, insn: JumpInsnNode) {
|
||||
private fun NullabilityAssumptions.injectAssumptionsForNullCheck(varIndex: Int, insn: JumpInsnNode, varType: Type) {
|
||||
// ALOAD v
|
||||
// IFNULL L
|
||||
// <...> -- v is not null here
|
||||
@@ -260,16 +300,15 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
|
||||
insert(insertAfterNull, listOfSynthetics {
|
||||
aconst(null)
|
||||
store(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
store(varIndex, varType)
|
||||
if (jumpsIfNull) {
|
||||
goTo(originalLabel.label)
|
||||
}
|
||||
})
|
||||
|
||||
insert(insertAfterNonNull, listOfSynthetics {
|
||||
load(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
asNotNull()
|
||||
store(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
anew(varType)
|
||||
store(varIndex, varType)
|
||||
if (!jumpsIfNull) {
|
||||
goTo(originalLabel.label)
|
||||
}
|
||||
@@ -277,7 +316,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForNotNullAssertion(varIndex: Int, insn: AbstractInsnNode) {
|
||||
private fun NullabilityAssumptions.injectAssumptionsForNotNullAssertion(varIndex: Int, insn: AbstractInsnNode, varType: Type) {
|
||||
// ALOAD v
|
||||
// LDC *
|
||||
// INVOKESTATIC checkParameterIsNotNull
|
||||
@@ -290,13 +329,12 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
// <...> -- v is not null here (otherwise an exception was thrown)
|
||||
|
||||
methodNode.instructions.insert(insn, listOfSynthetics {
|
||||
load(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
asNotNull()
|
||||
store(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
anew(varType)
|
||||
store(varIndex, varType)
|
||||
})
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectAssumptionsForInstanceOfCheck(varIndex: Int, insn: AbstractInsnNode) {
|
||||
private fun NullabilityAssumptions.injectAssumptionsForInstanceOfCheck(varIndex: Int, insn: AbstractInsnNode, varType: Type) {
|
||||
// ALOAD v
|
||||
// INSTANCEOF T
|
||||
// IFEQ L
|
||||
@@ -326,9 +364,8 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
|
||||
methodNode.instructions.run {
|
||||
insert(insertAfterNotNull, listOfSynthetics {
|
||||
load(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
asNotNull()
|
||||
store(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
anew(varType)
|
||||
store(varIndex, varType)
|
||||
if (originalLabel != null) {
|
||||
goTo(originalLabel.label)
|
||||
}
|
||||
@@ -347,7 +384,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
methodNode.maxStack = methodNode.maxStack + 1 //will be recalculated in prepareForEmitting
|
||||
}
|
||||
|
||||
private fun NullabilityAssumptions.injectCodeForStoreNotNull(insn: AbstractInsnNode) {
|
||||
private fun NullabilityAssumptions.injectCodeForStoreNotNull(insn: AbstractInsnNode, varType: Type) {
|
||||
// ASTORE v
|
||||
// [STORE_NOT_NULL]
|
||||
// <...> -- v is not null here because codegen told us so
|
||||
@@ -356,10 +393,8 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
||||
|
||||
methodNode.instructions.run {
|
||||
insert(insn, listOfSynthetics {
|
||||
val varIndex = (previous as VarInsnNode).`var`
|
||||
load(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
asNotNull()
|
||||
store(varIndex, AsmTypes.OBJECT_TYPE)
|
||||
anew(varType)
|
||||
store((previous as VarInsnNode).`var`, varType)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -419,6 +454,13 @@ internal fun AbstractInsnNode.isThrowIntrinsic() =
|
||||
name in THROW_INTRINSIC_METHOD_NAMES
|
||||
}
|
||||
|
||||
internal fun AbstractInsnNode.isThrowIntrinsicWithoutArguments() =
|
||||
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
|
||||
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
|
||||
name in THROW_INTRINSIC_METHOD_NAMES &&
|
||||
desc == "()V"
|
||||
}
|
||||
|
||||
internal val THROW_INTRINSIC_METHOD_NAMES =
|
||||
setOf(
|
||||
"throwNpe",
|
||||
|
||||
@@ -31,8 +31,7 @@ enum class PseudoInsn(val signature: String = "()V") {
|
||||
FAKE_ALWAYS_FALSE_IFEQ("()I"),
|
||||
SAVE_STACK_BEFORE_TRY,
|
||||
RESTORE_STACK_IN_TRY_CATCH,
|
||||
STORE_NOT_NULL,
|
||||
AS_NOT_NULL("(Ljava/lang/Object;)Ljava/lang/Object;")
|
||||
STORE_NOT_NULL
|
||||
;
|
||||
|
||||
fun emit(iv: InstructionAdapter) {
|
||||
@@ -73,9 +72,5 @@ fun InstructionAdapter.storeNotNull() {
|
||||
PseudoInsn.STORE_NOT_NULL.emit(this)
|
||||
}
|
||||
|
||||
fun InstructionAdapter.asNotNull() {
|
||||
PseudoInsn.AS_NOT_NULL.emit(this)
|
||||
}
|
||||
|
||||
fun AbstractInsnNode.isPseudo(pseudoInsn: PseudoInsn) =
|
||||
pseudoInsn.isa(this)
|
||||
@@ -17,7 +17,8 @@
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.shouldWritePreReleaseFlag
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.java.JvmBytecodeBinaryVersion
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
|
||||
@@ -36,7 +37,7 @@ fun writeKotlinMetadata(
|
||||
av.visit(JvmAnnotationNames.BYTECODE_VERSION_FIELD_NAME, JvmBytecodeBinaryVersion.INSTANCE.toArray())
|
||||
av.visit(JvmAnnotationNames.KIND_FIELD_NAME, kind.id)
|
||||
var flags = extraFlags
|
||||
if (state.languageVersionSettings.shouldWritePreReleaseFlag()) {
|
||||
if (KotlinCompilerVersion.isPreRelease() && state.languageVersionSettings.languageVersion == LanguageVersion.LATEST_STABLE) {
|
||||
flags = flags or JvmAnnotationNames.METADATA_PRE_RELEASE_FLAG
|
||||
}
|
||||
if (flags != 0) {
|
||||
@@ -50,4 +51,4 @@ fun writeSyntheticClassMetadata(cb: ClassBuilder, state: GenerationState) {
|
||||
writeKotlinMetadata(cb, state, KotlinClassHeader.Kind.SYNTHETIC_CLASS, 0) { _ ->
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,8 +74,7 @@ class BuiltInsSerializer(dependOnOldBuiltIns: Boolean) : MetadataSerializer(depe
|
||||
}
|
||||
|
||||
private fun createMessageCollector() = object : GroupingMessageCollector(
|
||||
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, false),
|
||||
false
|
||||
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, /* verbose = */ false)
|
||||
) {
|
||||
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
|
||||
// Only report diagnostics without a particular location because there's plenty of errors in built-in sources
|
||||
|
||||
@@ -110,7 +110,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
return HashMap<AnalysisFlag<*>, Any>().apply {
|
||||
put(AnalysisFlag.skipMetadataVersionCheck, skipMetadataVersionCheck)
|
||||
put(AnalysisFlag.multiPlatformDoNotCheckActual, noCheckActual)
|
||||
put(AnalysisFlag.allowKotlinPackage, allowKotlinPackage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.arguments
|
||||
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import java.io.Serializable
|
||||
|
||||
abstract class CommonToolArguments : Freezable(), Serializable {
|
||||
@@ -23,7 +24,7 @@ abstract class CommonToolArguments : Freezable(), Serializable {
|
||||
@JvmStatic private val serialVersionUID = 0L
|
||||
}
|
||||
|
||||
var freeArgs: List<String> by FreezableVar(emptyList())
|
||||
var freeArgs: MutableList<String> = SmartList()
|
||||
|
||||
@Transient var errors: ArgumentParseErrors = ArgumentParseErrors()
|
||||
|
||||
@@ -43,8 +44,4 @@ abstract class CommonToolArguments : Freezable(), Serializable {
|
||||
@GradleOption(DefaultValues.BooleanFalseDefault::class)
|
||||
@Argument(value = "-nowarn", description = "Generate no warnings")
|
||||
var suppressWarnings: Boolean by FreezableVar(false)
|
||||
|
||||
@GradleOption(DefaultValues.BooleanFalseDefault::class)
|
||||
@Argument(value = "-Werror", description = "Report an error if there are any warnings")
|
||||
var allWarningsAsErrors: Boolean by FreezableVar(false)
|
||||
}
|
||||
|
||||
@@ -106,16 +106,16 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
@Argument(value = "-Xno-param-assertions", description = "Don't generate not-null assertions on parameters of methods accessible from Java")
|
||||
var noParamAssertions: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xstrict-java-nullability-assertions", description = "Generate nullability assertions for non-null Java expressions")
|
||||
var strictJavaNullabilityAssertions: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(value = "-Xno-optimize", description = "Disable optimizations")
|
||||
var noOptimize: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xnormalize-constructor-calls",
|
||||
valueDescription = "{disable|enable}",
|
||||
description = "Normalize constructor calls (disable: don't normalize; enable: normalize), default is disable"
|
||||
valueDescription = "{disable|enable|preserve-class-initialization}",
|
||||
description = "Normalize constructor calls " +
|
||||
"(disable: don't normalize; enable: normalize; " +
|
||||
"preserve-class-initialization: normalize preserving class initialization order), " +
|
||||
"default is disable"
|
||||
)
|
||||
var constructorCallNormalizationMode: String? by FreezableVar(JVMConstructorCallNormalizationMode.DEFAULT.description)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.arguments
|
||||
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import com.intellij.util.SmartList
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
@@ -100,14 +100,12 @@ fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, resu
|
||||
return argument.value == arg
|
||||
}
|
||||
|
||||
val freeArgs = ArrayList<String>()
|
||||
|
||||
var i = 0
|
||||
loop@ while (i < args.size) {
|
||||
val arg = args[i++]
|
||||
|
||||
if (freeArgsStarted) {
|
||||
freeArgs.add(arg)
|
||||
result.freeArgs.add(arg)
|
||||
continue
|
||||
}
|
||||
if (arg == FREE_ARGS_DELIMITER) {
|
||||
@@ -120,7 +118,7 @@ fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, resu
|
||||
when {
|
||||
arg.startsWith(ADVANCED_ARGUMENT_PREFIX) -> errors.unknownExtraFlags.add(arg)
|
||||
arg.startsWith("-") -> errors.unknownArgs.add(arg)
|
||||
else -> freeArgs.add(arg)
|
||||
else -> result.freeArgs.add(arg)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -150,8 +148,6 @@ fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, resu
|
||||
|
||||
updateField(property, result, value, argument.delimiter)
|
||||
}
|
||||
|
||||
result.freeArgs += freeArgs
|
||||
}
|
||||
|
||||
private fun <A : CommonToolArguments> updateField(property: KMutableProperty1<A, Any?>, result: A, value: Any, delimiter: String) {
|
||||
|
||||
@@ -35,14 +35,11 @@ public enum CompilerMessageSeverity {
|
||||
*/
|
||||
OUTPUT;
|
||||
|
||||
public static final EnumSet<CompilerMessageSeverity> ERRORS = EnumSet.of(ERROR, EXCEPTION);
|
||||
public static final EnumSet<CompilerMessageSeverity> VERBOSE = EnumSet.of(LOGGING);
|
||||
|
||||
public boolean isError() {
|
||||
return this == EXCEPTION || this == ERROR;
|
||||
}
|
||||
|
||||
public boolean isWarning() {
|
||||
return this == STRONG_WARNING || this == WARNING;
|
||||
return ERRORS.contains(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -24,19 +24,16 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class GroupingMessageCollector implements MessageCollector {
|
||||
private final MessageCollector delegate;
|
||||
private final boolean treatWarningsAsErrors;
|
||||
|
||||
// Note that the key in this map can be null
|
||||
private final Multimap<CompilerMessageLocation, Message> groupedMessages = LinkedHashMultimap.create();
|
||||
|
||||
public GroupingMessageCollector(@NotNull MessageCollector delegate, boolean treatWarningsAsErrors) {
|
||||
public GroupingMessageCollector(@NotNull MessageCollector delegate) {
|
||||
this.delegate = delegate;
|
||||
this.treatWarningsAsErrors = treatWarningsAsErrors;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,29 +57,17 @@ public class GroupingMessageCollector implements MessageCollector {
|
||||
|
||||
@Override
|
||||
public boolean hasErrors() {
|
||||
return hasExplicitErrors() || (treatWarningsAsErrors && hasWarnings());
|
||||
}
|
||||
|
||||
private boolean hasExplicitErrors() {
|
||||
return groupedMessages.entries().stream().anyMatch(entry -> entry.getValue().severity.isError());
|
||||
}
|
||||
|
||||
private boolean hasWarnings() {
|
||||
return groupedMessages.entries().stream().anyMatch(entry -> entry.getValue().severity.isWarning());
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
boolean hasExplicitErrors = hasExplicitErrors();
|
||||
|
||||
if (treatWarningsAsErrors && !hasExplicitErrors && hasWarnings()) {
|
||||
report(CompilerMessageSeverity.ERROR, "warnings found and -Werror specified", null);
|
||||
}
|
||||
boolean hasErrors = hasErrors();
|
||||
|
||||
List<CompilerMessageLocation> sortedKeys =
|
||||
CollectionsKt.sortedWith(groupedMessages.keySet(), Comparator.nullsFirst(CompilerMessageLocationComparator.INSTANCE));
|
||||
for (CompilerMessageLocation location : sortedKeys) {
|
||||
for (Message message : groupedMessages.get(location)) {
|
||||
if (!hasExplicitErrors || message.severity.isError() || message.severity == CompilerMessageSeverity.STRONG_WARNING) {
|
||||
if (!hasErrors || message.severity.isError() || message.severity == CompilerMessageSeverity.STRONG_WARNING) {
|
||||
delegate.report(message.severity, message.message, message.location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
||||
@NotNull
|
||||
@Override
|
||||
public ExitCode execImpl(@NotNull MessageCollector messageCollector, @NotNull Services services, @NotNull A arguments) {
|
||||
GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector, arguments.getAllWarningsAsErrors());
|
||||
GroupingMessageCollector groupingCollector = new GroupingMessageCollector(messageCollector);
|
||||
|
||||
CompilerConfiguration configuration = new CompilerConfiguration();
|
||||
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, groupingCollector);
|
||||
@@ -204,8 +204,6 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
||||
extraLanguageFeatures.put(LanguageFeature.Coroutines, coroutinesState);
|
||||
}
|
||||
|
||||
setupPlatformSpecificLanguageFeatureSettings(extraLanguageFeatures, arguments);
|
||||
|
||||
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, new LanguageVersionSettingsImpl(
|
||||
languageVersion,
|
||||
ApiVersion.createByLanguageVersion(apiVersion),
|
||||
@@ -214,13 +212,6 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
||||
));
|
||||
}
|
||||
|
||||
protected void setupPlatformSpecificLanguageFeatureSettings(
|
||||
@NotNull Map<LanguageFeature, LanguageFeature.State> extraLanguageFeatures,
|
||||
@NotNull A commandLineArguments
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static KotlinPaths computeKotlinPaths(@NotNull MessageCollector messageCollector, @NotNull CommonCompilerArguments arguments) {
|
||||
KotlinPaths paths;
|
||||
|
||||
@@ -81,7 +81,7 @@ abstract class CLITool<A : CommonToolArguments> {
|
||||
fun exec(messageCollector: MessageCollector, services: Services, arguments: A): ExitCode {
|
||||
printVersionIfNeeded(messageCollector, arguments)
|
||||
|
||||
val fixedMessageCollector = if (arguments.suppressWarnings && !arguments.allWarningsAsErrors) {
|
||||
val fixedMessageCollector = if (arguments.suppressWarnings) {
|
||||
FilteringMessageCollector(messageCollector, Predicate.isEqual(CompilerMessageSeverity.WARNING))
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -180,7 +180,7 @@ object JvmRuntimeVersionsConsistencyChecker {
|
||||
// The "Core" jar files should not be newer than the compiler. This behavior is reserved for the future if we realise that we're
|
||||
// going to break language/library compatibility in such a way that it's easier to make the old compiler just report an error
|
||||
// in the case the new runtime library is specified in the classpath, rather than employing any other compatibility breakage tools
|
||||
// we have at our disposal (Deprecated, SinceKotlin, VersionRequirement in metadata, etc.)
|
||||
// we have at our disposal (Deprecated, SinceKotlin, SinceKotlinInfo in metadata, etc.)
|
||||
if (runtimeJarsInfo.coreJars.map {
|
||||
checkNotNewerThanCompiler(messageCollector, it)
|
||||
}.any { it }) return ClasspathConsistency.InconsistentWithCompilerVersion
|
||||
|
||||
@@ -228,17 +228,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun setupPlatformSpecificLanguageFeatureSettings(
|
||||
extraLanguageFeatures: MutableMap<LanguageFeature, LanguageFeature.State>,
|
||||
commandLineArguments: K2JVMCompilerArguments
|
||||
) {
|
||||
if (commandLineArguments.strictJavaNullabilityAssertions) {
|
||||
extraLanguageFeatures[LanguageFeature.StrictJavaNullabilityAssertions] = LanguageFeature.State.ENABLED
|
||||
}
|
||||
|
||||
super.setupPlatformSpecificLanguageFeatureSettings(extraLanguageFeatures, commandLineArguments)
|
||||
}
|
||||
|
||||
private fun registerJavacIfNeeded(environment: KotlinCoreEnvironment,
|
||||
arguments: K2JVMCompilerArguments): Boolean {
|
||||
if (arguments.useJavac) {
|
||||
@@ -385,6 +374,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
configuration.put(JVMConfigurationKeys.ADD_BUILT_INS_FROM_COMPILER_TO_DEPENDENCIES, arguments.addCompilerBuiltIns)
|
||||
configuration.put(JVMConfigurationKeys.CREATE_BUILT_INS_FROM_MODULE_DEPENDENCIES, arguments.loadBuiltInsFromDependencies)
|
||||
|
||||
|
||||
|
||||
arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,9 @@ package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiManager
|
||||
//import com.intellij.psi.impl.light.LightJavaModule
|
||||
import com.intellij.psi.impl.light.LightJavaModule
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
@@ -92,22 +91,22 @@ class ClasspathRootsResolver(
|
||||
|
||||
for ((root, packagePrefix) in javaSourceRoots) {
|
||||
val modularRoot = modularSourceRoot(root, hasOutputDirectoryInClasspath)
|
||||
if (modularRoot != null) {
|
||||
modules += modularRoot
|
||||
}
|
||||
else {
|
||||
if (modularRoot != null) {
|
||||
modules += modularRoot
|
||||
}
|
||||
else {
|
||||
result += JavaRoot(root, JavaRoot.RootType.SOURCE, packagePrefix?.let { prefix ->
|
||||
if (isValidJavaFqName(prefix)) FqName(prefix)
|
||||
else null.also {
|
||||
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
|
||||
}
|
||||
})
|
||||
if (isValidJavaFqName(prefix)) FqName(prefix)
|
||||
else null.also {
|
||||
report(STRONG_WARNING, "Invalid package prefix name is ignored: $prefix")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (root in jvmClasspathRoots) {
|
||||
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
||||
}
|
||||
result += JavaRoot(root, JavaRoot.RootType.BINARY)
|
||||
}
|
||||
|
||||
val outputDirectoryAddedAsPartOfModule = modules.any { module -> module.moduleRoots.any { it.file == outputDirectory } }
|
||||
|
||||
@@ -117,19 +116,17 @@ class ClasspathRootsResolver(
|
||||
if (outputDirectoryAddedAsPartOfModule && root == outputDirectory) continue
|
||||
|
||||
val module = modularBinaryRoot(root)
|
||||
if (module != null) {
|
||||
modules += module
|
||||
}
|
||||
}
|
||||
if (module != null) {
|
||||
modules += module
|
||||
}
|
||||
}
|
||||
|
||||
addModularRoots(modules, result)
|
||||
|
||||
return RootsAndModules(result, modules)
|
||||
}
|
||||
|
||||
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, *>? {
|
||||
return null
|
||||
/*
|
||||
private fun findSourceModuleInfo(root: VirtualFile): Pair<VirtualFile, PsiJavaModule>? {
|
||||
val moduleInfoFile =
|
||||
when {
|
||||
root.isDirectory -> root.findChild(PsiJavaModule.MODULE_INFO_FILE)
|
||||
@@ -141,12 +138,9 @@ class ClasspathRootsResolver(
|
||||
val psiJavaModule = psiFile.children.singleOrNull { it is PsiJavaModule } as? PsiJavaModule ?: return null
|
||||
|
||||
return moduleInfoFile to psiJavaModule
|
||||
*/
|
||||
}
|
||||
|
||||
private fun modularSourceRoot(root: VirtualFile, hasOutputDirectoryInClasspath: Boolean): JavaModule.Explicit? {
|
||||
return null
|
||||
/*
|
||||
val (moduleInfoFile, psiJavaModule) = findSourceModuleInfo(root) ?: return null
|
||||
val sourceRoot = JavaModule.Root(root, isBinary = false)
|
||||
val roots =
|
||||
@@ -154,14 +148,19 @@ class ClasspathRootsResolver(
|
||||
listOf(sourceRoot, JavaModule.Root(outputDirectory!!, isBinary = true))
|
||||
else listOf(sourceRoot)
|
||||
return JavaModule.Explicit(JavaModuleInfo.create(psiJavaModule), roots, moduleInfoFile)
|
||||
*/
|
||||
}
|
||||
|
||||
private fun modularBinaryRoot(root: VirtualFile): JavaModule? {
|
||||
return null
|
||||
/*
|
||||
val moduleInfoFile = root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE)
|
||||
return if (moduleInfoFile != null) {
|
||||
val isJar = root.fileSystem.protocol == StandardFileSystems.JAR_PROTOCOL
|
||||
val manifest: Attributes? by lazy(NONE) { readManifestAttributes(root) }
|
||||
|
||||
val moduleInfoFile =
|
||||
root.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE)
|
||||
?: root.takeIf { isJar }?.findFileByRelativePath(MULTI_RELEASE_MODULE_INFO_CLS_FILE)?.takeIf {
|
||||
manifest?.getValue(IS_MULTI_RELEASE)?.equals("true", ignoreCase = true) == true
|
||||
}
|
||||
|
||||
if (moduleInfoFile != null) {
|
||||
val moduleInfo = JavaModuleInfo.read(moduleInfoFile) ?: return null
|
||||
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(root, isBinary = true)), moduleInfoFile)
|
||||
}
|
||||
@@ -183,6 +182,8 @@ class ClasspathRootsResolver(
|
||||
}
|
||||
return JavaModule.Automatic(moduleName, moduleRoot)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun readManifestAttributes(jarRoot: VirtualFile): Attributes? {
|
||||
@@ -193,7 +194,6 @@ class ClasspathRootsResolver(
|
||||
catch (e: IOException) {
|
||||
null
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private fun addModularRoots(modules: List<JavaModule>, result: MutableList<JavaRoot>) {
|
||||
@@ -261,9 +261,9 @@ class ClasspathRootsResolver(
|
||||
else {
|
||||
for ((root, isBinary) in module.moduleRoots) {
|
||||
result.add(JavaRoot(root, if (isBinary) JavaRoot.RootType.BINARY else JavaRoot.RootType.SOURCE))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requireStdlibModule && sourceModule != null && KOTLIN_STDLIB_MODULE_NAME !in allDependencies) {
|
||||
report(
|
||||
@@ -320,6 +320,7 @@ class ClasspathRootsResolver(
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MULTI_RELEASE_MODULE_INFO_CLS_FILE = "META-INF/versions/9/${PsiJavaModule.MODULE_INFO_CLS_FILE}"
|
||||
const val AUTOMATIC_MODULE_NAME = "Automatic-Module-Name"
|
||||
const val IS_MULTI_RELEASE = "Multi-Release"
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.core.JavaCoreApplicationEnvironment
|
||||
import com.intellij.core.JavaCoreProjectEnvironment
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.lang.MetaLanguage
|
||||
import com.intellij.lang.java.JavaParserDefinition
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
@@ -120,6 +121,28 @@ class KotlinCoreEnvironment private constructor(
|
||||
override fun preregisterServices() {
|
||||
registerProjectExtensionPoints(Extensions.getArea(project))
|
||||
}
|
||||
|
||||
override fun registerJavaPsiFacade() {
|
||||
with (project) {
|
||||
registerService(CoreJavaFileManager::class.java, ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager)
|
||||
|
||||
val cliLightClassGenerationSupport = CliLightClassGenerationSupport(this)
|
||||
registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CodeAnalyzerInitializer::class.java, cliLightClassGenerationSupport)
|
||||
|
||||
registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager())
|
||||
registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager())
|
||||
|
||||
val area = Extensions.getArea(this)
|
||||
|
||||
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(JavaElementFinder(this, cliLightClassGenerationSupport))
|
||||
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(
|
||||
PsiElementFinderImpl(this, ServiceManager.getService(this, JavaFileManager::class.java)))
|
||||
}
|
||||
|
||||
super.registerJavaPsiFacade()
|
||||
}
|
||||
}
|
||||
|
||||
private val sourceFiles = mutableListOf<KtFile>()
|
||||
@@ -201,12 +224,12 @@ class KotlinCoreEnvironment private constructor(
|
||||
this.initialRoots = initialRoots
|
||||
|
||||
if (!configuration.getBoolean(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK) && messageCollector != null) {
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
}
|
||||
JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency(
|
||||
messageCollector,
|
||||
configuration,
|
||||
initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null }
|
||||
)
|
||||
}
|
||||
|
||||
val (roots, singleJavaFileRoots) =
|
||||
initialRoots.partition { (file) -> file.isDirectory || file.extension != JavaFileType.DEFAULT_EXTENSION }
|
||||
@@ -295,7 +318,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
|
||||
for (packagePartProvider in packagePartProviders) {
|
||||
packagePartProvider.addRoots(newRoots)
|
||||
}
|
||||
}
|
||||
|
||||
return rootsIndex.addNewIndexForRoots(newRoots)?.let { newIndex ->
|
||||
updateClasspathFromRootsIndex(newIndex)
|
||||
@@ -321,9 +344,9 @@ class KotlinCoreEnvironment private constructor(
|
||||
return findLocalFile(root.file.absolutePath).also {
|
||||
if (it == null) {
|
||||
report(STRONG_WARNING, "Classpath entry points to a non-existent location: ${root.file}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findJarRoot(file: File): VirtualFile? =
|
||||
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
|
||||
@@ -437,7 +460,6 @@ class KotlinCoreEnvironment private constructor(
|
||||
Extensions.cleanRootArea(parentDisposable)
|
||||
registerAppExtensionPoints()
|
||||
val applicationEnvironment = object : JavaCoreApplicationEnvironment(parentDisposable) {
|
||||
@Suppress("NOTHING_TO_OVERRIDE")
|
||||
override fun createJrtFileSystem(): VirtualFileSystem? = CoreJrtFileSystem()
|
||||
}
|
||||
|
||||
@@ -464,6 +486,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler::class.java)
|
||||
//
|
||||
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier::class.java)
|
||||
CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaLanguage.EP_NAME, MetaLanguage::class.java)
|
||||
}
|
||||
|
||||
private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) {
|
||||
@@ -531,23 +554,11 @@ class KotlinCoreEnvironment private constructor(
|
||||
}
|
||||
|
||||
private fun registerProjectServicesForCLI(@Suppress("UNUSED_PARAMETER") projectEnvironment: JavaCoreProjectEnvironment) {
|
||||
with (projectEnvironment.project) {
|
||||
registerService(CoreJavaFileManager::class.java, ServiceManager.getService(this, JavaFileManager::class.java) as CoreJavaFileManager)
|
||||
/**
|
||||
* Note that Kapt may restart code analysis process, and CLI services should be aware of that.
|
||||
* Use PsiManager.getModificationTracker() to ensure that all the data you cached is still valid.
|
||||
*/
|
||||
|
||||
val cliLightClassGenerationSupport = CliLightClassGenerationSupport(this)
|
||||
registerService(LightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CliLightClassGenerationSupport::class.java, cliLightClassGenerationSupport)
|
||||
registerService(CodeAnalyzerInitializer::class.java, cliLightClassGenerationSupport)
|
||||
|
||||
registerService(ExternalAnnotationsManager::class.java, MockExternalAnnotationsManager())
|
||||
registerService(InferredAnnotationsManager::class.java, MockInferredAnnotationsManager())
|
||||
|
||||
val area = Extensions.getArea(this)
|
||||
|
||||
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(JavaElementFinder(this, cliLightClassGenerationSupport))
|
||||
area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(
|
||||
PsiElementFinderImpl(this, ServiceManager.getService(this, JavaFileManager::class.java)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.setupJdkClasspathRoots(configFiles: EnvironmentConfigFiles) {
|
||||
|
||||
@@ -26,11 +26,5 @@ open class KotlinCoreProjectEnvironment(
|
||||
disposable: Disposable,
|
||||
applicationEnvironment: JavaCoreApplicationEnvironment
|
||||
) : JavaCoreProjectEnvironment(disposable, applicationEnvironment) {
|
||||
init {
|
||||
myProject.registerService<ControlFlowFactory>(ControlFlowFactory::class.java,
|
||||
ControlFlowFactory(myPsiManager))
|
||||
|
||||
}
|
||||
|
||||
override fun createCoreFileManager() = KotlinCliJavaFileManagerImpl(PsiManager.getInstance(project))
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
@@ -191,15 +191,14 @@ object KotlinToJVMBytecodeCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
val isJava9Module = false
|
||||
/*chunk.any { module ->
|
||||
val isJava9Module = chunk.any { module ->
|
||||
module.getJavaSourceRoots().any { (path, packagePrefix) ->
|
||||
val file = File(path)
|
||||
packagePrefix == null &&
|
||||
(file.name == PsiJavaModule.MODULE_INFO_FILE ||
|
||||
(file.isDirectory && file.listFiles().any { it.name == PsiJavaModule.MODULE_INFO_FILE }))
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
for (module in chunk) {
|
||||
for (classpathRoot in module.getClasspathRoots()) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.*
|
||||
|
||||
class MockExternalAnnotationsManager : ExternalAnnotationsManager() {
|
||||
@@ -40,4 +41,8 @@ class MockExternalAnnotationsManager : ExternalAnnotationsManager() {
|
||||
override fun editExternalAnnotation(listOwner: PsiModifierListOwner, annotationFQN: String, value: Array<out PsiNameValuePair>?): Boolean {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun hasAnnotationRootsForFile(file: VirtualFile): Boolean {
|
||||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@
|
||||
package org.jetbrains.kotlin.cli.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
@@ -41,11 +40,8 @@ class CliJavaModuleFinder(jrtFileSystemRoot: VirtualFile?) : JavaModuleFinder {
|
||||
modulesRoot?.findChild(name)?.let(this::findSystemModule) ?: userModules[name]
|
||||
|
||||
private fun findSystemModule(moduleRoot: VirtualFile): JavaModule.Explicit? {
|
||||
return null
|
||||
/*
|
||||
val file = moduleRoot.findChild(PsiJavaModule.MODULE_INFO_CLS_FILE) ?: return null
|
||||
val moduleInfo = JavaModuleInfo.read(file) ?: return null
|
||||
return JavaModule.Explicit(moduleInfo, listOf(JavaModule.Root(moduleRoot, isBinary = true)), file)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,8 +153,8 @@ class ReplCodeAnalyzer(environment: KotlinCoreEnvironment) {
|
||||
return delegateFactory.getPackageMemberDeclarationProvider(packageFqName)
|
||||
}
|
||||
|
||||
override fun diagnoseMissingPackageFragment(file: KtFile) {
|
||||
delegateFactory.diagnoseMissingPackageFragment(file)
|
||||
override fun diagnoseMissingPackageFragment(fqName: FqName, file: KtFile?) {
|
||||
delegateFactory.diagnoseMissingPackageFragment(fqName, file)
|
||||
}
|
||||
|
||||
class AdaptablePackageMemberDeclarationProvider(
|
||||
|
||||
@@ -28,8 +28,7 @@ class ReplTerminalDiagnosticMessageHolder : MessageCollectorBasedReporter, Diagn
|
||||
private val outputStream = ByteArrayOutputStream()
|
||||
|
||||
override val messageCollector: GroupingMessageCollector = GroupingMessageCollector(
|
||||
PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.WITHOUT_PATHS, false),
|
||||
false
|
||||
PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.WITHOUT_PATHS, false)
|
||||
)
|
||||
|
||||
override val renderedDiagnostics: String
|
||||
|
||||
@@ -69,7 +69,6 @@ messages/**)
|
||||
#-libraryjars '<bootstrap.script.runtime>'
|
||||
#-libraryjars '<tools.jar>'
|
||||
|
||||
-target 1.6
|
||||
-dontoptimize
|
||||
-dontobfuscate
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.container.*
|
||||
import org.jetbrains.kotlin.context.ModuleContext
|
||||
import org.jetbrains.kotlin.contracts.ContractDeserializerImpl
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackagePartProvider
|
||||
import org.jetbrains.kotlin.frontend.di.configureModule
|
||||
@@ -110,8 +109,6 @@ fun createContainerForLazyResolveWithJava(
|
||||
}
|
||||
|
||||
targetEnvironment.configure(this)
|
||||
|
||||
useImpl<ContractDeserializerImpl>()
|
||||
}.apply {
|
||||
get<AbstractJavaClassFinder>().initialize(bindingTrace, get<KotlinCodeAnalyzer>())
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
|
||||
class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass), VirtualFileBoundJavaClass, JavaAnnotationOwnerImpl, JavaModifierListOwnerImpl {
|
||||
init {
|
||||
@@ -115,7 +114,7 @@ class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass)
|
||||
override val virtualFile: VirtualFile?
|
||||
get() = psi.containingFile?.virtualFile
|
||||
|
||||
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = psi.containingFile in scope
|
||||
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = psi.containingFile.virtualFile in scope
|
||||
|
||||
override fun getAnnotationOwnerPsi() = psi.modifierList
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
|
||||
class KotlinBinaryClassCache : Disposable {
|
||||
private class RequestCache {
|
||||
@@ -55,6 +56,8 @@ class KotlinBinaryClassCache : Disposable {
|
||||
fun getKotlinBinaryClass(file: VirtualFile, fileContent: ByteArray? = null): KotlinJvmBinaryClass? {
|
||||
if (file.fileType !== JavaClassFileType.INSTANCE) return null
|
||||
|
||||
if (file.name == PsiJavaModule.MODULE_INFO_CLS_FILE) return null
|
||||
|
||||
val service = ServiceManager.getService(KotlinBinaryClassCache::class.java)
|
||||
val requestCache = service.cache.get()
|
||||
|
||||
|
||||
@@ -32,9 +32,6 @@ object JvmBindingContextSlices {
|
||||
@JvmField
|
||||
val RECEIVER_RUNTIME_ASSERTION_INFO: WritableSlice<ExpressionReceiver, RuntimeAssertionInfo> = BasicWritableSlice(RewritePolicy.DO_NOTHING)
|
||||
|
||||
@JvmField
|
||||
val BODY_RUNTIME_ASSERTION_INFO: WritableSlice<KtExpression, RuntimeAssertionInfo> = BasicWritableSlice(RewritePolicy.DO_NOTHING)
|
||||
|
||||
@JvmField
|
||||
val LOAD_FROM_JAVA_SIGNATURE_ERRORS: WritableSlice<DeclarationDescriptor, List<String>> = Slices.createCollectiveSlice()
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.resolve.jvm
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.DeclarationReturnTypeSanitizer
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.WrappedTypeFactory
|
||||
|
||||
object JvmDeclarationReturnTypeSanitizer : DeclarationReturnTypeSanitizer {
|
||||
override fun sanitizeReturnType(
|
||||
inferred: UnwrappedType,
|
||||
wrappedTypeFactory: WrappedTypeFactory,
|
||||
trace: BindingTrace,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
): UnwrappedType =
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.StrictJavaNullabilityAssertions)) {
|
||||
// NB can't check for presence of EnhancedNullability here,
|
||||
// because it will also cause recursion in declaration type resolution.
|
||||
inferred.replaceAnnotations(FilteredAnnotations(inferred.annotations) {
|
||||
it != JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION
|
||||
})
|
||||
}
|
||||
else inferred
|
||||
}
|
||||
@@ -18,12 +18,9 @@ package org.jetbrains.kotlin.resolve.jvm
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
@@ -34,9 +31,9 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.isClassType
|
||||
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class RuntimeAssertionInfo(val needNotNullAssertion: Boolean, val message: String) {
|
||||
@@ -86,9 +83,6 @@ class RuntimeAssertionInfo(val needNotNullAssertion: Boolean, val message: Strin
|
||||
}
|
||||
}
|
||||
|
||||
private val KtExpression.textForRuntimeAssertionInfo
|
||||
get() = StringUtil.trimMiddle(text, 50)
|
||||
|
||||
class RuntimeAssertionsDataFlowExtras(
|
||||
private val c: ResolutionContext<*>,
|
||||
private val dataFlowValue: DataFlowValue,
|
||||
@@ -99,7 +93,7 @@ class RuntimeAssertionsDataFlowExtras(
|
||||
override val possibleTypes: Set<KotlinType>
|
||||
get() = c.dataFlowInfo.getCollectedTypes(dataFlowValue)
|
||||
override val presentableText: String
|
||||
get() = expression.textForRuntimeAssertionInfo
|
||||
get() = StringUtil.trimMiddle(expression.text, 50)
|
||||
}
|
||||
|
||||
object RuntimeAssertionsTypeChecker : AdditionalTypeChecker {
|
||||
@@ -144,101 +138,4 @@ object RuntimeAssertionsOnExtensionReceiverCallChecker : CallChecker {
|
||||
c.trace.record(JvmBindingContextSlices.RECEIVER_RUNTIME_ASSERTION_INFO, expressionReceiverValue, assertionInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object RuntimeAssertionsOnDeclarationBodyChecker {
|
||||
@JvmStatic
|
||||
fun check(
|
||||
declaration: KtDeclaration,
|
||||
descriptor: DeclarationDescriptor,
|
||||
bindingTrace: BindingTrace,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.StrictJavaNullabilityAssertions)) return
|
||||
|
||||
when {
|
||||
declaration is KtProperty && descriptor is VariableDescriptor ->
|
||||
checkLocalVariable(declaration, descriptor, bindingTrace)
|
||||
declaration is KtFunction && descriptor is FunctionDescriptor ->
|
||||
checkFunction(declaration, descriptor, bindingTrace)
|
||||
declaration is KtProperty && descriptor is PropertyDescriptor ->
|
||||
checkProperty(declaration, descriptor, bindingTrace)
|
||||
declaration is KtPropertyAccessor && descriptor is PropertyAccessorDescriptor ->
|
||||
checkPropertyAccessor(declaration, descriptor, bindingTrace)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLocalVariable(
|
||||
declaration: KtProperty,
|
||||
descriptor: VariableDescriptor,
|
||||
bindingTrace: BindingTrace
|
||||
) {
|
||||
if (declaration.typeReference != null) return
|
||||
|
||||
checkNullabilityAssertion(declaration.initializer ?: return, descriptor.type, bindingTrace)
|
||||
}
|
||||
|
||||
private fun checkFunction(
|
||||
declaration: KtFunction,
|
||||
descriptor: FunctionDescriptor,
|
||||
bindingTrace: BindingTrace
|
||||
) {
|
||||
if (declaration.typeReference != null || declaration.hasBlockBody()) return
|
||||
|
||||
checkNullabilityAssertion(declaration.bodyExpression ?: return, descriptor.returnType ?: return,
|
||||
bindingTrace)
|
||||
}
|
||||
|
||||
private fun checkProperty(
|
||||
declaration: KtProperty,
|
||||
descriptor: PropertyDescriptor,
|
||||
bindingTrace: BindingTrace
|
||||
) {
|
||||
if (declaration.typeReference != null) return
|
||||
|
||||
// TODO nullability assertion on delegate initialization expression, see KT-20823
|
||||
if (declaration.hasDelegateExpression()) return
|
||||
|
||||
checkNullabilityAssertion(declaration.initializer ?: return, descriptor.type, bindingTrace)
|
||||
}
|
||||
|
||||
private fun checkPropertyAccessor(
|
||||
declaration: KtPropertyAccessor,
|
||||
descriptor: PropertyAccessorDescriptor,
|
||||
bindingTrace: BindingTrace
|
||||
) {
|
||||
if (declaration.property.typeReference != null || declaration.hasBlockBody()) return
|
||||
|
||||
checkNullabilityAssertion(declaration.bodyExpression ?: return, descriptor.correspondingProperty.type,
|
||||
bindingTrace)
|
||||
}
|
||||
|
||||
|
||||
private fun checkNullabilityAssertion(
|
||||
expression: KtExpression,
|
||||
declarationType: KotlinType,
|
||||
bindingTrace: BindingTrace
|
||||
) {
|
||||
if (declarationType.unwrap().canContainNull()) return
|
||||
|
||||
val expressionType = bindingTrace.getType(expression) ?: return
|
||||
if (expressionType.isError) return
|
||||
|
||||
if (!expressionType.hasEnhancedNullability()) return
|
||||
|
||||
bindingTrace.record(
|
||||
JvmBindingContextSlices.BODY_RUNTIME_ASSERTION_INFO,
|
||||
expression,
|
||||
RuntimeAssertionInfo(true, expression.textForRuntimeAssertionInfo)
|
||||
)
|
||||
}
|
||||
|
||||
private fun UnwrappedType.canContainNull(): Boolean {
|
||||
val upper = upperIfFlexible()
|
||||
return when {
|
||||
upper.isMarkedNullable -> true
|
||||
upper.isClassType -> false
|
||||
else -> upper.immediateSupertypes().all { it.unwrap().canContainNull() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.resolve.jvm.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.jvm.getCompileTimeConstant
|
||||
|
||||
object ApiVersionIsAtLeastArgumentsChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
if (!isApiVersionIsAtLeast(resolvedCall.resultingDescriptor)) return
|
||||
|
||||
val bindingContext = context.trace.bindingContext
|
||||
val shouldInlineConstVals = context.languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals)
|
||||
|
||||
for ((_, resolvedValueArgument) in resolvedCall.valueArguments) {
|
||||
for (valueArgument in resolvedValueArgument.arguments) {
|
||||
val ktExpression = KtPsiUtil.deparenthesize(valueArgument.getArgumentExpression() ?: continue) ?: continue
|
||||
|
||||
val constant = getCompileTimeConstant(ktExpression, bindingContext, false, shouldInlineConstVals)
|
||||
if (constant == null) {
|
||||
context.trace.report(ErrorsJvm.API_VERSION_IS_AT_LEAST_ARGUMENT_SHOULD_BE_CONSTANT.on(ktExpression))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isApiVersionIsAtLeast(descriptor: CallableDescriptor): Boolean {
|
||||
val functionDescriptor = descriptor as? FunctionDescriptor ?: return false
|
||||
|
||||
if (functionDescriptor.name.asString() != "apiVersionIsAtLeast") return false
|
||||
|
||||
val returnType = functionDescriptor.returnType ?: return false
|
||||
if (!KotlinBuiltIns.isBoolean(returnType)) return false
|
||||
|
||||
if (!functionDescriptor.valueParameters.all { KotlinBuiltIns.isInt(it.type) }) return false
|
||||
|
||||
val containingPackage = functionDescriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
|
||||
return containingPackage.fqName.asString() == "kotlin.internal"
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
"Please make explicit overrides (abstract or concrete) for the following non-abstract members of ''{1}'': {2}",
|
||||
NAME, NAME, STRING);
|
||||
|
||||
MAP.put(DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET, "Super calls to Java default methods are prohibited in JVM target 1.6. Recompile with '-jvm-target 1.8'");
|
||||
MAP.put(DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET, "Super calls to Java default methods are deprecated in JVM target 1.6. Recompile with '-jvm-target 1.8'");
|
||||
MAP.put(INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET, "Calls to static methods in Java interfaces are deprecated in JVM target 1.6. Recompile with '-jvm-target 1.8'");
|
||||
|
||||
MAP.put(INLINE_FROM_HIGHER_PLATFORM, "Cannot inline bytecode built with {0} into bytecode that is being built with {1}. Please specify proper ''-jvm-target'' option", STRING, STRING);
|
||||
@@ -123,8 +123,6 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE, "Symbol is declared in module ''{0}'' which current module does not depend on", STRING);
|
||||
MAP.put(JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE, "Symbol is declared in unnamed module which is not read by current module");
|
||||
MAP.put(JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE, "Symbol is declared in module ''{0}'' which does not export package ''{1}''", STRING, STRING);
|
||||
|
||||
MAP.put(API_VERSION_IS_AT_LEAST_ARGUMENT_SHOULD_BE_CONSTANT, "'apiVersionIsAtLeast' argument should be a constant expression");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -96,7 +96,7 @@ public interface ErrorsJvm {
|
||||
|
||||
DiagnosticFactory3<PsiElement, DeclarationDescriptor, DeclarationDescriptor, String> TARGET6_INTERFACE_INHERITANCE = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DEFAULT_METHOD_CALL_FROM_JAVA6_TARGET = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<PsiElement, String, String> INLINE_FROM_HIGHER_PLATFORM = DiagnosticFactory2.create(ERROR);
|
||||
@@ -105,8 +105,6 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<PsiElement> JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, String, String> JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtExpression> API_VERSION_IS_AT_LEAST_ARGUMENT_SHOULD_BE_CONSTANT = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
enum NullabilityInformationSource {
|
||||
KOTLIN {
|
||||
@NotNull
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* 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.resolve.jvm
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
|
||||
fun getCompileTimeConstant(
|
||||
expression: KtExpression,
|
||||
bindingContext: BindingContext,
|
||||
takeUpConstValsAsConst: Boolean,
|
||||
shouldInlineConstVals: Boolean
|
||||
): ConstantValue<*>? {
|
||||
val compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, bindingContext)
|
||||
if (compileTimeValue == null || compileTimeValue.usesNonConstValAsConstant) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!shouldInlineConstVals && !takeUpConstValsAsConst && compileTimeValue.usesVariableAsConstant) {
|
||||
val constantChecker = ConstantsChecker(bindingContext)
|
||||
expression.accept(constantChecker)
|
||||
if (constantChecker.containsKotlinConstVals) return null
|
||||
}
|
||||
|
||||
val expectedType = bindingContext.getType(expression) ?: return null
|
||||
|
||||
return compileTimeValue.toConstantValue(expectedType)
|
||||
}
|
||||
|
||||
|
||||
private class ConstantsChecker(private val bindingContext: BindingContext) : KtVisitorVoid() {
|
||||
var containsKotlinConstVals = false
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
val resolvedCall = expression.getResolvedCall(bindingContext)
|
||||
if (resolvedCall != null) {
|
||||
val callableDescriptor = resolvedCall.resultingDescriptor
|
||||
if (callableDescriptor is PropertyDescriptor && callableDescriptor !is JavaPropertyDescriptor && callableDescriptor.isConst) {
|
||||
containsKotlinConstVals = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
if (!containsKotlinConstVals) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,16 +17,15 @@
|
||||
package org.jetbrains.kotlin.resolve.jvm.modules
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
//import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiModifier
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
/*
|
||||
import org.jetbrains.kotlin.utils.compact
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
import org.jetbrains.org.objectweb.asm.ModuleVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
*/
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_TRANSITIVE
|
||||
import java.io.IOException
|
||||
|
||||
class JavaModuleInfo(
|
||||
@@ -42,7 +41,6 @@ class JavaModuleInfo(
|
||||
"Module $moduleName (${requires.size} requires, ${exports.size} exports)"
|
||||
|
||||
companion object {
|
||||
/*
|
||||
fun create(psiJavaModule: PsiJavaModule): JavaModuleInfo {
|
||||
return JavaModuleInfo(
|
||||
psiJavaModule.name,
|
||||
@@ -58,10 +56,7 @@ class JavaModuleInfo(
|
||||
}
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
fun read(file: VirtualFile): JavaModuleInfo? = null // unsupported with this version of ASM
|
||||
/*
|
||||
fun read(file: VirtualFile): JavaModuleInfo? {
|
||||
val contents = try { file.contentsToByteArray() } catch (e: IOException) { return null }
|
||||
|
||||
@@ -90,6 +85,5 @@ class JavaModuleInfo(
|
||||
JavaModuleInfo(moduleName!!, requires.compact(), exports.compact())
|
||||
else null
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,7 @@ object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
SuperCallWithDefaultArgumentsChecker(),
|
||||
ProtectedSyntheticExtensionCallChecker,
|
||||
ReifiedTypeParameterSubstitutionChecker(),
|
||||
RuntimeAssertionsOnExtensionReceiverCallChecker,
|
||||
ApiVersionIsAtLeastArgumentsChecker
|
||||
RuntimeAssertionsOnExtensionReceiverCallChecker
|
||||
),
|
||||
|
||||
additionalTypeCheckers = listOf(
|
||||
@@ -81,9 +80,7 @@ object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
|
||||
delegationFilter = JvmDelegationFilter,
|
||||
|
||||
overridesBackwardCompatibilityHelper = JvmOverridesBackwardCompatibilityHelper,
|
||||
|
||||
declarationReturnTypeSanitizer = JvmDeclarationReturnTypeSanitizer
|
||||
overridesBackwardCompatibilityHelper = JvmOverridesBackwardCompatibilityHelper
|
||||
) {
|
||||
override fun configureModuleComponents(container: StorageComponentContainer) {
|
||||
container.useImpl<JvmReflectionAPICallChecker>()
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.cfg
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -28,8 +27,9 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
|
||||
interface ControlFlowBuilder {
|
||||
// Subroutines
|
||||
fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null)
|
||||
fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode
|
||||
fun enterSubroutine(subroutine: KtElement)
|
||||
|
||||
fun exitSubroutine(subroutine: KtElement): Pseudocode
|
||||
|
||||
val currentSubroutine: KtElement
|
||||
val returnSubroutine: KtElement
|
||||
@@ -49,8 +49,6 @@ interface ControlFlowBuilder {
|
||||
fun declareVariable(property: KtVariableDeclaration)
|
||||
fun declareFunction(subroutine: KtElement, pseudocode: Pseudocode)
|
||||
|
||||
fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind)
|
||||
|
||||
fun declareEntryOrObject(entryOrObject: KtClassOrObject)
|
||||
|
||||
// Labels
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.cfg
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -135,12 +134,11 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
|
||||
delegateBuilder.exitTryFinally()
|
||||
}
|
||||
|
||||
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
|
||||
delegateBuilder.enterSubroutine(subroutine, invocationKind)
|
||||
override fun enterSubroutine(subroutine: KtElement) {
|
||||
delegateBuilder.enterSubroutine(subroutine)
|
||||
}
|
||||
|
||||
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode =
|
||||
delegateBuilder.exitSubroutine(subroutine, invocationKind)
|
||||
override fun exitSubroutine(subroutine: KtElement): Pseudocode = delegateBuilder.exitSubroutine(subroutine)
|
||||
|
||||
override val currentSubroutine: KtElement
|
||||
get() = delegateBuilder.currentSubroutine
|
||||
@@ -180,10 +178,6 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
|
||||
delegateBuilder.declareFunction(subroutine, pseudocode)
|
||||
}
|
||||
|
||||
override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind) {
|
||||
delegateBuilder.declareInlinedFunction(subroutine, pseudocode, invocationKind)
|
||||
}
|
||||
|
||||
override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
|
||||
delegateBuilder.declareEntryOrObject(entryOrObject)
|
||||
}
|
||||
|
||||
@@ -369,8 +369,7 @@ class ControlFlowInformationProvider private constructor(
|
||||
// Do not consider top-level properties
|
||||
if (containingDeclarationDescriptor is PackageFragmentDescriptor) return false
|
||||
var parentDeclaration = getElementParentDeclaration(writeValueInstruction.element)
|
||||
|
||||
loop@ while (true) {
|
||||
while (true) {
|
||||
val context = trace.bindingContext
|
||||
val parentDescriptor = getDeclarationDescriptorIncludingConstructors(context, parentDeclaration)
|
||||
if (parentDescriptor == containingDeclarationDescriptor) {
|
||||
@@ -382,13 +381,6 @@ class ControlFlowInformationProvider private constructor(
|
||||
parentDeclaration = getElementParentDeclaration(parentDeclaration)
|
||||
}
|
||||
is KtDeclarationWithBody -> {
|
||||
// If it is captured write in lambda that is called in-place, then skip it (treat as parent)
|
||||
val maybeEnclosingLambdaExpr = parentDeclaration.parent
|
||||
if (maybeEnclosingLambdaExpr is KtLambdaExpression && trace[BindingContext.LAMBDA_INVOCATIONS, maybeEnclosingLambdaExpr] != null) {
|
||||
parentDeclaration = getElementParentDeclaration(parentDeclaration)
|
||||
continue@loop
|
||||
}
|
||||
|
||||
if (parentDeclaration is KtFunction && parentDeclaration.isLocal) return true
|
||||
// miss non-local function or accessor just once
|
||||
parentDeclaration = getElementParentDeclaration(parentDeclaration)
|
||||
|
||||
@@ -30,9 +30,6 @@ import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.contracts.description.canBeRevisited
|
||||
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
@@ -61,20 +58,18 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||
import java.util.*
|
||||
|
||||
typealias DeferredGenerator = (ControlFlowBuilder) -> Unit
|
||||
|
||||
class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
|
||||
private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()
|
||||
|
||||
fun generatePseudocode(subroutine: KtElement): Pseudocode {
|
||||
val pseudocode = generate(subroutine, null)
|
||||
val pseudocode = generate(subroutine)
|
||||
(pseudocode as PseudocodeImpl).postProcess()
|
||||
return pseudocode
|
||||
}
|
||||
|
||||
private fun generate(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode {
|
||||
builder.enterSubroutine(subroutine, invocationKind)
|
||||
private fun generate(subroutine: KtElement): Pseudocode {
|
||||
builder.enterSubroutine(subroutine)
|
||||
val cfpVisitor = CFPVisitor(builder)
|
||||
if (subroutine is KtDeclarationWithBody && subroutine !is KtSecondaryConstructor) {
|
||||
val valueParameters = subroutine.valueParameters
|
||||
@@ -92,7 +87,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
else {
|
||||
cfpVisitor.generateInstructions(subroutine)
|
||||
}
|
||||
return builder.exitSubroutine(subroutine, invocationKind)
|
||||
return builder.exitSubroutine(subroutine)
|
||||
}
|
||||
|
||||
private fun generateImplicitReturnValue(bodyExpression: KtExpression, subroutine: KtElement) {
|
||||
@@ -110,7 +105,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
val afterDeclaration = builder.createUnboundLabel("after local declaration")
|
||||
|
||||
builder.nondeterministicJump(afterDeclaration, subroutine, null)
|
||||
generate(subroutine, null)
|
||||
generate(subroutine)
|
||||
builder.bindLabel(afterDeclaration)
|
||||
}
|
||||
|
||||
@@ -120,13 +115,6 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
|
||||
private val catchFinallyStack = Stack<CatchFinallyLabels>()
|
||||
|
||||
// Some language constructs (e.g. inlined lambdas) should be partially processed before call
|
||||
// (to provide argument for call itself), and partially - after (in case of inlined lambdas,
|
||||
// their body should be generated after call). To do so, we store deferred generators, which
|
||||
// will be called after call instruction is emitted.
|
||||
// Stack is necessary to store generators across nested calls
|
||||
private val deferredGeneratorsStack = Stack<MutableList<DeferredGenerator>>()
|
||||
|
||||
private val conditionVisitor = object : KtVisitorVoid() {
|
||||
|
||||
private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? =
|
||||
@@ -1032,42 +1020,14 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
return parent.parent is KtDoWhileExpression
|
||||
}
|
||||
|
||||
private fun visitFunction(function: KtFunction, invocationKind: InvocationKind? = null) {
|
||||
if (invocationKind == null) {
|
||||
processLocalDeclaration(function)
|
||||
}
|
||||
else {
|
||||
visitInlinedFunction(function, invocationKind)
|
||||
}
|
||||
|
||||
private fun visitFunction(function: KtFunction) {
|
||||
processLocalDeclaration(function)
|
||||
val isAnonymousFunction = function is KtFunctionLiteral || function.name == null
|
||||
if (isAnonymousFunction || function.isLocal && function.parent !is KtBlockExpression) {
|
||||
builder.createLambda(function)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitInlinedFunction(lambdaFunctionLiteral: KtFunction, invocationKind: InvocationKind) {
|
||||
// Defer emitting of inlined declaration
|
||||
deferredGeneratorsStack.peek().add({ builder ->
|
||||
val beforeDeclaration = builder.createUnboundLabel("before inlined declaration")
|
||||
val afterDeclaration = builder.createUnboundLabel("after inlined declaration")
|
||||
|
||||
builder.bindLabel(beforeDeclaration)
|
||||
|
||||
if (!invocationKind.isDefinitelyVisited()) {
|
||||
builder.nondeterministicJump(afterDeclaration, lambdaFunctionLiteral, null)
|
||||
}
|
||||
|
||||
generate(lambdaFunctionLiteral, invocationKind)
|
||||
|
||||
if (invocationKind.canBeRevisited()) {
|
||||
builder.nondeterministicJump(beforeDeclaration, lambdaFunctionLiteral, null)
|
||||
}
|
||||
|
||||
builder.bindLabel(afterDeclaration)
|
||||
})
|
||||
}
|
||||
|
||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||
visitFunction(function)
|
||||
}
|
||||
@@ -1075,11 +1035,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
|
||||
mark(lambdaExpression)
|
||||
val functionLiteral = lambdaExpression.functionLiteral
|
||||
|
||||
// NB. Behaviour here is implicitly controlled by the LanguageFeature 'CallsInPlaceEffect'
|
||||
// If this feature is turned off, then slice LAMBDA_INVOCATIONS is never written and invocationKind
|
||||
// in all subsequent calls always 'null', resulting in falling back to old behaviour
|
||||
visitFunction(functionLiteral, trace[BindingContext.LAMBDA_INVOCATIONS, lambdaExpression])
|
||||
visitFunction(functionLiteral)
|
||||
copyValue(functionLiteral, lambdaExpression)
|
||||
}
|
||||
|
||||
@@ -1529,8 +1485,6 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
|
||||
val receivers = getReceiverValues(resolvedCall)
|
||||
|
||||
deferredGeneratorsStack.push(mutableListOf())
|
||||
|
||||
var parameterValues = SmartFMap.emptyMap<PseudoValue, ValueParameterDescriptor>()
|
||||
for (argument in resolvedCall.call.valueArguments) {
|
||||
val argumentMapping = resolvedCall.getArgumentMapping(argument)
|
||||
@@ -1553,10 +1507,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
}
|
||||
|
||||
mark(resolvedCall.call.callElement)
|
||||
val callInstruction = builder.call(callElement, resolvedCall, receivers, parameterValues)
|
||||
val deferredGeneratorsForCall = deferredGeneratorsStack.pop()
|
||||
deferredGeneratorsForCall.forEach { it.invoke(builder) }
|
||||
return callInstruction
|
||||
return builder.call(callElement, resolvedCall, receivers, parameterValues)
|
||||
}
|
||||
|
||||
private fun getReceiverValues(resolvedCall: ResolvedCall<*>): Map<PseudoValue, ReceiverValue> {
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.cfg.pseudocodeTraverser
|
||||
import org.jetbrains.kotlin.cfg.ControlFlowInfo
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.InlinedLocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
|
||||
@@ -97,13 +96,8 @@ private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
|
||||
if (instruction is LocalFunctionDeclarationInstruction) {
|
||||
val subroutinePseudocode = instruction.body
|
||||
subroutinePseudocode.collectDataFromSubgraph(
|
||||
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true
|
||||
)
|
||||
// Special case for inlined functions: take flow from EXIT instructions (it contains flow which exits declaration normally)
|
||||
val lastInstruction = if (instruction is InlinedLocalFunctionDeclarationInstruction && traversalOrder == FORWARD)
|
||||
subroutinePseudocode.exitInstruction
|
||||
else
|
||||
subroutinePseudocode.getLastInstruction(traversalOrder)
|
||||
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true)
|
||||
val lastInstruction = subroutinePseudocode.getLastInstruction(traversalOrder)
|
||||
val previousValue = edgesMap[instruction]
|
||||
val newValue = edgesMap[lastInstruction]
|
||||
val updatedValue = newValue?.let {
|
||||
|
||||
@@ -190,7 +190,10 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon
|
||||
}
|
||||
|
||||
private fun WriteValueInstruction.isTrivialInitializer() =
|
||||
element is KtVariableDeclaration || element is KtParameter
|
||||
// WriteValueInstruction having KtDeclaration as an element means
|
||||
// it must be a write happened at the same time when
|
||||
// the variable (common variable/parameter/object) has been declared
|
||||
element is KtDeclaration
|
||||
|
||||
private inner class ReadOnlyInitControlFlowInfoImpl(
|
||||
val declaredSet: ImmutableSet<VariableDescriptor>,
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -48,8 +47,8 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
|
||||
private val allBlocks = Stack<BlockInfo>()
|
||||
|
||||
private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement, shouldInline: Boolean) {
|
||||
val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine, shouldInline)
|
||||
private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement) {
|
||||
val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine)
|
||||
builders.push(worker)
|
||||
builder = worker
|
||||
}
|
||||
@@ -65,42 +64,32 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
return worker
|
||||
}
|
||||
|
||||
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
|
||||
override fun enterSubroutine(subroutine: KtElement) {
|
||||
val builder = builder
|
||||
val shouldInlnie = invocationKind != null
|
||||
if (builder != null && subroutine is KtFunctionLiteral) {
|
||||
pushBuilder(subroutine, builder.returnSubroutine, shouldInlnie)
|
||||
pushBuilder(subroutine, builder.returnSubroutine)
|
||||
}
|
||||
else {
|
||||
pushBuilder(subroutine, subroutine, shouldInlnie)
|
||||
pushBuilder(subroutine, subroutine)
|
||||
}
|
||||
delegateBuilder.enterBlockScope(subroutine)
|
||||
delegateBuilder.enterSubroutine(subroutine)
|
||||
}
|
||||
|
||||
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode {
|
||||
super.exitSubroutine(subroutine, invocationKind)
|
||||
override fun exitSubroutine(subroutine: KtElement): Pseudocode {
|
||||
super.exitSubroutine(subroutine)
|
||||
delegateBuilder.exitBlockScope(subroutine)
|
||||
val worker = popBuilder()
|
||||
if (!builders.empty()) {
|
||||
val builder = builders.peek()
|
||||
if (invocationKind == null) {
|
||||
builder.declareFunction(subroutine, worker.pseudocode)
|
||||
}
|
||||
else {
|
||||
builder.declareInlinedFunction(subroutine, worker.pseudocode, invocationKind)
|
||||
}
|
||||
builder.declareFunction(subroutine, worker.pseudocode)
|
||||
}
|
||||
return worker.pseudocode
|
||||
}
|
||||
|
||||
private inner class ControlFlowInstructionsGeneratorWorker(
|
||||
scopingElement: KtElement,
|
||||
override val returnSubroutine: KtElement,
|
||||
shouldInline: Boolean
|
||||
) : ControlFlowBuilder {
|
||||
private inner class ControlFlowInstructionsGeneratorWorker(scopingElement: KtElement, override val returnSubroutine: KtElement) : ControlFlowBuilder {
|
||||
|
||||
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement, shouldInline)
|
||||
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement)
|
||||
private val error: Label = pseudocode.createLabel("error", null)
|
||||
private val sink: Label = pseudocode.createLabel("sink", null)
|
||||
|
||||
@@ -156,7 +145,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
override val currentLoop: KtLoopExpression?
|
||||
get() = if (loopInfo.empty()) null else loopInfo.peek().element
|
||||
|
||||
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
|
||||
override fun enterSubroutine(subroutine: KtElement) {
|
||||
val blockInfo = SubroutineInfo(
|
||||
subroutine,
|
||||
/* entry point */ createUnboundLabel(),
|
||||
@@ -215,7 +204,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode {
|
||||
override fun exitSubroutine(subroutine: KtElement): Pseudocode {
|
||||
getSubroutineExitPoint(subroutine)?.let { bindLabel(it) }
|
||||
pseudocode.addExitInstruction(SubroutineExitInstruction(subroutine, currentScope, false))
|
||||
bindLabel(error)
|
||||
@@ -272,10 +261,6 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
add(LocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope))
|
||||
}
|
||||
|
||||
override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind) {
|
||||
add(InlinedLocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope, invocationKind))
|
||||
}
|
||||
|
||||
override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
|
||||
add(VariableDeclarationInstruction(entryOrObject, currentScope))
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ interface Pseudocode {
|
||||
|
||||
val enterInstruction: SubroutineEnterInstruction
|
||||
|
||||
val isInlined: Boolean
|
||||
val containsDoWhile: Boolean
|
||||
val rootPseudocode: Pseudocode
|
||||
|
||||
|
||||
@@ -27,7 +27,10 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MergeInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult
|
||||
@@ -35,7 +38,7 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverseFollowingInstruction
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import java.util.*
|
||||
|
||||
class PseudocodeImpl(override val correspondingElement: KtElement, override val isInlined: Boolean) : Pseudocode {
|
||||
class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode {
|
||||
|
||||
internal val mutableInstructionList = ArrayList<Instruction>()
|
||||
override val instructions = ArrayList<Instruction>()
|
||||
@@ -53,8 +56,6 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
|
||||
getLocalDeclarations(this)
|
||||
}
|
||||
|
||||
val reachableInstructions = hashSetOf<Instruction>()
|
||||
|
||||
private val representativeInstructions = HashMap<KtElement, KtElementInstruction>()
|
||||
|
||||
private val labels = ArrayList<PseudocodeLabel>()
|
||||
@@ -109,7 +110,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
|
||||
override val reversedInstructions: List<Instruction>
|
||||
get() {
|
||||
val traversedInstructions = linkedSetOf<Instruction>()
|
||||
traverseFollowingInstructions(if (this.isInlined) instructions.last() else sinkInstruction, traversedInstructions, BACKWARD, null)
|
||||
traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null)
|
||||
if (traversedInstructions.size < instructions.size) {
|
||||
val simplyReversedInstructions = instructions.reversed()
|
||||
for (instruction in simplyReversedInstructions) {
|
||||
@@ -223,17 +224,22 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
|
||||
postPrecessed = true
|
||||
errorInstruction.sink = sinkInstruction
|
||||
exitInstruction.sink = sinkInstruction
|
||||
|
||||
for ((index, instruction) in mutableInstructionList.withIndex()) {
|
||||
//recursively invokes 'postProcess' for local declarations, thus it needs global set of reachable instructions
|
||||
//recursively invokes 'postProcess' for local declarations
|
||||
instruction.processInstruction(index)
|
||||
}
|
||||
if (parent != null) return
|
||||
|
||||
// Collecting reachable instructions should be done after processing all instructions
|
||||
// (including instructions in local declarations) to avoid being in incomplete state.
|
||||
collectAndCacheReachableInstructions()
|
||||
for (localFunctionDeclarationInstruction in localDeclarations) {
|
||||
(localFunctionDeclarationInstruction.body as PseudocodeImpl).collectAndCacheReachableInstructions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectAndCacheReachableInstructions() {
|
||||
collectReachableInstructions()
|
||||
val reachableInstructions = collectReachableInstructions()
|
||||
for (instruction in mutableInstructionList) {
|
||||
if (reachableInstructions.contains(instruction)) {
|
||||
instructions.add(instruction)
|
||||
@@ -281,14 +287,6 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
|
||||
instruction.next = sinkInstruction
|
||||
}
|
||||
|
||||
override fun visitInlinedLocalFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction) {
|
||||
val body = instruction.body as PseudocodeImpl
|
||||
body.parent = this@PseudocodeImpl
|
||||
body.postProcess()
|
||||
// Don't add edge to next instruction if flow can't reach exit of inlined declaration
|
||||
instruction.next = if (body.instructions.contains(body.exitInstruction)) getNextPosition(currentPosition) else sinkInstruction
|
||||
}
|
||||
|
||||
override fun visitSubroutineExit(instruction: SubroutineExitInstruction) {
|
||||
// Nothing
|
||||
}
|
||||
@@ -303,25 +301,25 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
|
||||
})
|
||||
}
|
||||
|
||||
private fun collectReachableInstructions() {
|
||||
val reachableFromThisPseudocode = hashSetOf<Instruction>()
|
||||
traverseFollowingInstructions(enterInstruction, reachableFromThisPseudocode, FORWARD
|
||||
private fun collectReachableInstructions(): Set<Instruction> {
|
||||
val visited = hashSetOf<Instruction>()
|
||||
traverseFollowingInstructions(enterInstruction, visited, FORWARD
|
||||
) { instruction ->
|
||||
if (instruction is MagicInstruction && instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
|
||||
return@traverseFollowingInstructions TraverseInstructionResult.SKIP
|
||||
}
|
||||
TraverseInstructionResult.CONTINUE
|
||||
}
|
||||
|
||||
// Don't force-add EXIT and ERROR for inlined pseudocodes because for such
|
||||
// declarations those instructions has special semantic
|
||||
if (!isInlined) {
|
||||
reachableFromThisPseudocode.add(exitInstruction)
|
||||
reachableFromThisPseudocode.add(errorInstruction)
|
||||
reachableFromThisPseudocode.add(sinkInstruction)
|
||||
if (!visited.contains(exitInstruction)) {
|
||||
visited.add(exitInstruction)
|
||||
}
|
||||
|
||||
reachableFromThisPseudocode.forEach { (it.owner as PseudocodeImpl).reachableInstructions.add(it) }
|
||||
if (!visited.contains(errorInstruction)) {
|
||||
visited.add(errorInstruction)
|
||||
}
|
||||
if (!visited.contains(sinkInstruction)) {
|
||||
visited.add(sinkInstruction)
|
||||
}
|
||||
return visited
|
||||
}
|
||||
|
||||
private fun markDeadInstructions() {
|
||||
@@ -345,7 +343,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
|
||||
}
|
||||
|
||||
override fun copy(): PseudocodeImpl {
|
||||
val result = PseudocodeImpl(correspondingElement, isInlined)
|
||||
val result = PseudocodeImpl(correspondingElement)
|
||||
result.repeatWhole(this)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ open class InstructionVisitor {
|
||||
visitInstructionWithNext(instruction)
|
||||
}
|
||||
|
||||
open fun visitInlinedLocalFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction) {
|
||||
visitLocalFunctionDeclarationInstruction(instruction)
|
||||
}
|
||||
|
||||
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction) {
|
||||
visitInstructionWithNext(instruction)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,6 @@ abstract class InstructionVisitorWithResult<out R> {
|
||||
open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction): R =
|
||||
visitInstructionWithNext(instruction)
|
||||
|
||||
open fun visitInlinedFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction): R =
|
||||
visitLocalFunctionDeclarationInstruction(instruction)
|
||||
|
||||
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction): R = visitInstructionWithNext(instruction)
|
||||
|
||||
open fun visitUnconditionalJump(instruction: UnconditionalJumpInstruction): R = visitJump(instruction)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* 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.cfg.pseudocode.instructions.special
|
||||
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
class InlinedLocalFunctionDeclarationInstruction(
|
||||
element: KtElement,
|
||||
body: Pseudocode,
|
||||
blockScope: BlockScope,
|
||||
val kind: InvocationKind
|
||||
) : LocalFunctionDeclarationInstruction(element, body, blockScope) {
|
||||
override fun createCopy(): InstructionImpl = InlinedLocalFunctionDeclarationInstruction(element, body, blockScope, kind)
|
||||
|
||||
override fun accept(visitor: InstructionVisitor) = visitor.visitInlinedLocalFunctionDeclarationInstruction(this)
|
||||
|
||||
override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitInlinedFunctionDeclarationInstruction(this)
|
||||
|
||||
override fun toString(): String = "inlined(${render(element)})"
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
|
||||
|
||||
open class LocalFunctionDeclarationInstruction(
|
||||
class LocalFunctionDeclarationInstruction(
|
||||
element: KtElement,
|
||||
val body: Pseudocode,
|
||||
blockScope: BlockScope
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.contracts
|
||||
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.contracts.model.ESValue
|
||||
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.IdentifierInfo
|
||||
|
||||
fun MutableContextInfo.toDataFlowInfo(languageVersionSettings: LanguageVersionSettings): DataFlowInfo {
|
||||
var resultingDataFlowInfo = DataFlowInfoFactory.EMPTY
|
||||
|
||||
extractDataFlowStatements(equalValues) { leftDfv, rightValue ->
|
||||
val rightDfv = rightValue.toDataFlowValue()
|
||||
if (rightDfv != null) {
|
||||
resultingDataFlowInfo = resultingDataFlowInfo.equate(leftDfv, rightDfv, false, languageVersionSettings)
|
||||
}
|
||||
IntArray(42) { it }
|
||||
}
|
||||
|
||||
extractDataFlowStatements(notEqualValues) { leftDfv, rightValue ->
|
||||
val rightDfv = rightValue.toDataFlowValue()
|
||||
if (rightDfv != null) {
|
||||
resultingDataFlowInfo = resultingDataFlowInfo.disequate(leftDfv, rightDfv, languageVersionSettings)
|
||||
}
|
||||
}
|
||||
|
||||
extractDataFlowStatements(subtypes) { leftDfv, type ->
|
||||
resultingDataFlowInfo = resultingDataFlowInfo.establishSubtyping(leftDfv, type, languageVersionSettings)
|
||||
}
|
||||
|
||||
return resultingDataFlowInfo
|
||||
}
|
||||
|
||||
inline private fun <D> extractDataFlowStatements(dictionary: Map<ESValue, Set<D>>, callback: (DataFlowValue, D) -> Unit) {
|
||||
for ((key, setOfValues) in dictionary) {
|
||||
val leftDfv = key.toDataFlowValue() ?: continue
|
||||
setOfValues.forEach { callback(leftDfv, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun ESValue.toDataFlowValue(): DataFlowValue? = when (this) {
|
||||
is ESDataFlowValue -> dataFlowValue
|
||||
ESConstant.NULL -> DataFlowValue.nullValue(DefaultBuiltIns.Instance)
|
||||
is ESConstant -> DataFlowValue(IdentifierInfo.NO, type)
|
||||
else -> null
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* 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.contracts
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.description.*
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.*
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.serialization.Flags
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ContractDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationConfiguration
|
||||
import org.jetbrains.kotlin.serialization.deserialization.TypeDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class ContractDeserializerImpl(private val configuration: DeserializationConfiguration) : ContractDeserializer {
|
||||
override fun deserializeContractFromFunction(
|
||||
proto: ProtoBuf.Function,
|
||||
ownerFunction: FunctionDescriptor,
|
||||
typeTable: TypeTable,
|
||||
typeDeserializer: TypeDeserializer
|
||||
): Pair<FunctionDescriptor.UserDataKey<*>, LazyContractProvider>? {
|
||||
if (!proto.hasContract()) return null
|
||||
|
||||
if (!configuration.returnsEffectAllowed && !configuration.callsInPlaceEffectAllowed) return null
|
||||
|
||||
val worker = ContractDeserializationWorker(typeTable, typeDeserializer, ownerFunction)
|
||||
val contract = worker.deserializeContract(proto.contract)
|
||||
return ContractProviderKey to LazyContractProvider.createInitialized(contract)
|
||||
}
|
||||
|
||||
private class ContractDeserializationWorker(
|
||||
private val typeTable: TypeTable,
|
||||
private val typeDeserializer: TypeDeserializer,
|
||||
private val ownerFunction: FunctionDescriptor
|
||||
) {
|
||||
|
||||
fun deserializeContract(proto: ProtoBuf.Contract): ContractDescription? {
|
||||
val effects = proto.effectList.map { deserializePossiblyConditionalEffect(it) ?: return null }
|
||||
return ContractDescription(effects, ownerFunction)
|
||||
}
|
||||
|
||||
private fun deserializePossiblyConditionalEffect(proto: ProtoBuf.Effect): EffectDeclaration? {
|
||||
if (proto.hasConclusionOfConditionalEffect()) {
|
||||
// conditional effect
|
||||
val conclusion = deserializeExpression(proto.conclusionOfConditionalEffect) ?: return null
|
||||
val effect = deserializeSimpleEffect(proto) ?: return null
|
||||
return ConditionalEffectDeclaration(effect, conclusion)
|
||||
}
|
||||
return deserializeSimpleEffect(proto)
|
||||
}
|
||||
|
||||
private fun deserializeSimpleEffect(proto: ProtoBuf.Effect): EffectDeclaration? {
|
||||
val type = if (proto.hasEffectType()) proto.effectType else return null
|
||||
return when (type!!) {
|
||||
ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> {
|
||||
val argument = proto.effectConstructorArgumentList.getOrNull(0)
|
||||
val returnValue = if (argument == null) ConstantReference.WILDCARD else deserializeExpression(argument) as? ConstantReference ?: return null
|
||||
ReturnsEffectDeclaration(returnValue)
|
||||
}
|
||||
|
||||
ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> {
|
||||
ReturnsEffectDeclaration(ConstantReference.NOT_NULL)
|
||||
}
|
||||
|
||||
ProtoBuf.Effect.EffectType.CALLS -> {
|
||||
val argument = proto.effectConstructorArgumentList.getOrNull(0) ?: return null
|
||||
val callable = extractVariable(argument) ?: return null
|
||||
val invocationKind = if (proto.hasKind())
|
||||
proto.kind.toDescriptorInvocationKind() ?: return null
|
||||
else
|
||||
InvocationKind.UNKNOWN
|
||||
CallsEffectDeclaration(callable, invocationKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeExpression(proto: ProtoBuf.Expression): BooleanExpression? {
|
||||
val primitiveType = getPrimitiveType(proto)
|
||||
val primitiveExpression = extractPrimitiveExpression(proto, primitiveType)
|
||||
|
||||
val complexType = getComplexType(proto)
|
||||
val childs: MutableList<BooleanExpression> = mutableListOf()
|
||||
childs.addIfNotNull(primitiveExpression)
|
||||
|
||||
return when (complexType) {
|
||||
ComplexExpressionType.AND_SEQUENCE -> {
|
||||
proto.andArgumentList.mapTo(childs) { deserializeExpression(it) ?: return null }
|
||||
childs.reduce { acc, booleanExpression -> LogicalAnd(acc, booleanExpression) }
|
||||
}
|
||||
|
||||
ComplexExpressionType.OR_SEQUENCE -> {
|
||||
proto.orArgumentList.mapTo(childs) { deserializeExpression(it) ?: return null }
|
||||
childs.reduce { acc, booleanExpression -> LogicalOr(acc, booleanExpression) }
|
||||
}
|
||||
|
||||
null -> primitiveExpression
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractPrimitiveExpression(proto: ProtoBuf.Expression, primitiveType: PrimitiveExpressionType?): BooleanExpression? {
|
||||
val isInverted = proto.hasFlags() && Flags.IS_NEGATED.get(proto.flags)
|
||||
|
||||
return when (primitiveType) {
|
||||
PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE, PrimitiveExpressionType.RECEIVER_REFERENCE -> {
|
||||
(extractVariable(proto) as? BooleanVariableReference?)?.invertIfNecessary(isInverted)
|
||||
}
|
||||
|
||||
PrimitiveExpressionType.CONSTANT ->
|
||||
(deserializeConstant(proto.constantValue) as? BooleanConstantReference)?.invertIfNecessary(isInverted)
|
||||
|
||||
PrimitiveExpressionType.INSTANCE_CHECK -> {
|
||||
val variable = extractVariable(proto) ?: return null
|
||||
val type = extractType(proto) ?: return null
|
||||
IsInstancePredicate(variable, type, isInverted)
|
||||
}
|
||||
|
||||
PrimitiveExpressionType.NULLABILITY_CHECK -> {
|
||||
val variable = extractVariable(proto) ?: return null
|
||||
IsNullPredicate(variable, isInverted)
|
||||
}
|
||||
|
||||
null -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun BooleanExpression.invertIfNecessary(shouldInvert: Boolean) = if (shouldInvert) LogicalNot(this) else this
|
||||
|
||||
private fun extractVariable(proto: ProtoBuf.Expression): VariableReference? {
|
||||
if (!proto.hasValueParameterReference()) return null
|
||||
|
||||
val parameterDescriptor = if (proto.valueParameterReference == 0)
|
||||
ownerFunction.extensionReceiverParameter ?: return null
|
||||
else
|
||||
ownerFunction.valueParameters.getOrNull(proto.valueParameterReference - 1) ?: return null
|
||||
|
||||
return if (!KotlinBuiltIns.isBoolean(parameterDescriptor.type))
|
||||
VariableReference(parameterDescriptor)
|
||||
else
|
||||
BooleanVariableReference(parameterDescriptor)
|
||||
}
|
||||
|
||||
private fun ProtoBuf.Effect.InvocationKind.toDescriptorInvocationKind(): InvocationKind? = when (this) {
|
||||
ProtoBuf.Effect.InvocationKind.AT_MOST_ONCE -> InvocationKind.AT_MOST_ONCE
|
||||
ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> InvocationKind.EXACTLY_ONCE
|
||||
ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE -> InvocationKind.AT_LEAST_ONCE
|
||||
}
|
||||
|
||||
private fun extractType(proto: ProtoBuf.Expression): KotlinType? {
|
||||
val protoType = when {
|
||||
proto.hasIsInstanceType() -> proto.isInstanceType
|
||||
proto.hasIsInstanceTypeId() -> typeTable.types.getOrNull(proto.isInstanceTypeId) ?: return null
|
||||
else -> return null
|
||||
}
|
||||
|
||||
return typeDeserializer.type(protoType)
|
||||
}
|
||||
|
||||
private fun deserializeConstant(value: ProtoBuf.Expression.ConstantValue): ConstantReference? = when (value) {
|
||||
ProtoBuf.Expression.ConstantValue.TRUE -> BooleanConstantReference.TRUE
|
||||
ProtoBuf.Expression.ConstantValue.FALSE -> BooleanConstantReference.FALSE
|
||||
ProtoBuf.Expression.ConstantValue.NULL -> ConstantReference.NULL
|
||||
}
|
||||
|
||||
private fun getComplexType(proto: ProtoBuf.Expression): ComplexExpressionType? {
|
||||
val isOrSequence = proto.orArgumentCount != 0
|
||||
val isAndSequence = proto.andArgumentCount != 0
|
||||
return when {
|
||||
isOrSequence && isAndSequence -> null
|
||||
isOrSequence -> ComplexExpressionType.OR_SEQUENCE
|
||||
isAndSequence -> ComplexExpressionType.AND_SEQUENCE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPrimitiveType(proto: ProtoBuf.Expression): PrimitiveExpressionType? {
|
||||
// Expected to be one element, but can be empty (unknown expression) or contain several elements (invalid data)
|
||||
val expressionTypes: MutableList<PrimitiveExpressionType> = mutableListOf()
|
||||
|
||||
// Check for predicates
|
||||
when {
|
||||
proto.hasValueParameterReference() && proto.hasType() ->
|
||||
expressionTypes.add(PrimitiveExpressionType.INSTANCE_CHECK)
|
||||
|
||||
proto.hasValueParameterReference() && proto.hasFlag(Flags.IS_NULL_CHECK_PREDICATE) ->
|
||||
expressionTypes.add(PrimitiveExpressionType.NULLABILITY_CHECK)
|
||||
}
|
||||
|
||||
// If message contains correct predicate, then predicate's type overrides type of value,
|
||||
// even is message has one
|
||||
if (expressionTypes.isNotEmpty()) {
|
||||
return expressionTypes.singleOrNull()
|
||||
}
|
||||
|
||||
// Otherwise, check if it is a value
|
||||
when {
|
||||
proto.hasValueParameterReference() && proto.valueParameterReference > 0 ->
|
||||
expressionTypes.add(PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE)
|
||||
|
||||
proto.hasValueParameterReference() && proto.valueParameterReference == 0 ->
|
||||
expressionTypes.add(PrimitiveExpressionType.RECEIVER_REFERENCE)
|
||||
|
||||
proto.hasConstantValue() -> expressionTypes.add(PrimitiveExpressionType.CONSTANT)
|
||||
}
|
||||
|
||||
return expressionTypes.singleOrNull()
|
||||
}
|
||||
|
||||
private fun ProtoBuf.Expression.hasType(): Boolean = this.hasIsInstanceType() || this.hasIsInstanceTypeId()
|
||||
|
||||
private fun ProtoBuf.Expression.hasFlag(flag: Flags.BooleanFlagField) =
|
||||
this.hasFlags() && flag.get(this.flags)
|
||||
|
||||
// Arguments of expressions with such types are never other expressions
|
||||
private enum class PrimitiveExpressionType {
|
||||
VALUE_PARAMETER_REFERENCE,
|
||||
RECEIVER_REFERENCE,
|
||||
CONSTANT,
|
||||
INSTANCE_CHECK,
|
||||
NULLABILITY_CHECK
|
||||
}
|
||||
|
||||
// Expressions with such type can take other expressions as arguments.
|
||||
// Additionally, for performance reasons, "complex expression" and "primitive expression"
|
||||
// can co-exist in the one and the same message. If "primitive expression" is present
|
||||
// in the current message, it is treated as the first argument of "complex expression".
|
||||
private enum class ComplexExpressionType {
|
||||
AND_SEQUENCE,
|
||||
OR_SEQUENCE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.contracts
|
||||
|
||||
import org.jetbrains.kotlin.contracts.model.ESValue
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESVariable
|
||||
import org.jetbrains.kotlin.contracts.model.ESExpressionVisitor
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
|
||||
class ESDataFlowValue(descriptor: ValueDescriptor, val dataFlowValue: DataFlowValue) : ESVariable(descriptor)
|
||||
|
||||
class ESLambda(val lambda: KtLambdaExpression) : ESValue(null) {
|
||||
override fun <T> accept(visitor: ESExpressionVisitor<T>): T {
|
||||
throw IllegalStateException("Lambdas shouldn't be visited by ESExpressionVisitor")
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* 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.contracts
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESCalls
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESReturns
|
||||
import org.jetbrains.kotlin.contracts.model.functors.EqualsFunctor
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
|
||||
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
|
||||
import org.jetbrains.kotlin.contracts.model.structure.lift
|
||||
import org.jetbrains.kotlin.contracts.model.ESEffect
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.MutableContextInfo
|
||||
import org.jetbrains.kotlin.contracts.model.visitors.InfoCollector
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.ConditionalDataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
|
||||
class EffectSystem(val languageVersionSettings: LanguageVersionSettings) {
|
||||
|
||||
fun getDataFlowInfoForFinishedCall(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
bindingTrace: BindingTrace,
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
): DataFlowInfo {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY
|
||||
|
||||
// Prevent launch of effect system machinery on pointless cases (constants/enums/constructors/etc.)
|
||||
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return DataFlowInfo.EMPTY
|
||||
if (callExpression is KtDeclaration) return DataFlowInfo.EMPTY
|
||||
|
||||
val resultContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
|
||||
|
||||
return resultContextInfo.toDataFlowInfo(languageVersionSettings)
|
||||
}
|
||||
|
||||
fun getDataFlowInfoWhenEquals(
|
||||
leftExpression: KtExpression?,
|
||||
rightExpression: KtExpression?,
|
||||
bindingTrace: BindingTrace,
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
): ConditionalDataFlowInfo {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return ConditionalDataFlowInfo.EMPTY
|
||||
if (leftExpression == null || rightExpression == null) return ConditionalDataFlowInfo.EMPTY
|
||||
|
||||
val leftComputation = getNonTrivialComputation(leftExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
|
||||
val rightComputation = getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
|
||||
|
||||
val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation)
|
||||
|
||||
val equalsContextInfo = InfoCollector(ESReturns(true.lift())).collectFromSchema(effects)
|
||||
val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects)
|
||||
|
||||
return ConditionalDataFlowInfo(
|
||||
equalsContextInfo.toDataFlowInfo(languageVersionSettings),
|
||||
notEqualsContextInfo.toDataFlowInfo(languageVersionSettings)
|
||||
)
|
||||
}
|
||||
|
||||
fun recordDefiniteInvocations(resolvedCall: ResolvedCall<*>, bindingTrace: BindingTrace, moduleDescriptor: ModuleDescriptor) {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.CallsInPlaceEffect)) return
|
||||
|
||||
// Prevent launch of effect system machinery on pointless cases (constants/enums/constructors/etc.)
|
||||
val callExpression = resolvedCall.call.callElement as? KtCallExpression ?: return
|
||||
if (callExpression is KtDeclaration) return
|
||||
|
||||
val resultingContextInfo = getContextInfoWhen(ESReturns(ESConstant.WILDCARD), callExpression, bindingTrace, moduleDescriptor)
|
||||
for (effect in resultingContextInfo.firedEffects) {
|
||||
val callsEffect = effect as? ESCalls ?: continue
|
||||
val lambdaExpression = (callsEffect.callable as? ESLambda)?.lambda ?: continue
|
||||
bindingTrace.record(BindingContext.LAMBDA_INVOCATIONS, lambdaExpression, callsEffect.kind)
|
||||
}
|
||||
}
|
||||
|
||||
fun extractDataFlowInfoFromCondition(
|
||||
condition: KtExpression?,
|
||||
value: Boolean,
|
||||
bindingTrace: BindingTrace,
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
): DataFlowInfo {
|
||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY
|
||||
if (condition == null) return DataFlowInfo.EMPTY
|
||||
|
||||
return getContextInfoWhen(ESReturns(value.lift()), condition, bindingTrace, moduleDescriptor)
|
||||
.toDataFlowInfo(languageVersionSettings)
|
||||
}
|
||||
|
||||
private fun getContextInfoWhen(
|
||||
observedEffect: ESEffect,
|
||||
expression: KtExpression,
|
||||
bindingTrace: BindingTrace,
|
||||
moduleDescriptor: ModuleDescriptor
|
||||
): MutableContextInfo {
|
||||
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
|
||||
return InfoCollector(observedEffect).collectFromSchema(computation.effects)
|
||||
}
|
||||
|
||||
private fun getNonTrivialComputation(expression: KtExpression, trace: BindingTrace, moduleDescriptor: ModuleDescriptor): Computation? {
|
||||
val computation = EffectsExtractingVisitor(trace, moduleDescriptor).extractOrGetCached(expression)
|
||||
return if (computation == UNKNOWN_COMPUTATION) null else computation
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* 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.contracts
|
||||
|
||||
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.interpretation.ContractInterpretationDispatcher
|
||||
import org.jetbrains.kotlin.contracts.model.Computation
|
||||
import org.jetbrains.kotlin.contracts.model.Functor
|
||||
import org.jetbrains.kotlin.contracts.model.functors.*
|
||||
import org.jetbrains.kotlin.contracts.model.structure.CallComputation
|
||||
import org.jetbrains.kotlin.contracts.model.structure.ESConstant
|
||||
import org.jetbrains.kotlin.contracts.model.structure.UNKNOWN_COMPUTATION
|
||||
import org.jetbrains.kotlin.contracts.model.structure.lift
|
||||
import org.jetbrains.kotlin.contracts.parsing.isEqualsDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
/**
|
||||
* Visits a given PSI-tree of call (and nested calls, if any) and extracts information
|
||||
* about effects of that call.
|
||||
*/
|
||||
class EffectsExtractingVisitor(
|
||||
private val trace: BindingTrace,
|
||||
private val moduleDescriptor: ModuleDescriptor
|
||||
) : KtVisitor<Computation, Unit>() {
|
||||
fun extractOrGetCached(element: KtElement): Computation {
|
||||
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
|
||||
return element.accept(this, Unit).also { trace.record(BindingContext.EXPRESSION_EFFECTS, element, it) }
|
||||
}
|
||||
|
||||
override fun visitKtElement(element: KtElement, data: Unit): Computation {
|
||||
val resolvedCall = element.getResolvedCall(trace.bindingContext) ?: return UNKNOWN_COMPUTATION
|
||||
if (resolvedCall.isCallWithUnsupportedReceiver()) return UNKNOWN_COMPUTATION
|
||||
|
||||
val arguments = resolvedCall.getCallArgumentsAsComputations() ?: return UNKNOWN_COMPUTATION
|
||||
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
return when {
|
||||
descriptor.isEqualsDescriptor() -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(false).invokeWithArguments(arguments))
|
||||
descriptor is ValueDescriptor -> ESDataFlowValue(descriptor, (element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION)
|
||||
descriptor is FunctionDescriptor -> CallComputation(descriptor.returnType, descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList())
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
// We need lambdas only as arguments currently, and it is processed in 'visitElement' while parsing arguments of call.
|
||||
// For all other cases we don't need lambdas.
|
||||
override fun visitLambdaExpression(expression: KtLambdaExpression, data: Unit?): Computation = UNKNOWN_COMPUTATION
|
||||
|
||||
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): Computation =
|
||||
KtPsiUtil.deparenthesize(expression)?.accept(this, data) ?: UNKNOWN_COMPUTATION
|
||||
|
||||
override fun visitConstantExpression(expression: KtConstantExpression, data: Unit): Computation {
|
||||
val bindingContext = trace.bindingContext
|
||||
|
||||
val type: KotlinType = bindingContext.getType(expression) ?: return UNKNOWN_COMPUTATION
|
||||
|
||||
val compileTimeConstant: CompileTimeConstant<*>
|
||||
= bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return UNKNOWN_COMPUTATION
|
||||
val value: Any? = compileTimeConstant.getValue(type)
|
||||
|
||||
return when (value) {
|
||||
is Boolean -> value.lift()
|
||||
null -> ESConstant.NULL
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIsExpression(expression: KtIsExpression, data: Unit): Computation {
|
||||
val rightType: KotlinType = trace[BindingContext.TYPE, expression.typeReference] ?: return UNKNOWN_COMPUTATION
|
||||
val arg = extractOrGetCached(expression.leftHandSide)
|
||||
return CallComputation(DefaultBuiltIns.Instance.booleanType, IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg)))
|
||||
}
|
||||
|
||||
override fun visitBinaryExpression(expression: KtBinaryExpression, data: Unit): Computation {
|
||||
val left = extractOrGetCached(expression.left ?: return UNKNOWN_COMPUTATION)
|
||||
val right = extractOrGetCached(expression.right ?: return UNKNOWN_COMPUTATION)
|
||||
|
||||
val args = listOf(left, right)
|
||||
|
||||
return when (expression.operationToken) {
|
||||
KtTokens.EXCLEQ -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(true).invokeWithArguments(args))
|
||||
KtTokens.EQEQ -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(false).invokeWithArguments(args))
|
||||
KtTokens.ANDAND -> CallComputation(DefaultBuiltIns.Instance.booleanType, AndFunctor().invokeWithArguments(args))
|
||||
KtTokens.OROR -> CallComputation(DefaultBuiltIns.Instance.booleanType, OrFunctor().invokeWithArguments(args))
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): Computation {
|
||||
val arg = extractOrGetCached(expression.baseExpression ?: return UNKNOWN_COMPUTATION)
|
||||
return when (expression.operationToken) {
|
||||
KtTokens.EXCL -> CallComputation(DefaultBuiltIns.Instance.booleanType, NotFunctor().invokeWithArguments(arg))
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
}
|
||||
|
||||
private fun ReceiverValue.toComputation(): Computation = when (this) {
|
||||
is ExpressionReceiver -> extractOrGetCached(expression)
|
||||
else -> UNKNOWN_COMPUTATION
|
||||
}
|
||||
|
||||
private fun KtExpression.createDataFlowValue(): DataFlowValue? {
|
||||
return DataFlowValueFactory.createDataFlowValue(
|
||||
expression = this,
|
||||
type = trace.getType(this) ?: return null,
|
||||
bindingContext = trace.bindingContext,
|
||||
containingDeclarationOrModule = moduleDescriptor
|
||||
)
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.getFunctor(): Functor? {
|
||||
trace[BindingContext.FUNCTOR, this]?.let { return it }
|
||||
|
||||
val functor = ContractInterpretationDispatcher().resolveFunctor(this) ?: return null
|
||||
trace.record(BindingContext.FUNCTOR, this, functor)
|
||||
return functor
|
||||
}
|
||||
|
||||
private fun ResolvedCall<*>.isCallWithUnsupportedReceiver(): Boolean =
|
||||
(extensionReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this ||
|
||||
(dispatchReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this ||
|
||||
(explicitReceiverKind == ExplicitReceiverKind.BOTH_RECEIVERS)
|
||||
|
||||
private fun ResolvedCall<*>.getCallArgumentsAsComputations(): List<Computation>? {
|
||||
val arguments = mutableListOf<Computation>()
|
||||
arguments.addIfNotNull(extensionReceiver?.toComputation())
|
||||
arguments.addIfNotNull(dispatchReceiver?.toComputation())
|
||||
|
||||
valueArgumentsByIndex?.mapTo(arguments) {
|
||||
val valueArgument = (it as? ExpressionValueArgument)?.valueArgument ?: return null
|
||||
when (valueArgument) {
|
||||
is KtLambdaArgument -> ESLambda(valueArgument.getLambdaExpression())
|
||||
else -> extractOrGetCached(valueArgument.getArgumentExpression() ?: return null)
|
||||
}
|
||||
} ?: return null
|
||||
|
||||
return arguments
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing
|
||||
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.contracts.description.ContractDescription
|
||||
import org.jetbrains.kotlin.contracts.description.ContractProviderKey
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
|
||||
|
||||
class ContractParsingServices(val languageVersionSettings: LanguageVersionSettings) {
|
||||
fun fastCheckIfContractPresent(element: KtElement): Boolean {
|
||||
if (!isContractAllowedHere(element)) return false
|
||||
val firstExpression = ((element as? KtFunction)?.bodyExpression as? KtBlockExpression)?.statements?.firstOrNull() ?: return false
|
||||
return isContractDescriptionCallFastCheck(firstExpression)
|
||||
}
|
||||
|
||||
fun checkContractAndRecordIfPresent(expression: KtExpression, trace: BindingTrace, scope: LexicalScope, isFirstStatement: Boolean) {
|
||||
val ownerDescriptor = scope.ownerDescriptor
|
||||
if (!isContractDescriptionCallFastCheck(expression) || ownerDescriptor !is FunctionDescriptor) return
|
||||
val contractProvider = ownerDescriptor.getUserData(ContractProviderKey) ?: return
|
||||
|
||||
val isFeatureTurnedOn = languageVersionSettings.supportsFeature(LanguageFeature.CallsInPlaceEffect) ||
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect) ||
|
||||
// This condition is here for technical purposes of compiling 1.2-runtime with contracts
|
||||
languageVersionSettings.getFlag(AnalysisFlag.Flags.allowKotlinPackage)
|
||||
|
||||
val contractDescriptor = when {
|
||||
!isFeatureTurnedOn || !isContractDescriptionCallPreciseCheck(expression, trace.bindingContext) -> null
|
||||
|
||||
!isContractAllowedHere(scope) || !isFirstStatement -> {
|
||||
trace.report(Errors.CONTRACT_NOT_ALLOWED.on(expression))
|
||||
null
|
||||
}
|
||||
|
||||
else -> parseContract(expression, trace, ownerDescriptor)
|
||||
}
|
||||
|
||||
contractProvider.setContractDescription(contractDescriptor)
|
||||
}
|
||||
|
||||
private fun parseContract(expression: KtExpression?, trace: BindingTrace, ownerDescriptor: FunctionDescriptor): ContractDescription? =
|
||||
PsiContractParserDispatcher(trace, this).parseContract(expression, ownerDescriptor)
|
||||
|
||||
internal fun isContractDescriptionCall(expression: KtExpression, context: BindingContext): Boolean =
|
||||
isContractDescriptionCallFastCheck(expression) && isContractDescriptionCallPreciseCheck(expression, context)
|
||||
|
||||
private fun isContractAllowedHere(element: KtElement): Boolean =
|
||||
element is KtNamedFunction && element.isTopLevel && element.hasBlockBody() && !element.hasModifier(KtTokens.OPERATOR_KEYWORD)
|
||||
|
||||
private fun isContractAllowedHere(scope: LexicalScope): Boolean =
|
||||
scope.kind == LexicalScopeKind.CODE_BLOCK && (scope.parent as? LexicalScope)?.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE
|
||||
|
||||
private fun isContractDescriptionCallFastCheck(expression: KtExpression): Boolean =
|
||||
expression is KtCallExpression && expression.calleeExpression?.text == "contract"
|
||||
|
||||
private fun isContractDescriptionCallPreciseCheck(expression: KtExpression, context: BindingContext): Boolean =
|
||||
expression.getResolvedCall(context)?.resultingDescriptor?.isContractCallDescriptor() ?: false
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.*
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.*
|
||||
import org.jetbrains.kotlin.descriptors.ValueDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
|
||||
internal class PsiConditionParser(val trace: BindingTrace, val dispatcher: PsiContractParserDispatcher) : KtVisitor<BooleanExpression?, Unit>() {
|
||||
override fun visitIsExpression(expression: KtIsExpression, data: Unit): BooleanExpression? {
|
||||
val variable = dispatcher.parseVariable(expression.leftHandSide) ?: return null
|
||||
val typeReference = expression.typeReference ?: return null
|
||||
val type = trace[BindingContext.TYPE, typeReference] ?: return null
|
||||
|
||||
return IsInstancePredicate(variable, type, expression.isNegated)
|
||||
}
|
||||
|
||||
override fun visitKtElement(element: KtElement, data: Unit): BooleanExpression? {
|
||||
val resolvedCall = element.getResolvedCall(trace.bindingContext)
|
||||
val descriptor = resolvedCall?.resultingDescriptor ?: return null
|
||||
|
||||
// boolean variable
|
||||
if (descriptor is ValueDescriptor) {
|
||||
val booleanVariable = dispatcher.parseVariable(element as? KtExpression) ?: return null
|
||||
// we don't report type mismatch because it will be reported by the typechecker
|
||||
return booleanVariable as? BooleanVariableReference
|
||||
}
|
||||
|
||||
// operator
|
||||
when {
|
||||
descriptor.isEqualsDescriptor() -> {
|
||||
val left = dispatcher.parseValue((resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression) ?: return null
|
||||
val right = dispatcher.parseValue(resolvedCall.firstArgumentAsExpressionOrNull()) ?: return null
|
||||
val isNegated = (element as? KtBinaryExpression)?.operationToken == KtTokens.EXCLEQ ?: false
|
||||
|
||||
if (left is ConstantReference && left == ConstantReference.NULL && right is VariableReference) {
|
||||
return IsNullPredicate(right, isNegated)
|
||||
}
|
||||
|
||||
if (right is ConstantReference && right == ConstantReference.NULL && left is VariableReference) {
|
||||
return IsNullPredicate(left, isNegated)
|
||||
}
|
||||
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(element, "only equality comparisons with 'null' allowed"))
|
||||
return null
|
||||
}
|
||||
|
||||
else -> {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(element, "unsupported construction"))
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitConstantExpression(expression: KtConstantExpression, data: Unit?): BooleanExpression? {
|
||||
// we don't report type mismatch because it will be reported by the typechecker
|
||||
return dispatcher.parseConstant(expression) as? BooleanConstantReference
|
||||
}
|
||||
|
||||
override fun visitCallExpression(expression: KtCallExpression, data: Unit?): BooleanExpression? {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "call-expressions are not supported yet"))
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitBinaryExpression(expression: KtBinaryExpression, data: Unit): BooleanExpression? {
|
||||
val operationConstructor: (BooleanExpression, BooleanExpression) -> BooleanExpression
|
||||
|
||||
when (expression.operationToken) {
|
||||
KtTokens.ANDAND -> operationConstructor = ::LogicalAnd
|
||||
KtTokens.OROR -> operationConstructor = ::LogicalOr
|
||||
else -> return super.visitBinaryExpression(expression, data) // pass binary expression further
|
||||
}
|
||||
|
||||
val left = expression.left?.accept(this, data) ?: return null
|
||||
val right = expression.right?.accept(this, data) ?: return null
|
||||
return operationConstructor(left, right)
|
||||
}
|
||||
|
||||
override fun visitUnaryExpression(expression: KtUnaryExpression, data: Unit): BooleanExpression? {
|
||||
if (expression.operationToken != KtTokens.EXCL) return super.visitUnaryExpression(expression, data)
|
||||
val arg = expression.baseExpression?.accept(this, data) ?: return null
|
||||
if (arg !is ContractDescriptionValue) {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression.baseExpression!!, "negations in contract description can be applied only to variables/values"))
|
||||
}
|
||||
return LogicalNot(arg)
|
||||
}
|
||||
|
||||
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): BooleanExpression? =
|
||||
KtPsiUtil.deparenthesize(expression)?.accept(this, data)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.BooleanConstantReference
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
|
||||
import org.jetbrains.kotlin.psi.KtConstantExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtVisitor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
internal class PsiConstantParser(val trace: BindingTrace) : KtVisitor<ConstantReference?, Unit>() {
|
||||
override fun visitKtElement(element: KtElement, data: Unit?): ConstantReference? = null
|
||||
|
||||
override fun visitConstantExpression(expression: KtConstantExpression, data: Unit?): ConstantReference? {
|
||||
val type: KotlinType = trace.getType(expression) ?: return null
|
||||
|
||||
val compileTimeConstant: CompileTimeConstant<*>
|
||||
= trace.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return null
|
||||
val value: Any? = compileTimeConstant.getValue(type)
|
||||
|
||||
return when (value) {
|
||||
true -> BooleanConstantReference.TRUE
|
||||
false -> BooleanConstantReference.FALSE
|
||||
null -> ConstantReference.NULL
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.contracts.description.BooleanExpression
|
||||
import org.jetbrains.kotlin.contracts.description.ContractDescription
|
||||
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.BooleanVariableReference
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.ContractDescriptionValue
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.VariableReference
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.CALLS_IN_PLACE_EFFECT
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.CONDITIONAL_EFFECT
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.RETURNS_EFFECT
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.RETURNS_NOT_NULL_EFFECT
|
||||
import org.jetbrains.kotlin.contracts.parsing.effects.PsiCallsEffectParser
|
||||
import org.jetbrains.kotlin.contracts.parsing.effects.PsiConditionalEffectParser
|
||||
import org.jetbrains.kotlin.contracts.parsing.effects.PsiReturnsEffectParser
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
|
||||
internal class PsiContractParserDispatcher(val trace: BindingTrace, val contractParsingServices: ContractParsingServices) {
|
||||
private val conditionParser = PsiConditionParser(trace, this)
|
||||
private val constantParser = PsiConstantParser(trace)
|
||||
private val effectsParsers: Map<Name, PsiEffectParser> = mapOf(
|
||||
RETURNS_EFFECT to PsiReturnsEffectParser(trace, this),
|
||||
RETURNS_NOT_NULL_EFFECT to PsiReturnsEffectParser(trace, this),
|
||||
CALLS_IN_PLACE_EFFECT to PsiCallsEffectParser(trace, this),
|
||||
CONDITIONAL_EFFECT to PsiConditionalEffectParser(trace, this)
|
||||
)
|
||||
|
||||
fun parseContract(expression: KtExpression?, ownerDescriptor: FunctionDescriptor): ContractDescription? {
|
||||
if (expression == null) return null
|
||||
if (!contractParsingServices.isContractDescriptionCall(expression, trace.bindingContext)) return null
|
||||
|
||||
val resolvedCall = expression.getResolvedCall(trace.bindingContext)!! // Must be non-null due to 'isContractDescriptionCall' check
|
||||
|
||||
val lambda = resolvedCall.firstArgumentAsExpressionOrNull() as? KtLambdaExpression ?: return null
|
||||
|
||||
val effects = lambda.bodyExpression?.statements?.mapNotNull { parseEffect(it) } ?: return null
|
||||
|
||||
if (effects.isEmpty()) return null
|
||||
|
||||
return ContractDescription(effects, ownerDescriptor)
|
||||
}
|
||||
|
||||
fun parseCondition(expression: KtExpression?): BooleanExpression? = expression?.accept(conditionParser, Unit)
|
||||
|
||||
fun parseEffect(expression: KtExpression?): EffectDeclaration? {
|
||||
if (expression == null) return null
|
||||
val returnType = expression.getType(trace.bindingContext) ?: return null
|
||||
val parser = effectsParsers[returnType.constructor.declarationDescriptor?.name]
|
||||
if (parser == null) {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "Unrecognized effect"))
|
||||
return null
|
||||
}
|
||||
return parser.tryParseEffect(expression)
|
||||
}
|
||||
|
||||
fun parseConstant(expression: KtExpression?): ConstantReference? {
|
||||
if (expression == null) return null
|
||||
return expression.accept(constantParser, Unit)
|
||||
}
|
||||
|
||||
fun parseVariable(expression: KtExpression?): VariableReference? {
|
||||
if (expression == null) return null
|
||||
val descriptor = expression.getResolvedCall(trace.bindingContext)?.resultingDescriptor ?: return null
|
||||
if (descriptor !is ParameterDescriptor) {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "only references to parameters are allowed in contract description"))
|
||||
return null
|
||||
}
|
||||
|
||||
if (descriptor is ReceiverParameterDescriptor && descriptor.type.constructor.declarationDescriptor?.isFromContractDsl() == true) {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "only references to parameters are allowed. Did you miss label on <this>?"))
|
||||
}
|
||||
|
||||
return if (KotlinBuiltIns.isBoolean(descriptor.type))
|
||||
BooleanVariableReference(descriptor)
|
||||
else
|
||||
VariableReference(descriptor)
|
||||
}
|
||||
|
||||
fun parseValue(expression: KtExpression?): ContractDescriptionValue? {
|
||||
val variable = parseVariable(expression)
|
||||
if (variable != null) return variable
|
||||
|
||||
return parseConstant(expression)
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.CALLS_IN_PLACE
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.CONTRACT
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.CONTRACTS_DSL_ANNOTATION_FQN
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.EFFECT
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.IMPLIES
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.INVOCATION_KIND_ENUM
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.RETURNS
|
||||
import org.jetbrains.kotlin.contracts.parsing.ContractsDslNames.RETURNS_NOT_NULL
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNullableAny
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
|
||||
object ContractsDslNames {
|
||||
// Internal marker-annotation for distinguishing our API
|
||||
val CONTRACTS_DSL_ANNOTATION_FQN = FqName("kotlin.internal.ContractsDsl")
|
||||
|
||||
// Types
|
||||
val EFFECT = Name.identifier("Effect")
|
||||
val CONDITIONAL_EFFECT = Name.identifier("ConditionalEffect")
|
||||
val SIMPLE_EFFECT = Name.identifier("SimpleEffect")
|
||||
val RETURNS_EFFECT = Name.identifier("Returns")
|
||||
val RETURNS_NOT_NULL_EFFECT = Name.identifier("ReturnsNotNull")
|
||||
val CALLS_IN_PLACE_EFFECT = Name.identifier("CallsInPlace")
|
||||
|
||||
// Structure-defining calls
|
||||
val CONTRACT = Name.identifier("contract")
|
||||
val IMPLIES = Name.identifier("implies")
|
||||
|
||||
// Effect-declaration calls
|
||||
val RETURNS = Name.identifier("returns")
|
||||
val RETURNS_NOT_NULL = Name.identifier("returnsNotNull")
|
||||
val CALLS_IN_PLACE = Name.identifier("callsInPlace")
|
||||
|
||||
// enum class InvocationKind
|
||||
val INVOCATION_KIND_ENUM = Name.identifier("InvocationKind")
|
||||
val EXACTLY_ONCE_KIND = Name.identifier("EXACTLY_ONCE")
|
||||
val AT_LEAST_ONCE_KIND = Name.identifier("AT_LEAST_ONCE")
|
||||
val UNKNOWN_KIND = Name.identifier("UNKNOWN")
|
||||
val AT_MOST_ONCE_KIND = Name.identifier("AT_MOST_ONCE")
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.isFromContractDsl(): Boolean = this.annotations.hasAnnotation(CONTRACTS_DSL_ANNOTATION_FQN)
|
||||
|
||||
fun DeclarationDescriptor.isContractCallDescriptor(): Boolean = equalsDslDescriptor(CONTRACT)
|
||||
|
||||
fun DeclarationDescriptor.isImpliesCallDescriptor(): Boolean = equalsDslDescriptor(IMPLIES)
|
||||
|
||||
fun DeclarationDescriptor.isReturnsEffectDescriptor(): Boolean = equalsDslDescriptor(RETURNS)
|
||||
|
||||
fun DeclarationDescriptor.isReturnsNotNullDescriptor(): Boolean = equalsDslDescriptor(RETURNS_NOT_NULL)
|
||||
|
||||
fun DeclarationDescriptor.isEffectDescriptor(): Boolean = equalsDslDescriptor(EFFECT)
|
||||
|
||||
fun DeclarationDescriptor.isCallsInPlaceEffectDescriptor(): Boolean = equalsDslDescriptor(CALLS_IN_PLACE)
|
||||
|
||||
fun DeclarationDescriptor.isInvocationKindEnum(): Boolean = equalsDslDescriptor(INVOCATION_KIND_ENUM)
|
||||
|
||||
fun DeclarationDescriptor.isEqualsDescriptor(): Boolean =
|
||||
this is FunctionDescriptor && this.name == Name.identifier("equals") && // fast checks
|
||||
this.returnType?.isBoolean() == true && this.valueParameters.singleOrNull()?.type?.isNullableAny() == true // signature matches
|
||||
|
||||
internal fun ResolvedCall<*>.firstArgumentAsExpressionOrNull(): KtExpression? =
|
||||
this.valueArgumentsByIndex?.firstOrNull()?.safeAs<ExpressionValueArgument>()?.valueArgument?.getArgumentExpression()
|
||||
|
||||
private fun DeclarationDescriptor.equalsDslDescriptor(dslName: Name): Boolean = this.name == dslName && this.isFromContractDsl()
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
|
||||
internal interface PsiEffectParser {
|
||||
fun tryParseEffect(expression: KtExpression): EffectDeclaration?
|
||||
}
|
||||
|
||||
internal abstract class AbstractPsiEffectParser(val trace: BindingTrace, val contractParserDispatcher: PsiContractParserDispatcher) : PsiEffectParser
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing.effects
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.CallsEffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.contracts.parsing.*
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
|
||||
|
||||
internal class PsiCallsEffectParser(
|
||||
trace: BindingTrace,
|
||||
contractParserDispatcher: PsiContractParserDispatcher
|
||||
) : AbstractPsiEffectParser(trace, contractParserDispatcher) {
|
||||
|
||||
override fun tryParseEffect(expression: KtExpression): EffectDeclaration? {
|
||||
val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
|
||||
if (!descriptor.isCallsInPlaceEffectDescriptor()) return null
|
||||
|
||||
val lambda = contractParserDispatcher.parseVariable(resolvedCall.firstArgumentAsExpressionOrNull()) ?: return null
|
||||
|
||||
val kindArgument = resolvedCall.valueArgumentsByIndex?.getOrNull(1)
|
||||
|
||||
val kind = when (kindArgument) {
|
||||
is DefaultValueArgument -> InvocationKind.UNKNOWN
|
||||
is ExpressionValueArgument -> kindArgument.valueArgument?.getArgumentExpression()?.toInvocationKind(trace) ?: return null
|
||||
else -> return null
|
||||
}
|
||||
|
||||
return CallsEffectDeclaration(lambda, kind)
|
||||
}
|
||||
|
||||
private fun KtExpression.toInvocationKind(trace: BindingTrace): InvocationKind? {
|
||||
val descriptor = this.getResolvedCall(trace.bindingContext)?.resultingDescriptor ?: return null
|
||||
if (!descriptor.parents.first().isInvocationKindEnum()) return null
|
||||
|
||||
return when (descriptor.fqNameSafe.shortName()) {
|
||||
ContractsDslNames.AT_MOST_ONCE_KIND -> InvocationKind.AT_MOST_ONCE
|
||||
ContractsDslNames.EXACTLY_ONCE_KIND -> InvocationKind.EXACTLY_ONCE
|
||||
ContractsDslNames.AT_LEAST_ONCE_KIND -> InvocationKind.AT_LEAST_ONCE
|
||||
ContractsDslNames.UNKNOWN_KIND -> InvocationKind.UNKNOWN
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing.effects
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.ConditionalEffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.parsing.AbstractPsiEffectParser
|
||||
import org.jetbrains.kotlin.contracts.parsing.PsiContractParserDispatcher
|
||||
import org.jetbrains.kotlin.contracts.parsing.firstArgumentAsExpressionOrNull
|
||||
import org.jetbrains.kotlin.contracts.parsing.isImpliesCallDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
internal class PsiConditionalEffectParser(
|
||||
trace: BindingTrace,
|
||||
dispatcher: PsiContractParserDispatcher
|
||||
) : AbstractPsiEffectParser(trace, dispatcher) {
|
||||
override fun tryParseEffect(expression: KtExpression): EffectDeclaration? {
|
||||
val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null
|
||||
|
||||
if (!resolvedCall.resultingDescriptor.isImpliesCallDescriptor()) return null
|
||||
|
||||
val effect = contractParserDispatcher.parseEffect(resolvedCall.dispatchReceiver.safeAs<ExpressionReceiver>()?.expression)
|
||||
?: return null
|
||||
val condition = contractParserDispatcher.parseCondition(resolvedCall.firstArgumentAsExpressionOrNull())
|
||||
?: return null
|
||||
|
||||
return ConditionalEffectDeclaration(effect, condition)
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.contracts.parsing.effects
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.EffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.ReturnsEffectDeclaration
|
||||
import org.jetbrains.kotlin.contracts.description.expressions.ConstantReference
|
||||
import org.jetbrains.kotlin.contracts.parsing.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
|
||||
internal class PsiReturnsEffectParser(
|
||||
trace: BindingTrace,
|
||||
contractParserDispatcher: PsiContractParserDispatcher
|
||||
) : AbstractPsiEffectParser(trace, contractParserDispatcher) {
|
||||
override fun tryParseEffect(expression: KtExpression): EffectDeclaration? {
|
||||
val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
|
||||
if (descriptor.isReturnsNotNullDescriptor())
|
||||
return ReturnsEffectDeclaration(ConstantReference.NOT_NULL)
|
||||
|
||||
if (!descriptor.isReturnsEffectDescriptor()) return null
|
||||
|
||||
val argumentExpression = resolvedCall.firstArgumentAsExpressionOrNull()
|
||||
val constantValue = if (argumentExpression == null)
|
||||
ConstantReference.WILDCARD
|
||||
else {
|
||||
// Note that we distinguish absence of an argument and unparsed argument
|
||||
val constant = contractParserDispatcher.parseConstant(argumentExpression)
|
||||
if (constant == null) {
|
||||
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(argumentExpression, "only true/false/null constants in Returns-effect are currently supported"))
|
||||
return null
|
||||
}
|
||||
constant
|
||||
}
|
||||
|
||||
return ReturnsEffectDeclaration(constantValue)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user