blob: f8bcaf12c0eac0e0e1e6be53d316a34dfdb441ba [file] [log] [blame]
/*
* Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.atomicfu.transformer
import kotlinx.metadata.*
import kotlinx.metadata.jvm.*
import org.objectweb.asm.tree.*
const val KOTLIN_METADATA_DESC = "Lkotlin/Metadata;"
class MetadataTransformer(
removeFields: Set<FieldId>,
removeMethods: Set<MethodId>
) {
private val removeFieldSignatures: List<JvmFieldSignature> =
removeFields.map { JvmFieldSignature(it.name, it.desc) }
private val removeMethodSignatures: List<JvmMethodSignature> =
removeMethods.map { JvmMethodSignature(it.name, it.desc) }
private var transformed = false
@Suppress("UNCHECKED_CAST")
fun transformMetadata(metadataAnnotation: AnnotationNode): Boolean {
val map = metadataAnnotation.asMap()
val hdr = KotlinClassHeader(
kind = map["k"] as Int?,
metadataVersion = (map["mv"] as? List<Int>)?.toIntArray(),
bytecodeVersion = (map["bv"] as? List<Int>)?.toIntArray(),
data1 = (map["d1"] as? List<String>)?.toTypedArray(),
data2 = (map["d2"] as? List<String>)?.toTypedArray(),
extraString = map["xs"] as String?,
packageName = map["pn"] as String?,
extraInt = map["xi"] as Int?
)
val result = when (val metadata = KotlinClassMetadata.read(hdr)) {
is KotlinClassMetadata.Class -> {
val w = KotlinClassMetadata.Class.Writer()
metadata.accept(ClassFilter(w))
w.write(hdr.metadataVersion, hdr.extraInt)
}
is KotlinClassMetadata.FileFacade -> {
val w = KotlinClassMetadata.FileFacade.Writer()
metadata.accept(PackageFilter(w))
w.write(hdr.metadataVersion, hdr.extraInt)
}
is KotlinClassMetadata.MultiFileClassPart -> {
val w = KotlinClassMetadata.MultiFileClassPart.Writer()
metadata.accept(PackageFilter(w))
w.write(metadata.facadeClassName, hdr.metadataVersion, hdr.extraInt)
}
else -> return false // not transformed
}
if (!transformed) return false
result.apply {
with (metadataAnnotation) {
// read resulting header & update annotation data
setKey("d1", header.data1.toList())
setKey("d2", header.data2.toList())
}
}
return true // transformed
}
private inner class ClassFilter(v: KmClassVisitor?) : KmClassVisitor(v) {
private val supertypes = mutableListOf<KmType>()
override fun visitSupertype(flags: Flags): KmTypeVisitor? =
KmType(flags).also { supertypes += it }
override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? =
PropertyFilter(KmProperty(flags, name, getterFlags, setterFlags), super.visitProperty(flags, name, getterFlags, setterFlags)!!)
override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor =
FunctionFilter(KmFunction(flags, name), super.visitFunction(flags, name)!!)
override fun visitEnd() {
// Skip supertype if it is SynchronizedObject (it is an alias to Any)
supertypes.forEach { type ->
if (type.abbreviatedType?.classifier == SynchronizedObjectAlias) {
transformed = true
} else
type.accept(super.visitSupertype(type.flags)!!)
}
super.visitEnd()
}
}
private inner class PackageFilter(v: KmPackageVisitor?) : KmPackageVisitor(v) {
override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? =
PropertyFilter(KmProperty(flags, name, getterFlags, setterFlags), super.visitProperty(flags, name, getterFlags, setterFlags)!!)
override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor =
FunctionFilter(KmFunction(flags, name), super.visitFunction(flags, name)!!)
}
private class PropertyExtensionNode : JvmPropertyExtensionVisitor() {
private var jvmFlags: Flags? = null
var fieldSignature: JvmFieldSignature? = null
private var getterSignature: JvmMethodSignature? = null
private var setterSignature: JvmMethodSignature? = null
private var syntheticMethodForAnnotationsDesc: JvmMethodSignature? = null
override fun visit(
jvmFlags: Flags,
fieldSignature: JvmFieldSignature?,
getterSignature: JvmMethodSignature?,
setterSignature: JvmMethodSignature?
) {
check(this.jvmFlags == null)
this.jvmFlags = jvmFlags
this.fieldSignature = fieldSignature
this.getterSignature = getterSignature
this.setterSignature = setterSignature
}
override fun visitSyntheticMethodForAnnotations(signature: JvmMethodSignature?) {
check(syntheticMethodForAnnotationsDesc == null)
this.syntheticMethodForAnnotationsDesc = signature
}
fun accept(v : JvmPropertyExtensionVisitor) {
if (jvmFlags != null) {
v.visit(jvmFlags!!, fieldSignature, getterSignature, setterSignature)
}
syntheticMethodForAnnotationsDesc?.let { v.visitSyntheticMethodForAnnotations(it) }
v.visitEnd()
}
}
private inner class PropertyFilter(
private val delegate: KmProperty,
private val v: KmPropertyVisitor
) : KmPropertyVisitor(delegate) {
private var extension: PropertyExtensionNode? = null
override fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor? {
check(type == JvmPropertyExtensionVisitor.TYPE)
check(extension == null)
return PropertyExtensionNode().also { extension = it }
}
override fun visitEnd() {
if (extension?.fieldSignature in removeFieldSignatures) {
// remove this function
transformed = true
return
}
delegate.receiverParameterType?.fixType { delegate.receiverParameterType = it }
delegate.returnType.fixType { delegate.returnType = it }
// keeping this property
extension?.accept(delegate.visitExtensions(JvmPropertyExtensionVisitor.TYPE) as JvmPropertyExtensionVisitor)
delegate.accept(v)
}
}
private class FunctionExtensionNode : JvmFunctionExtensionVisitor() {
var signature: JvmMethodSignature? = null
private var originalInternalName: String? = null
override fun visit(signature: JvmMethodSignature?) {
check(this.signature == null)
this.signature = signature
}
override fun visitLambdaClassOriginName(internalName: String) {
check(originalInternalName == null)
originalInternalName = internalName
}
fun accept(v : JvmFunctionExtensionVisitor) {
signature?.let { v.visit(it) }
originalInternalName?.let { v.visitLambdaClassOriginName(it) }
v.visitEnd()
}
}
private inner class FunctionFilter(
private val delegate: KmFunction,
private val v: KmFunctionVisitor
) : KmFunctionVisitor(delegate) {
private var extension: FunctionExtensionNode? = null
override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? {
check(type == JvmFunctionExtensionVisitor.TYPE)
check(extension == null)
return FunctionExtensionNode().also { extension = it }
}
override fun visitEnd() {
if (extension?.signature in removeMethodSignatures) {
// remove this function
transformed = true
return
}
// keeping this function
extension?.accept(delegate.visitExtensions(JvmFunctionExtensionVisitor.TYPE) as JvmFunctionExtensionVisitor)
delegate.accept(v)
}
}
private fun KmType.fixType(update: (KmType) -> Unit) {
if (this.abbreviatedType?.classifier == ReentrantLockAlias) {
update(ReentrantLockType)
transformed = true
}
}
}
private val SynchronizedObjectAlias = KmClassifier.TypeAlias("kotlinx/atomicfu/locks/SynchronizedObject")
private val ReentrantLockAlias = KmClassifier.TypeAlias("kotlinx/atomicfu/locks/ReentrantLock")
private val ReentrantLockType = KmType(0).apply {
classifier = KmClassifier.Class("java/util/concurrent/locks/ReentrantLock")
}
@Suppress("UNCHECKED_CAST")
private fun AnnotationNode.asMap(): Map<String, Any?> {
val result = HashMap<String, Any?>()
for (i in 0 until values.size step 2) {
result.put(values[i] as String, values[i + 1])
}
return result
}
private fun AnnotationNode.setKey(key: String, value: Any?) {
for (i in 0 until values.size step 2) {
if (values[i] == key) {
values[i + 1] = value
return
}
}
error("Annotation key '$key' is not found")
}