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.
jaroslav@170
     1
/**
jaroslav@170
     2
 * Back 2 Browser Bytecode Translator
jaroslav@170
     3
 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
jaroslav@170
     4
 *
jaroslav@170
     5
 * This program is free software: you can redistribute it and/or modify
jaroslav@170
     6
 * it under the terms of the GNU General Public License as published by
jaroslav@170
     7
 * the Free Software Foundation, version 2 of the License.
jaroslav@170
     8
 *
jaroslav@170
     9
 * This program is distributed in the hope that it will be useful,
jaroslav@170
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
jaroslav@170
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
jaroslav@170
    12
 * GNU General Public License for more details.
jaroslav@170
    13
 *
jaroslav@170
    14
 * You should have received a copy of the GNU General Public License
jaroslav@170
    15
 * along with this program. Look for COPYING file in the top folder.
jaroslav@170
    16
 * If not, see http://opensource.org/licenses/GPL-2.0.
jaroslav@170
    17
 */
jaroslav@170
    18
package org.apidesign.vm4brwsr;
jaroslav@170
    19
jaroslav@170
    20
import java.io.ByteArrayInputStream;
jaroslav@170
    21
import java.io.IOException;
jaroslav@503
    22
import java.io.InputStream;
jaroslav@214
    23
import org.apidesign.bck2brwsr.core.JavaScriptBody;
jaroslav@170
    24
jaroslav@170
    25
/**
jaroslav@170
    26
 *
jaroslav@170
    27
 * @author Jaroslav Tulach <jtulach@netbeans.org>
jaroslav@170
    28
 */
jaroslav@278
    29
