backend: introduce 'CodegenFactory' interface with default implementation
creating PackageCodegen and MultifileClassCodegen from an existing JVM back-end.
This commit is contained in:
committed by
Dmitry Petrov
parent
838b639692
commit
e8bacad62b
@@ -193,14 +193,14 @@ public class ClassFileFactory implements OutputFileCollection {
|
||||
public PackageCodegen forPackage(@NotNull FqName fqName, @NotNull Collection<KtFile> files) {
|
||||
assert !isDone : "Already done!";
|
||||
registerPackagePartSourceFiles(files);
|
||||
return new PackageCodegen(state, files, fqName, buildNewPackagePartRegistry(fqName));
|
||||
return state.getCodegenFactory().createPackageCodegen(state, files, fqName, buildNewPackagePartRegistry(fqName));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MultifileClassCodegen forMultifileClass(@NotNull FqName facadeFqName, @NotNull Collection<KtFile> files) {
|
||||
assert !isDone : "Already done!";
|
||||
registerPackagePartSourceFiles(files);
|
||||
return new MultifileClassCodegen(state, files, facadeFqName, buildNewPackagePartRegistry(facadeFqName.parent()));
|
||||
return state.getCodegenFactory().createMultifileClassCodegen(state, files, facadeFqName, buildNewPackagePartRegistry(facadeFqName.parent()));
|
||||
}
|
||||
|
||||
private PackagePartRegistry buildNewPackagePartRegistry(@NotNull FqName packageFqName) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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
|
||||
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
interface CodegenFactory {
|
||||
fun createPackageCodegen(state: GenerationState, files: Collection<KtFile>, fqName: FqName, registry: PackagePartRegistry): PackageCodegen
|
||||
fun createMultifileClassCodegen(state: GenerationState, files: Collection<KtFile>, fqName: FqName, registry: PackagePartRegistry): MultifileClassCodegen
|
||||
}
|
||||
|
||||
object DefaultCodegenFactory : CodegenFactory {
|
||||
override fun createPackageCodegen(state: GenerationState, files: Collection<KtFile>, fqName: FqName, registry: PackagePartRegistry) =
|
||||
PackageCodegenImpl(state, files, fqName, registry)
|
||||
|
||||
override fun createMultifileClassCodegen(state: GenerationState, files: Collection<KtFile>, fqName: FqName, registry: PackagePartRegistry) =
|
||||
MultifileClassCodegenImpl(state, files, fqName, registry)
|
||||
}
|
||||
@@ -51,12 +51,17 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class MultifileClassCodegen(
|
||||
interface MultifileClassCodegen {
|
||||
fun generate(errorHandler: CompilationErrorHandler)
|
||||
fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext<PackageFragmentDescriptor>)
|
||||
}
|
||||
|
||||
class MultifileClassCodegenImpl(
|
||||
private val state: GenerationState,
|
||||
private val files: Collection<KtFile>,
|
||||
private val facadeFqName: FqName,
|
||||
private val packagePartRegistry: PackagePartRegistry
|
||||
) {
|
||||
) : MultifileClassCodegen {
|
||||
private val facadeClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(facadeFqName)
|
||||
|
||||
private val packageFragment = getOnlyPackageFragment(facadeFqName.parent(), files, state.bindingContext)
|
||||
@@ -150,7 +155,7 @@ class MultifileClassCodegen(
|
||||
}
|
||||
}
|
||||
|
||||
fun generate(errorHandler: CompilationErrorHandler) {
|
||||
override fun generate(errorHandler: CompilationErrorHandler) {
|
||||
assert(delegateGenerationTasks.isEmpty()) { "generate() is called twice for facade class $facadeFqName" }
|
||||
|
||||
generateCodeForSourceFiles(errorHandler)
|
||||
@@ -194,7 +199,7 @@ class MultifileClassCodegen(
|
||||
writeKotlinMultifileFacadeAnnotationIfNeeded()
|
||||
}
|
||||
|
||||
fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext<PackageFragmentDescriptor>) {
|
||||
override fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext<PackageFragmentDescriptor>) {
|
||||
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,151 +16,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class PackageCodegen {
|
||||
private final GenerationState state;
|
||||
private final Collection<KtFile> files;
|
||||
private final PackageFragmentDescriptor packageFragment;
|
||||
private final PackagePartRegistry packagePartRegistry;
|
||||
public interface PackageCodegen {
|
||||
void generate(@NotNull CompilationErrorHandler errorHandler);
|
||||
|
||||
public PackageCodegen(
|
||||
@NotNull GenerationState state,
|
||||
@NotNull Collection<KtFile> files,
|
||||
@NotNull FqName packageFqName,
|
||||
@NotNull PackagePartRegistry registry
|
||||
) {
|
||||
this.state = state;
|
||||
this.files = files;
|
||||
this.packageFragment = getOnlyPackageFragment(packageFqName);
|
||||
packagePartRegistry = registry;
|
||||
}
|
||||
void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext);
|
||||
|
||||
public void generate(@NotNull CompilationErrorHandler errorHandler) {
|
||||
for (KtFile file : files) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
try {
|
||||
generateFile(file);
|
||||
state.afterIndependentPart();
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
VirtualFile vFile = file.getVirtualFile();
|
||||
errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
|
||||
DiagnosticUtils.throwIfRunningOnServer(e);
|
||||
if (ApplicationManager.getApplication().isInternal()) {
|
||||
//noinspection CallToPrintStackTrace
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateClassesAndObjectsInFile(@NotNull List<KtClassOrObject> classOrObjects, @NotNull PackageContext packagePartContext) {
|
||||
for (KtClassOrObject classOrObject : CodegenUtilKt.sortTopLevelClassesAndPrepareContextForSealedClasses(classOrObjects, packagePartContext, state)) {
|
||||
generateClassOrObject(classOrObject, packagePartContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFile(@NotNull KtFile file) {
|
||||
JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
|
||||
|
||||
if (fileClassInfo.getWithJvmMultifileClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Type fileClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFileClassFqName());
|
||||
PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, fileClassType, file);
|
||||
|
||||
boolean generatePackagePart = false;
|
||||
|
||||
List<KtClassOrObject> classOrObjects = new ArrayList<KtClassOrObject>();
|
||||
|
||||
for (KtDeclaration declaration : file.getDeclarations()) {
|
||||
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
|
||||
generatePackagePart = true;
|
||||
}
|
||||
else if (declaration instanceof KtClassOrObject) {
|
||||
KtClassOrObject classOrObject = (KtClassOrObject) declaration;
|
||||
if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) {
|
||||
classOrObjects.add(classOrObject);
|
||||
}
|
||||
}
|
||||
else if (declaration instanceof KtScript) {
|
||||
KtScript script = (KtScript) declaration;
|
||||
|
||||
if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) {
|
||||
ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate();
|
||||
}
|
||||
}
|
||||
}
|
||||
generateClassesAndObjectsInFile(classOrObjects, packagePartContext);
|
||||
|
||||
if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return;
|
||||
|
||||
String name = fileClassType.getInternalName();
|
||||
packagePartRegistry.addPart(name.substring(name.lastIndexOf('/') + 1));
|
||||
|
||||
ClassBuilder builder = state.getFactory().newVisitor(JvmDeclarationOriginKt.PackagePart(file, packageFragment), fileClassType, file);
|
||||
|
||||
new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedPackageFqName) {
|
||||
SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
|
||||
for (KtFile file : files) {
|
||||
PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
|
||||
assert fragment != null : "package fragment is null for " + file + "\n" + file.getText();
|
||||
|
||||
assert expectedPackageFqName.equals(fragment.getFqName()) :
|
||||
"expected package fq name: " + expectedPackageFqName + ", actual: " + fragment.getFqName();
|
||||
|
||||
if (!fragments.contains(fragment)) {
|
||||
fragments.add(fragment);
|
||||
}
|
||||
}
|
||||
if (fragments.size() > 1) {
|
||||
throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
|
||||
}
|
||||
|
||||
if (fragments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return fragments.get(0);
|
||||
}
|
||||
|
||||
public void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext) {
|
||||
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null);
|
||||
}
|
||||
|
||||
public Collection<KtFile> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
public PackageFragmentDescriptor getPackageFragment() {
|
||||
return packageFragment;
|
||||
}
|
||||
PackageFragmentDescriptor getPackageFragment();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class PackageCodegenImpl implements PackageCodegen {
|
||||
private final GenerationState state;
|
||||
private final Collection<KtFile> files;
|
||||
private final PackageFragmentDescriptor packageFragment;
|
||||
private final PackagePartRegistry packagePartRegistry;
|
||||
|
||||
public PackageCodegenImpl(
|
||||
@NotNull GenerationState state,
|
||||
@NotNull Collection<KtFile> files,
|
||||
@NotNull FqName packageFqName,
|
||||
@NotNull PackagePartRegistry registry
|
||||
) {
|
||||
this.state = state;
|
||||
this.files = files;
|
||||
this.packageFragment = getOnlyPackageFragment(packageFqName);
|
||||
packagePartRegistry = registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(@NotNull CompilationErrorHandler errorHandler) {
|
||||
for (KtFile file : files) {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
try {
|
||||
generateFile(file);
|
||||
state.afterIndependentPart();
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
VirtualFile vFile = file.getVirtualFile();
|
||||
errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
|
||||
DiagnosticUtils.throwIfRunningOnServer(e);
|
||||
if (ApplicationManager.getApplication().isInternal()) {
|
||||
//noinspection CallToPrintStackTrace
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateClassesAndObjectsInFile(@NotNull List<KtClassOrObject> classOrObjects, @NotNull PackageContext packagePartContext) {
|
||||
for (KtClassOrObject classOrObject : CodegenUtilKt.sortTopLevelClassesAndPrepareContextForSealedClasses(classOrObjects, packagePartContext, state)) {
|
||||
generateClassOrObject(classOrObject, packagePartContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFile(@NotNull KtFile file) {
|
||||
JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
|
||||
|
||||
if (fileClassInfo.getWithJvmMultifileClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Type fileClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFileClassFqName());
|
||||
PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, fileClassType, file);
|
||||
|
||||
boolean generatePackagePart = false;
|
||||
|
||||
List<KtClassOrObject> classOrObjects = new ArrayList<KtClassOrObject>();
|
||||
|
||||
for (KtDeclaration declaration : file.getDeclarations()) {
|
||||
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
|
||||
generatePackagePart = true;
|
||||
}
|
||||
else if (declaration instanceof KtClassOrObject) {
|
||||
KtClassOrObject classOrObject = (KtClassOrObject) declaration;
|
||||
if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) {
|
||||
classOrObjects.add(classOrObject);
|
||||
}
|
||||
}
|
||||
else if (declaration instanceof KtScript) {
|
||||
KtScript script = (KtScript) declaration;
|
||||
|
||||
if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) {
|
||||
ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate();
|
||||
}
|
||||
}
|
||||
}
|
||||
generateClassesAndObjectsInFile(classOrObjects, packagePartContext);
|
||||
|
||||
if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return;
|
||||
|
||||
String name = fileClassType.getInternalName();
|
||||
packagePartRegistry.addPart(name.substring(name.lastIndexOf('/') + 1));
|
||||
|
||||
ClassBuilder builder = state.getFactory().newVisitor(JvmDeclarationOriginKt.PackagePart(file, packageFragment), fileClassType, file);
|
||||
|
||||
new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedPackageFqName) {
|
||||
SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
|
||||
for (KtFile file : files) {
|
||||
PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
|
||||
assert fragment != null : "package fragment is null for " + file + "\n" + file.getText();
|
||||
|
||||
assert expectedPackageFqName.equals(fragment.getFqName()) :
|
||||
"expected package fq name: " + expectedPackageFqName + ", actual: " + fragment.getFqName();
|
||||
|
||||
if (!fragments.contains(fragment)) {
|
||||
fragments.add(fragment);
|
||||
}
|
||||
}
|
||||
if (fragments.size() > 1) {
|
||||
throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
|
||||
}
|
||||
|
||||
if (fragments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return fragments.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateClassOrObject(@NotNull KtClassOrObject classOrObject, @NotNull PackageContext packagePartContext) {
|
||||
MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageFragmentDescriptor getPackageFragment() {
|
||||
return packageFragment;
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@ class GenerationState @JvmOverloads constructor(
|
||||
// TODO: get rid of it with the proper module infrastructure
|
||||
val outDirectory: File? = null,
|
||||
private val onIndependentPartCompilationEnd: GenerationStateEventCallback = GenerationStateEventCallback.DO_NOTHING,
|
||||
val codegenFactory: CodegenFactory = DefaultCodegenFactory,
|
||||
wantsDiagnostics: Boolean = true
|
||||
) {
|
||||
abstract class GenerateClassFilter {
|
||||
|
||||
Reference in New Issue
Block a user