Skip to content

Commit

Permalink
feature: PathElement#dereferenceElement support (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell authored Aug 10, 2024
1 parent 5a5a0e7 commit 4a009bf
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 21 deletions.
5 changes: 4 additions & 1 deletion src/main/kotlin/de/sirywell/handlehints/dfa/SsaAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ class MemoryLayoutHelper(private val ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(
coords: MutableList<Type>
) = BotVarHandleType // TODO does that make sense?

override fun invalidAddressDereference(head: IndexedValue<DereferenceElementType>): MemoryLayoutType {
return emitProblem(contextElement(head.index), message("problem.foreign.memory.dereferenceElementInvalid"))
}

override fun pathElementAndLayoutTypeMismatch(
head: IndexedValue<PathElementType>,
memoryLayoutType: KClass<out MemoryLayoutType>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@ class PathElementHelper(ssaAnalyzer: SsaAnalyzer) : ProblemEmitter(ssaAnalyzer.t
}
return TopPathElementType
}

fun dereferenceElement(): PathElementType {
return DereferenceElementType
}
}
25 changes: 25 additions & 0 deletions src/main/kotlin/de/sirywell/handlehints/foreign/PathTraverser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,35 @@ interface PathTraverser<T> {
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<DereferenceElementType>
): 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<DereferenceElementType>): MemoryLayoutType

private fun sequenceElement(
layoutType: MemoryLayoutType,
head: IndexedValue<SequenceElementType>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -146,6 +153,8 @@ private fun methodPattern(vararg methodNames: String): PsiMethodPattern {
}

private class ReturningPathTraverser : PathTraverser<MemoryLayoutType?> {
override fun invalidAddressDereference(head: IndexedValue<DereferenceElementType>) = TopMemoryLayoutType

override fun pathElementAndLayoutTypeMismatch(
head: IndexedValue<PathElementType>,
memoryLayoutType: KClass<out MemoryLayoutType>,
Expand Down
30 changes: 17 additions & 13 deletions src/main/kotlin/de/sirywell/handlehints/presentation/TypePrinter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,23 @@ class TypePrinter : TypeVisitor<TypePrinter.PrintContext, Unit> {
}
}

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("{⊤}")
}
Expand Down Expand Up @@ -239,17 +256,4 @@ class TypePrinter : TypeVisitor<TypePrinter.PrintContext, Unit> {
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(")")

}
}

}
28 changes: 22 additions & 6 deletions src/main/kotlin/de/sirywell/handlehints/type/MemoryLayoutType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ data class AddressLayoutType(
override fun joinIdentical(other: MemoryLayoutType): Pair<MemoryLayoutType, TriState> {
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
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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 <C, R> accept(visitor: TypeVisitor<C, R>, context: C): R {
return visitor.visit(this, context)
override fun <C, R> accept(visitor: TypeVisitor<C, R>, context: C) = visitor.visit(this, context)
}

data object DereferenceElementType : PathElementType {
override fun joinIdentical(other: PathElementType): Pair<PathElementType, TriState> {
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 <C, R> accept(visitor: TypeVisitor<C, R>, context: C) = visitor.visit(this, context)
}

data object TopPathElementType : PathElementType, TopTypeLatticeElement<PathElementType> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ interface TypeVisitor<C, R> {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
10 changes: 10 additions & 0 deletions src/test/testData/MemoryLayoutVarHandle.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import java.lang.foreign.AddressLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.SequenceLayout;
Expand All @@ -19,6 +20,11 @@ void m(ValueLayout vlU, long any, String unknown) {
<info descr="[int4(a)|boolean1(a)]">UnionLayout ul3 = <info descr="[int4(a)|boolean1(a)]">MemoryLayout.unionLayout(<info descr="int4(a)">ValueLayout.JAVA_INT.withName("a")</info>, <info descr="boolean1(a)">ValueLayout.JAVA_BOOLEAN.withName("a")</info>)</info>;</info>
<info descr="[int4({⊤})|boolean1(a)]">UnionLayout ul4 = <info descr="[int4({⊤})|boolean1(a)]">MemoryLayout.unionLayout(<info descr="int4({⊤})">ValueLayout.JAVA_INT.withName(unknown)</info>, <info descr="boolean1(a)">ValueLayout.JAVA_BOOLEAN.withName("a")</info>)</info>;</info>
<info descr="[int4(a)|boolean1({⊤})]">UnionLayout ul5 = <info descr="[int4(a)|boolean1({⊤})]">MemoryLayout.unionLayout(<info descr="int4(a)">ValueLayout.JAVA_INT.withName("a")</info>, <info descr="boolean1({⊤})">ValueLayout.JAVA_BOOLEAN.withName(unknown)</info>)</info>;</info>
<info descr="4%[[int4(s00)](s0)[int4(s10)](s1)]">StructLayout sl3 = <info descr="4%[[int4(s00)](s0)[int4(s10)](s1)]">MemoryLayout.structLayout(
<info descr="[int4(s00)](s0)"><info descr="[int4(s00)]">MemoryLayout.structLayout(<info descr="int4(s00)">ValueLayout.JAVA_INT.withName("s00")</info>)</info>.withName("s0")</info>,
<info descr="[int4(s10)](s1)"><info descr="[int4(s10)]">MemoryLayout.structLayout(<info descr="int4(s10)">ValueLayout.JAVA_INT.withName("s10")</info>)</info>.withName("s1")</info>
)</info>;</info>
<info descr="a?:[int4(a)|boolean1(b)]">AddressLayout al0 = <info descr="a?:[int4(a)|boolean1(b)]">ValueLayout.ADDRESS.withTargetLayout(ul2)</info>;</info>
// invalid - not a value layout
<info descr="⊤">VarHandle vh1 = <info descr="⊤"><warning descr="The layout targeted by the given path is not a 'ValueLayout'.">sl0.varHandle</warning>()</info>;</info>
// valid
Expand Down Expand Up @@ -66,5 +72,9 @@ void m(ValueLayout vlU, long any, String unknown) {
<info descr="(0=MemorySegment)(⊤)">VarHandle vh26 = <info descr="(0=MemorySegment)(⊤)">ul2.varHandle(<warning descr="Group layout does not have a member layout with name x."><info descr="groupElement(x)">MemoryLayout.PathElement.groupElement("x")</info></warning>)</info>;</info>
// with an unknown name, it might exist though
<info descr="(0=MemorySegment)(⊤)">VarHandle vh27 = <info descr="(0=MemorySegment)(⊤)">ul5.varHandle(<info descr="groupElement(x)">MemoryLayout.PathElement.groupElement("x")</info>)</info>;</info>
// nested
<info descr="(MemorySegment)(int)">VarHandle vh28 = <info descr="(MemorySegment)(int)">sl3.varHandle(<info descr="groupElement(1)">MemoryLayout.PathElement.groupElement(1)</info>, <info descr="groupElement(0)">MemoryLayout.PathElement.groupElement(0)</info>)</info>;</info>
// dereference
<info descr="(MemorySegment)(int)">VarHandle vh29 = <info descr="(MemorySegment)(int)">al0.varHandle(<info descr="dereferenceElement()">MemoryLayout.PathElement.dereferenceElement()</info>, <info descr="groupElement(a)">MemoryLayout.PathElement.groupElement("a")</info>)</info>;</info>
}
}

0 comments on commit 4a009bf

Please sign in to comment.