rt/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 20 Oct 2013 20:36:03 +0200
changeset 1375 a6c71e376889
parent 1358 65dd5a650eab
child 1387 350f8aee0f60
permissions -rw-r--r--
Can load multiple resources of the same name even in testing and development mode
jaroslav@644
     1
/**
jaroslav@644
     2
 * Back 2 Browser Bytecode Translator
jaroslav@644
     3
 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
jaroslav@644
     4
 *
jaroslav@644
     5
 * This program is free software: you can redistribute it and/or modify
jaroslav@644
     6
 * it under the terms of the GNU General Public License as published by
jaroslav@644
     7
 * the Free Software Foundation, version 2 of the License.
jaroslav@644
     8
 *
jaroslav@644
     9
 * This program is distributed in the hope that it will be useful,
jaroslav@644
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
jaroslav@644
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
jaroslav@644
    12
 * GNU General Public License for more details.
jaroslav@644
    13
 *
jaroslav@644
    14
 * You should have received a copy of the GNU General Public License
jaroslav@644
    15
 * along with this program. Look for COPYING file in the top folder.
jaroslav@644
    16
 * If not, see http://opensource.org/licenses/GPL-2.0.
jaroslav@644
    17
 */
jaroslav@644
    18
package org.apidesign.vm4brwsr;
jaroslav@644
    19
jaroslav@672
    20
import java.io.ByteArrayInputStream;
jaroslav@644
    21
import java.io.IOException;
jaroslav@706
    22
import java.io.InputStream;
jaroslav@644
    23
import java.net.URL;
jaroslav@644
    24
import org.apidesign.bck2brwsr.core.JavaScriptBody;
jaroslav@706
    25
import org.apidesign.bck2brwsr.emul.zip.FastJar;
jaroslav@644
    26
jaroslav@644
    27
/** Conversion from classpath to load function.
jaroslav@644
    28
 *
jaroslav@644
    29
 * @author Jaroslav Tulach <jtulach@netbeans.org>
jaroslav@644
    30
 */
jaroslav@644
    31
