vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 26 Jan 2013 08:47:05 +0100
changeset 592 5e13b1ac2886
parent 571 62c327a1e23f
child 729 1ee59fe94653
permissions -rw-r--r--
In order to support fields of the same name in subclasses we are now prefixing them with name of the class that defines them. To provide convenient way to access them from generated bytecode and also directly from JavaScript, there is a getter/setter function for each field. It starts with _ followed by the field name. If called with a parameter, it sets the field, with a parameter it just returns it.
     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.ByteArrayInputStream;
    21 import java.io.IOException;
    22 import java.io.InputStream;
    23 import org.apidesign.bck2brwsr.core.JavaScriptBody;
    24 
    25 /**
    26  *
    27  * @author Jaroslav Tulach <jtulach@netbeans.org>
    28  */
    29 final class VMLazy {
    30     private final Object loader;
    31     private final Object[] args;
    32     
    33     private VMLazy(Object loader, Object[] args) {
    34         this.loader = loader;
    35         this.args = args;
    36     }
    37     
    38     static void init() {
    39     }
    40     
    41     @JavaScriptBody(args={"l", "res", "args" }, body = ""
    42         + "\ntry {"
    43         + "\n  return args[0](res.toString());"
    44         + "\n} catch (x) {"
    45         + "\n  throw Object.getOwnPropertyNames(l.vm).toString() + x.toString();"
    46         + "\n}")
    47     private static native byte[] read(Object l, String res, Object[] args);
    48     
    49     static Object load(Object loader, String name, Object[] arguments) 
    50     throws IOException, ClassNotFoundException {
    51         return new VMLazy(loader, arguments).load(name, false);
    52     }
    53     
    54     private Object load(String name, boolean instance)
    55     throws IOException, ClassNotFoundException {
    56         String res = name.replace('.', '/') + ".class";
    57         byte[] arr = read(loader, res, args);
    58         if (arr == null) {
    59             throw new ClassNotFoundException(name);
    60         }
    61 //        beingDefined(loader, name);
    62         StringBuilder out = new StringBuilder();
    63         out.append("var loader = arguments[0];\n");
    64         out.append("var vm = loader.vm;\n");
    65         int prelude = out.length();
    66         String initCode = new Gen(this, out).compile(new ByteArrayInputStream(arr));
    67         String code = out.toString().toString();
    68 //        dump("Loading " + name);
    69         dump(code);
    70         String under = name.replace('.', '_');
    71         Object fn = applyCode(loader, under, code, instance);
    72         
    73         if (!initCode.isEmpty()) {
    74             out.setLength(prelude);
    75             out.append(initCode);
    76             code = out.toString().toString();
    77             dump(code);
    78             applyCode(loader, null, code, false);
    79         }            
    80         
    81         return fn;
    82     }
    83 
    84 //    @JavaScriptBody(args = "s", body = "java.lang.System.out.println(s.toString());")
    85     static void dump(String s) {
    86     }
    87 
    88 /* possibly not needed:
    89     @JavaScriptBody(args = {"loader", "n" }, body =
    90         "var cls = n.replace__Ljava_lang_String_2CC(n, '.','_').toString();" +
    91         "loader.vm[cls] = true;\n"
    92     )
    93     private static native void beingDefined(Object loader, String name);
    94 */
    95     
    96 
    97     @JavaScriptBody(args = {"loader", "name", "script", "instance" }, body =
    98         "try {\n" +
    99         "  new Function(script)(loader, name);\n" +
   100         "} catch (ex) {\n" +
   101         "  throw 'Cannot compile ' + name + ' ' + ex + ' line: ' + ex.lineNumber + ' script:\\n' + script;\n" +
   102         "}\n" +
   103         "return name != null ? vm[name](instance) : null;\n"
   104     )
   105     private static native Object applyCode(Object loader, String name, String script, boolean instance);
   106     
   107     
   108     private static final class Gen extends ByteCodeToJavaScript {
   109         private final VMLazy lazy;
   110 
   111         public Gen(VMLazy vm, Appendable out) {
   112             super(out);
   113             this.lazy = vm;
   114         }
   115         
   116         @JavaScriptBody(args = {"n"},
   117         body =
   118         "var cls = n.replace__Ljava_lang_String_2CC('/','_').toString();"
   119         + "\nvar dot = n.replace__Ljava_lang_String_2CC('/','.').toString();"
   120         + "\nvar lazy = this._lazy();"
   121         + "\nvar loader = lazy._loader();"
   122         + "\nvar vm = loader.vm;"
   123         + "\nif (vm[cls]) return false;"
   124         + "\nvm[cls] = function() {"
   125         + "\n  var instance = arguments.length == 0 || arguments[0] === true;"
   126         + "\n  return lazy.load__Ljava_lang_Object_2Ljava_lang_String_2Z(dot, instance);"
   127         + "\n};"
   128         + "\nreturn true;")
   129         @Override
   130         protected boolean requireReference(String internalClassName) {
   131             throw new UnsupportedOperationException();
   132         }
   133 
   134         @Override
   135         protected void requireScript(String resourcePath) throws IOException {
   136             InputStream is = getClass().getResourceAsStream(resourcePath);
   137             StringBuilder sb = new StringBuilder();
   138             for (;;) {
   139                 int ch = is.read();
   140                 if (ch == -1) {
   141                     break;
   142                 }
   143                 sb.append((char)ch);
   144             }
   145             applyCode(lazy.loader, null, sb.toString(), false);
   146         }
   147 
   148         @Override
   149         String assignClass(String className) {
   150             return "vm[arguments[1]]=";
   151         }
   152 
   153         @Override
   154         String accessClass(String classOperation) {
   155             return "vm." + classOperation;
   156         }
   157     }
   158 }