# HG changeset patch # User Lubomir Nerad # Date 1363888126 -3600 # Node ID 151f4ccd7673eca52bf15b3b73dea27018a00eb5 # Parent eba8fd2ff535f2500f4e6f11344a9b9ad2bd69bc Initial attempt for advanced obfuscation diff -r eba8fd2ff535 -r 151f4ccd7673 javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Wed Mar 20 13:55:53 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java Thu Mar 21 18:48:46 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 eba8fd2ff535 -r 151f4ccd7673 javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Wed Mar 20 13:55:53 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Thu Mar 21 18:48:46 2013 +0100 @@ -31,7 +31,7 @@ ${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/ index.xhtml ${project.build.directory}/bck2brwsr.js - SIMPLE_OPTIMIZATIONS + FULL diff -r eba8fd2ff535 -r 151f4ccd7673 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Mar 20 13:55:53 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Thu Mar 21 18:48:46 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 @@ -138,6 +147,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); @@ -152,33 +163,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 = "); @@ -224,14 +241,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; } @@ -239,28 +259,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 +1566,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 +1601,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 eba8fd2ff535 -r 151f4ccd7673 rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Wed Mar 20 13:55:53 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Thu Mar 21 18:48:46 2013 +0100 @@ -22,27 +22,40 @@ 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 implements SourceFile.Generator { - private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" }; +final class ClosureWrapper extends CommandLineRunner { + private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ }; - private String code; + private final ClosuresObfuscationDelegate obfuscationDelegate; private final Bck2Brwsr.Resources res; private final StringArray classes; - private ClosureWrapper(Appendable out, ObfuscationLevel obfuscationLevel, + + private String compiledCode; + private String externsCode; + + private ClosureWrapper(Appendable out, + String compilationLevel, + ClosuresObfuscationDelegate obfuscationDelegate, Bck2Brwsr.Resources res, StringArray classes) { super( - generateArguments(obfuscationLevel), + generateArguments(compilationLevel), new PrintStream(new APS(out)), System.err ); + this.obfuscationDelegate = obfuscationDelegate; this.res = res; this.classes = classes; } @@ -52,21 +65,62 @@ 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", this)); + return Collections.nCopies( + 1, + SourceFile.fromGenerator( + "bck2brwsr-raw.js", + new SourceFile.Generator() { + @Override + public String getCode() { + return getCompiledCode(); + } + })); } + @Override - public String getCode() { - if (code == null) { + 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); + VM.compile(res, sb, classes, obfuscationDelegate); + compiledCode = sb.toString(); } catch (IOException ex) { - code = ex.getMessage(); + compiledCode = ex.getMessage(); } - code = sb.toString(); } - return code; + 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 { @@ -81,21 +135,198 @@ } } - private static String[] generateArguments( - ObfuscationLevel obfuscationLevel) { + private static String[] generateArguments(String compilationLevel) { String[] finalArgs = ARGS.clone(); - finalArgs[1] = obfuscationLevel.toString(); + finalArgs[1] = compilationLevel; return finalArgs; } static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException { - ClosureWrapper cw = new ClosureWrapper(w, obfuscationLevel, resources, - arr); + 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 eba8fd2ff535 -r 151f4ccd7673 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 Thu Mar 21 18:48:46 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 eba8fd2ff535 -r 151f4ccd7673 rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java Wed Mar 20 13:55:53 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java Thu Mar 21 18:48:46 2013 +0100 @@ -18,14 +18,13 @@ package org.apidesign.vm4brwsr; /** - * Defines obfuscation level of produced JavaScript files. Corresponds to the - * closure compiler compilation level. + * Defines obfuscation level of produced JavaScript files. * * @since 0.5 */ public enum ObfuscationLevel { NONE, - WHITESPACE_ONLY, - SIMPLE_OPTIMIZATIONS, - ADVANCED_OPTIMIZATIONS + MINIMAL, + MEDIUM, + FULL } diff -r eba8fd2ff535 -r 151f4ccd7673 rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Wed Mar 20 13:55:53 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Thu Mar 21 18:48:46 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 eba8fd2ff535 -r 151f4ccd7673 rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Wed Mar 20 13:55:53 2013 +0100 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Thu Mar 21 18:48:46 2013 +0100 @@ -151,5 +151,10 @@ String accessClass(String classOperation) { return "vm." + classOperation; } + + @Override + String getVMObject() { + return "vm"; + } } } diff -r eba8fd2ff535 -r 151f4ccd7673 rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Wed Mar 20 13:55:53 2013 +0100 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Thu Mar 21 18:48:46 2013 +0100 @@ -43,4 +43,9 @@ @Override protected void requireScript(String resourcePath) { } + + @Override + String getVMObject() { + return "global"; + } }