Skip to content

Commit 8a73d28

Browse files
committed
Read Java parameter names.
1 parent ec3abc7 commit 8a73d28

File tree

7 files changed

+56
-3
lines changed

7 files changed

+56
-3
lines changed

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ lazy val testSources = crossProject(JSPlatform, JVMPlatform)
6868
.settings(
6969
publish / skip := true,
7070
scalacOptions += "-Xfatal-warnings",
71+
javacOptions += "-parameters",
7172
)
7273

7374
lazy val tastyQuery =

tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileParser.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,19 @@ private[reader] object ClassfileParser {
219219
val sym = TermSymbol.create(name, owner)
220220
allRegisteredSymbols += sym
221221

222+
// Read parameter names
223+
val methodParamNames =
224+
if isMethod then readMethodParameters(attributes).map(_._1)
225+
else Nil
226+
222227
// Find the signature, or fall back to the descriptor
223228
val memberSig = attributes.get(attr.Signature) match
224229
case Some(stream) => stream.use(ClassfileReader.readSignature)
225230
case None => descriptor
226231

227232
// Parse the signature into a declared type for the symbol
228233
val declaredType =
229-
val parsedType = JavaSignatures.parseSignature(sym, isMethod, memberSig, allRegisteredSymbols)
234+
val parsedType = JavaSignatures.parseSignature(sym, isMethod, methodParamNames, memberSig, allRegisteredSymbols)
230235
val adaptedType =
231236
if isMethod && sym.name == nme.Constructor then cls.makePolyConstructorType(parsedType)
232237
else if isMethod && javaFlags.isVarargsIfMethod then patchForVarargs(sym, parsedType)
@@ -295,7 +300,9 @@ private[reader] object ClassfileParser {
295300
val parents = attributes.get(attr.Signature) match
296301
case Some(stream) =>
297302
val sig = stream.use(ClassfileReader.readSignature)
298-
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
299306
case mix: AndType => mix.parts
300307
case sup => sup :: Nil
301308
case None =>
@@ -367,6 +374,14 @@ private[reader] object ClassfileParser {
367374
None
368375
end ArrayTypeExtractor
369376

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+
370385
private def readAnnotations(
371386
sym: TermOrTypeSymbol,
372387
attributes: AttributeMap

tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/ClassfileReader.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,24 @@ private[reader] object ClassfileReader {
296296
result
297297
end readAttribute
298298

299+
def readMethodParameters()(using ds: DataStream, pool: ConstantPool): List[(UnsignedTermName, AccessFlags)] =
300+
val numParameters = data.readU1()
301+
val resultBuilder = List.newBuilder[(UnsignedTermName, AccessFlags)]
302+
303+
var index = 0
304+
while index != numParameters do
305+
val nameIndex = data.readU2()
306+
val name =
307+
if nameIndex == 0 then UniqueName(termName("x"), "$", index)
308+
else pool.utf8(pool.idx(nameIndex))
309+
val accessFlags = AccessFlags.read(data.readU2())
310+
resultBuilder += ((name, accessFlags))
311+
index += 1
312+
end while
313+
314+
resultBuilder.result()
315+
end readMethodParameters
316+
299317
def readAnnotation(typeDescriptors: Set[SimpleName])(using ds: DataStream, pool: ConstantPool): Option[Annotation] = {
300318
// pre: we are already inside the RuntimeVisibleAnnotations attribute
301319

tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/Constants.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ private[classfiles] object Constants:
88
val Scala = termName("Scala")
99
val ScalaSig = termName("ScalaSig")
1010
val InnerClasses = termName("InnerClasses")
11+
val MethodParameters = termName("MethodParameters")
1112
val RuntimeVisibleAnnotations = termName("RuntimeVisibleAnnotations") // RetentionPolicy.RUNTIME
1213
val RuntimeInvisibleAnnotations = termName("RuntimeInvisibleAnnotations") // RetentionPolicy.CLASS
1314
val RuntimeVisibleParameterAnnotations = termName("RuntimeVisibleParameterAnnotations")

tasty-query/shared/src/main/scala/tastyquery/reader/classfiles/JavaSignatures.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ private[classfiles] object JavaSignatures:
8282
def parseSignature(
8383
member: TermOrTypeSymbol,
8484
isMethod: Boolean,
85+
methodParameterNames: List[UnsignedTermName],
8586
signature: String,
8687
allRegisteredSymbols: Growable[TermOrTypeSymbol]
8788
)(using ReaderContext, InnerClasses, Resolver): TypeOrMethodic =
@@ -294,13 +295,20 @@ private[classfiles] object JavaSignatures:
294295
def termParamsRest(env: JavaSignature): List[Type] =
295296
readUntil(')', javaTypeSignature(env))
296297

298+
def uniqueParamNames(count: Int): List[UnsignedTermName] =
299+
val underlying = termName("x")
300+
(0 until count).toList.map(i => UniqueName(underlying, "$", i))
301+
297302
def methodSignature: MethodicType =
298303
def methodRest(env: JavaSignature): MethodType =
299304
if consume('(') then // must have '(', ')', and return type
300305
val params = termParamsRest(env)
301306
val ret = result(env)
302307
val _ = readWhile('^', throwsSignatureRest(env)) // ignore throws clauses
303-
MethodType((0 until params.size).map(i => termName(s"x$$$i")).toList, params, ret)
308+
val paramNames =
309+
if methodParameterNames.isEmpty && params.nonEmpty then uniqueParamNames(params.size)
310+
else methodParameterNames
311+
MethodType(paramNames, params, ret)
304312
else abort
305313
if consume('<') then
306314
PolyType(lookaheadTypeParamNames)(

tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,12 @@ class TypeSuite extends UnrestrictedUnpicklingSuite {
707707
assert(tpe.resultType.isArrayOf(_.isRef(JavaDefinedClass)))
708708
}
709709

710+
testDef(termName("multipleArguments")) { multipleArguments =>
711+
assertJavaPublic(multipleArguments)
712+
val tpe = multipleArguments.declaredType.asInstanceOf[MethodType]
713+
assert(clue(tpe.paramNames) == List(termName("foo"), termName("bar"), termName("foobar")))
714+
}
715+
710716
testDef(name"processBuilder") { processBuilder =>
711717
assertJavaPublic(processBuilder)
712718
val tpe = processBuilder.declaredType.asInstanceOf[MethodType]

test-sources/src/main/scala/javadefined/BagOfJavaDefinitions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public javadefined.JavaDefined[] arrIdentity(javadefined.JavaDefined[] arr) {
4747
return arr;
4848
}
4949

50+
public int multipleArguments(int foo, boolean bar, String foobar) {
51+
return foo;
52+
}
53+
5054
public java.lang.ProcessBuilder processBuilder() {
5155
return new ProcessBuilder("echo");
5256
}

0 commit comments

Comments
 (0)