# HG changeset patch # User Jaroslav Tulach # Date 1356332253 -3600 # Node ID e5b4bd29f26827365f54f2a7f528edcfd347c0f2 # Parent 52386a70f9d9ac2c6b41e0eb7c383a1814803944# Parent 3485327d30804f8940cdd9204e4d20b125693731 Merging launcher branch to default - seems to work reliably well diff -r 52386a70f9d9 -r e5b4bd29f268 emul/src/main/java/java/lang/Class.java --- a/emul/src/main/java/java/lang/Class.java Mon Dec 24 07:51:15 2012 +0100 +++ b/emul/src/main/java/java/lang/Class.java Mon Dec 24 07:57:33 2012 +0100 @@ -287,7 +287,12 @@ * @return {@code true} if this object represents an interface; * {@code false} otherwise. */ - public native boolean isInterface(); + public boolean isInterface() { + return (getAccess() & 0x200) != 0; + } + + @JavaScriptBody(args = "self", body = "return self.access;") + private native int getAccess(); /** @@ -330,6 +335,10 @@ * @see java.lang.Void#TYPE * @since JDK1.1 */ + @JavaScriptBody(args = "self", body = + "if (self.primitive) return true;" + + "else return false;" + ) public native boolean isPrimitive(); /** @@ -1090,10 +1099,13 @@ throw new UnsupportedOperationException(); } - static Class getPrimitiveClass(String type) { - // XXX - return Object.class; - } + @JavaScriptBody(args = "type", body = "" + + "var c = vm.java_lang_Class(true);" + + "c.jvmName = type;" + + "c.primitive = true;" + + "return c;" + ) + native static Class getPrimitiveClass(String type); public boolean desiredAssertionStatus() { return false; diff -r 52386a70f9d9 -r e5b4bd29f268 emul/src/main/java/java/lang/reflect/Method.java --- a/emul/src/main/java/java/lang/reflect/Method.java Mon Dec 24 07:51:15 2012 +0100 +++ b/emul/src/main/java/java/lang/reflect/Method.java Mon Dec 24 07:57:33 2012 +0100 @@ -137,7 +137,24 @@ * @return the return type for the method this object represents */ public Class getReturnType() { - throw new UnsupportedOperationException(); + switch (sig.charAt(0)) { + case 'I': return Integer.TYPE; + case 'J': return Long.TYPE; + case 'D': return Double.TYPE; + case 'F': return Float.TYPE; + case 'B': return Byte.TYPE; + case 'Z': return Boolean.TYPE; + case 'S': return Short.TYPE; +// case 'V': return Void.TYPE; + case 'L': try { + int up = sig.indexOf("_2"); + String type = sig.substring(1, up); + return Class.forName(type); + } catch (ClassNotFoundException ex) { + // should not happen + } + } + throw new UnsupportedOperationException(sig); } /** @@ -488,17 +505,54 @@ * @exception ExceptionInInitializerError if the initialization * provoked by this method fails. */ + public Object invoke(Object obj, Object... args) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + Object res = invoke0(this, obj, args); + if (getReturnType().isPrimitive()) { + res = fromPrimitive(getReturnType(), res); + } + return res; + } + @JavaScriptBody(args = { "method", "self", "args" }, body = "if (args.length > 0) throw 'unsupported now';" + "return method.fld_data(self);" ) - public Object invoke(Object obj, Object... args) - throws IllegalAccessException, IllegalArgumentException, - InvocationTargetException - { - throw new UnsupportedOperationException(); + private static native Object invoke0(Method m, Object self, Object[] args); + + private static Object fromPrimitive(Class type, Object o) { + if (type == Integer.TYPE) { + return fromRaw(Integer.class, "valueOf__Ljava_lang_Integer_2I", o); + } + if (type == Long.TYPE) { + return fromRaw(Long.class, "valueOf__Ljava_lang_Long_2J", o); + } + if (type == Double.TYPE) { + return fromRaw(Double.class, "valueOf__Ljava_lang_Double_2D", o); + } + if (type == Float.TYPE) { + return fromRaw(Float.class, "valueOf__Ljava_lang_Float_2F", o); + } + if (type == Byte.TYPE) { + return fromRaw(Byte.class, "valueOf__Ljava_lang_Byte_2B", o); + } + if (type == Boolean.TYPE) { + return fromRaw(Boolean.class, "valueOf__Ljava_lang_Boolean_2Z", o); + } + if (type == Short.TYPE) { + return fromRaw(Short.class, "valueOf__Ljava_lang_Short_2S", o); + } +// case 'V': return Void.TYPE; + throw new IllegalStateException("Can't convert " + o); } - + + @JavaScriptBody(args = { "cls", "m", "o" }, + body = "return cls.cnstr(false)[m](o);" + ) + private static native Integer fromRaw(Class cls, String m, Object o); + /** * Returns {@code true} if this method is a bridge * method; returns {@code false} otherwise. diff -r 52386a70f9d9 -r e5b4bd29f268 emul/src/main/java/java/net/URL.java --- a/emul/src/main/java/java/net/URL.java Mon Dec 24 07:51:15 2012 +0100 +++ b/emul/src/main/java/java/net/URL.java Mon Dec 24 07:57:33 2012 +0100 @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; /** * Class URL represents a Uniform Resource @@ -964,9 +965,16 @@ * @see java.net.URLConnection#getContent() */ public final Object getContent() throws java.io.IOException { - throw new IOException(); -// return openConnection().getContent(); + return loadText(toExternalForm()); } + + @JavaScriptBody(args = "url", body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, false);\n" + + "request.send();\n" + + "return request.responseText;\n" + ) + private static native String loadText(String url) throws IOException; /** * Gets the contents of this URL. This method is a shorthand for: @@ -984,8 +992,12 @@ */ public final Object getContent(Class[] classes) throws java.io.IOException { - throw new IOException(); -// return openConnection().getContent(classes); + for (Class c : classes) { + if (c == String.class) { + return getContent(); + } + } + return null; } static URLStreamHandler getURLStreamHandler(String protocol) { diff -r 52386a70f9d9 -r e5b4bd29f268 javap/src/main/java/org/apidesign/javap/ClassData.java --- a/javap/src/main/java/org/apidesign/javap/ClassData.java Mon Dec 24 07:51:15 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/ClassData.java Mon Dec 24 07:57:33 2012 +0100 @@ -326,6 +326,10 @@ return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } } + + public int getAccessFlags() { + return access; + } /** * Returns true if it is a class diff -r 52386a70f9d9 -r e5b4bd29f268 javaquery/demo-calculator/nbactions.xml --- a/javaquery/demo-calculator/nbactions.xml Mon Dec 24 07:51:15 2012 +0100 +++ b/javaquery/demo-calculator/nbactions.xml Mon Dec 24 07:57:33 2012 +0100 @@ -23,7 +23,7 @@ run process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr diff -r 52386a70f9d9 -r e5b4bd29f268 javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Mon Dec 24 07:51:15 2012 +0100 +++ b/javaquery/demo-calculator/pom.xml Mon Dec 24 07:57:33 2012 +0100 @@ -16,34 +16,19 @@ - - org.apidesign.bck2brwsr - mojo - 0.3-SNAPSHOT - - - - j2js - - - - - org.codehaus.mojo - exec-maven-plugin - 1.2.1 + org.apidesign.bck2brwsr + mojo + 0.3-SNAPSHOT - exec + brwsr - xdg-open - - ${project.build.directory}/classes/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml - + org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml diff -r 52386a70f9d9 -r e5b4bd29f268 javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml Mon Dec 24 07:51:15 2012 +0100 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml Mon Dec 24 07:57:33 2012 +0100 @@ -77,7 +77,11 @@ - + +
diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/pom.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/pom.xml	Mon Dec 24 07:57:33 2012 +0100
@@ -0,0 +1,49 @@
+
+
+  4.0.0
+  
+    org.apidesign
+    bck2brwsr
+    0.3-SNAPSHOT
+  
+  org.apidesign.bck2brwsr
+  launcher
+  0.3-SNAPSHOT
+  Bck2Brwsr Launcher
+  http://maven.apache.org
+    
+        
+            
+                org.apache.maven.plugins
+                maven-compiler-plugin
+                2.3.2
+                
+                    1.7
+                    1.7
+                
+            
+        
+    
+    
+    UTF-8
+  
+  
+    
+      junit
+      junit
+      3.8.1
+      test
+    
+    
+      org.glassfish.grizzly
+      grizzly-http-server
+      2.2.19
+    
+    
+      ${project.groupId}
+      vm4brwsr
+      ${project.version}
+    
+  
+
diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java	Mon Dec 24 07:57:33 2012 +0100
@@ -0,0 +1,422 @@
+/**
+ * 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.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apidesign.vm4brwsr.Bck2Brwsr;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+
+/**
+ * Lightweight server to launch Bck2Brwsr applications and tests.
+ * Supports execution in native browser as well as Java's internal 
+ * execution engine.
+ */
+public class Bck2BrwsrLauncher {
+    private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName());
+    private static final MethodInvocation END = new MethodInvocation(null, null);
+    private Set loaders = new LinkedHashSet<>();
+    private BlockingQueue methods = new LinkedBlockingQueue<>();
+    private long timeOut;
+    private final Res resources = new Res();
+    private Object[] brwsr;
+    private HttpServer server;
+    private CountDownLatch wait;
+    
+    
+    public MethodInvocation addMethod(Class clazz, String method) throws IOException {
+        loaders.add(clazz.getClassLoader());
+        MethodInvocation c = new MethodInvocation(clazz.getName(), method);
+        methods.add(c);
+        try {
+            c.await(timeOut);
+        } catch (InterruptedException ex) {
+            throw new IOException(ex);
+        }
+        return c;
+    }
+    
+    public void setTimeout(long ms) {
+        timeOut = ms;
+    }
+    
+    public void addClassLoader(ClassLoader url) {
+        this.loaders.add(url);
+    }
+    
+    public static void main( String[] args ) throws Exception {
+        Bck2BrwsrLauncher l = new Bck2BrwsrLauncher();
+        l.addClassLoader(Bck2BrwsrLauncher.class.getClassLoader());
+        l.showURL("org/apidesign/bck2brwsr/launcher/console.xhtml");
+        System.in.read();
+    }
+
+    public void showURL(String startpage) throws URISyntaxException, InterruptedException, IOException {
+        if (!startpage.startsWith("/")) {
+            startpage = "/" + startpage;
+        }
+        HttpServer s = initServer();
+        s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/");
+        launchServerAndBrwsr(s, startpage);
+    }
+
+    public void initialize() throws IOException {
+        try {
+            executeInBrowser();
+        } catch (InterruptedException ex) {
+            final InterruptedIOException iio = new InterruptedIOException(ex.getMessage());
+            iio.initCause(ex);
+            throw iio;
+        } catch (Exception ex) {
+            if (ex instanceof IOException) {
+                throw (IOException)ex;
+            }
+            if (ex instanceof RuntimeException) {
+                throw (RuntimeException)ex;
+            }
+            throw new IOException(ex);
+        }
+    }
+    
+    private HttpServer initServer() {
+        HttpServer s = HttpServer.createSimpleServer(".", new PortRange(8080, 65535));
+
+        final ServerConfiguration conf = s.getServerConfiguration();
+        conf.addHttpHandler(new Page(resources, 
+            "org/apidesign/bck2brwsr/launcher/console.xhtml",
+            "org.apidesign.bck2brwsr.launcher.Console", "welcome", "false"
+        ), "/console");
+        conf.addHttpHandler(new VM(resources), "/bck2brwsr.js");
+        conf.addHttpHandler(new VMInit(), "/vm.js");
+        conf.addHttpHandler(new Classes(resources), "/classes/");
+        return s;
+    }
+    
+    private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
+        wait = new CountDownLatch(1);
+        server = initServer();
+        ServerConfiguration conf = server.getServerConfiguration();
+        conf.addHttpHandler(new Page(resources, 
+            "org/apidesign/bck2brwsr/launcher/harness.xhtml"
+        ), "/execute");
+        conf.addHttpHandler(new HttpHandler() {
+            int cnt;
+            List cases = new ArrayList<>();
+            @Override
+            public void service(Request request, Response response) throws Exception {
+                String id = request.getParameter("request");
+                String value = request.getParameter("result");
+                
+                if (id != null && value != null) {
+                    LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value});
+                    value = value.replace("%20", " ");
+                    cases.get(Integer.parseInt(id)).result(value, null);
+                }
+                
+                MethodInvocation mi = methods.take();
+                if (mi == END) {
+                    response.getWriter().write("");
+                    wait.countDown();
+                    cnt = 0;
+                    LOG.log(Level.INFO, "End of data reached. Exiting.");
+                    return;
+                }
+                
+                cases.add(mi);
+                final String cn = mi.className;
+                final String mn = mi.methodName;
+                LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{cnt, cn, mn});
+                response.getWriter().write("{"
+                    + "className: '" + cn + "', "
+                    + "methodName: '" + mn + "', "
+                    + "request: " + cnt
+                    + "}");
+                cnt++;
+            }
+        }, "/data");
+
+        this.brwsr = launchServerAndBrwsr(server, "/execute");
+    }
+    
+    public void shutdown() throws InterruptedException, IOException {
+        methods.offer(END);
+        for (;;) {
+            int prev = methods.size();
+            if (wait.await(timeOut, TimeUnit.MILLISECONDS)) {
+                break;
+            }
+            if (prev == methods.size()) {
+                LOG.log(
+                    Level.WARNING, 
+                    "Timeout and no test has been executed meanwhile (at {0}). Giving up.", 
+                    methods.size()
+                );
+                break;
+            }
+            LOG.log(Level.INFO, 
+                "Timeout, but tests got from {0} to {1}. Trying again.", 
+                new Object[]{prev, methods.size()}
+            );
+        }
+        stopServerAndBrwsr(server, brwsr);
+    }
+    
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$') {
+                int cnt = is.read() - '0';
+                if (cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes());
+                }
+                if (cnt < params.length) {
+                    os.write(params[cnt].getBytes());
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
+        server.start();
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        
+        URI uri = new URI("http://localhost:" + port + page);
+        LOG.log(Level.INFO, "Showing {0}", uri);
+//        try {
+//            Desktop.getDesktop().browse(uri);
+//            return null;
+//        } catch (UnsupportedOperationException ex)
+        {
+//            File dir = File.createTempFile("chrome", ".dir");
+//            dir.delete();
+//            dir.mkdirs();
+//            String[] cmd = { 
+//                "google-chrome", "--user-data-dir=" + dir, "--app=" + uri.toString()
+//            };
+//            LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmd));
+//            final Process process = Runtime.getRuntime().exec(cmd);
+//            return new Object[] { process, dir };
+        }
+        {
+            String[] cmd = { 
+                "xdg-open", uri.toString()
+            };
+            LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmd));
+            final Process process = Runtime.getRuntime().exec(cmd);
+            return new Object[] { process, null };
+        }
+    }
+    
+    private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException, InterruptedException {
+        Process process = (Process)brwsr[0];
+        
+        server.stop();
+        InputStream stdout = process.getInputStream();
+        InputStream stderr = process.getErrorStream();
+        drain("StdOut", stdout);
+        drain("StdErr", stderr);
+        process.destroy();
+        int res = process.waitFor();
+        LOG.log(Level.INFO, "Exit code: {0}", res);
+
+        deleteTree((File)brwsr[1]);
+    }
+    
+    private static void drain(String name, InputStream is) throws IOException {
+        int av = is.available();
+        if (av > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("v== ").append(name).append(" ==v\n");
+            while (av-- > 0) {
+                sb.append((char)is.read());
+            }
+            sb.append("\n^== ").append(name).append(" ==^");
+            LOG.log(Level.INFO, sb.toString());
+        }
+    }
+
+    private void deleteTree(File file) {
+        if (file == null) {
+            return;
+        }
+        File[] arr = file.listFiles();
+        if (arr != null) {
+            for (File s : arr) {
+                deleteTree(s);
+            }
+        }
+        file.delete();
+    }
+
+    private class Res implements Bck2Brwsr.Resources {
+        @Override
+        public InputStream get(String resource) throws IOException {
+            for (ClassLoader l : loaders) {
+                URL u = null;
+                Enumeration en = l.getResources(resource);
+                while (en.hasMoreElements()) {
+                    u = en.nextElement();
+                }
+                if (u != null) {
+                    return u.openStream();
+                }
+            }
+            throw new IOException("Can't find " + resource);
+        }
+    }
+
+    private static class Page extends HttpHandler {
+        private final String resource;
+        private final String[] args;
+        private final Res res;
+        
+        public Page(Res res, String resource, String... args) {
+            this.res = res;
+            this.resource = resource;
+            this.args = args;
+        }
+
+        @Override
+        public void service(Request request, Response response) throws Exception {
+            String r = resource;
+            if (r == null) {
+                r = request.getHttpHandlerPath();
+                if (r.startsWith("/")) {
+                    r = r.substring(1);
+                }
+            }
+            if (r.endsWith(".html") || r.endsWith(".xhtml")) {
+                response.setContentType("text/html");
+            }
+            OutputStream os = response.getOutputStream();
+            try (InputStream is = res.get(r)) {
+                copyStream(is, os, request.getRequestURL().toString(), args);
+            } catch (IOException ex) {
+                response.setDetailMessage(ex.getLocalizedMessage());
+                response.setError();
+                response.setStatus(404);
+            }
+        }
+    }
+
+    private static class VM extends HttpHandler {
+        private final Res loader;
+
+        public VM(Res loader) {
+            this.loader = loader;
+        }
+
+        @Override
+        public void service(Request request, Response response) throws Exception {
+            response.setCharacterEncoding("UTF-8");
+            response.setContentType("text/javascript");
+            Bck2Brwsr.generate(response.getWriter(), loader);
+        }
+    }
+    private static class VMInit extends HttpHandler {
+        public VMInit() {
+        }
+
+        @Override
+        public void service(Request request, Response response) throws Exception {
+            response.setCharacterEncoding("UTF-8");
+            response.setContentType("text/javascript");
+            response.getWriter().append(
+                "function ldCls(res) {\n"
+                + "  var request = new XMLHttpRequest();\n"
+                + "  request.open('GET', '/classes/' + res, false);\n"
+                + "  request.send();\n"
+                + "  var arr = eval('(' + request.responseText + ')');\n"
+                + "  return arr;\n"
+                + "}\n"
+                + "var vm = new bck2brwsr(ldCls);\n");
+        }
+    }
+
+    private static class Classes extends HttpHandler {
+        private final Res loader;
+
+        public Classes(Res loader) {
+            this.loader = loader;
+        }
+
+        @Override
+        public void service(Request request, Response response) throws Exception {
+            String res = request.getHttpHandlerPath();
+            if (res.startsWith("/")) {
+                res = res.substring(1);
+            }
+            try (InputStream is = loader.get(res)) {
+                response.setContentType("text/javascript");
+                Writer w = response.getWriter();
+                w.append("[");
+                for (int i = 0;; i++) {
+                    int b = is.read();
+                    if (b == -1) {
+                        break;
+                    }
+                    if (i > 0) {
+                        w.append(", ");
+                    }
+                    if (i % 20 == 0) {
+                        w.write("\n");
+                    }
+                    if (b > 127) {
+                        b = b - 256;
+                    }
+                    w.append(Integer.toString(b));
+                }
+                w.append("\n]");
+            } catch (IOException ex) {
+                response.setError();
+                response.setDetailMessage(ex.getMessage());
+            }
+        }
+    }
+}
diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java	Mon Dec 24 07:57:33 2012 +0100
@@ -0,0 +1,178 @@
+/**
+ * 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.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Enumeration;
+import org.apidesign.bck2brwsr.core.JavaScriptBody;
+
+/**
+ *
+ * @author Jaroslav Tulach 
+ */
+public class Console {
+    public static String welcome() {
+        return "HellofromBck2Brwsr";
+    }
+    public static String multiply() {
+        return String.valueOf(Integer.MAX_VALUE / 2 + Integer.MAX_VALUE);
+    }
+    
+    @JavaScriptBody(args = {"id", "attr"}, body = 
+        "return window.document.getElementById(id)[attr].toString();")
+    private static native Object getAttr(String id, String attr);
+
+    @JavaScriptBody(args = {"id", "attr", "value"}, body = 
+        "window.document.getElementById(id)[attr] = value;")
+    private static native void setAttr(String id, String attr, Object value);
+    
+    @JavaScriptBody(args = {}, body = "window.close();")
+    private static native void closeWindow();
+
+    private static void log(String newText) {
+        String id = "result";
+        String attr = "value";
+        setAttr(id, attr, getAttr(id, attr) + "\n" + newText);
+        setAttr(id, "scrollTop", getAttr(id, "scrollHeight"));
+    }
+    
+    public static void execute() throws Exception {
+        String clazz = (String) getAttr("clazz", "value");
+        String method = (String) getAttr("method", "value");
+        Object res = invokeMethod(clazz, method);
+        setAttr("result", "value", res);
+    }
+    
+    public static void harness(String url) {
+        log("Connecting to " + url);
+        try {
+            URL u = new URL(url);
+            for (;;) {
+                String data = (String) u.getContent(new Class[] { String.class });
+                log("\nGot \"" + data + "\"");
+                if (data.isEmpty()) {
+                    log("No data, exiting");
+                    closeWindow();
+                    break;
+                }
+                
+                Case c = Case.parseData(data);
+                log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId());
+
+                Object result = invokeMethod(c.getClassName(), c.getMethodName());
+                
+                log("Result: " + result);
+                log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result);
+                u = new URL(url + "?request=" + c.getRequestId() + "&result=" + result);
+            }
+            
+            
+        } catch (Exception ex) {
+            log(ex.getMessage());
+        }
+    }
+    
+    static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {
+        final Object r = invokeMethod(clazz, method);
+        return r == null ? "null" : r.toString().toString();
+    }
+
+    /** Helper method that inspects the classpath and loads given resource
+     * (usually a class file). Used while running tests in Rhino.
+     * 
+     * @param name resource name to find
+     * @return the array of bytes in the given resource
+     * @throws IOException I/O in case something goes wrong
+     */
+    public static byte[] read(String name) throws IOException {
+        URL u = null;
+        Enumeration en = Console.class.getClassLoader().getResources(name);
+        while (en.hasMoreElements()) {
+            u = en.nextElement();
+        }
+        if (u == null) {
+            throw new IOException("Can't find " + name);
+        }
+        try (InputStream is = u.openStream()) {
+            byte[] arr;
+            arr = new byte[is.available()];
+            int offset = 0;
+            while (offset < arr.length) {
+                int len = is.read(arr, offset, arr.length - offset);
+                if (len == -1) {
+                    throw new IOException("Can't read " + name);
+                }
+                offset += len;
+            }
+            return arr;
+        }
+    }
+   
+    private static Object invokeMethod(String clazz, String method) 
+    throws ClassNotFoundException, InvocationTargetException, 
+    SecurityException, IllegalAccessException, IllegalArgumentException {
+        Method found = null;
+        Class c = Class.forName(clazz);
+        for (Method m : c.getMethods()) {
+            if (m.getName().equals(method)) {
+                found = m;
+            }
+        }
+        Object res;
+        if (found != null) {
+            res = found.invoke(null);
+        } else {
+            res = "Can't find method " + method + " in " + clazz;
+        }
+        return res;
+    }
+    
+    private static final class Case {
+        private final Object data;
+
+        private Case(Object data) {
+            this.data = data;
+        }
+        
+        public static Case parseData(String s) {
+            return new Case(toJSON(s));
+        }
+        
+        public String getMethodName() {
+            return value("methodName", data);
+        }
+
+        public String getClassName() {
+            return value("className", data);
+        }
+        
+        public String getRequestId() {
+            return value("request", data);
+        }
+        
+        @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');")
+        private static native Object toJSON(String s);
+        
+        @JavaScriptBody(args = {"p", "d"}, body = "return d[p].toString();")
+        private static native String value(String p, Object d);
+    }
+}
diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java	Mon Dec 24 07:57:33 2012 +0100
@@ -0,0 +1,114 @@
+/**
+ * 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.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import org.apidesign.vm4brwsr.Bck2Brwsr;
+
+/**
+ * Tests execution in Java's internal scripting engine.
+ */
+public final class JSLauncher {
+    private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName());
+    private Set loaders = new LinkedHashSet<>();
+    private final Res resources = new Res();
+    private Invocable code;
+    private Object console;
+    
+    
+    public MethodInvocation addMethod(Class clazz, String method) {
+        loaders.add(clazz.getClassLoader());
+        MethodInvocation mi = new MethodInvocation(clazz.getName(), method);
+        try {
+            mi.result(code.invokeMethod(
+                console,
+                "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2",
+                mi.className, mi.methodName).toString(), null);
+        } catch (ScriptException | NoSuchMethodException ex) {
+            mi.result(null, ex);
+        }
+        return mi;
+    }
+    
+    public void addClassLoader(ClassLoader url) {
+        this.loaders.add(url);
+    }
+
+    public void initialize() throws IOException {
+        try {
+            initRhino();
+        } catch (Exception ex) {
+            if (ex instanceof IOException) {
+                throw (IOException)ex;
+            }
+            if (ex instanceof RuntimeException) {
+                throw (RuntimeException)ex;
+            }
+            throw new IOException(ex);
+        }
+    }
+    
+    private void initRhino() throws IOException, ScriptException, NoSuchMethodException {
+        StringBuilder sb = new StringBuilder();
+        Bck2Brwsr.generate(sb, new Res());
+
+        ScriptEngineManager sem = new ScriptEngineManager();
+        ScriptEngine mach = sem.getEngineByExtension("js");
+
+        sb.append(
+              "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.Console.read);"
+            + "\nfunction initVM() { return vm; };"
+            + "\n");
+
+        Object res = mach.eval(sb.toString());
+        if (!(mach instanceof Invocable)) {
+            throw new IOException("It is invocable object: " + res);
+        }
+        code = (Invocable) mach;
+        
+        Object vm = code.invokeFunction("initVM");
+        console = code.invokeMethod(vm, "loadClass", Console.class.getName());
+    }
+    
+    private class Res implements Bck2Brwsr.Resources {
+        @Override
+        public InputStream get(String resource) throws IOException {
+            for (ClassLoader l : loaders) {
+                URL u = null;
+                Enumeration en = l.getResources(resource);
+                while (en.hasMoreElements()) {
+                    u = en.nextElement();
+                }
+                if (u != null) {
+                    return u.openStream();
+                }
+            }
+            throw new IOException("Can't find " + resource);
+        }
+    }
+}
diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java	Mon Dec 24 07:57:33 2012 +0100
@@ -0,0 +1,57 @@
+/**
+ * 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.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ * @author Jaroslav Tulach 
+ */
+public final class MethodInvocation {
+    final CountDownLatch wait = new CountDownLatch(1);
+    final String className;
+    final String methodName;
+    private String result;
+    private Exception exception;
+
+    MethodInvocation(String className, String methodName) {
+        this.className = className;
+        this.methodName = methodName;
+    }
+    
+    void await(long timeOut) throws InterruptedException {
+        wait.await(timeOut, TimeUnit.MILLISECONDS);
+    }
+    
+    void result(String r, Exception e) {
+        this.result = r;
+        this.exception = e;
+        wait.countDown();
+    }
+
+    @Override
+    public String toString() {
+        if (exception != null) {
+            return exception.toString();
+        }
+        return result;
+    }
+    
+}
diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/console.xhtml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/console.xhtml	Mon Dec 24 07:57:33 2012 +0100
@@ -0,0 +1,52 @@
+
+
+
+
+    
+        Bck2Brwsr Launcher
+    
+    
+        
+        
+        
+        

