jaroslav@750: /** jaroslav@750: * Back 2 Browser Bytecode Translator jaroslav@750: * Copyright (C) 2012 Jaroslav Tulach jaroslav@750: * jaroslav@750: * This program is free software: you can redistribute it and/or modify jaroslav@750: * it under the terms of the GNU General Public License as published by jaroslav@750: * the Free Software Foundation, version 2 of the License. jaroslav@750: * jaroslav@750: * This program is distributed in the hope that it will be useful, jaroslav@750: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@750: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@750: * GNU General Public License for more details. jaroslav@750: * jaroslav@750: * You should have received a copy of the GNU General Public License jaroslav@750: * along with this program. Look for COPYING file in the top folder. jaroslav@750: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@750: */ jaroslav@750: package org.apidesign.vm4brwsr; jaroslav@750: jaroslav@750: import com.google.javascript.jscomp.CommandLineRunner; jaroslav@750: import com.google.javascript.jscomp.SourceFile; jaroslav@750: import java.io.IOException; jaroslav@750: import java.io.OutputStream; jaroslav@750: import java.io.PrintStream; lubomir@869: import java.util.ArrayList; lubomir@869: import java.util.Arrays; lubomir@869: import java.util.Collection; jaroslav@750: import java.util.Collections; jaroslav@750: import java.util.List; jaroslav@750: import org.apidesign.bck2brwsr.core.ExtraJavaScript; lubomir@869: import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; lubomir@869: import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; lubomir@869: import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; jaroslav@750: jaroslav@750: /** jaroslav@750: * jaroslav@750: * @author Jaroslav Tulach jaroslav@750: */ jaroslav@750: @ExtraJavaScript(processByteCode = false, resource="") lubomir@869: final class ClosureWrapper extends CommandLineRunner { lubomir@869: private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ }; jaroslav@750: lubomir@869: private final ClosuresObfuscationDelegate obfuscationDelegate; jaroslav@750: private final Bck2Brwsr.Resources res; jaroslav@750: private final StringArray classes; lubomir@869: lubomir@869: private String compiledCode; lubomir@869: private String externsCode; lubomir@869: lubomir@869: private ClosureWrapper(Appendable out, lubomir@869: String compilationLevel, lubomir@869: ClosuresObfuscationDelegate obfuscationDelegate, lubomir@849: Bck2Brwsr.Resources res, StringArray classes) { jaroslav@750: super( lubomir@869: generateArguments(compilationLevel), jaroslav@750: new PrintStream(new APS(out)), System.err jaroslav@750: ); lubomir@869: this.obfuscationDelegate = obfuscationDelegate; jaroslav@750: this.res = res; jaroslav@750: this.classes = classes; jaroslav@750: } jaroslav@750: jaroslav@750: @Override jaroslav@750: protected List createInputs(List files, boolean allowStdIn) throws FlagUsageException, IOException { jaroslav@750: if (files.size() != 1 || !"bck2brwsr-raw.js".equals(files.get(0))) { jaroslav@750: throw new IOException("Unexpected files: " + files); jaroslav@750: } lubomir@869: return Collections.nCopies( lubomir@869: 1, lubomir@869: SourceFile.fromGenerator( lubomir@869: "bck2brwsr-raw.js", lubomir@869: new SourceFile.Generator() { lubomir@869: @Override lubomir@869: public String getCode() { lubomir@869: return getCompiledCode(); lubomir@869: } lubomir@869: })); jaroslav@750: } jaroslav@750: lubomir@869: jaroslav@750: @Override lubomir@869: protected List createExterns() lubomir@869: throws FlagUsageException, IOException { lubomir@869: final List externsFiles = lubomir@869: new ArrayList(super.createExterns()); lubomir@869: lubomir@869: externsFiles.add( lubomir@869: SourceFile.fromGenerator( lubomir@869: "bck2brwsr_externs.js", lubomir@869: new SourceFile.Generator() { lubomir@869: @Override lubomir@869: public String getCode() { lubomir@869: return getExternsCode(); lubomir@869: } lubomir@869: })); lubomir@869: return externsFiles; lubomir@869: } lubomir@869: lubomir@869: private String getCompiledCode() { lubomir@869: if (compiledCode == null) { jaroslav@750: StringBuilder sb = new StringBuilder(); jaroslav@750: try { lubomir@869: VM.compile(res, sb, classes, obfuscationDelegate); lubomir@869: compiledCode = sb.toString(); jaroslav@750: } catch (IOException ex) { lubomir@869: compiledCode = ex.getMessage(); jaroslav@750: } jaroslav@750: } lubomir@869: return compiledCode; lubomir@869: } lubomir@869: lubomir@869: private String getExternsCode() { lubomir@869: if (externsCode == null) { lubomir@869: // need compiled code at this point lubomir@869: getCompiledCode(); lubomir@869: lubomir@869: final StringBuilder sb = new StringBuilder("function RAW() {};\n"); lubomir@869: for (final String extern: obfuscationDelegate.getExterns()) { lubomir@869: sb.append("RAW.prototype.").append(extern).append(";\n"); lubomir@869: } lubomir@869: externsCode = sb.toString(); lubomir@869: } lubomir@869: return externsCode; jaroslav@750: } lubomir@849: jaroslav@750: private static final class APS extends OutputStream { jaroslav@750: private final Appendable out; jaroslav@750: jaroslav@750: public APS(Appendable out) { jaroslav@750: this.out = out; jaroslav@750: } jaroslav@750: @Override jaroslav@750: public void write(int b) throws IOException { jaroslav@750: out.append((char)b); jaroslav@750: } jaroslav@750: } lubomir@849: lubomir@869: private static String[] generateArguments(String compilationLevel) { lubomir@849: String[] finalArgs = ARGS.clone(); lubomir@869: finalArgs[1] = compilationLevel; lubomir@849: lubomir@849: return finalArgs; lubomir@849: } lubomir@849: lubomir@849: static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException { lubomir@869: ClosureWrapper cw = create(w, obfuscationLevel, resources, arr); jaroslav@750: try { jaroslav@750: return cw.doRun(); jaroslav@750: } catch (FlagUsageException ex) { jaroslav@750: throw new IOException(ex); jaroslav@750: } jaroslav@750: } lubomir@869: lubomir@869: private static ClosureWrapper create(Appendable w, lubomir@869: ObfuscationLevel obfuscationLevel, lubomir@869: Bck2Brwsr.Resources resources, lubomir@869: StringArray arr) { lubomir@869: switch (obfuscationLevel) { lubomir@869: case MINIMAL: lubomir@869: return new ClosureWrapper(w, "SIMPLE_OPTIMIZATIONS", lubomir@869: new SimpleObfuscationDelegate(), lubomir@869: resources, arr); lubomir@869: case MEDIUM: lubomir@869: return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", lubomir@869: new MediumObfuscationDelegate(), lubomir@869: resources, arr); lubomir@869: case FULL: lubomir@869: return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", lubomir@869: new FullObfuscationDelegate(), lubomir@869: resources, arr); lubomir@869: default: lubomir@869: throw new IllegalArgumentException( lubomir@869: "Unsupported level: " + obfuscationLevel); lubomir@869: } lubomir@869: } lubomir@869: lubomir@869: private static abstract class ClosuresObfuscationDelegate lubomir@869: extends ObfuscationDelegate { lubomir@869: public abstract Collection getExterns(); lubomir@869: } lubomir@869: lubomir@869: private static final class SimpleObfuscationDelegate lubomir@869: extends ClosuresObfuscationDelegate { lubomir@869: @Override lubomir@869: public void exportJSProperty(Appendable out, lubomir@869: String destObject, lubomir@869: String propertyName) throws IOException { lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public void exportClass(Appendable out, lubomir@869: String destObject, lubomir@869: String mangledName, lubomir@869: ClassData classData) throws IOException { lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public void exportMethod(Appendable out, lubomir@869: String destObject, lubomir@869: String mangledName, lubomir@869: MethodData methodData) throws IOException { lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public void exportField(Appendable out, lubomir@869: String destObject, lubomir@869: String mangledName, lubomir@869: FieldData fieldData) throws IOException { lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public Collection getExterns() { lubomir@869: return Collections.EMPTY_LIST; lubomir@869: } lubomir@869: } lubomir@869: lubomir@869: private static abstract class AdvancedObfuscationDelegate lubomir@869: extends ClosuresObfuscationDelegate { lubomir@869: private static final String[] INITIAL_EXTERNS = { lubomir@869: "bck2brwsr", lubomir@869: "$class", lubomir@869: "anno", lubomir@869: "array", lubomir@869: "access", lubomir@869: "cls", lubomir@869: "vm", lubomir@869: "loadClass", lubomir@869: "loadBytes", lubomir@869: "jvmName", lubomir@869: "primitive", lubomir@869: "superclass", lubomir@869: "cnstr", lubomir@869: "add32", lubomir@869: "sub32", lubomir@869: "mul32", lubomir@869: "neg32", lubomir@869: "toInt8", lubomir@869: "toInt16", lubomir@869: "next32", lubomir@869: "high32", lubomir@869: "toInt32", lubomir@869: "toFP", lubomir@869: "toLong", lubomir@869: "toExactString", lubomir@869: "add64", lubomir@869: "sub64", lubomir@869: "mul64", lubomir@869: "and64", lubomir@869: "or64", lubomir@869: "xor64", lubomir@869: "shl64", lubomir@869: "shr64", lubomir@869: "ushr64", lubomir@869: "compare64", lubomir@869: "neg64", lubomir@869: "div32", lubomir@869: "mod32", lubomir@869: "div64", lubomir@869: "mod64", lubomir@869: "at", lubomir@869: "getClass__Ljava_lang_Class_2", lubomir@869: "clone__Ljava_lang_Object_2" lubomir@869: }; lubomir@869: lubomir@869: private final Collection externs; lubomir@869: lubomir@869: protected AdvancedObfuscationDelegate() { lubomir@869: externs = new ArrayList(Arrays.asList(INITIAL_EXTERNS)); lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public void exportClass(Appendable out, lubomir@869: String destObject, lubomir@869: String mangledName, lubomir@869: ClassData classData) throws IOException { lubomir@869: exportJSProperty(out, destObject, mangledName); lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public void exportMethod(Appendable out, lubomir@869: String destObject, lubomir@869: String mangledName, lubomir@869: MethodData methodData) throws IOException { lubomir@869: if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) { lubomir@869: exportJSProperty(out, destObject, mangledName); lubomir@869: } lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public void exportField(Appendable out, lubomir@869: String destObject, lubomir@869: String mangledName, lubomir@869: FieldData fieldData) throws IOException { lubomir@869: if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) { lubomir@869: exportJSProperty(out, destObject, mangledName); lubomir@869: } lubomir@869: } lubomir@869: lubomir@869: @Override lubomir@869: public Collection getExterns() { lubomir@869: return externs; lubomir@869: } lubomir@869: lubomir@869: protected void addExtern(String extern) { lubomir@869: externs.add(extern); lubomir@869: } lubomir@869: } lubomir@869: lubomir@869: private static final class MediumObfuscationDelegate lubomir@869: extends AdvancedObfuscationDelegate { lubomir@869: @Override lubomir@869: public void exportJSProperty(Appendable out, lubomir@869: String destObject, lubomir@869: String propertyName) { lubomir@869: addExtern(propertyName); lubomir@869: } lubomir@869: } lubomir@869: lubomir@869: private static final class FullObfuscationDelegate lubomir@869: extends AdvancedObfuscationDelegate { lubomir@869: @Override lubomir@869: public void exportJSProperty(Appendable out, lubomir@869: String destObject, lubomir@869: String propertyName) throws IOException { lubomir@869: out.append("\n").append(destObject).append("['") lubomir@869: .append(propertyName) lubomir@869: .append("'] = ") lubomir@869: .append(destObject).append(".").append(propertyName) lubomir@869: .append(";\n"); lubomir@869: } lubomir@869: } jaroslav@750: }