rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java
author Lubomir Nerad <lubomir.nerad@oracle.com>
Thu, 21 Mar 2013 18:48:46 +0100
branchclosure
changeset 869 151f4ccd7673
parent 849 d95117153304
child 882 60d9ea48ec99
permissions -rw-r--r--
Initial attempt for advanced obfuscation
     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             case MEDIUM:
   164                 return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS",
   165                                           new MediumObfuscationDelegate(),
   166                                           resources, arr);
   167             case FULL:
   168                 return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS",
   169                                           new FullObfuscationDelegate(),
   170                                           resources, arr);
   171             default:
   172                 throw new IllegalArgumentException(
   173                         "Unsupported level: " + obfuscationLevel);
   174         }
   175     }
   176 
   177     private static abstract class ClosuresObfuscationDelegate
   178             extends ObfuscationDelegate {
   179         public abstract Collection<String> getExterns();
   180     }
   181 
   182     private static final class SimpleObfuscationDelegate
   183             extends ClosuresObfuscationDelegate {
   184         @Override
   185         public void exportJSProperty(Appendable out,
   186                                      String destObject,
   187                                      String propertyName) throws IOException {
   188         }
   189 
   190         @Override
   191         public void exportClass(Appendable out,
   192                                 String destObject,
   193                                 String mangledName,
   194                                 ClassData classData) throws IOException {
   195         }
   196 
   197         @Override
   198         public void exportMethod(Appendable out,
   199                                  String destObject,
   200                                  String mangledName,
   201                                  MethodData methodData) throws IOException {
   202         }
   203 
   204         @Override
   205         public void exportField(Appendable out,
   206                                 String destObject,
   207                                 String mangledName,
   208                                 FieldData fieldData) throws IOException {
   209         }
   210 
   211         @Override
   212         public Collection<String> getExterns() {
   213             return Collections.EMPTY_LIST;
   214         }
   215     }
   216 
   217     private static abstract class AdvancedObfuscationDelegate
   218             extends ClosuresObfuscationDelegate {
   219         private static final String[] INITIAL_EXTERNS = {
   220             "bck2brwsr",
   221             "$class",
   222             "anno",
   223             "array",
   224             "access",
   225             "cls",
   226             "vm",
   227             "loadClass",
   228             "loadBytes",
   229             "jvmName",
   230             "primitive",
   231             "superclass",
   232             "cnstr",
   233             "add32",
   234             "sub32",
   235             "mul32",
   236             "neg32",
   237             "toInt8",
   238             "toInt16",
   239             "next32",
   240             "high32",
   241             "toInt32",
   242             "toFP",
   243             "toLong",
   244             "toExactString",
   245             "add64",
   246             "sub64",
   247             "mul64",
   248             "and64",
   249             "or64",
   250             "xor64",
   251             "shl64",
   252             "shr64",
   253             "ushr64",
   254             "compare64",
   255             "neg64",
   256             "div32",
   257             "mod32",
   258             "div64",
   259             "mod64",
   260             "at",
   261             "getClass__Ljava_lang_Class_2",
   262             "clone__Ljava_lang_Object_2"
   263         };
   264 
   265         private final Collection<String> externs;
   266 
   267         protected AdvancedObfuscationDelegate() {
   268             externs = new ArrayList<String>(Arrays.asList(INITIAL_EXTERNS));
   269         }
   270 
   271         @Override
   272         public void exportClass(Appendable out,
   273                                 String destObject,
   274                                 String mangledName,
   275                                 ClassData classData) throws IOException {
   276             exportJSProperty(out, destObject, mangledName);
   277         }
   278 
   279         @Override
   280         public void exportMethod(Appendable out,
   281                                  String destObject,
   282                                  String mangledName,
   283                                  MethodData methodData) throws IOException {
   284             if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   285                 exportJSProperty(out, destObject, mangledName);
   286             }
   287         }
   288 
   289         @Override
   290         public void exportField(Appendable out,
   291                                 String destObject,
   292                                 String mangledName,
   293                                 FieldData fieldData) throws IOException {
   294             if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   295                 exportJSProperty(out, destObject, mangledName);
   296             }
   297         }
   298 
   299         @Override
   300         public Collection<String> getExterns() {
   301             return externs;
   302         }
   303 
   304         protected void addExtern(String extern) {
   305             externs.add(extern);
   306         }
   307     }
   308 
   309     private static final class MediumObfuscationDelegate
   310             extends AdvancedObfuscationDelegate {
   311         @Override
   312         public void exportJSProperty(Appendable out,
   313                                      String destObject,
   314                                      String propertyName) {
   315             addExtern(propertyName);
   316         }
   317     }
   318 
   319     private static final class FullObfuscationDelegate
   320             extends AdvancedObfuscationDelegate {
   321         @Override
   322         public void exportJSProperty(Appendable out,
   323                                      String destObject,
   324                                      String propertyName) throws IOException {
   325             out.append("\n").append(destObject).append("['")
   326                                                .append(propertyName)
   327                                                .append("'] = ")
   328                             .append(destObject).append(".").append(propertyName)
   329                .append(";\n");
   330         }
   331     }
   332 }