vm/src/main/java/org/apidesign/vm4brwsr/Zips.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 10 Feb 2013 12:14:40 +0100
branchemul
changeset 706 a48961ff3e6b
parent 702 fa42b3d8cbbc
child 715 7572022945a0
permissions -rw-r--r--
Using FastJar to read the content table of JAR files
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@706
    35
        long bef = currentTimeMillis();
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@706
    40
        log("Iterating thru " + path + " took " + (currentTimeMillis() - bef) + "ms");
jaroslav@644
    41
    }
jaroslav@644
    42
    
jaroslav@644
    43
    public static void init() {
jaroslav@644
    44
    }
jaroslav@644
    45
    
jaroslav@702
    46
    public static byte[] loadFromCp(Object[] classpath, String res) throws Exception {
jaroslav@644
    47
        for (int i = 0; i < classpath.length; i++) {
jaroslav@644
    48
            Object c = classpath[i];
jaroslav@644
    49
            if (c instanceof String) {
jaroslav@644
    50
                try {
jaroslav@672
    51
                    String url = (String)c;
jaroslav@672
    52
                    final Zips z = toZip(url);
jaroslav@672
    53
                    c = classpath[i] = z;
jaroslav@672
    54
                    final byte[] man = z.findRes("META-INF/MANIFEST.MF");
jaroslav@672
    55
                    if (man != null) {
jaroslav@702
    56
                        String mainClass = processClassPathAttr(man, url, classpath);
jaroslav@702
    57
                        if (mainClass != null) {
jaroslav@702
    58
                            Class.forName(mainClass);
jaroslav@702
    59
                        }
jaroslav@672
    60
                    }
jaroslav@702
    61
                } catch (Exception ex) {
jaroslav@644
    62
                    classpath[i] = ex;
jaroslav@702
    63
                    throw ex;
jaroslav@644
    64
                }
jaroslav@644
    65
            }
jaroslav@702
    66
            if (res != null && c instanceof Zips) {
jaroslav@644
    67
                Object checkRes = ((Zips)c).findRes(res);
jaroslav@644
    68
                if (checkRes instanceof byte[]) {
jaroslav@644
    69
                    return (byte[])checkRes;
jaroslav@644
    70
                }
jaroslav@644
    71
            }
jaroslav@644
    72
        }
jaroslav@644
    73
        return null;
jaroslav@644
    74
    }
jaroslav@706
    75
    
jaroslav@706
    76
    @JavaScriptBody(args = { "msg" }, body = "console.log(msg.toString());")
jaroslav@706
    77
    private static native void log(String msg);
jaroslav@706
    78
jaroslav@706
    79
    private byte[] findRes(String res) throws IOException {
jaroslav@706
    80
        Object arr = findResImpl(res);
jaroslav@706
    81
        if (arr instanceof FastJar.Entry) {
jaroslav@706
    82
            long bef = currentTimeMillis();
jaroslav@706
    83
            InputStream zip = fj.getInputStream((FastJar.Entry)arr);
jaroslav@706
    84
            arr = readFully(new byte[512], zip);
jaroslav@706
    85
            putRes(res, arr);
jaroslav@706
    86
            log("Reading " + res + " took " + (currentTimeMillis() - bef) + "ms");
jaroslav@706
    87
        }
jaroslav@706
    88
        return (byte[]) arr;
jaroslav@706
    89
    }
jaroslav@644
    90
jaroslav@644
    91
    @JavaScriptBody(args = { "res" }, body = "var r = this[res]; return r ? r : null;")
jaroslav@706
    92
    private native Object findResImpl(String res);
jaroslav@644
    93
jaroslav@644
    94
    @JavaScriptBody(args = { "res", "arr" }, body = "this[res] = arr;")
jaroslav@706
    95
    private native void putRes(String res, Object arr);
jaroslav@644
    96
    
jaroslav@644
    97
    private static Zips toZip(String path) throws IOException {
jaroslav@644
    98
        URL u = new URL(path);
jaroslav@706
    99
        byte[] zipData = (byte[]) u.getContent(new Class[] { byte[].class });
jaroslav@706
   100
        return new Zips(path, zipData);
jaroslav@644
   101
    }
jaroslav@644
   102
jaroslav@702
   103
    private static String processClassPathAttr(final byte[] man, String url, Object[] classpath) throws IOException {
jaroslav@702
   104
        try (ParseMan is = new ParseMan(new ByteArrayInputStream(man))) {
jaroslav@672
   105
            String cp = is.toString();
jaroslav@702
   106
            if (cp != null) {
jaroslav@702
   107
                cp = cp.trim();
jaroslav@702
   108
                for (int p = 0; p < cp.length();) {
jaroslav@702
   109
                    int n = cp.indexOf(' ', p);
jaroslav@702
   110
                    if (n == -1) {
jaroslav@702
   111
                        n = cp.length();
jaroslav@702
   112
                    }
jaroslav@702
   113
                    String el = cp.substring(p, n);
jaroslav@702
   114
                    URL u = new URL(new URL(url), el);
jaroslav@702
   115
                    classpath = addToArray(classpath, u.toString());
jaroslav@702
   116
                    p = n + 1;
jaroslav@702
   117
                }
jaroslav@672
   118
            }
jaroslav@702
   119
            return is.getMainClass();
jaroslav@672
   120
        }
jaroslav@672
   121
    }
jaroslav@672
   122
jaroslav@672
   123
    private static Object[] addToArray(Object[] arr, String value) {
jaroslav@672
   124
        final int last = arr.length;
jaroslav@672
   125
        Object[] ret = enlargeArray(arr, last + 1);
jaroslav@672
   126
        ret[last] = value;
jaroslav@672
   127
        return ret;
jaroslav@672
   128
    }
jaroslav@672
   129
jaroslav@672
   130
    @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(null); return arr;throw('Arr: ' + arr);")
jaroslav@672
   131
    private static native Object[] enlargeArray(Object[] arr, int len);
jaroslav@644
   132
    @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(0);")
jaroslav@644
   133
    private static native void enlargeArray(byte[] arr, int len);
jaroslav@644
   134
jaroslav@644
   135
    @JavaScriptBody(args = { "arr", "len" }, body = "arr.splice(len, arr.length - len);")
jaroslav@644
   136
    private static native void sliceArray(byte[] arr, int len);
jaroslav@706
   137
jaroslav@706
   138
    private static Object readFully(byte[] arr, InputStream zip) throws IOException {
jaroslav@706
   139
        int offset = 0;
jaroslav@706
   140
        for (;;) {
jaroslav@706
   141
            int len = zip.read(arr, offset, arr.length - offset);
jaroslav@706
   142
            if (len == -1) {
jaroslav@706
   143
                break;
jaroslav@706
   144
            }
jaroslav@706
   145
            offset += len;
jaroslav@706
   146
            if (offset == arr.length) {
jaroslav@706
   147
                enlargeArray(arr, arr.length + 4096);
jaroslav@706
   148
            }
jaroslav@706
   149
        }
jaroslav@706
   150
        sliceArray(arr, offset);
jaroslav@706
   151
        return arr;
jaroslav@706
   152
    }
jaroslav@706
   153
jaroslav@706
   154
    private static long currentTimeMillis() {
jaroslav@706
   155
        return (long)m();
jaroslav@706
   156
    }
jaroslav@706
   157
    @JavaScriptBody(args = {  }, body = "return window.performance.now();")
jaroslav@706
   158
    private static native double m();
jaroslav@644
   159
    
jaroslav@644
   160
    
jaroslav@644
   161
}