Skip to content

Commit acbeb99

Browse files
committed
Rewrite static ctors to set all fields to default value.
1 parent 61c9203 commit acbeb99

2 files changed

Lines changed: 70 additions & 1 deletion

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ org.ikvm:java-ref-plugin
1414
- Plugin class: `org.ikvm.javarefplugin.JavaRefPlugin`
1515
- Automatic module name: `org.ikvm.javarefplugin`
1616

17+
## Plugin behavior
18+
19+
The plugin rewrites method bodies to throw `NullPointerException` on invocation:
20+
21+
- **Instance methods**: replaced with `throw null`.
22+
- **Constructors**: preserved `this()/super()` chaining, then `throw null`.
23+
- **Static initializers**: replaced with minimal field assignments (sets static fields to type-appropriate defaults: `0`, `false`, or `null`). This drastically reduces bytecode size while ensuring the class loads correctly.
24+
- **Abstract/native methods**: bodies left unchanged (they have no bodies).
25+
- **Synthetic bridge methods**: rewritten like regular methods (bridges still exist as method symbols).
26+
1727
### Plugin arguments
1828

1929
- `ignorePackage=<packageName>`: skips rewriting for classes in the package and its subpackages. Repeat the argument to ignore multiple package roots. Ignored classes are logged as `javac` notices during compilation.

src/main/java/org/ikvm/javarefplugin/MethodBodyStripper.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,67 @@ void strip(Object compilationUnit) {
3030
public void visitClassDef(JCTree.JCClassDecl tree) {
3131
boolean previousInAnnotationType = inAnnotationType;
3232
inAnnotationType = (tree.mods.flags & Flags.ANNOTATION) != 0;
33+
3334
super.visitClassDef(tree);
35+
36+
// Rewrite static initializers to set fields to default values
37+
if (tree.defs != null && !inAnnotationType) {
38+
for (JCTree def : tree.defs) {
39+
if (def instanceof JCTree.JCBlock) {
40+
JCTree.JCBlock block = (JCTree.JCBlock) def;
41+
if ((block.flags & Flags.STATIC) != 0) {
42+
// Replace with minimal field assignments
43+
block.stats = generateDefaultFieldAssignments(tree);
44+
}
45+
}
46+
}
47+
}
48+
3449
inAnnotationType = previousInAnnotationType;
3550
result = tree;
3651
}
3752

53+
private List<JCTree.JCStatement> generateDefaultFieldAssignments(JCTree.JCClassDecl classTree) {
54+
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
55+
56+
if (classTree.defs == null) {
57+
return statements.toList();
58+
}
59+
60+
// Collect all static fields
61+
for (JCTree def : classTree.defs) {
62+
if (def instanceof JCTree.JCVariableDecl) {
63+
JCTree.JCVariableDecl varDecl = (JCTree.JCVariableDecl) def;
64+
if ((varDecl.mods.flags & Flags.STATIC) != 0) {
65+
// Generate assignment: field = defaultValue;
66+
JCTree.JCExpression defaultValue = generateDefaultValue(varDecl.vartype);
67+
JCTree.JCAssign assignment = maker.Assign(
68+
maker.Ident(varDecl.name),
69+
defaultValue
70+
);
71+
statements.append(maker.Exec(assignment));
72+
}
73+
}
74+
}
75+
76+
return statements.toList();
77+
}
78+
79+
private JCTree.JCExpression generateDefaultValue(JCTree.JCExpression typeExpr) {
80+
// For simplicity: 0 for numeric types, false for boolean, null for everything else
81+
String typeStr = typeExpr.toString();
82+
if ("int".equals(typeStr) || "byte".equals(typeStr) || "short".equals(typeStr) || "long".equals(typeStr)
83+
|| "float".equals(typeStr) || "double".equals(typeStr)) {
84+
return maker.Literal(TypeTag.INT, 0);
85+
} else if ("boolean".equals(typeStr)) {
86+
return maker.Literal(TypeTag.BOOLEAN, Boolean.FALSE);
87+
} else if ("char".equals(typeStr)) {
88+
return maker.Literal(TypeTag.CHAR, 0);
89+
}
90+
// Reference type or unknown: null
91+
return maker.Literal(TypeTag.BOT, null);
92+
}
93+
3894
@Override
3995
public void visitMethodDef(JCTree.JCMethodDecl tree) {
4096
super.visitMethodDef(tree);
@@ -57,6 +113,10 @@ private boolean isConstructor(JCTree.JCMethodDecl tree) {
57113
return tree.name == names.init;
58114
}
59115

116+
private boolean isClassInitializer(JCTree.JCMethodDecl tree) {
117+
return tree.name == names.clinit;
118+
}
119+
60120

61121
private List<JCTree.JCStatement> replacementStatements(JCTree.JCMethodDecl tree) {
62122
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
@@ -106,4 +166,3 @@ private JCTree.JCStatement findConstructorInvocation(List<JCTree.JCStatement> st
106166
}
107167

108168
}
109-

0 commit comments

Comments
 (0)