Initial attempt for advanced obfuscation closure
authorLubomir Nerad <lubomir.nerad@oracle.com>
Thu, 21 Mar 2013 18:48:46 +0100
branchclosure
changeset 869151f4ccd7673
parent 863 eba8fd2ff535
child 872 986c9bc2642f
Initial attempt for advanced obfuscation
javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java
javaquery/demo-calculator/pom.xml
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java
rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java
     1.1 --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java	Wed Mar 20 13:55:53 2013 +0100
     1.2 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/Knockout.java	Thu Mar 21 18:48:46 2013 +0100
     1.3 @@ -70,18 +70,18 @@
     1.4      
     1.5      @JavaScriptBody(args = { "bindings", "model", "prop", "getter", "setter", "primitive" }, body =
     1.6            "var bnd = {\n"
     1.7 -        + "  read: function() {\n"
     1.8 +        + "  'read': function() {\n"
     1.9          + "    var v = model[getter]();\n"
    1.10          + "    return v;\n"
    1.11          + "  },\n"
    1.12 -        + "  owner: bindings\n"
    1.13 +        + "  'owner': bindings\n"
    1.14          + "};\n"
    1.15          + "if (setter != null) {\n"
    1.16 -        + "  bnd.write = function(val) {\n"
    1.17 +        + "  bnd['write'] = function(val) {\n"
    1.18          + "    model[setter](primitive ? new Number(val) : val);\n"
    1.19          + "  };\n"
    1.20          + "}\n"
    1.21 -        + "bindings[prop] = ko.computed(bnd);"
    1.22 +        + "bindings[prop] = ko['computed'](bnd);"
    1.23      )
    1.24      private static void bind(
    1.25          Object bindings, Object model, String prop, String getter, String setter, boolean primitive
     2.1 --- a/javaquery/demo-calculator/pom.xml	Wed Mar 20 13:55:53 2013 +0100
     2.2 +++ b/javaquery/demo-calculator/pom.xml	Thu Mar 21 18:48:46 2013 +0100
     2.3 @@ -31,7 +31,7 @@
     2.4                      <directory>${project.build.directory}/${project.build.finalName}-bck2brwsr/public_html/</directory>
     2.5                      <startpage>index.xhtml</startpage>
     2.6                      <javascript>${project.build.directory}/bck2brwsr.js</javascript>
     2.7 -                    <obfuscation>SIMPLE_OPTIMIZATIONS</obfuscation>
     2.8 +                    <obfuscation>FULL</obfuscation>
     2.9                  </configuration>
    2.10              </plugin>
    2.11           <plugin>
     3.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Wed Mar 20 13:55:53 2013 +0100
     3.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Thu Mar 21 18:48:46 2013 +0100
     3.3 @@ -28,9 +28,16 @@
     3.4  abstract class ByteCodeToJavaScript {
     3.5      private ClassData jc;
     3.6      final Appendable out;
     3.7 +    final ObfuscationDelegate obfuscationDelegate;
     3.8  
     3.9      protected ByteCodeToJavaScript(Appendable out) {
    3.10 +        this(out, ObfuscationDelegate.NULL);
    3.11 +    }
    3.12 +
    3.13 +    protected ByteCodeToJavaScript(
    3.14 +            Appendable out, ObfuscationDelegate obfuscationDelegate) {
    3.15          this.out = out;
    3.16 +        this.obfuscationDelegate = obfuscationDelegate;
    3.17      }
    3.18      
    3.19      /* Collects additional required resources.
    3.20 @@ -58,7 +65,9 @@
    3.21      /* protected */ String accessClass(String classOperation) {
    3.22          return classOperation;
    3.23      }
    3.24 -    
    3.25 +
    3.26 +    abstract String getVMObject();
    3.27 +
    3.28      /** Prints out a debug message. 
    3.29       * 
    3.30       * @param msg the message
    3.31 @@ -138,6 +147,8 @@
    3.32                      append(className).append('_').append(v.getName())
    3.33                     .append("; };");
    3.34              }
    3.35 +
    3.36 +            obfuscationDelegate.exportField(out, "c", "_" + v.getName(), v);
    3.37          }
    3.38          for (MethodData m : jc.getMethods()) {
    3.39              byte[] onlyArr = m.findAnnotationData(true);
    3.40 @@ -152,33 +163,39 @@
    3.41                  }
    3.42                  continue;
    3.43              }
    3.44 -            String prefix;
    3.45 +            String destObject;
    3.46              String mn;
    3.47 +            out.append("\n    ");
    3.48              if (m.isStatic()) {
    3.49 -                prefix = "\n    c.";
    3.50 -                mn = generateStaticMethod(prefix, m, toInitilize);
    3.51 +                destObject = "c";
    3.52 +                mn = generateStaticMethod(destObject, m, toInitilize);
    3.53              } else {
    3.54                  if (m.isConstructor()) {
    3.55 -                    prefix = "\n    CLS.";
    3.56 -                    mn = generateInstanceMethod(prefix, m);
    3.57 +                    destObject = "CLS";
    3.58 +                    mn = generateInstanceMethod(destObject, m);
    3.59                  } else {
    3.60 -                    prefix = "\n    c.";
    3.61 -                    mn = generateInstanceMethod(prefix, m);
    3.62 +                    destObject = "c";
    3.63 +                    mn = generateInstanceMethod(destObject, m);
    3.64                  }
    3.65              }
    3.66 +            obfuscationDelegate.exportMethod(out, destObject, mn, m);
    3.67              byte[] runAnno = m.findAnnotationData(false);
    3.68              if (runAnno != null) {
    3.69 -                out.append(prefix).append(mn).append(".anno = {");
    3.70 +                out.append("\n    ").append(destObject).append(".").append(mn).append(".anno = {");
    3.71                  generateAnno(jc, out, runAnno);
    3.72                  out.append("\n    };");
    3.73              }
    3.74 -            out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";");
    3.75 -            out.append(prefix).append(mn).append(".cls = CLS;");
    3.76 +            out.append("\n    ").append(destObject).append(".").append(mn).append(".access = " + m.getAccess()).append(";");
    3.77 +            out.append("\n    ").append(destObject).append(".").append(mn).append(".cls = CLS;");
    3.78          }
    3.79          out.append("\n    c.constructor = CLS;");
    3.80 -        out.append("\n    c.$instOf_").append(className).append(" = true;");
    3.81 +        String instOfName = "$instOf_" + className;
    3.82 +        out.append("\n    c.").append(instOfName).append(" = true;");
    3.83 +        obfuscationDelegate.exportJSProperty(out, "c", instOfName);
    3.84          for (String superInterface : jc.getSuperInterfaces()) {
    3.85 -            out.append("\n    c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;");
    3.86 +            instOfName = "$instOf_" + superInterface.replace('/', '_');
    3.87 +            out.append("\n    c.").append(instOfName).append(" = true;");
    3.88 +            obfuscationDelegate.exportJSProperty(out, "c", instOfName);
    3.89          }
    3.90          out.append("\n    CLS.$class = 'temp';");
    3.91          out.append("\n    CLS.$class = ");
    3.92 @@ -224,14 +241,17 @@
    3.93          out.append("\n  }");
    3.94          out.append("\n  return arguments[0] ? new CLS() : CLS.prototype;");
    3.95          out.append("\n};");
    3.96 +
    3.97 +        obfuscationDelegate.exportClass(out, getVMObject(), className, jc);
    3.98 +
    3.99  //        StringBuilder sb = new StringBuilder();
   3.100  //        for (String init : toInitilize.toArray()) {
   3.101  //            sb.append("\n").append(init).append("();");
   3.102  //        }
   3.103          return "";
   3.104      }
   3.105 -    private String generateStaticMethod(String prefix, MethodData m, StringArray toInitilize) throws IOException {
   3.106 -        String jsb = javaScriptBody(prefix, m, true);
   3.107 +    private String generateStaticMethod(String destObject, MethodData m, StringArray toInitilize) throws IOException {
   3.108 +        String jsb = javaScriptBody(destObject, m, true);
   3.109          if (jsb != null) {
   3.110              return jsb;
   3.111          }
   3.112 @@ -239,28 +259,28 @@
   3.113          if (mn.equals("class__V")) {
   3.114              toInitilize.add(accessClass(className(jc)) + "(false)." + mn);
   3.115          }
   3.116 -        generateMethod(prefix, mn, m);
   3.117 +        generateMethod(destObject, mn, m);
   3.118          return mn;
   3.119      }
   3.120  
   3.121 -    private String generateInstanceMethod(String prefix, MethodData m) throws IOException {
   3.122 -        String jsb = javaScriptBody(prefix, m, false);
   3.123 +    private String generateInstanceMethod(String destObject, MethodData m) throws IOException {
   3.124 +        String jsb = javaScriptBody(destObject, m, false);
   3.125          if (jsb != null) {
   3.126              return jsb;
   3.127          }
   3.128          final String mn = findMethodName(m, new StringBuilder());
   3.129 -        generateMethod(prefix, mn, m);
   3.130 +        generateMethod(destObject, mn, m);
   3.131          return mn;
   3.132      }
   3.133  
   3.134 -    private void generateMethod(String prefix, String name, MethodData m)
   3.135 +    private void generateMethod(String destObject, String name, MethodData m)
   3.136              throws IOException {
   3.137          final StackMapIterator stackMapIterator = m.createStackMapIterator();
   3.138          TrapDataIterator trap = m.getTrapDataIterator();
   3.139          final LocalsMapper lmapper =
   3.140                  new LocalsMapper(stackMapIterator.getArguments());
   3.141  
   3.142 -        out.append(prefix).append(name).append(" = function(");
   3.143 +        out.append(destObject).append(".").append(name).append(" = function(");
   3.144          lmapper.outputArguments(out, m.isStatic());
   3.145          out.append(") {").append("\n");
   3.146  
   3.147 @@ -1546,7 +1566,7 @@
   3.148          return s;
   3.149      }
   3.150  
   3.151 -    private String javaScriptBody(String prefix, MethodData m, boolean isStatic) throws IOException {
   3.152 +    private String javaScriptBody(String destObject, MethodData m, boolean isStatic) throws IOException {
   3.153          byte[] arr = m.findAnnotationData(true);
   3.154          if (arr == null) {
   3.155              return null;
   3.156 @@ -1581,7 +1601,7 @@
   3.157          }
   3.158          StringBuilder cnt = new StringBuilder();
   3.159          final String mn = findMethodName(m, cnt);
   3.160 -        out.append(prefix).append(mn);
   3.161 +        out.append(destObject).append(".").append(mn);
   3.162          out.append(" = function(");
   3.163          String space = "";
   3.164          int index = 0;
     4.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java	Wed Mar 20 13:55:53 2013 +0100
     4.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java	Thu Mar 21 18:48:46 2013 +0100
     4.3 @@ -22,27 +22,40 @@
     4.4  import java.io.IOException;
     4.5  import java.io.OutputStream;
     4.6  import java.io.PrintStream;
     4.7 +import java.util.ArrayList;
     4.8 +import java.util.Arrays;
     4.9 +import java.util.Collection;
    4.10  import java.util.Collections;
    4.11  import java.util.List;
    4.12  import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    4.13 +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData;
    4.14 +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData;
    4.15 +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData;
    4.16  
    4.17  /**
    4.18   *
    4.19   * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.20   */
    4.21  @ExtraJavaScript(processByteCode = false, resource="")
    4.22 -final class ClosureWrapper extends CommandLineRunner implements SourceFile.Generator {
    4.23 -    private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" };
    4.24 +final class ClosureWrapper extends CommandLineRunner {
    4.25 +    private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ };
    4.26  
    4.27 -    private String code;
    4.28 +    private final ClosuresObfuscationDelegate obfuscationDelegate;
    4.29      private final Bck2Brwsr.Resources res;
    4.30      private final StringArray classes;
    4.31 -    private ClosureWrapper(Appendable out, ObfuscationLevel obfuscationLevel,
    4.32 +
    4.33 +    private String compiledCode;
    4.34 +    private String externsCode;
    4.35 +
    4.36 +    private ClosureWrapper(Appendable out, 
    4.37 +                           String compilationLevel,
    4.38 +                           ClosuresObfuscationDelegate obfuscationDelegate,
    4.39                             Bck2Brwsr.Resources res, StringArray classes) {
    4.40          super(
    4.41 -            generateArguments(obfuscationLevel),
    4.42 +            generateArguments(compilationLevel),
    4.43              new PrintStream(new APS(out)), System.err
    4.44          );
    4.45 +        this.obfuscationDelegate = obfuscationDelegate;
    4.46          this.res = res;
    4.47          this.classes = classes;
    4.48      }
    4.49 @@ -52,21 +65,62 @@
    4.50          if (files.size() != 1 || !"bck2brwsr-raw.js".equals(files.get(0))) {
    4.51              throw new IOException("Unexpected files: " + files);
    4.52          }
    4.53 -        return Collections.nCopies(1, SourceFile.fromGenerator("bck2brwsr-raw.js", this));
    4.54 +        return Collections.nCopies(
    4.55 +                   1,
    4.56 +                   SourceFile.fromGenerator(
    4.57 +                       "bck2brwsr-raw.js",
    4.58 +                       new SourceFile.Generator() {
    4.59 +                           @Override
    4.60 +                           public String getCode() {
    4.61 +                               return getCompiledCode();
    4.62 +                           }
    4.63 +                       }));
    4.64      }
    4.65  
    4.66 +
    4.67      @Override
    4.68 -    public String getCode() {
    4.69 -        if (code == null) {
    4.70 +    protected List<SourceFile> createExterns()
    4.71 +            throws FlagUsageException, IOException {
    4.72 +        final List<SourceFile> externsFiles =
    4.73 +                new ArrayList<SourceFile>(super.createExterns());
    4.74 +
    4.75 +        externsFiles.add(
    4.76 +                SourceFile.fromGenerator(
    4.77 +                        "bck2brwsr_externs.js",
    4.78 +                        new SourceFile.Generator() {
    4.79 +                            @Override
    4.80 +                            public String getCode() {
    4.81 +                                return getExternsCode();
    4.82 +                            }
    4.83 +                        }));
    4.84 +        return externsFiles;
    4.85 +    }
    4.86 +
    4.87 +    private String getCompiledCode() {
    4.88 +        if (compiledCode == null) {
    4.89              StringBuilder sb = new StringBuilder();
    4.90              try {
    4.91 -                VM.compile(res, sb, classes);
    4.92 +                VM.compile(res, sb, classes, obfuscationDelegate);
    4.93 +                compiledCode = sb.toString();
    4.94              } catch (IOException ex) {
    4.95 -                code = ex.getMessage();
    4.96 +                compiledCode = ex.getMessage();
    4.97              }
    4.98 -            code = sb.toString();
    4.99          }
   4.100 -        return code;
   4.101 +        return compiledCode;
   4.102 +    }
   4.103 +
   4.104 +    private String getExternsCode() {
   4.105 +        if (externsCode == null) {
   4.106 +            // need compiled code at this point
   4.107 +            getCompiledCode();
   4.108 +
   4.109 +            final StringBuilder sb = new StringBuilder("function RAW() {};\n");
   4.110 +            for (final String extern: obfuscationDelegate.getExterns()) {
   4.111 +                sb.append("RAW.prototype.").append(extern).append(";\n");
   4.112 +            }
   4.113 +            externsCode = sb.toString();
   4.114 +        }
   4.115 +        return externsCode;
   4.116      }
   4.117  
   4.118      private static final class APS extends OutputStream {
   4.119 @@ -81,21 +135,198 @@
   4.120          }
   4.121      }
   4.122  
   4.123 -    private static String[] generateArguments(
   4.124 -            ObfuscationLevel obfuscationLevel) {
   4.125 +    private static String[] generateArguments(String compilationLevel) {
   4.126          String[] finalArgs = ARGS.clone();
   4.127 -        finalArgs[1] = obfuscationLevel.toString();
   4.128 +        finalArgs[1] = compilationLevel;
   4.129  
   4.130          return finalArgs;
   4.131      }
   4.132  
   4.133      static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException {
   4.134 -        ClosureWrapper cw = new ClosureWrapper(w, obfuscationLevel, resources,
   4.135 -                                               arr);
   4.136 +        ClosureWrapper cw = create(w, obfuscationLevel, resources, arr);
   4.137          try {
   4.138              return cw.doRun();
   4.139          } catch (FlagUsageException ex) {
   4.140              throw new IOException(ex);
   4.141          }
   4.142      }
   4.143 +
   4.144 +    private static ClosureWrapper create(Appendable w,
   4.145 +                                         ObfuscationLevel obfuscationLevel,
   4.146 +                                         Bck2Brwsr.Resources resources,
   4.147 +                                         StringArray arr) {
   4.148 +        switch (obfuscationLevel) {
   4.149 +            case MINIMAL:
   4.150 +                return new ClosureWrapper(w, "SIMPLE_OPTIMIZATIONS",
   4.151 +                                          new SimpleObfuscationDelegate(),
   4.152 +                                          resources, arr);
   4.153 +            case MEDIUM:
   4.154 +                return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS",
   4.155 +                                          new MediumObfuscationDelegate(),
   4.156 +                                          resources, arr);
   4.157 +            case FULL:
   4.158 +                return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS",
   4.159 +                                          new FullObfuscationDelegate(),
   4.160 +                                          resources, arr);
   4.161 +            default:
   4.162 +                throw new IllegalArgumentException(
   4.163 +                        "Unsupported level: " + obfuscationLevel);
   4.164 +        }
   4.165 +    }
   4.166 +
   4.167 +    private static abstract class ClosuresObfuscationDelegate
   4.168 +            extends ObfuscationDelegate {
   4.169 +        public abstract Collection<String> getExterns();
   4.170 +    }
   4.171 +
   4.172 +    private static final class SimpleObfuscationDelegate
   4.173 +            extends ClosuresObfuscationDelegate {
   4.174 +        @Override
   4.175 +        public void exportJSProperty(Appendable out,
   4.176 +                                     String destObject,
   4.177 +                                     String propertyName) throws IOException {
   4.178 +        }
   4.179 +
   4.180 +        @Override
   4.181 +        public void exportClass(Appendable out,
   4.182 +                                String destObject,
   4.183 +                                String mangledName,
   4.184 +                                ClassData classData) throws IOException {
   4.185 +        }
   4.186 +
   4.187 +        @Override
   4.188 +        public void exportMethod(Appendable out,
   4.189 +                                 String destObject,
   4.190 +                                 String mangledName,
   4.191 +                                 MethodData methodData) throws IOException {
   4.192 +        }
   4.193 +
   4.194 +        @Override
   4.195 +        public void exportField(Appendable out,
   4.196 +                                String destObject,
   4.197 +                                String mangledName,
   4.198 +                                FieldData fieldData) throws IOException {
   4.199 +        }
   4.200 +
   4.201 +        @Override
   4.202 +        public Collection<String> getExterns() {
   4.203 +            return Collections.EMPTY_LIST;
   4.204 +        }
   4.205 +    }
   4.206 +
   4.207 +    private static abstract class AdvancedObfuscationDelegate
   4.208 +            extends ClosuresObfuscationDelegate {
   4.209 +        private static final String[] INITIAL_EXTERNS = {
   4.210 +            "bck2brwsr",
   4.211 +            "$class",
   4.212 +            "anno",
   4.213 +            "array",
   4.214 +            "access",
   4.215 +            "cls",
   4.216 +            "vm",
   4.217 +            "loadClass",
   4.218 +            "loadBytes",
   4.219 +            "jvmName",
   4.220 +            "primitive",
   4.221 +            "superclass",
   4.222 +            "cnstr",
   4.223 +            "add32",
   4.224 +            "sub32",
   4.225 +            "mul32",
   4.226 +            "neg32",
   4.227 +            "toInt8",
   4.228 +            "toInt16",
   4.229 +            "next32",
   4.230 +            "high32",
   4.231 +            "toInt32",
   4.232 +            "toFP",
   4.233 +            "toLong",
   4.234 +            "toExactString",
   4.235 +            "add64",
   4.236 +            "sub64",
   4.237 +            "mul64",
   4.238 +            "and64",
   4.239 +            "or64",
   4.240 +            "xor64",
   4.241 +            "shl64",
   4.242 +            "shr64",
   4.243 +            "ushr64",
   4.244 +            "compare64",
   4.245 +            "neg64",
   4.246 +            "div32",
   4.247 +            "mod32",
   4.248 +            "div64",
   4.249 +            "mod64",
   4.250 +            "at",
   4.251 +            "getClass__Ljava_lang_Class_2",
   4.252 +            "clone__Ljava_lang_Object_2"
   4.253 +        };
   4.254 +
   4.255 +        private final Collection<String> externs;
   4.256 +
   4.257 +        protected AdvancedObfuscationDelegate() {
   4.258 +            externs = new ArrayList<String>(Arrays.asList(INITIAL_EXTERNS));
   4.259 +        }
   4.260 +
   4.261 +        @Override
   4.262 +        public void exportClass(Appendable out,
   4.263 +                                String destObject,
   4.264 +                                String mangledName,
   4.265 +                                ClassData classData) throws IOException {
   4.266 +            exportJSProperty(out, destObject, mangledName);
   4.267 +        }
   4.268 +
   4.269 +        @Override
   4.270 +        public void exportMethod(Appendable out,
   4.271 +                                 String destObject,
   4.272 +                                 String mangledName,
   4.273 +                                 MethodData methodData) throws IOException {
   4.274 +            if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   4.275 +                exportJSProperty(out, destObject, mangledName);
   4.276 +            }
   4.277 +        }
   4.278 +
   4.279 +        @Override
   4.280 +        public void exportField(Appendable out,
   4.281 +                                String destObject,
   4.282 +                                String mangledName,
   4.283 +                                FieldData fieldData) throws IOException {
   4.284 +            if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   4.285 +                exportJSProperty(out, destObject, mangledName);
   4.286 +            }
   4.287 +        }
   4.288 +
   4.289 +        @Override
   4.290 +        public Collection<String> getExterns() {
   4.291 +            return externs;
   4.292 +        }
   4.293 +
   4.294 +        protected void addExtern(String extern) {
   4.295 +            externs.add(extern);
   4.296 +        }
   4.297 +    }
   4.298 +
   4.299 +    private static final class MediumObfuscationDelegate
   4.300 +            extends AdvancedObfuscationDelegate {
   4.301 +        @Override
   4.302 +        public void exportJSProperty(Appendable out,
   4.303 +                                     String destObject,
   4.304 +                                     String propertyName) {
   4.305 +            addExtern(propertyName);
   4.306 +        }
   4.307 +    }
   4.308 +
   4.309 +    private static final class FullObfuscationDelegate
   4.310 +            extends AdvancedObfuscationDelegate {
   4.311 +        @Override
   4.312 +        public void exportJSProperty(Appendable out,
   4.313 +                                     String destObject,
   4.314 +                                     String propertyName) throws IOException {
   4.315 +            out.append("\n").append(destObject).append("['")
   4.316 +                                               .append(propertyName)
   4.317 +                                               .append("'] = ")
   4.318 +                            .append(destObject).append(".").append(propertyName)
   4.319 +               .append(";\n");
   4.320 +        }
   4.321 +    }
   4.322  }
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java	Thu Mar 21 18:48:46 2013 +0100
     5.3 @@ -0,0 +1,75 @@
     5.4 +/**
     5.5 + * Back 2 Browser Bytecode Translator
     5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, version 2 of the License.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details.
    5.16 + *
    5.17 + * You should have received a copy of the GNU General Public License
    5.18 + * along with this program. Look for COPYING file in the top folder.
    5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    5.20 + */
    5.21 +package org.apidesign.vm4brwsr;
    5.22 +
    5.23 +import java.io.IOException;
    5.24 +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData;
    5.25 +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData;
    5.26 +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData;
    5.27 +
    5.28 +abstract class ObfuscationDelegate {
    5.29 +    static ObfuscationDelegate NULL =
    5.30 +            new ObfuscationDelegate() {
    5.31 +                @Override
    5.32 +                public void exportJSProperty(Appendable out,
    5.33 +                                             String destObject,
    5.34 +                                             String propertyName)
    5.35 +                            throws IOException {
    5.36 +                }
    5.37 +
    5.38 +                @Override
    5.39 +                public void exportClass(Appendable out,
    5.40 +                                        String destObject,
    5.41 +                                        String mangledName,
    5.42 +                                        ClassData classData)
    5.43 +                                            throws IOException {
    5.44 +                }
    5.45 +
    5.46 +                @Override
    5.47 +                public void exportMethod(Appendable out,
    5.48 +                                         String destObject,
    5.49 +                                         String mangledName,
    5.50 +                                         MethodData methodData)
    5.51 +                                             throws IOException {
    5.52 +                }
    5.53 +
    5.54 +                @Override
    5.55 +                public void exportField(Appendable out,
    5.56 +                                        String destObject,
    5.57 +                                        String mangledName,
    5.58 +                                        FieldData fieldData)
    5.59 +                                            throws IOException {
    5.60 +                }
    5.61 +            };
    5.62 +
    5.63 +    public abstract void exportJSProperty(
    5.64 +            Appendable out, String destObject, String propertyName)
    5.65 +                throws IOException;
    5.66 +
    5.67 +    public abstract void exportClass(
    5.68 +            Appendable out, String destObject, String mangledName,
    5.69 +            ClassData classData) throws IOException;
    5.70 +
    5.71 +    public abstract void exportMethod(
    5.72 +            Appendable out, String destObject, String mangledName,
    5.73 +            MethodData methodData) throws IOException;
    5.74 +
    5.75 +    public abstract void exportField(
    5.76 +            Appendable out, String destObject, String mangledName,
    5.77 +            FieldData fieldData) throws IOException;
    5.78 +}
     6.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java	Wed Mar 20 13:55:53 2013 +0100
     6.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationLevel.java	Thu Mar 21 18:48:46 2013 +0100
     6.3 @@ -18,14 +18,13 @@
     6.4  package org.apidesign.vm4brwsr;
     6.5  
     6.6  /**
     6.7 - * Defines obfuscation level of produced JavaScript files. Corresponds to the
     6.8 - * closure compiler compilation level.
     6.9 + * Defines obfuscation level of produced JavaScript files.
    6.10   *
    6.11   * @since 0.5
    6.12   */
    6.13  public enum ObfuscationLevel {
    6.14      NONE,
    6.15 -    WHITESPACE_ONLY,
    6.16 -    SIMPLE_OPTIMIZATIONS,
    6.17 -    ADVANCED_OPTIMIZATIONS
    6.18 +    MINIMAL,
    6.19 +    MEDIUM,
    6.20 +    FULL
    6.21  }
     7.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java	Wed Mar 20 13:55:53 2013 +0100
     7.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java	Thu Mar 21 18:48:46 2013 +0100
     7.3 @@ -28,7 +28,11 @@
     7.4      public VM(Appendable out) {
     7.5          super(out);
     7.6      }
     7.7 -    
     7.8 +
     7.9 +    private VM(Appendable out, ObfuscationDelegate obfuscationDelegate) {
    7.10 +        super(out, obfuscationDelegate);
    7.11 +    }
    7.12 +
    7.13      static {
    7.14          // uses VMLazy to load dynamic classes
    7.15          boolean assertsOn = false;
    7.16 @@ -47,6 +51,12 @@
    7.17      static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException {
    7.18          new VM(out).doCompile(l, names);
    7.19      }
    7.20 +
    7.21 +    static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names,
    7.22 +                        ObfuscationDelegate obfuscationDelegate) throws IOException {
    7.23 +        new VM(out, obfuscationDelegate).doCompile(l, names);
    7.24 +    }
    7.25 +
    7.26      protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException {
    7.27          out.append("(function VM(global) {var fillInVMSkeleton = function(vm) {");
    7.28          StringArray processed = new StringArray();
    7.29 @@ -230,4 +240,9 @@
    7.30      String accessClass(String className) {
    7.31          return "vm." + className;
    7.32      }
    7.33 +
    7.34 +    @Override
    7.35 +    String getVMObject() {
    7.36 +        return "vm";
    7.37 +    }
    7.38  }
     8.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java	Wed Mar 20 13:55:53 2013 +0100
     8.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java	Thu Mar 21 18:48:46 2013 +0100
     8.3 @@ -151,5 +151,10 @@
     8.4          String accessClass(String classOperation) {
     8.5              return "vm." + classOperation;
     8.6          }
     8.7 +
     8.8 +        @Override
     8.9 +        String getVMObject() {
    8.10 +            return "vm";
    8.11 +        }
    8.12      }
    8.13  }
     9.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java	Wed Mar 20 13:55:53 2013 +0100
     9.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java	Thu Mar 21 18:48:46 2013 +0100
     9.3 @@ -43,4 +43,9 @@
     9.4      @Override
     9.5      protected void requireScript(String resourcePath) {
     9.6      }
     9.7 +
     9.8 +    @Override
     9.9 +    String getVMObject() {
    9.10 +        return "global";
    9.11 +    }
    9.12  }