From 4a009bf84fcacf911597801decbeefc5b8a39d41 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 10 Aug 2024 09:56:37 +0200 Subject: [PATCH] feature: PathElement#dereferenceElement support (#119) --- .../sirywell/handlehints/dfa/SsaAnalyzer.kt | 5 +++- .../handlehints/foreign/MemoryLayoutHelper.kt | 4 +++ .../handlehints/foreign/PathElementHelper.kt | 4 +++ .../handlehints/foreign/PathTraverser.kt | 25 ++++++++++++++++ .../lookup/HandleHintsReferenceContributor.kt | 11 ++++++- .../handlehints/presentation/TypePrinter.kt | 30 +++++++++++-------- .../handlehints/type/MemoryLayoutType.kt | 28 +++++++++++++---- .../sirywell/handlehints/type/TypeVisitor.kt | 1 + .../messages/MethodHandleMessages.properties | 1 + src/test/testData/MemoryLayoutVarHandle.java | 10 +++++++ 10 files changed, 98 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt b/src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt index 1a99c93..d783fbf 100644 --- a/src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt +++ b/src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt @@ -318,7 +318,10 @@ class SsaAnalyzer(private val controlFlow: ControlFlow, val typeData: TypeData) if (arguments.size != 1) noMatch() else pathElementHelper.groupElement(arguments[0]) } - + "dereferenceElement" -> { + if (arguments.isNotEmpty()) return noMatch() + else pathElementHelper.dereferenceElement() + } else -> noMatch() } } diff --git a/src/main/kotlin/de/sirywell/handlehints/foreign/MemoryLayoutHelper.kt b/src/main/kotlin/de/sirywell/handlehints/foreign/MemoryLayoutHelper.kt index bb8ec0d..ed84ad7 100644 --- a/src/main/kotlin/de/sirywell/handlehints/foreign/MemoryLayoutHelper.kt +++ b/src/main/kotlin/de/sirywell/handlehints/foreign/MemoryLayoutHelper.kt @@ -232,6 +232,10 @@ class MemoryLayoutHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter( coords: MutableList ) = BotVarHandleType // TODO does that make sense? + override fun invalidAddressDereference(head: IndexedValue): MemoryLayoutType { + return emitProblem(contextElement(head.index), message("problem.foreign.memory.dereferenceElementInvalid")) + } + override fun pathElementAndLayoutTypeMismatch( head: IndexedValue, memoryLayoutType: KClass, diff --git a/src/main/kotlin/de/sirywell/handlehints/foreign/PathElementHelper.kt b/src/main/kotlin/de/sirywell/handlehints/foreign/PathElementHelper.kt index fa74279..a198e3e 100644 --- a/src/main/kotlin/de/sirywell/handlehints/foreign/PathElementHelper.kt +++ b/src/main/kotlin/de/sirywell/handlehints/foreign/PathElementHelper.kt @@ -60,4 +60,8 @@ class PathElementHelper(ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAnalyzer.t } return TopPathElementType } + + fun dereferenceElement(): PathElementType { + return DereferenceElementType + } } \ No newline at end of file diff --git a/src/main/kotlin/de/sirywell/handlehints/foreign/PathTraverser.kt b/src/main/kotlin/de/sirywell/handlehints/foreign/PathTraverser.kt index b5a4b4c..9ebd7a0 100644 --- a/src/main/kotlin/de/sirywell/handlehints/foreign/PathTraverser.kt +++ b/src/main/kotlin/de/sirywell/handlehints/foreign/PathTraverser.kt @@ -36,10 +36,35 @@ interface PathTraverser { layoutType, IndexedValue(head.index, head.value as GroupElementType) ) + + DereferenceElementType -> dereferenceElement(layoutType, IndexedValue(head.index, DereferenceElementType)) } return resolvePath(tail, resolvedLayout, coords) } + fun dereferenceElement( + layoutType: MemoryLayoutType, + head: IndexedValue + ): MemoryLayoutType { + return when (layoutType) { + BotMemoryLayoutType -> return BotMemoryLayoutType + TopMemoryLayoutType -> return TopMemoryLayoutType + is AddressLayoutType -> { + layoutType.targetLayout ?: // knowingly no target layout present + return invalidAddressDereference(head) + } + is StructLayoutType, + is UnionLayoutType, + is PaddingLayoutType, + is SequenceLayoutType, + is NormalValueLayoutType -> { + return pathElementAndLayoutTypeMismatch(head, layoutType::class, DereferenceElementType::class) + } + } + } + + fun invalidAddressDereference(head: IndexedValue): MemoryLayoutType + private fun sequenceElement( layoutType: MemoryLayoutType, head: IndexedValue, diff --git a/src/main/kotlin/de/sirywell/handlehints/lookup/HandleHintsReferenceContributor.kt b/src/main/kotlin/de/sirywell/handlehints/lookup/HandleHintsReferenceContributor.kt index 800b919..ac6273c 100644 --- a/src/main/kotlin/de/sirywell/handlehints/lookup/HandleHintsReferenceContributor.kt +++ b/src/main/kotlin/de/sirywell/handlehints/lookup/HandleHintsReferenceContributor.kt @@ -68,8 +68,15 @@ class HandleHintsReferenceContributor : CompletionContributor() { val expression = factory.createExpressionFromText(expressionText, qualifier) val lookupElement = lookupExpression(expression, icon, short, true, short, method, short, med) result.consume(lookupElement) + } else if (targeted is AddressLayoutType && targeted.targetLayout != null) { + val method = "dereferenceElement()" + val short = "PathElement.$method" + val med = "MemoryLayout.$short" + val expressionText = "java.lang.foreign.$med" + val expression = factory.createExpressionFromText(expressionText, qualifier) + val lookupElement = lookupExpression(expression, icon, short, false, short, method, short, med) + result.consume(lookupElement) } - // TODO AddressLayout/dereferenceElement support } private fun toPath( @@ -146,6 +153,8 @@ private fun methodPattern(vararg methodNames: String): PsiMethodPattern { } private class ReturningPathTraverser : PathTraverser { + override fun invalidAddressDereference(head: IndexedValue) = TopMemoryLayoutType + override fun pathElementAndLayoutTypeMismatch( head: IndexedValue, memoryLayoutType: KClass, diff --git a/src/main/kotlin/de/sirywell/handlehints/presentation/TypePrinter.kt b/src/main/kotlin/de/sirywell/handlehints/presentation/TypePrinter.kt index 6db0ffb..3791a4e 100644 --- a/src/main/kotlin/de/sirywell/handlehints/presentation/TypePrinter.kt +++ b/src/main/kotlin/de/sirywell/handlehints/presentation/TypePrinter.kt @@ -211,6 +211,23 @@ class TypePrinter : TypeVisitor { } } + override fun visit(type: GroupElementType, context: PrintContext) { + when (type.variant) { + is IndexGroupElementVariant -> context.append("groupElement(") + .append((type.variant.index ?: "index?").toString()) + .append(")") + + is NameGroupElementVariant -> context.append("groupElement(") + .append((type.variant.name ?: "name?").toString()) + .append(")") + + } + } + + override fun visit(type: DereferenceElementType, context: PrintContext) { + context.append("dereferenceElement()") + } + override fun visit(type: TopPathElementType, context: PrintContext) { context.append("{⊤}") } @@ -239,17 +256,4 @@ class TypePrinter : TypeVisitor { context.append("...") } - override fun visit(type: GroupElementType, context: PrintContext) { - when (type.variant) { - is IndexGroupElementVariant -> context.append("groupElement(") - .append((type.variant.index ?: "index?").toString()) - .append(")") - - is NameGroupElementVariant -> context.append("groupElement(") - .append((type.variant.name ?: "name?").toString()) - .append(")") - - } - } - } \ No newline at end of file diff --git a/src/main/kotlin/de/sirywell/handlehints/type/MemoryLayoutType.kt b/src/main/kotlin/de/sirywell/handlehints/type/MemoryLayoutType.kt index 577e80f..46dddc8 100644 --- a/src/main/kotlin/de/sirywell/handlehints/type/MemoryLayoutType.kt +++ b/src/main/kotlin/de/sirywell/handlehints/type/MemoryLayoutType.kt @@ -64,7 +64,7 @@ data class AddressLayoutType( override fun joinIdentical(other: MemoryLayoutType): Pair { if (other is AddressLayoutType) { val (targetLayout, identicalTargetLayout) = other.targetLayout?.let { this.targetLayout?.joinIdentical(it) } - // if both are null, they are the same in this aspect + // if both are null, they are the same in this aspect ?: if (other.targetLayout == null && this.targetLayout == null) null to TriState.YES // if only one is null, they are definitely not the same else TopMemoryLayoutType to TriState.NO @@ -392,7 +392,8 @@ data class SequenceElementType(val variant: SequenceElementVariant) : PathElemen return TopPathElementType to TriState.NO } - is GroupElementType -> TopPathElementType to TriState.NO + is GroupElementType, + DereferenceElementType -> TopPathElementType to TriState.NO } } @@ -431,14 +432,29 @@ data class GroupElementType(val variant: GroupElementVariant) : PathElementType return TopPathElementType to TriState.NO } - is SequenceElementType -> TopPathElementType to TriState.NO + is SequenceElementType, + DereferenceElementType -> TopPathElementType to TriState.NO } - } - override fun accept(visitor: TypeVisitor, context: C): R { - return visitor.visit(this, context) + override fun accept(visitor: TypeVisitor, context: C) = visitor.visit(this, context) +} + +data object DereferenceElementType : PathElementType { + override fun joinIdentical(other: PathElementType): Pair { + return when (other) { + TopPathElementType -> other to TriState.UNKNOWN + BotPathElementType -> this to TriState.UNKNOWN + DereferenceElementType -> { + return this to TriState.YES + } + + is SequenceElementType, + is GroupElementType -> TopPathElementType to TriState.NO + } } + + override fun accept(visitor: TypeVisitor, context: C) = visitor.visit(this, context) } data object TopPathElementType : PathElementType, TopTypeLatticeElement { diff --git a/src/main/kotlin/de/sirywell/handlehints/type/TypeVisitor.kt b/src/main/kotlin/de/sirywell/handlehints/type/TypeVisitor.kt index 9f4eaaa..3b1c7bb 100644 --- a/src/main/kotlin/de/sirywell/handlehints/type/TypeVisitor.kt +++ b/src/main/kotlin/de/sirywell/handlehints/type/TypeVisitor.kt @@ -32,6 +32,7 @@ interface TypeVisitor { fun visit(type: BotLayoutName, context: C): R fun visit(type: SequenceElementType, context: C): R fun visit(type: GroupElementType, context: C): R + fun visit(type: DereferenceElementType, context: C): R fun visit(type: TopPathElementType, context: C): R fun visit(type: BotPathElementType, context: C): R fun visit(type: TopPathElementList, context: C): R diff --git a/src/main/resources/messages/MethodHandleMessages.properties b/src/main/resources/messages/MethodHandleMessages.properties index 63f6b91..dd12efa 100644 --- a/src/main/resources/messages/MethodHandleMessages.properties +++ b/src/main/resources/messages/MethodHandleMessages.properties @@ -45,3 +45,4 @@ problem.foreign.memory.pathTargetNotValueLayout=The layout targeted by the given problem.foreign.memory.pathElementMismatch=A {0} path element cannot be applied to a {1}. problem.foreign.memory.pathGroupElementOutOfBounds=Group element at index {0} exceeds the number of member layouts {1}. problem.foreign.memory.pathGroupElementUnknownName=Group layout does not have a member layout with name {0}. +problem.foreign.memory.dereferenceElementInvalid=Address layout has no target layout to dereference. diff --git a/src/test/testData/MemoryLayoutVarHandle.java b/src/test/testData/MemoryLayoutVarHandle.java index 2df29e5..f178b95 100644 --- a/src/test/testData/MemoryLayoutVarHandle.java +++ b/src/test/testData/MemoryLayoutVarHandle.java @@ -1,3 +1,4 @@ +import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.PaddingLayout; import java.lang.foreign.SequenceLayout; @@ -19,6 +20,11 @@ void m(ValueLayout vlU, long any, String unknown) { UnionLayout ul3 = MemoryLayout.unionLayout(ValueLayout.JAVA_INT.withName("a"), ValueLayout.JAVA_BOOLEAN.withName("a")); UnionLayout ul4 = MemoryLayout.unionLayout(ValueLayout.JAVA_INT.withName(unknown), ValueLayout.JAVA_BOOLEAN.withName("a")); UnionLayout ul5 = MemoryLayout.unionLayout(ValueLayout.JAVA_INT.withName("a"), ValueLayout.JAVA_BOOLEAN.withName(unknown)); + StructLayout sl3 = MemoryLayout.structLayout( + MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("s00")).withName("s0"), + MemoryLayout.structLayout(ValueLayout.JAVA_INT.withName("s10")).withName("s1") + ); + AddressLayout al0 = ValueLayout.ADDRESS.withTargetLayout(ul2); // invalid - not a value layout VarHandle vh1 = sl0.varHandle(); // valid @@ -66,5 +72,9 @@ void m(ValueLayout vlU, long any, String unknown) { VarHandle vh26 = ul2.varHandle(MemoryLayout.PathElement.groupElement("x")); // with an unknown name, it might exist though VarHandle vh27 = ul5.varHandle(MemoryLayout.PathElement.groupElement("x")); + // nested + VarHandle vh28 = sl3.varHandle(MemoryLayout.PathElement.groupElement(1), MemoryLayout.PathElement.groupElement(0)); + // dereference + VarHandle vh29 = al0.varHandle(MemoryLayout.PathElement.dereferenceElement(), MemoryLayout.PathElement.groupElement("a")); } } \ No newline at end of file