# HG changeset patch # User Jaroslav Tulach # Date 1364214783 -3600 # Node ID 8c14a9f0c232c6f6f1f5529e4d2f7c4c32cfd6ec # Parent 6168fb585ab4f20cbcfd38252e6a0d9a6b10a5f9# Parent 60d9ea48ec9938f1bc33084ce051de50072eddf5 Merging Lubo's closure work into default branch. Release 0.5 will have (partial) support for effective static compilation diff -r 6168fb585ab4 -r 8c14a9f0c232 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Fri Mar 22 00:05:41 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Mon Mar 25 13:33:03 2013 +0100 @@ -70,18 +70,18 @@ @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive" }, body = "var bnd = {\n" - + " read: function() {\n" + + " 'read': function() {\n" + " var v = model[getter]();\n" + " return v;\n" + " },\n" - + " owner: bindings\n" + + " 'owner': bindings\n" + "};\n" + "if (setter != null) {\n" - + " bnd.write = function(val) {\n" + + " bnd['write'] = function(val) {\n" + " model[setter](primitive ? new Number(val) : val);\n" + " };\n" + "}\n" - + "bindings[prop] = ko.computed(bnd);" + + "bindings[prop] = ko['computed'](bnd);" ) private static void bind( Object bindings, Object model, String prop, String getter, String setter, boolean primitive diff -r 6168fb585ab4 -r 8c14a9f0c232 javaquery/demo-calculator-dynamic/pom.xml --- a/javaquery/demo-calculator-dynamic/pom.xml Fri Mar 22 00:05:41 2013 +0100 +++ b/javaquery/demo-calculator-dynamic/pom.xml Mon Mar 25 13:33:03 2013 +0100 @@ -27,7 +27,8 @@ - org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml + ${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html + index.xhtml diff -r 6168fb585ab4 -r 8c14a9f0c232 javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml --- a/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Fri Mar 22 00:05:41 2013 +0100 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/demo/calc/Calculator.xhtml Mon Mar 25 13:33:03 2013 +0100 @@ -80,7 +80,7 @@ diff -r 6168fb585ab4 -r 8c14a9f0c232 javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Fri Mar 22 00:05:41 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Mon Mar 25 13:33:03 2013 +0100 @@ -31,6 +31,7 @@ ${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/ index.xhtml ${project.build.directory}/bck2brwsr.js + FULL diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/emul/mini/src/main/java/java/lang/reflect/Array.java --- a/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Mon Mar 25 13:33:03 2013 +0100 @@ -636,9 +636,9 @@ + "arr.jvmName = sig;\n" + "return arr;" ) - private static native Object newArray(boolean primitive, String sig, int length); + static native Object newArray(boolean primitive, String sig, int length); - private static Object multiNewArray(String sig, int[] dims, int index) + static Object multiNewArray(String sig, int[] dims, int index) throws IllegalArgumentException, NegativeArraySizeException { if (dims.length == index + 1) { return newArray(sig.length() == 2, sig, dims[index]); diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java --- a/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Mon Mar 25 13:33:03 2013 +0100 @@ -35,6 +35,7 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.apidesign.vm4brwsr.ObfuscationLevel; /** Compiles classes into JavaScript. */ @Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) @@ -47,7 +48,7 @@ /** JavaScript file to generate */ @Parameter private File javascript; - + /** Additional classes that should be pre-compiled into the javascript * file. By default compiles all classes found under classes * directory and their transitive closure. @@ -57,8 +58,14 @@ @Parameter(defaultValue="${project}") private MavenProject prj; - - + + /** + * The obfuscation level for the generated JavaScript file. + * + * @since 0.5 + */ + @Parameter(defaultValue="NONE") + private ObfuscationLevel obfuscation; @Override public void execute() throws MojoExecutionException { @@ -81,7 +88,11 @@ try { URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); FileWriter w = new FileWriter(javascript); - Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); + Bck2Brwsr.newCompiler(). + obfuscation(obfuscation). + resources(url). + addRootClasses(arr.toArray(new String[0])). + generate(w); w.close(); } catch (IOException ex) { throw new MojoExecutionException("Can't compile", ex); diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/pom.xml --- a/rt/vm/pom.xml Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/pom.xml Mon Mar 25 13:33:03 2013 +0100 @@ -83,18 +83,23 @@ generate-js process-classes + + java + + -cp + + org.apidesign.vm4brwsr.Main + --obfuscatelevel + MINIMAL + ${project.build.directory}/bck2brwsr.js + org/apidesign/vm4brwsr/Bck2Brwsr + + - java + exec - - org.apidesign.vm4brwsr.Main - - ${project.build.directory}/bck2brwsr.js - org/apidesign/vm4brwsr/Bck2Brwsr - - maven-assembly-plugin @@ -140,5 +145,11 @@ ${project.version} compile + + com.google.javascript + closure-compiler + r2388 + compile + diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Mon Mar 25 13:33:03 2013 +0100 @@ -54,46 +54,127 @@ * @author Jaroslav Tulach */ public final class Bck2Brwsr { - private Bck2Brwsr() { + private final ObfuscationLevel level; + private final StringArray classes; + private final Resources res; + + private Bck2Brwsr(ObfuscationLevel level, StringArray classes, Resources resources) { + this.level = level; + this.classes = classes; + this.res = resources; } - /** Generates virtual machine from bytes served by a resources + /** Helper method to generate virtual machine from bytes served by a resources * provider. - * + * * @param out the output to write the generated JavaScript to * @param resources provider of class files to use * @param classes additional classes to include in the generated script * @throws IOException I/O exception can be thrown when something goes wrong */ public static void generate(Appendable out, Resources resources, String... classes) throws IOException { - StringArray arr = StringArray.asList(classes); - arr.add(VM.class.getName().replace('.', '/')); - VM.compile(resources, out, arr); + newCompiler().resources(resources).addRootClasses(classes).generate(out); } - - /** Generates virtual machine from bytes served by a class loader. - * + + /** Helper method to generate virtual machine from bytes served by a class loader. + * * @param out the output to write the generated JavaScript to * @param loader class loader to load needed classes from * @param classes additional classes to include in the generated script * @throws IOException I/O exception can be thrown when something goes wrong */ - public static void generate(Appendable out, final ClassLoader loader, String... classes) throws IOException { - class R implements Resources { - @Override - public InputStream get(String name) throws IOException { - Enumeration en = loader.getResources(name); - URL u = null; - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - return u.openStream(); + public static void generate(Appendable out, ClassLoader loader, String... classes) throws IOException { + newCompiler().resources(loader).addRootClasses(classes).generate(out); + } + + /** Creates new instance of Bck2Brwsr compiler which is ready to generate + * empty Bck2Brwsr virtual machine. The instance can be further + * configured by calling chain of methods. For example: + *
+     * {@link #createCompiler()}.{@link #resources(org.apidesign.vm4brwsr.Bck2Brwsr.Resources) resources(loader)}.{@link #addRootClasses(java.lang.String[]) addRootClasses("your/Clazz")}.{@link #generate(java.lang.Appendable) generate(out)};
+     * 
+ * + * @return new instance of the Bck2Brwsr compiler + * @since 0.5 + */ + public static Bck2Brwsr newCompiler() { + StringArray arr = StringArray.asList(VM.class.getName().replace('.', '/')); + return new Bck2Brwsr(ObfuscationLevel.NONE, arr, null); + } + + /** Creates new instance of the Bck2Brwsr compiler which inherits + * all values from this instance and adds additional classes + * to the list of those that should be compiled by the {@link #generate(java.lang.Appendable)} + * method. + * + * @param classes the classes to add to the compilation + * @return new instance of the compiler + */ + public Bck2Brwsr addRootClasses(String... classes) { + if (classes.length == 0) { + return this; + } else { + return new Bck2Brwsr(level, this.classes.addAndNew(classes), res); + } + } + + /** Changes the obfuscation level for the compiler by creating new instance + * which inherits all values from this and adjust the level + * of obfuscation. + * + * @param level the new level of obfuscation + * @return new instance of the compiler with changed level of obfuscation + * @since 0.5 + */ + public Bck2Brwsr obfuscation(ObfuscationLevel level) { + return new Bck2Brwsr(level, classes, res); + } + + /** A way to change the provider of additional resources (classes) for the + * compiler. + * + * @param res the implementation of resources provider + * @return new instance of the compiler with all values remaining the same, just + * with different resources provider + * @since 0.5 + */ + public Bck2Brwsr resources(Resources res) { + return new Bck2Brwsr(level, classes, res); + } + + /** A way to change the provider of additional resources (classes) for the + * compiler by specifying classloader to use for loading them. + * + * @param loader class loader to load the resources from + * @return new instance of the compiler with all values being the same, just + * different resources provider + * @since 0.5 + */ + public Bck2Brwsr resources(final ClassLoader loader) { + return resources(new LdrRsrcs(loader)); + } + + /** Generates virtual machine based on previous configuration of the + * compiler. + * + * @param out the output to write the generated JavaScript to + * @since 0.5 + */ + public void generate(Appendable out) throws IOException { + Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader()); + if (level != ObfuscationLevel.NONE) { + try { + ClosureWrapper.produceTo(out, level, r, classes); + return; + } catch (IOException ex) { + throw ex; + } catch (Throwable ex) { + out.append("/* Failed to obfuscate: " + ex.getMessage() + + " */\n"); } } - generate(out, new R(), classes); + + VM.compile(r, out, classes); } /** Provider of resources (classes and other files). The diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Mar 25 13:33:03 2013 +0100 @@ -28,9 +28,16 @@ abstract class ByteCodeToJavaScript { private ClassData jc; final Appendable out; + final ObfuscationDelegate obfuscationDelegate; protected ByteCodeToJavaScript(Appendable out) { + this(out, ObfuscationDelegate.NULL); + } + + protected ByteCodeToJavaScript( + Appendable out, ObfuscationDelegate obfuscationDelegate) { this.out = out; + this.obfuscationDelegate = obfuscationDelegate; } /* Collects additional required resources. @@ -58,7 +65,9 @@ /* protected */ String accessClass(String classOperation) { return classOperation; } - + + abstract String getVMObject(); + /** Prints out a debug message. * * @param msg the message @@ -92,7 +101,9 @@ "resource", "processByteCode" ); if (arr != null) { - requireScript(arr[0]); + if (!arr[0].isEmpty()) { + requireScript(arr[0]); + } if ("0".equals(arr[1])) { return null; } @@ -104,7 +115,8 @@ StringArray toInitilize = new StringArray(); final String className = className(jc); out.append("\n\n").append(assignClass(className)); - out.append("function CLS() {"); + out.append("function ").append(className).append("() {"); + out.append("\n var CLS = ").append(className).append(';'); out.append("\n if (!CLS.$class) {"); if (proto == null) { String sc = jc.getSuperClassName(); // with _ @@ -136,6 +148,8 @@ append(className).append('_').append(v.getName()) .append("; };"); } + + obfuscationDelegate.exportField(out, "c", "_" + v.getName(), v); } for (MethodData m : jc.getMethods()) { byte[] onlyArr = m.findAnnotationData(true); @@ -150,33 +164,39 @@ } continue; } - String prefix; + String destObject; String mn; + out.append("\n "); if (m.isStatic()) { - prefix = "\n c."; - mn = generateStaticMethod(prefix, m, toInitilize); + destObject = "c"; + mn = generateStaticMethod(destObject, m, toInitilize); } else { if (m.isConstructor()) { - prefix = "\n CLS."; - mn = generateInstanceMethod(prefix, m); + destObject = "CLS"; + mn = generateInstanceMethod(destObject, m); } else { - prefix = "\n c."; - mn = generateInstanceMethod(prefix, m); + destObject = "c"; + mn = generateInstanceMethod(destObject, m); } } + obfuscationDelegate.exportMethod(out, destObject, mn, m); byte[] runAnno = m.findAnnotationData(false); if (runAnno != null) { - out.append(prefix).append(mn).append(".anno = {"); + out.append("\n ").append(destObject).append(".").append(mn).append(".anno = {"); generateAnno(jc, out, runAnno); out.append("\n };"); } - out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";"); - out.append(prefix).append(mn).append(".cls = CLS;"); + out.append("\n ").append(destObject).append(".").append(mn).append(".access = " + m.getAccess()).append(";"); + out.append("\n ").append(destObject).append(".").append(mn).append(".cls = CLS;"); } out.append("\n c.constructor = CLS;"); - out.append("\n c.$instOf_").append(className).append(" = true;"); + String instOfName = "$instOf_" + className; + out.append("\n c.").append(instOfName).append(" = true;"); + obfuscationDelegate.exportJSProperty(out, "c", instOfName); for (String superInterface : jc.getSuperInterfaces()) { - out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;"); + instOfName = "$instOf_" + superInterface.replace('/', '_'); + out.append("\n c.").append(instOfName).append(" = true;"); + obfuscationDelegate.exportJSProperty(out, "c", instOfName); } out.append("\n CLS.$class = 'temp';"); out.append("\n CLS.$class = "); @@ -222,14 +242,17 @@ out.append("\n }"); out.append("\n return arguments[0] ? new CLS() : CLS.prototype;"); out.append("\n};"); + + obfuscationDelegate.exportClass(out, getVMObject(), className, jc); + // StringBuilder sb = new StringBuilder(); // for (String init : toInitilize.toArray()) { // sb.append("\n").append(init).append("();"); // } return ""; } - private String generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException { - String jsb = javaScriptBody(prefix, m, true); + private String generateStaticMethod(String destObject, MethodData m, StringArray toInitilize) throws IOException { + String jsb = javaScriptBody(destObject, m, true); if (jsb != null) { return jsb; } @@ -237,28 +260,28 @@ if (mn.equals("class__V")) { toInitilize.add(accessClass(className(jc)) + "(false)." + mn); } - generateMethod(prefix, mn, m); + generateMethod(destObject, mn, m); return mn; } - private String generateInstanceMethod(String prefix, MethodData m) throws IOException { - String jsb = javaScriptBody(prefix, m, false); + private String generateInstanceMethod(String destObject, MethodData m) throws IOException { + String jsb = javaScriptBody(destObject, m, false); if (jsb != null) { return jsb; } final String mn = findMethodName(m, new StringBuilder()); - generateMethod(prefix, mn, m); + generateMethod(destObject, mn, m); return mn; } - private void generateMethod(String prefix, String name, MethodData m) + private void generateMethod(String destObject, String name, MethodData m) throws IOException { final StackMapIterator stackMapIterator = m.createStackMapIterator(); TrapDataIterator trap = m.getTrapDataIterator(); final LocalsMapper lmapper = new LocalsMapper(stackMapIterator.getArguments()); - out.append(prefix).append(name).append(" = function("); + out.append(destObject).append(".").append(name).append(" = function("); lmapper.outputArguments(out, m.isStatic()); out.append(") {").append("\n"); @@ -1546,7 +1569,7 @@ return s; } - private String javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException { + private String javaScriptBody(String destObject, MethodData m, boolean isStatic) throws IOException { byte[] arr = m.findAnnotationData(true); if (arr == null) { return null; @@ -1581,7 +1604,7 @@ } StringBuilder cnt = new StringBuilder(); final String mn = findMethodName(m, cnt); - out.append(prefix).append(mn); + out.append(destObject).append(".").append(mn); out.append(" = function("); String space = ""; int index = 0; diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Mon Mar 25 13:33:03 2013 +0100 @@ -0,0 +1,334 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import com.google.javascript.jscomp.CommandLineRunner; +import com.google.javascript.jscomp.SourceFile; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; + +/** + * + * @author Jaroslav Tulach + */ +@ExtraJavaScript(processByteCode = false, resource="") +final class ClosureWrapper extends CommandLineRunner { + private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ }; + + private final ClosuresObfuscationDelegate obfuscationDelegate; + private final Bck2Brwsr.Resources res; + private final StringArray classes; + + private String compiledCode; + private String externsCode; + + private ClosureWrapper(Appendable out, + String compilationLevel, + ClosuresObfuscationDelegate obfuscationDelegate, + Bck2Brwsr.Resources res, StringArray classes) { + super( + generateArguments(compilationLevel), + new PrintStream(new APS(out)), System.err + ); + this.obfuscationDelegate = obfuscationDelegate; + this.res = res; + this.classes = classes; + } + + @Override + protected List createInputs(List files, boolean allowStdIn) throws FlagUsageException, IOException { + if (files.size() != 1 || !"bck2brwsr-raw.js".equals(files.get(0))) { + throw new IOException("Unexpected files: " + files); + } + return Collections.nCopies( + 1, + SourceFile.fromGenerator( + "bck2brwsr-raw.js", + new SourceFile.Generator() { + @Override + public String getCode() { + return getCompiledCode(); + } + })); + } + + + @Override + protected List createExterns() + throws FlagUsageException, IOException { + final List externsFiles = + new ArrayList(super.createExterns()); + + externsFiles.add( + SourceFile.fromGenerator( + "bck2brwsr_externs.js", + new SourceFile.Generator() { + @Override + public String getCode() { + return getExternsCode(); + } + })); + return externsFiles; + } + + private String getCompiledCode() { + if (compiledCode == null) { + StringBuilder sb = new StringBuilder(); + try { + VM.compile(res, sb, classes, obfuscationDelegate); + compiledCode = sb.toString(); + } catch (IOException ex) { + compiledCode = ex.getMessage(); + } + } + return compiledCode; + } + + private String getExternsCode() { + if (externsCode == null) { + // need compiled code at this point + getCompiledCode(); + + final StringBuilder sb = new StringBuilder("function RAW() {};\n"); + for (final String extern: obfuscationDelegate.getExterns()) { + sb.append("RAW.prototype.").append(extern).append(";\n"); + } + externsCode = sb.toString(); + } + return externsCode; + } + + private static final class APS extends OutputStream { + private final Appendable out; + + public APS(Appendable out) { + this.out = out; + } + @Override + public void write(int b) throws IOException { + out.append((char)b); + } + } + + private static String[] generateArguments(String compilationLevel) { + String[] finalArgs = ARGS.clone(); + finalArgs[1] = compilationLevel; + + return finalArgs; + } + + static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException { + ClosureWrapper cw = create(w, obfuscationLevel, resources, arr); + try { + return cw.doRun(); + } catch (FlagUsageException ex) { + throw new IOException(ex); + } + } + + private static ClosureWrapper create(Appendable w, + ObfuscationLevel obfuscationLevel, + Bck2Brwsr.Resources resources, + StringArray arr) { + switch (obfuscationLevel) { + case MINIMAL: + return new ClosureWrapper(w, "SIMPLE_OPTIMIZATIONS", + new SimpleObfuscationDelegate(), + resources, arr); +/* + case MEDIUM: + return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", + new MediumObfuscationDelegate(), + resources, arr); +*/ + case FULL: + return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", + new FullObfuscationDelegate(), + resources, arr); + default: + throw new IllegalArgumentException( + "Unsupported level: " + obfuscationLevel); + } + } + + private static abstract class ClosuresObfuscationDelegate + extends ObfuscationDelegate { + public abstract Collection getExterns(); + } + + private static final class SimpleObfuscationDelegate + extends ClosuresObfuscationDelegate { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) throws IOException { + } + + @Override + public void exportClass(Appendable out, + String destObject, + String mangledName, + ClassData classData) throws IOException { + } + + @Override + public void exportMethod(Appendable out, + String destObject, + String mangledName, + MethodData methodData) throws IOException { + } + + @Override + public void exportField(Appendable out, + String destObject, + String mangledName, + FieldData fieldData) throws IOException { + } + + @Override + public Collection getExterns() { + return Collections.EMPTY_LIST; + } + } + + private static abstract class AdvancedObfuscationDelegate + extends ClosuresObfuscationDelegate { + private static final String[] INITIAL_EXTERNS = { + "bck2brwsr", + "$class", + "anno", + "array", + "access", + "cls", + "vm", + "loadClass", + "loadBytes", + "jvmName", + "primitive", + "superclass", + "cnstr", + "add32", + "sub32", + "mul32", + "neg32", + "toInt8", + "toInt16", + "next32", + "high32", + "toInt32", + "toFP", + "toLong", + "toExactString", + "add64", + "sub64", + "mul64", + "and64", + "or64", + "xor64", + "shl64", + "shr64", + "ushr64", + "compare64", + "neg64", + "div32", + "mod32", + "div64", + "mod64", + "at", + "getClass__Ljava_lang_Class_2", + "clone__Ljava_lang_Object_2" + }; + + private final Collection externs; + + protected AdvancedObfuscationDelegate() { + externs = new ArrayList(Arrays.asList(INITIAL_EXTERNS)); + } + + @Override + public void exportClass(Appendable out, + String destObject, + String mangledName, + ClassData classData) throws IOException { + exportJSProperty(out, destObject, mangledName); + } + + @Override + public void exportMethod(Appendable out, + String destObject, + String mangledName, + MethodData methodData) throws IOException { + if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) { + exportJSProperty(out, destObject, mangledName); + } + } + + @Override + public void exportField(Appendable out, + String destObject, + String mangledName, + FieldData fieldData) throws IOException { + if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) { + exportJSProperty(out, destObject, mangledName); + } + } + + @Override + public Collection getExterns() { + return externs; + } + + protected void addExtern(String extern) { + externs.add(extern); + } + } + + private static final class MediumObfuscationDelegate + extends AdvancedObfuscationDelegate { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) { + addExtern(propertyName); + } + } + + private static final class FullObfuscationDelegate + extends AdvancedObfuscationDelegate { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) throws IOException { + out.append("\n").append(destObject).append("['") + .append(propertyName) + .append("'] = ") + .append(destObject).append(".").append(propertyName) + .append(";\n"); + } + } +} diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/LdrRsrcs.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/LdrRsrcs.java Mon Mar 25 13:33:03 2013 +0100 @@ -0,0 +1,48 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** Implementation of Resources that delegates to some class loader. + * + * @author Jaroslav Tulach + */ +final class LdrRsrcs implements Bck2Brwsr.Resources { + private final ClassLoader loader; + + LdrRsrcs(ClassLoader loader) { + this.loader = loader; + } + + @Override + public InputStream get(String name) throws IOException { + Enumeration en = loader.getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + return u.openStream(); + } +} diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Mon Mar 25 13:33:03 2013 +0100 @@ -31,20 +31,62 @@ private Main() {} public static void main(String... args) throws IOException { + final String obfuscate = "--obfuscatelevel"; + if (args.length < 2) { System.err.println("Bck2Brwsr Translator from Java(tm) to JavaScript, (c) Jaroslav Tulach 2012"); - System.err.println("Usage: java -cp ... -jar ... java/lang/Class org/your/App ..."); - return; + System.err.println("Usage: java -cp ... -jar ... ["); + System.err.print(obfuscate); + System.err.print(" ["); + boolean first = true; + for (ObfuscationLevel l : ObfuscationLevel.values()) { + if (!first) { + System.err.print('|'); + } + System.err.print(l.name()); + first = false; + } + + System.err.println("] java/lang/Class org/your/App ..."); + System.exit(9); } - Writer w = new BufferedWriter(new FileWriter(args[0])); - StringArray classes = StringArray.asList(args); - classes.delete(0); - try { - Bck2Brwsr.generate(w, Main.class.getClassLoader(), - classes.toArray()); - } finally { - w.close(); + ObfuscationLevel obfLevel = ObfuscationLevel.NONE; + StringArray classes = new StringArray(); + String generateTo = null; + for (int i = 0; i < args.length; i++) { + if (obfuscate.equals(args[i])) { // NOI18N + i++; + try { + obfLevel = ObfuscationLevel.valueOf(args[i]); + } catch (Exception e) { + System.err.print(obfuscate); + System.err.print(" parameter needs to be followed by one of "); + boolean first = true; + for (ObfuscationLevel l : ObfuscationLevel.values()) { + if (!first) { + System.err.print(", "); + } + System.err.print(l.name()); + first = false; + } + System.err.println(); + System.exit(1); + } + continue; + } + if (generateTo == null) { + generateTo = args[i]; + } else { + classes = classes.addAndNew(args[i]); + } + } + try (Writer w = new BufferedWriter(new FileWriter(generateTo))) { + Bck2Brwsr.newCompiler(). + obfuscation(obfLevel). + addRootClasses(classes.toArray()). + resources(Main.class.getClassLoader()). + generate(w); } } } diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java Mon Mar 25 13:33:03 2013 +0100 @@ -0,0 +1,75 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; + +abstract class ObfuscationDelegate { + static ObfuscationDelegate NULL = + new ObfuscationDelegate() { + @Override + public void exportJSProperty(Appendable out, + String destObject, + String propertyName) + throws IOException { + } + + @Override + public void exportClass(Appendable out, + String destObject, + String mangledName, + ClassData classData) + throws IOException { + } + + @Override + public void exportMethod(Appendable out, + String destObject, + String mangledName, + MethodData methodData) + throws IOException { + } + + @Override + public void exportField(Appendable out, + String destObject, + String mangledName, + FieldData fieldData) + throws IOException { + } + }; + + public abstract void exportJSProperty( + Appendable out, String destObject, String propertyName) + throws IOException; + + public abstract void exportClass( + Appendable out, String destObject, String mangledName, + ClassData classData) throws IOException; + + public abstract void exportMethod( + Appendable out, String destObject, String mangledName, + MethodData methodData) throws IOException; + + public abstract void exportField( + Appendable out, String destObject, String mangledName, + FieldData fieldData) throws IOException; +} diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java Mon Mar 25 13:33:03 2013 +0100 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +/** + * Defines obfuscation level of produced JavaScript files. + * + * @since 0.5 + */ +public enum ObfuscationLevel { + /** Generated JavaScript is (sort of) human readable. Useful for debugging. + * Dynamic capabilities of the virtual machine work on all classes. + */ + NONE, + /** White spaces are removed. Names of external symbols remain unchanged. + * Dynamic capabilities of the virtual machine work on all classes. + */ + MINIMAL, +// temporarily commented out before merge. not well defined yet: +// MEDIUM, + /** Aggressive obfuscation of everything. Compact, unreadable "one-liner". + * One cannot load classes dynamically. Useful mostly for static compilation + * of self contained application. + */ + FULL +} diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java Mon Mar 25 13:33:03 2013 +0100 @@ -43,6 +43,25 @@ } arr[arr.length - 1] = s; } + + StringArray addAndNew(String... values) { + int j; + String[] tmp; + if (arr == null) { + tmp = new String[values.length]; + j = 0; + } else { + tmp = new String[arr.length + values.length]; + for (int i = 0; i < arr.length; i++) { + tmp[i] = arr[i]; + } + j = arr.length; + } + for (int i = 0; i < values.length;) { + tmp[j++] = values[i++]; + } + return new StringArray(tmp); + } public String[] toArray() { return arr == null ? new String[0] : arr; @@ -93,5 +112,4 @@ } return -1; } - } diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Mon Mar 25 13:33:03 2013 +0100 @@ -28,7 +28,11 @@ public VM(Appendable out) { super(out); } - + + private VM(Appendable out, ObfuscationDelegate obfuscationDelegate) { + super(out, obfuscationDelegate); + } + static { // uses VMLazy to load dynamic classes boolean assertsOn = false; @@ -47,6 +51,12 @@ static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException { new VM(out).doCompile(l, names); } + + static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names, + ObfuscationDelegate obfuscationDelegate) throws IOException { + new VM(out, obfuscationDelegate).doCompile(l, names); + } + protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); StringArray processed = new StringArray(); @@ -230,4 +240,9 @@ String accessClass(String className) { return "vm." + className; } + + @Override + String getVMObject() { + return "vm"; + } } diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Mon Mar 25 13:33:03 2013 +0100 @@ -151,5 +151,10 @@ String accessClass(String classOperation) { return "vm." + classOperation; } + + @Override + String getVMObject() { + return "vm"; + } } } diff -r 6168fb585ab4 -r 8c14a9f0c232 rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Fri Mar 22 00:05:41 2013 +0100 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Mon Mar 25 13:33:03 2013 +0100 @@ -43,4 +43,9 @@ @Override protected void requireScript(String resourcePath) { } + + @Override + String getVMObject() { + return "global"; + } }