Skip to content

Commit 74569f4

Browse files
Optimize native usage detection in ByteCodeTranslator
Replaced linear scan of native sources (O(N*M)) with a tokenized Set lookup (O(1)) for method usage checks. Pre-tokenized native sources in Parser.readNativeFiles. Updated ByteCodeClass and BytecodeMethod to accept and use the Set<String> of native tokens. Added resolveNativeUsage optimization in ByteCodeClass to determine class usage via token set lookup.
1 parent 5e675aa commit 74569f4

File tree

3 files changed

+59
-45
lines changed

3 files changed

+59
-45
lines changed

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

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,10 @@ public void addWritableField(String field) {
178178
/**
179179
* Marks dependencies in this class based on the provided classes in this round of optimization.
180180
* @param lst The list of classes that are available in this optimization step.
181-
* @param nativeSources Array of native sources in this round. Used to check if native files reference
181+
* @param nativeTokens Set of native tokens in this round. Used to check if native files reference
182182
* this class or methods.
183183
*/
184-
public static void markDependencies(List<ByteCodeClass> lst, String[] nativeSources) {
184+
public static void markDependencies(List<ByteCodeClass> lst, Set<String> nativeTokens) {
185185
mainClass.markDependent(lst);
186186
for(ByteCodeClass bc : lst) {
187187
if (bc.marked) {
@@ -237,7 +237,7 @@ public static void markDependencies(List<ByteCodeClass> lst, String[] nativeSour
237237
if (bc.getUsedByNative() == UsedByNativeResult.Unknown) {
238238
// We don't yet know if this class is used by native
239239
// calculate it now.
240-
bc.calcUsedByNative(nativeSources);
240+
bc.calcUsedByNative(nativeTokens);
241241
}
242242
if(bc.getUsedByNative() == UsedByNativeResult.Used){
243243
bc.markDependent(lst);
@@ -1818,18 +1818,30 @@ public UsedByNativeResult getUsedByNative() {
18181818
return usedByNative;
18191819
}
18201820

1821+
public void resolveNativeUsage(Set<String> nativeTokens) {
1822+
if (usedByNative != UsedByNativeResult.Unknown) return;
1823+
for(String token : nativeTokens) {
1824+
if(token.contains(clsName)) {
1825+
setUsedByNative(true);
1826+
return;
1827+
}
1828+
}
1829+
setUsedByNative(false);
1830+
}
1831+
18211832
/**
18221833
* Calculates whether this class is used in any of the native sources.
1823-
* @param nativeSources The native sources to check.
1834+
* @param nativeTokens The native sources to check.
18241835
* @see #getUsedByNative()
18251836
* @see #setUsedByNative(boolean)
18261837
*/
1827-
public void calcUsedByNative(String[] nativeSources) {
1838+
public void calcUsedByNative(Set<String> nativeTokens) {
1839+
resolveNativeUsage(nativeTokens);
1840+
if (usedByNative == UsedByNativeResult.Unused) {
1841+
return;
1842+
}
18281843
for (BytecodeMethod m : methods) {
1829-
if (usedByNative != UsedByNativeResult.Unknown) {
1830-
return;
1831-
}
1832-
m.isMethodUsedByNative(nativeSources, this);
1844+
m.isMethodUsedByNative(nativeTokens, this);
18331845
}
18341846
}
18351847

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

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -294,28 +294,33 @@ public boolean isMethodUsed(BytecodeMethod bm0) {
294294
* Internal use: to track the list of native sources that were used to calculate the
295295
* usedByNative flag.
296296
*/
297-
private String[] usedByNativeSources;
297+
private Set<String> usedByNativeSources;
298298

299299
/**
300300
* Checks to see if this method is used by any of the provided native sources.
301-
* @param nativeSources The native sources to check.
301+
* @param nativeTokens The native sources to check.
302302
* @param cls The class that the method belongs to. This is used to improve performance by first checking to
303303
* see if the class is Not referenced in native sources. If the class is not referenced, then we know that
304304
* neither is the method. This method will also set the {@link ByteCodeClass#setUsedByNative(boolean)} flag
305305
* to improve the performance for the next method that is checked in the same class.
306306
* @return True if the method is used by native.
307307
*/
308-
public boolean isMethodUsedByNative(String[] nativeSources, ByteCodeClass cls) {
309-
if (nativeSources == null) return false;
310-
if (nativeSources == usedByNativeSources) {
308+
public boolean isMethodUsedByNative(Set<String> nativeTokens, ByteCodeClass cls) {
309+
if (nativeTokens == null) return false;
310+
if (nativeTokens == usedByNativeSources) {
311311
return usedByNative;
312312
}
313-
usedByNativeSources = nativeSources;
313+
usedByNativeSources = nativeTokens;
314314

315-
if (cls != null && cls.getUsedByNative() == ByteCodeClass.UsedByNativeResult.Unused) {
316-
// If the class isn't used, then neither is the method.
317-
usedByNative = false;
318-
return false;
315+
if (cls != null) {
316+
if (cls.getUsedByNative() == ByteCodeClass.UsedByNativeResult.Unknown) {
317+
cls.resolveNativeUsage(nativeTokens);
318+
}
319+
if (cls.getUsedByNative() == ByteCodeClass.UsedByNativeResult.Unused) {
320+
// If the class isn't used, then neither is the method.
321+
usedByNative = false;
322+
return false;
323+
}
319324
}
320325

321326

@@ -324,26 +329,15 @@ public boolean isMethodUsedByNative(String[] nativeSources, ByteCodeClass cls) {
324329
StringBuilder b = new StringBuilder();
325330
this.appendFunctionPointer(b);
326331
String str = b.toString();
327-
boolean foundClassName = false;
328-
for(String s : nativeSources) {
329-
if (cls != null && !foundClassName && s.contains(clsName)) {
330-
// For later we record whether the class is used.
331-
foundClassName = true;
332-
}
333-
if(s.contains(str)) {
334-
usedByNative = true;
335-
if (cls != null) {
336-
cls.setUsedByNative(true);
337-
}
338-
return true;
339-
}
340-
}
341-
if (!foundClassName && cls != null) {
342-
// We didn't find the class at all.
343-
// Let's record that as it will save us time
344-
// when looking up other methods in this class.
345-
cls.setUsedByNative(false);
332+
333+
if (nativeTokens.contains(str)) {
334+
usedByNative = true;
335+
if (cls != null) {
336+
cls.setUsedByNative(true);
337+
}
338+
return true;
346339
}
340+
347341
usedByNative = false;
348342
return false;
349343
}

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@
4848
public class Parser extends ClassVisitor {
4949
private ByteCodeClass cls;
5050
private String clsName;
51-
private static String[] nativeSources;
51+
private static Set<String> nativeTokens;
5252
private static List<ByteCodeClass> classes = new ArrayList<ByteCodeClass>();
5353
private int lambdaCounter;
5454
public static void cleanup() {
55-
nativeSources = null;
55+
nativeTokens = null;
5656
classes.clear();
5757
LabelInstruction.cleanup();
5858
}
@@ -420,7 +420,7 @@ public static void writeOutput(File outputDirectory) throws Exception {
420420
file = bc.getClsName();
421421
bc.updateAllDependencies();
422422
}
423-
ByteCodeClass.markDependencies(classes, nativeSources);
423+
ByteCodeClass.markDependencies(classes, nativeTokens);
424424
Set<ByteCodeClass> unmarked = new HashSet<ByteCodeClass>(classes);
425425
classes = ByteCodeClass.clearUnmarked(classes);
426426
unmarked.removeAll(classes);
@@ -471,7 +471,7 @@ public boolean accept(File file) {
471471
return file.getName().endsWith(".m") || file.getName().endsWith("." + ByteCodeTranslator.output.extension());
472472
}
473473
});
474-
nativeSources = new String[mFiles.length];
474+
nativeTokens = new HashSet<String>();
475475
int size = 0;
476476
System.out.println(""+mFiles.length +" native files");
477477
for(int iter = 0 ; iter < mFiles.length ; iter++) {
@@ -482,7 +482,15 @@ public boolean accept(File file) {
482482
byte[] dat = new byte[len];
483483
di.readFully(dat);
484484
fi.close();
485-
nativeSources[iter] = new String(dat, "UTF-8");
485+
String s = new String(dat, "UTF-8");
486+
487+
// tokenize the file content
488+
String[] tokens = s.split("[^a-zA-Z0-9_$]+");
489+
for(String t : tokens) {
490+
if(t.length() > 1) {
491+
nativeTokens.add(t);
492+
}
493+
}
486494
}
487495
System.out.println("Native files total "+(size/1024)+"K");
488496

@@ -577,7 +585,7 @@ private static int cullClasses(boolean found, int depth) {
577585
bc.updateAllDependencies();
578586
}
579587

580-
ByteCodeClass.markDependencies(classes, nativeSources);
588+
ByteCodeClass.markDependencies(classes, nativeTokens);
581589
List<ByteCodeClass> tmp = ByteCodeClass.clearUnmarked(classes);
582590
/*if(ByteCodeTranslator.verbose) {
583591
System.out.println("Classes removed from: " + classCount + " to " + classes.size());
@@ -610,7 +618,7 @@ private static int cullClasses(boolean found, int depth) {
610618

611619

612620
private static boolean isMethodUsed(BytecodeMethod m, ByteCodeClass cls) {
613-
if (!m.isEliminated() && m.isMethodUsedByNative(nativeSources, cls)) {
621+
if (!m.isEliminated() && m.isMethodUsedByNative(nativeTokens, cls)) {
614622
return true;
615623
}
616624
for(ByteCodeClass bc : classes) {

0 commit comments

Comments
 (0)