diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java index c61ea2193f..248b5344aa 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java @@ -25,10 +25,14 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; /** * Parsed class file @@ -79,6 +83,7 @@ public void setIsAnnotation(boolean isAnnotation) { private static Set arrayTypes = new TreeSet(); + private static ReadWriteLock arrayTypesLock = new ReentrantReadWriteLock(); private ByteCodeClass baseClassObject; private List baseInterfacesObject; @@ -89,10 +94,12 @@ public void setIsAnnotation(boolean isAnnotation) { private int classOffset; private boolean marked; - private static ByteCodeClass mainClass; + private static volatile ByteCodeClass mainClass; + private static ReentrantLock mainClassLock = new ReentrantLock(); private boolean finalClass; private boolean isEnum; private static Set writableFields = new HashSet(); + private static ReadWriteLock writableFieldsLock = new ReentrantReadWriteLock(); /** * @@ -151,10 +158,15 @@ static void setSaveUnitTests(boolean save) { public void addMethod(BytecodeMethod m) { if(m.isMain()) { - if (mainClass == null) { - mainClass = this; - } else { - throw new RuntimeException("Multiple main classes: "+mainClass.clsName+" and "+this.clsName); + mainClassLock.lock(); + try { + if (mainClass == null) { + mainClass = this; + } else { + throw new RuntimeException("Multiple main classes: "+mainClass.clsName+" and "+this.clsName); + } + } finally { + mainClassLock.unlock(); } } m.setSourceFile(sourceFile); @@ -172,7 +184,12 @@ public String generateCSharpCode() { } public void addWritableField(String field) { - writableFields.add(field); + writableFieldsLock.writeLock().lock(); + try { + writableFields.add(field); + } finally { + writableFieldsLock.writeLock().unlock(); + } } /** @@ -429,8 +446,21 @@ private boolean hasFinalizer() { public static void addArrayType(String type, int dimenstions) { String arr = dimenstions + "_" + type; - if(!arrayTypes.contains(arr)) { - arrayTypes.add(arr); + arrayTypesLock.readLock().lock(); + try { + if(arrayTypes.contains(arr)) { + return; + } + } finally { + arrayTypesLock.readLock().unlock(); + } + arrayTypesLock.writeLock().lock(); + try { + if(!arrayTypes.contains(arr)) { + arrayTypes.add(arr); + } + } finally { + arrayTypesLock.writeLock().unlock(); } } @@ -566,8 +596,17 @@ public String generateCCode(List allClasses) { // create class objects for 1 - 3 dimension arrays for(int iter = 1 ; iter < 4 ; iter++) { - if(!(arrayTypes.contains(iter + "_" + clsName) || arrayTypes.contains((iter + 1) + "_" + clsName) || - arrayTypes.contains((iter + 2) + "_" + clsName))) { + boolean shouldContinue = false; + arrayTypesLock.readLock().lock(); + try { + if(!(arrayTypes.contains(iter + "_" + clsName) || arrayTypes.contains((iter + 1) + "_" + clsName) || + arrayTypes.contains((iter + 2) + "_" + clsName))) { + shouldContinue = true; + } + } finally { + arrayTypesLock.readLock().unlock(); + } + if(shouldContinue) { continue; } b.append("struct clazz class_array"); @@ -629,7 +668,14 @@ public String generateCCode(List allClasses) { if (isEnum && ("_VALUES".equals(bf.getFieldName().replace('$','_')) || "ENUM_VALUES".equals(bf.getFieldName().replace('$','_')))) { enumValuesField = bf.getFieldName(); } - if(bf.isFinal() && bf.getValue() != null && !writableFields.contains(bf.getFieldName())) { + boolean isWritable = false; + writableFieldsLock.readLock().lock(); + try { + isWritable = writableFields.contains(bf.getFieldName()); + } finally { + writableFieldsLock.readLock().unlock(); + } + if(bf.isFinal() && bf.getValue() != null && !isWritable) { // static getter b.append(bf.getCDefinition()); b.append(" get_static_"); @@ -870,7 +916,14 @@ public String generateCCode(List allClasses) { } } - if(arrayTypes.contains("1_" + clsName) || arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName)) { + boolean containsArrayTypes = false; + arrayTypesLock.readLock().lock(); + try { + containsArrayTypes = arrayTypes.contains("1_" + clsName) || arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName); + } finally { + arrayTypesLock.readLock().unlock(); + } + if(containsArrayTypes) { b.append("JAVA_OBJECT __NEW_ARRAY_"); b.append(clsName); b.append("(CODENAME_ONE_THREAD_STATE, JAVA_INT size) {\n"); @@ -1011,18 +1064,29 @@ public String generateCCode(List allClasses) { b.append(clsName); b.append(");\n return;\n }\n\n"); - if(arrayTypes.contains("1_" + clsName) || arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName)) { + boolean arrayTypeC1 = false; + boolean arrayTypeC2 = false; + boolean arrayTypeC3 = false; + arrayTypesLock.readLock().lock(); + try { + arrayTypeC1 = arrayTypes.contains("1_" + clsName) || arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName); + arrayTypeC2 = arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName); + arrayTypeC3 = arrayTypes.contains("3_" + clsName); + } finally { + arrayTypesLock.readLock().unlock(); + } + if(arrayTypeC1) { b.append("class_array1__"); b.append(clsName); b.append(".vtable = initVtableForInterface();\n "); } - if( arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName)) { + if(arrayTypeC2) { b.append("class_array2__"); b.append(clsName); b.append(".vtable = initVtableForInterface();\n "); } - if(arrayTypes.contains("3_" + clsName)) { + if(arrayTypeC3) { b.append("class_array3__"); b.append(clsName); b.append(".vtable = initVtableForInterface();\n "); @@ -1236,19 +1300,30 @@ public String generateCHeader() { b.append(clsName); b.append(";\n"); - if(arrayTypes.contains("1_" + clsName) || arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName)) { + boolean arrayTypeC1 = false; + boolean arrayTypeC2 = false; + boolean arrayTypeC3 = false; + arrayTypesLock.readLock().lock(); + try { + arrayTypeC1 = arrayTypes.contains("1_" + clsName) || arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName); + arrayTypeC2 = arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName); + arrayTypeC3 = arrayTypes.contains("3_" + clsName); + } finally { + arrayTypesLock.readLock().unlock(); + } + if(arrayTypeC1) { b.append("extern struct clazz class_array1__"); b.append(clsName); b.append(";\n"); } - if(arrayTypes.contains("2_" + clsName) || arrayTypes.contains("3_" + clsName)) { + if(arrayTypeC2) { b.append("extern struct clazz class_array2__"); b.append(clsName); b.append(";\n"); } - if(arrayTypes.contains("3_" + clsName)) { + if(arrayTypeC3) { b.append("extern struct clazz class_array3__"); b.append(clsName); b.append(";\n"); @@ -1288,7 +1363,14 @@ public String generateCHeader() { b.append("extern JAVA_OBJECT __VALUE_OF_").append(clsName).append("(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT value);\n"); } - if(arrayTypes.contains("1_" + clsName)) { + boolean arrayTypeC1_arr = false; + arrayTypesLock.readLock().lock(); + try { + arrayTypeC1_arr = arrayTypes.contains("1_" + clsName); + } finally { + arrayTypesLock.readLock().unlock(); + } + if(arrayTypeC1_arr) { b.append("extern JAVA_OBJECT __NEW_ARRAY_"); b.append(clsName); b.append("(CODENAME_ONE_THREAD_STATE, JAVA_INT size);\n"); @@ -1333,7 +1415,14 @@ public String generateCHeader() { b.append("_"); b.append(bf.getFieldName()); b.append("();\n"); - if(!(bf.isFinal() && bf.getValue() != null && !writableFields.contains(bf.getFieldName()))) { + boolean isWritable = false; + writableFieldsLock.readLock().lock(); + try { + isWritable = writableFields.contains(bf.getFieldName()); + } finally { + writableFieldsLock.readLock().unlock(); + } + if(!(bf.isFinal() && bf.getValue() != null && !isWritable)) { b.append("extern "); b.append(bf.getCDefinition()); b.append(" STATIC_FIELD_"); @@ -1842,10 +1931,15 @@ void setIsEnum(boolean b) { } private String getArrayClazz(int dim) { - if((arrayTypes.contains(dim + "_" + clsName) )) { - return "&class_array"+dim+"__"+clsName; - } else { - return "0"; + arrayTypesLock.readLock().lock(); + try { + if((arrayTypes.contains(dim + "_" + clsName) )) { + return "&class_array"+dim+"__"+clsName; + } else { + return "0"; + } + } finally { + arrayTypesLock.readLock().unlock(); } } diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java index 425f6c7a90..8660a6b195 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java @@ -38,6 +38,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; /** * @@ -79,12 +83,26 @@ public String extension() { * Recursively parses the files in the hierarchy to the output directory */ void execute(File[] sourceDirs, File outputDir) throws Exception { - for(File f : sourceDirs) { - execute(f, outputDir); + ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + List> futures = new ArrayList>(); + try { + for(File f : sourceDirs) { + execute(f, outputDir, executor, futures); + } + for(Future future : futures) { + try { + future.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof Exception) throw (Exception)e.getCause(); + throw e; + } + } + } finally { + executor.shutdown(); } } - void execute(File sourceDir, File outputDir) throws Exception { + void execute(File sourceDir, final File outputDir, ExecutorService executor, List> futures) throws Exception { File[] directoryList = sourceDir.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { @@ -98,7 +116,7 @@ public boolean accept(File pathname) { } }); if(fileList != null) { - for(File f : fileList) { + for(final File f : fileList) { if (f.getName().equals("module-info.class")) { // Remove module-info.class that might have been added by jdk9 compile 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."); @@ -106,11 +124,27 @@ public boolean accept(File pathname) { continue; } if(f.getName().endsWith(".class")) { - Parser.parse(f); + futures.add(executor.submit(new Runnable() { + public void run() { + try { + Parser.parse(f); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + })); } else { if(!f.isDirectory()) { // copy the file to the dest dir - copy(new FileInputStream(f), new FileOutputStream(new File(outputDir, f.getName()))); + futures.add(executor.submit(new Runnable() { + public void run() { + try { + copy(new FileInputStream(f), new FileOutputStream(new File(outputDir, f.getName()))); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + })); } } } @@ -121,7 +155,7 @@ public boolean accept(File pathname) { copyDir(f, outputDir); continue; } - execute(f, outputDir); + execute(f, outputDir, executor, futures); } } } diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java index ff008acd94..f57ae56950 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java @@ -47,6 +47,7 @@ import com.codename1.tools.translator.bytecodes.TypeInstruction; import com.codename1.tools.translator.bytecodes.VarOp; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -54,6 +55,8 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; @@ -98,7 +101,8 @@ public static void setAcceptStaticOnEquals(boolean aAcceptStaticOnEquals) { private boolean virtualOverriden; private boolean finalMethod; private boolean synchronizedMethod; - private final static Set virtualMethodsInvoked = new TreeSet(); + private final static Set virtualMethodsInvoked = new TreeSet(); + private final static ReadWriteLock virtualMethodsInvokedLock = new ReentrantReadWriteLock(); private String desc; private boolean eliminated; @@ -826,8 +830,21 @@ public void appendVirtualMethodC(String cls, StringBuilder b, String offset) { } public static void addVirtualMethodsInvoked(String m) { - if(!virtualMethodsInvoked.contains(m)) { - virtualMethodsInvoked.add(m); + virtualMethodsInvokedLock.readLock().lock(); + try { + if(virtualMethodsInvoked.contains(m)) { + return; + } + } finally { + virtualMethodsInvokedLock.readLock().unlock(); + } + virtualMethodsInvokedLock.writeLock().lock(); + try { + if(!virtualMethodsInvoked.contains(m)) { + virtualMethodsInvoked.add(m); + } + } finally { + virtualMethodsInvokedLock.writeLock().unlock(); } } @@ -860,7 +877,14 @@ public void appendVirtualMethodC(String cls, StringBuilder b, String offset, boo returnType.appendCMethodExt(bld); } - if(!forceVirtual && !virtualMethodsInvoked.contains(bld.toString())) { + boolean invoked = false; + virtualMethodsInvokedLock.readLock().lock(); + try { + invoked = virtualMethodsInvoked.contains(bld.toString()); + } finally { + virtualMethodsInvokedLock.readLock().unlock(); + } + if(!forceVirtual && !invoked) { return; } @@ -913,7 +937,14 @@ public void appendVirtualMethodHeader(StringBuilder b, String cls) { bld.append("_R"); returnType.appendCMethodExt(bld); } - if(!forceVirtual && !virtualMethodsInvoked.contains(bld.toString())) { + boolean invoked = false; + virtualMethodsInvokedLock.readLock().lock(); + try { + invoked = virtualMethodsInvoked.contains(bld.toString()); + } finally { + virtualMethodsInvokedLock.readLock().unlock(); + } + if(!forceVirtual && !invoked) { return; } appendCMethodPrefix(b, "virtual_", cls); diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java index dbc8d9f510..691e0405f9 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java @@ -25,6 +25,12 @@ import java.io.*; import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; @@ -50,10 +56,16 @@ public class Parser extends ClassVisitor { private String clsName; private static String[] nativeSources; private static List classes = new ArrayList(); + private static ReadWriteLock classesLock = new ReentrantReadWriteLock(); private int lambdaCounter; public static void cleanup() { nativeSources = null; - classes.clear(); + classesLock.writeLock().lock(); + try { + classes.clear(); + } finally { + classesLock.writeLock().unlock(); + } LabelInstruction.cleanup(); } public static void parse(File sourceFile) throws Exception { @@ -70,16 +82,26 @@ public static void parse(File sourceFile) throws Exception { p.cls = new ByteCodeClass(p.clsName, r.getClassName()); r.accept(p, ClassReader.EXPAND_FRAMES); - classes.add(p.cls); + classesLock.writeLock().lock(); + try { + classes.add(p.cls); + } finally { + classesLock.writeLock().unlock(); + } } private static ByteCodeClass getClassByName(String name) { name = name.replace('/', '_').replace('$', '_'); - for(ByteCodeClass bc : classes) { - if(bc.getClsName().equals(name)) { - return bc; + classesLock.readLock().lock(); + try { + for(ByteCodeClass bc : classes) { + if(bc.getClsName().equals(name)) { + return bc; + } } - } + } finally { + classesLock.readLock().unlock(); + } return null; } @@ -102,13 +124,19 @@ private static void appendClassOffset(ByteCodeClass bc, List clsIds) { } } - private static ArrayList constantPool = new ArrayList(); + private static List constantPool = new ArrayList(); + private static ReadWriteLock constantPoolLock = new ReentrantReadWriteLock(); public static ByteCodeClass getClassObject(String name) { - for(ByteCodeClass cls : classes) { - if(cls.getClsName().equals(name)) { - return cls; + classesLock.readLock().lock(); + try { + for(ByteCodeClass cls : classes) { + if(cls.getClsName().equals(name)) { + return cls; + } } + } finally { + classesLock.readLock().unlock(); } return null; } @@ -117,12 +145,26 @@ public static ByteCodeClass getClassObject(String name) { * Adds the given string to the hardcoded constant pool strings returns the offset in the pool */ public static int addToConstantPool(String s) { - int i = constantPool.indexOf(s); - if(i < 0) { - constantPool.add(s); - return constantPool.size() - 1; + constantPoolLock.readLock().lock(); + try { + int i = constantPool.indexOf(s); + if(i > -1) { + return i; + } + } finally { + constantPoolLock.readLock().unlock(); + } + constantPoolLock.writeLock().lock(); + try { + int i = constantPool.indexOf(s); + if(i < 0) { + constantPool.add(s); + return constantPool.size() - 1; + } + return i; + } finally { + constantPoolLock.writeLock().unlock(); } - return i; } @@ -264,30 +306,35 @@ private static void generateClassAndMethodIndexHeader(File outputDirectory) thro bldM.append("};\n\n"); bld.append("#define CN1_CONSTANT_POOL_SIZE "); - bld.append(constantPool.size()); - bld.append("\n\nextern const char * const constantPool[];\n"); - - bldM.append("\n\nconst char * const constantPool[] = {\n"); - first = true; - int offset = 0; - for(String con : constantPool) { - if(first) { - bldM.append("\n \""); - } else { - bldM.append(",\n \""); - } - first = false; - try { - bldM.append(encodeString(con)); - } catch(Throwable t) { - t.printStackTrace(); - System.out.println("Error writing the constant pool string: '" + con + "'"); - System.exit(1); + constantPoolLock.readLock().lock(); + try { + bld.append(constantPool.size()); + bld.append("\n\nextern const char * const constantPool[];\n"); + + bldM.append("\n\nconst char * const constantPool[] = {\n"); + first = true; + int offset = 0; + for(String con : constantPool) { + if(first) { + bldM.append("\n \""); + } else { + bldM.append(",\n \""); + } + first = false; + try { + bldM.append(encodeString(con)); + } catch(Throwable t) { + t.printStackTrace(); + System.out.println("Error writing the constant pool string: '" + con + "'"); + System.exit(1); + } + bldM.append("\" /* "); + bldM.append(offset); + offset++; + bldM.append(" */"); } - bldM.append("\" /* "); - bldM.append(offset); - offset++; - bldM.append(" */"); + } finally { + constantPoolLock.readLock().unlock(); } bldM.append("};\n\nint classListSize = "); bldM.append(classes.size()); @@ -381,7 +428,14 @@ public static void writeOutput(File outputDirectory) throws Exception { } String file = "Unknown File"; try { - for(ByteCodeClass bc : classes) { + List classesCopy; + classesLock.readLock().lock(); + try { + classesCopy = new ArrayList(classes); + } finally { + classesLock.readLock().unlock(); + } + for(ByteCodeClass bc : classesCopy) { // special case for object if(bc.getClsName().equals("java_lang_Object")) { continue; @@ -402,7 +456,7 @@ public static void writeOutput(File outputDirectory) throws Exception { boolean foundNewUnitTests = true; while (foundNewUnitTests) { foundNewUnitTests = false; - for (ByteCodeClass bc : classes) { + for (ByteCodeClass bc : classesCopy) { if (!bc.isUnitTest() && bc.getBaseClassObject() != null && bc.getBaseClassObject().isUnitTest()) { bc.setIsUnitTest(true); foundNewUnitTests = true; @@ -416,14 +470,35 @@ public static void writeOutput(File outputDirectory) throws Exception { // and the class may be purged before it even has a shot. readNativeFiles(outputDirectory); - for(ByteCodeClass bc : classes) { + ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + List> futures = new ArrayList>(); + for(final ByteCodeClass bc : classesCopy) { file = bc.getClsName(); - bc.updateAllDependencies(); + futures.add(executor.submit(new Runnable() { + public void run() { + bc.updateAllDependencies(); + } + })); + } + for (Future f : futures) { + try { + f.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof Exception) throw (Exception)e.getCause(); + throw new RuntimeException(e); + } + } + futures.clear(); + ByteCodeClass.markDependencies(classesCopy, nativeSources); + Set unmarked = new HashSet(classesCopy); + List markedClasses = ByteCodeClass.clearUnmarked(classesCopy); + classesLock.writeLock().lock(); + try { + classes = markedClasses; + } finally { + classesLock.writeLock().unlock(); } - ByteCodeClass.markDependencies(classes, nativeSources); - Set unmarked = new HashSet(classes); - classes = ByteCodeClass.clearUnmarked(classes); - unmarked.removeAll(classes); + unmarked.removeAll(markedClasses); int neliminated = 0; for (ByteCodeClass removedClass : unmarked) { removedClass.setEliminated(true); @@ -445,11 +520,48 @@ public static void writeOutput(File outputDirectory) throws Exception { boolean concatenate = "true".equals(System.getProperty("concatenateFiles", "false")); ConcatenatingFileOutputStream cos = concatenate ? new ConcatenatingFileOutputStream(outputDirectory) : null; - for(ByteCodeClass bc : classes) { - file = bc.getClsName(); - writeFile(bc, outputDirectory, cos); + if (cos != null) { + classesLock.readLock().lock(); + try { + classesCopy = new ArrayList(classes); + } finally { + classesLock.readLock().unlock(); + } + for(ByteCodeClass bc : classesCopy) { + file = bc.getClsName(); + writeFile(bc, outputDirectory, cos); + } + cos.realClose(); + executor.shutdown(); + } else { + classesLock.readLock().lock(); + try { + classesCopy = new ArrayList(classes); + } finally { + classesLock.readLock().unlock(); + } + for(final ByteCodeClass bc : classesCopy) { + file = bc.getClsName(); + futures.add(executor.submit(new Runnable() { + public void run() { + try { + writeFile(bc, outputDirectory, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + })); + } + for (Future f : futures) { + try { + f.get(); + } catch (ExecutionException e) { + if (e.getCause() instanceof Exception) throw (Exception)e.getCause(); + throw new RuntimeException(e); + } + } + executor.shutdown(); } - if (cos != null) cos.realClose(); } catch(Throwable t) { System.out.println("Error while working with the class: " + file); @@ -500,7 +612,14 @@ private static int eliminateUnusedMethods(boolean forceFound, int depth) { private static int cullMethods() { int nfound = 0; - for(ByteCodeClass bc : classes) { + List classesCopy; + classesLock.readLock().lock(); + try { + classesCopy = new ArrayList(classes); + } finally { + classesLock.readLock().unlock(); + } + for(ByteCodeClass bc : classesCopy) { bc.unmark(); if(bc.isIsInterface() || bc.getBaseClass() == null) { continue; @@ -573,12 +692,19 @@ private static boolean checkMethodUsedByBaseClassOrInterface(BytecodeMethod mtd, private static int cullClasses(boolean found, int depth) { System.out.println("cullClasses()"); if(found && depth < 4) { - for(ByteCodeClass bc : classes) { + List classesCopy; + classesLock.readLock().lock(); + try { + classesCopy = new ArrayList(classes); + } finally { + classesLock.readLock().unlock(); + } + for(ByteCodeClass bc : classesCopy) { bc.updateAllDependencies(); } - ByteCodeClass.markDependencies(classes, nativeSources); - List tmp = ByteCodeClass.clearUnmarked(classes); + ByteCodeClass.markDependencies(classesCopy, nativeSources); + List tmp = ByteCodeClass.clearUnmarked(classesCopy); /*if(ByteCodeTranslator.verbose) { System.out.println("Classes removed from: " + classCount + " to " + classes.size()); for(ByteCodeClass bc : classes) { @@ -591,13 +717,18 @@ private static int cullClasses(boolean found, int depth) { // 2nd pass to mark classes as eliminated so that we can propagate down to each // method of the class to mark it eliminated so that virtual methods // aren't included later on when writing virtual methods - Set removedClasses = new HashSet(classes); + Set removedClasses = new HashSet(classesCopy); removedClasses.removeAll(tmp); int nfound = 0; for (ByteCodeClass cls : removedClasses) { nfound += cls.setEliminated(true); } - classes = tmp; + classesLock.writeLock().lock(); + try { + classes = tmp; + } finally { + classesLock.writeLock().unlock(); + } return nfound + eliminateUnusedMethods(nfound > 0, depth + 1); } @@ -613,7 +744,14 @@ private static boolean isMethodUsed(BytecodeMethod m, ByteCodeClass cls) { if (!m.isEliminated() && m.isMethodUsedByNative(nativeSources, cls)) { return true; } - for(ByteCodeClass bc : classes) { + List classesCopy; + classesLock.readLock().lock(); + try { + classesCopy = new ArrayList(classes); + } finally { + classesLock.readLock().unlock(); + } + for(ByteCodeClass bc : classesCopy) { for(BytecodeMethod mtd : bc.getMethods()) { if(mtd.isEliminated() || mtd == m) { continue; @@ -994,7 +1132,12 @@ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object. factory.setMaxes(localIndex + 2, localIndex); // 7. Register the new class - classes.add(lambdaClass); + classesLock.writeLock().lock(); + try { + classes.add(lambdaClass); + } finally { + classesLock.writeLock().unlock(); + } // 8. Replace invokedynamic with INVOKESTATIC to factory mtd.addInvoke(Opcodes.INVOKESTATIC, lambdaClassName, factoryMethodName, actualFactoryDesc, false); diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java index 4a55df8d05..b8f48b56f1 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/BytecodeInstructionIntegrationTest.java @@ -378,12 +378,39 @@ public AnnotationVisitor visitArray(String name) { @Test void byteCodeTranslatorFilenameFilterMatchesExpectedFiles() throws Exception { - Class filterClass = Class.forName("com.codename1.tools.translator.ByteCodeTranslator$3"); + Class filterClass = null; + File directory = Files.createTempDirectory("bytecode-filter").toFile(); + // Search for the anonymous FilenameFilter class + for (int i = 1; i < 50; i++) { + try { + Class c = Class.forName("com.codename1.tools.translator.ByteCodeTranslator$" + i); + if (FilenameFilter.class.isAssignableFrom(c)) { + // Check if it has a no-arg constructor (static context) + try { + Constructor ctor = c.getDeclaredConstructor(); + ctor.setAccessible(true); + FilenameFilter filter = (FilenameFilter) ctor.newInstance(); + // Verify it's the correct filter by testing behavior + if (filter.accept(directory, "assets.bundle") && !filter.accept(directory, "Images.xcassets")) { + filterClass = c; + break; + } + } catch (Exception e) { + // It might be an instance inner class or different constructor + } + } + } catch (ClassNotFoundException e) { + // End of inner classes + break; + } + } + + assertNotNull(filterClass, "Could not find the expected FilenameFilter inner class in ByteCodeTranslator"); + Constructor ctor = filterClass.getDeclaredConstructor(); ctor.setAccessible(true); FilenameFilter filter = (FilenameFilter) ctor.newInstance(); - File directory = Files.createTempDirectory("bytecode-filter").toFile(); assertTrue(filter.accept(directory, "assets.bundle")); assertTrue(filter.accept(directory, "model.xcdatamodeld"));