Skip to content

Commit acd385d

Browse files
Optimize ByteCodeTranslator by parallelizing class parsing and code generation.
* Parallelized file processing in `ByteCodeTranslator.execute` using `ExecutorService`. * Parallelized dependency updates and file writing in `Parser.writeOutput`. * Ensured thread safety in `Parser`, `ByteCodeClass`, and `BytecodeMethod` by using synchronized collections and synchronized blocks for shared state. * Updated `vm/JavaAPI/pom.xml` to use source/target 1.8 to fix build failure. * Fixed brittle test `BytecodeInstructionIntegrationTest` that relied on anonymous inner class index.
1 parent 258c0e9 commit acd385d

File tree

6 files changed

+141
-31
lines changed

6 files changed

+141
-31
lines changed

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import java.util.ArrayList;
2727
import java.util.Arrays;
28+
import java.util.Collections;
2829
import java.util.HashSet;
2930
import java.util.List;
3031
import java.util.Set;
@@ -78,7 +79,7 @@ public void setIsAnnotation(boolean isAnnotation) {
7879
private boolean isUnitTest;
7980

8081

81-
private static Set<String> arrayTypes = new TreeSet<String>();
82+
private static Set<String> arrayTypes = Collections.synchronizedSet(new TreeSet<String>());
8283

8384
private ByteCodeClass baseClassObject;
8485
private List<ByteCodeClass> baseInterfacesObject;
@@ -89,10 +90,10 @@ public void setIsAnnotation(boolean isAnnotation) {
8990
private int classOffset;
9091

9192
private boolean marked;
92-
private static ByteCodeClass mainClass;
93+
private static volatile ByteCodeClass mainClass;
9394
private boolean finalClass;
9495
private boolean isEnum;
95-
private static Set<String> writableFields = new HashSet<String>();
96+
private static Set<String> writableFields = Collections.synchronizedSet(new HashSet<String>());
9697

9798
/**
9899
*
@@ -151,10 +152,12 @@ static void setSaveUnitTests(boolean save) {
151152

152153
public void addMethod(BytecodeMethod m) {
153154
if(m.isMain()) {
154-
if (mainClass == null) {
155-
mainClass = this;
156-
} else {
157-
throw new RuntimeException("Multiple main classes: "+mainClass.clsName+" and "+this.clsName);
155+
synchronized(ByteCodeClass.class) {
156+
if (mainClass == null) {
157+
mainClass = this;
158+
} else {
159+
throw new RuntimeException("Multiple main classes: "+mainClass.clsName+" and "+this.clsName);
160+
}
158161
}
159162
}
160163
m.setSourceFile(sourceFile);

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

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
import java.util.HashSet;
3939
import java.util.List;
4040
import java.util.Set;
41+
import java.util.concurrent.ExecutionException;
42+
import java.util.concurrent.ExecutorService;
43+
import java.util.concurrent.Executors;
44+
import java.util.concurrent.Future;
4145

4246
/**
4347
*
@@ -79,12 +83,26 @@ public String extension() {
7983
* Recursively parses the files in the hierarchy to the output directory
8084
*/
8185
void execute(File[] sourceDirs, File outputDir) throws Exception {
82-
for(File f : sourceDirs) {
83-
execute(f, outputDir);
86+
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
87+
List<Future<?>> futures = new ArrayList<Future<?>>();
88+
try {
89+
for(File f : sourceDirs) {
90+
execute(f, outputDir, executor, futures);
91+
}
92+
for(Future<?> future : futures) {
93+
try {
94+
future.get();
95+
} catch (ExecutionException e) {
96+
if (e.getCause() instanceof Exception) throw (Exception)e.getCause();
97+
throw e;
98+
}
99+
}
100+
} finally {
101+
executor.shutdown();
84102
}
85103
}
86104

87-
void execute(File sourceDir, File outputDir) throws Exception {
105+
void execute(File sourceDir, final File outputDir, ExecutorService executor, List<Future<?>> futures) throws Exception {
88106
File[] directoryList = sourceDir.listFiles(new FileFilter() {
89107
@Override
90108
public boolean accept(File pathname) {
@@ -98,19 +116,35 @@ public boolean accept(File pathname) {
98116
}
99117
});
100118
if(fileList != null) {
101-
for(File f : fileList) {
119+
for(final File f : fileList) {
102120
if (f.getName().equals("module-info.class")) {
103121
// Remove module-info.class that might have been added by jdk9 compile
104122
System.out.println("WARNING: Found module-info.class file at "+f+". One or more of your jars must have been built for JDK9 or higher. -target 8 or lower is required.");
105123
System.out.println(" Will ignore this warning and attempt build anyways.");
106124
continue;
107125
}
108126
if(f.getName().endsWith(".class")) {
109-
Parser.parse(f);
127+
futures.add(executor.submit(new Runnable() {
128+
public void run() {
129+
try {
130+
Parser.parse(f);
131+
} catch(Exception e) {
132+
throw new RuntimeException(e);
133+
}
134+
}
135+
}));
110136
} else {
111137
if(!f.isDirectory()) {
112138
// copy the file to the dest dir
113-
copy(new FileInputStream(f), new FileOutputStream(new File(outputDir, f.getName())));
139+
futures.add(executor.submit(new Runnable() {
140+
public void run() {
141+
try {
142+
copy(new FileInputStream(f), new FileOutputStream(new File(outputDir, f.getName())));
143+
} catch(Exception e) {
144+
throw new RuntimeException(e);
145+
}
146+
}
147+
}));
114148
}
115149
}
116150
}
@@ -121,7 +155,7 @@ public boolean accept(File pathname) {
121155
copyDir(f, outputDir);
122156
continue;
123157
}
124-
execute(f, outputDir);
158+
execute(f, outputDir, executor, futures);
125159
}
126160
}
127161
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import com.codename1.tools.translator.bytecodes.TypeInstruction;
4848
import com.codename1.tools.translator.bytecodes.VarOp;
4949
import java.util.ArrayList;
50+
import java.util.Collections;
5051
import java.util.HashMap;
5152
import java.util.HashSet;
5253
import java.util.Hashtable;
@@ -98,7 +99,7 @@ public static void setAcceptStaticOnEquals(boolean aAcceptStaticOnEquals) {
9899
private boolean virtualOverriden;
99100
private boolean finalMethod;
100101
private boolean synchronizedMethod;
101-
private final static Set<String> virtualMethodsInvoked = new TreeSet<String>();
102+
private final static Set<String> virtualMethodsInvoked = Collections.synchronizedSet(new TreeSet<String>());
102103
private String desc;
103104
private boolean eliminated;
104105

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

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525

2626
import java.io.*;
2727
import java.util.*;
28+
import java.util.concurrent.ExecutionException;
29+
import java.util.concurrent.ExecutorService;
30+
import java.util.concurrent.Executors;
31+
import java.util.concurrent.Future;
2832

2933
import org.objectweb.asm.AnnotationVisitor;
3034
import org.objectweb.asm.Attribute;
@@ -49,7 +53,7 @@ public class Parser extends ClassVisitor {
4953
private ByteCodeClass cls;
5054
private String clsName;
5155
private static String[] nativeSources;
52-
private static List<ByteCodeClass> classes = new ArrayList<ByteCodeClass>();
56+
private static List<ByteCodeClass> classes = Collections.synchronizedList(new ArrayList<ByteCodeClass>());
5357
private int lambdaCounter;
5458
public static void cleanup() {
5559
nativeSources = null;
@@ -102,12 +106,14 @@ private static void appendClassOffset(ByteCodeClass bc, List<Integer> clsIds) {
102106
}
103107
}
104108

105-
private static ArrayList<String> constantPool = new ArrayList<String>();
109+
private static List<String> constantPool = Collections.synchronizedList(new ArrayList<String>());
106110

107111
public static ByteCodeClass getClassObject(String name) {
108-
for(ByteCodeClass cls : classes) {
109-
if(cls.getClsName().equals(name)) {
110-
return cls;
112+
synchronized(classes) {
113+
for(ByteCodeClass cls : classes) {
114+
if(cls.getClsName().equals(name)) {
115+
return cls;
116+
}
111117
}
112118
}
113119
return null;
@@ -116,7 +122,7 @@ public static ByteCodeClass getClassObject(String name) {
116122
/**
117123
* Adds the given string to the hardcoded constant pool strings returns the offset in the pool
118124
*/
119-
public static int addToConstantPool(String s) {
125+
public static synchronized int addToConstantPool(String s) {
120126
int i = constantPool.indexOf(s);
121127
if(i < 0) {
122128
constantPool.add(s);
@@ -416,10 +422,25 @@ public static void writeOutput(File outputDirectory) throws Exception {
416422
// and the class may be purged before it even has a shot.
417423
readNativeFiles(outputDirectory);
418424

419-
for(ByteCodeClass bc : classes) {
425+
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
426+
List<Future<?>> futures = new ArrayList<Future<?>>();
427+
for(final ByteCodeClass bc : classes) {
420428
file = bc.getClsName();
421-
bc.updateAllDependencies();
429+
futures.add(executor.submit(new Runnable() {
430+
public void run() {
431+
bc.updateAllDependencies();
432+
}
433+
}));
422434
}
435+
for (Future<?> f : futures) {
436+
try {
437+
f.get();
438+
} catch (ExecutionException e) {
439+
if (e.getCause() instanceof Exception) throw (Exception)e.getCause();
440+
throw new RuntimeException(e);
441+
}
442+
}
443+
futures.clear();
423444
ByteCodeClass.markDependencies(classes, nativeSources);
424445
Set<ByteCodeClass> unmarked = new HashSet<ByteCodeClass>(classes);
425446
classes = ByteCodeClass.clearUnmarked(classes);
@@ -445,11 +466,35 @@ public static void writeOutput(File outputDirectory) throws Exception {
445466
boolean concatenate = "true".equals(System.getProperty("concatenateFiles", "false"));
446467
ConcatenatingFileOutputStream cos = concatenate ? new ConcatenatingFileOutputStream(outputDirectory) : null;
447468

448-
for(ByteCodeClass bc : classes) {
449-
file = bc.getClsName();
450-
writeFile(bc, outputDirectory, cos);
469+
if (cos != null) {
470+
for(ByteCodeClass bc : classes) {
471+
file = bc.getClsName();
472+
writeFile(bc, outputDirectory, cos);
473+
}
474+
cos.realClose();
475+
} else {
476+
for(final ByteCodeClass bc : classes) {
477+
file = bc.getClsName();
478+
futures.add(executor.submit(new Runnable() {
479+
public void run() {
480+
try {
481+
writeFile(bc, outputDirectory, null);
482+
} catch (Exception e) {
483+
throw new RuntimeException(e);
484+
}
485+
}
486+
}));
487+
}
488+
for (Future<?> f : futures) {
489+
try {
490+
f.get();
491+
} catch (ExecutionException e) {
492+
if (e.getCause() instanceof Exception) throw (Exception)e.getCause();
493+
throw new RuntimeException(e);
494+
}
495+
}
496+
executor.shutdown();
451497
}
452-
if (cos != null) cos.realClose();
453498

454499
} catch(Throwable t) {
455500
System.out.println("Error while working with the class: " + file);

vm/JavaAPI/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
<artifactId>maven-compiler-plugin</artifactId>
2222
<version>3.11.0</version>
2323
<configuration>
24-
<source>1.5</source>
25-
<target>1.5</target>
24+
<source>1.8</source>
25+
<target>1.8</target>
2626
<!-- Allow compiling java.* packages -->
2727
<compilerArgs>
2828
<arg>-Xlint:-options</arg>

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,39 @@ public AnnotationVisitor visitArray(String name) {
378378

379379
@Test
380380
void byteCodeTranslatorFilenameFilterMatchesExpectedFiles() throws Exception {
381-
Class<?> filterClass = Class.forName("com.codename1.tools.translator.ByteCodeTranslator$3");
381+
Class<?> filterClass = null;
382+
File directory = Files.createTempDirectory("bytecode-filter").toFile();
383+
// Search for the anonymous FilenameFilter class
384+
for (int i = 1; i < 50; i++) {
385+
try {
386+
Class<?> c = Class.forName("com.codename1.tools.translator.ByteCodeTranslator$" + i);
387+
if (FilenameFilter.class.isAssignableFrom(c)) {
388+
// Check if it has a no-arg constructor (static context)
389+
try {
390+
Constructor<?> ctor = c.getDeclaredConstructor();
391+
ctor.setAccessible(true);
392+
FilenameFilter filter = (FilenameFilter) ctor.newInstance();
393+
// Verify it's the correct filter by testing behavior
394+
if (filter.accept(directory, "assets.bundle") && !filter.accept(directory, "Images.xcassets")) {
395+
filterClass = c;
396+
break;
397+
}
398+
} catch (Exception e) {
399+
// It might be an instance inner class or different constructor
400+
}
401+
}
402+
} catch (ClassNotFoundException e) {
403+
// End of inner classes
404+
break;
405+
}
406+
}
407+
408+
assertNotNull(filterClass, "Could not find the expected FilenameFilter inner class in ByteCodeTranslator");
409+
382410
Constructor<?> ctor = filterClass.getDeclaredConstructor();
383411
ctor.setAccessible(true);
384412

385413
FilenameFilter filter = (FilenameFilter) ctor.newInstance();
386-
File directory = Files.createTempDirectory("bytecode-filter").toFile();
387414

388415
assertTrue(filter.accept(directory, "assets.bundle"));
389416
assertTrue(filter.accept(directory, "model.xcdatamodeld"));

0 commit comments

Comments
 (0)