Bck2Browser Console Launcher

+ + Class Name: + +
+ Method Name: + + +
+ + + +
+ + + + + diff -r 52386a70f9d9 -r e5b4bd29f268 launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,39 @@ + + + + + + Bck2Brwsr Harness + + + + + +

Bck2Browser Execution Harness

+ + + + + + diff -r 52386a70f9d9 -r e5b4bd29f268 mojo/pom.xml --- a/mojo/pom.xml Mon Dec 24 07:51:15 2012 +0100 +++ b/mojo/pom.xml Mon Dec 24 07:57:33 2012 +0100 @@ -77,5 +77,10 @@ 3.0.2 jar + + ${project.groupId} + launcher + ${project.version} + diff -r 52386a70f9d9 -r e5b4bd29f268 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,118 @@ +/** + * 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.mojo; + +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher; + +/** Executes given HTML page in a browser. */ +@Mojo(name="brwsr", defaultPhase=LifecyclePhase.DEPLOY) +public class BrswrMojo extends AbstractMojo { + public BrswrMojo() { + } + /** Resource to show as initial page */ + @Parameter + private String startpage; + + @Parameter(defaultValue="${project}") + private MavenProject prj; + + /** Root of the class files */ + @Parameter(defaultValue="${project.build.directory}/classes") + private File classes; + + @Override + public void execute() throws MojoExecutionException { + if (startpage == null) { + throw new MojoExecutionException("You have to provide a start page"); + } + + try { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + + Bck2BrwsrLauncher httpServer = new Bck2BrwsrLauncher(); + httpServer.addClassLoader(url); + try { + httpServer.showURL(startpage); + } catch (Exception ex) { + throw new MojoExecutionException("Can't open " + startpage, ex); + } + + System.in.read(); + } catch (IOException ex) { + throw new MojoExecutionException("Can't show the browser", ex); + } + } + + private static File findNonEmptyFolder(File dir) throws MojoExecutionException { + if (!dir.isDirectory()) { + throw new MojoExecutionException("Not a directory " + dir); + } + File[] arr = dir.listFiles(); + if (arr.length == 1 && arr[0].isDirectory()) { + return findNonEmptyFolder(arr[0]); + } + return dir; + } + + private static long collectAllClasses(String prefix, File toCheck, List arr) { + File[] files = toCheck.listFiles(); + if (files != null) { + long newest = 0L; + for (File f : files) { + long lastModified = collectAllClasses(prefix + f.getName() + "/", f, arr); + if (newest < lastModified) { + newest = lastModified; + } + } + return newest; + } else if (toCheck.getName().endsWith(".class")) { + arr.add(prefix.substring(0, prefix.length() - 7)); + return toCheck.lastModified(); + } else { + return 0L; + } + } + + private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { + List arr = new ArrayList(); + arr.add(root.toURI().toURL()); + for (Artifact a : deps) { + arr.add(a.getFile().toURI().toURL()); + } + return new URLClassLoader(arr.toArray(new URL[0]), BrswrMojo.class.getClassLoader()); + } +} diff -r 52386a70f9d9 -r e5b4bd29f268 pom.xml --- a/pom.xml Mon Dec 24 07:51:15 2012 +0100 +++ b/pom.xml Mon Dec 24 07:57:33 2012 +0100 @@ -14,6 +14,8 @@ javaquery javap benchmarks + launcher + vmtest @@ -108,4 +110,4 @@ COPYING - + \ No newline at end of file diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Dec 24 07:51:15 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Dec 24 07:57:33 2012 +0100 @@ -155,6 +155,7 @@ out.append(accessClass("java_lang_Class(true);")); out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';"); out.append("\n CLS.$class.superclass = sprcls;"); + out.append("\n CLS.$class.access = ").append(jc.getAccessFlags()+";"); out.append("\n CLS.$class.cnstr = CLS;"); byte[] classAnno = jc.findAnnotationData(false); if (classAnno != null) { @@ -1028,7 +1029,7 @@ int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); - emit(out, "@1 = @2.@3;", + emit(out, "@1 = @2(false).constructor.@3;", smapper.pushT(type), accessClass(fi[0].replace('/', '_')), fi[1]); i += 2; @@ -1048,7 +1049,7 @@ int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); - emit(out, "@1.@2 = @3;", + emit(out, "@1(false).constructor.@2 = @3;", accessClass(fi[0].replace('/', '_')), fi[1], smapper.popT(type)); i += 2; diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Mon Dec 24 07:51:15 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Mon Dec 24 07:57:33 2012 +0100 @@ -61,10 +61,27 @@ StringBuilder out = new StringBuilder(); out.append("var loader = arguments[0];\n"); out.append("var vm = loader.vm;\n"); - new Gen(this, out).compile(new ByteArrayInputStream(arr)); + int prelude = out.length(); + String initCode = new Gen(this, out).compile(new ByteArrayInputStream(arr)); String code = out.toString().toString(); +// dump("Loading " + name); + dump(code); String under = name.replace('.', '_'); - return applyCode(loader, under, code, instance); + Object fn = applyCode(loader, under, code, instance); + + if (!initCode.isEmpty()) { + out.setLength(prelude); + out.append(initCode); + code = out.toString().toString(); + dump(code); + applyCode(loader, null, code, false); + } + + return fn; + } + +// @JavaScriptBody(args = "s", body = "java.lang.System.out.println(s.toString());") + static void dump(String s) { } /* possibly not needed: @@ -82,7 +99,7 @@ "} catch (ex) {\n" + " throw 'Cannot compile ' + ex + ' line: ' + ex.lineNumber + ' script:\\n' + script;\n" + "}\n" + - "return vm[name](instance);\n" + "return name != null ? vm[name](instance) : null;\n" ) private static native Object applyCode(Object loader, String name, String script, boolean instance); diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Mon Dec 24 07:51:15 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Mon Dec 24 07:57:33 2012 +0100 @@ -101,6 +101,18 @@ "java.io.IOException", false, "name" ); } + @Test public void primitiveReturnType() throws Exception { + assertExec("Tries to get an integer via reflection", Classes.class, + "primitiveType__Ljava_lang_String_2Ljava_lang_String_2", + Classes.primitiveType("primitive"), "primitive" + ); + } + @Test public void primitiveBoolReturnType() throws Exception { + assertExec("Tries to get an integer via reflection", Classes.class, + "primitiveType__Ljava_lang_String_2Ljava_lang_String_2", + Classes.primitiveType("primitiveB"), "primitiveB" + ); + } @Test public void javaAnnotatedMethod() throws Exception { assertEquals(Classes.reflectiveMethodCall(false, null), "java.io.IOException", "Calls the name() method via reflection"); } @@ -116,6 +128,26 @@ "java.io.IOException" ); } + @Test public void noInterface() throws Exception { + assertExec("Calls Class.isInterface", Classes.class, + "isInterface__ZLjava_lang_String_2", + 0.0, "java.lang.String" + ); + } + /* + @Test public void isInterface() throws Exception { + assertExec("Calls Class.isInterface", Classes.class, + "isInterface__ZLjava_lang_String_2", + 1.0, "java.lang.Runnable" + ); + } + */ + @Test public void integerType() throws Exception { + assertExec("Computes the type", Classes.class, + "intType__Ljava_lang_String_2", + Classes.intType() + ); + } private static CharSequence codeSeq; private static Invocable code; diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Mon Dec 24 07:51:15 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Mon Dec 24 07:57:33 2012 +0100 @@ -38,6 +38,10 @@ return c.getName(); } + public static boolean isInterface(String s) throws ClassNotFoundException { + return Class.forName(s).isInterface(); + } + public static boolean equalsClassesOfExceptions() { return MalformedURLException.class.getSuperclass() == IOException.class; } @@ -88,6 +92,21 @@ return null; } + public static String intType() { + return Integer.TYPE.getName(); + } + + public static int primitive() { + return 1; + } + public static boolean primitiveB() { + return true; + } + + public static String primitiveType(String method) throws Exception { + return reflectiveMethodCall(false, method).getClass().getName(); + } + @JavaScriptBody(args = "msg", body = "throw msg;") private static native void thrw(String msg); diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/test/java/org/apidesign/vm4brwsr/Compare.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Compare.java Mon Dec 24 07:51:15 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * 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.vm4brwsr; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Can be applied on a method that yields a return value. - * Together with {@link VMCompare#create} it can be used to write - * methods which are executed in real as well as JavaScript VMs and - * their results are compared. - * - * @author Jaroslav Tulach - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Compare { - -} diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java Mon Dec 24 07:51:15 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -/** - * 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.vm4brwsr; - -import java.lang.reflect.Method; -import java.util.Map; -import java.util.WeakHashMap; -import javax.script.Invocable; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import org.testng.Assert; -import org.testng.ITest; -import org.testng.annotations.Factory; -import org.testng.annotations.Test; - -/** A TestNG {@link Factory} that seeks for {@link Compare} annotations - * in provided class and builds set of tests that compare the computations - * in real as well as JavaScript virtual machines. Use as:
- * {@code @}{@link Factory} public static create() {
- *   return @{link CompareVMs}.{@link #create(YourClass.class);
- * }
- * - * @author Jaroslav Tulach - */ -public final class CompareVMs implements ITest { - private final Run first, second; - private final Method m; - - private CompareVMs(Method m, Run first, Run second) { - this.first = first; - this.second = second; - this.m = m; - } - - public static Object[] create(Class clazz) { - Method[] arr = clazz.getMethods(); - Object[] ret = new Object[3 * arr.length]; - int cnt = 0; - for (Method m : arr) { - Compare c = m.getAnnotation(Compare.class); - if (c == null) { - continue; - } - final Run real = new Run(m, false); - final Run js = new Run(m, true); - ret[cnt++] = real; - ret[cnt++] = js; - ret[cnt++] = new CompareVMs(m, real, js); - } - Object[] r = new Object[cnt]; - for (int i = 0; i < cnt; i++) { - r[i] = ret[i]; - } - return r; - } - - @Test(dependsOnGroups = "run") public void compareResults() throws Throwable { - Object v1 = first.value; - Object v2 = second.value; - if (v1 instanceof Number) { - v1 = ((Number)v1).doubleValue(); - } - Assert.assertEquals(v2, v1, "Comparing results"); - } - - @Override - public String getTestName() { - return m.getName() + "[Compare]"; - } - - public static final class Run implements ITest { - private final Method m; - private final boolean js; - Object value; - private Invocable code; - private CharSequence codeSeq; - private static final Map compiled = new WeakHashMap(); - - private Run(Method m, boolean js) { - this.m = m; - this.js = js; - } - - private void compileTheCode(Class clazz) throws Exception { - final Object[] data = compiled.get(clazz); - if (data != null) { - code = (Invocable) data[0]; - codeSeq = (CharSequence) data[1]; - return; - } - StringBuilder sb = new StringBuilder(); - Bck2Brwsr.generate(sb, CompareVMs.class.getClassLoader()); - - ScriptEngineManager sem = new ScriptEngineManager(); - ScriptEngine js = sem.getEngineByExtension("js"); - js.getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE); - - sb.append("\nfunction initVM() {" - + "\n return bck2brwsr(" - + "\n function(name) { return loader.get(name);}" - + "\n );" - + "\n};"); - - Object res = js.eval(sb.toString()); - Assert.assertTrue(js instanceof Invocable, "It is invocable object: " + res); - code = (Invocable) js; - codeSeq = sb; - compiled.put(clazz, new Object[] { code, codeSeq }); - } - - @Test(groups = "run") public void executeCode() throws Throwable { - if (js) { - try { - compileTheCode(m.getDeclaringClass()); - Object vm = code.invokeFunction("initVM"); - Object inst = code.invokeMethod(vm, "loadClass", m.getDeclaringClass().getName()); - value = code.invokeMethod(inst, m.getName() + "__" + computeSignature(m)); - } catch (Exception ex) { - throw new AssertionError(StaticMethodTest.dumpJS(codeSeq)).initCause(ex); - } - } else { - value = m.invoke(m.getDeclaringClass().newInstance()); - } - } - @Override - public String getTestName() { - return m.getName() + (js ? "[JavaScript]" : "[Java]"); - } - - private static String computeSignature(Method m) { - StringBuilder sb = new StringBuilder(); - appendType(sb, m.getReturnType()); - for (Class c : m.getParameterTypes()) { - appendType(sb, c); - } - return sb.toString(); - } - - private static void appendType(StringBuilder sb, Class t) { - if (t == null) { - sb.append('V'); - return; - } - if (t.isPrimitive()) { - int ch = -1; - if (t == int.class) { - ch = 'I'; - } - if (t == short.class) { - ch = 'S'; - } - if (t == byte.class) { - ch = 'B'; - } - if (t == boolean.class) { - ch = 'Z'; - } - if (t == long.class) { - ch = 'J'; - } - if (t == float.class) { - ch = 'F'; - } - if (t == double.class) { - ch = 'D'; - } - assert ch != -1 : "Unknown primitive type " + t; - sb.append((char)ch); - return; - } - if (t.isArray()) { - sb.append("_3"); - appendType(sb, t.getComponentType()); - return; - } - sb.append('L'); - sb.append(t.getName().replace('.', '_')); - sb.append("_2"); - } - } -} diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java Mon Dec 24 07:51:15 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * 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.vm4brwsr.tck; - -import org.apidesign.vm4brwsr.Compare; -import org.apidesign.vm4brwsr.CompareVMs; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareHashTest { - @Compare public int hashOfString() { - return "Ahoj".hashCode(); - } - - @Compare public int hashRemainsYieldsZero() { - Object o = new Object(); - return o.hashCode() - o.hashCode(); - } - - @Factory - public static Object[] create() { - return CompareVMs.create(CompareHashTest.class); - } -} diff -r 52386a70f9d9 -r e5b4bd29f268 vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java Mon Dec 24 07:51:15 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/** - * 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.vm4brwsr.tck; - -import java.net.MalformedURLException; -import java.net.URL; -import org.apidesign.vm4brwsr.Compare; -import org.apidesign.vm4brwsr.CompareVMs; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareStringsTest { - @Compare public static Object compareURLs() throws MalformedURLException { - return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); - } - - @Compare public String deleteLastTwoCharacters() { - StringBuilder sb = new StringBuilder(); - sb.append("453.0"); - if (sb.toString().endsWith(".0")) { - final int l = sb.length(); - sb.delete(l - 2, l); - } - return sb.toString().toString(); - } - - @Compare public String nameOfStringClass() throws Exception { - return Class.forName("java.lang.String").getName(); - } - @Compare public String nameOfArrayClass() throws Exception { - return Class.forName("org.apidesign.vm4brwsr.Array").getName(); - } - - @Compare public String lowerHello() { - return "HeLlO".toLowerCase(); - } - - @Compare public String lowerA() { - return String.valueOf(Character.toLowerCase('A')).toString(); - } - @Compare public String upperHello() { - return "hello".toUpperCase(); - } - - @Compare public String upperA() { - return String.valueOf(Character.toUpperCase('a')).toString(); - } - - @Compare public boolean matchRegExp() throws Exception { - return "58038503".matches("\\d*"); - } - - @Compare public boolean doesNotMatchRegExp() throws Exception { - return "58038503GH".matches("\\d*"); - } - - @Compare public boolean doesNotMatchRegExpFully() throws Exception { - return "Hello".matches("Hell"); - } - - @Compare public String variousCharacterTests() throws Exception { - StringBuilder sb = new StringBuilder(); - - sb.append(Character.isUpperCase('a')); - sb.append(Character.isUpperCase('A')); - sb.append(Character.isLowerCase('a')); - sb.append(Character.isLowerCase('A')); - - sb.append(Character.isLetter('A')); - sb.append(Character.isLetterOrDigit('9')); - sb.append(Character.isLetterOrDigit('A')); - sb.append(Character.isLetter('0')); - - return sb.toString().toString(); - } - - @Compare - public String nullFieldInitialized() { - NullField nf = new NullField(); - return ("" + nf.name).toString(); - } - - @Factory - public static Object[] create() { - return CompareVMs.create(CompareStringsTest.class); - } - - private static final class NullField { - - String name; - } -} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/pom.xml Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.apidesign + bck2brwsr + 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + vmtest + 0.3-SNAPSHOT + + VM Testing APIs + http://bck2brwsr.apidesign.org + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + + UTF-8 + + + + org.testng + testng + compile + + + junit + junit + + + + + ${project.groupId} + vm4brwsr + 0.3-SNAPSHOT + jar + + + ${project.groupId} + emul + 0.3-SNAPSHOT + + + ${project.groupId} + launcher + ${project.version} + + + diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,36 @@ +/** + * 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.vmtest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Can be applied on a method that yields a return value. + * Together with {@link VMTest#create} it can be used to write + * methods which are executed in real as well as JavaScript VMs and + * their results are compared. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Compare { + +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,43 @@ +/** + * 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.vmtest; + +import org.apidesign.bck2brwsr.vmtest.impl.CompareCase; +import org.testng.annotations.Factory; + +/** A TestNG {@link Factory} that seeks for {@link Compare} annotations + * in provided class and builds set of tests that compare the computations + * in real as well as JavaScript virtual machines. Use as:
+ * {@code @}{@link Factory} public static create() {
+ *   return @{link VMTest}.{@link #create(YourClass.class);
+ * }
+ * + * @author Jaroslav Tulach + */ +public final class VMTest { + /** Inspects clazz and for each {@lik Compare} method creates + * instances of tests. Each instance runs the test in different virtual + * machine and at the end they compare the results. + * + * @param clazz the class to inspect + * @return the set of created tests + */ + public static Object[] create(Class clazz) { + return CompareCase.create(clazz); + } +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,130 @@ +/** + * 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.vmtest.impl; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.WeakHashMap; +import javax.script.Invocable; +import org.apidesign.bck2brwsr.launcher.MethodInvocation; +import org.testng.ITest; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public final class Bck2BrwsrCase implements ITest { + private final Method m; + private final Launchers l; + private final int type; + Object value; + private Invocable code; + private CharSequence codeSeq; + private static final Map compiled = new WeakHashMap<>(); + private Object inst; + + Bck2BrwsrCase(Method m, int type, Launchers l) { + this.l = l; + this.m = m; + this.type = type; + } + + @Test(groups = "run") + public void executeCode() throws Throwable { + if (type == 1) { + MethodInvocation c = l.addMethod(m.getDeclaringClass(), m.getName(), false); + value = c.toString(); + } else if (type == 2) { + MethodInvocation c = l.addMethod(m.getDeclaringClass(), m.getName(), true); + value = c.toString(); + } else { + value = m.invoke(m.getDeclaringClass().newInstance()); + } + } + + @Override + public String getTestName() { + return m.getName() + "[" + typeName() + "]"; + } + + final String typeName() { + switch (type) { + case 0: + return "Java"; + case 1: + return "JavaScript"; + case 2: + return "Browser"; + default: + return "Unknown type " + type; + } + } + + private static String computeSignature(Method m) { + StringBuilder sb = new StringBuilder(); + appendType(sb, m.getReturnType()); + for (Class c : m.getParameterTypes()) { + appendType(sb, c); + } + return sb.toString(); + } + + private static void appendType(StringBuilder sb, Class t) { + if (t == null) { + sb.append('V'); + return; + } + if (t.isPrimitive()) { + int ch = -1; + if (t == int.class) { + ch = 'I'; + } + if (t == short.class) { + ch = 'S'; + } + if (t == byte.class) { + ch = 'B'; + } + if (t == boolean.class) { + ch = 'Z'; + } + if (t == long.class) { + ch = 'J'; + } + if (t == float.class) { + ch = 'F'; + } + if (t == double.class) { + ch = 'D'; + } + assert ch != -1 : "Unknown primitive type " + t; + sb.append((char) ch); + return; + } + if (t.isArray()) { + sb.append("_3"); + appendType(sb, t.getComponentType()); + return; + } + sb.append('L'); + sb.append(t.getName().replace('.', '_')); + sb.append("_2"); + } + +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,114 @@ +/** + * 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.vmtest.impl; + +import org.apidesign.bck2brwsr.vmtest.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import org.testng.Assert; +import org.testng.ITest; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +/** A TestNG {@link Factory} that seeks for {@link Compare} annotations + * in provided class and builds set of tests that compare the computations + * in real as well as JavaScript virtual machines. Use as:
+ * {@code @}{@link Factory} public static create() {
+ *   return @{link VMTest}.{@link #create(YourClass.class);
+ * }
+ * + * @author Jaroslav Tulach + */ +public final class CompareCase implements ITest { + private final Bck2BrwsrCase first, second; + private final Method m; + + private CompareCase(Method m, Bck2BrwsrCase first, Bck2BrwsrCase second) { + this.first = first; + this.second = second; + this.m = m; + } + + /** Inspects clazz and for each {@lik Compare} method creates + * instances of tests. Each instance runs the test in different virtual + * machine and at the end they compare the results. + * + * @param clazz the class to inspect + * @return the set of created tests + */ + public static Object[] create(Class clazz) { + Method[] arr = clazz.getMethods(); + List ret = new ArrayList<>(); + + final Launchers l = Launchers.INSTANCE; + + ret.add(l); + + for (Method m : arr) { + Compare c = m.getAnnotation(Compare.class); + if (c == null) { + continue; + } + final Bck2BrwsrCase real = new Bck2BrwsrCase(m, 0, null); + final Bck2BrwsrCase js = new Bck2BrwsrCase(m, 1, l); + final Bck2BrwsrCase brwsr = new Bck2BrwsrCase(m, 2, l); + + ret.add(real); + ret.add(js); + ret.add(brwsr); + + ret.add(new CompareCase(m, real, js)); + ret.add(new CompareCase(m, real, brwsr)); + } + return ret.toArray(); + } + + /** Test that compares the previous results. + * @throws Throwable + */ + @Test(dependsOnGroups = "run") public void compareResults() throws Throwable { + Object v1 = first.value; + Object v2 = second.value; + if (v1 != null) { + v1 = v1.toString(); + } else { + v1 = "null"; + } + Assert.assertEquals(v2, v1, "Comparing results"); + } + + /** Test name. + * @return name of the tested method followed by a suffix + */ + @Override + public String getTestName() { + return m.getName() + "[Compare " + second.typeName() + "]"; + } + + static StringBuilder dumpJS(CharSequence sb) throws IOException { + File f = File.createTempFile("execution", ".js"); + try (FileWriter w = new FileWriter(f)) { + w.append(sb); + } + return new StringBuilder(f.getPath()); + } +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Launchers.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Launchers.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,67 @@ +/** + * 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.vmtest.impl; + +import java.io.IOException; +import org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher; +import org.apidesign.bck2brwsr.launcher.JSLauncher; +import org.apidesign.bck2brwsr.launcher.MethodInvocation; +import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.BeforeSuite; + +/** + * + * @author Jaroslav Tulach + */ +public final class Launchers { + public static final Launchers INSTANCE = new Launchers(); + + private JSLauncher jsl; + private Bck2BrwsrLauncher brwsr; + + private Launchers() { + } + + @BeforeGroups("run") + public void initializeLauncher() throws IOException { + jsl = new JSLauncher(); + jsl.addClassLoader(Bck2Brwsr.class.getClassLoader()); + jsl.initialize(); + Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(); + l.addClassLoader(Bck2Brwsr.class.getClassLoader()); + l.initialize(); + l.setTimeout(180000); + brwsr = l; + } + + @AfterGroups("run") + public void shutDownLauncher() throws IOException, InterruptedException { + brwsr.shutdown(); + } + + public MethodInvocation addMethod(Class clazz, String name, boolean inBrwsr) throws IOException { + if (!inBrwsr) { + return jsl.addMethod(clazz, name); + } else { + return brwsr.addMethod(clazz, name); + } + } +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,46 @@ +/** + * 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.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareHashTest { + @Compare public int hashOfString() { + return "Ahoj".hashCode(); + } + + @Compare public int hashRemainsYieldsZero() { + Object o = new Object(); + return o.hashCode() - o.hashCode(); + } + + @Compare public int initializeInStatic() { + return StaticUse.NON_NULL.hashCode() - StaticUse.NON_NULL.hashCode(); + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareHashTest.class); + } +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,110 @@ +/** + * 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.tck; + +import java.net.MalformedURLException; +import java.net.URL; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareStringsTest { + @Compare public static Object compareURLs() throws MalformedURLException { + return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); + } + + @Compare public String deleteLastTwoCharacters() { + StringBuilder sb = new StringBuilder(); + sb.append("453.0"); + if (sb.toString().endsWith(".0")) { + final int l = sb.length(); + sb.delete(l - 2, l); + } + return sb.toString().toString(); + } + + @Compare public String nameOfStringClass() throws Exception { + return Class.forName("java.lang.String").getName(); + } + @Compare public String nameOfArrayClass() throws Exception { + return Class.forName("org.apidesign.bck2brwsr.tck.CompareHashTest").getName(); + } + + @Compare public String lowerHello() { + return "HeLlO".toLowerCase(); + } + + @Compare public String lowerA() { + return String.valueOf(Character.toLowerCase('A')).toString(); + } + @Compare public String upperHello() { + return "hello".toUpperCase(); + } + + @Compare public String upperA() { + return String.valueOf(Character.toUpperCase('a')).toString(); + } + + @Compare public boolean matchRegExp() throws Exception { + return "58038503".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExp() throws Exception { + return "58038503GH".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExpFully() throws Exception { + return "Hello".matches("Hell"); + } + + @Compare public String variousCharacterTests() throws Exception { + StringBuilder sb = new StringBuilder(); + + sb.append(Character.isUpperCase('a')); + sb.append(Character.isUpperCase('A')); + sb.append(Character.isLowerCase('a')); + sb.append(Character.isLowerCase('A')); + + sb.append(Character.isLetter('A')); + sb.append(Character.isLetterOrDigit('9')); + sb.append(Character.isLetterOrDigit('A')); + sb.append(Character.isLetter('0')); + + return sb.toString().toString(); + } + + @Compare + public String nullFieldInitialized() { + NullField nf = new NullField(); + return ("" + nf.name).toString(); + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareStringsTest.class); + } + + private static final class NullField { + + String name; + } +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,43 @@ +/** + * 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.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ReflectionTest { + @Compare public String intType() { + return Integer.TYPE.toString(); + } + + @Compare public String longClass() { + return long.class.toString(); + } + + + @Factory + public static Object[] create() { + return VMTest.create(ReflectionTest.class); + } + +} diff -r 52386a70f9d9 -r e5b4bd29f268 vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java Mon Dec 24 07:57:33 2012 +0100 @@ -0,0 +1,22 @@ +/** + * 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.tck; + +class StaticUse { + public static final Object NON_NULL = new Object(); +}