rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 25 Mar 2013 13:29:42 +0100
branchclosure
changeset 882 60d9ea48ec99
parent 869 151f4ccd7673
child 967 f19f17f8f8dc
child 1477 b012365f8fb7
permissions -rw-r--r--
Adding javadoc to various obfuscation levels. Disabling MEDIUM for the upcoming merge to default, as it is not finished yet.
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.vm4brwsr;
    19 
    20 import com.google.javascript.jscomp.CommandLineRunner;
    21 import com.google.javascript.jscomp.SourceFile;
    22 import java.io.IOException;
    23 import java.io.OutputStream;
    24 import java.io.PrintStream;
    25 import java.util.ArrayList;
    26 import java.util.Arrays;
    27 import java.util.Collection;
    28 import java.util.Collections;
    29 import java.util.List;
    30 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    31 import org.apidesign.vm4brwsr.ByteCodeParser.ClassData;
    32 import org.apidesign.vm4brwsr.ByteCodeParser.FieldData;
    33 import org.apidesign.vm4brwsr.ByteCodeParser.MethodData;
    34 
    35 /**
    36  *
    37  * @author Jaroslav Tulach <jtulach@netbeans.org>
    38  */
    39 @ExtraJavaScript(processByteCode = false, resource="")
    40 final class ClosureWrapper extends CommandLineRunner {
    41     private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ };
    42 
    43     private final ClosuresObfuscationDelegate obfuscationDelegate;
    44     private final Bck2Brwsr.Resources res;
    45     private final StringArray classes;
    46 
    47     private String compiledCode;
    48     private String externsCode;
    49 
    50     private ClosureWrapper(Appendable out, 
    51                            String compilationLevel,
    52                            ClosuresObfuscationDelegate obfuscationDelegate,
    53                            Bck2Brwsr.Resources res, StringArray classes) {
    54         super(
    55             generateArguments(compilationLevel),
    56             new PrintStream(new APS(out)), System.err
    57         );
    58         this.obfuscationDelegate = obfuscationDelegate;
    59         this.res = res;
    60         this.classes = classes;
    61     }
    62 
    63     @Override
    64     protected List<SourceFile> createInputs(List<String> files, boolean allowStdIn) throws FlagUsageException, IOException {
    65         if (files.size() != 1 || !"bck2brwsr-raw.js".equals(files.get(0))) {
    66             throw new IOException("Unexpected files: " + files);
    67         }
    68         return Collections.nCopies(
    69                    1,
    70                    SourceFile.fromGenerator(
    71                        "bck2brwsr-raw.js",
    72                        new SourceFile.Generator() {
    73                            @Override
    74                            public String getCode() {
    75                                return getCompiledCode();
    76                            }
    77                        }));
    78     }
    79 
    80 
    81     @Override
    82     protected List<SourceFile> createExterns()
    83             throws FlagUsageException, IOException {
    84         final List<SourceFile> externsFiles =
    85                 new ArrayList<SourceFile>(super.createExterns());
    86 
    87         externsFiles.add(
    88                 SourceFile.fromGenerator(
    89                         "bck2brwsr_externs.js",
    90                         new SourceFile.Generator() {
    91                             @Override
    92                             public String getCode() {
    93                                 return getExternsCode();
    94                             }
    95                         }));
    96         return externsFiles;
    97     }
    98 
    99     private String getCompiledCode() {
   100         if (compiledCode == null) {
   101             StringBuilder sb = new StringBuilder();
   102             try {
   103                 VM.compile(res, sb, classes, obfuscationDelegate);
   104                 compiledCode = sb.toString();
   105             } catch (IOException ex) {
   106                 compiledCode = ex.getMessage();
   107             }
   108         }
   109         return compiledCode;
   110     }
   111 
   112     private String getExternsCode() {
   113         if (externsCode == null) {
   114             // need compiled code at this point
   115             getCompiledCode();
   116 
   117             final StringBuilder sb = new StringBuilder("function RAW() {};\n");
   118             for (final String extern: obfuscationDelegate.getExterns()) {
   119                 sb.append("RAW.prototype.").append(extern).append(";\n");
   120             }
   121             externsCode = sb.toString();
   122         }
   123         return externsCode;
   124     }
   125 
   126     private static final class APS extends OutputStream {
   127         private final Appendable out;
   128 
   129         public APS(Appendable out) {
   130             this.out = out;
   131         }
   132         @Override
   133         public void write(int b) throws IOException {
   134             out.append((char)b);
   135         }
   136     }
   137 
   138     private static String[] generateArguments(String compilationLevel) {
   139         String[] finalArgs = ARGS.clone();
   140         finalArgs[1] = compilationLevel;
   141 
   142         return finalArgs;
   143     }
   144 
   145     static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException {
   146         ClosureWrapper cw = create(w, obfuscationLevel, resources, arr);
   147         try {
   148             return cw.doRun();
   149         } catch (FlagUsageException ex) {
   150             throw new IOException(ex);
   151         }
   152     }
   153 
   154     private static ClosureWrapper create(Appendable w,
   155                                          ObfuscationLevel obfuscationLevel,
   156                                          Bck2Brwsr.Resources resources,
   157                                          StringArray arr) {
   158         switch (obfuscationLevel) {
   159             case MINIMAL:
   160                 return new ClosureWrapper(w, "SIMPLE_OPTIMIZATIONS",
   161                                           new SimpleObfuscationDelegate(),
   162                                           resources, arr);
   163 /*                
   164             case MEDIUM:
   165                 return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS",
   166                                           new MediumObfuscationDelegate(),
   167                                           resources, arr);
   168 */
   169             case FULL:
   170                 return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS",
   171                                           new FullObfuscationDelegate(),
   172                                           resources, arr);
   173             default:
   174                 throw new IllegalArgumentException(
   175                         "Unsupported level: " + obfuscationLevel);
   176         }
   177     }
   178 
   179     private static abstract class ClosuresObfuscationDelegate
   180             extends ObfuscationDelegate {
   181         public abstract Collection<String> getExterns();
   182     }
   183 
   184     private static final class SimpleObfuscationDelegate
   185             extends ClosuresObfuscationDelegate {
   186         @Override
   187         public void exportJSProperty(Appendable out,
   188                                      String destObject,
   189                                      String propertyName) throws IOException {
   190         }
   191 
   192         @Override
   193         public void exportClass(Appendable out,
   194                                 String destObject,
   195                                 String mangledName,
   196                                 ClassData classData) throws IOException {
   197         }
   198 
   199         @Override
   200         public void exportMethod(Appendable out,
   201                                  String destObject,
   202                                  String mangledName,
   203                                  MethodData methodData) throws IOException {
   204         }
   205 
   206         @Override
   207         public void exportField(Appendable out,
   208                                 String destObject,
   209                                 String mangledName,
   210                                 FieldData fieldData) throws IOException {
   211         }
   212 
   213         @Override
   214         public Collection<String> getExterns() {
   215             return Collections.EMPTY_LIST;
   216         }
   217     }
   218 
   219     private static abstract class AdvancedObfuscationDelegate
   220             extends ClosuresObfuscationDelegate {
   221         private static final String[] INITIAL_EXTERNS = {
   222             "bck2brwsr",
   223             "$class",
   224             "anno",
   225             "array",
   226             "access",
   227             "cls",
   228             "vm",
   229             "loadClass",
   230             "loadBytes",
   231             "jvmName",
   232             "primitive",
   233             "superclass",
   234             "cnstr",
   235             "add32",
   236             "sub32",
   237             "mul32",
   238             "neg32",
   239             "toInt8",
   240             "toInt16",
   241             "next32",
   242             "high32",
   243             "toInt32",
   244             "toFP",
   245             "toLong",
   246             "toExactString",
   247             "add64",
   248             "sub64",
   249             "mul64",
   250             "and64",
   251             "or64",
   252             "xor64",
   253             "shl64",
   254             "shr64",
   255             "ushr64",
   256             "compare64",
   257             "neg64",
   258             "div32",
   259             "mod32",
   260             "div64",
   261             "mod64",
   262             "at",
   263             "getClass__Ljava_lang_Class_2",
   264             "clone__Ljava_lang_Object_2"
   265         };
   266 
   267         private final Collection<String> externs;
   268 
   269         protected AdvancedObfuscationDelegate() {
   270             externs = new ArrayList<String>(Arrays.asList(INITIAL_EXTERNS));
   271         }
   272 
   273         @Override
   274         public void exportClass(Appendable out,
   275                                 String destObject,
   276                                 String mangledName,
   277                                 ClassData classData) throws IOException {
   278             exportJSProperty(out, destObject, mangledName);
   279         }
   280 
   281         @Override
   282         public void exportMethod(Appendable out,
   283                                  String destObject,
   284                                  String mangledName,
   285                                  MethodData methodData) throws IOException {
   286             if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   287                 exportJSProperty(out, destObject, mangledName);
   288             }
   289         }
   290 
   291         @Override
   292         public void exportField(Appendable out,
   293                                 String destObject,
   294                                 String mangledName,
   295                                 FieldData fieldData) throws IOException {
   296             if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   297                 exportJSProperty(out, destObject, mangledName);
   298             }
   299         }
   300 
   301         @Override
   302         public Collection<String> getExterns() {
   303             return externs;
   304         }
   305 
   306         protected void addExtern(String extern) {
   307             externs.add(extern);
   308         }
   309     }
   310 
   311     private static final class MediumObfuscationDelegate
   312             extends AdvancedObfuscationDelegate {
   313         @Override
   314         public void exportJSProperty(Appendable out,
   315                                      String destObject,
   316                                      String propertyName) {
   317             addExtern(propertyName);
   318         }
   319     }
   320 
   321     private static final class FullObfuscationDelegate
   322             extends AdvancedObfuscationDelegate {
   323         @Override
   324         public void exportJSProperty(Appendable out,
   325                                      String destObject,
   326                                      String propertyName) throws IOException {
   327             out.append("\n").append(destObject).append("['")
   328                                                .append(propertyName)
   329                                                .append("'] = ")
   330                             .append(destObject).append(".").append(propertyName)
   331                .append(";\n");
   332         }
   333     }
   334 }