# HG changeset patch # User Jaroslav Tulach # Date 1398532436 -7200 # Node ID 8d0fc428ff7234e08adfed0b3a2d392e55023e21 # Parent c2a6be99ffe2e1f1b9e71c01eb1c1b927b5973c6 Can execute VMTest in browser, if it is annotated with @Exported annotation like the BooleanTest diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/fx/pom.xml --- a/launcher/fx/pom.xml Fri Apr 25 15:06:09 2014 +0200 +++ b/launcher/fx/pom.xml Sat Apr 26 19:13:56 2014 +0200 @@ -53,5 +53,10 @@ system ${jfxrt.jar} + + org.testng + testng + test + diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Fri Apr 25 15:06:09 2014 +0200 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Sat Apr 26 19:13:56 2014 +0200 @@ -26,6 +26,7 @@ import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.net.JarURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -40,6 +41,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.jar.JarFile; import java.util.logging.Level; import java.util.logging.Logger; import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; @@ -495,16 +497,28 @@ abstract void generateBck2BrwsrJS(StringBuilder sb, Res loader) throws IOException; abstract String harnessResource(); + String compileJar(JarFile jar) throws IOException { + return null; + } + String compileFromClassPath(URL f) { + return null; + } - class Res { - public InputStream get(String resource) throws IOException { + final class Res { + String compileJar(JarFile jar) throws IOException { + return BaseHTTPLauncher.this.compileJar(jar); + } + String compileFromClassPath(URL f) { + return BaseHTTPLauncher.this.compileFromClassPath(f); + } + public URL get(String resource) throws IOException { URL u = null; for (ClassLoader l : loaders) { Enumeration en = l.getResources(resource); while (en.hasMoreElements()) { u = en.nextElement(); if (u.toExternalForm().matches("^.*emul.*rt\\.jar.*$")) { - return u.openStream(); + return u; } } } @@ -512,7 +526,7 @@ if (u.toExternalForm().contains("rt.jar")) { LOG.log(Level.WARNING, "Fallback to bootclasspath for {0}", u); } - return u.openStream(); + return u; } throw new IOException("Can't find " + resource); } @@ -547,7 +561,7 @@ replace = args; } OutputStream os = response.getOutputStream(); - try (InputStream is = res.get(r)) { + try (InputStream is = res.get(r).openStream()) { copyStream(is, os, request.getRequestURL().toString(), replace); } catch (IOException ex) { response.setDetailMessage(ex.getLocalizedMessage()); @@ -603,10 +617,32 @@ if (res.startsWith("/")) { res = res.substring(1); } - try (InputStream is = loader.get(res)) { + URL url = loader.get(res); + if (url.getProtocol().equals("jar")) { + JarURLConnection juc = (JarURLConnection) url.openConnection(); + String s = loader.compileJar(juc.getJarFile()); + if (s != null) { + Writer w = response.getWriter(); + w.append(s); + w.close(); + return; + } + } + if (url.getProtocol().equals("file")) { + String s = loader.compileFromClassPath(url); + if (s != null) { + Writer w = response.getWriter(); + w.append(s); + w.close(); + return; + } + } + Exception ex = new Exception("Won't server bytes of " + url); + /* + try (InputStream is = url.openStream()) { response.setContentType("text/javascript"); Writer w = response.getWriter(); - w.append("["); + w.append("(["); for (int i = 0;; i++) { int b = is.read(); if (b == -1) { @@ -623,12 +659,14 @@ } w.append(Integer.toString(b)); } - w.append("\n]"); - } catch (IOException ex) { + w.append("\n])"); + } catch (IOException ex) + */ { response.setStatus(HttpStatus.NOT_FOUND_404); response.setError(); response.setDetailMessage(ex.getMessage()); } } + } } diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/http/pom.xml --- a/launcher/http/pom.xml Fri Apr 25 15:06:09 2014 +0200 +++ b/launcher/http/pom.xml Sat Apr 26 19:13:56 2014 +0200 @@ -56,5 +56,16 @@ vm4brwsr ${project.version} + + org.testng + testng + test + + + ${project.groupId} + emul.mini + ${project.version} + test + diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Fri Apr 25 15:06:09 2014 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Sat Apr 26 19:13:56 2014 +0200 @@ -17,8 +17,11 @@ */ package org.apidesign.bck2brwsr.launcher; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import java.util.jar.JarFile; import org.apidesign.vm4brwsr.Bck2Brwsr; /** @@ -36,13 +39,27 @@ String harnessResource() { return "org/apidesign/bck2brwsr/launcher/harness.xhtml"; } + + @Override + String compileJar(JarFile jar) throws IOException { + return CompileCP.compileJAR(jar); + } + + @Override String compileFromClassPath(URL f) { + try { + return CompileCP.compileFromClassPath(f); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } @Override void generateBck2BrwsrJS(StringBuilder sb, final Res loader) throws IOException { class R implements Bck2Brwsr.Resources { @Override public InputStream get(String resource) throws IOException { - return loader.get(resource); + return loader.get(resource).openStream(); } } @@ -54,7 +71,7 @@ + " request.open('GET', '/classes/' + res, false);\n" + " request.send();\n" + " if (request.status !== 200) return null;\n" - + " var arr = eval('(' + request.responseText + ')');\n" + + " var arr = eval(request.responseText);\n" + " return arr;\n" + " }\n" + " var prevvm = global.bck2brwsr;\n" @@ -63,6 +80,7 @@ + " args.unshift(ldCls);\n" + " return prevvm.apply(null, args);\n" + " };\n" + + " global.bck2brwsr.registerExtension = prevvm.registerExtension;\n" + "})(this);\n" ); } diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/CompileCP.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/CompileCP.java Sat Apr 26 19:13:56 2014 +0200 @@ -0,0 +1,154 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** + * + * @author Jaroslav Tulach + */ +class CompileCP { + static String compileJAR(final JarFile jar) throws IOException { + List arr = new ArrayList<>(); + List classes = new ArrayList<>(); + listJAR(jar, classes, arr); + StringWriter w = new StringWriter(); + try { + class JarRes extends EmulationResources implements Bck2Brwsr.Resources { + @Override + public InputStream get(String resource) throws IOException { + InputStream is = jar.getInputStream(new ZipEntry(resource)); + return is == null ? super.get(resource) : is; + } + } + + Bck2Brwsr.newCompiler() + .addRootClasses(classes.toArray(new String[0])) + .extension(true) + .resources(new JarRes()) + .generate(w); + w.flush(); + return w.toString(); + } catch (Throwable ex) { + throw new IOException("Cannot compile: ", ex); + } finally { + w.close(); + } + } + + static String compileFromClassPath(URL u) throws IOException, URISyntaxException { + File f = new File(u.toURI()); + for (String s : System.getProperty("java.class.path").split(File.pathSeparator)) { + if (!f.getPath().startsWith(s)) { + continue; + } + File root = new File(s); + List arr = new ArrayList<>(); + List classes = new ArrayList<>(); + listDir(root, null, classes, arr); + StringWriter w = new StringWriter(); + try { + Bck2Brwsr.newCompiler() + .addRootClasses(classes.toArray(new String[0])) + .extension(true) + .resources(new EmulationResources()) + .generate(w); + w.flush(); + return w.toString(); + } catch (ClassFormatError ex) { + throw new IOException(ex); + } finally { + w.close(); + } + } + return null; + } + + private static void listJAR(JarFile j, List classes, List resources) throws IOException { + Enumeration en = j.entries(); + while (en.hasMoreElements()) { + JarEntry e = en.nextElement(); + final String n = e.getName(); + if (n.endsWith("/")) { + continue; + } + int last = n.lastIndexOf('/'); + String pkg = n.substring(0, last + 1); + if (skipPkg(pkg)) { + continue; + } + if (n.endsWith(".class")) { + classes.add(n.substring(0, n.length() - 6)); + } else { + resources.add(n); + } + } + } + + private static boolean skipPkg(String pkg) { + return pkg.equals("org/apidesign/bck2brwsr/launcher/"); + } + + private static void listDir(File f, String pref, List classes, List resources) throws IOException { + File[] arr = f.listFiles(); + if (arr == null) { + if (f.getName().endsWith(".class")) { + classes.add(pref + f.getName().substring(0, f.getName().length() - 6)); + } else { + resources.add(pref + f.getName()); + } + } else { + for (File ch : arr) { + + listDir(ch, pref == null ? "" : pref + f.getName() + "/", classes, resources); + } + } + } + + static class EmulationResources implements Bck2Brwsr.Resources { + + @Override + public InputStream get(String name) throws IOException { + Enumeration en = CompileCP.class.getClassLoader().getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + if (u.toExternalForm().contains("rt.jar!")) { + throw new IOException("No emulation for " + u); + } + return u.openStream(); + } + } +} diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Fri Apr 25 15:06:09 2014 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Sat Apr 26 19:13:56 2014 +0200 @@ -31,6 +31,7 @@ * * @author Jaroslav Tulach */ +@org.apidesign.bck2brwsr.core.Exported public class Console { private Console() { } diff -r c2a6be99ffe2 -r 8d0fc428ff72 launcher/http/src/test/java/org/apidesign/bck2brwsr/launcher/CompileCPTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/http/src/test/java/org/apidesign/bck2brwsr/launcher/CompileCPTest.java Sat Apr 26 19:13:56 2014 +0200 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.File; +import java.net.URL; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class CompileCPTest { + public CompileCPTest() { + } + + @Test public void compileClassPath() throws Exception { + URL u = CompileCPTest.class.getResource("/" + CompileCPTest.class.getName().replace('.', '/') + ".class"); + assertNotNull(u, "URL found"); + assertEquals(u.getProtocol(), "file", "It comes from a disk"); + + String resources = CompileCP.compileFromClassPath(u); + assertNotNull(resources, "something compiled"); + } +} diff -r c2a6be99ffe2 -r 8d0fc428ff72 rt/emul/brwsrtest/src/test/java/org/apidesign/bck2brwsr/brwsrtest/BooleanTest.java --- a/rt/emul/brwsrtest/src/test/java/org/apidesign/bck2brwsr/brwsrtest/BooleanTest.java Fri Apr 25 15:06:09 2014 +0200 +++ b/rt/emul/brwsrtest/src/test/java/org/apidesign/bck2brwsr/brwsrtest/BooleanTest.java Sat Apr 26 19:13:56 2014 +0200 @@ -19,7 +19,6 @@ import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.vmtest.BrwsrTest; -import org.apidesign.bck2brwsr.vmtest.Compare; import org.apidesign.bck2brwsr.vmtest.VMTest; import org.testng.annotations.Factory; @@ -27,6 +26,7 @@ * * @author Jaroslav Tulach */ +@org.apidesign.bck2brwsr.core.Exported public class BooleanTest { @JavaScriptBody(args = { "tr" }, body = "return tr ? true : false;") private static native Object trueFalse(boolean tr); diff -r c2a6be99ffe2 -r 8d0fc428ff72 rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Apr 25 15:06:09 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Sat Apr 26 19:13:56 2014 +0200 @@ -433,14 +433,24 @@ + " for (var i = 0; i < extensions.length; ++i) {\n" + " extensions[i](vm);\n" + " }\n" + + " var knownExtensions = extensions.length;\n" + " var loader = {};\n" + " loader.vm = vm;\n" + " loader.loadClass = function(name) {\n" + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" + " var fn = vm[attr];\n" + " if (fn) return fn(false);\n" - + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" - + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " try {\n" + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " } catch (err) {\n" + + " while (knownExtensions < extensions.length) {\n" + + " extensions[knownExtensions++](vm);\n" + + " }\n" + + " fn = vm[attr];\n" + + " if (fn) return fn(false);\n" + + " throw err;\n" + + " }\n" + " }\n" + " if (vm.loadClass) {\n" + " throw 'Cannot initialize the bck2brwsr VM twice!';\n" @@ -456,9 +466,9 @@ + " return loader;\n" + " };\n"); out.append( - " global.bck2brwsr.registerExtension" - + " = function(extension) {\n" + " global.bck2brwsr.registerExtension = function(extension) {\n" + " extensions.push(extension);\n" + + " return null;\n" + " };\n"); out.append("}(this));"); }