From e8bacad62bb9a81f176286aa3eeb7e771f4e2c25 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 9 Sep 2016 11:20:32 +0300 Subject: [PATCH] backend: introduce 'CodegenFactory' interface with default implementation creating PackageCodegen and MultifileClassCodegen from an existing JVM back-end. --- .../kotlin/codegen/ClassFileFactory.java | 4 +- .../kotlin/codegen/CodegenFactory.kt | 34 ++++ .../kotlin/codegen/MultifileClassCodegen.kt | 13 +- .../kotlin/codegen/PackageCodegen.java | 145 +-------------- .../kotlin/codegen/PackageCodegenImpl.java | 165 ++++++++++++++++++ .../kotlin/codegen/state/GenerationState.kt | 1 + 6 files changed, 217 insertions(+), 145 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java index ff7d5b2cba1..b91c6f1d96d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java @@ -193,14 +193,14 @@ public class ClassFileFactory implements OutputFileCollection { public PackageCodegen forPackage(@NotNull FqName fqName, @NotNull Collection 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 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) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt new file mode 100644 index 00000000000..e3c2c24be4e --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/CodegenFactory.kt @@ -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, fqName: FqName, registry: PackagePartRegistry): PackageCodegen + fun createMultifileClassCodegen(state: GenerationState, files: Collection, fqName: FqName, registry: PackagePartRegistry): MultifileClassCodegen +} + +object DefaultCodegenFactory : CodegenFactory { + override fun createPackageCodegen(state: GenerationState, files: Collection, fqName: FqName, registry: PackagePartRegistry) = + PackageCodegenImpl(state, files, fqName, registry) + + override fun createMultifileClassCodegen(state: GenerationState, files: Collection, fqName: FqName, registry: PackagePartRegistry) = + MultifileClassCodegenImpl(state, files, fqName, registry) +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt index 406a8ba6aa5..66f90cbc491 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MultifileClassCodegen.kt @@ -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) +} + +class MultifileClassCodegenImpl( private val state: GenerationState, private val files: Collection, 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) { + override fun generateClassOrObject(classOrObject: KtClassOrObject, packagePartContext: FieldOwnerContext) { MemberCodegen.genClassOrObject(packagePartContext, classOrObject, state, null) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java index 6233b746253..4e28438275c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java @@ -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 files; - private final PackageFragmentDescriptor packageFragment; - private final PackagePartRegistry packagePartRegistry; +public interface PackageCodegen { + void generate(@NotNull CompilationErrorHandler errorHandler); - public PackageCodegen( - @NotNull GenerationState state, - @NotNull Collection 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 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 classOrObjects = new ArrayList(); - - 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 fragments = new SmartList(); - 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 getFiles() { - return files; - } - - public PackageFragmentDescriptor getPackageFragment() { - return packageFragment; - } + PackageFragmentDescriptor getPackageFragment(); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java new file mode 100644 index 00000000000..f643bba2892 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegenImpl.java @@ -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 files; + private final PackageFragmentDescriptor packageFragment; + private final PackagePartRegistry packagePartRegistry; + + public PackageCodegenImpl( + @NotNull GenerationState state, + @NotNull Collection 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 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 classOrObjects = new ArrayList(); + + 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 fragments = new SmartList(); + 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; + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 76100b12802..8ec27b30031 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -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 {