final class VMLazy {
jaroslav@277
    30
    private final Object loader;
jaroslav@278
    31
    private final Object[] args;
jaroslav@214
    32
    
jaroslav@278
    33
    private VMLazy(Object loader, Object[] args) {
jaroslav@277
    34
        this.loader = loader;
jaroslav@278
    35
        this.args = args;
jaroslav@170
    36
    }
jaroslav@170
    37
    
jaroslav@277
    38
    static void init() {
jaroslav@277
    39
    }
jaroslav@277
    40
    
jaroslav@278
    41
    @JavaScriptBody(args={"l", "res", "args" }, body = ""
jaroslav@299
    42
        + "\ntry {"
jaroslav@299
    43
        + "\n  return args[0](res.toString());"
jaroslav@299
    44
        + "\n} catch (x) {"
jaroslav@299
    45
        + "\n  throw Object.getOwnPropertyNames(l.vm).toString() + x.toString();"
jaroslav@299
    46
        + "\n}")
jaroslav@278
    47
    private static native byte[] read(Object l, String res, Object[] args);
jaroslav@277
    48
    
jaroslav@277
    49
    static Object load(Object loader, String name, Object[] arguments) 
jaroslav@277
    50
    throws IOException, ClassNotFoundException {
jaroslav@328
    51
        return new VMLazy(loader, arguments).load(name, false);
jaroslav@278
    52
    }
jaroslav@278
    53
    
jaroslav@328
    54
    private Object load(String name, boolean instance)
jaroslav@278
    55
    throws IOException, ClassNotFoundException {
jaroslav@277
    56
        String res = name.replace('.', '/') + ".class";
jaroslav@278
    57
        byte[] arr = read(loader, res, args);
jaroslav@277
    58
        if (arr == null) {
jaroslav@277
    59
            throw new ClassNotFoundException(name);
jaroslav@277
    60
        }
jaroslav@278
    61
//        beingDefined(loader, name);
jaroslav@278
    62
        StringBuilder out = new StringBuilder();
jaroslav@278
    63
        out.append("var loader = arguments[0];\n");
jaroslav@278
    64
        out.append("var vm = loader.vm;\n");
jaroslav@358
    65
        int prelude = out.length();
jaroslav@358
    66
        String initCode = new Gen(this, out).compile(new ByteArrayInputStream(arr));
jaroslav@278
    67
        String code = out.toString().toString();
jaroslav@358
    68
//        dump("Loading " + name);
jaroslav@358
    69
        dump(code);
jaroslav@278
    70
        String under = name.replace('.', '_');
jaroslav@358
    71
        Object fn = applyCode(loader, under, code, instance);
jaroslav@358
    72
        
jaroslav@358
    73
        if (!initCode.isEmpty()) {
jaroslav@358
    74
            out.setLength(prelude);
jaroslav@358
    75
            out.append(initCode);
jaroslav@358
    76
            code = out.toString().toString();
jaroslav@358
    77
            dump(code);
jaroslav@358
    78
            applyCode(loader, null, code, false);
jaroslav@358
    79
        }            
jaroslav@358
    80
        
jaroslav@358
    81
        return fn;
jaroslav@358
    82
    }
jaroslav@358
    83
jaroslav@358
    84
//    @JavaScriptBody(args = "s", body = "java.lang.System.out.println(s.toString());")
jaroslav@358
    85
    static void dump(String s) {
jaroslav@277
    86
    }
jaroslav@278
    87
jaroslav@278
    88
/* possibly not needed:
jaroslav@278
    89
    @JavaScriptBody(args = {"loader", "n" }, body =
jaroslav@278
    90
        "var cls = n.replace__Ljava_lang_String_2CC(n, '.','_').toString();" +
jaroslav@278
    91
        "loader.vm[cls] = true;\n"
jaroslav@278
    92
    )
jaroslav@278
    93
    private static native void beingDefined(Object loader, String name);
jaroslav@278
    94
*/
jaroslav@277
    95
    
jaroslav@278
    96
jaroslav@328
    97
    @JavaScriptBody(args = {"loader", "name", "script", "instance" }, body =
jaroslav@277
    98
        "try {\n" +
jaroslav@277
    99
        "  new Function(script)(loader, name);\n" +
jaroslav@277
   100
        "} catch (ex) {\n" +
jaroslav@571
   101
        "  throw 'Cannot compile ' + name + ' ' + ex + ' line: ' + ex.lineNumber + ' script:\\n' + script;\n" +
jaroslav@277
   102
        "}\n" +
jaroslav@358
   103
        "return name != null ? vm[name](instance) : null;\n"
jaroslav@277
   104
    )
jaroslav@328
   105
    private static native Object applyCode(Object loader, String name, String script, boolean instance);
jaroslav@277
   106
    
jaroslav@278
   107
    
jaroslav@278
   108
    private static final class Gen extends ByteCodeToJavaScript {
jaroslav@278
   109
        private final VMLazy lazy;
jaroslav@170
   110
jaroslav@278
   111
        public Gen(VMLazy vm, Appendable out) {
jaroslav@278
   112
            super(out);
jaroslav@278
   113
            this.lazy = vm;
jaroslav@278
   114
        }
jaroslav@278
   115
        
jaroslav@443
   116
        @JavaScriptBody(args = {"n"},
jaroslav@278
   117
        body =
jaroslav@442
   118
        "var cls = n.replace__Ljava_lang_String_2CC('/','_').toString();"
jaroslav@442
   119
        + "\nvar dot = n.replace__Ljava_lang_String_2CC('/','.').toString();"
jaroslav@592
   120
        + "\nvar lazy = this._lazy();"
jaroslav@592
   121
        + "\nvar loader = lazy._loader();"
jaroslav@278
   122
        + "\nvar vm = loader.vm;"
jaroslav@278
   123
        + "\nif (vm[cls]) return false;"
jaroslav@278
   124
        + "\nvm[cls] = function() {"
jaroslav@328
   125
        + "\n  var instance = arguments.length == 0 || arguments[0] === true;"
jaroslav@442
   126
        + "\n  return lazy.load__Ljava_lang_Object_2Ljava_lang_String_2Z(dot, instance);"
jaroslav@278
   127
        + "\n};"
jaroslav@278
   128
        + "\nreturn true;")
jaroslav@278
   129
        @Override
jaroslav@278
   130
        protected boolean requireReference(String internalClassName) {
jaroslav@278
   131
            throw new UnsupportedOperationException();
jaroslav@278
   132
        }
jaroslav@170
   133
jaroslav@278
   134
        @Override
jaroslav@503
   135
        protected void requireScript(String resourcePath) throws IOException {
jaroslav@503
   136
            InputStream is = getClass().getResourceAsStream(resourcePath);
jaroslav@503
   137
            StringBuilder sb = new StringBuilder();
jaroslav@503
   138
            for (;;) {
jaroslav@503
   139
                int ch = is.read();
jaroslav@503
   140
                if (ch == -1) {
jaroslav@503
   141
                    break;
jaroslav@503
   142
                }
jaroslav@503
   143
                sb.append((char)ch);
jaroslav@503
   144
            }
jaroslav@503
   145
            applyCode(lazy.loader, null, sb.toString(), false);
jaroslav@278
   146
        }
jaroslav@213
   147
jaroslav@278
   148
        @Override
jaroslav@278
   149
        String assignClass(String className) {
jaroslav@278
   150
            return "vm[arguments[1]]=";
jaroslav@278
   151
        }
jaroslav@277
   152
jaroslav@278
   153
        @Override
jaroslav@278
   154
        String accessClass(String classOperation) {
jaroslav@278
   155
            return "vm." + classOperation;
jaroslav@278
   156
        }
jaroslav@213
   157
    }
jaroslav@170
   158
}