Skip to content

Commit

Permalink
fields are now inherited by the inheriting class
Browse files Browse the repository at this point in the history
  • Loading branch information
junkdog committed Nov 12, 2023
1 parent 20f3b4b commit df856d0
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 87 deletions.
93 changes: 20 additions & 73 deletions core/src/main/kotlin/sift/core/api/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -140,8 +137,28 @@ internal data class Context(
}

fun fieldAccessBy(mn: MethodNode): Iterable<FieldNode> {
// update field owner if it is inherited
fun ClassNode.inheritFieldNode(fn: FieldNode, implemented: Set<Type>): 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<TypeClassNode>::value)
.map(TypeClassNode::type)
.toSet()

fieldAccessBy(methodsInvokedBy(mn), classByType)
.map { fn -> mn.owner.inheritFieldNode(fn, implemented) }
}
}

Expand Down Expand Up @@ -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<TypeParameter>? = null
) {
constructor(type: Type, cn: ClassNode)
: this(type, cn, cn.isInterface)
override fun toString(): String = type.simpleName
}

internal fun Tree<TypeClassNode>.resolveGenerics() {
// only need to resolve generics once
if (value.generics != null)
return

fun update(node: Tree<TypeClassNode>) {
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<TypeClassNode>.methods(): List<MethodNode> {
val cn = value.cn ?: return listOf()
val ftps = value.generics!!.associateBy(TypeParameter::name)
return cn.methods.map { mn -> mn.reify(ftps) }
}

internal fun Tree<TypeClassNode>.fields(): List<FieldNode> {
val cn = value.cn ?: return listOf()
val ftps = value.generics!!.associateBy(TypeParameter::name)
return cn.fields.map { fn -> fn.reify(ftps) }
}

//private val Tree<TypeClassNode>.genericTypes: Map<String, BoundTypeParameter>
private fun Tree<TypeClassNode>.genericTypes(): Map<String, TypeParameter> {
val signature = value.cn?.signature ?: return mapOf()

val innerTypes = value.type.innerTypes.takeIf(List<Type>::isNotEmpty)
?: signature.formalParameters
.map(FormalTypeParameter::name)
.map(Type::from)
.takeIf(List<Type>::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
}

81 changes: 81 additions & 0 deletions core/src/main/kotlin/sift/core/api/TypeClassNode.kt
Original file line number Diff line number Diff line change
@@ -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<TypeParameter>? = null
) {
constructor(type: Type, cn: ClassNode)
: this(type, cn, cn.isInterface)
override fun toString(): String = type.simpleName
}

internal fun Tree<TypeClassNode>.fields(): List<FieldNode> {
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<TypeClassNode>.resolveGenerics() {
// only need to resolve generics once
if (value.generics != null)
return

fun update(node: Tree<TypeClassNode>) {
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<TypeClassNode>.methods(): List<MethodNode> {
val cn = value.cn ?: return listOf()
val ftps = value.generics!!.associateBy(TypeParameter::name)
return cn.methods.map { mn -> mn.reify(ftps) }
}

private fun Tree<TypeClassNode>.genericTypes(): Map<String, TypeParameter> {
val signature = value.cn?.signature ?: return mapOf()

val innerTypes = value.type.innerTypes.takeIf(List<Type>::isNotEmpty)
?: signature.formalParameters
.map(FormalTypeParameter::name)
.map(Type::from)
.takeIf(List<Type>::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
}
4 changes: 2 additions & 2 deletions core/src/main/kotlin/sift/core/api/debug/Inheritance.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Visibility> {
Private,
PackagePrivate,
Protected,
Expand Down
1 change: 1 addition & 0 deletions core/src/main/kotlin/sift/core/dsl/MethodSelection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/kotlin/sift/core/element/ClassNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/kotlin/sift/core/element/MethodNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,7 +19,7 @@ class MethodNode private constructor(
private val mn: AsmMethodNode,
override val annotations: List<AnnotationNode>,
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() {

Expand Down Expand Up @@ -78,6 +79,8 @@ class MethodNode private constructor(

fun instructions(): Sequence<AbstractInsnNode> = mn.asSequence()

internal fun toDebugString(): String = mn.toDebugString()

override fun equals(other: Any?): Boolean {
return other is MethodNode
&& mn === other.mn
Expand Down
12 changes: 5 additions & 7 deletions core/src/main/kotlin/sift/core/tree/TreeWalker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ internal class TreeWalker<T>(val root: Tree<T>, val traversal: TraversalType) :
return value
}

private fun prepareNext(): Tree<T>? {
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<T>? = when (traversal) {
TraversalType.DEPTH_FIRST -> buffer.removeLastOrNull()
?.also { node -> buffer += node.children().reversed() }
TraversalType.BREADTH_FIRST -> buffer.removeFirstOrNull()
?.also { node -> buffer += node.children() }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit df856d0

Please sign in to comment.