jaroslav@644: /** jaroslav@644: * Back 2 Browser Bytecode Translator jaroslav@644: * Copyright (C) 2012 Jaroslav Tulach jaroslav@644: * jaroslav@644: * This program is free software: you can redistribute it and/or modify jaroslav@644: * it under the terms of the GNU General Public License as published by jaroslav@644: * the Free Software Foundation, version 2 of the License. jaroslav@644: * jaroslav@644: * This program is distributed in the hope that it will be useful, jaroslav@644: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@644: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@644: * GNU General Public License for more details. jaroslav@644: * jaroslav@644: * You should have received a copy of the GNU General Public License jaroslav@644: * along with this program. Look for COPYING file in the top folder. jaroslav@644: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@644: */ jaroslav@644: package org.apidesign.vm4brwsr; jaroslav@644: jaroslav@672: import java.io.ByteArrayInputStream; jaroslav@644: import java.io.IOException; jaroslav@1551: import java.io.InputStream; jaroslav@644: import java.net.URL; jaroslav@1575: import org.apidesign.bck2brwsr.core.Exported; jaroslav@644: import org.apidesign.bck2brwsr.core.JavaScriptBody; jaroslav@644: jaroslav@644: /** Conversion from classpath to load function. jaroslav@644: * jaroslav@644: * @author Jaroslav Tulach jaroslav@644: */ jaroslav@1550: final class ClassPath { jaroslav@1550: private ClassPath() { jaroslav@644: } jaroslav@644: jaroslav@644: public static void init() { jaroslav@644: } jaroslav@729: @JavaScriptBody(args = { "arr" }, body = "return arr.length;") jaroslav@729: private static native int length(Object arr); jaroslav@729: @JavaScriptBody(args = { "arr", "index" }, body = "return arr[index];") jaroslav@729: private static native Object at(Object arr, int index); jaroslav@729: @JavaScriptBody(args = { "arr", "index", "value" }, body = "arr[index] = value; return value;") jaroslav@729: private static native Object set(Object arr, int index, Object value); jaroslav@644: jaroslav@1554: private static boolean doingToZip; jaroslav@1575: jaroslav@1575: jaroslav@1575: @Exported static byte[] loadBytes(Object loader, String name, Object[] arguments, int skip) throws Exception { jaroslav@1575: return ClassPath.loadFromCp(arguments, name, skip); jaroslav@1575: } jaroslav@1575: static byte[] loadFromCp(Object classpath, String res, int skip) jaroslav@729: throws IOException, ClassNotFoundException { jaroslav@729: for (int i = 0; i < length(classpath); i++) { jaroslav@729: Object c = at(classpath, i); jaroslav@1554: if (c instanceof String && !doingToZip) { jaroslav@644: try { jaroslav@1554: doingToZip = true; jaroslav@672: String url = (String)c; jaroslav@1551: final Bck2Brwsr.Resources z = toZip(url); jaroslav@729: c = set(classpath, i, z); jaroslav@1551: final byte[] man = readBytes(z, "META-INF/MANIFEST.MF"); jaroslav@672: if (man != null) { jaroslav@702: String mainClass = processClassPathAttr(man, url, classpath); jaroslav@743: // if (mainClass != null) { jaroslav@743: // Class.forName(mainClass); jaroslav@743: // } jaroslav@672: } jaroslav@743: } catch (IOException ex) { jaroslav@729: set(classpath, i, ex); jaroslav@746: log("Cannot load " + c + " - " + ex.getClass().getName() + ":" + ex.getMessage()); jaroslav@1554: } finally { jaroslav@1554: doingToZip = false; jaroslav@644: } jaroslav@644: } jaroslav@729: if (res != null) { jaroslav@729: byte[] checkRes; jaroslav@1551: if (c instanceof Bck2Brwsr.Resources) { jaroslav@1551: checkRes = readBytes((Bck2Brwsr.Resources)c, res); jaroslav@1387: if (checkRes != null && --skip < 0) { jaroslav@1387: return checkRes; jaroslav@1387: } jaroslav@729: } else { jaroslav@1375: checkRes = callFunction(c, res, skip); jaroslav@1387: if (checkRes != null) { jaroslav@1387: return checkRes; jaroslav@1387: } jaroslav@644: } jaroslav@644: } jaroslav@644: } jaroslav@644: return null; jaroslav@644: } jaroslav@706: jaroslav@1375: @JavaScriptBody(args = { "fn", "res", "skip" }, body = jaroslav@1375: "if (typeof fn === 'function') return fn(res, skip);\n" jaroslav@729: + "return null;" jaroslav@729: ) jaroslav@1375: private static native byte[] callFunction(Object fn, String res, int skip); jaroslav@729: jaroslav@1358: @JavaScriptBody(args = { "msg" }, body = "if (typeof console !== 'undefined') console.log(msg.toString());") jaroslav@706: private static native void log(String msg); jaroslav@706: jaroslav@729: private static String processClassPathAttr(final byte[] man, String url, Object classpath) throws IOException { jaroslav@702: try (ParseMan is = new ParseMan(new ByteArrayInputStream(man))) { jaroslav@672: String cp = is.toString(); jaroslav@702: if (cp != null) { jaroslav@702: cp = cp.trim(); jaroslav@702: for (int p = 0; p < cp.length();) { jaroslav@702: int n = cp.indexOf(' ', p); jaroslav@702: if (n == -1) { jaroslav@702: n = cp.length(); jaroslav@702: } jaroslav@702: String el = cp.substring(p, n); jaroslav@702: URL u = new URL(new URL(url), el); jaroslav@702: classpath = addToArray(classpath, u.toString()); jaroslav@702: p = n + 1; jaroslav@702: } jaroslav@672: } jaroslav@702: return is.getMainClass(); jaroslav@672: } jaroslav@672: } jaroslav@672: jaroslav@729: private static Object addToArray(Object arr, String value) { jaroslav@729: final int last = length(arr); jaroslav@729: Object ret = enlargeArray(arr, last + 1); jaroslav@729: set(ret, last, value); jaroslav@672: return ret; jaroslav@672: } jaroslav@672: jaroslav@729: @JavaScriptBody(args = { "arr", "len" }, body = "while (arr.length < len) arr.push(null); return arr;") jaroslav@729: private static native Object enlargeArray(Object arr, int len); jaroslav@1554: jaroslav@1551: private static Bck2Brwsr.Resources toZip(String path) throws IOException { jaroslav@1551: URL u = new URL(path); jaroslav@1551: byte[] zipData = (byte[]) u.getContent(new Class[]{byte[].class}); jaroslav@1551: Bck2Brwsr.Resources r; jaroslav@1551: try { jaroslav@1551: Class fastJar = Class.forName("org.apidesign.bck2brwsr.vmzip.ZipResources"); jaroslav@1551: return (Bck2Brwsr.Resources) fastJar.getConstructor(byte[].class).newInstance(zipData); jaroslav@1551: } catch (Exception ex) { jaroslav@1551: log("Reading JARs is only possible with enum.zip module included: " + ex.getMessage()); jaroslav@1551: ex.printStackTrace(); jaroslav@1551: throw new IOException(ex); jaroslav@1551: } jaroslav@1551: } jaroslav@1551: jaroslav@1551: private static byte[] readBytes(Bck2Brwsr.Resources r, String res) throws IOException { jaroslav@1551: InputStream is = r.get(res); jaroslav@1551: if (is == null) { jaroslav@1551: return null; jaroslav@1551: } jaroslav@1551: byte[] arr = new byte[is.available()]; jaroslav@1551: int off = 0; jaroslav@1551: for (;;) { jaroslav@1551: int len = is.read(arr, off, arr.length - off); jaroslav@1551: if (len == -1) { jaroslav@1551: break; jaroslav@1551: } jaroslav@1551: off += len; jaroslav@1551: } jaroslav@1551: is.close(); jaroslav@1551: return arr; jaroslav@1551: } jaroslav@644: }