Skip to content

Commit bbd890b

Browse files
committed
Merge pull request #4735 from soc/SI-9437
SI-9437 Emit and support parameter names in class files
2 parents ef77a54 + c78d771 commit bbd890b

File tree

11 files changed

+194
-6
lines changed

11 files changed

+194
-6
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,17 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
841841
}
842842
}
843843

844+
/*
845+
* must-single-thread
846+
*/
847+
def emitParamNames(jmethod: asm.MethodVisitor, params: List[Symbol]) = {
848+
for (param <- params) {
849+
var access = asm.Opcodes.ACC_FINAL
850+
if (param.isArtifact)
851+
access |= asm.Opcodes.ACC_SYNTHETIC
852+
jmethod.visitParameter(param.name.decoded, access)
853+
}
854+
}
844855
} // end of trait BCAnnotGen
845856

846857
trait BCJGenSigGen {

src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
512512
/*
513513
* must-single-thread
514514
*/
515-
def initJMethod(flags: Int, paramAnnotations: List[List[AnnotationInfo]]) {
515+
def initJMethod(flags: Int, params: List[Symbol]) {
516516

517517
val jgensig = getGenericSignature(methSymbol, claszSymbol)
518518
addRemoteExceptionAnnot(isCZRemote, hasPublicBitSet(flags), methSymbol)
@@ -532,10 +532,9 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
532532
mkArray(thrownExceptions)
533533
).asInstanceOf[asm.tree.MethodNode]
534534

535-
// TODO param names: (m.params map (p => javaName(p.sym)))
536-
535+
emitParamNames(mnode, params)
537536
emitAnnotations(mnode, others)
538-
emitParamAnnotations(mnode, paramAnnotations)
537+
emitParamAnnotations(mnode, params.map(_.annotations))
539538

540539
} // end of method initJMethod
541540

@@ -574,8 +573,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
574573
if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes
575574
)
576575

577-
// TODO needed? for(ann <- m.symbol.annotations) { ann.symbol.initialize }
578-
initJMethod(flags, params.map(p => p.symbol.annotations))
576+
initJMethod(flags, params.map(_.symbol))
579577

