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