Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.16-release: cherry-pick changes manually. #1650

Merged
merged 16 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.intellij.util.containers.MultiMap
import java.io.File

interface LookupTrackerWrapper {
fun record(
filePath: String,
scopeFqName: String,
name: String
)

val lookups: MultiMap<LookupSymbolWrapper, String>
}

data class LookupSymbolWrapper(val name: String, val scope: String) : Comparable<LookupSymbolWrapper> {
override fun compareTo(other: LookupSymbolWrapper): Int {
val scopeCompare = scope.compareTo(other.scope)
if (scopeCompare != 0) return scopeCompare

return name.compareTo(other.name)
}
}

interface LookupStorageWrapper {
fun removeLookupsFrom(files: Sequence<File>)
fun update(lookupTracker: LookupTrackerWrapper, filesToCompile: Iterable<File>, removedFiles: Iterable<File>)
fun flush()
fun close()
operator fun get(lookupSymbol: LookupSymbolWrapper): Collection<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,12 @@ class FileToFilesMap(
FileKeyDescriptor,
ListExternalizer(FileExternalizer)
)

class FileToSymbolsMap(
storageFile: File,
lookupSymbolExternalizer: DataExternalizer<LookupSymbolWrapper>
) : PersistentMap<File, List<LookupSymbolWrapper>>(
storageFile,
FileKeyDescriptor,
ListExternalizer(lookupSymbolExternalizer),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* 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.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)
}
Loading
Loading