Skip to content

Commit 45bbf89

Browse files
committed
Optimized static initialization
1 parent edc18f2 commit 45bbf89

3 files changed

Lines changed: 69 additions & 9 deletions

File tree

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

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
import com.codename1.tools.translator.bytecodes.TypeInstruction;
1717
import com.codename1.tools.translator.bytecodes.VarOp;
1818
import java.util.HashMap;
19+
import java.util.HashSet;
1920
import java.util.List;
2021
import java.util.Map;
22+
import java.util.Set;
2123
import org.objectweb.asm.Label;
2224
import org.objectweb.asm.Opcodes;
2325
import org.objectweb.asm.Type;
@@ -277,6 +279,9 @@ private static boolean appendStraightLineMethodBody(StringBuilder out, ByteCodeC
277279
StringBuilder instructionBody = new StringBuilder();
278280
StringBuilder body = new StringBuilder();
279281
StraightLineContext ctx = new StraightLineContext(method.getMaxLocals(), method.getMaxStack());
282+
if (method.isStatic() && !"__CLINIT__".equals(method.getMethodName())) {
283+
ctx.initializedClasses.add(cls.getClsName());
284+
}
280285
if (!method.isStatic()) {
281286
setup.append(" let l0 = __cn1ThisObject;\n");
282287
ctx.localsInitialized[0] = true;
@@ -628,7 +633,14 @@ private static boolean appendStraightLineBasicInstruction(StringBuilder out, Byt
628633
String value = ctx.pop();
629634
String idx = ctx.pop();
630635
String arr = ctx.pop();
631-
out.append(" { const __arr = ").append(arr).append("; const __idx = ").append(idx).append("; if (!__arr.__array) throw new Error(\"Array expected\"); if (__idx < 0 || __idx >= __arr.length) throw new Error(\"ArrayIndexOutOfBoundsException\"); __arr[__idx] = ").append(value).append("; }\n");
636+
String arrayTemp = ctx.nextTemp("__arr");
637+
String indexTemp = ctx.nextTemp("__idx");
638+
out.append(" { const ").append(arrayTemp).append(" = ").append(arr)
639+
.append("; const ").append(indexTemp).append(" = ").append(idx)
640+
.append("; if (!").append(arrayTemp).append(".__array) throw new Error(\"Array expected\"); if (")
641+
.append(indexTemp).append(" < 0 || ").append(indexTemp).append(" >= ").append(arrayTemp)
642+
.append(".length) throw new Error(\"ArrayIndexOutOfBoundsException\"); ")
643+
.append(arrayTemp).append("[").append(indexTemp).append("] = ").append(value).append("; }\n");
632644
return true;
633645
}
634646
default:
@@ -744,11 +756,11 @@ private static boolean appendStraightLineFieldInstruction(StringBuilder out, Fie
744756
String propertyName = JavascriptNameUtil.fieldProperty(field.getOwner(), fieldName);
745757
switch (field.getOpcode()) {
746758
case Opcodes.GETSTATIC:
747-
out.append(" jvm.ensureClassInitialized(\"").append(owner).append("\");\n");
759+
appendStraightLineEnsureClassInitialized(out, ctx, owner);
748760
out.append(" ").append(ctx.push("jvm.classes[\"" + owner + "\"].staticFields[\"" + fieldName + "\"]")).append(";\n");
749761
return true;
750762
case Opcodes.PUTSTATIC:
751-
out.append(" jvm.ensureClassInitialized(\"").append(owner).append("\");\n");
763+
appendStraightLineEnsureClassInitialized(out, ctx, owner);
752764
out.append(" jvm.classes[\"").append(owner).append("\"].staticFields[\"").append(fieldName).append("\"] = ")
753765
.append(ctx.pop()).append(";\n");
754766
return true;
@@ -768,6 +780,12 @@ private static boolean appendStraightLineFieldInstruction(StringBuilder out, Fie
768780
}
769781
}
770782

783+
private static void appendStraightLineEnsureClassInitialized(StringBuilder out, StraightLineContext ctx, String owner) {
784+
if (ctx.initializedClasses.add(owner)) {
785+
out.append(" jvm.ensureClassInitialized(\"").append(owner).append("\");\n");
786+
}
787+
}
788+
771789
private static boolean appendStraightLineInvokeInstruction(StringBuilder out, Invoke invoke, StraightLineContext ctx) {
772790
String methodId = JavascriptNameUtil.methodIdentifier(invoke.getOwner(), invoke.getName(), invoke.getDesc());
773791
List<String> args = JavascriptNameUtil.argumentTypes(invoke.getDesc());
@@ -791,9 +809,8 @@ private static boolean appendStraightLineInvokeInstruction(StringBuilder out, In
791809
if (invoke.getOpcode() == Opcodes.INVOKEVIRTUAL || invoke.getOpcode() == Opcodes.INVOKEINTERFACE) {
792810
out.append(" {\n");
793811
out.append(" const __target = ").append(target).append(";\n");
794-
out.append(" const __class = jvm.classes[__target.__class];\n");
795-
out.append(" const __method = (__class && __class.methods && __class.methods[\"").append(methodId)
796-
.append("\"]) || jvm.resolveVirtual(__target.__class, \"").append(methodId).append("\");\n");
812+
out.append(" const __method = ((jvm.classes[__target.__class] && jvm.classes[__target.__class].methods) ? jvm.classes[__target.__class].methods[\"").append(methodId)
813+
.append("\"] : null) || jvm.resolveVirtual(__target.__class, \"").append(methodId).append("\");\n");
797814
if (hasReturn) {
798815
out.append(" const __result = yield* __method(");
799816
appendInvocationArgumentExpressions(out, "__target", argValues);
@@ -839,13 +856,15 @@ private static void appendInvocationArgumentExpressions(StringBuilder out, Strin
839856
private static final class StraightLineContext {
840857
private final boolean[] localsInitialized;
841858
private final boolean[] localsUsed;
859+
private final Set<String> initializedClasses;
842860
private int sp;
843861
private int maxObservedStack;
844862
private int nextTempId;
845863

846864
private StraightLineContext(int maxLocals, int maxStack) {
847865
this.localsInitialized = new boolean[Math.max(1, maxLocals)];
848866
this.localsUsed = new boolean[Math.max(1, maxLocals)];
867+
this.initializedClasses = new HashSet<String>();
849868
this.sp = 0;
850869
this.maxObservedStack = 0;
851870
this.nextTempId = 0;
@@ -1575,9 +1594,8 @@ private static void appendInvokeInstruction(StringBuilder out, Invoke invoke, in
15751594
out.append(" {\n");
15761595
appendInvocationArgumentBindings(out, argCount, " ", "stack.pop()");
15771596
out.append(" const __target = stack.pop();\n");
1578-
out.append(" const __class = jvm.classes[__target.__class];\n");
1579-
out.append(" const __method = (__class && __class.methods && __class.methods[\"").append(methodId)
1580-
.append("\"]) || jvm.resolveVirtual(__target.__class, \"").append(methodId).append("\");\n");
1597+
out.append(" const __method = ((jvm.classes[__target.__class] && jvm.classes[__target.__class].methods) ? jvm.classes[__target.__class].methods[\"").append(methodId)
1598+
.append("\"] : null) || jvm.resolveVirtual(__target.__class, \"").append(methodId).append("\");\n");
15811599
if (hasReturn) {
15821600
out.append(" const __result = yield* __method(");
15831601
appendInvocationArguments(out, true, argCount);

vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,37 @@ void simpleStraightLineMethodsLowerToLocalsInsteadOfInterpreterLoop(CompilerHelp
207207
"Straight-line lowering should avoid the interpreter locals/stack/pc loop");
208208
}
209209

210+
@ParameterizedTest
211+
@org.junit.jupiter.params.provider.MethodSource("com.codename1.tools.translator.BytecodeInstructionIntegrationTest#provideCompilerConfigs")
212+
void repeatedStaticAccessesOnlyEmitOneClassInitCheckInStraightLineMode(CompilerHelper.CompilerConfig config) throws Exception {
213+
Parser.cleanup();
214+
215+
Path sourceDir = Files.createTempDirectory("js-static-access-sources");
216+
Path classesDir = Files.createTempDirectory("js-static-access-classes");
217+
Path javaApiDir = Files.createTempDirectory("java-api-static-access-classes");
218+
219+
Files.write(sourceDir.resolve("JsStaticAccess.java"), loadFixture("JsStaticAccess.java").getBytes(StandardCharsets.UTF_8));
220+
221+
compileAgainstJavaApi(config, sourceDir, classesDir, javaApiDir);
222+
223+
Path outputDir = Files.createTempDirectory("js-static-access-output");
224+
runJavascriptTranslator(classesDir, outputDir, "JsStaticAccess");
225+
226+
Path distDir = outputDir.resolve("dist").resolve("JsStaticAccess-js");
227+
String translatedApp = new String(Files.readAllBytes(distDir.resolve("translated_app.js")), StandardCharsets.UTF_8);
228+
229+
String marker = "function* cn1_JsStaticAccess_twice_R_int(){";
230+
int start = translatedApp.indexOf(marker);
231+
assertTrue(start >= 0, "Static access fixture should emit the twice() method");
232+
int end = translatedApp.indexOf("\n}\n", start);
233+
assertTrue(end > start, "Static access fixture should have a bounded method body");
234+
String methodBody = translatedApp.substring(start, end);
235+
236+
String initCheck = "jvm.ensureClassInitialized(\"JsStaticAccess\");";
237+
assertEquals(methodBody.indexOf(initCheck), methodBody.lastIndexOf(initCheck),
238+
"Repeated static field access should only emit one class-init check in straight-line mode");
239+
}
240+
210241
static void compileAgainstJavaApi(CompilerHelper.CompilerConfig config, Path sourceDir, Path classesDir, Path javaApiDir) throws Exception {
211242
assertTrue(CompilerHelper.isJavaApiCompatible(config),
212243
"JDK " + config.jdkVersion + " must target matching bytecode level for JavaAPI");
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public class JsStaticAccess {
2+
static int value = 7;
3+
4+
static int twice() {
5+
return value + value;
6+
}
7+
8+
public static void main(String[] args) {
9+
System.exit(twice());
10+
}
11+
}

0 commit comments

Comments
 (0)