final class Zips {
jaroslav@706
    32
    private final FastJar fj;
jaroslav@706
    33
jaroslav@706
    34
    private Zips(String path, byte[] zipData) throws IOException {
jaroslav@715
    35
        long bef = timeNow();
jaroslav@706
    36
        fj = new FastJar(zipData);
jaroslav@706
    37
        for (FastJar.Entry e : fj.list()) {
jaroslav@706
    38
            putRes(e.name, e);
jaroslav@706
    39
        }
jaroslav@715
    40
        log("Iterating thru " + path + " took " + (timeNow() - bef) + "ms");
jaroslav@644
    41
    }
jaroslav@644
    42
    
jaroslav@644
    43
    public static void init() {
jaroslav@644
    44
    }
jaroslav@729
    45
    @JavaScriptBody(args = { "arr" }, body = "return arr.length;")
jaroslav@729
    46
    private static native int length(Object arr);
jaroslav@729
    47
    @JavaScriptBody(args = { "arr", "index" }, body = "return arr[index];")
jaroslav@729
    48
    private static native Object at(Object arr, int index);
jaroslav@729
    49
    @JavaScriptBody(args = { "arr", "index", "value" }, body = "arr[index] = value; return value;")
jaroslav@729
    50
    private static native Object set(Object arr, int index, Object value);
jaroslav@644
    51
    
jaroslav@1375
    52
    public static byte[] loadFromCp(Object classpath, String res, int skip) 
jaroslav@729
    53
    throws IOException, ClassNotFoundException {
jaroslav@729
    54
        for (int i = 0; i < length(classpath); i++) {
jaroslav@729
    55
            Object c = at(classpath, i);
jaroslav@644
    56
            if (c instanceof String) {
jaroslav@644
    57
                try {
jaroslav@672
    58
                    String url = (String)c;
jaroslav@672
    59
                    final Zips z = toZip(url);
jaroslav@729
    60
                    c = set(classpath, i, z);
jaroslav@672
    61
                    final byte[] man = z.findRes("META-INF/MANIFEST.MF");
jaroslav@672
    62
                    if (man != null) {
jaroslav@702
    63
                        String mainClass = processClassPathAttr(man, url, classpath);
jaroslav@743
    64
//                        if (mainClass != null) {
jaroslav@743
    65
//                            Class.forName(mainClass);
jaroslav@743
    66
//                        }
jaroslav@672
    67
                    }
jaroslav@743
    68
                } catch (IOException ex) {
jaroslav@729
    69
                    set(classpath, i, ex);
jaroslav@746
    70
                    log("Cannot load " + c + " - " + ex.getClass().getName() + ":" + ex.getMessage());
jaroslav@644
    71
                }
jaroslav@644
    72
            }
jaroslav@729
    73
            if (res != null) {
jaroslav@729
    74
                byte[] checkRes;
jaroslav@729
    75
                if (c instanceof Zips) {
jaroslav@729
    76
                    checkRes = ((Zips)c).findRes(res);
jaroslav@729
    77
                } else {
jaroslav@1375
    78
                    checkRes = callFunction(c, res, skip);
jaroslav@1375
    79
                    skip = 0;
jaroslav@729
    80
                }
jaroslav@1375
    81
                if (checkRes != null && --skip < 0) {
jaroslav@729
    82
                    return checkRes;
jaroslav@644
    83
                }
jaroslav@644
    84
            }
jaroslav@644
    85
        }
jaroslav@644
    86
        return null;
jaroslav@644
    87
    }
jaroslav@706
    88
    
jaroslav@1375
    89
    @JavaScriptBody(args = { "fn", "res", "skip" }, body = 
jaroslav@1375
    90
        "if (typeof fn === 'function') return fn(res, skip);\n"
jaroslav@729
    91
      + "return null;"
jaroslav@729
    92
    )
jaroslav@1375
    93
    private static native byte[] callFunction(Object fn, String res, int skip);
jaroslav@729
    94
    
jaroslav@1358
    95
    @JavaScriptBody(args = { "msg" }, body = "if (typeof console !== 'undefined') console.log(msg.toString());")
jaroslav@706
    96
    private static native void log(String msg);
jaroslav@706
    97
jaroslav@706
    98
    private byte[] findRes(String res) throws IOException {
jaroslav@706
    99
        Object arr = findResImpl(res);
jaroslav@706
   100
        if (arr instanceof FastJar.Entry) {
jaroslav@715
   101
            long bef = timeNow();
jaroslav@706
   102
            InputStream zip = fj.getInputStream((FastJar.Entry)arr);
jaroslav@706
   103
            arr = readFully(new byte[512], zip);
jaroslav@706
   104
            putRes(res, arr);
jaroslav@715
   105
            log("Reading " + res + " took " + (timeNow() - bef) + "ms");
jaroslav@706
   106
        }
jaroslav@706
   107
        return (byte[]) arr;
jaroslav@706
   108
    }
jaroslav@644
   109
jaroslav@644
   110
    @JavaScriptBody(args = { "res" }, body = "var r = this[res]; return r ? r : null;")
jaroslav@706
   111
    private native Object findResImpl(String res);
jaroslav@644
   112
jaroslav@644
   113
    @JavaScriptBody(args = { "res", "arr" }, body = "this[res] = arr;")
jaroslav@706
   114
    private native void putRes(String res, Object arr);
jaroslav@644
   115
    
jaroslav@644
   116
    private static Zips toZip(String path) throws IOException {
jaroslav@644
   117
        URL u = new URL(path);
jaroslav@706
   118
        byte[] zipData = (byte[]) u.getContent(new Class[] { byte[].class });
jaroslav@706
   119
        return new Zips(path, zipData);
jaroslav@644
   120
    }
jaroslav@644
   121
jaroslav@729
   122
    private static String processClassPathAttr(final byte[] man, String url, Object classpath) throws IOException {
jaroslav@702
   123
        try (ParseMan is = new ParseMan(new ByteArrayInputStream(man))) {
jaroslav@672
   124
            String cp = is.toString();
jaroslav@702
   125
            if (cp != null) {
jaroslav@702
   126
                cp = cp.trim();
jaroslav@702
   127
                for (int p = 0; p < cp.length();) {
jaroslav@702
   128
                    int n = cp.indexOf(' ', p);
jaroslav@702
   129
                    if (n == -1) {
jaroslav@702
   130
                        n = cp.length();
jaroslav@702
   131
                    }
jaroslav@702
   132
                    String el = cp.substring(p, n);
jaroslav@702
   133
                    URL u = new URL(new URL(url), el);
jaroslav@702
   134
                    classpath = addToArray(classpath, u.toString());
jaroslav@702
   135
                    p = n + 1;
jaroslav@702
   136
                }
jaroslav@672
   137
            }
jaroslav@702
   138
            return is.getMainClass();
jaroslav@672
   139
        }
jaroslav@672
   140
    }
jaroslav@672
   141
jaroslav@729
   142
    private static Object addToArray(Object arr, String value) {
jaroslav@729
   143
        final int last = length(arr);
jaroslav@729
   144
        Object ret = enlargeArray(arr, last + 1);
jaroslav@729
   145
        set(ret, last, value);
jaroslav@672
   146
        return ret;
jaroslav@672
   147
    }
jaroslav@672
   148
jaroslav@729
   149
    @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(null); return arr;")
jaroslav@729
   150
    private static native Object enlargeArray(Object arr, int len);
jaroslav@644
   151
    @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(0);")
jaroslav@729
   152
    private static native void enlargeBytes(byte[] arr, int len);
jaroslav@644
   153
jaroslav@644
   154
    @JavaScriptBody(args = { "arr", "len" }, body = "arr.splice(len, arr.length - len);")
jaroslav@644
   155
    private static native void sliceArray(byte[] arr, int len);
jaroslav@706
   156
jaroslav@706
   157
    private static Object readFully(byte[] arr, InputStream zip) throws IOException {
jaroslav@706
   158
        int offset = 0;
jaroslav@706
   159
        for (;;) {
jaroslav@706
   160
            int len = zip.read(arr, offset, arr.length - offset);
jaroslav@706
   161
            if (len == -1) {
jaroslav@706
   162
                break;
jaroslav@706
   163
            }
jaroslav@706
   164
            offset += len;
jaroslav@706
   165
            if (offset == arr.length) {
jaroslav@729
   166
                enlargeBytes(arr, arr.length + 4096);
jaroslav@706
   167
            }
jaroslav@706
   168
        }
jaroslav@706
   169
        sliceArray(arr, offset);
jaroslav@706
   170
        return arr;
jaroslav@706
   171
    }
jaroslav@706
   172
jaroslav@715
   173
    private static long timeNow() {
jaroslav@715
   174
        double time = m();
jaroslav@715
   175
        if (time >= 0) {
jaroslav@715
   176
            return (long)time;
jaroslav@715
   177
        }
jaroslav@715
   178
        return org.apidesign.bck2brwsr.emul.lang.System.currentTimeMillis();
jaroslav@706
   179
    }
jaroslav@715
   180
    @JavaScriptBody(args = {}, body = 
jaroslav@715
   181
        "if (typeof window.performance === 'undefined') return -1;\n"
jaroslav@715
   182
      + "if (typeof window.performance.now === 'undefined') return -1;\n"
jaroslav@715
   183
      + "return window.performance.now();"
jaroslav@715
   184
    )
jaroslav@706
   185
    private static native double m();
jaroslav@644
   186
    
jaroslav@644
   187
    
jaroslav@644
   188
}