diff --git a/core/src/main/kotlin/sift/core/api/Context.kt b/core/src/main/kotlin/sift/core/api/Context.kt index e229115..0681239 100644 --- a/core/src/main/kotlin/sift/core/api/Context.kt +++ b/core/src/main/kotlin/sift/core/api/Context.kt @@ -8,9 +8,6 @@ import sift.core.SynthesisTemplate import sift.core.Throw.entityTypeAlreadyBoundToElementType import sift.core.api.MeasurementScope.Template import sift.core.asm.classNode -import sift.core.asm.signature.ArgType -import sift.core.asm.signature.FormalTypeParameter -import sift.core.asm.signature.TypeParameter import sift.core.asm.signature.TypeSignature import sift.core.dsl.Type import sift.core.element.* @@ -140,8 +137,28 @@ internal data class Context( } fun fieldAccessBy(mn: MethodNode): Iterable { + // update field owner if it is inherited + fun ClassNode.inheritFieldNode(fn: FieldNode, implemented: Set): FieldNode { + val fieldOwner = fn.owner.type + return when { + inheritedFields == null -> fn + fieldOwner == type -> fn + fieldOwner !in implemented -> fn + else -> inheritedFields!!.first { it.name == fn.name } + } + } + return methodFieldAccessCache.getOrPut(mn) { + // all interfaces/classes implemented by the method's class + val implemented = inheritance[mn.owner.type.rawType]!! // fixme: when generic types are implemented + .walk() + .drop(1) // self + .map(Tree::value) + .map(TypeClassNode::type) + .toSet() + fieldAccessBy(methodsInvokedBy(mn), classByType) + .map { fn -> mn.owner.inheritFieldNode(fn, implemented) } } } @@ -395,73 +412,3 @@ enum class MeasurementScope(val id: String) { Exception("exception-scope"), // used as marker when an exception is thrown FromContext("") } - -internal data class TypeClassNode( - val type: Type, - val cn: ClassNode?, - val isInterface: Boolean, - var generics: List? = null -) { - constructor(type: Type, cn: ClassNode) - : this(type, cn, cn.isInterface) - override fun toString(): String = type.simpleName -} - -internal fun Tree.resolveGenerics() { - // only need to resolve generics once - if (value.generics != null) - return - - fun update(node: Tree) { - val propagatedGenerics = node.parent?.value?.generics ?: listOf() - var genericTypes = node.genericTypes() - if (propagatedGenerics.isNotEmpty() && genericTypes.isNotEmpty()) { - val lookup = propagatedGenerics.associateBy { Type.from(it.name) } - genericTypes = genericTypes.mapValues { (_, v) -> lookup[v.bound]?.let { v.copy(bound = it.bound) } ?: v } - } - - node.value.generics = genericTypes.map { (_, v) -> v } - } - - walk().forEach(::update) -} - - - -// with resolved generic types methods -internal fun Tree.methods(): List { - val cn = value.cn ?: return listOf() - val ftps = value.generics!!.associateBy(TypeParameter::name) - return cn.methods.map { mn -> mn.reify(ftps) } -} - -internal fun Tree.fields(): List { - val cn = value.cn ?: return listOf() - val ftps = value.generics!!.associateBy(TypeParameter::name) - return cn.fields.map { fn -> fn.reify(ftps) } -} - -//private val Tree.genericTypes: Map -private fun Tree.genericTypes(): Map { - val signature = value.cn?.signature ?: return mapOf() - - val innerTypes = value.type.innerTypes.takeIf(List::isNotEmpty) - ?: signature.formalParameters - .map(FormalTypeParameter::name) - .map(Type::from) - .takeIf(List::isNotEmpty) // fixme: when only some type parameters are specified - ?: return mapOf() - - - val ftps = signature.formalParameters - .mapIndexed { i, ftp -> TypeParameter(ftp.name, innerTypes[i], ftp.extends.map { it.toType() }) } - .associateBy(TypeParameter::name) - - // todo: fix or can we ignore interfaces? - val interfaces = signature.implements - .flatMap(TypeSignature::args) - .mapNotNull { it.argType as? ArgType.Var } - - return ftps -} - diff --git a/core/src/main/kotlin/sift/core/api/TypeClassNode.kt b/core/src/main/kotlin/sift/core/api/TypeClassNode.kt new file mode 100644 index 0000000..1758a2c --- /dev/null +++ b/core/src/main/kotlin/sift/core/api/TypeClassNode.kt @@ -0,0 +1,81 @@ +package sift.core.api + +import sift.core.asm.signature.ArgType +import sift.core.asm.signature.FormalTypeParameter +import sift.core.asm.signature.TypeParameter +import sift.core.asm.signature.TypeSignature +import sift.core.dsl.Type +import sift.core.element.ClassNode +import sift.core.element.FieldNode +import sift.core.element.MethodNode +import sift.core.element.toType +import sift.core.tree.Tree + +internal data class TypeClassNode( + val type: Type, + val cn: ClassNode?, + val isInterface: Boolean, + var generics: List? = null +) { + constructor(type: Type, cn: ClassNode) + : this(type, cn, cn.isInterface) + override fun toString(): String = type.simpleName +} + +internal fun Tree.fields(): List { + val cn = value.cn ?: return listOf() + val ftps = value.generics!!.associateBy(TypeParameter::name) + return cn.fields.map { fn -> fn.reify(ftps) } +} + +/** propagates generic types from parent to children */ +internal fun Tree.resolveGenerics() { + // only need to resolve generics once + if (value.generics != null) + return + + fun update(node: Tree) { + val propagatedGenerics = node.parent?.value?.generics ?: listOf() + var genericTypes = node.genericTypes() + if (propagatedGenerics.isNotEmpty() && genericTypes.isNotEmpty()) { + val lookup = propagatedGenerics.associateBy { Type.from(it.name) } + genericTypes = genericTypes.mapValues { (_, v) -> lookup[v.bound]?.let { v.copy(bound = it.bound) } ?: v } + } + + node.value.generics = genericTypes.map { (_, v) -> v } + } + + walk().forEach(::update) +} + + + +// with resolved generic types methods +internal fun Tree.methods(): List { + val cn = value.cn ?: return listOf() + val ftps = value.generics!!.associateBy(TypeParameter::name) + return cn.methods.map { mn -> mn.reify(ftps) } +} + +private fun Tree.genericTypes(): Map { + val signature = value.cn?.signature ?: return mapOf() + + val innerTypes = value.type.innerTypes.takeIf(List::isNotEmpty) + ?: signature.formalParameters + .map(FormalTypeParameter::name) + .map(Type::from) + .takeIf(List::isNotEmpty) // fixme: when only some type parameters are specified + ?: return mapOf() + + + val ftps = signature.formalParameters + .mapIndexed { i, ftp -> TypeParameter(ftp.name, innerTypes[i], ftp.extends.map { it.toType() }) } + .associateBy(TypeParameter::name) + + // todo: fix or can we ignore interfaces? + val interfaces = signature.implements + .flatMap(TypeSignature::args) + .mapNotNull { it.argType as? ArgType.Var } + + return ftps +} diff --git a/core/src/main/kotlin/sift/core/api/debug/Inheritance.kt b/core/src/main/kotlin/sift/core/api/debug/Inheritance.kt index 6b43693..559f020 100644 --- a/core/src/main/kotlin/sift/core/api/debug/Inheritance.kt +++ b/core/src/main/kotlin/sift/core/api/debug/Inheritance.kt @@ -42,13 +42,13 @@ private fun TypeClassNode.toDebugString(): String { val className = toString() val methods = (cn ?: return fg(className)) .methods - .map(MethodNode::toDebugString) + .map(MethodNode::debugString) .joinToString(" ") return "${fg(className)} ${dark3(methods)}" } -private fun MethodNode.toDebugString(): String = +private fun MethodNode.debugString(): String = "$name(${parameters.joinToString { "${it.name}: ${it.type.simpleName}" }})" private val TypeClassNode.properties: String diff --git a/core/src/main/kotlin/sift/core/dsl/FilterableByVisibility.kt b/core/src/main/kotlin/sift/core/dsl/FilterableByVisibility.kt index d930f71..3418ba1 100644 --- a/core/src/main/kotlin/sift/core/dsl/FilterableByVisibility.kt +++ b/core/src/main/kotlin/sift/core/dsl/FilterableByVisibility.kt @@ -7,7 +7,7 @@ import sift.core.api.Iter import sift.core.element.Element /** Element visiblity modifier. Note that [Internal] corresponds to kotlin's `internal` modifier. */ -enum class Visibility { +enum class Visibility : Comparable { Private, PackagePrivate, Protected, diff --git a/core/src/main/kotlin/sift/core/dsl/MethodSelection.kt b/core/src/main/kotlin/sift/core/dsl/MethodSelection.kt index a20deb4..bba3f95 100644 --- a/core/src/main/kotlin/sift/core/dsl/MethodSelection.kt +++ b/core/src/main/kotlin/sift/core/dsl/MethodSelection.kt @@ -31,6 +31,7 @@ enum class MethodSelection : MethodSelectionFilter { inherited { override fun invoke(mn: MethodNode): Boolean { return mn.normalMethod + && (mn.originalCn == null || mn.visibility >= Visibility.Protected) } }, /** Matches synthetic methods, such as default-value functions in Kotlin. */ diff --git a/core/src/main/kotlin/sift/core/element/ClassNode.kt b/core/src/main/kotlin/sift/core/element/ClassNode.kt index 86b7bd0..4b294eb 100644 --- a/core/src/main/kotlin/sift/core/element/ClassNode.kt +++ b/core/src/main/kotlin/sift/core/element/ClassNode.kt @@ -3,12 +3,12 @@ package sift.core.element import net.onedaybeard.collectionsby.findBy import org.objectweb.asm.tree.InnerClassNode import sift.core.AsmNodeHashcoder.idHash -import sift.core.api.AccessFlags import sift.core.api.AccessFlags.acc_annotation import sift.core.api.AccessFlags.acc_interface import sift.core.asm.signature.ClassSignatureNode import sift.core.asm.signature.signature import sift.core.asm.superType +import sift.core.asm.toDebugString import sift.core.dsl.Type import sift.core.dsl.Visibility import sift.core.kotlin.KotlinClass @@ -93,6 +93,7 @@ class ClassNode private constructor( private val hash = idHash(cn) override fun toString() = simpleName + internal fun toDebugString(): String = cn.toDebugString() override fun equals(other: Any?): Boolean { return cn === (other as? ClassNode)?.cn diff --git a/core/src/main/kotlin/sift/core/element/MethodNode.kt b/core/src/main/kotlin/sift/core/element/MethodNode.kt index 2424954..0d83f5f 100644 --- a/core/src/main/kotlin/sift/core/element/MethodNode.kt +++ b/core/src/main/kotlin/sift/core/element/MethodNode.kt @@ -9,6 +9,7 @@ import sift.core.asm.signature.FormalTypeParameter import sift.core.asm.signature.MethodSignatureNode import sift.core.asm.signature.TypeParameter import sift.core.asm.signature.signature +import sift.core.asm.toDebugString import sift.core.dsl.Type import sift.core.dsl.Visibility import sift.core.kotlin.KotlinCallable @@ -18,7 +19,7 @@ class MethodNode private constructor( private val mn: AsmMethodNode, override val annotations: List, private val kfn: KotlinCallable?, - private val originalCn: ClassNode? = null, // when method is inherited + internal val originalCn: ClassNode? = null, // when method is inherited internal val signature: MethodSignatureNode? = defaultSignature(originalCn ?: cn, mn) ) : Element() { @@ -78,6 +79,8 @@ class MethodNode private constructor( fun instructions(): Sequence = mn.asSequence() + internal fun toDebugString(): String = mn.toDebugString() + override fun equals(other: Any?): Boolean { return other is MethodNode && mn === other.mn diff --git a/core/src/main/kotlin/sift/core/tree/TreeWalker.kt b/core/src/main/kotlin/sift/core/tree/TreeWalker.kt index 394ccd3..97db68e 100644 --- a/core/src/main/kotlin/sift/core/tree/TreeWalker.kt +++ b/core/src/main/kotlin/sift/core/tree/TreeWalker.kt @@ -18,13 +18,11 @@ internal class TreeWalker(val root: Tree, val traversal: TraversalType) : return value } - private fun prepareNext(): Tree? { - return when (traversal) { - TraversalType.DEPTH_FIRST -> buffer.removeLastOrNull() - ?.also { node -> buffer.addAll(node.children().reversed()) } - TraversalType.BREADTH_FIRST -> buffer.removeFirstOrNull() - ?.also { node -> buffer.addAll(node.children()) } - } + private fun prepareNext(): Tree? = when (traversal) { + TraversalType.DEPTH_FIRST -> buffer.removeLastOrNull() + ?.also { node -> buffer += node.children().reversed() } + TraversalType.BREADTH_FIRST -> buffer.removeFirstOrNull() + ?.also { node -> buffer += node.children() } } } } \ No newline at end of file diff --git a/core/src/test/kotlin/sift/core/dsl/ScopeInheritedElementsTest.kt b/core/src/test/kotlin/sift/core/dsl/ScopeInheritedElementsTest.kt index 0893c26..f9c5375 100644 --- a/core/src/test/kotlin/sift/core/dsl/ScopeInheritedElementsTest.kt +++ b/core/src/test/kotlin/sift/core/dsl/ScopeInheritedElementsTest.kt @@ -76,8 +76,8 @@ class ScopeInheritedElementsTest { │ └─ Concrete.baseField ├─ Concrete::concrete │ ├─ Concrete.baseField - │ ├─ Concrete.coreField - │ └─ Concrete.concreteField + │ ├─ Concrete.concreteField + │ └─ Concrete.coreField └─ Concrete::fooCore ├─ Concrete.baseField └─ Concrete.coreField