JVM: remove StubClassBuilder and its usage

After legacy light classes were removed, the only remaining usage was in
KotlinLightClassBuilderFactory where we ran JVM backend in the
LIGHT_CLASSES mode, and that was only used from diagnostic tests to
report JVM backend diagnostics.

We don't actually need to build stubs here, so we can just use ASM's
class writer, which means that StubClassBuilder and all its dependencies
can be removed.
This commit is contained in:
Alexander Udalov
2024-03-09 00:14:37 +01:00
committed by Space Team
parent e5ad035039
commit 2f056fe155
9 changed files with 17 additions and 378 deletions
@@ -5,38 +5,22 @@
package org.jetbrains.kotlin.cli.jvm.compiler.builder
import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.stubs.StubElement
import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.AbstractClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.ClassWriter
class KotlinLightClassBuilderFactory(private val javaFileStub: PsiJavaFileStub) : ClassBuilderFactory {
private val stubStack = Stack<StubElement<PsiElement>>().apply {
@Suppress("UNCHECKED_CAST")
push(javaFileStub as StubElement<PsiElement>)
}
object KotlinLightClassBuilderFactory : ClassBuilderFactory {
override fun getClassBuilderMode(): ClassBuilderMode = ClassBuilderMode.LIGHT_CLASSES
override fun newClassBuilder(origin: JvmDeclarationOrigin) =
StubClassBuilder(stubStack, javaFileStub)
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder =
object : AbstractClassBuilder() {
override fun getVisitor(): ClassVisitor = ClassWriter(0)
}
override fun asText(builder: ClassBuilder) = throw UnsupportedOperationException("asText is not implemented")
override fun asBytes(builder: ClassBuilder) = throw UnsupportedOperationException("asBytes is not implemented")
override fun close() {}
fun result(): PsiJavaFileStub {
val pop = stubStack.pop()
if (pop !== javaFileStub) {
LOG.error("Unbalanced stack operations: $pop")
}
return javaFileStub
}
}
private val LOG = Logger.getInstance(KotlinLightClassBuilderFactory::class.java)
@@ -9,10 +9,6 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
@@ -22,7 +18,7 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
data class CodeGenerationResult(val stub: PsiJavaFileStub, val bindingContext: BindingContext, val diagnostics: Diagnostics)
data class CodeGenerationResult(val bindingContext: BindingContext, val diagnostics: Diagnostics)
fun extraJvmDiagnosticsFromBackend(
packageFqName: FqName,
@@ -34,10 +30,9 @@ fun extraJvmDiagnosticsFromBackend(
val project = files.first().project
try {
val classBuilderFactory = KotlinLightClassBuilderFactory(createJavaFileStub(packageFqName, files))
val state = GenerationState.Builder(
project,
classBuilderFactory,
KotlinLightClassBuilderFactory,
context.module,
context.bindingContext,
context.languageVersionSettings?.let {
@@ -53,8 +48,7 @@ fun extraJvmDiagnosticsFromBackend(
generate(state, files)
val javaFileStub = classBuilderFactory.result()
return CodeGenerationResult(javaFileStub, context.bindingContext, state.collectedExtraJvmDiagnostics)
return CodeGenerationResult(context.bindingContext, state.collectedExtraJvmDiagnostics)
} catch (e: ProcessCanceledException) {
throw e
} catch (e: RuntimeException) {
@@ -63,21 +57,6 @@ fun extraJvmDiagnosticsFromBackend(
}
}
private fun createJavaFileStub(packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), /* compiled = */true)
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
val fakeFile = object : ClsFileImpl(files.first().viewProvider) {
override fun getStub() = javaFileStub
override fun getPackageName() = packageFqName.asString()
override fun isPhysical() = false
override fun getText(): String = files.singleOrNull()?.text ?: super.getText()
}
javaFileStub.psi = fakeFile
return javaFileStub
}
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
val path = virtualFile?.path ?: "<null>"
LOG.error(
@@ -1,203 +0,0 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.jvm.compiler.builder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.compiled.InnerClassSourceStrategy;
import com.intellij.psi.impl.compiled.StubBuildingVisitor;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory;
import org.jetbrains.kotlin.asJava.builder.LightElementOrigin;
import org.jetbrains.kotlin.asJava.builder.LightElementOriginKt;
import org.jetbrains.kotlin.codegen.AbstractClassBuilder;
import org.jetbrains.kotlin.fileClasses.OldPackageFacadeClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.FieldVisitor;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import java.util.List;
public class StubClassBuilder extends AbstractClassBuilder {
private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
@Override
public Object findInnerClass(String s, Object o) {
return null;
}
@Override
public void accept(Object innerClass, StubBuildingVisitor<Object> visitor) {
throw new UnsupportedOperationException("Shall not be called!");
}
};
private final StubElement parent;
private final PsiJavaFileStub fileStub;
private StubBuildingVisitor v;
private final Stack<StubElement> parentStack;
private boolean isPackageClass = false;
public StubClassBuilder(@NotNull Stack<StubElement> parentStack, @NotNull PsiJavaFileStub fileStub) {
this.parentStack = parentStack;
this.parent = parentStack.peek();
this.fileStub = fileStub;
}
@NotNull
@Override
public ClassVisitor getVisitor() {
assert v != null : "Called before class is defined";
return v;
}
@Override
public void defineClass(
PsiElement origin,
int version,
int access,
@NotNull String name,
@Nullable String signature,
@NotNull String superName,
@NotNull String[] interfaces
) {
assert v == null : "defineClass() called twice?";
//noinspection ConstantConditions
v = new StubBuildingVisitor<>(null, EMPTY_STRATEGY, parent, access, calculateShortName(name));
super.defineClass(origin, version, access, name, signature, superName, interfaces);
if (origin instanceof KtFile) {
FqName packageName = ((KtFile) origin).getPackageFqName();
String packageClassName = OldPackageFacadeClassUtils.getPackageClassName(packageName);
if (name.equals(packageClassName) || name.endsWith("/" + packageClassName)) {
isPackageClass = true;
}
}
if (!isPackageClass) {
parentStack.push(v.getResult());
}
((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightClassOrigin(origin));
}
@Nullable
private String calculateShortName(@NotNull String internalName) {
if (parent instanceof PsiJavaFileStub) {
assert parent == fileStub;
String packagePrefix = getPackageInternalNamePrefix();
assert internalName.startsWith(packagePrefix) : internalName + " : " + packagePrefix;
return internalName.substring(packagePrefix.length());
}
if (parent instanceof PsiClassStub<?>) {
String parentPrefix = getClassInternalNamePrefix((PsiClassStub) parent);
if (parentPrefix == null) return null;
assert internalName.startsWith(parentPrefix) : internalName + " : " + parentPrefix;
return internalName.substring(parentPrefix.length());
}
return null;
}
@Nullable
private String getClassInternalNamePrefix(@NotNull PsiClassStub classStub) {
String packageName = fileStub.getPackageName();
String classStubQualifiedName = classStub.getQualifiedName();
if (classStubQualifiedName == null) return null;
if (packageName.isEmpty()) {
return classStubQualifiedName.replace('.', '$') + "$";
}
else {
return packageName.replace('.', '/') + "/" + classStubQualifiedName.substring(packageName.length() + 1).replace('.', '$') + "$";
}
}
@NotNull
private String getPackageInternalNamePrefix() {
String packageName = fileStub.getPackageName();
if (packageName.isEmpty()) {
return "";
}
else {
return packageName.replace('.', '/') + "/";
}
}
@NotNull
@Override
public MethodVisitor newMethod(
@NotNull JvmDeclarationOrigin origin,
int access,
@NotNull String name,
@NotNull String desc,
@Nullable String signature,
@Nullable String[] exceptions
) {
MethodVisitor internalVisitor = super.newMethod(origin, access, name, desc, signature, exceptions);
if (internalVisitor != EMPTY_METHOD_VISITOR) {
// If stub for method generated
markLastChild(origin);
}
return internalVisitor;
}
@NotNull
@Override
public FieldVisitor newField(
@NotNull JvmDeclarationOrigin origin,
int access,
@NotNull String name,
@NotNull String desc,
@Nullable String signature,
@Nullable Object value
) {
FieldVisitor internalVisitor = super.newField(origin, access, name, desc, signature, value);
if (internalVisitor != EMPTY_FIELD_VISITOR) {
// If stub for field generated
markLastChild(origin);
}
return internalVisitor;
}
private void markLastChild(@NotNull JvmDeclarationOrigin origin) {
List children = v.getResult().getChildrenStubs();
StubBase last = (StubBase) children.get(children.size() - 1);
LightElementOrigin oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN);
if (oldOrigin != null) {
PsiElement originalElement = oldOrigin.getOriginalElement();
throw new IllegalStateException("Rewriting origin element: " +
(originalElement != null ? originalElement.getText() : null) + " for stub " + last.toString());
}
last.putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightMemberOrigin(origin));
}
@Override
public void done(boolean generateSmapCopyToAnnotation) {
if (!isPackageClass) {
StubElement pop = parentStack.pop();
assert pop == v.getResult() : "parentStack: got " + pop + ", expected " + v.getResult();
}
super.done(generateSmapCopyToAnnotation);
}
}