rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 18 Feb 2014 22:20:50 +0100
branchReducedStack
changeset 1477 b012365f8fb7
parent 882 60d9ea48ec99
child 1513 ba912ef24b27
permissions -rw-r--r--
More effective replaces on stack
     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             "compare",
   258             "neg64",
   259             "div32",
   260             "mod32",
   261             "div64",
   262             "mod64",
   263             "at",
   264             "getClass__Ljava_lang_Class_2",
   265             "clone__Ljava_lang_Object_2"
   266         };
   267 
   268         private final Collection<String> externs;
   269 
   270         protected AdvancedObfuscationDelegate() {
   271             externs = new ArrayList<String>(Arrays.asList(INITIAL_EXTERNS));
   272         }
   273 
   274         @Override
   275         public void exportClass(Appendable out,
   276                                 String destObject,
   277                                 String mangledName,
   278                                 ClassData classData) throws IOException {
   279             exportJSProperty(out, destObject, mangledName);
   280         }
   281 
   282         @Override
   283         public void exportMethod(Appendable out,
   284                                  String destObject,
   285                                  String mangledName,
   286                                  MethodData methodData) throws IOException {
   287             if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   288                 exportJSProperty(out, destObject, mangledName);
   289             }
   290         }
   291 
   292         @Override
   293         public void exportField(Appendable out,
   294                                 String destObject,
   295                                 String mangledName,
   296                                 FieldData fieldData) throws IOException {
   297             if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) {
   298                 exportJSProperty(out, destObject, mangledName);
   299             }
   300         }
   301 
   302         @Override
   303         public Collection<String> getExterns() {
   304             return externs;
   305         }
   306 
   307         protected void addExtern(String extern) {
   308             externs.add(extern);
   309         }
   310     }
   311 
   312     private static final class MediumObfuscationDelegate
   313             extends AdvancedObfuscationDelegate {
   314         @Override
   315         public void exportJSProperty(Appendable out,
   316                                      String destObject,
   317                                      String propertyName) {
   318             addExtern(propertyName);
   319         }
   320     }
   321 
   322     private static final class FullObfuscationDelegate
   323             extends AdvancedObfuscationDelegate {
   324         @Override
   325         public void exportJSProperty(Appendable out,
   326                                      String destObject,
   327                                      String propertyName) throws IOException {
   328             out.append("\n").append(destObject).append("['")
   329                                                .append(propertyName)
   330                                                .append("'] = ")
   331                             .append(destObject).append(".").append(propertyName)
   332                .append(";\n");
   333         }
   334     }
   335 }