Skip to content

Output names of function parameters #11515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
}
}

Expand Down Expand Up @@ -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))

Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
18 changes: 11 additions & 7 deletions compiler/test/dotty/tools/vulpix/TestFlags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
}
4 changes: 4 additions & 0 deletions tests/pos-special/java-param-names/Foo_1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public class Foo_1 {
public Foo_1(int number, String text) {}
public void bar(String barText, int barNumber) {}
}
2 changes: 2 additions & 0 deletions tests/pos-special/java-param-names/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@main def Test =
new Foo_1(number = 6, text = "param-name").bar(barNumber = 8, barText = "some-method")
3 changes: 3 additions & 0 deletions tests/run/i11486/Old_1.scala
Original file line number Diff line number Diff line change
@@ -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) = ()
21 changes: 21 additions & 0 deletions tests/run/i11486/Test_2.scala
Original file line number Diff line number Diff line change
@@ -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])