When reading MethodParameters attributes, javac reads the names of all provided parameters including 'mandated' parameters (e.g. the enclosing instance parameter of inner classes) [1]. When setting the parameters names of a MethodSymbol it only skips over enclosing instance parameters if the names were inferred from the local variable table, and not if they were read from the MethodParameters attribute [2].
The result is that mandated parameters names read from the MethodParameter attribute are used for non-mandated parameters, and the real names are offset.
[1] http://hg.openjdk.java.net/jdk9/dev/langtools/file/ee787e34231d/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java#l1288
[2] http://hg.openjdk.java.net/jdk9/dev/langtools/file/ee787e34231d/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java#l2476
Skipping over mandated parameter names in MethodParameter attributes avoids this issue:
diff -r 9b2de82e7e49 src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Feb 15 16:18:18 2017 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Thu Mar 23 15:22:23 2017 -0700
@@ -1285,10 +1285,14 @@
int numEntries = nextByte();
parameterNameIndices = new int[numEntries];
haveParameterNameIndices = true;
+ int index = 0;
for (int i = 0; i < numEntries; i++) {
int nameIndex = nextChar();
int flags = nextChar();
- parameterNameIndices[i] = nameIndex;
+ if ((flags & Flags.MANDATED) == Flags.MANDATED) {
+ continue;
+ }
+ parameterNameIndices[index++] = nameIndex;
}
}
bp = newbp;
Repro:
=== ./plugin/module-info.java
module parameterplugin {
requires transitive jdk.compiler;
provides com.sun.source.util.Plugin with parameterplugin.ParameterPlugin;
}
=== ./plugin/parameterplugin/ParameterPlugin.java
package parameterplugin;
import static java.util.stream.Collectors.joining;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import javax.lang.model.element.*;
public class ParameterPlugin implements Plugin {
@Override
public String getName() {
return "ParameterPlugin";
}
@Override
public void init(JavacTask javacTask, String... strings) {
javacTask.addTaskListener(
new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getKind() != TaskEvent.Kind.ENTER) {
return;
}
new TreePathScanner<Void, Void>() {
@Override
public Void visitNewClass(NewClassTree node, Void aVoid) {
ExecutableElement e =
(ExecutableElement) Trees.instance(javacTask).getElement(getCurrentPath());
System.err.println(
e.getParameters()
.stream()
.map(p -> p.asType() + " " + p.getSimpleName())
.collect(joining(", ")));
return null;
}
}.scan(new TreePath(e.getCompilationUnit()), null);
}
});
}
}
=== ./A.java
class A {
class I {
I(int a, int b) {}
}
}
=== ./B.java
class B {
void f(A a) {
a.new I(1, 2);
}
}
===
Actual:
$ javac -parameters $(find . -name "*.java")
$ javac --processor-module-path plugin -Xplugin:Parameter
Plugin B.java -sourcepath : -parameters
int this$0, int a
Expected:
int a, int b
The result is that mandated parameters names read from the MethodParameter attribute are used for non-mandated parameters, and the real names are offset.
[1] http://hg.openjdk.java.net/jdk9/dev/langtools/file/ee787e34231d/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java#l1288
[2] http://hg.openjdk.java.net/jdk9/dev/langtools/file/ee787e34231d/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java#l2476
Skipping over mandated parameter names in MethodParameter attributes avoids this issue:
diff -r 9b2de82e7e49 src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Feb 15 16:18:18 2017 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Thu Mar 23 15:22:23 2017 -0700
@@ -1285,10 +1285,14 @@
int numEntries = nextByte();
parameterNameIndices = new int[numEntries];
haveParameterNameIndices = true;
+ int index = 0;
for (int i = 0; i < numEntries; i++) {
int nameIndex = nextChar();
int flags = nextChar();
- parameterNameIndices[i] = nameIndex;
+ if ((flags & Flags.MANDATED) == Flags.MANDATED) {
+ continue;
+ }
+ parameterNameIndices[index++] = nameIndex;
}
}
bp = newbp;
Repro:
=== ./plugin/module-info.java
module parameterplugin {
requires transitive jdk.compiler;
provides com.sun.source.util.Plugin with parameterplugin.ParameterPlugin;
}
=== ./plugin/parameterplugin/ParameterPlugin.java
package parameterplugin;
import static java.util.stream.Collectors.joining;
import com.sun.source.tree.*;
import com.sun.source.util.*;
import javax.lang.model.element.*;
public class ParameterPlugin implements Plugin {
@Override
public String getName() {
return "ParameterPlugin";
}
@Override
public void init(JavacTask javacTask, String... strings) {
javacTask.addTaskListener(
new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getKind() != TaskEvent.Kind.ENTER) {
return;
}
new TreePathScanner<Void, Void>() {
@Override
public Void visitNewClass(NewClassTree node, Void aVoid) {
ExecutableElement e =
(ExecutableElement) Trees.instance(javacTask).getElement(getCurrentPath());
System.err.println(
e.getParameters()
.stream()
.map(p -> p.asType() + " " + p.getSimpleName())
.collect(joining(", ")));
return null;
}
}.scan(new TreePath(e.getCompilationUnit()), null);
}
});
}
}
=== ./A.java
class A {
class I {
I(int a, int b) {}
}
}
=== ./B.java
class B {
void f(A a) {
a.new I(1, 2);
}
}
===
Actual:
$ javac -parameters $(find . -name "*.java")
$ javac --processor-module-path plugin -Xplugin:Parameter
Plugin B.java -sourcepath : -parameters
int this$0, int a
Expected:
int a, int b