diff --git a/CodenameOne/src/com/codename1/ui/TextArea.java b/CodenameOne/src/com/codename1/ui/TextArea.java index 104fa9be81..07ec8187d7 100644 --- a/CodenameOne/src/com/codename1/ui/TextArea.java +++ b/CodenameOne/src/com/codename1/ui/TextArea.java @@ -781,6 +781,7 @@ private void initRowString() { return; } } + System.out.println("Test5"); Style style = getUnselectedStyle(); rowStrings= new ArrayList(); widthForRowCalculations = getWidth() - style.getPadding(false, RIGHT) - style.getPadding(false, LEFT); diff --git a/Ports/iOSPort/nativeSources/libzbar.a b/Ports/iOSPort/nativeSources/libzbar.a index 7533efb95c..422e024023 100644 Binary files a/Ports/iOSPort/nativeSources/libzbar.a and b/Ports/iOSPort/nativeSources/libzbar.a differ diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java index 6eff701ead..eb20ffa972 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeClass.java @@ -86,6 +86,9 @@ public String generateCSharpCode() { } public static void markDependencies(List<ByteCodeClass> lst) { + if (mainClass == null) { + throw new RuntimeException("Missing main class.."); + } mainClass.markDependent(lst); for(ByteCodeClass bc : lst) { if(bc.clsName.equals("java_lang_Boolean")) { @@ -1522,5 +1525,8 @@ public boolean isUsedByNative() { return usedByNative; } - + @Override + public String toString() { + return "[ByteCodeClass "+clsName+"]"; + } } diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java index 05b3d64812..e445f57bfe 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/ByteCodeTranslator.java @@ -61,8 +61,9 @@ public String extension() { public abstract String extension(); }; public static OutputType output = OutputType.OUTPUT_TYPE_IOS; - public static boolean verbose = true; - + public static boolean verbose = Boolean.parseBoolean(System.getProperty("ByteCodeTranslator.verbose","true")); + public static boolean draft = Boolean.parseBoolean(System.getProperty("ByteCodeTranslator.draft","false")); + ByteCodeTranslator() { } @@ -95,7 +96,9 @@ public boolean accept(File pathname) { } else { if(!f.isDirectory()) { // copy the file to the dest dir - copy(new FileInputStream(f), new FileOutputStream(new File(outputDir, f.getName()))); + if (f.getName().equals("package.html")) + continue; + copy(new FileInputStream(f), new PreservingFileOutputStream(new File(outputDir, f.getName()))); } } } @@ -111,17 +114,23 @@ public boolean accept(File pathname) { } } - private void copyDir(File source, File destDir) throws IOException { + private boolean copyDir(File source, File destDir) throws IOException { File destFile = new File(destDir, source.getName()); destFile.mkdirs(); File[] files = source.listFiles(); + boolean retval = false; for(File f : files) { if(f.isDirectory()) { - copyDir(f, destFile); + long savedModified = destFile.lastModified(); + boolean modified = copyDir(f, destFile); + if (!modified) { + destFile.setLastModified(savedModified); + } } else { - copy(new FileInputStream(f), new FileOutputStream(new File(destFile, f.getName()))); + retval |= copy(new FileInputStream(f), new PreservingFileOutputStream(new File(destFile, f.getName()))); } } + return retval; } /** @@ -145,6 +154,7 @@ public static void main(String[] args) throws Exception { final String appVersion = args[6]; final String appType = args[7]; final String addFrameworks = args[8]; + StringBuilder appFonts = new StringBuilder("\n"); // we accept 3 arguments output type, input directory & output directory if(args[0].equalsIgnoreCase("csharp")) { output = OutputType.OUTPUT_TYPE_CSHARP; @@ -179,11 +189,11 @@ public static void main(String[] args) throws Exception { imagesXcassets.mkdirs(); File launchImageLaunchimage = new File(imagesXcassets, "LaunchImage.launchimage"); launchImageLaunchimage.mkdirs(); - copy(ByteCodeTranslator.class.getResourceAsStream("/LaunchImages.json"), new FileOutputStream(new File(launchImageLaunchimage, "Contents.json"))); + copy(ByteCodeTranslator.class.getResourceAsStream("/LaunchImages.json"), new PreservingFileOutputStream(new File(launchImageLaunchimage, "Contents.json"))); File appIconAppiconset = new File(imagesXcassets, "AppIcon.appiconset"); appIconAppiconset.mkdirs(); - copy(ByteCodeTranslator.class.getResourceAsStream("/Icons.json"), new FileOutputStream(new File(appIconAppiconset, "Contents.json"))); + copy(ByteCodeTranslator.class.getResourceAsStream("/Icons.json"), new PreservingFileOutputStream(new File(appIconAppiconset, "Contents.json"))); File xcproj = new File(root, appName + ".xcodeproj"); @@ -196,35 +206,38 @@ public static void main(String[] args) throws Exception { b.execute(sources, srcRoot); File cn1Globals = new File(srcRoot, "cn1_globals.h"); - copy(ByteCodeTranslator.class.getResourceAsStream("/cn1_globals.h"), new FileOutputStream(cn1Globals)); + copy(ByteCodeTranslator.class.getResourceAsStream("/cn1_globals.h"), new PreservingFileOutputStream(cn1Globals)); File cn1GlobalsM = new File(srcRoot, "cn1_globals.m"); - copy(ByteCodeTranslator.class.getResourceAsStream("/cn1_globals.m"), new FileOutputStream(cn1GlobalsM)); + copy(ByteCodeTranslator.class.getResourceAsStream("/cn1_globals.m"), new PreservingFileOutputStream(cn1GlobalsM)); File nativeMethods = new File(srcRoot, "nativeMethods.m"); - copy(ByteCodeTranslator.class.getResourceAsStream("/nativeMethods.m"), new FileOutputStream(nativeMethods)); + copy(ByteCodeTranslator.class.getResourceAsStream("/nativeMethods.m"), new PreservingFileOutputStream(nativeMethods)); Parser.writeOutput(srcRoot); - File templateInfoPlist = new File(srcRoot, appName + "-Info.plist"); - copy(ByteCodeTranslator.class.getResourceAsStream("/template/template/template-Info.plist"), new FileOutputStream(templateInfoPlist)); + File templateInfoPlist = new File(srcRoot, appName + "-Info.plist" + PreservingFileOutputStream.NEW_SUFFIX); + copy(ByteCodeTranslator.class.getResourceAsStream("/template/template/template-Info.plist"), new PreservingFileOutputStream(templateInfoPlist)); File templatePch = new File(srcRoot, appName + "-Prefix.pch"); - copy(ByteCodeTranslator.class.getResourceAsStream("/template/template/template-Prefix.pch"), new FileOutputStream(templatePch)); + copy(ByteCodeTranslator.class.getResourceAsStream("/template/template/template-Prefix.pch"), new PreservingFileOutputStream(templatePch)); File xmlvm = new File(srcRoot, "xmlvm.h"); - copy(ByteCodeTranslator.class.getResourceAsStream("/xmlvm.h"), new FileOutputStream(xmlvm)); + copy(ByteCodeTranslator.class.getResourceAsStream("/xmlvm.h"), new PreservingFileOutputStream(xmlvm)); - File projectWorkspaceData = new File(projectXCworkspace, "contents.xcworkspacedata"); - copy(ByteCodeTranslator.class.getResourceAsStream("/template/template.xcodeproj/project.xcworkspace/contents.xcworkspacedata"), new FileOutputStream(projectWorkspaceData)); + File projectWorkspaceData = new File(projectXCworkspace, "contents.xcworkspacedata"+ PreservingFileOutputStream.NEW_SUFFIX); + copy(ByteCodeTranslator.class.getResourceAsStream("/template/template.xcodeproj/project.xcworkspace/contents.xcworkspacedata"), new PreservingFileOutputStream(projectWorkspaceData)); replaceInFile(projectWorkspaceData, "KitchenSink", appName); - File projectPbx = new File(xcproj, "project.pbxproj"); - copy(ByteCodeTranslator.class.getResourceAsStream("/template/template.xcodeproj/project.pbxproj"), new FileOutputStream(projectPbx)); + File projectPbx = new File(xcproj, "project.pbxproj"+ PreservingFileOutputStream.NEW_SUFFIX); + copy(ByteCodeTranslator.class.getResourceAsStream("/template/template.xcodeproj/project.pbxproj"), new PreservingFileOutputStream(projectPbx)); String[] sourceFiles = srcRoot.list(new FilenameFilter() { @Override - public boolean accept(File pathname, String string) { - return string.endsWith(".bundle") || string.endsWith(".xcdatamodeld") || !pathname.isHidden() && !string.startsWith(".") && !"Images.xcassets".equals(string); + public boolean accept(File pathname, String filename) { + if (filename.endsWith(".ttf")) { + appFonts.append("<string>"+filename+"</string>\n"); + } + return filename.endsWith(".bundle") || filename.endsWith(".xcdatamodeld") || !pathname.isHidden() && !filename.startsWith(".") && !"Images.xcassets".equals(filename); } }); @@ -278,6 +291,11 @@ public boolean accept(File pathname, String string) { arr.addAll(Arrays.asList(sourceFiles)); for(String file : arr) { + if (file.endsWith(PreservingFileOutputStream.NEW_SUFFIX)) { + file = file.substring(0, file.length()-PreservingFileOutputStream.NEW_SUFFIX.length()); + } else { + if (arr.contains(file + PreservingFileOutputStream.NEW_SUFFIX)) continue; // remove duplicates + } fileListEntry.append(" 0"); currentValue++; String fileOneValue = Integer.toHexString(currentValue).toUpperCase(); @@ -417,7 +435,15 @@ public boolean accept(File pathname, String string) { } String bundleVersion = System.getProperty("bundleVersionNumber", appVersion); - replaceInFile(templateInfoPlist, "com.codename1pkg", appPackageName, "${PRODUCT_NAME}", appDisplayName, "VERSION_VALUE", appVersion, "VERSION_BUNDLE_VALUE", bundleVersion); + replaceInFile(templateInfoPlist, + "com.codename1pkg", appPackageName, + "${PRODUCT_NAME}", appDisplayName, + "VERSION_VALUE", appVersion, + "VERSION_BUNDLE_VALUE", bundleVersion, + "${APP_FONTS}", appFonts.toString()); + PreservingFileOutputStream.finishWithNewFile(projectPbx); + PreservingFileOutputStream.finishWithNewFile(templateInfoPlist); + PreservingFileOutputStream.finishWithNewFile(projectWorkspaceData); } else { b.execute(sources, dest); Parser.writeOutput(dest); @@ -495,8 +521,8 @@ private static void replaceInFile(File sourceFile, String... values) throws IOEx * @param i source * @param o destination */ - public static void copy(InputStream i, OutputStream o) throws IOException { - copy(i, o, 8192); + public static boolean copy(InputStream i, OutputStream o) throws IOException { + return copy(i, o, 8192); } /** @@ -507,7 +533,8 @@ public static void copy(InputStream i, OutputStream o) throws IOException { * @param o destination * @param bufferSize the size of the buffer, which should be a power of 2 large enoguh */ - public static void copy(InputStream i, OutputStream o, int bufferSize) throws IOException { + public static boolean copy(InputStream i, OutputStream o, int bufferSize) throws IOException { + boolean modified = true; try { byte[] buffer = new byte[bufferSize]; int size = i.read(buffer); @@ -516,9 +543,10 @@ public static void copy(InputStream i, OutputStream o, int bufferSize) throws IO size = i.read(buffer); } } finally { - cleanup(o); cleanup(i); + modified = cleanup(o); } + return modified; } /** @@ -526,19 +554,24 @@ public static void copy(InputStream i, OutputStream o, int bufferSize) throws IO * object is null * * @param o Connection, Stream or other closeable object + * @return true if modified */ - public static void cleanup(Object o) { + public static boolean cleanup(Object o) { try { if(o instanceof OutputStream) { ((OutputStream)o).close(); - return; + if (o instanceof PreservingFileOutputStream) { + return !((PreservingFileOutputStream)o).equal; + } + return true; } if(o instanceof InputStream) { ((InputStream)o).close(); - return; + return false; } } catch(IOException err) { err.printStackTrace(); } + return true; } } diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java index 81e22e3397..2ff3229505 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/BytecodeMethod.java @@ -824,7 +824,12 @@ public void addDebugInfo(int line) { } public void addLabel(Label l) { - addInstruction(new com.codename1.tools.translator.bytecodes.LabelInstruction(l)); + addInstruction(new com.codename1.tools.translator.bytecodes.LabelInstruction(l) { + @Override + public String toString() { + return "BL_"+clsName+"__"+methodName+"__"+instructions.size(); + } + }); } public void addInvoke(int opcode, String owner, String name, String desc, boolean itf) { diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java index 0f59b907f8..96b8955ee2 100644 --- a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/Parser.java @@ -23,12 +23,7 @@ package com.codename1.tools.translator; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.util.ArrayList; import java.util.List; import org.objectweb.asm.AnnotationVisitor; @@ -42,6 +37,7 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.TypePath; import org.objectweb.asm.commons.JSRInlinerAdapter; +import org.objectweb.asm.tree.LabelNode; /** * @@ -56,10 +52,20 @@ public static void parse(File sourceFile) throws Exception { if(ByteCodeTranslator.verbose) { System.out.println("Parsing: " + sourceFile.getAbsolutePath()); } - ClassReader r = new ClassReader(new FileInputStream(sourceFile)); - /*if(ByteCodeTranslator.verbose) { - System.out.println("Class: " + r.getClassName() + " derives from: " + r.getSuperName() + " interfaces: " + Arrays.asList(r.getInterfaces())); - }*/ + ClassReader r = new ClassReader(new FileInputStream(sourceFile)) { + String clsName; + @Override + protected Label readLabel(int offset, Label[] labels) { + if (clsName == null) { + clsName = getClassName().replace('/', '_').replace('$', '_'); + } + if (labels[offset] == null) { + final String labelName = "L" + clsName + "___" + offset; + labels[offset] = new MyLabel(labelName); + } + return labels[offset]; + } + }; Parser p = new Parser(); p.clsName = r.getClassName().replace('/', '_').replace('$', '_'); if(p.clsName.startsWith("java_lang_annotation") || p.clsName.startsWith("java_lang_Deprecated") @@ -322,10 +328,10 @@ private static void generateClassAndMethodIndexHeader(File outputDirectory) thro bld.append("\n\n#endif // __CN1_CLASS_METHOD_INDEX_H__\n"); - FileOutputStream fos = new FileOutputStream(new File(outputDirectory, "cn1_class_method_index.h")); + OutputStream fos = new PreservingFileOutputStream(new File(outputDirectory, "cn1_class_method_index.h")); fos.write(bld.toString().getBytes("UTF-8")); fos.close(); - fos = new FileOutputStream(new File(outputDirectory, "cn1_class_method_index.m")); + fos = new PreservingFileOutputStream(new File(outputDirectory, "cn1_class_method_index.m")); fos.write(bldM.toString().getBytes("UTF-8")); fos.close(); } @@ -374,6 +380,7 @@ public static void writeOutput(File outputDirectory) throws Exception { System.out.println("outputDirectory is: " + outputDirectory.getAbsolutePath() ); String file = "Unknown File"; try { + System.out.println("Iterate first.."); for(ByteCodeClass bc : classes) { // special case for object if(bc.getClsName().equals("java_lang_Object")) { @@ -387,24 +394,33 @@ public static void writeOutput(File outputDirectory) throws Exception { } bc.setBaseInterfacesObject(lst); } + if(ByteCodeTranslator.verbose) System.out.println("Iterate second.."); for(ByteCodeClass bc : classes) { file = bc.getClsName(); bc.updateAllDependencies(); - } + } + if(ByteCodeTranslator.verbose) System.out.println("Mark deps.."); ByteCodeClass.markDependencies(classes); classes = ByteCodeClass.clearUnmarked(classes); // load the native sources (including user native code) + if(ByteCodeTranslator.verbose) System.out.println("Load natives.."); readNativeFiles(outputDirectory); // loop over methods and start eliminating the body of unused methods - eliminateUnusedMethods(); + if(ByteCodeTranslator.verbose) System.out.println("Eliminate unused.."); + if (!ByteCodeTranslator.draft) + eliminateUnusedMethods(); + if(ByteCodeTranslator.verbose) System.out.println("Generate common header.."); generateClassAndMethodIndexHeader(outputDirectory); + if(ByteCodeTranslator.verbose) System.out.println("Generate all classes.."); for(ByteCodeClass bc : classes) { file = bc.getClsName(); writeFile(bc.getClsName(), bc, outputDirectory); } + int created = PreservingFileOutputStream.total - PreservingFileOutputStream.preserved; + if(ByteCodeTranslator.verbose) System.out.println("Updated/created: "+created+" files"); } catch(Throwable t) { System.out.println("Error while working with the class: " + file); t.printStackTrace(); @@ -577,11 +593,11 @@ private static boolean isMethodUsed(BytecodeMethod m) { return false; } - + private static void writeFile(String clsName, ByteCodeClass cls, File outputDir) throws Exception { String fileName = clsName + "." + ByteCodeTranslator.output.extension(); - - FileOutputStream outMain = new FileOutputStream(new File(outputDir, fileName)); + + PreservingFileOutputStream outMain = new PreservingFileOutputStream(new File(outputDir, fileName)); // we also need to write the header file for iOS if(ByteCodeTranslator.output == ByteCodeTranslator.OutputType.OUTPUT_TYPE_IOS) { @@ -589,7 +605,7 @@ private static void writeFile(String clsName, ByteCodeClass cls, File outputDir) outMain.close(); String headerName = clsName + ".h"; - FileOutputStream outHeader = new FileOutputStream(new File(outputDir, headerName)); + PreservingFileOutputStream outHeader = new PreservingFileOutputStream(new File(outputDir, headerName)); outHeader.write(cls.generateCHeader().getBytes()); outHeader.close(); @@ -612,7 +628,20 @@ public void visitEnd() { public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { BytecodeMethod mtd = new BytecodeMethod(clsName, access, name, desc, signature, exceptions); cls.addMethod(mtd); - JSRInlinerAdapter a = new JSRInlinerAdapter(new MethodVisitorWrapper(super.visitMethod(access, name, desc, signature, exceptions), mtd), access, name, desc, signature, exceptions); + final int labelN = cls.getMethods().size(); + JSRInlinerAdapter a = new JSRInlinerAdapter(Opcodes.ASM5, + new MethodVisitorWrapper( + super.visitMethod(access, name, desc, signature, exceptions), + mtd), + access, name, desc, signature, exceptions) { + @Override + protected LabelNode getLabelNode(Label l) { + if (!(l.info instanceof LabelNode)) { + l.info = new LabelNode(new MyLabel("JSRL_"+labelN+"_"+l.toString())); + } + return (LabelNode) l.info; + } + }; return a; } @@ -671,8 +700,28 @@ public void visit(int version, int access, String name, String signature, String cls.setFinalClass(true); } super.visit(version, access, name, signature, superName, interfaces); - } - + } + + public static class MyLabel extends Label { + private final String labelName; + + public MyLabel(String labelName) { + this.labelName = labelName; + } + + int count = 0; + + @Override + public String toString() { + // persistence of label names causes non-regeneration of resulting sources. + return labelName; + } + } + + + + + class MethodVisitorWrapper extends MethodVisitor { private BytecodeMethod mtd; public MethodVisitorWrapper(MethodVisitor mv, BytecodeMethod mtd) { diff --git a/vm/ByteCodeTranslator/src/com/codename1/tools/translator/PreservingFileOutputStream.java b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/PreservingFileOutputStream.java new file mode 100644 index 0000000000..5f21788eff --- /dev/null +++ b/vm/ByteCodeTranslator/src/com/codename1/tools/translator/PreservingFileOutputStream.java @@ -0,0 +1,104 @@ +package com.codename1.tools.translator; + +import java.io.*; +import java.util.Arrays; +import java.util.HashSet; + +/** + * Created by admin on 8/17/15. + */ +public class PreservingFileOutputStream extends OutputStream { + + final public static String NEW_SUFFIX = ".new_by_translator"; + + static int total; + static int preserved; + boolean equal; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + File file; + + + public PreservingFileOutputStream(File file) throws FileNotFoundException { + this.file = file; + total++; + } + + @Override + public void write(int b) throws IOException { + baos.write(b); + } + + @Override + public void close() throws IOException { + equal = false; + if (file.exists() && file.length() == baos.size()) { + byte[] oldCopy = new byte[baos.size()]; + FileInputStream fis = new FileInputStream(file); + fis.read(oldCopy); + fis.close(); + final byte[] thisCopy = baos.toByteArray(); + equal = true; + for (int i = 0; i < thisCopy.length; i++) { + if (thisCopy[i] != oldCopy[i]) { + equal = false; + break; + } + } + if (equal) { + preserved++; + return; + } + } + System.out.println("Producing(stream): " + file.getName()); + FileOutputStream x = new FileOutputStream(file); + baos.writeTo(x); + x.close(); + } + + + /** + * move temporary created file to its final destination. If final destination doesn't differ from + * new file, do nothing (remove new file), thus keeping timestamps. + */ + public static boolean preservingMove(File from, File to) throws IOException { + if (from.exists() && to.exists() && to.length() == from.length()) { + byte[] thisCopy = new byte[(int)from.length()]; + FileInputStream fis = new FileInputStream(from); + fis.read(thisCopy); + fis.close(); + + byte[] oldCopy = new byte[(int)to.length()]; + fis = new FileInputStream(to); + fis.read(oldCopy); + fis.close(); + + boolean equal = true; + for (int i = 0; i < thisCopy.length; i++) { + if (thisCopy[i] != thisCopy[i]) { + equal = false; + break; + } + } + + if (equal) { + // preserve + return from.delete(); // keep old + } + } + if(ByteCodeTranslator.verbose) { + System.out.println("Producing(move): " + to.getName()); + } + to.delete(); + return from.renameTo(to); + } + + public static boolean finishWithNewFile(File newFile) throws IOException { + String path = newFile.getPath(); + if (path.endsWith(NEW_SUFFIX)) { + String finalDestination = path.substring(0, path.length() - NEW_SUFFIX.length()); + return preservingMove(newFile, new File(finalDestination)); + } + return true; // not a new file + } +} diff --git a/vm/ByteCodeTranslator/src/template/template/template-Info.plist b/vm/ByteCodeTranslator/src/template/template/template-Info.plist index 0faec8023e..1c2bac0bf6 100644 --- a/vm/ByteCodeTranslator/src/template/template/template-Info.plist +++ b/vm/ByteCodeTranslator/src/template/template/template-Info.plist @@ -50,5 +50,7 @@ <string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string> </array> + <key>UIAppFonts</key> + <array>${APP_FONTS}</array> </dict> </plist>