Skip to content

Commit ddecb64

Browse files
authored
Merge pull request #3 from Pokechu22/parameter-annotations
Add ParameterAnnotationFixer
2 parents 5ab530d + cd5f231 commit ddecb64

File tree

4 files changed

+233
-3
lines changed

4 files changed

+233
-3
lines changed

build.gradle

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ repositories {
2424

2525
jar {
2626
manifest.attributes('Main-Class': 'de.oceanlabs.mcp.mcinjector.MCInjector')
27+
manifest.attributes('Implementation-Version': project.version)
2728
}
2829
shadowJar {
2930
classifier 'fatjar'
3031
manifest.attributes('Main-Class': 'de.oceanlabs.mcp.mcinjector.MCInjector')
32+
manifest.attributes('Implementation-Version': project.version)
3133
}
3234

3335
artifacts {
@@ -36,8 +38,8 @@ artifacts {
3638
}
3739

3840
dependencies {
39-
compile 'org.ow2.asm:asm:6.0'
40-
compile 'org.ow2.asm:asm-tree:6.0'
41+
compile 'org.ow2.asm:asm:6.1-beta2'
42+
compile 'org.ow2.asm:asm-tree:6.1-beta2'
4143
compile 'net.sf.jopt-simple:jopt-simple:4.5'
4244
compile 'com.google.code.gson:gson:2.2.4'
4345
}

src/main/java/de/oceanlabs/mcp/mcinjector/MCInjector.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
public class MCInjector
1313
{
1414
private final static Logger log = Logger.getLogger("MCInjector");
15-
public static final String VERSION = "MCInjector v3.4 by Searge, LexManos, Fesh0r";
15+
public static final String VERSION;
16+
static {
17+
String implVersion = MCInjector.class.getPackage().getImplementationVersion();
18+
if (implVersion == null) {
19+
implVersion = "Unknown";
20+
}
21+
VERSION = "MCInjector v" + implVersion + " by Searge, LexManos, Fesh0r";
22+
}
1623

1724
public static void main(String[] args) throws Exception
1825
{

src/main/java/de/oceanlabs/mcp/mcinjector/MCInjectorImpl.java

+3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import de.oceanlabs.mcp.mcinjector.adaptors.LVTFernflower;
4949
import de.oceanlabs.mcp.mcinjector.adaptors.LVTLvt;
5050
import de.oceanlabs.mcp.mcinjector.adaptors.LVTStrip;
51+
import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer;
5152
import de.oceanlabs.mcp.mcinjector.adaptors.ReadMarker;
5253

5354
public class MCInjectorImpl
@@ -638,6 +639,8 @@ public byte[] processClass(byte[] cls, boolean readOnly)
638639
}
639640

640641
ca = new AccessFixer(ca, this);
642+
643+
ca = new ParameterAnnotationFixer(ca, this);
641644
}
642645
ca = new AccessReader(ca, this);
643646

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package de.oceanlabs.mcp.mcinjector.adaptors;
2+
3+
import static org.objectweb.asm.Opcodes.*;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
import java.util.logging.Logger;
8+
9+
import org.objectweb.asm.ClassVisitor;
10+
import org.objectweb.asm.Opcodes;
11+
import org.objectweb.asm.Type;
12+
import org.objectweb.asm.tree.AnnotationNode;
13+
import org.objectweb.asm.tree.ClassNode;
14+
import org.objectweb.asm.tree.InnerClassNode;
15+
import org.objectweb.asm.tree.MethodNode;
16+
17+
import de.oceanlabs.mcp.mcinjector.MCInjectorImpl;
18+
19+
public class ParameterAnnotationFixer extends ClassVisitor
20+
{
21+
private static final Logger LOGGER = Logger.getLogger("MCInjector");
22+
23+
public ParameterAnnotationFixer(ClassVisitor cn, MCInjectorImpl mci)
24+
{
25+
super(Opcodes.ASM6, cn);
26+
// Extra version check, since these were added in ASM 6.1 and there
27+
// isn't a constant for it
28+
try {
29+
MethodNode.class.getField("visibleAnnotableParameterCount");
30+
MethodNode.class.getField("invisibleAnnotableParameterCount");
31+
} catch (Exception ex) {
32+
throw new IllegalArgumentException(
33+
"AnnotableParameterCount fields are not present -- wrong ASM version?",
34+
ex);
35+
}
36+
}
37+
38+
@Override
39+
public void visitEnd()
40+
{
41+
super.visitEnd();
42+
43+
ClassNode cls = MCInjectorImpl.getClassNode(cv);
44+
Type[] syntheticParams = getExpectedSyntheticParams(cls);
45+
if (syntheticParams != null)
46+
{
47+
for (MethodNode mn : cls.methods)
48+
{
49+
if (mn.name.equals("<init>"))
50+
{
51+
processConstructor(cls, mn, syntheticParams);
52+
}
53+
}
54+
}
55+
}
56+
57+
/**
58+
* Checks if the given class might have synthetic parameters in the
59+
* constructor. There are two cases where this might happen:
60+
* <ol>
61+
* <li>If the given class is an inner class, the first parameter is the
62+
* instance of the outer class.</li>
63+
* <li>If the given class is an enum, the first parameter is the enum
64+
* constant name and the second parameter is its ordinal.</li>
65+
* </ol>
66+
*
67+
* @return An array of types for synthetic parameters if the class can have
68+
* synthetic parameters, otherwise null.
69+
*/
70+
private Type[] getExpectedSyntheticParams(ClassNode cls)
71+
{
72+
// Check for enum
73+
// http://hg.openjdk.java.net/jdk8/jdk8/langtools/file/1ff9d5118aae/src/share/classes/com/sun/tools/javac/comp/Lower.java#l2866
74+
if ((cls.access & ACC_ENUM) != 0)
75+
{
76+
LOGGER.fine(" Considering " + cls.name
77+
+ " for extra parameter annotations as it is an enum");
78+
return new Type[] { Type.getObjectType("java/lang/String"), Type.INT_TYPE };
79+
}
80+
81+
// Check for inner class
82+
InnerClassNode info = null;
83+
for (InnerClassNode node : cls.innerClasses) // note: cls.innerClasses is never null
84+
{
85+
if (node.name.equals(cls.name))
86+
{
87+
info = node;
88+
break;
89+
}
90+
}
91+
// http://hg.openjdk.java.net/jdk8/jdk8/langtools/file/1ff9d5118aae/src/share/classes/com/sun/tools/javac/code/Symbol.java#l398
92+
if (info == null)
93+
{
94+
LOGGER.fine(" Not considering " + cls.name
95+
+ " for extra parameter annotations as it is not an inner class");
96+
return null; // It's not an inner class
97+
}
98+
if ((info.access & (ACC_STATIC | ACC_INTERFACE)) != 0)
99+
{
100+
LOGGER.fine(" Not considering " + cls.name
101+
+ " for extra parameter annotations as is an interface or static");
102+
return null; // It's static or can't have a constructor
103+
}
104+
105+
// http://hg.openjdk.java.net/jdk8/jdk8/langtools/file/1ff9d5118aae/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java#l2011
106+
if (info.innerName == null)
107+
{
108+
LOGGER.fine(" Not considering " + cls.name
109+
+ " for extra parameter annotations as it is annonymous");
110+
return null; // It's an anonymous class
111+
}
112+
113+
LOGGER.fine(" Considering " + cls.name
114+
+ " for extra parameter annotations as it is an inner class of "
115+
+ info.outerName);
116+
117+
return new Type[] { Type.getObjectType(info.outerName) };
118+
}
119+
120+
/**
121+
* Removes the parameter annotations for the given synthetic parameters,
122+
* if there are parameter annotations and the synthetic parameters exist.
123+
*/
124+
private void processConstructor(ClassNode cls, MethodNode mn, Type[] syntheticParams) {
125+
String methodInfo = mn.name + mn.desc + " in " + cls.name;
126+
Type[] params = Type.getArgumentTypes(mn.desc);
127+
128+
if (beginsWith(params, syntheticParams))
129+
{
130+
mn.visibleParameterAnnotations = process(methodInfo,
131+
"RuntimeVisibleParameterAnnotations", params.length,
132+
syntheticParams.length, mn.visibleParameterAnnotations);
133+
mn.invisibleParameterAnnotations = process(methodInfo,
134+
"RuntimeInvisibleParameterAnnotations", params.length,
135+
syntheticParams.length, mn.invisibleParameterAnnotations);
136+
// ASM uses this value, not the length of the array
137+
// Note that this was added in ASM 6.1
138+
if (mn.visibleParameterAnnotations != null)
139+
{
140+
mn.visibleAnnotableParameterCount = mn.visibleParameterAnnotations.length;
141+
}
142+
if (mn.invisibleParameterAnnotations != null)
143+
{
144+
mn.invisibleAnnotableParameterCount = mn.invisibleParameterAnnotations.length;
145+
}
146+
}
147+
else
148+
{
149+
LOGGER.warning("Unexpected lack of synthetic args to the constructor: expected "
150+
+ Arrays.toString(syntheticParams) + " at the start of " + methodInfo);
151+
}
152+
}
153+
154+
private boolean beginsWith(Type[] values, Type[] prefix)
155+
{
156+
if (values.length < prefix.length)
157+
{
158+
return false;
159+
}
160+
for (int i = 0; i < prefix.length; i++)
161+
{
162+
if (!values[i].equals(prefix[i]))
163+
{
164+
return false;
165+
}
166+
}
167+
return true;
168+
}
169+
170+
/**
171+
* Removes annotation nodes corresponding to synthetic parameters, after
172+
* the existence of synthetic parameters has already been checked.
173+
*
174+
* @param methodInfo
175+
* A description of the method, for logging
176+
* @param attributeName
177+
* The name of the attribute, for logging
178+
* @param numParams
179+
* The number of parameters in the method
180+
* @param numSynthetic
181+
* The number of synthetic parameters (should not be 0)
182+
* @param annotations
183+
* The current array of annotation nodes, may be null
184+
* @return The new array of annotation nodes, may be null
185+
*/
186+
private List<AnnotationNode>[] process(String methodInfo,
187+
String attributeName, int numParams, int numSynthetic,
188+
List<AnnotationNode>[] annotations)
189+
{
190+
if (annotations == null)
191+
{
192+
LOGGER.finer(" " + methodInfo + " does not have a "
193+
+ attributeName + " attribute");
194+
return null;
195+
}
196+
197+
int numAnnotations = annotations.length;
198+
if (numParams == numAnnotations)
199+
{
200+
LOGGER.info("Found extra " + attributeName + " entries in "
201+
+ methodInfo + ": removing " + numSynthetic);
202+
return Arrays.copyOfRange(annotations, numSynthetic,
203+
numAnnotations);
204+
}
205+
else if (numParams == numAnnotations - numSynthetic)
206+
{
207+
LOGGER.info("Number of " + attributeName + " entries in "
208+
+ methodInfo + " is already as we want");
209+
return annotations;
210+
}
211+
else
212+
{
213+
LOGGER.warning("Unexpected number of " + attributeName
214+
+ " entries in " + methodInfo + ": " + numAnnotations);
215+
return annotations;
216+
}
217+
}
218+
}

0 commit comments

Comments
 (0)