Skip to content

Commit 9297c01

Browse files
committed
Optimized static method invocations
1 parent b58ad6b commit 9297c01

4 files changed

Lines changed: 248 additions & 22 deletions

File tree

vm/ByteCodeTranslator/src/com/codename1/tools/translator/JavascriptMethodGenerator.java

Lines changed: 137 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
192192
List<Instruction> instructions = method.getInstructions();
193193
Map<Label, Integer> labelToIndex = buildLabelMap(instructions);
194194
String jsMethodName = jsMethodIdentifier(cls, method);
195-
out.append("function* ").append(jsMethodName).append("(");
195+
String jsMethodBodyName = jsMethodBodyIdentifier(cls, method);
196+
boolean wrappedStaticMethod = isWrappedStaticMethod(method);
197+
out.append("function* ").append(wrappedStaticMethod ? jsMethodBodyName : jsMethodName).append("(");
196198
boolean first = true;
197199
if (!method.isStatic()) {
198200
out.append("__cn1ThisObject");
@@ -207,15 +209,25 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
207209
out.append("__cn1Arg").append(i + 1);
208210
}
209211
out.append("){\n");
210-
if (method.isStatic() && !"__CLINIT__".equals(method.getMethodName())) {
212+
if (!wrappedStaticMethod && method.isStatic() && !"__CLINIT__".equals(method.getMethodName())) {
211213
out.append(" jvm.ensureClassInitialized(\"").append(cls.getClsName()).append("\");\n");
212214
}
213-
if (appendStraightLineMethodBody(out, cls, method, instructions, jsMethodName)) {
215+
if (appendStraightLineMethodBody(out, cls, method, instructions, wrappedStaticMethod ? jsMethodBodyName : jsMethodName)) {
216+
if (wrappedStaticMethod) {
217+
appendWrappedStaticMethod(out, cls, method, jsMethodName, jsMethodBodyName);
218+
}
214219
return;
215220
}
221+
boolean usesClassInitCache = hasClassInitSensitiveAccess(instructions);
216222
out.append(" const locals = new Array(").append(Math.max(1, method.getMaxLocals())).append(").fill(null);\n");
217223
out.append(" const stack = [];\n");
218224
out.append(" let pc = 0;\n");
225+
if (usesClassInitCache) {
226+
out.append(" const __cn1Init = Object.create(null);\n");
227+
if (method.isStatic() && !"__CLINIT__".equals(method.getMethodName())) {
228+
out.append(" __cn1Init[\"").append(cls.getClsName()).append("\"] = true;\n");
229+
}
230+
}
219231
if (!method.isStatic()) {
220232
out.append(" locals[0] = __cn1ThisObject;\n");
221233
}
@@ -239,7 +251,7 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
239251
for (int i = 0; i < instructions.size(); i++) {
240252
Instruction instruction = instructions.get(i);
241253
out.append(" case ").append(i).append(": {\n");
242-
appendInstruction(out, method, instructions, labelToIndex, instruction, i);
254+
appendInstruction(out, method, instructions, labelToIndex, instruction, i, usesClassInitCache);
243255
out.append(" }\n");
244256
}
245257
out.append(" default:\n");
@@ -261,15 +273,66 @@ private static void appendMethod(StringBuilder out, ByteCodeClass cls, BytecodeM
261273
out.append(" }\n");
262274
}
263275
out.append("}\n");
276+
if (wrappedStaticMethod) {
277+
appendWrappedStaticMethod(out, cls, method, jsMethodName, jsMethodBodyName);
278+
}
264279
if ("__CLINIT__".equals(method.getMethodName())) {
265-
out.append("jvm.classes[\"").append(cls.getClsName()).append("\"].clinit = ").append(jsMethodName).append(";\n");
280+
out.append("jvm.classes[\"").append(cls.getClsName()).append("\"].clinit = ")
281+
.append(wrappedStaticMethod ? jsMethodBodyName : jsMethodName).append(";\n");
266282
}
267283
if (!method.isStatic() && !method.isConstructor()) {
268284
out.append("jvm.addVirtualMethod(\"").append(cls.getClsName()).append("\", \"")
269285
.append(jsMethodName).append("\", ").append(jsMethodName).append(");\n");
270286
}
271287
}
272288

289+
private static boolean isWrappedStaticMethod(BytecodeMethod method) {
290+
return method.isStatic() && !"__CLINIT__".equals(method.getMethodName());
291+
}
292+
293+
private static void appendWrappedStaticMethod(StringBuilder out, ByteCodeClass cls, BytecodeMethod method, String wrapperName, String bodyName) {
294+
out.append("function* ").append(wrapperName).append("(");
295+
appendMethodParameters(out, method);
296+
out.append("){\n");
297+
out.append(" jvm.ensureClassInitialized(\"").append(cls.getClsName()).append("\");\n");
298+
out.append(" return yield* ").append(bodyName).append("(");
299+
appendMethodParameterArguments(out, method);
300+
out.append(");\n");
301+
out.append("}\n");
302+
}
303+
304+
private static void appendMethodParameters(StringBuilder out, BytecodeMethod method) {
305+
boolean first = true;
306+
if (!method.isStatic()) {
307+
out.append("__cn1ThisObject");
308+
first = false;
309+
}
310+
List<ByteCodeMethodArg> arguments = method.getArguments();
311+
for (int i = 0; i < arguments.size(); i++) {
312+
if (!first) {
313+
out.append(", ");
314+
}
315+
first = false;
316+
out.append("__cn1Arg").append(i + 1);
317+
}
318+
}
319+
320+
private static void appendMethodParameterArguments(StringBuilder out, BytecodeMethod method) {
321+
boolean first = true;
322+
if (!method.isStatic()) {
323+
out.append("__cn1ThisObject");
324+
first = false;
325+
}
326+
List<ByteCodeMethodArg> arguments = method.getArguments();
327+
for (int i = 0; i < arguments.size(); i++) {
328+
if (!first) {
329+
out.append(", ");
330+
}
331+
first = false;
332+
out.append("__cn1Arg").append(i + 1);
333+
}
334+
}
335+
273336
private static boolean appendStraightLineMethodBody(StringBuilder out, ByteCodeClass cls, BytecodeMethod method,
274337
List<Instruction> instructions, String jsMethodName) {
275338
if (!isStraightLineEligible(method, instructions)) {
@@ -279,7 +342,7 @@ private static boolean appendStraightLineMethodBody(StringBuilder out, ByteCodeC
279342
StringBuilder instructionBody = new StringBuilder();
280343
StringBuilder body = new StringBuilder();
281344
StraightLineContext ctx = new StraightLineContext(method.getMaxLocals(), method.getMaxStack());
282-
if (method.isStatic() && !"__CLINIT__".equals(method.getMethodName())) {
345+
if (isWrappedStaticMethod(method)) {
283346
ctx.initializedClasses.add(cls.getClsName());
284347
}
285348
if (!method.isStatic()) {
@@ -787,7 +850,9 @@ private static void appendStraightLineEnsureClassInitialized(StringBuilder out,
787850
}
788851

789852
private static boolean appendStraightLineInvokeInstruction(StringBuilder out, Invoke invoke, StraightLineContext ctx) {
853+
String owner = JavascriptNameUtil.sanitizeClassName(invoke.getOwner());
790854
String methodId = JavascriptNameUtil.methodIdentifier(invoke.getOwner(), invoke.getName(), invoke.getDesc());
855+
String methodBodyId = jsStaticMethodBodyIdentifier(invoke.getOwner(), invoke.getName(), invoke.getDesc());
791856
List<String> args = JavascriptNameUtil.argumentTypes(invoke.getDesc());
792857
boolean hasReturn = invoke.getDesc().charAt(invoke.getDesc().length() - 1) != 'V';
793858
String[] argValues = new String[args.size()];
@@ -824,10 +889,16 @@ private static boolean appendStraightLineInvokeInstruction(StringBuilder out, In
824889
out.append(" }\n");
825890
return true;
826891
}
892+
if (invoke.getOpcode() == Opcodes.INVOKESTATIC) {
893+
appendStraightLineEnsureClassInitialized(out, ctx, owner);
894+
}
895+
String invokedName = invoke.getOpcode() == Opcodes.INVOKESTATIC
896+
? "(" + staticInvocationTargetExpression(methodId, methodBodyId) + ")"
897+
: methodId;
827898
if (hasReturn) {
828-
out.append(" { const __result = yield* ").append(methodId).append("(");
899+
out.append(" { const __result = yield* ").append(invokedName).append("(");
829900
} else {
830-
out.append(" { yield* ").append(methodId).append("(");
901+
out.append(" { yield* ").append(invokedName).append("(");
831902
}
832903
appendInvocationArgumentExpressions(out, target, argValues);
833904
out.append(");");
@@ -934,6 +1005,17 @@ private static String jsMethodIdentifier(ByteCodeClass cls, BytecodeMethod metho
9341005
return JavascriptNameUtil.methodIdentifier(cls.getClsName(), method.getMethodName(), method.getSignature());
9351006
}
9361007

1008+
private static String jsMethodBodyIdentifier(ByteCodeClass cls, BytecodeMethod method) {
1009+
if (isWrappedStaticMethod(method)) {
1010+
return jsStaticMethodBodyIdentifier(cls.getClsName(), method.getMethodName(), method.getSignature());
1011+
}
1012+
return jsMethodIdentifier(cls, method);
1013+
}
1014+
1015+
private static String jsStaticMethodBodyIdentifier(String owner, String name, String desc) {
1016+
return JavascriptNameUtil.methodIdentifier(owner, name, desc) + "__impl";
1017+
}
1018+
9371019
private static void appendNativeStubIfNeeded(StringBuilder out, ByteCodeClass cls, BytecodeMethod method) {
9381020
String jsMethodName = jsMethodIdentifier(cls, method);
9391021
JavascriptNativeRegistry.NativeCategory category = JavascriptNativeRegistry.categoryFor(jsMethodName);
@@ -997,7 +1079,7 @@ private static Map<Label, Integer> buildLabelMap(List<Instruction> instructions)
9971079
}
9981080

9991081
private static void appendInstruction(StringBuilder out, BytecodeMethod method, List<Instruction> allInstructions,
1000-
Map<Label, Integer> labelToIndex, Instruction instruction, int index) {
1082+
Map<Label, Integer> labelToIndex, Instruction instruction, int index, boolean usesClassInitCache) {
10011083
if (instruction instanceof LabelInstruction || instruction instanceof LineNumber || instruction instanceof LocalVariable
10021084
|| instruction instanceof TryCatch) {
10031085
out.append(" pc = ").append(index + 1).append("; break;\n");
@@ -1027,15 +1109,15 @@ private static void appendInstruction(StringBuilder out, BytecodeMethod method,
10271109
return;
10281110
}
10291111
if (instruction instanceof Field) {
1030-
appendFieldInstruction(out, (Field) instruction, index);
1112+
appendFieldInstruction(out, (Field) instruction, index, usesClassInitCache);
10311113
return;
10321114
}
10331115
if (instruction instanceof Jump) {
10341116
appendJumpInstruction(out, (Jump) instruction, labelToIndex, index);
10351117
return;
10361118
}
10371119
if (instruction instanceof Invoke) {
1038-
appendInvokeInstruction(out, (Invoke) instruction, index);
1120+
appendInvokeInstruction(out, (Invoke) instruction, index, usesClassInitCache);
10391121
return;
10401122
}
10411123
if (instruction instanceof SwitchInstruction) {
@@ -1483,20 +1565,20 @@ private static void appendTypeInstruction(StringBuilder out, TypeInstruction ins
14831565
}
14841566
}
14851567

1486-
private static void appendFieldInstruction(StringBuilder out, Field field, int index) {
1568+
private static void appendFieldInstruction(StringBuilder out, Field field, int index, boolean usesStaticFieldInitCache) {
14871569
String owner = JavascriptNameUtil.sanitizeClassName(field.getOwner());
14881570
String fieldName = field.getFieldName();
14891571
String propertyName = JavascriptNameUtil.fieldProperty(field.getOwner(), fieldName);
14901572
switch (field.getOpcode()) {
14911573
case Opcodes.GETSTATIC:
1492-
out.append(" jvm.ensureClassInitialized(\"").append(owner).append("\"); stack.push(jvm.classes[\"")
1493-
.append(owner).append("\"].staticFields[\"").append(fieldName).append("\"]); pc = ")
1494-
.append(index + 1).append("; break;\n");
1574+
appendInterpreterEnsureClassInitialized(out, owner, usesStaticFieldInitCache);
1575+
out.append(" stack.push(jvm.classes[\"").append(owner).append("\"].staticFields[\"")
1576+
.append(fieldName).append("\"]); pc = ").append(index + 1).append("; break;\n");
14951577
return;
14961578
case Opcodes.PUTSTATIC:
1497-
out.append(" jvm.ensureClassInitialized(\"").append(owner).append("\"); jvm.classes[\"")
1498-
.append(owner).append("\"].staticFields[\"").append(fieldName).append("\"] = stack.pop(); pc = ")
1499-
.append(index + 1).append("; break;\n");
1579+
appendInterpreterEnsureClassInitialized(out, owner, usesStaticFieldInitCache);
1580+
out.append(" jvm.classes[\"").append(owner).append("\"].staticFields[\"").append(fieldName)
1581+
.append("\"] = stack.pop(); pc = ").append(index + 1).append("; break;\n");
15001582
return;
15011583
case Opcodes.GETFIELD:
15021584
out.append(" { const target = stack.pop(); stack.push(target[\"").append(propertyName)
@@ -1511,6 +1593,30 @@ private static void appendFieldInstruction(StringBuilder out, Field field, int i
15111593
}
15121594
}
15131595

1596+
private static void appendInterpreterEnsureClassInitialized(StringBuilder out, String owner, boolean usesStaticFieldInitCache) {
1597+
if (usesStaticFieldInitCache) {
1598+
out.append(" if (!__cn1Init[\"").append(owner).append("\"]) { jvm.ensureClassInitialized(\"")
1599+
.append(owner).append("\"); __cn1Init[\"").append(owner).append("\"] = true; }\n");
1600+
return;
1601+
}
1602+
out.append(" jvm.ensureClassInitialized(\"").append(owner).append("\");\n");
1603+
}
1604+
1605+
private static boolean hasClassInitSensitiveAccess(List<Instruction> instructions) {
1606+
for (Instruction instruction : instructions) {
1607+
if (instruction instanceof Field) {
1608+
int opcode = instruction.getOpcode();
1609+
if (opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC) {
1610+
return true;
1611+
}
1612+
}
1613+
if (instruction instanceof Invoke && instruction.getOpcode() == Opcodes.INVOKESTATIC) {
1614+
return true;
1615+
}
1616+
}
1617+
return false;
1618+
}
1619+
15141620
private static void appendJumpInstruction(StringBuilder out, Jump jump, Map<Label, Integer> labelToIndex, int index) {
15151621
Integer target = labelToIndex.get(jump.getLabel());
15161622
if (target == null) {
@@ -1573,9 +1679,10 @@ private static void appendJumpInstruction(StringBuilder out, Jump jump, Map<Labe
15731679
}
15741680
}
15751681

1576-
private static void appendInvokeInstruction(StringBuilder out, Invoke invoke, int index) {
1682+
private static void appendInvokeInstruction(StringBuilder out, Invoke invoke, int index, boolean usesClassInitCache) {
15771683
String owner = JavascriptNameUtil.sanitizeClassName(invoke.getOwner());
15781684
String methodId = JavascriptNameUtil.methodIdentifier(invoke.getOwner(), invoke.getName(), invoke.getDesc());
1685+
String methodBodyId = jsStaticMethodBodyIdentifier(invoke.getOwner(), invoke.getName(), invoke.getDesc());
15791686
List<String> args = JavascriptNameUtil.argumentTypes(invoke.getDesc());
15801687
boolean hasReturn = invoke.getDesc().charAt(invoke.getDesc().length() - 1) != 'V';
15811688
int argCount = args.size();
@@ -1615,11 +1722,16 @@ private static void appendInvokeInstruction(StringBuilder out, Invoke invoke, in
16151722
appendInvocationArgumentBindings(out, argCount, " ", "stack.pop()");
16161723
if (invoke.getOpcode() != Opcodes.INVOKESTATIC) {
16171724
out.append(" const __target = stack.pop();\n");
1725+
} else {
1726+
appendInterpreterEnsureClassInitialized(out, owner, usesClassInitCache);
16181727
}
1728+
String invokedName = invoke.getOpcode() == Opcodes.INVOKESTATIC
1729+
? "(" + staticInvocationTargetExpression(methodId, methodBodyId) + ")"
1730+
: methodId;
16191731
if (hasReturn) {
1620-
out.append(" const __result = yield* ").append(methodId).append("(");
1732+
out.append(" const __result = yield* ").append(invokedName).append("(");
16211733
} else {
1622-
out.append(" yield* ").append(methodId).append("(");
1734+
out.append(" yield* ").append(invokedName).append("(");
16231735
}
16241736
appendInvocationArguments(out, invoke.getOpcode() != Opcodes.INVOKESTATIC, argCount);
16251737
out.append(");\n");
@@ -1650,4 +1762,8 @@ private static void appendInvocationArgumentBindings(StringBuilder out, int argC
16501762
out.append(indent).append("const __arg").append(i).append(" = ").append(sourceExpression).append(";\n");
16511763
}
16521764
}
1765+
1766+
private static String staticInvocationTargetExpression(String methodId, String methodBodyId) {
1767+
return "typeof " + methodBodyId + " === \"function\" ? " + methodBodyId + " : " + methodId;
1768+
}
16531769
}

0 commit comments

Comments
 (0)