202: Fix compatibility with 202 IDEA branch (code)
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.backend.common.bridges.findImplementationFromInterface
|
||||
import org.jetbrains.kotlin.backend.common.bridges.firstSuperMethodFromKotlin
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
|
||||
class InterfaceImplBodyCodegen(
|
||||
aClass: KtPureClassOrObject,
|
||||
context: ClassContext,
|
||||
v: ClassBuilder,
|
||||
state: GenerationState,
|
||||
parentCodegen: MemberCodegen<*>?
|
||||
) : ClassBodyCodegen(aClass, context, InterfaceImplBodyCodegen.InterfaceImplClassBuilder(v), state, parentCodegen) {
|
||||
private var isAnythingGenerated: Boolean = false
|
||||
get() = (v as InterfaceImplClassBuilder).isAnythingGenerated
|
||||
|
||||
private val defaultImplType = typeMapper.mapDefaultImpls(descriptor)
|
||||
|
||||
override fun generateDeclaration() {
|
||||
val codegenFlags = ACC_PUBLIC or ACC_FINAL or ACC_SUPER
|
||||
val flags = if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) codegenFlags or ACC_STATIC else codegenFlags
|
||||
v.defineClass(
|
||||
myClass.psiOrParent, state.classFileVersion, flags,
|
||||
defaultImplType.internalName,
|
||||
null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY
|
||||
)
|
||||
v.visitSource(myClass.containingKtFile.name, null)
|
||||
}
|
||||
|
||||
override fun classForInnerClassRecord(): ClassDescriptor? {
|
||||
if (!isAnythingGenerated) return null
|
||||
return InnerClassConsumer.classForInnerClassRecord(descriptor, true)
|
||||
}
|
||||
|
||||
override fun generateSyntheticPartsAfterBody() {
|
||||
for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) {
|
||||
if (memberDescriptor !is CallableMemberDescriptor) continue
|
||||
|
||||
if (memberDescriptor.kind.isReal) continue
|
||||
if (memberDescriptor.visibility == Visibilities.INVISIBLE_FAKE) continue
|
||||
if (memberDescriptor.modality == Modality.ABSTRACT) continue
|
||||
|
||||
val implementation = findImplementationFromInterface(memberDescriptor) ?: continue
|
||||
|
||||
// If implementation is a default interface method (JVM 8 only)
|
||||
if (implementation.isDefinitelyNotDefaultImplsMethod()) continue
|
||||
|
||||
if (memberDescriptor is FunctionDescriptor) {
|
||||
generateDelegationToSuperDefaultImpls(memberDescriptor, implementation as FunctionDescriptor)
|
||||
}
|
||||
else if (memberDescriptor is PropertyDescriptor) {
|
||||
implementation as PropertyDescriptor
|
||||
val getter = memberDescriptor.getter
|
||||
val implGetter = implementation.getter
|
||||
if (getter != null && implGetter != null) {
|
||||
generateDelegationToSuperDefaultImpls(getter, implGetter)
|
||||
}
|
||||
val setter = memberDescriptor.setter
|
||||
val implSetter = implementation.setter
|
||||
if (setter != null && implSetter != null) {
|
||||
generateDelegationToSuperDefaultImpls(setter, implSetter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generateSyntheticAccessors()
|
||||
}
|
||||
|
||||
private fun generateDelegationToSuperDefaultImpls(descriptor: FunctionDescriptor, implementation: FunctionDescriptor) {
|
||||
val delegateTo = firstSuperMethodFromKotlin(descriptor, implementation) as FunctionDescriptor? ?: return
|
||||
|
||||
// We can't call super methods from Java 1.8 interfaces because that requires INVOKESPECIAL which is forbidden from TImpl class
|
||||
if (delegateTo is JavaMethodDescriptor) return
|
||||
|
||||
functionCodegen.generateMethod(
|
||||
JvmDeclarationOrigin(
|
||||
JvmDeclarationOriginKind.DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL,
|
||||
DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor
|
||||
),
|
||||
descriptor,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
val iv = codegen.v
|
||||
|
||||
val method = typeMapper.mapToCallableMethod(delegateTo, true)
|
||||
val myParameters = signature.valueParameters
|
||||
val calleeParameters = method.getValueParameters()
|
||||
|
||||
if (myParameters.size != calleeParameters.size) {
|
||||
throw AssertionError(
|
||||
"Method from super interface has a different signature.\n" +
|
||||
"This method:\n%s\n%s\n%s\nSuper method:\n%s\n%s\n%s".format(
|
||||
descriptor, signature, myParameters, delegateTo, method, calleeParameters
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
var k = 0
|
||||
val it = calleeParameters.iterator()
|
||||
for (parameter in myParameters) {
|
||||
val type = parameter.asmType
|
||||
StackValue.local(k, type).put(it.next().asmType, iv)
|
||||
k += type.size
|
||||
}
|
||||
|
||||
method.genInvokeInstruction(iv)
|
||||
StackValue.coerce(method.returnType, signature.returnType, iv)
|
||||
iv.areturn(signature.returnType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun generateKotlinMetadataAnnotation() {
|
||||
(v as InterfaceImplClassBuilder).stopCounting()
|
||||
|
||||
writeSyntheticClassMetadata(v, state)
|
||||
}
|
||||
|
||||
override fun done() {
|
||||
super.done()
|
||||
if (!isAnythingGenerated) {
|
||||
state.factory.removeClasses(setOf(defaultImplType.internalName))
|
||||
}
|
||||
}
|
||||
|
||||
private class InterfaceImplClassBuilder(private val v: ClassBuilder) : DelegatingClassBuilder() {
|
||||
private var shouldCount: Boolean = true
|
||||
var isAnythingGenerated: Boolean = false
|
||||
private set
|
||||
|
||||
fun stopCounting() {
|
||||
shouldCount = false
|
||||
}
|
||||
|
||||
override fun getDelegate() = v
|
||||
|
||||
override fun newMethod(
|
||||
origin: JvmDeclarationOrigin,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String?>?
|
||||
): MethodVisitor {
|
||||
if (shouldCount) {
|
||||
isAnythingGenerated = true
|
||||
}
|
||||
return super.newMethod(origin, access, name, desc, signature, exceptions)
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateSyntheticPartsBeforeBody() {
|
||||
generatePropertyMetadataArrayFieldIfNeeded(defaultImplType)
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.codegen
|
||||
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.tree.*
|
||||
|
||||
class OriginCollectingClassBuilderFactory(private val builderMode: ClassBuilderMode) : ClassBuilderFactory {
|
||||
val compiledClasses = mutableListOf<ClassNode>()
|
||||
val origins = mutableMapOf<Any, JvmDeclarationOrigin>()
|
||||
|
||||
override fun getClassBuilderMode(): ClassBuilderMode = builderMode
|
||||
|
||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete {
|
||||
val classNode = ClassNode()
|
||||
compiledClasses += classNode
|
||||
origins[classNode] = origin
|
||||
return OriginCollectingClassBuilder(classNode)
|
||||
}
|
||||
|
||||
private inner class OriginCollectingClassBuilder(val classNode: ClassNode) : AbstractClassBuilder.Concrete(classNode) {
|
||||
override fun newField(
|
||||
origin: JvmDeclarationOrigin,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
value: Any?
|
||||
): FieldVisitor {
|
||||
val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode
|
||||
origins[fieldNode] = origin
|
||||
return fieldNode
|
||||
}
|
||||
|
||||
override fun newMethod(
|
||||
origin: JvmDeclarationOrigin,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String?>?
|
||||
): MethodVisitor {
|
||||
val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode
|
||||
origins[methodNode] = origin
|
||||
|
||||
// ASM doesn't read information about local variables for the `abstract` methods so we need to get it manually
|
||||
if ((access and Opcodes.ACC_ABSTRACT) != 0 && methodNode.localVariables == null) {
|
||||
methodNode.localVariables = mutableListOf<LocalVariableNode>()
|
||||
}
|
||||
|
||||
return methodNode
|
||||
}
|
||||
}
|
||||
|
||||
override fun asBytes(builder: ClassBuilder): ByteArray {
|
||||
val classWriter = ClassWriter(0)
|
||||
(builder as OriginCollectingClassBuilder).classNode.accept(classWriter)
|
||||
return classWriter.toByteArray()
|
||||
}
|
||||
|
||||
override fun asText(builder: ClassBuilder) = throw UnsupportedOperationException()
|
||||
|
||||
override fun close() {}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.psi.PsiElement
|
||||
import com.intellij.util.containers.LinkedMultiMap
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData
|
||||
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
|
||||
|
||||
abstract class SignatureCollectingClassBuilderFactory(
|
||||
delegate: ClassBuilderFactory, val shouldGenerate: (JvmDeclarationOrigin) -> Boolean
|
||||
) : DelegatingClassBuilderFactory(delegate) {
|
||||
|
||||
protected abstract fun handleClashingSignatures(data: ConflictingJvmDeclarationsData)
|
||||
protected abstract fun onClassDone(classOrigin: JvmDeclarationOrigin,
|
||||
classInternalName: String,
|
||||
signatures: MultiMap<RawSignature, JvmDeclarationOrigin>)
|
||||
|
||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder {
|
||||
return SignatureCollectingClassBuilder(origin, delegate.newClassBuilder(origin))
|
||||
}
|
||||
|
||||
private inner class SignatureCollectingClassBuilder(
|
||||
private val classCreatedFor: JvmDeclarationOrigin,
|
||||
internal val _delegate: ClassBuilder
|
||||
) : DelegatingClassBuilder() {
|
||||
|
||||
override fun getDelegate() = _delegate
|
||||
|
||||
private lateinit var classInternalName: String
|
||||
|
||||
private val signatures = LinkedMultiMap<RawSignature, JvmDeclarationOrigin>()
|
||||
|
||||
override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<out String>) {
|
||||
classInternalName = name
|
||||
super.defineClass(origin, version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor {
|
||||
signatures.putValue(RawSignature(name, desc, MemberKind.FIELD), origin)
|
||||
if (!shouldGenerate(origin)) {
|
||||
return AbstractClassBuilder.EMPTY_FIELD_VISITOR
|
||||
}
|
||||
return super.newField(origin, access, name, desc, signature, value)
|
||||
}
|
||||
|
||||
override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String?>?): MethodVisitor {
|
||||
signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin)
|
||||
if (!shouldGenerate(origin)) {
|
||||
return AbstractClassBuilder.EMPTY_METHOD_VISITOR
|
||||
}
|
||||
return super.newMethod(origin, access, name, desc, signature, exceptions)
|
||||
}
|
||||
|
||||
override fun done() {
|
||||
for ((signature, elementsAndDescriptors) in signatures.entrySet()) {
|
||||
if (elementsAndDescriptors.size == 1) continue // no clash
|
||||
handleClashingSignatures(ConflictingJvmDeclarationsData(
|
||||
classInternalName,
|
||||
classCreatedFor,
|
||||
signature,
|
||||
elementsAndDescriptors
|
||||
))
|
||||
}
|
||||
onClassDone(classCreatedFor, classInternalName, signatures)
|
||||
super.done()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+1236
File diff suppressed because it is too large
Load Diff
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. 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.codegen.optimization.common
|
||||
|
||||
import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME
|
||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.IincInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||
import java.util.*
|
||||
|
||||
|
||||
class VariableLivenessFrame(val maxLocals: Int) : VarFrame<VariableLivenessFrame> {
|
||||
private val bitSet = BitSet(maxLocals)
|
||||
|
||||
override fun mergeFrom(other: VariableLivenessFrame) {
|
||||
bitSet.or(other.bitSet)
|
||||
}
|
||||
|
||||
fun markAlive(varIndex: Int) {
|
||||
bitSet.set(varIndex, true)
|
||||
}
|
||||
|
||||
fun markAllAlive(bitSet: BitSet) {
|
||||
this.bitSet.or(bitSet)
|
||||
}
|
||||
|
||||
fun markDead(varIndex: Int) {
|
||||
bitSet.set(varIndex, false)
|
||||
}
|
||||
|
||||
fun isAlive(varIndex: Int): Boolean = bitSet.get(varIndex)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is VariableLivenessFrame) return false
|
||||
return bitSet == other.bitSet
|
||||
}
|
||||
|
||||
override fun hashCode() = bitSet.hashCode()
|
||||
}
|
||||
|
||||
fun analyzeLiveness(node: MethodNode): List<VariableLivenessFrame> {
|
||||
val typeAnnotatedFrames = MethodTransformer.analyze("fake", node, OptimizationBasicInterpreter())
|
||||
val visibleByDebuggerVariables = analyzeVisibleByDebuggerVariables(node, typeAnnotatedFrames)
|
||||
return analyze(node, object : BackwardAnalysisInterpreter<VariableLivenessFrame> {
|
||||
override fun newFrame(maxLocals: Int) = VariableLivenessFrame(maxLocals)
|
||||
override fun def(frame: VariableLivenessFrame, insn: AbstractInsnNode) = defVar(frame, insn)
|
||||
override fun use(frame: VariableLivenessFrame, insn: AbstractInsnNode) =
|
||||
useVar(frame, insn, node, visibleByDebuggerVariables[node.instructions.indexOf(insn)])
|
||||
})
|
||||
}
|
||||
|
||||
private fun analyzeVisibleByDebuggerVariables(
|
||||
node: MethodNode,
|
||||
typeAnnotatedFrames: Array<Frame<BasicValue>>
|
||||
): Array<BitSet> {
|
||||
val res = Array(node.instructions.size()) { BitSet(node.maxLocals) }
|
||||
for (local in node.localVariables) {
|
||||
if (local.name.isInvisibleDebuggerVariable()) continue
|
||||
for (index in node.instructions.indexOf(local.start) until node.instructions.indexOf(local.end)) {
|
||||
if (Type.getType(local.desc).sort == typeAnnotatedFrames[index]?.getLocal(local.index)?.type?.sort) {
|
||||
res[index].set(local.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
private fun defVar(frame: VariableLivenessFrame, insn: AbstractInsnNode) {
|
||||
if (insn is VarInsnNode && insn.isStoreOperation()) {
|
||||
frame.markDead(insn.`var`)
|
||||
}
|
||||
}
|
||||
|
||||
private fun useVar(
|
||||
frame: VariableLivenessFrame,
|
||||
insn: AbstractInsnNode,
|
||||
node: MethodNode,
|
||||
visibleByDebuggerVariables: BitSet
|
||||
) {
|
||||
frame.markAllAlive(visibleByDebuggerVariables)
|
||||
|
||||
if (insn is VarInsnNode && insn.isLoadOperation()) {
|
||||
frame.markAlive(insn.`var`)
|
||||
} else if (insn is IincInsnNode) {
|
||||
frame.markAlive(insn.`var`)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.isInvisibleDebuggerVariable(): Boolean =
|
||||
startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) ||
|
||||
startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) ||
|
||||
this == SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
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 {
|
||||
withSourceFileForTopLevel = false
|
||||
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]\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<Pair<RawSignature, DeclarationDescriptor?>>()
|
||||
private lateinit var javaClassName: String
|
||||
|
||||
override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<out String>) {
|
||||
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<out String?>?): MethodVisitor {
|
||||
signatures += RawSignature(name, desc, MemberKind.METHOD) to origin.descriptor?.let {
|
||||
if (it is CallableDescriptor) it.unwrapInitialDescriptorForSuspendFunction() else it
|
||||
}
|
||||
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.internalDisplayName).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.internalDisplayName).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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user