KT-60821 [KAPT] Generate stubs without KaptTreeMaker

Merge-request: KT-MR-12979
Merged-by: Pavel Mikhailovskii <Pavel.Mikhailovskii@jetbrains.com>
This commit is contained in:
strangepleasures
2023-11-16 21:14:36 +00:00
committed by Space Team
parent 0fba58eee7
commit 9c2ce475e2
21 changed files with 1035 additions and 1644 deletions
@@ -29,6 +29,12 @@ import kotlin.collections.List
import kotlin.system.measureTimeMillis
import com.sun.tools.javac.util.List as JavacList
fun KaptContext.doAnnotationProcessing(
javaSourceFiles: List<File>,
processors: List<IncrementalProcessor>,
binaryTypesToReprocess: List<String> = emptyList()
) = doAnnotationProcessing(javaSourceFiles, processors, JavacList.nil(), binaryTypesToReprocess)
fun KaptContext.doAnnotationProcessing(
javaSourceFiles: List<File>,
processors: List<IncrementalProcessor>,
@@ -1,152 +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.kapt3.stubs
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.sun.tools.javac.parser.Tokens
import com.sun.tools.javac.tree.DCTree
import com.sun.tools.javac.tree.DocCommentTable
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeScanner
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.org.objectweb.asm.Opcodes
abstract class AbstractKDocCommentKeeper<T: KaptContext>(protected val kaptContext: T) {
private val docCommentTable = KaptDocCommentTable()
fun getDocTable(file: JCTree.JCCompilationUnit): DocCommentTable {
val map = docCommentTable.takeIf { it.map.isNotEmpty() } ?: return docCommentTable
// Enum values with doc comments are rendered incorrectly in javac pretty print,
// so we delete the comments.
file.accept(object : TreeScanner() {
var removeComments = false
override fun visitVarDef(def: JCTree.JCVariableDecl) {
if (!removeComments && (def.modifiers.flags and Opcodes.ACC_ENUM.toLong()) != 0L) {
map.removeComment(def)
removeComments = true
super.visitVarDef(def)
removeComments = false
return
}
super.visitVarDef(def)
}
override fun scan(tree: JCTree?) {
if (removeComments && tree != null) {
map.removeComment(tree)
}
super.scan(tree)
}
})
return docCommentTable
}
protected fun saveKDocComment(tree: JCTree, comment: KDoc) {
docCommentTable.putComment(tree, KDocComment(escapeNestedComments(extractCommentText(comment))))
}
private fun escapeNestedComments(text: String): String {
val result = StringBuilder()
var index = 0
var commentLevel = 0
while (index < text.length) {
val currentChar = text[index]
fun nextChar() = text.getOrNull(index + 1)
if (currentChar == '/' && nextChar() == '*') {
commentLevel++
index++
result.append("/ *")
} else if (currentChar == '*' && nextChar() == '/') {
commentLevel = maxOf(0, commentLevel - 1)
index++
result.append("* /")
} else {
result.append(currentChar)
}
index++
}
return result.toString()
}
private fun extractCommentText(docComment: KDoc): String {
return buildString {
docComment.accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
if (element is LeafPsiElement) {
if (element.isKDocLeadingAsterisk()) {
val indent = takeLastWhile { it == ' ' || it == '\t' }.length
if (indent > 0) {
delete(length - indent, length)
}
} else if (!element.isKDocStart() && !element.isKDocEnd()) {
append(element.text)
}
}
super.visitElement(element)
}
})
}.trimIndent().trim()
}
private fun LeafPsiElement.isKDocStart() = elementType == KDocTokens.START
private fun LeafPsiElement.isKDocEnd() = elementType == KDocTokens.END
private fun LeafPsiElement.isKDocLeadingAsterisk() = elementType == KDocTokens.LEADING_ASTERISK
}
private class KDocComment(val body: String) : Tokens.Comment {
override fun getSourcePos(index: Int) = -1
override fun getStyle() = Tokens.Comment.CommentStyle.JAVADOC
override fun getText() = body
override fun isDeprecated() = false
}
private class KaptDocCommentTable(map: Map<JCTree, Tokens.Comment> = emptyMap()) : DocCommentTable {
private val table = map.toMutableMap()
val map: Map<JCTree, Tokens.Comment>
get() = table
override fun hasComment(tree: JCTree) = tree in table
override fun getComment(tree: JCTree) = table[tree]
override fun getCommentText(tree: JCTree) = getComment(tree)?.text
override fun getCommentTree(tree: JCTree): DCTree.DCDocComment? = null
override fun putComment(tree: JCTree, c: Tokens.Comment) {
table[tree] = c
}
fun removeComment(tree: JCTree) {
table.remove(tree)
}
}
@@ -5,19 +5,31 @@
package org.jetbrains.kotlin.kapt3.stubs
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.sun.tools.javac.parser.Tokens
import com.sun.tools.javac.tree.DCTree
import com.sun.tools.javac.tree.DocCommentTable
import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.tree.TreeScanner
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.kapt3.KaptContextForStubGeneration
import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.FieldNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class Kapt3DocCommentKeeper(kaptContext: KaptContextForStubGeneration) : AbstractKDocCommentKeeper<KaptContextForStubGeneration>(kaptContext) {
internal class Kapt3DocCommentKeeper(private val kaptContext: KaptContextForStubGeneration) {
private val docCommentTable = KaptDocCommentTable()
fun saveKDocComment(tree: JCTree, node: Any) {
val origin = kaptContext.origins[node] ?: return
val psiElement = origin.element as? KtDeclaration ?: return
@@ -45,4 +57,125 @@ class Kapt3DocCommentKeeper(kaptContext: KaptContextForStubGeneration) : Abstrac
saveKDocComment(tree, docComment)
}
}
fun getDocTable(file: JCTree.JCCompilationUnit): DocCommentTable {
val map = docCommentTable.takeIf { it.map.isNotEmpty() } ?: return docCommentTable
// Enum values with doc comments are rendered incorrectly in javac pretty print,
// so we delete the comments.
file.accept(object : TreeScanner() {
var removeComments = false
override fun visitVarDef(def: JCTree.JCVariableDecl) {
if (!removeComments && (def.modifiers.flags and Opcodes.ACC_ENUM.toLong()) != 0L) {
map.removeComment(def)
removeComments = true
super.visitVarDef(def)
removeComments = false
return
}
super.visitVarDef(def)
}
override fun scan(tree: JCTree?) {
if (removeComments && tree != null) {
map.removeComment(tree)
}
super.scan(tree)
}
})
return docCommentTable
}
protected fun saveKDocComment(tree: JCTree, comment: KDoc) {
docCommentTable.putComment(tree, KDocComment(extractComment(comment)))
}
}
private class KDocComment(val body: String) : Tokens.Comment {
override fun getSourcePos(index: Int) = -1
override fun getStyle() = Tokens.Comment.CommentStyle.JAVADOC
override fun getText() = body
override fun isDeprecated() = false
}
private class KaptDocCommentTable(map: Map<JCTree, Tokens.Comment> = emptyMap()) : DocCommentTable {
private val table = map.toMutableMap()
val map: Map<JCTree, Tokens.Comment>
get() = table
override fun hasComment(tree: JCTree) = tree in table
override fun getComment(tree: JCTree) = table[tree]
override fun getCommentText(tree: JCTree) = getComment(tree)?.text
override fun getCommentTree(tree: JCTree): DCTree.DCDocComment? = null
override fun putComment(tree: JCTree, c: Tokens.Comment) {
table[tree] = c
}
fun removeComment(tree: JCTree) {
table.remove(tree)
}
}
fun extractComment(comment: KDoc) = escapeNestedComments(extractCommentText(comment))
private fun escapeNestedComments(text: String): String {
val result = StringBuilder()
var index = 0
var commentLevel = 0
while (index < text.length) {
val currentChar = text[index]
fun nextChar() = text.getOrNull(index + 1)
if (currentChar == '/' && nextChar() == '*') {
commentLevel++
index++
result.append("/ *")
} else if (currentChar == '*' && nextChar() == '/') {
commentLevel = maxOf(0, commentLevel - 1)
index++
result.append("* /")
} else {
result.append(currentChar)
}
index++
}
return result.toString()
}
private fun extractCommentText(docComment: KDoc): String {
return buildString {
docComment.accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
if (element is LeafPsiElement) {
if (element.isKDocLeadingAsterisk()) {
val indent = takeLastWhile { it == ' ' || it == '\t' }.length
if (indent > 0) {
delete(length - indent, length)
}
} else if (!element.isKDocStart() && !element.isKDocEnd()) {
append(element.text)
}
}
super.visitElement(element)
}
})
}.trimIndent().trim()
}
private fun LeafPsiElement.isKDocStart() = elementType == KDocTokens.START
private fun LeafPsiElement.isKDocEnd() = elementType == KDocTokens.END
private fun LeafPsiElement.isKDocLeadingAsterisk() = elementType == KDocTokens.LEADING_ASTERISK
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.kapt3.stubs
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
/**
@@ -20,9 +19,9 @@ import org.jetbrains.kotlin.kapt3.base.stubs.KotlinPosition
* The consequence is that the contents of the generated stub files may not be consistent across a clean build and an incremental
* build, making the build non-deterministic and dependent tasks run unnecessarily (see KT-40882).
*/
class MembersPositionComparator(val classSource: KotlinPosition?, val memberData: Map<JCTree, MemberData>) :
Comparator<JCTree> {
override fun compare(o1: JCTree, o2: JCTree): Int {
class MembersPositionComparator<T>(val classSource: KotlinPosition?, val memberData: Map<T, MemberData>) :
Comparator<T> {
override fun compare(o1: T, o2: T): Int {
val data1 = memberData.getValue(o1)
val data2 = memberData.getValue(o2)
classSource ?: return compareDescriptors(data1, data2)
@@ -51,4 +50,4 @@ class MembersPositionComparator(val classSource: KotlinPosition?, val memberData
return m1.descriptor.compareTo(m2.descriptor)
}
}
class MemberData(val name: String, val descriptor: String, val position: KotlinPosition?)
class MemberData(val name: String, val descriptor: String, val position: KotlinPosition?)
@@ -44,6 +44,7 @@ class Test4 {
}
enum class EnumError {
/** This is the one */
One {
override fun doIt() = ""
@@ -135,10 +135,10 @@ public enum Em {
@kotlin.Metadata()
public final class Foo {
private final boolean z = true;
private final byte b = (byte)0;
private final byte b = 0;
private final char c = 'c';
private final char c2 = '\n';
private final short sh = (short)10;
private final short sh = 10;
private final int i = 10;
private final long l = -10L;
private final float f = 1.0F;
@@ -79,7 +79,7 @@ public final class JvmStaticTest {
private static final int one = 1;
public static final int two = 2;
public static final char c = 'C';
public final byte three = (byte)3;
public final byte three = 3;
public final char d = 'D';
public JvmStaticTest() {
@@ -150,9 +150,9 @@ public final class PrimitiveTypes {
public static final int intMinValue = -2147483648;
public static final int intMaxValue = 2147483647;
public static final int intHex = -1;
public static final byte byte0 = (byte)0;
public static final byte byte50 = (byte)50;
public static final short short5 = (short)5;
public static final byte byte0 = 0;
public static final byte byte50 = 50;
public static final short short5 = 5;
public static final char charC = 'C';
public static final char char0 = '\u0000';
public static final char char10 = '\n';