-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJavaMapUtils.scala
More file actions
134 lines (116 loc) · 4.79 KB
/
JavaMapUtils.scala
File metadata and controls
134 lines (116 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package org.encalmo.utils
import scala.quoted.*
object JavaMapUtils {
object TypeReprIsJavaMap {
def unapply(using
Quotes
)(tpe: quotes.reflect.TypeRepr): Option[(quotes.reflect.TypeRepr, quotes.reflect.TypeRepr)] = {
import quotes.reflect.*
// 1. Normalize: Widen (singleton -> type) and Dealias (alias -> underlying)
val normalized = tpe.widen.dealias
// 2. Resolve Java Map Symbol
val mapSym = Symbol.requiredClass("java.util.Map")
// 3. Check Inheritance
if (normalized.derivesFrom(mapSym)) {
// 4. Upcast to java.util.Map[K, V]
val base = normalized.baseType(mapSym)
base match {
case AppliedType(_, List(keyArg, valueArg)) =>
// Helper to handle wildcards (e.g., Map.of often returns Map[? <: K, ? <: V])
def unwrap(t: TypeRepr): TypeRepr = t match {
case TypeBounds(_, hi) => hi // Extract 'String' from '? <: String'
case concrete => concrete
}
Some((unwrap(keyArg), unwrap(valueArg)))
// Edge case: Raw types (e.g. raw 'java.util.HashMap')
case _ =>
Some((TypeRepr.of[Any], TypeRepr.of[Any]))
}
} else {
None
}
}
}
def buildMapLoop(using
cache: StatementsCache
)(
iteratorName: String,
keyTpe: cache.quotes.reflect.TypeRepr,
valueTpe: cache.quotes.reflect.TypeRepr,
target: cache.quotes.reflect.Term,
functionOnEntry: (
cache.quotes.reflect.Term,
cache.quotes.reflect.Term
) => cache.quotes.reflect.Term
): cache.quotes.reflect.Term = {
given cache.quotes.type = cache.quotes
import cache.quotes.reflect.*
// 1. Resolve Java Symbols
val mapSym = Symbol.requiredClass("java.util.Map")
val entrySym = Symbol.requiredClass("java.util.Map.Entry") // Nested interface
val setSym = Symbol.requiredClass("java.util.Set")
val iteratorSym = Symbol.requiredClass("java.util.Iterator")
// 2. Resolve Methods
val entrySetMethod = mapSym.methodMember("entrySet").head
val iteratorMethod = setSym.methodMember("iterator").head
val hasNextMethod = iteratorSym.methodMember("hasNext").head
val nextMethod = iteratorSym.methodMember("next").head
val getKeyMethod = entrySym.methodMember("getKey").head
val getValueMethod = entrySym.methodMember("getValue").head
// 3. Construct Types
// Type: java.util.Map.Entry[K, V]
val entryType = entrySym.typeRef.appliedTo(List(keyTpe, valueTpe))
// Type: java.util.Iterator[java.util.Map.Entry[K, V]]
val iteratorType = iteratorSym.typeRef.appliedTo(entryType)
// Type: java.util.Map[K, V] (for casting)
val mapType = mapSym.typeRef.appliedTo(List(keyTpe, valueTpe))
// 4. Cast Target to Map[K, V]
// Crucial for handling private subclasses (e.g. Map.of)
val typedTarget = Typed(target, Inferred(mapType))
// 5. Create Iterator Chain: map.entrySet().iterator()
// A. call .entrySet()
val entrySetCall = Apply(Select(typedTarget, entrySetMethod), Nil)
// B. call .iterator() on the result
val iteratorCall = Apply(Select(entrySetCall, iteratorMethod), Nil)
// Define 'val it = ...'
val iteratorSymVal = Symbol.newVal(
Symbol.spliceOwner,
iteratorName + "Iterator",
iteratorType,
Flags.EmptyFlags,
Symbol.noSymbol
)
val iteratorValDef = ValDef(iteratorSymVal, Some(iteratorCall))
val iteratorRef = Ref(iteratorSymVal)
// 6. Loop Condition: it.hasNext()
val condition = Apply(Select(iteratorRef, hasNextMethod), Nil)
// 7. Loop Body
val loopBody = {
// A. val entry = it.next()
val nextCall = Apply(Select(iteratorRef, nextMethod), Nil)
val entryValSym = Symbol.newVal(Symbol.spliceOwner, "entry", entryType, Flags.EmptyFlags, Symbol.noSymbol)
val entryValDef = ValDef(entryValSym, Some(nextCall))
val entryRef = Ref(entryValSym)
// B. val k = entry.getKey()
val keyCall = Apply(Select(entryRef, getKeyMethod), Nil)
val keyValSym = Symbol.newVal(Symbol.spliceOwner, "key", keyTpe, Flags.EmptyFlags, Symbol.noSymbol)
val keyValDef = ValDef(keyValSym, Some(keyCall))
// C. val v = entry.getValue()
val valueCall = Apply(Select(entryRef, getValueMethod), Nil)
val valValSym = Symbol.newVal(Symbol.spliceOwner, "value", valueTpe, Flags.EmptyFlags, Symbol.noSymbol)
val valValDef = ValDef(valValSym, Some(valueCall))
// D. User Logic: functionOnEntry(k, v)
val userCode = functionOnEntry(Ref(keyValSym), Ref(valValSym))
// E. Block Construction
Block(
List(entryValDef, keyValDef, valValDef, userCode),
Literal(UnitConstant())
)
}
// 8. Assemble Loop
Block(
List(iteratorValDef),
While(condition, loopBody)
)
}
}