1
1
package tastyquery .reader .classfiles
2
2
3
+ import scala .annotation .switch
4
+
3
5
import scala .collection .mutable
4
6
5
- import tastyquery .Annotations .Annotation as TQAnnotation
7
+ import tastyquery .Annotations .*
6
8
import tastyquery .Classpaths .*
7
9
import tastyquery .Contexts .*
10
+ import tastyquery .Constants .*
8
11
import tastyquery .Exceptions .*
9
12
import tastyquery .Flags
10
13
import tastyquery .Flags .*
11
14
import tastyquery .Names .*
12
15
import tastyquery .SourceLanguage
16
+ import tastyquery .SourcePosition
13
17
import tastyquery .Symbols .*
18
+ import tastyquery .Trees .*
14
19
import tastyquery .Types .*
15
20
16
21
import tastyquery .reader .ReaderContext
17
22
import tastyquery .reader .ReaderContext .rctx
18
23
import tastyquery .reader .pickles .{Unpickler , PickleReader }
19
24
20
- import ClassfileReader .*
25
+ import ClassfileReader .{ Annotation as CFAnnotation , * }
21
26
import ClassfileReader .Access .AccessFlags
22
27
import Constants .*
23
28
@@ -63,6 +68,8 @@ private[reader] object ClassfileParser {
63
68
def resolve (binaryName : SimpleName )(using ReaderContext , InnerClasses ): TypeRef =
64
69
lookup(binaryName, isStatic = false ).asTypeRef
65
70
71
+ def resolveStatic (binaryName : SimpleName )(using ReaderContext , InnerClasses ): TermRef =
72
+ lookup(binaryName, isStatic = true ).asTermRef
66
73
end Resolver
67
74
68
75
/** The inner classes local to a class file */
@@ -127,10 +134,10 @@ private[reader] object ClassfileParser {
127
134
128
135
val sigBytes = scalaSigAnnotation.tpe match {
129
136
case annot.ScalaSignature =>
130
- val bytesArg = scalaSigAnnotation.values.head.asInstanceOf [AnnotationValue .Const ]
137
+ val bytesArg = scalaSigAnnotation.values.head._2. asInstanceOf [AnnotationValue .Const ]
131
138
pool.sigbytes(bytesArg.valueIdx)
132
139
case annot.ScalaLongSignature =>
133
- val bytesArrArg = scalaSigAnnotation.values.head.asInstanceOf [AnnotationValue .Arr ]
140
+ val bytesArrArg = scalaSigAnnotation.values.head._2. asInstanceOf [AnnotationValue .Arr ]
134
141
val idxs = bytesArrArg.values.map(_.asInstanceOf [AnnotationValue .Const ].valueIdx)
135
142
pool.sigbytes(idxs)
136
143
}
@@ -200,15 +207,31 @@ private[reader] object ClassfileParser {
200
207
else false
201
208
end isSignaturePolymorphic
202
209
203
- def createMember (name : SimpleName , isMethod : Boolean , javaFlags : AccessFlags , memberSig : MemberSig ): TermSymbol =
210
+ def createMember (
211
+ name : SimpleName ,
212
+ isMethod : Boolean ,
213
+ javaFlags : AccessFlags ,
214
+ descriptor : String ,
215
+ attributes : Map [SimpleName , Forked [DataStream ]]
216
+ ): TermSymbol =
204
217
// Select the right owner and create the symbol
205
218
val owner = if javaFlags.isStatic then moduleClass else cls
206
219
val sym = TermSymbol .create(name, owner)
207
220
allRegisteredSymbols += sym
208
221
222
+ // Read parameter names
223
+ val methodParamNames =
224
+ if isMethod then readMethodParameters(attributes).map(_._1)
225
+ else Nil
226
+
227
+ // Find the signature, or fall back to the descriptor
228
+ val memberSig = attributes.get(attr.Signature ) match
229
+ case Some (stream) => stream.use(ClassfileReader .readSignature)
230
+ case None => descriptor
231
+
209
232
// Parse the signature into a declared type for the symbol
210
233
val declaredType =
211
- val parsedType = JavaSignatures .parseSignature(sym, isMethod, memberSig, allRegisteredSymbols)
234
+ val parsedType = JavaSignatures .parseSignature(sym, isMethod, methodParamNames, memberSig, allRegisteredSymbols)
212
235
val adaptedType =
213
236
if isMethod && sym.name == nme.Constructor then cls.makePolyConstructorType(parsedType)
214
237
else if isMethod && javaFlags.isVarargsIfMethod then patchForVarargs(sym, parsedType)
@@ -226,23 +249,46 @@ private[reader] object ClassfileParser {
226
249
end flags
227
250
sym.withFlags(flags, privateWithin(javaFlags))
228
251
229
- // Auto fill the param symbols from the declared type
230
- sym.autoFillParamSymss()
231
-
232
- sym.setAnnotations(Nil ) // TODO Read Java annotations on fields and methods
252
+ // Read and fill annotations
253
+ val annots = readAnnotations(sym, attributes)
254
+ sym.setAnnotations(annots)
255
+
256
+ // Handle parameters
257
+ if isMethod then
258
+ // Auto fill the param symbols from the declared type
259
+ sym.autoFillParamSymss()
260
+
261
+ val termParamAnnots = readTermParamAnnotations(attributes)
262
+ if termParamAnnots.isEmpty then
263
+ // fast path
264
+ sym.paramSymss.foreach(_.merge.foreach(_.setAnnotations(Nil )))
265
+ else
266
+ val termParamAnnotsIter = termParamAnnots.iterator
267
+
268
+ for paramSyms <- sym.paramSymss do
269
+ paramSyms match
270
+ case Left (termParamSyms) =>
271
+ for termParamSym <- termParamSyms do
272
+ val annots = if termParamAnnotsIter.hasNext then termParamAnnotsIter.next() else Nil
273
+ termParamSym.setAnnotations(annots)
274
+ case Right (typeParamSyms) =>
275
+ // TODO Maybe one day we also read type annotations
276
+ typeParamSyms.foreach(_.setAnnotations(Nil ))
277
+ end if
278
+ end if
233
279
234
280
sym
235
281
end createMember
236
282
237
283
def loadMembers (): Unit =
238
284
structure.fields.use {
239
- ClassfileReader .readFields { (name, sigOrDesc, javaFlags ) =>
240
- createMember(name, isMethod = false , javaFlags, sigOrDesc )
285
+ ClassfileReader .readMembers { (javaFlags, name, descriptor, attributes ) =>
286
+ createMember(name, isMethod = false , javaFlags, descriptor, attributes )
241
287
}
242
288
}
243
289
structure.methods.use {
244
- ClassfileReader .readMethods { (name, sigOrDesc, javaFlags ) =>
245
- createMember(name, isMethod = true , javaFlags, sigOrDesc )
290
+ ClassfileReader .readMembers { (javaFlags, name, descriptor, attributes ) =>
291
+ createMember(name, isMethod = true , javaFlags, descriptor, attributes )
246
292
}
247
293
}
248
294
end loadMembers
@@ -254,7 +300,9 @@ private[reader] object ClassfileParser {
254
300
val parents = attributes.get(attr.Signature ) match
255
301
case Some (stream) =>
256
302
val sig = stream.use(ClassfileReader .readSignature)
257
- JavaSignatures .parseSignature(cls, isMethod = false , sig, allRegisteredSymbols).requireType match
303
+ val parsedSig =
304
+ JavaSignatures .parseSignature(cls, isMethod = false , methodParameterNames = Nil , sig, allRegisteredSymbols)
305
+ parsedSig.requireType match
258
306
case mix : AndType => mix.parts
259
307
case sup => sup :: Nil
260
308
case None =>
@@ -273,7 +321,6 @@ private[reader] object ClassfileParser {
273
321
274
322
cls.withGivenSelfType(None )
275
323
cls.withFlags(clsFlags, clsPrivateWithin)
276
- cls.setAnnotations(Nil ) // TODO Read Java annotations on classes
277
324
initParents()
278
325
279
326
// Intercept special classes to create their magic methods
@@ -284,6 +331,9 @@ private[reader] object ClassfileParser {
284
331
285
332
loadMembers()
286
333
334
+ val annotations = readAnnotations(cls, attributes)
335
+ cls.setAnnotations(annotations)
336
+
287
337
for sym <- allRegisteredSymbols do
288
338
sym.checkCompleted()
289
339
assert(sym.sourceLanguage == SourceLanguage .Java , s " $sym of ${sym.getClass()}" )
@@ -324,6 +374,115 @@ private[reader] object ClassfileParser {
324
374
None
325
375
end ArrayTypeExtractor
326
376
377
+ private def readMethodParameters (attributes : AttributeMap )(
378
+ using ConstantPool
379
+ ): List [(UnsignedTermName , AccessFlags )] =
380
+ attributes.get(attr.MethodParameters ) match
381
+ case Some (stream) => stream.use(ClassfileReader .readMethodParameters())
382
+ case None => Nil
383
+ end readMethodParameters
384
+
385
+ private def readAnnotations (
386
+ sym : TermOrTypeSymbol ,
387
+ attributes : AttributeMap
388
+ )(using ConstantPool , ReaderContext , InnerClasses , Resolver ): List [Annotation ] =
389
+ readAnnotations(sym, attributes.get(attr.RuntimeVisibleAnnotations ))
390
+ ::: readAnnotations(sym, attributes.get(attr.RuntimeInvisibleAnnotations ))
391
+ end readAnnotations
392
+
393
+ private def readAnnotations (
394
+ sym : TermOrTypeSymbol ,
395
+ annotationsStream : Option [Forked [DataStream ]]
396
+ )(using ConstantPool , ReaderContext , InnerClasses , Resolver ): List [Annotation ] =
397
+ annotationsStream.fold(Nil )(readAnnotations(sym, _))
398
+ end readAnnotations
399
+
400
+ private def readAnnotations (
401
+ sym : TermOrTypeSymbol ,
402
+ annotationsStream : Forked [DataStream ]
403
+ )(using ConstantPool , ReaderContext , InnerClasses , Resolver ): List [Annotation ] =
404
+ val classfileAnnots = annotationsStream.use(ClassfileReader .readAllAnnotations())
405
+ classfileAnnots.map(classfileAnnotToAnnot(_))
406
+
407
+ private def readTermParamAnnotations (
408
+ attributes : AttributeMap
409
+ )(using ConstantPool , ReaderContext , InnerClasses , Resolver ): List [List [Annotation ]] =
410
+ val runtimeVisible = attributes.get(attr.RuntimeVisibleParameterAnnotations ) match
411
+ case None => Nil
412
+ case Some (stream) => stream.use(ClassfileReader .readAllParameterAnnotations())
413
+
414
+ val runtimeInvisible = attributes.get(attr.RuntimeInvisibleParameterAnnotations ) match
415
+ case None => Nil
416
+ case Some (stream) => stream.use(ClassfileReader .readAllParameterAnnotations())
417
+
418
+ if runtimeVisible.isEmpty && runtimeInvisible.isEmpty then
419
+ // fast path
420
+ Nil
421
+ else
422
+ for (rtVisible, rtInvisible) <- runtimeVisible.zipAll(runtimeInvisible, Nil , Nil )
423
+ yield (rtVisible ::: rtInvisible).map(classfileAnnotToAnnot(_))
424
+ end readTermParamAnnotations
425
+
426
+ private def classfileAnnotToAnnot (
427
+ classfileAnnot : CFAnnotation
428
+ )(using ConstantPool , ReaderContext , InnerClasses , Resolver ): Annotation =
429
+ val annotationType = JavaSignatures .parseFieldDescriptor(classfileAnnot.tpe.name)
430
+
431
+ val args : List [TermTree ] =
432
+ for (name, value) <- classfileAnnot.values.toList yield
433
+ val valueTree = annotationValueToTree(value)
434
+ NamedArg (name, valueTree)(SourcePosition .NoPosition )
435
+
436
+ Annotation .fromAnnotTypeAndArgs(annotationType, args)
437
+ end classfileAnnotToAnnot
438
+
439
+ private def annotationValueToTree (
440
+ value : AnnotationValue
441
+ )(using ConstantPool , ReaderContext , InnerClasses , Resolver ): TermTree =
442
+ import AnnotationValue .Tags
443
+
444
+ val pool = summon[ConstantPool ]
445
+ val pos = SourcePosition .NoPosition
446
+
447
+ value match
448
+ case AnnotationValue .Const (tag, valueIdx) =>
449
+ val constant = (tag : @ switch) match
450
+ case Tags .Byte => Constant (pool.integer(valueIdx).toByte)
451
+ case Tags .Char => Constant (pool.integer(valueIdx).toChar)
452
+ case Tags .Double => Constant (pool.double(valueIdx))
453
+ case Tags .Float => Constant (pool.float(valueIdx))
454
+ case Tags .Int => Constant (pool.integer(valueIdx))
455
+ case Tags .Long => Constant (pool.long(valueIdx))
456
+ case Tags .Short => Constant (pool.integer(valueIdx).toShort)
457
+ case Tags .Boolean => Constant (pool.integer(valueIdx) != 0 )
458
+ case Tags .String => Constant (pool.utf8(valueIdx).name)
459
+ Literal (constant)(pos)
460
+
461
+ case AnnotationValue .EnumConst (descriptor, constName) =>
462
+ /* JVMS says that it can be any field descriptor,
463
+ * but I don't see what we would do with a base type or array type.
464
+ */
465
+ val binaryName = descriptor.name match
466
+ case s " L $binaryName; " => binaryName
467
+ case other => throw ClassfileFormatException (s " unexpected non-class field descriptor: $other" )
468
+ val enumClassStaticRef = resolver.resolveStatic(termName(binaryName))
469
+ val constRef = TermRef (enumClassStaticRef, constName)
470
+ Ident (constName)(constRef)(pos)
471
+
472
+ case AnnotationValue .ClassConst (descriptor) =>
473
+ val classType = JavaSignatures .parseReturnDescriptor(descriptor.name)
474
+ Literal (Constant (classType))(pos)
475
+
476
+ case AnnotationValue .NestedAnnotation (annotation) =>
477
+ val nestedAnnot = classfileAnnotToAnnot(annotation)
478
+ nestedAnnot.tree
479
+
480
+ case AnnotationValue .Arr (values) =>
481
+ val valueTrees = values.map(annotationValueToTree(_)).toList
482
+ val elemType = rctx.AnyType // TODO This will not be type-correct
483
+ SeqLiteral (valueTrees, TypeWrapper (elemType)(pos))(pos)
484
+ end annotationValueToTree
485
+
327
486
def detectClassKind (structure : Structure ): ClassKind =
328
487
import structure .given
329
488
0 commit comments