rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java
author Lubomir Nerad <lubomir.nerad@oracle.com>
Fri, 26 Apr 2013 18:48:34 +0200
branchclosure
changeset 1029 b1fe994d4267
parent 1020 a6bacea2518f
child 1086 2ac4283ee209
permissions -rw-r--r--
Partially working extension modules
     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 java.io.IOException;
    21 import java.io.InputStream;
    22 import java.net.URL;
    23 import java.util.Enumeration;
    24 
    25 /** Build your own virtual machine! Use methods in this class to generate
    26  * a skeleton JVM in JavaScript that contains pre-compiled classes of your
    27  * choice. The generated script defines one JavaScript method that can
    28  * be used to bootstrap and load the virtual machine: <pre>
    29  * var vm = bck2brwsr();
    30  * var main = vm.loadClass('org.your.pkg.Main');
    31  * main.main__V_3Ljava_lang_String_2(null);
    32  * </pre>
    33  * In case one wants to initialize the virtual machine with ability to
    34  * load classes lazily when needed, one can provide a loader function to
    35  * when creating the virtual machine: <pre>
    36  * var vm = bck2brwsr(function(resource) { 
    37  *   return null; // byte[] for the resource
    38  * });
    39  * </pre>
    40  * In this scenario, when a request for an unknown class is made, the loader
    41  * function is asked for its byte code and the system dynamically transforms
    42  * it to JavaScript.
    43  * <p>
    44  * Instead of a loader function, one can also provide a URL to a JAR file.
    45  * The <code>bck2brwsr</code> system will do its best to download the file
    46  * and provide loader function for it automatically.
    47  * <p>
    48  * One can provide as many loader functions and JAR URL references as necessary.
    49  * Then the initialization code would look like:<pre>
    50  * var vm = bck2brwsr(url1, url2, fnctn1, url3, functn2);
    51  * </pre>
    52  * The provided URLs and loader functions will be consulted one by one.
    53  *
    54  * @author Jaroslav Tulach <jtulach@netbeans.org>
    55  */
    56 public final class Bck2Brwsr {
    57     private final ObfuscationLevel level;
    58     private final StringArray classes;
    59     private final Resources res;
    60     private final boolean extension;
    61 
    62     private Bck2Brwsr(ObfuscationLevel level, StringArray classes, Resources resources, boolean extension) {
    63         this.level = level;
    64         this.classes = classes;
    65         this.res = resources;
    66         this.extension = extension;
    67     }
    68     
    69     /** Helper method to generate virtual machine from bytes served by a <code>resources</code>
    70      * provider.
    71      *
    72      * @param out the output to write the generated JavaScript to
    73      * @param resources provider of class files to use
    74      * @param classes additional classes to include in the generated script
    75      * @throws IOException I/O exception can be thrown when something goes wrong
    76      */
    77     public static void generate(Appendable out, Resources resources, String... classes) throws IOException {
    78         newCompiler().resources(resources).addRootClasses(classes).generate(out);
    79     }
    80 
    81     /** Helper method to generate virtual machine from bytes served by a class loader.
    82      *
    83      * @param out the output to write the generated JavaScript to
    84      * @param loader class loader to load needed classes from
    85      * @param classes additional classes to include in the generated script
    86      * @throws IOException I/O exception can be thrown when something goes wrong
    87      */
    88     public static void generate(Appendable out, ClassLoader loader, String... classes) throws IOException {
    89         newCompiler().resources(loader).addRootClasses(classes).generate(out);
    90     }
    91     
    92     /** Creates new instance of Bck2Brwsr compiler which is ready to generate
    93      * empty Bck2Brwsr virtual machine. The instance can be further
    94      * configured by calling chain of methods. For example: 
    95      * <pre>
    96      * {@link #createCompiler()}.{@link #resources(org.apidesign.vm4brwsr.Bck2Brwsr.Resources) resources(loader)}.{@link #addRootClasses(java.lang.String[]) addRootClasses("your/Clazz")}.{@link #generate(java.lang.Appendable) generate(out)};
    97      * </pre>
    98      * 
    99      * @return new instance of the Bck2Brwsr compiler
   100      * @since 0.5
   101      */
   102     public static Bck2Brwsr newCompiler() {
   103         StringArray arr = StringArray.asList(
   104                               Class.class.getName().replace('.', '/'),
   105                               VM.class.getName().replace('.', '/'));
   106         return new Bck2Brwsr(ObfuscationLevel.NONE, arr, null, false);
   107     }
   108 
   109     /** Creates new instance of the Bck2Brwsr compiler which inherits
   110      * all values from <code>this</code> instance and adds additional classes 
   111      * to the list of those that should be compiled by the {@link #generate(java.lang.Appendable)} 
   112      * method.
   113      * 
   114      * @param classes the classes to add to the compilation
   115      * @return new instance of the compiler
   116      */
   117     public Bck2Brwsr addRootClasses(String... classes) {
   118         if (classes.length == 0) {
   119             return this;
   120         } else {
   121             return new Bck2Brwsr(level, this.classes.addAndNew(classes), res,
   122                                  extension);
   123         }
   124     }
   125     
   126     /** Changes the obfuscation level for the compiler by creating new instance
   127      * which inherits all values from <code>this</code> and adjust the level
   128      * of obfuscation.
   129      * 
   130      * @param level the new level of obfuscation
   131      * @return new instance of the compiler with changed level of obfuscation
   132      * @since 0.5
   133      */
   134     public Bck2Brwsr obfuscation(ObfuscationLevel level) {
   135         return new Bck2Brwsr(level, classes, res, extension);
   136     }
   137     
   138     /** A way to change the provider of additional resources (classes) for the 
   139      * compiler. 
   140      * 
   141      * @param res the implementation of resources provider
   142      * @return new instance of the compiler with all values remaining the same, just 
   143      *   with different resources provider
   144      * @since 0.5
   145      */
   146     public Bck2Brwsr resources(Resources res) {
   147         return new Bck2Brwsr(level, classes, res, extension);
   148     }
   149 
   150     public Bck2Brwsr extension(boolean extension) {
   151         return new Bck2Brwsr(level, classes, res, extension);
   152     }
   153 
   154     /** A way to change the provider of additional resources (classes) for the 
   155      * compiler by specifying classloader to use for loading them.
   156      * 
   157      * @param loader class loader to load the resources from
   158      * @return new instance of the compiler with all values being the same, just 
   159      *   different resources provider
   160      * @since 0.5
   161      */
   162     public Bck2Brwsr resources(final ClassLoader loader) {
   163         return resources(new LdrRsrcs(loader));
   164     }
   165     
   166     /** Generates virtual machine based on previous configuration of the 
   167      * compiler.
   168      * 
   169      * @param out the output to write the generated JavaScript to
   170      * @since 0.5
   171      */
   172     public void generate(Appendable out) throws IOException {
   173         Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader());
   174 //        if (level != ObfuscationLevel.NONE) {
   175 //            try {
   176 //                ClosureWrapper.produceTo(out, level, r, classes);
   177 //                return;
   178 //            } catch (IOException ex) {
   179 //                throw ex;
   180 //            } catch (Throwable ex) {
   181 //                out.append("/* Failed to obfuscate: " + ex.getMessage()
   182 //                               + " */\n");
   183 //            }
   184 //        }
   185 
   186         if (extension) {
   187             VM.compileExtension(r, out, classes);
   188         } else {
   189             VM.compileStandalone(r, out, classes);
   190         }
   191     }
   192     
   193     /** Provider of resources (classes and other files). The 
   194      * {@link #generate(java.lang.Appendable, org.apidesign.vm4brwsr.Bck2Brwsr.Resources, java.lang.String[]) 
   195      * generator method} will call back here for all classes needed during
   196      * translation to JavaScript.
   197      */
   198     public interface Resources {
   199         /** Loads given resource (class or other file like image). The 
   200          * resource name to load bytes for the {@link String} class
   201          * would be <code>"java/lang/String.class"</code>.
   202          * 
   203          * @param resource path to resource to load
   204          * @return the input stream for the resource 
   205          * @throws IOException can be thrown if the loading fails on some error
   206          *   or the file cannot be found
   207          */
   208         public InputStream get(String resource) throws IOException;
   209     }
   210 }