From 4e491875496dd6dca170bad452a01393697d1fb7 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 11 Mar 2016 22:55:01 +0300 Subject: [PATCH] Dump declaration mapping during codegen process. --- .../kotlin/codegen/state/GenerationState.kt | 41 ++--- .../state/SignatureDumpingBuilderFactory.kt | 146 ++++++++++++++++++ .../compiler/KotlinToJVMBytecodeCompiler.kt | 3 +- 3 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt 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 0c38b1373f9..694fc6b10a4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -53,7 +53,7 @@ class GenerationState @JvmOverloads constructor( val files: List, disableCallAssertions: Boolean = true, disableParamAssertions: Boolean = true, - val generateDeclaredClassFilter: GenerationState.GenerateClassFilter = GenerationState.GenerateClassFilter.GENERATE_ALL, + val generateDeclaredClassFilter: GenerateClassFilter = GenerationState.GenerateClassFilter.GENERATE_ALL, disableInline: Boolean = false, disableOptimization: Boolean = false, val useTypeTableInSerializer: Boolean = false, @@ -68,7 +68,8 @@ class GenerationState @JvmOverloads constructor( val outDirectory: File? = null, val incrementalCompilationComponents: IncrementalCompilationComponents? = null, val progress: Progress = Progress.DEAF, - private val onIndependentPartCompilationEnd: GenerationStateEventCallback = GenerationStateEventCallback.DO_NOTHING + private val onIndependentPartCompilationEnd: GenerationStateEventCallback = GenerationStateEventCallback.DO_NOTHING, + dumpBinarySignatureMappingTo: File? = null ) { abstract class GenerateClassFilter { abstract fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean @@ -122,7 +123,7 @@ class GenerationState @JvmOverloads constructor( val reflectionTypes: ReflectionTypes = ReflectionTypes(module) val jvmRuntimeTypes: JvmRuntimeTypes = JvmRuntimeTypes() val factory: ClassFileFactory - private val duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics + private lateinit var duplicateSignatureFactory: BuilderFactoryForDuplicateSignatureDiagnostics val replSpecific = ForRepl() @@ -147,22 +148,20 @@ class GenerationState @JvmOverloads constructor( val rootContext: CodegenContext<*> = RootContext(this) init { - val optimizationClassBuilderFactory = OptimizationClassBuilderFactory(builderFactory, disableOptimization) - duplicateSignatureFactory = BuilderFactoryForDuplicateSignatureDiagnostics( - optimizationClassBuilderFactory, this.bindingContext, diagnostics, fileClassesProvider, - getIncrementalCacheForThisTarget(), - this.moduleName) + this.interceptedBuilderFactory = builderFactory + .wrapWith( + { OptimizationClassBuilderFactory(it, disableOptimization) }, + { BuilderFactoryForDuplicateSignatureDiagnostics( + it, this.bindingContext, diagnostics, fileClassesProvider, + getIncrementalCacheForThisTarget(), + this.moduleName).apply { duplicateSignatureFactory = this } }, + { BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) }, + { dumpBinarySignatureMappingTo?.let { destination -> SignatureDumpingBuilderFactory(it, destination) } ?: it } + ) + .wrapWith(ClassBuilderInterceptorExtension.getInstances(project)) { builderFactory, extension -> + extension.interceptClassBuilderFactory(builderFactory, bindingContext, diagnostics) + } - var interceptedBuilderFactory: ClassBuilderFactory - = BuilderFactoryForDuplicateClassNameDiagnostics(duplicateSignatureFactory, diagnostics) - - val interceptExtensions = ClassBuilderInterceptorExtension.getInstances(project) - - for (extension in interceptExtensions) { - interceptedBuilderFactory = extension.interceptClassBuilderFactory(interceptedBuilderFactory, bindingContext, diagnostics) - } - - this.interceptedBuilderFactory = interceptedBuilderFactory this.factory = ClassFileFactory(this, interceptedBuilderFactory) } @@ -214,3 +213,9 @@ fun GenerationStateEventCallback(block: (GenerationState) -> Unit): GenerationSt object : GenerationStateEventCallback { override fun invoke(s: GenerationState) = block(s) } + +private fun ClassBuilderFactory.wrapWith(vararg wrappers: (ClassBuilderFactory) -> ClassBuilderFactory): ClassBuilderFactory = + wrappers.fold(this) { builderFactory, wrapper -> wrapper(builderFactory) } + +private inline fun ClassBuilderFactory.wrapWith(elements: Iterable, wrapper: (ClassBuilderFactory, T) -> ClassBuilderFactory): ClassBuilderFactory = + elements.fold(this, wrapper) \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt new file mode 100644 index 00000000000..c29b49ac4e1 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt @@ -0,0 +1,146 @@ +/* + * 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.state + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.codegen.ClassBuilder +import org.jetbrains.kotlin.codegen.ClassBuilderFactory +import org.jetbrains.kotlin.codegen.DelegatingClassBuilder +import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.renderer.DescriptorRendererModifier +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind +import org.jetbrains.kotlin.resolve.jvm.diagnostics.RawSignature +import org.jetbrains.org.objectweb.asm.FieldVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import java.io.BufferedWriter +import java.io.File + + +class SignatureDumpingBuilderFactory( + builderFactory: ClassBuilderFactory, + val destination: File +) : DelegatingClassBuilderFactory(builderFactory) { + + companion object { + val MEMBER_RENDERER = DescriptorRenderer.withOptions { + withDefinedIn = false + modifiers -= DescriptorRendererModifier.VISIBILITY + } + val TYPE_RENDERER = DescriptorRenderer.withOptions { + modifiers -= DescriptorRendererModifier.VISIBILITY + } + } + + private val outputStream: BufferedWriter by lazy { + // TODO: Replace with LOG.info and make log output go to MessageCollector + println("[INFO] Dumping signatures to $destination") + destination.parentFile?.mkdirs() + destination.bufferedWriter().apply { append("[\n") } + } + private var firstClassWritten: Boolean = false + + override fun close() { + outputStream.append("\n]") + outputStream.close() + super.close() + } + + override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder { + return SignatureDumpingClassBuilder(origin, delegate.newClassBuilder(origin)) + } + + + private inner class SignatureDumpingClassBuilder(val origin: JvmDeclarationOrigin, val _delegate: ClassBuilder) : DelegatingClassBuilder() { + override fun getDelegate() = _delegate + + private val signatures = mutableListOf>() + private lateinit var javaClassName: String + + override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array) { + javaClassName = name + + super.defineClass(origin, version, access, name, signature, superName, interfaces) + } + + override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { + signatures += RawSignature(name, desc, MemberKind.METHOD) to origin.descriptor + return super.newMethod(origin, access, name, desc, signature, exceptions) + } + + override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor { + signatures += RawSignature(name, desc, MemberKind.FIELD) to origin.descriptor + return super.newField(origin, access, name, desc, signature, value) + } + + override fun done() { + if (firstClassWritten) outputStream.append(",\n") else firstClassWritten = true + outputStream.append("\t{\n") + origin.descriptor?.let { + outputStream.append("\t\t").appendNameValue("declaration", TYPE_RENDERER.render(it)).append(",\n") + (it as? DeclarationDescriptorWithVisibility)?.visibility?.let { + outputStream.append("\t\t").appendNameValue("visibility", it.displayName).append(",\n") + } + } + outputStream.append("\t\t").appendNameValue("class", javaClassName).append(", \n") + + outputStream.append("\t\t").appendQuoted("members").append(": [\n") + signatures.joinTo(outputStream, ",\n") { buildString { + val (signature, descriptor) = it + append("\t\t\t{") + descriptor?.let { + (it as? DeclarationDescriptorWithVisibility)?.visibility?.let { + appendNameValue("visibility", it.displayName).append(",\t") + } + appendNameValue("declaration", MEMBER_RENDERER.render(it)).append(", ") + + } + appendNameValue("name", signature.name).append(", ") + appendNameValue("desc", signature.desc).append("}") + }} + outputStream.append("\n\t\t]\n\t}") + + super.done() + } + } +} + +private fun Appendable.appendQuoted(value: String?): Appendable = value?.let { append('"').append(jsonEscape(it)).append('"') } ?: append("null") +private fun Appendable.appendNameValue(name: String, value: String?): Appendable = appendQuoted(name).append(": ").appendQuoted(value) + +private fun jsonEscape(value: String): String = buildString { + for (index in 0..value.length - 1) { + val ch = value[index] + when (ch) { + '\b' -> append("\\b") + '\t' -> append("\\t") + '\n' -> append("\\n") + '\r' -> append("\\r") + '\"' -> append("\\\"") + '\\' -> append("\\\\") + else -> if (ch.toInt() < 32) { + append("\\u" + Integer.toHexString(ch.toInt()).padStart(4, '0')) + } + else { + append(ch) + } + } + } +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt index a659f74c828..c83ab6d767b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt @@ -398,7 +398,8 @@ object KotlinToJVMBytecodeCompiler { moduleName, outputDirectory, incrementalCompilationComponents, - onIndependentPartCompilationEnd = onIndependentPartCompilationEnd) + onIndependentPartCompilationEnd = onIndependentPartCompilationEnd, + dumpBinarySignatureMappingTo = outputDirectory?.resolve("binary-signatures.json") ?: createTempFile("binary-signatures", ".json")) ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() val generationStart = PerformanceCounter.currentTime()