rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 26 Apr 2014 21:30:06 +0200
branchclosure
changeset 1491 4a1398eff4fb
parent 1094 36961c9a009f
child 1493 234fea368401
permissions -rw-r--r--
Different meaning of root vs. added classes. Ability to explicitly enumerate classes that should be exported and available with fully qualified name.
     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 org.apidesign.bck2brwsr.core.Exported;
    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 rootcls;
    59     private final StringArray classes;
    60     private final Resources res;
    61     private final boolean extension;
    62 
    63     private Bck2Brwsr(ObfuscationLevel level, StringArray rootcls, StringArray classes, Resources resources, boolean extension) {
    64         this.level = level;
    65         this.rootcls = rootcls;
    66         this.classes = classes;
    67         this.res = resources;
    68         this.extension = extension;
    69     }
    70     
    71     /** Helper method to generate virtual machine from bytes served by a <code>resources</code>
    72      * provider.
    73      *
    74      * @param out the output to write the generated JavaScript to
    75      * @param resources provider of class files to use
    76      * @param classes additional classes to include in the generated script
    77      * @throws IOException I/O exception can be thrown when something goes wrong
    78      */
    79     public static void generate(Appendable out, Resources resources, String... classes) throws IOException {
    80         newCompiler().resources(resources).addRootClasses(classes).generate(out);
    81     }
    82 
    83     /** Helper method to generate virtual machine from bytes served by a class loader.
    84      *
    85      * @param out the output to write the generated JavaScript to
    86      * @param loader class loader to load needed classes from
    87      * @param classes additional classes to include in the generated script
    88      * @throws IOException I/O exception can be thrown when something goes wrong
    89      */
    90     public static void generate(Appendable out, ClassLoader loader, String... classes) throws IOException {
    91         newCompiler().resources(loader).addRootClasses(classes).generate(out);
    92     }
    93     
    94     /** Creates new instance of Bck2Brwsr compiler which is ready to generate
    95      * empty Bck2Brwsr virtual machine. The instance can be further
    96      * configured by calling chain of methods. For example: 
    97      * <pre>
    98      * {@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)};
    99      * </pre>
   100      * 
   101      * @return new instance of the Bck2Brwsr compiler
   102      * @since 0.5
   103      */
   104     public static Bck2Brwsr newCompiler() {
   105         return new Bck2Brwsr(ObfuscationLevel.NONE, new StringArray(), new StringArray(), null, false);
   106     }
   107 
   108     /** Adds additional classes 
   109      * to the list of those that should be included in the generated
   110      * JavaScript file.
   111      * These classes are guaranteed to be available in the
   112      * generated virtual machine code accessible using their fully 
   113      * qualified name. This brings the same behavior as if the
   114      * classes were added by {@link #addClasses(java.lang.String...) } and
   115      * were annotated with {@link Exported} annotation.
   116      * 
   117      * @param classes the classes to add to the compilation
   118      * @return new instance of the Bck2Brwsr compiler which inherits
   119      * all values from <code>this</code>
   120      */
   121     public Bck2Brwsr addRootClasses(String... classes) {
   122         if (classes.length == 0) {
   123             return this;
   124         } else {
   125             return new Bck2Brwsr(level, rootcls.addAndNew(classes), this.classes, res,
   126                                  extension);
   127         }
   128     }
   129     
   130     /** Adds additional classes 
   131      * to the list of those that should be included in the generated
   132      * JavaScript file. These classes are guaranteed to be present,
   133      * but they may not be accessible through their fully qualified
   134      * name.
   135      * 
   136      * @param classes the classes to add to the compilation
   137      * @return new instance of the Bck2Brwsr compiler which inherits
   138      * all values from <code>this</code>
   139      * @since 0.9
   140      */
   141     public Bck2Brwsr addClasses(String... classes) {
   142         if (classes.length == 0) {
   143             return this;
   144         } else {
   145             return new Bck2Brwsr(level, rootcls, this.classes.addAndNew(classes), res,
   146                 extension);
   147         }
   148     }
   149     
   150     /** Changes the obfuscation level for the compiler by creating new instance
   151      * which inherits all values from <code>this</code> and adjust the level
   152      * of obfuscation.
   153      * 
   154      * @param level the new level of obfuscation
   155      * @return new instance of the compiler with changed level of obfuscation
   156      * @since 0.5
   157      */
   158     public Bck2Brwsr obfuscation(ObfuscationLevel level) {
   159         return new Bck2Brwsr(level, rootcls, classes, res, extension);
   160     }
   161     
   162     /** A way to change the provider of additional resources (classes) for the 
   163      * compiler. 
   164      * 
   165      * @param res the implementation of resources provider
   166      * @return new instance of the compiler with all values remaining the same, just 
   167      *   with different resources provider
   168      * @since 0.5
   169      */
   170     public Bck2Brwsr resources(Resources res) {
   171         return new Bck2Brwsr(level, rootcls, classes, res, extension);
   172     }
   173 
   174     /** Should one generate a library? By default the system generates
   175      * all transitive classes needed by the the transitive closure of
   176      * {@link #addRootClasses(java.lang.String...)} and {@link #addClasses(java.lang.String...)}.
   177      * By turning on the library mode, only classes explicitly listed
   178      * will be included in the archive. The others will be referenced
   179      * as external ones.
   180      * 
   181      * @param library turn on the library mode?
   182      * @return new instance of the compiler with library flag changed
   183      * @since 0.9
   184      */
   185     public Bck2Brwsr library(boolean library) {
   186         return new Bck2Brwsr(level, rootcls, classes, res, library);
   187     }
   188 
   189     /** A way to change the provider of additional resources (classes) for the 
   190      * compiler by specifying classloader to use for loading them.
   191      * 
   192      * @param loader class loader to load the resources from
   193      * @return new instance of the compiler with all values being the same, just 
   194      *   different resources provider
   195      * @since 0.5
   196      */
   197     public Bck2Brwsr resources(final ClassLoader loader) {
   198         return resources(new LdrRsrcs(loader));
   199     }
   200     
   201     /** Generates virtual machine based on previous configuration of the 
   202      * compiler.
   203      * 
   204      * @param out the output to write the generated JavaScript to
   205      * @since 0.5
   206      */
   207     public void generate(Appendable out) throws IOException {
   208         Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader());
   209         if (level != ObfuscationLevel.NONE) {
   210             try {
   211                 ClosureWrapper.produceTo(out, level, r, rootcls, classes, extension);
   212                 return;
   213             } catch (IOException ex) {
   214                 throw ex;
   215             } catch (Throwable ex) {
   216                 out.append("/* Failed to obfuscate: " + ex.getMessage()
   217                                + " */\n");
   218             }
   219         }
   220 
   221         VM.compile(out, r, rootcls, classes, extension);
   222     }
   223 
   224     /** Provider of resources (classes and other files). The 
   225      * {@link #generate(java.lang.Appendable, org.apidesign.vm4brwsr.Bck2Brwsr.Resources, java.lang.String[]) 
   226      * generator method} will call back here for all classes needed during
   227      * translation to JavaScript.
   228      */
   229     public interface Resources {
   230         /** Loads given resource (class or other file like image). The 
   231          * resource name to load bytes for the {@link String} class
   232          * would be <code>"java/lang/String.class"</code>.
   233          * 
   234          * @param resource path to resource to load
   235          * @return the input stream for the resource 
   236          * @throws IOException can be thrown if the loading fails on some error
   237          *   or the file cannot be found
   238          */
   239         public InputStream get(String resource) throws IOException;
   240     }
   241 }