From a4062254526872ea1531cf9ab26dc89a1695185b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 Feb 2021 12:33:50 +0100 Subject: [PATCH 1/3] Output parameter names Partially ports https://github.com/scala/scala/pull/4735 --- .../tools/backend/jvm/BCodeHelpers.scala | 8 +++++++ .../tools/backend/jvm/BCodeSkelBuilder.scala | 7 ++++--- tests/run/i11486/Old_1.scala | 3 +++ tests/run/i11486/Test_2.scala | 21 +++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 tests/run/i11486/Old_1.scala create mode 100644 tests/run/i11486/Test_2.scala diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index 2391574edeb2..d8a38e263970 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -335,6 +335,14 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { emitAssocs(av, assocs, BCodeHelpers.this)(this) } + /* + * must-single-thread + */ + def emitParamNames(jmethod: asm.MethodVisitor, params: List[Symbol]) = + for param <- params do + var access = asm.Opcodes.ACC_FINAL + jmethod.visitParameter(param.name.mangledString, access) + /* * must-single-thread */ diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 9401307e8d62..253dc495d9c0 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -615,7 +615,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* * must-single-thread */ - def initJMethod(flags: Int, paramAnnotations: List[List[Annotation]]): Unit = { + def initJMethod(flags: Int, params: List[Symbol]): Unit = { val jgensig = getGenericSignature(methSymbol, claszSymbol) val (excs, others) = methSymbol.annotations.partition(_.symbol eq defn.ThrowsAnnot) @@ -637,7 +637,8 @@ trait BCodeSkelBuilder extends BCodeHelpers { // TODO param names: (m.params map (p => javaName(p.sym))) emitAnnotations(mnode, others) - emitParamAnnotations(mnode, paramAnnotations) + emitParamNames(mnode, params) + emitParamAnnotations(mnode, params.map(_.annotations)) } // end of method initJMethod @@ -749,7 +750,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { .addFlagIf(isNative, asm.Opcodes.ACC_NATIVE) // native methods of objects are generated in mirror classes // TODO needed? for(ann <- m.symbol.annotations) { ann.symbol.initialize } - initJMethod(flags, params.map(p => p.symbol.annotations)) + initJMethod(flags, params.map(_.symbol)) if (!isAbstractMethod && !isNative) { diff --git a/tests/run/i11486/Old_1.scala b/tests/run/i11486/Old_1.scala new file mode 100644 index 000000000000..f1a6513145ec --- /dev/null +++ b/tests/run/i11486/Old_1.scala @@ -0,0 +1,3 @@ +case class TestedOld(foo: Int, bar: String, baz: Double): + def target(abc: TestedOld, efg: TestedOld) = () + def symbolic(`def`: Int, *** : Int, `unary_!`: Int) = () \ No newline at end of file diff --git a/tests/run/i11486/Test_2.scala b/tests/run/i11486/Test_2.scala new file mode 100644 index 000000000000..64e03dcd978b --- /dev/null +++ b/tests/run/i11486/Test_2.scala @@ -0,0 +1,21 @@ +import java.lang.reflect.Executable + +case class Tested(foo: Int, bar: String, baz: Double): + def target(abc: Tested, efg: Tested) = () + def symbolic(`def`: Int, *** : Int, `unary_!`: Int) = () + +def run(cls: Class[_]) = + extension(m: Executable) def parameters: List[String] = m.getParameters.toList.map(_.getName) + + val ctorParams = cls.getConstructors.head.parameters + assert(ctorParams == List("foo", "bar", "baz")) + + val targetParams = cls.getMethods.toList.find(_.getName == "target").get.parameters + assert(targetParams == List("abc", "efg")) + + val symbolicParams = cls.getMethods.toList.find(_.getName == "symbolic").get.parameters + assert(symbolicParams == List("def", "$times$times$times", "unary_$bang")) + +@main def Test = + run(classOf[TestedOld]) + run(classOf[Tested]) From 0902c3ba3ed6f83a380c2582df64e676d9b88991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 Feb 2021 17:58:38 +0100 Subject: [PATCH 2/3] Parse parameter names from classfiles --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotc/core/classfile/ClassfileParser.scala | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 3570be66e8e3..5ab34a71c0e6 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -257,6 +257,7 @@ object StdNames { final val DeprecatedATTR: N = "Deprecated" final val ExceptionsATTR: N = "Exceptions" final val InnerClassesATTR: N = "InnerClasses" + final val MethodParametersATTR: N = "MethodParameters" final val LineNumberTableATTR: N = "LineNumberTable" final val LocalVariableTableATTR: N = "LocalVariableTable" final val RuntimeVisibleAnnotationATTR: N = "RuntimeVisibleAnnotations" // RetentionPolicy.RUNTIME diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 50167643daef..bc64f34d8d7a 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -657,6 +657,7 @@ class ClassfileParser( var constant: Constant = null var exceptions: List[NameOrString] = Nil var annotations: List[Annotation] = Nil + var namedParams: Map[Int, TermName] = Map.empty def complete(tp: Type, isVarargs: Boolean = false)(using Context): Type = { val updatedType = if sig == null then tp @@ -680,7 +681,14 @@ class ClassfileParser( sym.addAnnotation(ThrowsAnnotation(cls.asClass)) } - cook.apply(newType) + def fillInParamNames(t: Type): Type = t match + case mt @ MethodType(oldp) if namedParams.nonEmpty => + mt.derivedLambdaType(List.tabulate(oldp.size)(n => namedParams.getOrElse(n, oldp(n)))) + case pt: PolyType if namedParams.nonEmpty => + pt.derivedLambdaType(pt.paramNames, pt.paramInfos, fillInParamNames(pt.resultType)) + case _ => t + + cook.apply(fillInParamNames(newType)) } } @@ -714,6 +722,14 @@ class ClassfileParser( if (c ne null) res.constant = c else report.warning(s"Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}") + case tpnme.MethodParametersATTR => + val paramCount = in.nextByte + for i <- 0 until paramCount do + val name = pool.getName(in.nextChar) + val flags = in.nextChar + if (flags & JAVA_ACC_SYNTHETIC) == 0 then + res.namedParams += (i -> name.name) + case tpnme.AnnotationDefaultATTR => sym.addAnnotation(Annotation(defn.AnnotationDefaultAnnot, Nil)) From e63752b6062c5e2a28a9d4f463b77e226227b6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 Feb 2021 19:11:53 +0100 Subject: [PATCH 3/3] Add test for named parameters from compiled classfile --- .../dotty/tools/dotc/CompilationTests.scala | 1 + .../test/dotty/tools/vulpix/TestFlags.scala | 18 +++++++++++------- tests/pos-special/java-param-names/Foo_1.java | 4 ++++ .../pos-special/java-param-names/Test_2.scala | 2 ++ 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 tests/pos-special/java-param-names/Foo_1.java create mode 100644 tests/pos-special/java-param-names/Test_2.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 6aece6004d05..6e0bd47576a3 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -42,6 +42,7 @@ class CompilationTests { compileFilesInDir("tests/pos-custom-args/erased", defaultOptions.and("-Yerased-terms")), compileFilesInDir("tests/pos", defaultOptions), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), + compileDir("tests/pos-special/java-param-names", defaultOptions.withJavacOnlyOptions("-parameters")), compileFile( // succeeds despite -Xfatal-warnings because of -nowarn "tests/neg-custom-args/fatal-warnings/xfatalWarnings.scala", diff --git a/compiler/test/dotty/tools/vulpix/TestFlags.scala b/compiler/test/dotty/tools/vulpix/TestFlags.scala index 0037d5f551fe..b6726e4b8862 100644 --- a/compiler/test/dotty/tools/vulpix/TestFlags.scala +++ b/compiler/test/dotty/tools/vulpix/TestFlags.scala @@ -5,19 +5,23 @@ import java.io.{File => JFile} final case class TestFlags( defaultClassPath: String, runClassPath: String, // class path that is used when running `run` tests (not compiling) - options: Array[String]) { + options: Array[String], + javacOptions: Array[String]) { def and(flags: String*): TestFlags = - TestFlags(defaultClassPath, runClassPath, options ++ flags) + TestFlags(defaultClassPath, runClassPath, options ++ flags, javacOptions) def without(flags: String*): TestFlags = - TestFlags(defaultClassPath, runClassPath, options diff flags) + TestFlags(defaultClassPath, runClassPath, options diff flags, javacOptions) def withClasspath(classPath: String): TestFlags = - TestFlags(s"$defaultClassPath${JFile.pathSeparator}$classPath", runClassPath, options) + TestFlags(s"$defaultClassPath${JFile.pathSeparator}$classPath", runClassPath, options, javacOptions) def withRunClasspath(classPath: String): TestFlags = - TestFlags(defaultClassPath, s"$runClassPath${JFile.pathSeparator}$classPath", options) + TestFlags(defaultClassPath, s"$runClassPath${JFile.pathSeparator}$classPath", options, javacOptions) + + def withJavacOnlyOptions(flags: String*): TestFlags = + TestFlags(defaultClassPath, runClassPath, options, javacOptions ++ flags) def all: Array[String] = Array("-classpath", defaultClassPath) ++ options @@ -43,10 +47,10 @@ final case class TestFlags( val flags = all val cp = flags.dropWhile(_ != "-classpath").take(2) val output = flags.dropWhile(_ != "-d").take(2) - cp ++ output + cp ++ output ++ javacOptions } } object TestFlags { - def apply(classPath: String, flags: Array[String]): TestFlags = TestFlags(classPath, classPath, flags) + def apply(classPath: String, flags: Array[String]): TestFlags = TestFlags(classPath, classPath, flags, Array.empty) } diff --git a/tests/pos-special/java-param-names/Foo_1.java b/tests/pos-special/java-param-names/Foo_1.java new file mode 100644 index 000000000000..abb04bf5a998 --- /dev/null +++ b/tests/pos-special/java-param-names/Foo_1.java @@ -0,0 +1,4 @@ +public class Foo_1 { + public Foo_1(int number, String text) {} + public void bar(String barText, int barNumber) {} +} diff --git a/tests/pos-special/java-param-names/Test_2.scala b/tests/pos-special/java-param-names/Test_2.scala new file mode 100644 index 000000000000..d4606f340691 --- /dev/null +++ b/tests/pos-special/java-param-names/Test_2.scala @@ -0,0 +1,2 @@ +@main def Test = + new Foo_1(number = 6, text = "param-name").bar(barNumber = 8, barText = "some-method") \ No newline at end of file