580578
/* Add method-local vars for LabelDef-params.
581579
*

src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,23 @@ abstract class ClassfileParser {
816816
val c1 = convertTo(c, symtype)
817817
if (c1 ne null) sym.setInfo(ConstantType(c1))
818818
else devWarning(s"failure to convert $c to $symtype")
819+
case tpnme.MethodParametersATTR =>
820+
def readParamNames(): Unit = {
821+
import tools.asm.Opcodes.ACC_SYNTHETIC
822+
val paramCount = u1
823+
var i = 0
824+
while (i < paramCount) {
825+
val name = pool.getName(u2)
826+
val access = u2
827+
if ((access & ACC_SYNTHETIC) != ACC_SYNTHETIC) { // name not synthetic
828+
val params = sym.paramss.head // Java only has exactly one parameter list
829+
params(i).name = name.encode
830+
params(i).resetFlag(SYNTHETIC)
831+
}
832+
i += 1
833+
}
834+
}
835+
readParamNames()
819836
case tpnme.ScalaSignatureATTR =>
820837
if (!isScalaAnnot) {
821838
devWarning(s"symbol ${sym.fullName} has pickled signature in attribute")

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ trait StdNames {
291291
final val DeprecatedATTR: NameType = "Deprecated"
292292
final val ExceptionsATTR: NameType = "Exceptions"
293293
final val InnerClassesATTR: NameType = "InnerClasses"
294+
final val MethodParametersATTR: NameType = "MethodParameters"
294295
final val RuntimeAnnotationATTR: NameType = "RuntimeVisibleAnnotations" // RetentionPolicy.RUNTIME
295296
final val ScalaATTR: NameType = "Scala"
296297
final val ScalaSignatureATTR: NameType = "ScalaSig"

test/files/run/t9437a.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: a; isNamePresent: true; isSynthetic: false
2+
name: _; isNamePresent: true; isSynthetic: false
3+
name: ***; isNamePresent: true; isSynthetic: false
4+
name: unary_!; isNamePresent: true; isSynthetic: false
5+
name: ABC; isNamePresent: true; isSynthetic: false
6+
name: a; isNamePresent: true; isSynthetic: false
7+
name: _; isNamePresent: true; isSynthetic: false
8+
name: ***; isNamePresent: true; isSynthetic: false
9+
name: unary_!; isNamePresent: true; isSynthetic: false
10+
name: ABC; isNamePresent: true; isSynthetic: false

test/files/run/t9437a/Test.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class Foo(a: Int, `_`: String, *** : Long, `unary_!` : Float, ABC: Double) {
2+
def bar(a: Int, `_`: String, *** : Long, `unary_!` : Float, ABC: Double) = null
3+
}
4+
5+
object Test extends App {
6+
val constrParams = classOf[Foo].getConstructors.head.getParameters
7+
val methodParams = classOf[Foo].getDeclaredMethods.head.getParameters
8+
9+
def printParams(params: Array[java.lang.reflect.Parameter]) = {
10+
params.foreach { param =>
11+
println(s"name: ${param.getName}; isNamePresent: ${param.isNamePresent}; isSynthetic: ${param.isSynthetic}")
12+
}
13+
}
14+
15+
printParams(constrParams)
16+
printParams(methodParams)
17+
18+
val foo = new Foo(a = 1, `_` = "2", *** = 3L, `unary_!` = 4.0f, ABC = 5.0)
19+
foo.bar(a = 1, `_` = "2", *** = 3L, `unary_!` = 4.0f, ABC = 5.0)
20+
}

test/files/run/t9437b.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: a; isNamePresent: true; isSynthetic: false
2+
name: _; isNamePresent: true; isSynthetic: false
3+
name: ***; isNamePresent: true; isSynthetic: false
4+
name: unary_!; isNamePresent: true; isSynthetic: false
5+
name: ABC; isNamePresent: true; isSynthetic: false
6+
name: a; isNamePresent: true; isSynthetic: false
7+
name: _; isNamePresent: true; isSynthetic: false
8+
name: ***; isNamePresent: true; isSynthetic: false
9+
name: unary_!; isNamePresent: true; isSynthetic: false
10+
name: ABC; isNamePresent: true; isSynthetic: false

test/files/run/t9437b/Foo_1.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Foo(a: Int, `_`: String, *** : Long, `unary_!` : Float, ABC: Double) {
2+
def bar(a: Int, `_`: String, *** : Long, `unary_!` : Float, ABC: Double) = null
3+
}

test/files/run/t9437b/Test_2.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
object Test extends App {
2+
val constrParams = classOf[Foo].getConstructors.head.getParameters
3+
val methodParams = classOf[Foo].getDeclaredMethods.head.getParameters
4+
5+
def printParams(params: Array[java.lang.reflect.Parameter]) = {
6+
params.foreach { param =>
7+
println(s"name: ${param.getName}; isNamePresent: ${param.isNamePresent}; isSynthetic: ${param.isSynthetic}")
8+
}
9+
}
10+
11+
printParams(constrParams)
12+
printParams(methodParams)
13+
14+
val foo = new Foo(a = 1, `_` = "2", *** = 3L, `unary_!` = 4.0f, ABC = 5.0)
15+
foo.bar(a = 1, `_` = "2", *** = 3L, `unary_!` = 4.0f, ABC = 5.0)
16+
}

test/files/run/t9437c.check

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: a; isNamePresent: true; isSynthetic: false
2+
name: _; isNamePresent: true; isSynthetic: false
3+
name: ***; isNamePresent: true; isSynthetic: false
4+
name: unary_!; isNamePresent: true; isSynthetic: false
5+
name: ABC; isNamePresent: true; isSynthetic: false
6+
name: a; isNamePresent: true; isSynthetic: false
7+
name: _; isNamePresent: true; isSynthetic: false
8+
name: ***; isNamePresent: true; isSynthetic: false
9+
name: unary_!; isNamePresent: true; isSynthetic: false
10+
name: ABC; isNamePresent: true; isSynthetic: false

test/files/run/t9437c/Test.scala

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import java.io.{File, FileOutputStream}
2+
3+
import scala.tools.nsc.settings.ScalaVersion
4+
import scala.tools.partest._
5+
import scala.tools.asm
6+
import asm.{AnnotationVisitor, ClassWriter, FieldVisitor, Handle, MethodVisitor, Opcodes}
7+
import Opcodes._
8+
9+
// This test ensures that we can read JDK 8 (classfile format 52) files with
10+
// parameter names. To do that it first uses ASM to generate a class containing
11+
// these additional attributes. Then it runs a normal compile on Scala source
12+
// that uses the class with named arguments.
13+
// Any failure will be dumped to std out.
14+
object Test extends DirectTest {
15+
override def extraSettings: String = "-usejavacp -d " + testOutput.path + " -cp " + testOutput.path
16+
17+
def generateCode(): Unit = {
18+
val className = "Foo"
19+
20+
val cw = new ClassWriter(0)
21+
cw.visit(52, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", null);
22+
23+
val mvC = cw.visitMethod(ACC_PUBLIC, "<init>", "(ILjava/lang/String;JFD)V", null, null);
24+
mvC.visitParameter("a", ACC_FINAL);
25+
mvC.visitParameter("_", ACC_FINAL);
26+
mvC.visitParameter("***", ACC_FINAL);
27+
mvC.visitParameter("unary_!", ACC_FINAL);
28+
mvC.visitParameter("ABC", ACC_FINAL);
29+
mvC.visitCode();
30+
mvC.visitVarInsn(ALOAD, 0);
31+
mvC.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
32+
mvC.visitInsn(RETURN);
33+
mvC.visitMaxs(1, 8);
34+
mvC.visitEnd();
35+
36+
val mvM = cw.visitMethod(ACC_PUBLIC, "bar", "(ILjava/lang/String;JFD)Lscala/runtime/Null$;", null, null);
37+
mvM.visitParameter("a", ACC_FINAL);
38+
mvM.visitParameter("_", ACC_FINAL);
39+
mvM.visitParameter("***", ACC_FINAL);
40+
mvM.visitParameter("unary_!", ACC_FINAL);
41+
mvM.visitParameter("ABC", ACC_FINAL);
42+
mvM.visitCode();
43+
mvM.visitInsn(ACONST_NULL);
44+
mvM.visitInsn(ARETURN);
45+
mvM.visitMaxs(1, 8);
46+
mvM.visitEnd();
47+
48+
cw.visitEnd();
49+
50+
val bytes = cw.toByteArray()
51+
52+
val fos = new FileOutputStream(new File(s"${testOutput.path}/$className.class"))
53+
try
54+
fos write bytes
55+
finally
56+
fos.close()
57+
58+
}
59+
60+
def code =
61+
"""
62+
class Driver {
63+
val constrParams = classOf[Foo].getConstructors.head.getParameters
64+
val methodParams = classOf[Foo].getDeclaredMethods.head.getParameters
65+
66+
def printParams(params: Array[java.lang.reflect.Parameter]) = {
67+
params.foreach { param =>
68+
println(s"name: ${param.getName}; isNamePresent: ${param.isNamePresent}; isSynthetic: ${param.isSynthetic}")
69+
}
70+
}
71+
72+
printParams(constrParams)
73+
printParams(methodParams)
74+
75+
val foo = new Foo(a = 1, `_` = "2", *** = 3L, `unary_!` = 4.0f, ABC = 5.0)
76+
foo.bar(a = 1, `_` = "2", *** = 3L, `unary_!` = 4.0f, ABC = 5.0)
77+
}
78+
"""
79+
80+
override def show(): Unit = {
81+
// redirect err to out, for logging
82+
val prevErr = System.err
83+
System.setErr(System.out)
84+
try {
85+
generateCode()
86+
compile()
87+
Class.forName("Driver").newInstance()
88+
}
89+
finally
90+
System.setErr(prevErr)
91+
}
92+
}

0 commit comments

Comments
 (0)