/
IncrementalContext.kt
263 lines (233 loc) · 10.7 KB
/
IncrementalContext.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/*
* Copyright 2023 Google LLC
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
*
* 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 com.google.devtools.ksp
import com.google.devtools.ksp.common.FileToSymbolsMap
import com.google.devtools.ksp.common.IncrementalContextBase
import com.google.devtools.ksp.common.LookupStorageWrapper
import com.google.devtools.ksp.common.LookupSymbolWrapper
import com.google.devtools.ksp.common.LookupTrackerWrapper
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.impl.findPsi
import com.google.devtools.ksp.symbol.impl.java.KSFunctionDeclarationJavaImpl
import com.google.devtools.ksp.symbol.impl.java.KSPropertyDeclarationJavaImpl
import com.intellij.psi.PsiArrayType
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiField
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiType
import com.intellij.psi.PsiWildcardType
import com.intellij.psi.impl.source.PsiClassReferenceType
import com.intellij.util.containers.MultiMap
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.IOUtil
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
import org.jetbrains.kotlin.incremental.LookupStorage
import org.jetbrains.kotlin.incremental.LookupSymbol
import org.jetbrains.kotlin.incremental.LookupTrackerImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.Position
import org.jetbrains.kotlin.incremental.components.ScopeKind
import org.jetbrains.kotlin.incremental.storage.FileToPathConverter
import org.jetbrains.kotlin.incremental.update
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
import java.io.DataInput
import java.io.DataOutput
import java.io.File
class IncrementalContext(
options: KspOptions,
componentProvider: ComponentProvider,
anyChangesWildcard: File,
) : IncrementalContextBase(
anyChangesWildcard,
options.incrementalLog,
options.projectBaseDir,
options.cachesDir,
options.kspOutputDir,
options.knownModified,
options.knownRemoved,
options.changedClasses,
) {
private val lookupTracker: LookupTracker = componentProvider.get()
// Disable incremental processing if somehow DualLookupTracker failed to be registered.
// This may happen when a platform hasn't support incremental compilation yet. E.g, Common / Metadata.
override val isIncremental = options.incremental && lookupTracker is DualLookupTracker
private val PATH_CONVERTER = RelativeFileToPathConverter(baseDir)
private val icContext = IncrementalCompilationContext(PATH_CONVERTER, PATH_CONVERTER, true)
private val symbolLookupCacheDir = File(cachesDir, "symbolLookups")
override val symbolLookupTracker =
LookupTrackerWrapperImpl((lookupTracker as? DualLookupTracker)?.symbolTracker ?: LookupTracker.DO_NOTHING)
override val symbolLookupCache = LookupStorageWrapperImpl(LookupStorage(symbolLookupCacheDir, icContext))
// TODO: rewrite LookupStorage to share file-to-id, etc.
private val classLookupCacheDir = File(cachesDir, "classLookups")
override val classLookupTracker =
LookupTrackerWrapperImpl((lookupTracker as? DualLookupTracker)?.classTracker ?: LookupTracker.DO_NOTHING)
override val classLookupCache = LookupStorageWrapperImpl(LookupStorage(classLookupCacheDir, icContext))
override val sealedMap = FileToSymbolsMap(File(cachesDir, "sealed"), LookupSymbolExternalizer)
override val symbolsMap = FileToSymbolsMap(File(cachesDir, "symbols"), LookupSymbolExternalizer)
// Debugging and testing only.
fun dumpLookupRecords(): Map<String, List<String>> {
val map = mutableMapOf<String, List<String>>()
symbolLookupTracker.lookups.entrySet().forEach { e ->
val key = "${e.key.scope}.${e.key.name}"
map[key] = e.value.map { PATH_CONVERTER.toFile(it).path }
}
return map
}
// Record a type reference, including its type arguments.
fun recordLookup(ref: PsiType) {
// Record a *leaf* type reference. This doesn't address type arguments.
fun recordLookup(ref: PsiClassReferenceType, def: PsiClass) {
val psiFile = ref.reference.containingFile as? PsiJavaFile ?: return
// A type parameter doesn't have qualified name.
//
// Note that bounds of type parameters, or other references in classes,
// are not addressed recursively here. They are recorded in other places
// with more contexts, when necessary.
def.qualifiedName?.let { recordLookup(psiFile, it) }
}
when (ref) {
is PsiArrayType -> recordLookup(ref.componentType)
is PsiClassReferenceType -> {
val def = ref.resolve() ?: return
recordLookup(ref, def)
// in case the corresponding KotlinType is passed through ways other than KSTypeReferenceJavaImpl
ref.typeArguments().forEach {
if (it is PsiType) {
recordLookup(it)
}
}
}
is PsiWildcardType -> ref.bound?.let { recordLookup(it) }
}
}
// Record all type references in a Java field.
private fun recordLookupForJavaField(psi: PsiField) {
recordLookup(psi.type)
}
// Record all type references in a Java method.
private fun recordLookupForJavaMethod(psi: PsiMethod) {
psi.parameterList.parameters.forEach {
recordLookup(it.type)
}
psi.returnType?.let { recordLookup(it) }
psi.typeParameters.forEach {
it.bounds.mapNotNull { it as? PsiType }.forEach {
recordLookup(it)
}
}
}
// Record all references to super types (if they are written in Java) of a given type,
// in its type hierarchy.
fun recordLookupWithSupertypes(kotlinType: KotlinType) {
(listOf(kotlinType) + kotlinType.supertypes()).mapNotNull {
it.constructor.declarationDescriptor?.findPsi() as? PsiClass
}.forEach {
it.superTypes.forEach {
recordLookup(it)
}
}
}
// Record all type references in a CallableMemberDescriptor
fun recordLookupForCallableMemberDescriptor(descriptor: CallableMemberDescriptor) {
val psi = descriptor.findPsi()
when (psi) {
is PsiMethod -> recordLookupForJavaMethod(psi)
is PsiField -> recordLookupForJavaField(psi)
}
}
// Record references from all declared functions in the type hierarchy of the given class.
// TODO: optimization: filter out inaccessible members
fun recordLookupForGetAllFunctions(descriptor: ClassDescriptor) {
recordLookupForGetAll(descriptor) {
it.methods.forEach {
recordLookupForJavaMethod(it)
}
}
}
// Record references from all declared fields in the type hierarchy of the given class.
// TODO: optimization: filter out inaccessible members
fun recordLookupForGetAllProperties(descriptor: ClassDescriptor) {
recordLookupForGetAll(descriptor) {
it.fields.forEach {
recordLookupForJavaField(it)
}
}
}
private fun recordLookupForGetAll(descriptor: ClassDescriptor, doChild: (PsiClass) -> Unit) {
(descriptor.getAllSuperclassesWithoutAny() + descriptor).mapNotNull {
it.findPsi() as? PsiClass
}.forEach { psiClass ->
psiClass.superTypes.forEach {
recordLookup(it)
}
doChild(psiClass)
}
}
// Record all type references in a KSDeclaration
fun recordLookupForDeclaration(declaration: KSDeclaration) {
when (declaration) {
is KSPropertyDeclarationJavaImpl -> recordLookupForJavaField(declaration.psi)
is KSFunctionDeclarationJavaImpl -> recordLookupForJavaMethod(declaration.psi)
}
}
}
class LookupTrackerWrapperImpl(val lookupTracker: LookupTracker) : LookupTrackerWrapper {
override val lookups: MultiMap<LookupSymbolWrapper, String>
get() = MultiMap<LookupSymbolWrapper, String>().also { wrapper ->
(lookupTracker as LookupTrackerImpl).lookups.entrySet().forEach { e ->
wrapper.putValues(LookupSymbolWrapper(e.key.name, e.key.scope), e.value)
}
}
override fun record(filePath: String, scopeFqName: String, name: String) {
lookupTracker.record(filePath, Position.NO_POSITION, scopeFqName, ScopeKind.PACKAGE, name)
}
}
class LookupStorageWrapperImpl(
val impl: LookupStorage
) : LookupStorageWrapper {
override fun get(lookupSymbol: LookupSymbolWrapper): Collection<String> =
impl.get(LookupSymbol(lookupSymbol.name, lookupSymbol.scope))
override fun update(
lookupTracker: LookupTrackerWrapper,
filesToCompile: Iterable<File>,
removedFiles: Iterable<File>
) {
impl.update((lookupTracker as LookupTrackerWrapperImpl).lookupTracker, filesToCompile, removedFiles)
}
override fun removeLookupsFrom(files: Sequence<File>) = impl.removeLookupsFrom(files)
override fun close() = impl.close()
override fun flush() = impl.flush(true)
}
object LookupSymbolExternalizer : DataExternalizer<LookupSymbolWrapper> {
override fun read(input: DataInput): LookupSymbolWrapper =
LookupSymbolWrapper(IOUtil.readString(input), IOUtil.readString(input))
override fun save(output: DataOutput, value: LookupSymbolWrapper) {
IOUtil.writeString(value.name, output)
IOUtil.writeString(value.scope, output)
}
}
internal class RelativeFileToPathConverter(val baseDir: File) : FileToPathConverter {
override fun toPath(file: File): String = file.path
override fun toFile(path: String): File = File(path).relativeTo(baseDir)
}