# HG changeset patch # User Jaroslav Tulach # Date 1399391207 -7200 # Node ID ae8575329e1bbe91853523cf6b93f8c0d0c26165 # Parent 5171ac3b4232fecda835dfcecb8a1420438cab4f# Parent dfddf572c7ec72439f30f18f154e8b8158b44d44 Merging the closure branch as it seems to be in a state when it is ready to work and not break anything diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Wed Apr 30 09:26:28 2014 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Tue May 06 17:46:47 2014 +0200 @@ -60,6 +60,7 @@ import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.StandardLocation; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.bck2brwsr.htmlpage.api.ComputedProperty; import org.apidesign.bck2brwsr.htmlpage.api.Model; import org.apidesign.bck2brwsr.htmlpage.api.On; @@ -75,6 +76,7 @@ * * @author Jaroslav Tulach */ +@ExtraJavaScript(processByteCode = false, resource = "") @ServiceProvider(service=Processor.class) @SupportedAnnotationTypes({ "org.apidesign.bck2brwsr.htmlpage.api.Model", @@ -193,6 +195,7 @@ w.append("package " + pkg + ";\n"); w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n"); + w.append("import org.apidesign.bck2brwsr.core.Exported;\n"); w.append("import org.apidesign.bck2brwsr.core.JavaScriptOnly;\n"); w.append("final class ").append(className).append(" implements Cloneable {\n"); w.append(" private boolean locked;\n"); @@ -344,6 +347,7 @@ w.append("package " + pkg + ";\n"); w.append("import org.apidesign.bck2brwsr.htmlpage.api.*;\n"); w.append("import org.apidesign.bck2brwsr.htmlpage.KOList;\n"); + w.append("import org.apidesign.bck2brwsr.core.Exported;\n"); w.append("final class ").append(className).append(" {\n"); w.append(" private boolean locked;\n"); if (!initializeOnClick(className, (TypeElement) e, w, pp)) { @@ -555,6 +559,7 @@ } w.write(";\n"); + w.write("@Exported\n"); w.write("public java.util.List<" + tn + "> " + gs[0] + "() {\n"); w.write(" if (locked) throw new IllegalStateException();\n"); w.write(" prop_" + p.name() + ".assign(ko);\n"); @@ -562,10 +567,12 @@ w.write("}\n"); } else { w.write("private " + tn + " prop_" + p.name() + ";\n"); + w.write("@Exported\n"); w.write("public " + tn + " " + gs[0] + "() {\n"); w.write(" if (locked) throw new IllegalStateException();\n"); w.write(" return prop_" + p.name() + ";\n"); w.write("}\n"); + w.write("@Exported\n"); w.write("public void " + gs[1] + "(" + tn + " v) {\n"); w.write(" if (locked) throw new IllegalStateException();\n"); w.write(" prop_" + p.name() + " = v;\n"); @@ -621,6 +628,7 @@ final String sn = ee.getSimpleName().toString(); String[] gs = toGetSet(sn, tn, array); + w.write("@Exported\n"); w.write("public " + tn + " " + gs[0] + "() {\n"); w.write(" if (locked) throw new IllegalStateException();\n"); int arg = 0; @@ -796,6 +804,7 @@ return false; } String n = e.getSimpleName().toString(); + body.append("@Exported\n"); body.append("private void ").append(n).append("(Object data, Object ev) {\n"); body.append(" ").append(clazz.getSimpleName()).append(".").append(n).append("("); body.append(wrapParams(e, null, className, "ev", "data")); diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java Wed Apr 30 09:26:28 2014 +0200 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/ProcessPage.java Tue May 06 17:46:47 2014 +0200 @@ -25,11 +25,13 @@ import java.util.TreeMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +@ExtraJavaScript(processByteCode = false, resource = "") class ProcessPage { private final Map ids2Elems = new TreeMap(); diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package org.apidesign.bck2brwsr.htmlpage.api; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package org.apidesign.bck2brwsr.htmlpage; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java Wed Apr 30 09:26:28 2014 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/Compile.java Tue May 06 17:46:47 2014 +0200 @@ -42,11 +42,13 @@ import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; /** * * @author Jaroslav Tulach */ +@ExtraJavaScript(processByteCode = false, resource = "") final class Compile implements DiagnosticListener { private final List> errors = new ArrayList<>(); private final Map classes; @@ -89,23 +91,8 @@ final Map class2BAOS = new HashMap<>(); - JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return code; - } - }; - final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return html; - } - - @Override - public InputStream openInputStream() throws IOException { - return new ByteArrayInputStream(html.getBytes()); - } - }; + JavaFileObject file = new Mem(URI.create("mem://mem"), Kind.SOURCE, code); + final JavaFileObject htmlFile = new Mem2(URI.create("mem://mem2"), Kind.OTHER, html); final URI scratch; try { @@ -114,52 +101,7 @@ throw new IOException(ex); } - JavaFileManager jfm = new ForwardingJavaFileManager(sjfm) { - @Override - public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { - if (kind == Kind.CLASS) { - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - class2BAOS.put(className.replace('.', '/') + ".class", buffer); - return new SimpleJavaFileObject(sibling.toUri(), kind) { - @Override - public OutputStream openOutputStream() throws IOException { - return buffer; - } - }; - } - - if (kind == Kind.SOURCE) { - return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) { - private final ByteArrayOutputStream data = new ByteArrayOutputStream(); - @Override - public OutputStream openOutputStream() throws IOException { - return data; - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - data.close(); - return new String(data.toByteArray()); - } - }; - } - - throw new IllegalStateException(); - } - - @Override - public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { - if (location == StandardLocation.SOURCE_PATH) { - if (packageName.equals(pkg)) { - return htmlFile; - } - } - - return null; - } - - }; + JavaFileManager jfm = new ForwardingJavaFileManagerImpl(sjfm, class2BAOS, scratch, htmlFile); ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call(); @@ -200,4 +142,117 @@ String fqn = "'" + pkg + '.' + cls + "'"; return html.replace("'${fqn}'", fqn); } + + @ExtraJavaScript(processByteCode = false, resource = "") + private class ForwardingJavaFileManagerImpl extends ForwardingJavaFileManager { + + private final Map class2BAOS; + private final URI scratch; + private final JavaFileObject htmlFile; + + public ForwardingJavaFileManagerImpl(JavaFileManager fileManager, Map class2BAOS, URI scratch, JavaFileObject htmlFile) { + super(fileManager); + this.class2BAOS = class2BAOS; + this.scratch = scratch; + this.htmlFile = htmlFile; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { + if (kind == Kind.CLASS) { + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + class2BAOS.put(className.replace('.', '/') + ".class", buffer); + return new Sibling(sibling.toUri(), kind, buffer); + } + + if (kind == Kind.SOURCE) { + return new Source(scratch/*sibling.toUri()*/, kind); + } + + throw new IllegalStateException(); + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + if (location == StandardLocation.SOURCE_PATH) { + if (packageName.equals(pkg)) { + return htmlFile; + } + } + + return null; + } + + @ExtraJavaScript(processByteCode = false, resource = "") + private class Sibling extends SimpleJavaFileObject { + private final ByteArrayOutputStream buffer; + + public Sibling(URI uri, Kind kind, ByteArrayOutputStream buffer) { + super(uri, kind); + this.buffer = buffer; + } + + @Override + public OutputStream openOutputStream() throws IOException { + return buffer; + } + } + + @ExtraJavaScript(processByteCode = false, resource = "") + private class Source extends SimpleJavaFileObject { + public Source(URI uri, Kind kind) { + super(uri, kind); + } + private final ByteArrayOutputStream data = new ByteArrayOutputStream(); + + @Override + public OutputStream openOutputStream() throws IOException { + return data; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + data.close(); + return new String(data.toByteArray()); + } + } + } + + @ExtraJavaScript(processByteCode = false, resource = "") + private static class Mem extends SimpleJavaFileObject { + + private final String code; + + public Mem(URI uri, Kind kind, String code) { + super(uri, kind); + this.code = code; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return code; + } + } + + @ExtraJavaScript(processByteCode = false, resource = "") + private static class Mem2 extends SimpleJavaFileObject { + + private final String html; + + public Mem2(URI uri, Kind kind, String html) { + super(uri, kind); + this.html = html; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return html; + } + + @Override + public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(html.getBytes()); + } + } } diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Wed Apr 30 09:26:28 2014 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageTest.java Tue May 06 17:46:47 2014 +0200 @@ -21,6 +21,7 @@ import java.util.Locale; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; import static org.testng.Assert.*; import org.testng.annotations.Test; @@ -28,6 +29,7 @@ * * @author Jaroslav Tulach */ +@ExtraJavaScript(processByteCode = false, resource = "") public class PageTest { @Test public void verifyWrongType() throws IOException { String html = "" diff -r 5171ac3b4232 -r ae8575329e1b javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Wed Apr 30 09:26:28 2014 +0200 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Tue May 06 17:46:47 2014 +0200 @@ -24,10 +24,12 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.vm4brwsr.Bck2Brwsr; import org.testng.annotations.Test; import static org.testng.Assert.*; +@ExtraJavaScript(processByteCode = false, resource = "") public class ProcessPageTest { diff -r 5171ac3b4232 -r ae8575329e1b javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/demo/calc/staticcompilation/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package org.apidesign.bck2brwsr.demo.calc.staticcompilation; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b launcher/fx/pom.xml --- a/launcher/fx/pom.xml Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/fx/pom.xml Tue May 06 17:46:47 2014 +0200 @@ -58,11 +58,13 @@ testng test + ${project.groupId} core diff -r 5171ac3b4232 -r ae8575329e1b launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Tue May 06 17:46:47 2014 +0200 @@ -28,12 +28,14 @@ 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; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -42,6 +44,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; @@ -59,7 +62,6 @@ import org.glassfish.grizzly.websockets.WebSocketAddOn; import org.glassfish.grizzly.websockets.WebSocketApplication; import org.glassfish.grizzly.websockets.WebSocketEngine; -import org.openide.util.Exceptions; /** * Lightweight server to launch Bck2Brwsr applications and tests. @@ -176,7 +178,7 @@ nl.getTransport().setWorkerThreadPoolConfig(fewThreads); nl.getTransport().setKernelThreadPoolConfig(oneKernel); } - */ +*/ final ServerConfiguration conf = s.getServerConfiguration(); VMAndPages vm = new VMAndPages(); conf.addHttpHandler(vm, "/"); @@ -560,6 +562,12 @@ abstract void generateBck2BrwsrJS(StringBuilder sb, Res loader) throws IOException; abstract String harnessResource(); + String compileJar(JarFile jar) throws IOException { + return null; + } + String compileFromClassPath(URL f, Res loader) throws IOException { + return null; + } private static URI pageURL(String protocol, HttpServer server, final String page) { NetworkListener listener = server.getListeners().iterator().next(); @@ -571,8 +579,18 @@ } } - class Res { - public InputStream get(String resource, int skip) throws IOException { + final class Res { + private final Set ignore = new HashSet(); + + String compileJar(JarFile jar, URL jarURL) throws IOException { + String ret = BaseHTTPLauncher.this.compileJar(jar); + ignore.add(jarURL); + return ret; + } + String compileFromClassPath(URL f) throws IOException { + return BaseHTTPLauncher.this.compileFromClassPath(f, this); + } + public URL get(String resource, int skip) throws IOException { if (!resource.endsWith(".class")) { return getResource(resource, skip); } @@ -582,7 +600,7 @@ while (en.hasMoreElements()) { u = en.nextElement(); if (u.toExternalForm().matches("^.*emul.*rt\\.jar.*$")) { - return u.openStream(); + return u; } } } @@ -591,11 +609,11 @@ LOG.log(Level.WARNING, "No fallback to bootclasspath for {0}", u); return null; } - return u.openStream(); + return u; } throw new IOException("Can't find " + resource); } - private InputStream getResource(String resource, int skip) throws IOException { + private URL getResource(String resource, int skip) throws IOException { for (ClassLoader l : loaders) { Enumeration en = l.getResources(resource); while (en.hasMoreElements()) { @@ -605,8 +623,14 @@ // module is not compiled with target 1.6, currently continue; } + if (now.getProtocol().equals("jar")) { + JarURLConnection juc = (JarURLConnection) now.openConnection(); + if (ignore.contains(juc.getJarFileURL())) { + continue; + } + } if (--skip < 0) { - return now.openStream(); + return now; } } } @@ -644,7 +668,7 @@ } OutputStream os = response.getOutputStream(); try { - InputStream is = res.get(r, 0); + InputStream is = res.get(r, 0).openStream(); copyStream(is, os, request.getRequestURL().toString(), replace); } catch (IOException ex) { response.setDetailMessage(ex.getLocalizedMessage()); @@ -714,14 +738,42 @@ if (res.startsWith("/")) { res = res.substring(1); } + String skip = request.getParameter("skip"); + int skipCnt = skip == null ? 0 : Integer.parseInt(skip); + URL url = loader.get(res, skipCnt); + if (url != null && !res.equals("META-INF/MANIFEST.MF")) try { + response.setCharacterEncoding("UTF-8"); + if (url.getProtocol().equals("jar")) { + JarURLConnection juc = (JarURLConnection) url.openConnection(); + String s = loader.compileJar(juc.getJarFile(), juc.getJarFileURL()); + 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; + } + } + } catch (IOException ex) { + LOG.log(Level.SEVERE, "Cannot handle " + res, ex); + } InputStream is = null; try { - String skip = request.getParameter("skip"); - int skipCnt = skip == null ? 0 : Integer.parseInt(skip); - is = loader.get(res, skipCnt); + if (url == null) { + throw new IOException("Resource not found"); + } + 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) { @@ -738,7 +790,7 @@ } w.append(Integer.toString(b)); } - w.append("\n]"); + w.append("\n])"); } catch (IOException ex) { response.setStatus(HttpStatus.NOT_FOUND_404); response.setError(); @@ -749,6 +801,7 @@ } } } + } private static class WS extends WebSocketApplication { @@ -767,7 +820,7 @@ String s = new String(out.toByteArray(), "UTF-8"); socket.send(s); } catch (IOException ex) { - Exceptions.printStackTrace(ex); + LOG.log(Level.WARNING, null, ex); } } diff -r 5171ac3b4232 -r ae8575329e1b launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Tue May 06 17:46:47 2014 +0200 @@ -37,7 +37,6 @@ import java.util.logging.Logger; import javafx.application.Platform; import org.apidesign.bck2brwsr.launcher.fximpl.JVMBridge; -import org.openide.util.Exceptions; /** * diff -r 5171ac3b4232 -r ae8575329e1b launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXInspect.java --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXInspect.java Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXInspect.java Tue May 06 17:46:47 2014 +0200 @@ -29,7 +29,6 @@ import javafx.application.Platform; import javafx.scene.web.WebEngine; import javafx.util.Callback; -import org.openide.util.Exceptions; /** * diff -r 5171ac3b4232 -r ae8575329e1b launcher/http/pom.xml --- a/launcher/http/pom.xml Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/http/pom.xml Tue May 06 17:46:47 2014 +0200 @@ -56,5 +56,16 @@ vm4brwsr ${project.version} + + org.testng + testng + test + + + ${project.groupId} + emul.mini + ${project.version} + test + diff -r 5171ac3b4232 -r ae8575329e1b launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Tue May 06 17:46:47 2014 +0200 @@ -25,8 +25,10 @@ import java.io.Reader; import java.net.MalformedURLException; import java.net.URL; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarFile; import java.util.logging.Level; -import org.apidesign.vm4brwsr.Bck2Brwsr; /** * Lightweight server to launch Bck2Brwsr applications and tests. @@ -34,6 +36,7 @@ * execution engine. */ final class Bck2BrwsrLauncher extends BaseHTTPLauncher { + private Set testClasses = new HashSet(); public Bck2BrwsrLauncher(String cmd) { super(cmd); @@ -43,15 +46,24 @@ String harnessResource() { return "org/apidesign/bck2brwsr/launcher/harness.xhtml"; } + + @Override + String compileJar(JarFile jar) throws IOException { + return CompileCP.compileJAR(jar, testClasses); + } + + @Override + public InvocationContext createInvocation(Class clazz, String method) { + testClasses.add(clazz.getName().replace('.', '/')); + return super.createInvocation(clazz, method); + } + + @Override String compileFromClassPath(URL f, Res loader) throws IOException { + return CompileCP.compileFromClassPath(f, loader); + } @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, 0); - } - } String b2b = System.getProperty("bck2brwsr.js"); if (b2b != null) { LOG.log(Level.INFO, "Serving bck2brwsr.js from {0}", b2b); @@ -78,16 +90,17 @@ } } else { LOG.log(Level.INFO, "Generating bck2brwsr.js from scratch", b2b); - Bck2Brwsr.generate(sb, new R()); + CompileCP.compileVM(sb, loader); } sb.append( "(function WrapperVM(global) {\n" + " var cache = {};\n" + + " var empty = {};\n" + " function ldCls(res, skip) {\n" + " var c = cache[res];\n" + " if (c) {\n" + + " if (c[skip] === empty) return null;\n" + " if (c[skip]) return c[skip];\n" - + " if (c[skip] === null) return null;\n" + " } else {\n" + " cache[res] = c = new Array();\n" + " }\n" @@ -98,8 +111,9 @@ + " c[skip] = null;\n" + " return null;\n" + " }\n" - + " var arr = eval('(' + request.responseText + ')');\n" - + " c[skip] = arr;\n" + + " var arr = eval(request.responseText);\n" + + " if (arr === null) c[skip] = empty;\n" + + " else c[skip] = arr;\n" + " return arr;\n" + " }\n" + " var prevvm = global.bck2brwsr;\n" @@ -108,6 +122,7 @@ + " args.unshift(ldCls);\n" + " return prevvm.apply(null, args);\n" + " };\n" + + " global.bck2brwsr.registerExtension = prevvm.registerExtension;\n" + "})(this);\n" ); LOG.log(Level.INFO, "Serving bck2brwsr.js", b2b); diff -r 5171ac3b4232 -r ae8575329e1b 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 Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,249 @@ +/** + * 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.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.net.JarURLConnection; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import org.apidesign.bck2brwsr.launcher.BaseHTTPLauncher.Res; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** + * + * @author Jaroslav Tulach + */ +class CompileCP { + private static final Logger LOG = Logger.getLogger(CompileCP.class.getName()); + static String compileJAR(final JarFile jar, Set testClasses) + throws IOException { + List arr = new ArrayList<>(); + List classes = new ArrayList<>(); + Set exported = new HashSet(); + Set keep = new HashSet(testClasses); + listJAR(jar, classes, arr, exported, keep); + List root = new ArrayList<>(); + for (String c : classes) { + if (keep.contains(c)) { + root.add(c); + continue; + } + int slash = c.lastIndexOf('/'); + String pkg = c.substring(0, slash + 1); + if (exported.contains(pkg)) { + root.add(c); + } + } + + 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() + .addClasses(classes.toArray(new String[0])) + .addRootClasses(root.toArray(new String[0])) + .addResources(arr.toArray(new String[0])) + .library(true) + .resources(new JarRes()) + .generate(w); + w.flush(); + return w.toString(); + } catch (IOException ex) { + throw ex; + } catch (Throwable ex) { + throw new IOException("Cannot compile: ", ex); + } finally { + w.close(); + } + } + + static String compileFromClassPath(URL u, final Res r) throws IOException { + File f; + try { + f = new File(u.toURI()); + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + 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])) + .addResources(arr.toArray(new String[0])) + .library(true) + .resources(new EmulationResources() { + @Override + public InputStream get(String resource) throws IOException { + if (r != null) { + final URL url = r.get(resource, 0); + return url == null ? null : url.openStream(); + } + return super.get(resource); + } + }) + .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, Set exported, Set keep + ) throws IOException { + Enumeration en = j.entries(); + while (en.hasMoreElements()) { + JarEntry e = en.nextElement(); + final String n = e.getName(); + if (n.contains("package-info")) { + continue; + } + 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); + if (n.startsWith("META-INF/services/") && keep != null) { + BufferedReader r = new BufferedReader(new InputStreamReader(j.getInputStream(e))); + for (;;) { + String l = r.readLine(); + if (l == null) { + break; + } + if (l.startsWith("#")) { + continue; + } + keep.add(l.replace('.', '/')); + } + } + } + } + String exp = j.getManifest().getMainAttributes().getValue("Export-Package"); + if (exp != null && exported != null) { + for (String def : exp.split(",")) { + for (String sep : def.split(";")) { + exported.add(sep.replace('.', '/') + "/"); + break; + } + } + } + } + + 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().equals("package-info.class")) { + return; + } + 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 void compileVM(StringBuilder sb, final Res r) throws IOException { + URL u = r.get(InterruptedException.class.getName().replace('.', '/') + ".class", 0); + JarURLConnection juc = (JarURLConnection)u.openConnection(); + + List arr = new ArrayList<>(); + List classes = new ArrayList<>(); + listJAR(juc.getJarFile(), classes, arr, null, null); + + Bck2Brwsr.newCompiler().addRootClasses(classes.toArray(new String[0])) + .resources(new Bck2Brwsr.Resources() { + @Override + public InputStream get(String resource) throws IOException { + final URL url = r.get(resource, 0); + return url == null ? null : url.openStream(); + } + }).generate(sb); + } + + 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) { + LOG.log(Level.WARNING, "Cannot find {0}", name); + return null; + } + if (u.toExternalForm().contains("/rt.jar!")) { + LOG.warning(name + "No bootdelegation for "); + return null; + } + return u.openStream(); + } + } +} diff -r 5171ac3b4232 -r ae8575329e1b 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 Wed Apr 30 09:26:28 2014 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Tue May 06 17:46:47 2014 +0200 @@ -31,6 +31,7 @@ * * @author Jaroslav Tulach */ +@org.apidesign.bck2brwsr.core.Exported public class Console { private Console() { } @@ -265,7 +266,7 @@ return u; } - @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") + @JavaScriptBody(args = {}, body = "vm.java_lang_Class(false).desiredAssertionStatus = true;") private static void turnAssetionStatusOn() { } diff -r 5171ac3b4232 -r ae8575329e1b 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 Tue May 06 17:46:47 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, null); + assertNotNull(resources, "something compiled"); + } +} diff -r 5171ac3b4232 -r ae8575329e1b pom.xml --- a/pom.xml Wed Apr 30 09:26:28 2014 +0200 +++ b/pom.xml Tue May 06 17:46:47 2014 +0200 @@ -13,7 +13,7 @@ UTF-8 - RELEASE74 + RELEASE80 COPYING 0.7.6 none @@ -89,7 +89,7 @@ rt/emul/compact/src/main/** rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java rt/archetype/src/main/resources/archetype-resources/** - rt/emul/compact/src/test/resources/** + rt/emul/*/src/test/resources/** javaquery/api/src/main/resources/org/apidesign/bck2brwsr/htmlpage/knockout*.js ko/archetype/src/main/resources/archetype-resources/** diff -r 5171ac3b4232 -r ae8575329e1b rt/core/src/main/java/org/apidesign/bck2brwsr/core/Exported.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/core/src/main/java/org/apidesign/bck2brwsr/core/Exported.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,38 @@ +/** + * 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.core; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks the corresponding program element as exported. Exported elements are + * visible from other modules. Can be used on packages, classes, methods, + * constructors and fields. + * + * @since 0.6 + */ +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.PACKAGE, ElementType.TYPE, + ElementType.METHOD, ElementType.CONSTRUCTOR, + ElementType.FIELD }) +public @interface Exported { + +} diff -r 5171ac3b4232 -r ae8575329e1b rt/core/src/main/java/org/apidesign/bck2brwsr/core/impl/JavaScriptProcesor.java --- a/rt/core/src/main/java/org/apidesign/bck2brwsr/core/impl/JavaScriptProcesor.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/core/src/main/java/org/apidesign/bck2brwsr/core/impl/JavaScriptProcesor.java Tue May 06 17:46:47 2014 +0200 @@ -35,6 +35,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.tools.Diagnostic; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.openide.util.lookup.ServiceProvider; @@ -42,6 +43,7 @@ * * @author Jaroslav Tulach */ +@ExtraJavaScript(processByteCode = false, resource="") @ServiceProvider(service = Processor.class) public final class JavaScriptProcesor extends AbstractProcessor { @Override diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/brwsrtest/pom.xml --- a/rt/emul/brwsrtest/pom.xml Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/brwsrtest/pom.xml Tue May 06 17:46:47 2014 +0200 @@ -48,5 +48,11 @@ ${project.version} provided + + ${project.groupId} + emul + ${project.version} + test + diff -r 5171ac3b4232 -r ae8575329e1b 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 Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/brwsrtest/src/test/java/org/apidesign/bck2brwsr/brwsrtest/BooleanTest.java Tue May 06 17:46:47 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; @@ -28,6 +27,8 @@ * @author Jaroslav Tulach */ public class BooleanTest { + private static Boolean TRUE = Boolean.TRUE; + @JavaScriptBody(args = { "tr" }, body = "return tr ? true : false;") private static native Object trueFalse(boolean tr); diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/brwsrtest/src/test/java/org/apidesign/bck2brwsr/brwsrtest/ResourcesInBrwsrTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/brwsrtest/src/test/java/org/apidesign/bck2brwsr/brwsrtest/ResourcesInBrwsrTest.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,71 @@ +/** + * 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.brwsrtest; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ResourcesInBrwsrTest { + + @Compare public String readResourceAsStream() throws Exception { + InputStream is = getClass().getResourceAsStream("Resources.txt"); + return readString(is); + } + + @Compare public String readResourceViaConnection() throws Exception { + InputStream is = getClass().getResource("Resources.txt").openConnection().getInputStream(); + return readString(is); + } + + private String readString(InputStream is) throws IOException { + StringBuilder sb = new StringBuilder(); + byte[] b = new byte[512]; + for (;;) { + int len = is.read(b); + if (len == -1) { + return sb.toString(); + } + for (int i = 0; i < len; i++) { + sb.append((char)b[i]); + } + } + } + + @Compare public String readResourceAsStreamFromClassLoader() throws Exception { + InputStream is = getClass().getClassLoader().getResourceAsStream("org/apidesign/bck2brwsr/brwsrtest/Resources.txt"); + return readString(is); + } + + @Compare public String toURIFromURL() throws Exception { + URL u = new URL("http://apidesign.org"); + return u.toURI().toString(); + } + + @Factory public static Object[] create() { + return VMTest.create(ResourcesInBrwsrTest.class); + } +} diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/brwsrtest/src/test/resources/org/apidesign/bck2brwsr/brwsrtest/Resources.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/brwsrtest/src/test/resources/org/apidesign/bck2brwsr/brwsrtest/Resources.txt Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,1 @@ +Ahoj diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/compact/pom.xml --- a/rt/emul/compact/pom.xml Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/compact/pom.xml Tue May 06 17:46:47 2014 +0200 @@ -86,7 +86,25 @@ - + + + org.apache.maven.plugins + maven-surefire-plugin + + + brwsr + + test + + verify + + + brwsr + + + + + diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/compact/src/main/java/org/apidesign/bck2brwsr/emul/reflect/ProxyImpl.java --- a/rt/emul/compact/src/main/java/org/apidesign/bck2brwsr/emul/reflect/ProxyImpl.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/compact/src/main/java/org/apidesign/bck2brwsr/emul/reflect/ProxyImpl.java Tue May 06 17:46:47 2014 +0200 @@ -50,6 +50,7 @@ import java.util.WeakHashMap; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.emul.reflect.MethodImpl; +import org.apidesign.vm4brwsr.api.VM; /** * {@code Proxy} provides static methods for creating dynamic proxy @@ -677,7 +678,10 @@ } @JavaScriptBody(args = { "ignore", "name", "byteCode" }, - body = "return vm._reload(name, byteCode).constructor.$class;" + body = + "var r = vm._reload;" + + "if (!r) r = exports._reload;" + + "return r(name, byteCode).constructor.$class;" ) private static native Class defineClass0( ClassLoader loader, String name, byte[] b diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/NotifyWaitTest.java --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/NotifyWaitTest.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/NotifyWaitTest.java Tue May 06 17:46:47 2014 +0200 @@ -28,6 +28,7 @@ */ public class NotifyWaitTest { + @Compare public synchronized String canCallNotify() throws Exception { notify(); return "OK"; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/fake/src/main/java/java/io/PrintWriter.java --- a/rt/emul/fake/src/main/java/java/io/PrintWriter.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/fake/src/main/java/java/io/PrintWriter.java Tue May 06 17:46:47 2014 +0200 @@ -21,6 +21,6 @@ * @author Jaroslav Tulach */ public abstract class PrintWriter { - public abstract PrintWriter append(String s); + public abstract PrintWriter append(CharSequence s); public abstract void println(String s); } diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/io/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/io/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package java.io; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/Class.java --- a/rt/emul/mini/src/main/java/java/lang/Class.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Tue May 06 17:46:47 2014 +0200 @@ -35,6 +35,7 @@ import java.lang.reflect.TypeVariable; import java.net.URL; import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.core.JavaScriptOnly; import org.apidesign.bck2brwsr.emul.reflect.AnnotationImpl; import org.apidesign.bck2brwsr.emul.reflect.MethodImpl; @@ -150,7 +151,7 @@ public static Class forName(String className) throws ClassNotFoundException { if (className.startsWith("[")) { - Class arrType = defineArray(className); + Class arrType = defineArray(className, null); Class c = arrType; while (c != null && c.isArray()) { c = c.getComponentType0(); // verify component type is sane @@ -1543,13 +1544,24 @@ public Class getComponentType() { if (isArray()) { try { - return getComponentType0(); + Class c = getComponentTypeByFnc(); + return c != null ? c : getComponentType0(); } catch (ClassNotFoundException cnfe) { throw new IllegalStateException(cnfe); } } return null; } + + @JavaScriptBody(args = { }, body = + "if (this.fnc) {\n" + + " var c = this.fnc(false).constructor.$class;\n" +// + " java.lang.System.out.println('will call: ' + (!!this.fnc) + ' res: ' + c.jvmName);\n" + + " if (c) return c;\n" + + "}\n" + + "return null;" + ) + private native Class getComponentTypeByFnc(); private Class getComponentType0() throws ClassNotFoundException { String n = getName().substring(1); @@ -1576,24 +1588,26 @@ case 'C': return Character.TYPE; case '[': - return defineArray(n); + return defineArray(n, null); default: throw new ClassNotFoundException("Unknown component type of " + getName()); } } - @JavaScriptBody(args = { "sig" }, body = + @JavaScriptBody(args = { "sig", "fnc" }, body = "if (!sig) sig = '[Ljava/lang/Object;';\n" + "var c = Array[sig];\n" + - "if (c) return c;\n" + - "c = vm.java_lang_Class(true);\n" + - "c.jvmName = sig;\n" + - "c.superclass = vm.java_lang_Object(false).$class;\n" + - "c.array = true;\n" + - "Array[sig] = c;\n" + + "if (!c) {\n" + + " c = vm.java_lang_Class(true);\n" + + " c.jvmName = sig;\n" + + " c.superclass = vm.java_lang_Object(false).$class;\n" + + " c.array = true;\n" + + " Array[sig] = c;\n" + + "}\n" + + "if (!c.fnc) c.fnc = fnc;\n" + "return c;" ) - private static native Class defineArray(String sig); + private static native Class defineArray(String sig, Object fnc); /** * Returns true if and only if this class was declared as an enum in the @@ -1725,7 +1739,7 @@ native static Class getPrimitiveClass(String type); @JavaScriptBody(args = {}, body = - "return vm.desiredAssertionStatus ? vm.desiredAssertionStatus : false;" + "return this.desiredAssertionStatus ? this.desiredAssertionStatus : false;" ) public native boolean desiredAssertionStatus(); @@ -1760,7 +1774,7 @@ static native int defaultHashCode(Object self); @JavaScriptBody(args = "self", body - = "\nif (!self.$instOf_java_lang_Cloneable) {" + = "\nif (!self['$instOf_java_lang_Cloneable']) {" + "\n return null;" + "\n} else {" + "\n var clone = self.constructor(true);" @@ -1773,5 +1787,39 @@ + "\n}" ) static native Object clone(Object self) throws CloneNotSupportedException; + + @JavaScriptOnly(name = "toJS", value = "function(v) {\n" + + " if (v === null) return null;\n" + + " if (Object.prototype.toString.call(v) === '[object Array]') {\n" + + " return vm.org_apidesign_bck2brwsr_emul_lang_System(false).convArray__Ljava_lang_Object_2Ljava_lang_Object_2(v);\n" + + " }\n" + + " return v.valueOf();\n" + + "}\n" + ) + static native int toJS(); + + @JavaScriptOnly(name = "activate__Ljava_io_Closeable_2Lorg_apidesign_html_boot_spi_Fn$Presenter_2", value = "function() {\n" + + " return vm.org_apidesign_bck2brwsr_emul_lang_System(false).activate__Ljava_io_Closeable_2();" + + "}\n" + ) + static native int activate(); + + private static Object bck2BrwsrCnvrt(Object o) { + if (o instanceof Throwable) { + return o; + } + final String msg = msg(o); + if (msg == null || msg.startsWith("TypeError")) { + return new NullPointerException(msg); + } + return new Throwable(msg); + } + + @JavaScriptBody(args = {"o"}, body = "return o ? o.toString() : null;") + private static native String msg(Object o); + + @JavaScriptOnly(name = "bck2BrwsrThrwrbl", value = "c.bck2BrwsrCnvrt__Ljava_lang_Object_2Ljava_lang_Object_2") + private static void bck2BrwsrCnvrtVM() { + } } diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/ClassLoader.java --- a/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Tue May 06 17:46:47 2014 +0200 @@ -588,6 +588,9 @@ * @since 1.2 */ public Enumeration getResources(String name) throws IOException { + if (this == SYSTEM) { + return findResources(name); + } Enumeration[] tmp = new Enumeration[2]; if (parent != null) { tmp[0] = parent.getResources(name); diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/String.java --- a/rt/emul/mini/src/main/java/java/lang/String.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/String.java Tue May 06 17:46:47 2014 +0200 @@ -1016,7 +1016,7 @@ * @see #equalsIgnoreCase(String) */ @JavaScriptBody(args = { "obj" }, body = - "return obj != null && obj.$instOf_java_lang_String && " + "return obj != null && obj['$instOf_java_lang_String'] && " + "this.toString() === obj.toString();" ) public boolean equals(Object anObject) { diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/Throwable.java --- a/rt/emul/mini/src/main/java/java/lang/Throwable.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Throwable.java Tue May 06 17:46:47 2014 +0200 @@ -1025,22 +1025,4 @@ // else // return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); } - - private static Object bck2BrwsrCnvrt(Object o) { - if (o instanceof Throwable) { - return o; - } - final String msg = msg(o); - if (msg == null || msg.startsWith("TypeError")) { - return new NullPointerException(msg); - } - return new Throwable(msg); - } - - @JavaScriptBody(args = { "o" }, body = "return o ? o.toString() : null;") - private static native String msg(Object o); - - @JavaScriptOnly(name = "bck2BrwsrCnvrt", value = "c.bck2BrwsrCnvrt__Ljava_lang_Object_2Ljava_lang_Object_2") - private static void bck2BrwsrCnvrtVM() { - } } diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/annotation/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/lang/annotation/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package java.lang.annotation; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/lang/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package java.lang; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/reflect/Array.java --- a/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/Array.java Tue May 06 17:46:47 2014 +0200 @@ -25,6 +25,7 @@ package java.lang.reflect; +import org.apidesign.bck2brwsr.core.Exported; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.core.JavaScriptPrototype; @@ -75,7 +76,7 @@ throw new NegativeArraySizeException(); } String sig = findSignature(componentType); - return newArray(componentType.isPrimitive(), sig, length); + return newArray(componentType.isPrimitive(), sig, null, length); } private static String findSignature(Class type) { @@ -629,25 +630,67 @@ * Private */ - @JavaScriptBody(args = { "primitive", "sig", "length" }, body = + @JavaScriptBody(args = { "primitive", "sig", "fn", "length" }, body = "var arr = new Array(length);\n" + "var value = primitive ? 0 : null;\n" + "for(var i = 0; i < length; i++) arr[i] = value;\n" + "arr.jvmName = sig;\n" + + "arr.fnc = fn;\n" +// + "java.lang.System.out.println('Assigned ' + arr.jvmName + ' fn: ' + (!!arr.fnc));\n" + "return arr;" ) - static native Object newArray(boolean primitive, String sig, int length); + @Exported + private static native Object newArray(boolean primitive, String sig, Object fn, int length); - static Object multiNewArray(String sig, int[] dims, int index) + @Exported + private static boolean isInstance(Object arr, String sig) { + if (arr == null) { + return false; + } + return sig.equals(arr.getClass().getName()); + } + + @Exported + private static boolean isInstance(Object arr, int dimensions, Object fn) throws ClassNotFoundException { + if (arr == null) { + return false; + } +// log("isInstance for " + arr + " and " + dimensions); + Class c = arr.getClass(); + while (dimensions-- > 0) { +// log(" class: " + c); + c = c.getComponentType(); +// log(" next class: " + c); + if (c == null) { + return false; + } + } + Class t = classFromFn(fn); +// log(" to check: " + t); + return t.isAssignableFrom(c); + } + + @JavaScriptBody(args = { "cntstr" }, body = "return cntstr(false).constructor.$class;") + private static native Class classFromFn(Object cntstr); + +// @JavaScriptBody(args = { "m" }, body = "java.lang.System.out.println(m.toString().toString());") +// private static native void log(Object m); + + @Exported + private static Object multiNewArray(String sig, int[] dims, Object fn) + throws IllegalArgumentException, NegativeArraySizeException { + return multiNewArray(sig, dims, 0, fn); + } + private static Object multiNewArray(String sig, int[] dims, int index, Object fn) throws IllegalArgumentException, NegativeArraySizeException { if (dims.length == index + 1) { - return newArray(sig.length() == 2, sig, dims[index]); + return newArray(sig.length() == 2, sig, fn, dims[index]); } - Object arr = newArray(false, sig, dims[index]); + Object arr = newArray(false, sig, null, dims[index]); String compsig = sig.substring(1); int len = getLength(arr); for (int i = 0; i < len; i++) { - setArray(arr, i, multiNewArray(compsig, dims, index + 1)); + setArray(arr, i, multiNewArray(compsig, dims, index + 1, fn)); } return arr; } diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/lang/reflect/package-info.java --- a/rt/emul/mini/src/main/java/java/lang/reflect/package-info.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/reflect/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -46,4 +46,7 @@ * * @since JDK1.1 */ +@Exported package java.lang.reflect; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/net/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/net/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package java.net; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/util/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/util/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package java.util; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/java/util/zip/package-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/util/zip/package-info.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,21 @@ +/** + * 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. + */ +@Exported +package java.util.zip; + +import org.apidesign.bck2brwsr.core.Exported; diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/lang/System.java Tue May 06 17:46:47 2014 +0200 @@ -17,9 +17,10 @@ */ package org.apidesign.bck2brwsr.emul.lang; +import java.io.Closeable; +import java.io.IOException; import java.lang.reflect.Method; import org.apidesign.bck2brwsr.core.JavaScriptBody; -import org.apidesign.bck2brwsr.core.JavaScriptOnly; /** * @@ -73,15 +74,15 @@ @JavaScriptBody(args = { "obj" }, body="return vm.java_lang_Object(false).hashCode__I.call(obj);") public static native int identityHashCode(Object obj); - @JavaScriptOnly(name = "toJS", value = "function(v) {\n" + - " if (v === null) return null;\n" + - " if (Object.prototype.toString.call(v) === '[object Array]') {\n" + - " return vm.org_apidesign_bck2brwsr_emul_lang_System(false).convArray__Ljava_lang_Object_2Ljava_lang_Object_2(v);\n" + - " }\n" + - " return v.valueOf();\n" + - "}\n" - ) - public static native int toJS(); + public static Closeable activate() { + return DUMMY; + } + private static final Closeable DUMMY = new Closeable() { + @Override + public void close() throws IOException { + } + }; + private static Object convArray(Object o) { if (o instanceof Object[]) { diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/java/org/apidesign/vm4brwsr/api/VM.java --- a/rt/emul/mini/src/main/java/org/apidesign/vm4brwsr/api/VM.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/java/org/apidesign/vm4brwsr/api/VM.java Tue May 06 17:46:47 2014 +0200 @@ -40,7 +40,11 @@ reloadImpl(clazz.getName(), byteCode); } - @JavaScriptBody(args = { "name", "byteCode" }, body = "vm._reload(name, byteCode);") + @JavaScriptBody(args = { "name", "byteCode" }, body = + "var r = vm._reload;" + + "if (!r) r = exports._reload;" + + "r(name, byteCode);" + ) private static void reloadImpl(String name, byte[] byteCode) throws IOException { throw new IOException(); } diff -r 5171ac3b4232 -r ae8575329e1b rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js --- a/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js Tue May 06 17:46:47 2014 +0200 @@ -14,7 +14,7 @@ return arr[indx]; }; Array.prototype.getClass__Ljava_lang_Class_2 = function() { - return vm.java_lang_Class(false).defineArray__Ljava_lang_Class_2Ljava_lang_String_2(this.jvmName); + return vm.java_lang_Class(false).defineArray__Ljava_lang_Class_2Ljava_lang_String_2Ljava_lang_Object_2(this.jvmName, this.fnc); }; Array.prototype.clone__Ljava_lang_Object_2 = function() { var s = this.length; @@ -23,5 +23,6 @@ ret[i] = this[i]; } ret.jvmName = this.jvmName; + ret.fnc = this.fnc; return ret; }; diff -r 5171ac3b4232 -r ae8575329e1b rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java --- a/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Java2JavaScript.java Tue May 06 17:46:47 2014 +0200 @@ -75,6 +75,15 @@ @Parameter(defaultValue = "false") private boolean ignoreBootClassPath; + /** + * Indicates whether to create an extension library + * module instead of a standalone JavaScript VM. + * + * @since 0.9 + */ + @Parameter(defaultValue="false") + private boolean library; + @Override public void execute() throws MojoExecutionException { if (!classes.isDirectory()) { @@ -101,6 +110,7 @@ FileWriter w = new FileWriter(javascript); Bck2Brwsr.newCompiler(). obfuscation(obfuscation). + library(library). resources(url, ignoreBootClassPath). addRootClasses(arr.toArray(new String[0])). generate(w); @@ -123,7 +133,9 @@ return newest; } else if (toCheck.getName().endsWith(".class")) { final String cls = prefix.substring(0, prefix.length() - 7); - arr.add(cls); + if (!cls.endsWith("package-info")) { + arr.add(cls); + } return toCheck.lastModified(); } else { return 0L; diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/pom.xml --- a/rt/vm/pom.xml Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/pom.xml Tue May 06 17:46:47 2014 +0200 @@ -159,5 +159,11 @@ test ${net.java.html.version} + + ${project.groupId} + fake + ${project.version} + test + diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Tue May 06 17:46:47 2014 +0200 @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; +import org.apidesign.bck2brwsr.core.Exported; /** Build your own virtual machine! Use methods in this class to generate * a skeleton JVM in JavaScript that contains pre-compiled classes of your @@ -53,13 +54,19 @@ */ public final class Bck2Brwsr { private final ObfuscationLevel level; + private final StringArray rootcls; private final StringArray classes; + private final StringArray resources; private final Resources res; + private final boolean extension; - private Bck2Brwsr(ObfuscationLevel level, StringArray classes, Resources resources) { + private Bck2Brwsr(ObfuscationLevel level, StringArray rootcls, StringArray classes, StringArray resources, Resources res, boolean extension) { this.level = level; + this.rootcls = rootcls; this.classes = classes; - this.res = resources; + this.resources = resources; + this.res = res; + this.extension = extension; } /** Helper method to generate virtual machine from bytes served by a resources @@ -96,23 +103,69 @@ * @since 0.5 */ public static Bck2Brwsr newCompiler() { - StringArray arr = StringArray.asList(VM.class.getName().replace('.', '/')); - return new Bck2Brwsr(ObfuscationLevel.NONE, arr, null); + return new Bck2Brwsr(ObfuscationLevel.NONE, new StringArray(), new StringArray(), new StringArray(), null, false); } - /** Creates new instance of the Bck2Brwsr compiler which inherits - * all values from this instance and adds additional classes - * to the list of those that should be compiled by the {@link #generate(java.lang.Appendable)} - * method. + /** Adds additional classes + * to the list of those that should be included in the generated + * JavaScript file. + * These classes are guaranteed to be available in the + * generated virtual machine code accessible using their fully + * qualified name. This brings the same behavior as if the + * classes were added by {@link #addClasses(java.lang.String...) } and + * were annotated with {@link Exported} annotation. * * @param classes the classes to add to the compilation - * @return new instance of the compiler + * @return new instance of the Bck2Brwsr compiler which inherits + * all values from this */ public Bck2Brwsr addRootClasses(String... classes) { if (classes.length == 0) { return this; } else { - return new Bck2Brwsr(level, this.classes.addAndNew(classes), res); + return new Bck2Brwsr(level, rootcls.addAndNew(classes), this.classes, resources, res, + extension); + } + } + + /** Adds additional classes + * to the list of those that should be included in the generated + * JavaScript file. These classes are guaranteed to be present, + * but they may not be accessible through their fully qualified + * name. + * + * @param classes the classes to add to the compilation + * @return new instance of the Bck2Brwsr compiler which inherits + * all values from this + * @since 0.9 + */ + public Bck2Brwsr addClasses(String... classes) { + if (classes.length == 0) { + return this; + } else { + return new Bck2Brwsr(level, rootcls, this.classes.addAndNew(classes), resources, res, + extension); + } + } + + /** These resources should be made available in the compiled file in + * binary form. These resources can then be loaded + * by {@link ClassLoader#getResource(java.lang.String)} and similar + * methods. + * + * @param resources names of the resources to be loaded by {@link Resources#get(java.lang.String)} + * @return new instance of the Bck2Brwsr compiler which inherits + * all values from this just adds few more resource names + * for processing + * @since 0.9 + */ + public Bck2Brwsr addResources(String... resources) { + if (resources.length == 0) { + return this; + } else { + return new Bck2Brwsr(level, rootcls, this.classes, + this.resources.addAndNew(resources), res, extension + ); } } @@ -125,7 +178,7 @@ * @since 0.5 */ public Bck2Brwsr obfuscation(ObfuscationLevel level) { - return new Bck2Brwsr(level, classes, res); + return new Bck2Brwsr(level, rootcls, classes, resources, res, extension); } /** A way to change the provider of additional resources (classes) for the @@ -137,7 +190,22 @@ * @since 0.5 */ public Bck2Brwsr resources(Resources res) { - return new Bck2Brwsr(level, classes, res); + return new Bck2Brwsr(level, rootcls, classes, resources, res, extension); + } + + /** Should one generate a library? By default the system generates + * all transitive classes needed by the the transitive closure of + * {@link #addRootClasses(java.lang.String...)} and {@link #addClasses(java.lang.String...)}. + * By turning on the library mode, only classes explicitly listed + * will be included in the archive. The others will be referenced + * as external ones. + * + * @param library turn on the library mode? + * @return new instance of the compiler with library flag changed + * @since 0.9 + */ + public Bck2Brwsr library(boolean library) { + return new Bck2Brwsr(level, rootcls, classes, resources, res, library); } /** A way to change the provider of additional resources (classes) for the @@ -173,10 +241,9 @@ * @since 0.5 */ public void generate(Appendable out) throws IOException { - Resources r = res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader(), false); if (level != ObfuscationLevel.NONE) { try { - ClosureWrapper.produceTo(out, level, r, classes); + ClosureWrapper.produceTo(out, level, this); return; } catch (IOException ex) { throw ex; @@ -186,9 +253,33 @@ } } - VM.compile(r, out, classes); + VM.compile(out, this); } + // + // Internal getters + // + + Resources getResources() { + return res != null ? res : new LdrRsrcs(Bck2Brwsr.class.getClassLoader(), false); + } + + String[] allClasses() { + return classes.addAndNew(rootcls.toArray()).toArray(); + } + StringArray allResources() { + return resources; + } + + + StringArray rootClasses() { + return rootcls; + } + + boolean isExtension() { + return extension; + } + /** Provider of resources (classes and other files). The * {@link #generate(java.lang.Appendable, org.apidesign.vm4brwsr.Bck2Brwsr.Resources, java.lang.String[]) * generator method} will call back here for all classes needed during diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Tue May 06 17:46:47 2014 +0200 @@ -1186,7 +1186,7 @@ pkgPrefixLen = classname.lastIndexOf("/") + 1; if (pkgPrefixLen != 0) { pkgPrefix = classname.substring(0, pkgPrefixLen); - return ("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";\n"); + return /* ("package " + */ pkgPrefix.substring(0, pkgPrefixLen - 1) /* + ";\n") */; } else { return null; } @@ -1239,6 +1239,30 @@ return getNameAndType(c2.cpx2, 1, arr); } + public MethodData findMethod(String name, String signature) { + for (MethodData md: methods) { + if (md.getName().equals(name) + && md.getInternalSig().equals(signature)) { + return md; + } + } + + // not found + return null; + } + + public FieldData findField(String name, String signature) { + for (FieldData fd: fields) { + if (fd.getName().equals(name) + && fd.getInternalSig().equals(signature)) { + return fd; + } + } + + // not found + return null; + } + static byte[] findAttr(String n, AttrData[] attrs) { for (AttrData ad : attrs) { if (n.equals(ad.getAttrName())) { diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue May 06 17:46:47 2014 +0200 @@ -29,16 +29,10 @@ private ClassData jc; private final Appendable out; private boolean outChanged; - final ObfuscationDelegate obfuscationDelegate; + private boolean callbacks; protected ByteCodeToJavaScript(Appendable out) { - this(out, ObfuscationDelegate.NULL); - } - - protected ByteCodeToJavaScript( - Appendable out, ObfuscationDelegate obfuscationDelegate) { this.out = out; - this.obfuscationDelegate = obfuscationDelegate; } @Override @@ -88,7 +82,38 @@ return classOperation; } - abstract String getVMObject(); + protected String accessField(String object, String mangledName, + String[] fieldInfoName) throws IOException { + return object + "." + mangledName; + } + + protected String accessStaticMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + return object + "." + mangledName; + } + + protected String accessVirtualMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + return object + "." + mangledName; + } + + protected void declaredClass(ClassData classData, String mangledName) + throws IOException { + } + + protected void declaredField(FieldData fieldData, + String destObject, + String mangledName) throws IOException { + } + + protected void declaredMethod(MethodData methodData, + String destObject, + String mangledName) throws IOException { + } /** Prints out a debug message. * @@ -111,7 +136,12 @@ */ public String compile(InputStream classFile) throws IOException { - this.jc = new ClassData(classFile); + return compile(new ClassData(classFile)); + } + + protected String compile(ClassData classData) throws IOException { + this.jc = classData; + this.callbacks = this.jc.getClassName().endsWith("/$JsCallbacks$"); if (jc.getMajor_version() < 50) { throw new IOException("Can't compile " + jc.getClassName() + ". Class file version " + jc.getMajor_version() + "." + jc.getMinor_version() + " - recompile with -target 1.6 (at least)." @@ -194,7 +224,7 @@ .append("; };"); } - obfuscationDelegate.exportField(this, "c", "_" + v.getName(), v); + declaredField(v, "c", "_" + v.getName()); } for (MethodData m : jc.getMethods()) { byte[] onlyArr = m.findAnnotationData(true); @@ -224,7 +254,7 @@ mn = generateInstanceMethod(destObject, m); } } - obfuscationDelegate.exportMethod(this, destObject, mn, m); + declaredMethod(m, destObject, mn); byte[] runAnno = m.findAnnotationData(false); if (runAnno != null) { append("\n ").append(destObject).append(".").append(mn).append(".anno = {"); @@ -237,16 +267,16 @@ append("\n c.constructor = CLS;"); append("\n function fillInstOf(x) {"); String instOfName = "$instOf_" + className; - append("\n x.").append(instOfName).append(" = true;"); + append("\n x['").append(instOfName).append("'] = true;"); for (String superInterface : jc.getSuperInterfaces()) { String intrfc = superInterface.replace('/', '_'); - append("\n vm.").append(intrfc).append("(false).fillInstOf(x);"); + append("\n vm.").append(intrfc).append("(false)['fillInstOf'](x);"); requireReference(superInterface); } append("\n }"); - append("\n c.fillInstOf = fillInstOf;"); + append("\n c['fillInstOf'] = fillInstOf;"); append("\n fillInstOf(c);"); - obfuscationDelegate.exportJSProperty(this, "c", instOfName); +// obfuscationDelegate.exportJSProperty(this, "c", instOfName); append("\n CLS.$class = 'temp';"); append("\n CLS.$class = "); append(accessClass("java_lang_Class(true);")); @@ -292,7 +322,7 @@ append("\n return arguments[0] ? new CLS() : CLS.prototype;"); append("\n};"); - obfuscationDelegate.exportClass(this, getVMObject(), className, jc); + declaredClass(jc, className); // StringBuilder sb = new StringBuilder(); // for (String init : toInitilize.toArray()) { @@ -1251,9 +1281,10 @@ final int type = VarType.fromFieldType(fi[2].charAt(0)); final String mangleClass = mangleClassName(fi[0]); final String mangleClassAccess = accessClass(mangleClass); - smapper.replace(this, type, "@3(false)._@2.call(@1)", + smapper.replace(this, type, "@2.call(@1)", smapper.getA(0), - fi[1], mangleClassAccess + accessField(mangleClassAccess + "(false)", + "_" + fi[1], fi) ); i += 2; break; @@ -1264,11 +1295,11 @@ final int type = VarType.fromFieldType(fi[2].charAt(0)); final String mangleClass = mangleClassName(fi[0]); final String mangleClassAccess = accessClass(mangleClass); - emit(smapper, this, "@4(false)._@3.call(@2, @1);", + emit(smapper, this, "@3.call(@2, @1);", smapper.popT(type), - smapper.popA(), fi[1], - mangleClassAccess - ); + smapper.popA(), + accessField(mangleClassAccess + "(false)", + "_" + fi[1], fi)); i += 2; break; } @@ -1557,12 +1588,17 @@ } final String in = mi[0]; - append(accessClass(mangleClassName(in))); - append("(false)."); + String mcn; + if (callbacks && in.equals("org/apidesign/html/boot/spi/Fn")) { + mcn = "java_lang_Class"; + } else { + mcn = mangleClassName(in); + } + String object = accessClass(mcn) + "(false)"; if (mn.startsWith("cons_")) { - append("constructor."); + object += ".constructor"; } - append(mn); + append(accessStaticMethod(object, mn, mi)); if (isStatic) { append('('); } else { @@ -1601,8 +1637,7 @@ .append(" = "); } - append(vars[0]).append('.'); - append(mn); + append(accessVirtualMethod(vars[0].toString(), mn, mi)); append('('); String sep = ""; for (int j = 1; j < numArguments; ++j) { @@ -1640,7 +1675,7 @@ String s = jc.stringValue(entryIndex, classRef); if (classRef[0] != null) { if (classRef[0].startsWith("[")) { - s = accessClass("java_lang_Class") + "(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('" + classRef[0] + "')"; + s = accessClass("java_lang_Class") + "(false)['forName__Ljava_lang_Class_2Ljava_lang_String_2']('" + classRef[0] + "')"; } else { addReference(classRef[0]); s = accessClass(mangleClassName(s)) + "(false).constructor.$class"; @@ -1708,7 +1743,8 @@ append(space); space = outputArg(this, p.args, index); if (p.html4j && space.length() > 0) { - toValue.append("\n ").append(p.args[index]).append(" = vm.org_apidesign_bck2brwsr_emul_lang_System(false).toJS("). + toValue.append("\n ").append(p.args[index]).append(" = ") + .append(accessClass("java_lang_Class")).append("(false).toJS("). append(p.args[index]).append(");"); } index++; @@ -2026,8 +2062,8 @@ if (e.catch_cpx != 0) { //not finally final String classInternalName = jc.getClassName(e.catch_cpx); addReference(classInternalName); - append("e = vm.java_lang_Throwable(false).bck2BrwsrCnvrt(e);"); - append("if (e.$instOf_" + classInternalName.replace('/', '_') + ") {"); + append("e = vm.java_lang_Class(false).bck2BrwsrThrwrbl(e);"); + append("if (e['$instOf_" + classInternalName.replace('/', '_') + "']) {"); append("var stA0 = e;"); goTo(this, current, e.handler_pc, topMostLabel); append("}\n"); @@ -2080,19 +2116,23 @@ case 11: jvmType = "[J"; break; default: throw new IllegalStateException("Array type: " + atype); } - emit(smapper, this, "var @2 = Array.prototype.newArray__Ljava_lang_Object_2ZLjava_lang_String_2I(true, '@3', @1);", + emit(smapper, this, + "var @2 = Array.prototype['newArray__Ljava_lang_Object_2ZLjava_lang_String_2Ljava_lang_Object_2I'](true, '@3', null, @1);", smapper.popI(), smapper.pushA(), jvmType); } private void generateANewArray(int type, final StackMapper smapper) throws IOException { String typeName = jc.getClassName(type); + String ref = "null"; if (typeName.startsWith("[")) { - typeName = "[" + typeName; + typeName = "'[" + typeName + "'"; } else { - typeName = "[L" + typeName + ";"; + ref = "vm." + mangleClassName(typeName); + typeName = "'[L" + typeName + ";'"; } - emit(smapper, this, "var @2 = Array.prototype.newArray__Ljava_lang_Object_2ZLjava_lang_String_2I(false, '@3', @1);", - smapper.popI(), smapper.pushA(), typeName); + emit(smapper, this, + "var @2 = Array.prototype['newArray__Ljava_lang_Object_2ZLjava_lang_String_2Ljava_lang_Object_2I'](false, @3, @4, @1);", + smapper.popI(), smapper.pushA(), typeName, ref); } private int generateMultiANewArray(int type, final byte[] byteCodes, int i, final StackMapper smapper) throws IOException { @@ -2107,8 +2147,14 @@ dims.insert(1, smapper.popI()); } dims.append(']'); - emit(smapper, this, "var @2 = Array.prototype.multiNewArray__Ljava_lang_Object_2Ljava_lang_String_2_3II('@3', @1, 0);", - dims.toString(), smapper.pushA(), typeName); + String fn = "null"; + if (typeName.charAt(dim) == 'L') { + fn = "vm." + mangleClassName(typeName.substring(dim + 1, typeName.length() - 1)); + } + emit(smapper, this, + "var @2 = Array.prototype['multiNewArray__Ljava_lang_Object_2Ljava_lang_String_2_3ILjava_lang_Object_2']('@3', @1, @4);", + dims.toString(), smapper.pushA(), typeName, fn + ); return i; } @@ -2160,29 +2206,56 @@ } private void generateInstanceOf(int indx, final StackMapper smapper) throws IOException { - final String type = jc.getClassName(indx); + String type = jc.getClassName(indx); if (!type.startsWith("[")) { - emit(smapper, this, "var @2 = @1 != null && @1.$instOf_@3 ? 1 : 0;", + emit(smapper, this, + "var @2 = @1 != null && @1['$instOf_@3'] ? 1 : 0;", smapper.popA(), smapper.pushI(), type.replace('/', '_')); } else { - emit(smapper, this, "var @2 = vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('@3').isInstance__ZLjava_lang_Object_2(@1);", - smapper.popA(), smapper.pushI(), - type - ); + int cnt = 0; + while (type.charAt(cnt) == '[') { + cnt++; + } + if (type.charAt(cnt) == 'L') { + type = "vm." + mangleClassName(type.substring(cnt + 1, type.length() - 1)); + emit(smapper, this, + "var @2 = Array.prototype['isInstance__ZLjava_lang_Object_2ILjava_lang_Object_2'](@1, @4, @3);", + smapper.popA(), smapper.pushI(), + type, "" + cnt + ); + } else { + emit(smapper, this, + "var @2 = Array.prototype['isInstance__ZLjava_lang_Object_2Ljava_lang_String_2'](@1, '@3');", + smapper.popA(), smapper.pushI(), type + ); + } } } private void generateCheckcast(int indx, final StackMapper smapper) throws IOException { - final String type = jc.getClassName(indx); + String type = jc.getClassName(indx); if (!type.startsWith("[")) { emitNoFlush(smapper, - "if (@1 !== null && !@1.$instOf_@2) throw vm.java_lang_ClassCastException(true);", + "if (@1 !== null && !@1['$instOf_@2']) throw vm.java_lang_ClassCastException(true);", smapper.getT(0, VarType.REFERENCE, false), type.replace('/', '_')); } else { - emitNoFlush(smapper, "vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2('@2').cast__Ljava_lang_Object_2Ljava_lang_Object_2(@1);", - smapper.getT(0, VarType.REFERENCE, false), type - ); + int cnt = 0; + while (type.charAt(cnt) == '[') { + cnt++; + } + if (type.charAt(cnt) == 'L') { + type = "vm." + mangleClassName(type.substring(cnt + 1, type.length() - 1)); + emitNoFlush(smapper, + "if (@1 !== null && !Array.prototype['isInstance__ZLjava_lang_Object_2ILjava_lang_Object_2'](@1, @3, @2)) throw vm.java_lang_ClassCastException(true);", + smapper.getT(0, VarType.REFERENCE, false), type, "" + cnt + ); + } else { + emitNoFlush(smapper, + "if (@1 !== null && !Array.prototype['isInstance__ZLjava_lang_Object_2Ljava_lang_String_2'](@1, '@2')) throw vm.java_lang_ClassCastException(true);", + smapper.getT(0, VarType.REFERENCE, false), type + ); + } } } diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClassDataCache.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,213 @@ +/** + * 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.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; + +@ExtraJavaScript(processByteCode = false, resource="") +final class ClassDataCache { + private static final Object MISSING_CLASS = new Object(); + + private final Bck2Brwsr.Resources resources; + private final Map classDataMap; + + ClassDataCache(final Bck2Brwsr.Resources resources) { + this.resources = resources; + + classDataMap = new HashMap(); + } + + ClassData getClassData(String className) throws IOException { + if (className.startsWith("[")) { + // required for accessVirtualMethod, shouldn't be problematic for + // calls from other sources + className = "java/lang/Object"; + } + Object cacheEntry = classDataMap.get(className); + if (cacheEntry == null) { + final InputStream is = loadClass(resources, className); + cacheEntry = (is != null) ? new ClassData(is) : MISSING_CLASS; + classDataMap.put(className, cacheEntry); + } + + return (cacheEntry != MISSING_CLASS) ? (ClassData) cacheEntry : null; + } + + MethodData findMethod(final String startingClass, + final String name, + final String signature) throws IOException { + return findMethod(getClassData(startingClass), name, signature); + } + + FieldData findField(final String startingClass, + final String name, + final String signature) throws IOException { + return findField(getClassData(startingClass), name, signature); + } + + MethodData findMethod(final ClassData startingClass, + final String name, + final String signature) throws IOException { + final FindFirstTraversalCallback ffTraversalCallback = + new FindFirstTraversalCallback(); + + findMethods(startingClass, name, signature, ffTraversalCallback); + return ffTraversalCallback.getFirst(); + } + + FieldData findField(final ClassData startingClass, + final String name, + final String signature) throws IOException { + final FindFirstTraversalCallback ffTraversalCallback = + new FindFirstTraversalCallback(); + + findFields(startingClass, name, signature, ffTraversalCallback); + return ffTraversalCallback.getFirst(); + } + + void findMethods(final ClassData startingClass, + final String methodName, + final String methodSignature, + final TraversalCallback mdTraversalCallback) + throws IOException { + traverseHierarchy( + startingClass, + new FindMethodsTraversalCallback(methodName, methodSignature, + mdTraversalCallback)); + } + + void findFields(final ClassData startingClass, + final String fieldName, + final String fieldSignature, + final TraversalCallback fdTraversalCallback) + throws IOException { + traverseHierarchy( + startingClass, + new FindFieldsTraversalCallback(fieldName, fieldSignature, + fdTraversalCallback)); + } + + private boolean traverseHierarchy( + ClassData currentClass, + final TraversalCallback cdTraversalCallback) + throws IOException { + while (currentClass != null) { + if (!cdTraversalCallback.traverse(currentClass)) { + return false; + } + + for (final String superIfaceName: + currentClass.getSuperInterfaces()) { + if (!traverseHierarchy(getClassData(superIfaceName), + cdTraversalCallback)) { + return false; + } + } + + final String superClassName = currentClass.getSuperClassName(); + if (superClassName == null) { + break; + } + + currentClass = getClassData(superClassName); + } + + return true; + } + + interface TraversalCallback { + boolean traverse(T object); + } + + private final class FindFirstTraversalCallback + implements TraversalCallback { + private T firstObject; + + @Override + public boolean traverse(final T object) { + firstObject = object; + return false; + } + + public T getFirst() { + return firstObject; + } + } + + private final class FindMethodsTraversalCallback + implements TraversalCallback { + private final String methodName; + private final String methodSignature; + private final TraversalCallback mdTraversalCallback; + + public FindMethodsTraversalCallback( + final String methodName, + final String methodSignature, + final TraversalCallback mdTraversalCallback) { + this.methodName = methodName; + this.methodSignature = methodSignature; + this.mdTraversalCallback = mdTraversalCallback; + } + + @Override + public boolean traverse(final ClassData classData) { + final MethodData methodData = + classData.findMethod(methodName, methodSignature); + return (methodData != null) + ? mdTraversalCallback.traverse(methodData) + : true; + } + } + + private final class FindFieldsTraversalCallback + implements TraversalCallback { + private final String fieldName; + private final String fieldSignature; + private final TraversalCallback fdTraversalCallback; + + public FindFieldsTraversalCallback( + final String fieldName, + final String fieldSignature, + final TraversalCallback fdTraversalCallback) { + this.fieldName = fieldName; + this.fieldSignature = fieldSignature; + this.fdTraversalCallback = fdTraversalCallback; + } + + @Override + public boolean traverse(final ClassData classData) { + final FieldData fieldData = + classData.findField(fieldName, fieldSignature); + return (fieldData != null) + ? fdTraversalCallback.traverse(fieldData) + : true; + } + } + + private static InputStream loadClass(Bck2Brwsr.Resources l, String name) + throws IOException { + return l.get(name + ".class"); // NOI18N + } +} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ClosureWrapper.java Tue May 06 17:46:47 2014 +0200 @@ -23,14 +23,9 @@ import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import org.apidesign.bck2brwsr.core.ExtraJavaScript; -import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; -import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; -import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; /** * @@ -40,24 +35,18 @@ final class ClosureWrapper extends CommandLineRunner { private static final String[] ARGS = { "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--js", "bck2brwsr-raw.js" /*, "--debug", "--formatting", "PRETTY_PRINT" */ }; - private final ClosuresObfuscationDelegate obfuscationDelegate; - private final Bck2Brwsr.Resources res; - private final StringArray classes; + private final Bck2Brwsr config; private String compiledCode; private String externsCode; - private ClosureWrapper(Appendable out, - String compilationLevel, - ClosuresObfuscationDelegate obfuscationDelegate, - Bck2Brwsr.Resources res, StringArray classes) { + private ClosureWrapper(Appendable out, + String compilationLevel, Bck2Brwsr config) { super( generateArguments(compilationLevel), new PrintStream(new APS(out)), System.err ); - this.obfuscationDelegate = obfuscationDelegate; - this.res = res; - this.classes = classes; + this.config = config; } @Override @@ -100,7 +89,7 @@ if (compiledCode == null) { StringBuilder sb = new StringBuilder(); try { - VM.compile(res, sb, classes, obfuscationDelegate); + VM.compile(sb, config); compiledCode = sb.toString(); } catch (IOException ex) { compiledCode = ex.getMessage(); @@ -115,7 +104,7 @@ getCompiledCode(); final StringBuilder sb = new StringBuilder("function RAW() {};\n"); - for (final String extern: obfuscationDelegate.getExterns()) { + for (final String extern: FIXED_EXTERNS) { sb.append("RAW.prototype.").append(extern).append(";\n"); } externsCode = sb.toString(); @@ -142,8 +131,16 @@ return finalArgs; } - static int produceTo(Appendable w, ObfuscationLevel obfuscationLevel, Bck2Brwsr.Resources resources, StringArray arr) throws IOException { - ClosureWrapper cw = create(w, obfuscationLevel, resources, arr); + static int produceTo(Appendable output, + ObfuscationLevel obfuscationLevel, + Bck2Brwsr config + ) throws IOException { + final ClosureWrapper cw = + new ClosureWrapper(output, + (obfuscationLevel == ObfuscationLevel.FULL) + ? "ADVANCED_OPTIMIZATIONS" + : "SIMPLE_OPTIMIZATIONS", + config); try { return cw.doRun(); } catch (FlagUsageException ex) { @@ -151,185 +148,51 @@ } } - private static ClosureWrapper create(Appendable w, - ObfuscationLevel obfuscationLevel, - Bck2Brwsr.Resources resources, - StringArray arr) { - switch (obfuscationLevel) { - case MINIMAL: - return new ClosureWrapper(w, "SIMPLE_OPTIMIZATIONS", - new SimpleObfuscationDelegate(), - resources, arr); -/* - case MEDIUM: - return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", - new MediumObfuscationDelegate(), - resources, arr); -*/ - case FULL: - return new ClosureWrapper(w, "ADVANCED_OPTIMIZATIONS", - new FullObfuscationDelegate(), - resources, arr); - default: - throw new IllegalArgumentException( - "Unsupported level: " + obfuscationLevel); - } - } - - private static abstract class ClosuresObfuscationDelegate - extends ObfuscationDelegate { - public abstract Collection getExterns(); - } - - private static final class SimpleObfuscationDelegate - extends ClosuresObfuscationDelegate { - @Override - public void exportJSProperty(Appendable out, - String destObject, - String propertyName) throws IOException { - } - - @Override - public void exportClass(Appendable out, - String destObject, - String mangledName, - ClassData classData) throws IOException { - } - - @Override - public void exportMethod(Appendable out, - String destObject, - String mangledName, - MethodData methodData) throws IOException { - } - - @Override - public void exportField(Appendable out, - String destObject, - String mangledName, - FieldData fieldData) throws IOException { - } - - @Override - public Collection getExterns() { - return Collections.EMPTY_LIST; - } - } - - private static abstract class AdvancedObfuscationDelegate - extends ClosuresObfuscationDelegate { - private static final String[] INITIAL_EXTERNS = { - "bck2brwsr", - "$class", - "anno", - "array", - "access", - "cls", - "vm", - "loadClass", - "loadBytes", - "jvmName", - "primitive", - "superclass", - "cnstr", - "add32", - "sub32", - "mul32", - "neg32", - "toInt8", - "toInt16", - "next32", - "high32", - "toInt32", - "toFP", - "toLong", - "toExactString", - "add64", - "sub64", - "mul64", - "and64", - "or64", - "xor64", - "shl64", - "shr64", - "ushr64", - "compare64", - "compare", - "neg64", - "div32", - "mod32", - "div64", - "mod64", - "at", - "getClass__Ljava_lang_Class_2", - "clone__Ljava_lang_Object_2" - }; - - private final Collection externs; - - protected AdvancedObfuscationDelegate() { - externs = new ArrayList(Arrays.asList(INITIAL_EXTERNS)); - } - - @Override - public void exportClass(Appendable out, - String destObject, - String mangledName, - ClassData classData) throws IOException { - exportJSProperty(out, destObject, mangledName); - } - - @Override - public void exportMethod(Appendable out, - String destObject, - String mangledName, - MethodData methodData) throws IOException { - if ((methodData.access & ByteCodeParser.ACC_PRIVATE) == 0) { - exportJSProperty(out, destObject, mangledName); - } - } - - @Override - public void exportField(Appendable out, - String destObject, - String mangledName, - FieldData fieldData) throws IOException { - if ((fieldData.access & ByteCodeParser.ACC_PRIVATE) == 0) { - exportJSProperty(out, destObject, mangledName); - } - } - - @Override - public Collection getExterns() { - return externs; - } - - protected void addExtern(String extern) { - externs.add(extern); - } - } - - private static final class MediumObfuscationDelegate - extends AdvancedObfuscationDelegate { - @Override - public void exportJSProperty(Appendable out, - String destObject, - String propertyName) { - addExtern(propertyName); - } - } - - private static final class FullObfuscationDelegate - extends AdvancedObfuscationDelegate { - @Override - public void exportJSProperty(Appendable out, - String destObject, - String propertyName) throws IOException { - out.append("\n").append(destObject).append("['") - .append(propertyName) - .append("'] = ") - .append(destObject).append(".").append(propertyName) - .append(";\n"); - } - } + private static final String[] FIXED_EXTERNS = { + "bck2brwsr", + "bck2BrwsrThrwrbl", + "registerExtension", + "$class", + "anno", + "array", + "access", + "cls", + "vm", + "loadClass", + "loadBytes", + "jvmName", + "primitive", + "superclass", + "cnstr", + "add32", + "sub32", + "mul32", + "neg32", + "toInt8", + "toInt16", + "next32", + "high32", + "toInt32", + "toFP", + "toLong", + "toExactString", + "add64", + "sub64", + "mul64", + "and64", + "or64", + "xor64", + "shl64", + "shr64", + "ushr64", + "compare64", + "neg64", + "div32", + "mod32", + "div64", + "mod64", + "at", + "getClass__Ljava_lang_Class_2", + "clone__Ljava_lang_Object_2" + }; } diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/ExportedSymbols.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ExportedSymbols.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,153 @@ +/** + * 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.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; +import org.apidesign.vm4brwsr.ByteCodeParser.AnnotationParser; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; + +@ExtraJavaScript(processByteCode = false, resource="") +final class ExportedSymbols { + private final Bck2Brwsr.Resources resources; + private final StringArray exported; + private final Map isMarkedAsExportedCache; + + ExportedSymbols(final Bck2Brwsr.Resources resources, StringArray explicitlyExported) { + this.resources = resources; + this.exported = explicitlyExported; + + isMarkedAsExportedCache = new HashMap(); + } + + boolean isExported(ClassData classData) throws IOException { + if (exported.contains(classData.getClassName())) { + return true; + } + return classData.isPublic() && isMarkedAsExportedPackage( + classData.getPkgName()) + || isMarkedAsExported(classData); + } + + boolean isExported(MethodData methodData) throws IOException { + return isAccessible(methodData.access) && isExported(methodData.cls) + || isMarkedAsExported(methodData); + } + + boolean isExported(FieldData fieldData) throws IOException { + return isAccessible(fieldData.access) && isExported(fieldData.cls) + || isMarkedAsExported(fieldData); + } + + private boolean isMarkedAsExportedPackage(String pkgName) { + if (pkgName == null) { + return false; + } + + final Boolean cachedValue = isMarkedAsExportedCache.get(pkgName); + if (cachedValue != null) { + return cachedValue; + } + + final boolean newValue = resolveIsMarkedAsExportedPackage(pkgName); + isMarkedAsExportedCache.put(pkgName, newValue); + + return newValue; + } + + private boolean isMarkedAsExported(ClassData classData) + throws IOException { + final Boolean cachedValue = isMarkedAsExportedCache.get(classData); + if (cachedValue != null) { + return cachedValue; + } + + final boolean newValue = + isMarkedAsExported(classData.findAnnotationData(true), + classData); + isMarkedAsExportedCache.put(classData, newValue); + + return newValue; + } + + private boolean isMarkedAsExported(MethodData methodData) + throws IOException { + return isMarkedAsExported(methodData.findAnnotationData(true), + methodData.cls); + } + + private boolean isMarkedAsExported(FieldData fieldData) + throws IOException { + return isMarkedAsExported(fieldData.findAnnotationData(true), + fieldData.cls); + } + + private boolean resolveIsMarkedAsExportedPackage(String pkgName) { + try { + final InputStream is = + resources.get(pkgName + "/package-info.class"); + if (is == null) { + return false; + } + + try { + final ClassData pkgInfoClass = new ClassData(is); + return isMarkedAsExported( + pkgInfoClass.findAnnotationData(true), + pkgInfoClass); + } finally { + is.close(); + } + } catch (final IOException e) { + return false; + } + } + + private boolean isMarkedAsExported(byte[] arrData, ClassData cd) + throws IOException { + if (arrData == null) { + return false; + } + + final boolean[] found = { false }; + final AnnotationParser annotationParser = + new AnnotationParser(false, false) { + @Override + protected void visitAnnotationStart( + String type, + boolean top) { + if (top && type.equals("Lorg/apidesign/bck2brwsr" + + "/core/Exported;")) { + found[0] = true; + } + } + }; + annotationParser.parse(arrData, cd); + return found[0]; + } + + private static boolean isAccessible(int access) { + return (access & (ByteCodeParser.ACC_PUBLIC + | ByteCodeParser.ACC_PROTECTED)) != 0; + } +} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Tue May 06 17:46:47 2014 +0200 @@ -22,6 +22,12 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; /** Generator of JavaScript from bytecode of classes on classpath of the VM * with a Main method. @@ -31,12 +37,13 @@ final class Main { private Main() {} - public static void main(String... args) throws IOException { + public static void main(String... args) throws IOException, URISyntaxException { final String obfuscate = "--obfuscatelevel"; - + final String extension = "--createextension"; + if (args.length < 2) { System.err.println("Bck2Brwsr Translator from Java(tm) to JavaScript, (c) Jaroslav Tulach 2012"); - System.err.println("Usage: java -cp ... -jar ... ["); + System.err.print("Usage: java -cp ... -jar ... ["); System.err.print(obfuscate); System.err.print(" ["); boolean first = true; @@ -47,12 +54,16 @@ System.err.print(l.name()); first = false; } - + System.err.print("]] ["); + System.err.print(extension); System.err.println("] java/lang/Class org/your/App ..."); System.exit(9); } - + + final ClassLoader mainClassLoader = Main.class.getClassLoader(); + ObfuscationLevel obfLevel = ObfuscationLevel.NONE; + boolean createExtension = false; StringArray classes = new StringArray(); String generateTo = null; for (int i = 0; i < args.length; i++) { @@ -76,10 +87,14 @@ } continue; } + if (extension.equals(args[i])) { // NOI18N + createExtension = true; + continue; + } if (generateTo == null) { generateTo = args[i]; } else { - classes = classes.addAndNew(args[i]); + collectClasses(classes, mainClassLoader, args[i]); } } @@ -90,11 +105,110 @@ } try (Writer w = new BufferedWriter(new FileWriter(gt))) { - Bck2Brwsr.newCompiler(). + Bck2Brwsr.newCompiler().library(createExtension). obfuscation(obfLevel). addRootClasses(classes.toArray()). resources(new LdrRsrcs(Main.class.getClassLoader(), true)). generate(w); } } + + private static void collectClasses( + final StringArray dest, + final ClassLoader cl, final String relativePath) + throws IOException, URISyntaxException { + final Enumeration urls = cl.getResources(relativePath); + if (!urls.hasMoreElements()) { + dest.add(relativePath); + return; + } + do { + final URL url = urls.nextElement(); + switch (url.getProtocol()) { + case "file": + collectClasses(dest, relativePath, + new File(new URI(url.toString()))); + continue; + case "jar": + final String fullPath = url.getPath(); + final int sepIndex = fullPath.indexOf('!'); + final String jarFilePath = + (sepIndex != -1) ? fullPath.substring(0, sepIndex) + : fullPath; + + final URI jarUri = new URI(jarFilePath); + if (jarUri.getScheme().equals("file")) { + try (JarFile jarFile = new JarFile(new File(jarUri))) { + collectClasses(dest, relativePath, jarFile); + continue; + } + } + break; + } + + dest.add(relativePath); + } while (urls.hasMoreElements()); + } + + private static void collectClasses(final StringArray dest, + final String relativePath, + final File file) { + if (file.isDirectory()) { + final File[] subFiles = file.listFiles(); + for (final File subFile: subFiles) { + collectClasses(dest, + extendPath(relativePath, subFile.getName()), + subFile); + } + + return; + } + + final String filePath = file.getPath(); + if (filePath.endsWith(".class")) { + validateAndAddClass(dest, relativePath); + } + } + + private static void collectClasses(final StringArray dest, + final String relativePath, + final JarFile jarFile) { + if (relativePath.endsWith(".class")) { + if (jarFile.getJarEntry(relativePath) != null) { + validateAndAddClass(dest, relativePath); + } + + return; + } + + final String expectedPrefix = + relativePath.endsWith("/") ? relativePath + : relativePath + '/'; + final Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + final JarEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + final String entryName = entry.getName(); + if (entryName.startsWith(expectedPrefix) + && entryName.endsWith(".class")) { + validateAndAddClass(dest, entryName); + } + } + } + } + + private static String extendPath(final String relativePath, + final String fileName) { + return relativePath.endsWith("/") ? relativePath + fileName + : relativePath + '/' + fileName; + } + + private static void validateAndAddClass(final StringArray dest, + final String relativePath) { + final String className = + relativePath.substring(0, relativePath.length() - 6); + if (!className.endsWith("package-info")) { + dest.add(className); + } + } } diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ObfuscationDelegate.java Wed Apr 30 09:26:28 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +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.io.IOException; -import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; -import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; -import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; - -abstract class ObfuscationDelegate { - static ObfuscationDelegate NULL = - new ObfuscationDelegate() { - @Override - public void exportJSProperty(Appendable out, - String destObject, - String propertyName) - throws IOException { - } - - @Override - public void exportClass(Appendable out, - String destObject, - String mangledName, - ClassData classData) - throws IOException { - } - - @Override - public void exportMethod(Appendable out, - String destObject, - String mangledName, - MethodData methodData) - throws IOException { - } - - @Override - public void exportField(Appendable out, - String destObject, - String mangledName, - FieldData fieldData) - throws IOException { - } - }; - - public abstract void exportJSProperty( - Appendable out, String destObject, String propertyName) - throws IOException; - - public abstract void exportClass( - Appendable out, String destObject, String mangledName, - ClassData classData) throws IOException; - - public abstract void exportMethod( - Appendable out, String destObject, String mangledName, - MethodData methodData) throws IOException; - - public abstract void exportField( - Appendable out, String destObject, String mangledName, - FieldData fieldData) throws IOException; -} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Tue May 06 17:46:47 2014 +0200 @@ -19,18 +19,34 @@ import java.io.IOException; import java.io.InputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.vm4brwsr.ByteCodeParser.ClassData; +import org.apidesign.vm4brwsr.ByteCodeParser.FieldData; +import org.apidesign.vm4brwsr.ByteCodeParser.MethodData; /** Generator of JavaScript from bytecode of classes on classpath of the VM. * * @author Jaroslav Tulach */ -class VM extends ByteCodeToJavaScript { - public VM(Appendable out) { +abstract class VM extends ByteCodeToJavaScript { + protected final ClassDataCache classDataCache; + + private final Bck2Brwsr.Resources resources; + private final ExportedSymbols exportedSymbols; + private final StringArray invokerMethods; + + private static final Class FIXED_DEPENDENCIES[] = { + Class.class, + ArithmeticException.class, + VM.class + }; + + private VM(Appendable out, Bck2Brwsr.Resources resources, StringArray explicitlyExported) { super(out); - } - - private VM(Appendable out, ObfuscationDelegate obfuscationDelegate) { - super(out, obfuscationDelegate); + this.resources = resources; + this.classDataCache = new ClassDataCache(resources); + this.exportedSymbols = new ExportedSymbols(resources, explicitlyExported); + this.invokerMethods = new StringArray(); } static { @@ -48,17 +64,131 @@ return false; } - static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException { - new VM(out).doCompile(l, names); + static void compile(Appendable out, + Bck2Brwsr config + ) throws IOException { + String[] both = config.allClasses(); + + VM vm = config.isExtension() ? + new Extension(out, config.getResources(), both, config.rootClasses()) + : + new Standalone(out, config.getResources(), config.rootClasses()); + + final StringArray fixedNames = new StringArray(); + + for (final Class fixedClass: FIXED_DEPENDENCIES) { + fixedNames.add(fixedClass.getName().replace('.', '/')); + } + + vm.doCompile(fixedNames.addAndNew(both), config.allResources()); } - static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names, - ObfuscationDelegate obfuscationDelegate) throws IOException { - new VM(out, obfuscationDelegate).doCompile(l, names); + private void doCompile(StringArray names, StringArray asBinary) throws IOException { + generatePrologue(); + append( + "\n var invoker = {};"); + generateBody(names); + for (String invokerMethod: invokerMethods.toArray()) { + append("\n invoker." + invokerMethod + " = function(target) {" + + "\n return function() {" + + "\n return target['" + invokerMethod + "'].apply(target, arguments);" + + "\n };" + + "\n };" + ); + } + + for (String r : asBinary.toArray()) { + append("\n ").append(getExportsObject()).append(".registerResource('"); + append(r).append("', '"); + InputStream is = this.resources.get(r); + byte[] arr = new byte[is.available()]; + int offset = 0; + for (;;) { + if (offset == arr.length) { + byte[] tmp = new byte[arr.length * 2]; + System.arraycopy(arr, 0, tmp, 0, arr.length); + arr = tmp; + } + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + break; + } + offset += len; + } + if (offset != arr.length) { + byte[] tmp = new byte[offset]; + System.arraycopy(arr, 0, tmp, 0, offset); + arr = tmp; + } + append(btoa(arr)); + append("');"); + } + + append("\n"); + generateEpilogue(); } - protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { - append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); + @JavaScriptBody(args = { "arr" }, body = "return btoa(arr);") + private static String btoa(byte[] arr) { + return javax.xml.bind.DatatypeConverter.printBase64Binary(arr); + } + + protected abstract void generatePrologue() throws IOException; + + protected abstract void generateEpilogue() throws IOException; + + protected abstract String getExportsObject(); + + protected abstract boolean isExternalClass(String className); + + @Override + protected final void declaredClass(ClassData classData, String mangledName) + throws IOException { + if (exportedSymbols.isExported(classData)) { + append("\n").append(getExportsObject()).append("['") + .append(mangledName) + .append("'] = ") + .append(accessClass(mangledName)) + .append(";\n"); + } + } + + protected String generateClass(String className) throws IOException { + ClassData classData = classDataCache.getClassData(className); + if (classData == null) { + throw new IOException("Can't find class " + className); + } + return compile(classData); + } + + @Override + protected void declaredField(FieldData fieldData, + String destObject, + String mangledName) throws IOException { + if (exportedSymbols.isExported(fieldData)) { + exportMember(destObject, mangledName); + } + } + + @Override + protected void declaredMethod(MethodData methodData, + String destObject, + String mangledName) throws IOException { + if (isHierarchyExported(methodData)) { + exportMember(destObject, mangledName); + } + } + + private void exportMember(String destObject, String memberName) + throws IOException { + append("\n").append(destObject).append("['") + .append(memberName) + .append("'] = ") + .append(destObject).append(".").append(memberName) + .append(";\n"); + } + + private void generateBody(StringArray names) throws IOException { StringArray processed = new StringArray(); StringArray initCode = new StringArray(); StringArray skipClass = new StringArray(); @@ -78,14 +208,14 @@ if (name == null) { break; } - InputStream is = loadClass(l, name); + InputStream is = resources.get(name + ".class"); if (is == null) { lazyReference(this, name); skipClass.add(name); continue; } try { - String ic = compile(is); + String ic = generateClass(name); processed.add(name); initCode.add(ic == null ? "" : ic); } catch (RuntimeException ex) { @@ -97,14 +227,14 @@ while (resource.startsWith("/")) { resource = resource.substring(1); } - InputStream emul = l.get(resource); + InputStream emul = resources.get(resource); if (emul == null) { throw new IOException("Can't find " + resource); } readResource(emul, this); } scripts = new StringArray(); - + StringArray toInit = StringArray.asList(references.toArray()); toInit.reverse(); @@ -119,6 +249,7 @@ } } } +/* append( " return vm;\n" + " };\n" @@ -158,7 +289,9 @@ + " return loader;\n" + " };\n"); append("}(this));"); +*/ } + private static void readResource(InputStream emul, Appendable out) throws IOException { try { int state = 0; @@ -205,10 +338,6 @@ } } - private static InputStream loadClass(Bck2Brwsr.Resources l, String name) throws IOException { - return l.get(name + ".class"); // NOI18N - } - static String toString(String name) throws IOException { StringBuilder sb = new StringBuilder(); // compile(sb, name); @@ -243,8 +372,287 @@ } @Override - String getVMObject() { - return "vm"; + protected String accessField(String object, String mangledName, + String[] fieldInfoName) throws IOException { + final FieldData field = + classDataCache.findField(fieldInfoName[0], + fieldInfoName[1], + fieldInfoName[2]); + return accessNonVirtualMember(object, mangledName, + (field != null) ? field.cls : null); + } + + @Override + protected String accessStaticMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + final MethodData method = + classDataCache.findMethod(fieldInfoName[0], + fieldInfoName[1], + fieldInfoName[2]); + return accessNonVirtualMember(object, mangledName, + (method != null) ? method.cls : null); + } + + @Override + protected String accessVirtualMethod( + String object, + String mangledName, + String[] fieldInfoName) throws IOException { + final ClassData referencedClass = + classDataCache.getClassData(fieldInfoName[0]); + final MethodData method = + classDataCache.findMethod(referencedClass, + fieldInfoName[1], + fieldInfoName[2]); + + if ((method != null) + && !isExternalClass(method.cls.getClassName()) + && (((method.access & ByteCodeParser.ACC_FINAL) != 0) + || ((referencedClass.getAccessFlags() + & ByteCodeParser.ACC_FINAL) != 0) + || !isHierarchyExported(method))) { + return object + "." + mangledName; + } + + return accessThroughInvoker(object, mangledName); + } + + private String accessThroughInvoker(String object, String mangledName) { + if (!invokerMethods.contains(mangledName)) { + invokerMethods.add(mangledName); + } + return "invoker." + mangledName + '(' + object + ')'; + } + + private boolean isHierarchyExported(final MethodData methodData) + throws IOException { + if (exportedSymbols.isExported(methodData)) { + return true; + } + if ((methodData.access & (ByteCodeParser.ACC_PRIVATE + | ByteCodeParser.ACC_STATIC)) != 0) { + return false; + } + + final ExportedMethodFinder exportedMethodFinder = + new ExportedMethodFinder(exportedSymbols); + + classDataCache.findMethods( + methodData.cls, + methodData.getName(), + methodData.getInternalSig(), + exportedMethodFinder); + + return (exportedMethodFinder.getFound() != null); + } + + private String accessNonVirtualMember(String object, + String mangledName, + ClassData declaringClass) { + return ((declaringClass != null) + && !isExternalClass(declaringClass.getClassName())) + ? object + "." + mangledName + : object + "['" + mangledName + "']"; + } + + private static final class ExportedMethodFinder + implements ClassDataCache.TraversalCallback { + private final ExportedSymbols exportedSymbols; + private MethodData found; + + public ExportedMethodFinder(final ExportedSymbols exportedSymbols) { + this.exportedSymbols = exportedSymbols; + } + + @Override + public boolean traverse(final MethodData methodData) { + try { + if (exportedSymbols.isExported(methodData)) { + found = methodData; + return false; + } + } catch (final IOException e) { + } + + return true; + } + + public MethodData getFound() { + return found; + } + } + + private static final class Standalone extends VM { + private Standalone(Appendable out, Bck2Brwsr.Resources resources, StringArray explicitlyExported) { + super(out, resources, explicitlyExported); + } + + @Override + protected void generatePrologue() throws IOException { + append("(function VM(global) {var fillInVMSkeleton = function(vm) {"); + } + + @Override + protected void generateEpilogue() throws IOException { + append( + " return vm;\n" + + " };\n" + + " var extensions = [];\n" + + " function mangleClass(name) {\n" + + " return name.replace__Ljava_lang_String_2Ljava_lang_CharSequence_2Ljava_lang_CharSequence_2(\n" + + " '_', '_1').replace__Ljava_lang_String_2CC('.','_');\n" + + " };\n" + + " global.bck2brwsr = function() {\n" + + " var args = Array.prototype.slice.apply(arguments);\n" + + " var resources = {};\n" + + " function registerResource(n, a64) {\n" + + " var str = atob(a64);\n" + + " var arr = [];\n" + + " for (var i = 0; i < str.length; i++) {\n" + + " var ch = str.charCodeAt(i) & 0xff;\n" + + " if (ch > 127) ch -= 256;\n" + + " arr.push(ch);\n" + + " }\n" + + " if (!resources[n]) resources[n] = [arr];\n" + + " else resources[n].push(arr);\n" + + " }\n" + + " var vm = fillInVMSkeleton({ 'registerResource' : registerResource });\n" + + " for (var i = 0; i < extensions.length; ++i) {\n" + + " extensions[i](vm);\n" + + " }\n" + + " vm.registerResource = null;\n" + + " var knownExtensions = extensions.length;\n" + + " var loader = {};\n" + + " loader.vm = vm;\n" + + " loader.loadClass = function(name) {\n" + + " var attr = mangleClass(name);\n" + + " var fn = vm[attr];\n" + + " if (fn) return fn(false);\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" + + " vm.registerResource = registerResource;\n" + + " extensions[knownExtensions++](vm);\n" + + " vm.registerResource = null;\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" + + " }\n" + + " vm.loadClass = loader.loadClass;\n" + + " vm._reload = function(name, byteCode) {;\n" + + " var attr = mangleClass(name);\n" + + " delete vm[attr];\n" + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " reload__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2_3B(loader, name, args, byteCode);\n" + + " };\n" + + " vm.loadBytes = function(name, skip) {\n" + + " skip = typeof skip == 'number' ? skip : 0;\n" + + " var arr = resources[name];\n" + + " if (arr) {\n" + + " var arrSize = arr.length;\n" + + " if (skip < arrSize) return arr[skip];\n" + + " skip -= arrSize;\n" + + " } else {\n" + + " var arrSize = 0;\n" + + " };\n" + + " var ret = vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2I(loader, name, args, skip);\n" + + " if (ret !== null) return ret;\n" + + " while (knownExtensions < extensions.length) {\n" + + " vm.registerResource = registerResource;\n" + + " extensions[knownExtensions++](vm);\n" + + " vm.registerResource = null;\n" + + " }\n" + + " var arr = resources[name];\n" + + " return (arr && arr.length > arrSize) ? arr[arrSize] : null;\n" + + " }\n" + + " vm.java_lang_reflect_Array(false);\n" + + " vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " loadBytes___3BLjava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2I(loader, null, args, 0);\n" + + " return loader;\n" + + " };\n"); + append( + " global.bck2brwsr.registerExtension = function(extension) {\n" + + " extensions.push(extension);\n" + + " return null;\n" + + " };\n"); + append("}(this));"); + } + + @Override + protected String getExportsObject() { + return "vm"; + } + + @Override + protected boolean isExternalClass(String className) { + return false; + } + } + + private static final class Extension extends VM { + private final StringArray extensionClasses; + + private Extension(Appendable out, Bck2Brwsr.Resources resources, + String[] extClassesArray, StringArray explicitlyExported) { + super(out, resources, explicitlyExported); + this.extensionClasses = StringArray.asList(extClassesArray); + } + + @Override + protected void generatePrologue() throws IOException { + append("bck2brwsr.registerExtension(function(exports) {\n" + + " var vm = {};\n"); + append(" function link(n, inst) {\n" + + " var cls = n['replace__Ljava_lang_String_2CC']" + + "('/', '_').toString();\n" + + " var dot = n['replace__Ljava_lang_String_2CC']" + + "('/', '.').toString();\n" + + " exports.loadClass(dot);\n" + + " vm[cls] = exports[cls];\n" + + " return vm[cls](inst);\n" + + " };\n"); + } + + @Override + protected void generateEpilogue() throws IOException { + append("});"); + } + + @Override + protected String generateClass(String className) throws IOException { + if (isExternalClass(className)) { + append("\n").append(assignClass( + className.replace('/', '_'))) + .append("function() {\n return link('") + .append(className) + .append("', arguments.length == 0 || arguments[0] === true);" + + "\n};"); + + return null; + } + + return super.generateClass(className); + } + + @Override + protected String getExportsObject() { + return "exports"; + } + + @Override + protected boolean isExternalClass(String className) { + return !extensionClasses.contains(className); + } } private static void lazyReference(Appendable out, String n) throws IOException { diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Tue May 06 17:46:47 2014 +0200 @@ -151,10 +151,5 @@ String accessClass(String classOperation) { return "vm." + classOperation; } - - @Override - String getVMObject() { - return "vm"; - } } } diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/Array.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Array.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Array.java Tue May 06 17:46:47 2014 +0200 @@ -17,6 +17,8 @@ */ package org.apidesign.vm4brwsr; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + /** * * @author Jaroslav Tulach @@ -75,6 +77,7 @@ } public static double sum() { + disableClassForName(); double sum = 0.0; for (Array[] row : arr()) { int indx = -1; @@ -109,13 +112,50 @@ } public static String objectArrayClass() { - return Object[].class.getName(); + enableClassForName(prevClassForName); + prevClassForName = null; + String s = Object[].class.getName(); + disableClassForName(); + return s; } public static boolean instanceOfArray(Object obj) { + if ("string-array".equals(obj)) { + obj = new String[] { "Ahoj" }; + } return obj instanceof Object[]; } + public static boolean castArray(int type) { + try { + Object orig = new Object(); + Object res = orig; + if (type == 0) { + Object[] arr = new Integer[1]; + String[] str = (String[]) arr; + res = str; + } + if (type == 1) { + Object[] arr = null; + String[] str = (String[]) arr; + res = str; + } + if (type == 2) { + Object[] arr = new String[1]; + String[] str = (String[]) arr; + res = str; + } + if (type == 3) { + Object[] arr = new String[1]; + CharSequence[] str = (CharSequence[]) arr; + res = str; + } + return res != orig; + } catch (ClassCastException ex) { + return false; + } + } + public static int sum(int size) { int[] arr = new int[size]; return arr[0] + arr[1]; @@ -133,6 +173,37 @@ return arr[0]; } + @JavaScriptBody(args = { }, body = + "var prev = vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2;\n" + + "if (!prev) throw 'forName not defined';\n" + + "vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2 = function(s) {\n" + + " throw 'Do not call me: ' + s;\n" + + "};\n" + + "return prev;\n") + private static Object disableClassForNameImpl() { + return null; + } + + private static void disableClassForName() { + if (prevClassForName == null) { + prevClassForName = disableClassForNameImpl(); + } + } + + @JavaScriptBody(args = { "fn" }, body = + "if (fn !== null) vm.java_lang_Class(false).forName__Ljava_lang_Class_2Ljava_lang_String_2 = fn;" + ) + private static void enableClassForName(Object fn) { + } + + private static Object prevClassForName; + public static String nameOfClonedComponent() { + disableClassForName(); + Object[] intArr = new Integer[10]; + intArr = intArr.clone(); + return intArr.getClass().getComponentType().getName(); + } + public static int multiLen() { return new int[1][0].length; } diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Tue May 06 17:46:47 2014 +0200 @@ -43,6 +43,13 @@ Double.valueOf(15), true ); } + + @Test public void cloneOnArrayAndComponentType() throws Exception { + String exp = Array.nameOfClonedComponent(); + assertExec("getComponentType on clone", Array.class, "nameOfClonedComponent__Ljava_lang_String_2", + exp + ); + } @Test public void realOperationOnArrays() throws Exception { assertEquals(Array.sum(), 105.0, "Computes to 105"); @@ -75,9 +82,28 @@ @Test public void verifyInstanceOfArray() throws Exception { assertExec("Returns 'false'", Array.class, "instanceOfArray__ZLjava_lang_Object_2", Double.valueOf(0), "non-array"); } + @Test public void verifyInstanceOfArrayOnNull() throws Exception { + assertFalse(Array.instanceOfArray(null), "Null is not an instance of array"); + assertExec("Returns 'false'", Array.class, "instanceOfArray__ZLjava_lang_Object_2", Double.valueOf(0), (Object) null); + } + @Test public void verifyInstanceOfArrayStrings() throws Exception { + assertExec("Returns 'false'", Array.class, "instanceOfArray__ZLjava_lang_Object_2", Double.valueOf(1), "string-array"); + } @Test public void verifyMultiLen() throws Exception { assertExec("Multi len is one", Array.class, "multiLen__I", Double.valueOf(1)); } + @Test public void upCastAnArray() throws Exception { + assertExec("Cannot cast int to string array", Array.class, "castArray__ZI", Double.valueOf(0), Double.valueOf(0)); + } + @Test public void upCastANullArray() throws Exception { + assertExec("Can cast null to string array", Array.class, "castArray__ZI", Double.valueOf(1), Double.valueOf(1)); + } + @Test public void upCastOK() throws Exception { + assertExec("Can cast string to string array", Array.class, "castArray__ZI", Double.valueOf(1), Double.valueOf(2)); + } + @Test public void upCastOK2() throws Exception { + assertExec("Can cast string to char sequence array", Array.class, "castArray__ZI", Double.valueOf(1), Double.valueOf(3)); + } private static TestVM code; diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/NumbersAsExtensionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/NumbersAsExtensionTest.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,240 @@ +/** + * 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 javax.script.ScriptEngine; +import static org.testng.Assert.*; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public class NumbersAsExtensionTest { + @Test public void integerFromString() throws Exception { + assertExec("Can convert string to integer", Integer.class, "parseInt__ILjava_lang_String_2", + Double.valueOf(333), "333" + ); + } + + @Test public void doubleFromString() throws Exception { + assertExec("Can convert string to double", Double.class, "parseDouble__DLjava_lang_String_2", + Double.valueOf(33.3), "33.3" + ); + } + + @Test public void autoboxDouble() throws Exception { + assertExec("Autoboxing of doubles is OK", Numbers.class, "autoboxDblToString__Ljava_lang_String_2", + "3.3" + ); + } + + @Test public void javalog1000() throws Exception { + assertEquals(3.0, Math.log10(1000.0), 0.00003, "log_10(1000) == 3"); + } + + @Test public void jslog1000() throws Exception { + assertExec("log_10(1000) == 3", Math.class, "log10__DD", + Double.valueOf(3.0), 1000.0 + ); + } + + @Test public void javaRem() { + assertEquals(3, Numbers.rem(303, 10)); + } + @Test public void jsRem() throws Exception { + assertExec("Should be three", Numbers.class, "rem__III", + Double.valueOf(3.0), 303, 10 + ); + } + + @Test public void deserializeInt() throws Exception { + int exp = Numbers.deserInt(); + assertExec("Should be the same", Numbers.class, "deserInt__I", + Double.valueOf(exp) + ); + } + + @Test public void deserializeSimpleLong() throws Exception { + assertExec("Should be 3454", Numbers.class, "deserLong__J_3B", + Double.valueOf(3454), + new byte[] { (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)13, (byte)126 } + ); + } + /* XXX: JavaScript cannot represent as big longs as Java. + @Test public void deserializeLargeLong() throws Exception { + final byte[] arr = new byte[] { + (byte)64, (byte)8, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0 + }; + long exp = Numbers.deserLong(arr); + assertExec("Should be " + exp, "org_apidesign_vm4brwsr_Numbers_deserLong__JAB", + Double.valueOf(exp), arr); + } + */ + + @Test public void deserializeFloatInJava() throws Exception { + float f = 54324.32423f; + float r = Numbers.deserFloat(); + assertEquals(r, f, "Floats are the same"); + } + + @Test public void deserializeFloatInJS() throws Exception { + float f = 54324.32423f; + assertExec("Should be the same", Numbers.class, "deserFloat__F", + Double.valueOf(f) + ); + } + + @Test public void deserializeDoubleInJava() throws Exception { + double f = 3.0; + double r = Numbers.deserDouble(); + assertEquals(r, f, 0.001, "Doubles are the same"); + } + + @Test public void deserializeDoubleInJS() throws Exception { + double f = 3.0; + assertExec("Should be the same", Numbers.class, "deserDouble__D", f); + } + /* + @Test public void serDouble() throws IOException { + double f = 3.0; + ByteArrayOutputStream os = new ByteArrayOutputStream(); + DataOutputStream d = new DataOutputStream(os); + d.writeLong(3454); + d.close(); + + StringBuilder sb = new StringBuilder(); + byte[] arr = os.toByteArray(); + for (int i = 0; i < arr.length; i++) { + sb.append("(byte)").append(arr[i]).append(", "); + } + fail("" + sb); + } +*/ + @Test public void fiveInStringJS() throws Exception { + String s = Numbers.intToString(); + assertExec("Should be the same: " + s, + Numbers.class, "intToString__Ljava_lang_String_2", + s + ); + } + + @Test public void sevenInStringJS() throws Exception { + String s = Numbers.floatToString(); + assertExec("Should be the same: " + s, + Numbers.class, "floatToString__Ljava_lang_String_2", + s + ); + } + + @Test public void everyNumberHasJavaLangNumberMethods() throws Exception { + assertExec("Can we call doubleValue?", + Numbers.class, "seven__DI", + Double.valueOf(7.0), 0 + ); + } + @Test public void everyNumberHasJavaLangNumberMethodsInt() throws Exception { + assertExec("Can we call doubleValue?", + Numbers.class, "seven__DI", + Double.valueOf(7.0), 1 + ); + } + @Test public void everyNumberHasJavaLangNumberMethodsLong() throws Exception { + assertExec("Can we call doubleValue?", + Numbers.class, "seven__DI", + Double.valueOf(7.0), 2 + ); + } + @Test public void everyNumberHasJavaLangNumberMethodsShort() throws Exception { + assertExec("Can we call doubleValue?", + Numbers.class, "seven__DI", + Double.valueOf(7.0), 3 + ); + } + @Test public void everyNumberHasJavaLangNumberMethodsByte() throws Exception { + assertExec("Can we call doubleValue?", + Numbers.class, "seven__DI", + Double.valueOf(7.0), 4 + ); + } + @Test public void valueOfNumber() throws Exception { + assertExec("Can we call JavaScripts valueOf?", + Numbers.class, "seven__DI", + Double.valueOf(7.0), 8 + ); + } + @Test public void valueOfLongNumber() throws Exception { + assertExec("Can we call JavaScripts valueOf?", + Numbers.class, "seven__DI", + Double.valueOf(Long.MAX_VALUE / 5), 9 + ); + } + @Test public void valueOfLongCharA() throws Exception { + assertExec("Can we call JavaScripts valueOf on Character?", + Numbers.class, "seven__DI", + Double.valueOf('A'), 65 + ); + } + + @Test public void valueOfLongBooleanTrue() throws Exception { + assertExec("Can we call JavaScripts valueOf on Boolean?", + Numbers.class, "bseven__ZI", + true, 31 + ); + } + @Test public void valueOfLongBooleanFalse() throws Exception { + assertExec("Can we call JavaScripts valueOf on Boolean?", + Numbers.class, "bseven__ZI", + false, 30 + ); + } + + private static TestVM code; + + @BeforeClass + public static void compileTheCode() throws Exception { + ScriptEngine[] eng = { null }; + code = TestVM.compileClassAsExtension(null, eng, "org/apidesign/vm4brwsr/Numbers", null, null); + } + @AfterClass + public static void releaseTheCode() { + code = null; + } + + private static void assertExec( + String msg, Class clazz, String method, Object expRes, Object... args) throws Exception + { + Object ret = code.execCode(msg, clazz, method, expRes, args); + if (ret == null) { + return; + } + if (expRes instanceof Double && ret instanceof Double) { + double expD = ((Double)expRes).doubleValue(); + double retD = ((Double)ret).doubleValue(); + assertEquals(retD, expD, 0.000004, msg + " " + + code.toString()); + return; + } + assertEquals(ret, expRes, msg + " " + code.toString()); + } + + + +} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/Resources.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Resources.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,87 @@ +/** + * 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.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** + * + * @author Jaroslav Tulach + */ +public class Resources { + public static String loadKO() throws IOException { + InputStream is = Resources.class.getResourceAsStream("ko.js"); + return readIS(is, false); + } + + static String loadClazz() throws IOException { + InputStream is = Resources.class.getResourceAsStream("Bck2BrwsrToolkit.class"); + return readIS(is, false); + } + + private static String readIS(InputStream is, boolean asString) throws IOException { + if (is == null) { + return "No resource found!"; + } + byte[] arr = new byte[4092]; + int len = is.read(arr); + if (len < 5) { + return "No data read! Len: " + len; + } + + if (asString) { + return new String(arr, 0, len, "UTF-8").toString().toString(); + } + + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < len; i++) { + sb.append(arr[i]).append(", "); + } + + return sb.toString().toString(); + } + static long bytesToLong(byte b1, byte b2, int shift) { + return (((long)b1 << 56) + + ((long)b2 & 255) << 48) >> shift; + } + + static String loadHello() throws IOException { + Enumeration en; + try { + en = Resources.class.getClassLoader().getResources("META-INF/ahoj"); + } catch (SecurityException ex) { + return "SecurityException"; + } + StringBuilder sb = new StringBuilder(); + while (en.hasMoreElements()) { + URL url = en.nextElement(); + sb.append(readIS(url.openStream(), true)); + } + return sb.toString().toString(); + } + static String loadJustHello() throws IOException { + URL url = Resources.class.getResource("/META-INF/ahoj"); + StringBuilder sb = new StringBuilder(); + sb.append(readIS(url.openStream(), true)); + return sb.toString().toString(); + } +} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/ResourcesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ResourcesTest.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,80 @@ +/** + * 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.io.UnsupportedEncodingException; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** Tests related to loading resources from the VM. + * + * @author Jaroslav Tulach + */ +public class ResourcesTest { + @Test public void loadPrecompiledResource() throws Exception { + String exp = Resources.loadKO(); + + assertExec("Loading a precompiled resource:", + Resources.class, "loadKO__Ljava_lang_String_2", + exp + ); + } + + @Test public void loadBinaryPrecompiledResource() throws Exception { + String exp = Resources.loadClazz(); + + assertExec("Loading a precompiled resource:", + Resources.class, "loadClazz__Ljava_lang_String_2", + exp + ); + } + + private static TestVM code; + + @BeforeClass + public static void compileTheCode() throws Exception { + StringBuilder sb = new StringBuilder(); + code = TestVM.compileClassAndResources(sb, null, + "org/apidesign/vm4brwsr/Resources", + "org/apidesign/vm4brwsr/ko.js", + "org/apidesign/vm4brwsr/Bck2BrwsrToolkit.class" + ); + } + @AfterClass + public static void releaseTheCode() { + code = null; + } + + private void assertExec( + String msg, Class clazz, String method, + Object ret, Object... args + ) throws Exception { + code.assertExec(msg, clazz, method, ret, args); + } + + public static String parseBase64Binary(String s) throws UnsupportedEncodingException { + final byte[] arr = javax.xml.bind.DatatypeConverter.parseBase64Binary(s); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < arr.length; i++) { + int ch = arr[i]; + sb.append((char)ch); + } + return sb.toString(); + } +} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/ResourcesWithExtensionsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ResourcesWithExtensionsTest.java Tue May 06 17:46:47 2014 +0200 @@ -0,0 +1,85 @@ +/** + * 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.io.UnsupportedEncodingException; +import javax.script.ScriptEngine; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** Tests related to loading resources from the VM. + * + * @author Jaroslav Tulach + */ +public class ResourcesWithExtensionsTest { + @Test public void checkHello() throws Exception { + String exp = "Hello "; + + assertExec("Loading a precompiled resource:", + Resources.class, "loadJustHello__Ljava_lang_String_2", + exp + ); + } + + @Test public void checkHelloWorld() throws Exception { + String exp = "Hello World!"; + + assertExec("Loading precompiled resources:", + Resources.class, "loadHello__Ljava_lang_String_2", + exp + ); + } + + private static TestVM code; + + @BeforeClass + public static void compileTheCode() throws Exception { + StringBuilder sb = new StringBuilder(); + ScriptEngine[] eng = { null }; + code = TestVM.compileClassAsExtension(sb, eng, + "org/apidesign/vm4brwsr/Resources", + "META-INF/ahoj", "Hello " + ); + code = TestVM.compileClassAsExtension(sb, eng, + "org/apidesign/vm4brwsr/Resources", + "META-INF/ahoj", "World!" + ); + } + @AfterClass + public static void releaseTheCode() { + code = null; + } + + private void assertExec( + String msg, Class clazz, String method, + Object ret, Object... args + ) throws Exception { + code.assertExec(msg, clazz, method, ret, args); + } + + public static String parseBase64Binary(String s) throws UnsupportedEncodingException { + final byte[] arr = javax.xml.bind.DatatypeConverter.parseBase64Binary(s); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < arr.length; i++) { + int ch = arr[i]; + sb.append((char)ch); + } + return sb.toString(); + } +} diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Tue May 06 17:46:47 2014 +0200 @@ -17,6 +17,7 @@ */ package org.apidesign.vm4brwsr; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -135,6 +136,82 @@ return null; } } + + static TestVM compileClassAsExtension( + StringBuilder sb, ScriptEngine[] eng, + String name, final String resourceName, final String resourceContent + ) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + if (eng[0] == null) { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + eng[0] = js; + Bck2Brwsr.generate(sb, new EmulationResources()); + } + Bck2Brwsr b2b = Bck2Brwsr.newCompiler(). + resources(new EmulationResources() { + @Override + public InputStream get(String name) throws IOException { + if (name.equals(resourceName)) { + return new ByteArrayInputStream(resourceContent.getBytes("UTF-8")); + } + return super.get(name); + } + }). + addRootClasses(name).library(true); + if (resourceName != null) { + b2b = b2b.addResources(resourceName); + } + b2b.generate(sb); + try { + defineAtoB(eng[0]); + Object res = eng[0].eval(sb.toString()); + assertTrue(eng[0] instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) eng[0], sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex); + return null; + } + } + + static TestVM compileClassAndResources(StringBuilder sb, ScriptEngine[] eng, String name, String... resources) throws ScriptException, IOException { + if (sb == null) { + sb = new StringBuilder(); + } + Bck2Brwsr b2b = Bck2Brwsr.newCompiler(). + resources(new EmulationResources()). + addRootClasses(name). + addResources(resources). + library(false); + b2b.generate(sb); + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine js = sem.getEngineByExtension("js"); + if (eng != null) { + eng[0] = js; + } + try { + defineAtoB(js); + + Object res = js.eval(sb.toString()); + assertTrue(js instanceof Invocable, "It is invocable object: " + res); + return new TestVM((Invocable) js, sb); + } catch (Exception ex) { + if (sb.length() > 2000) { + sb = dumpJS(sb); + } + fail("Could not evaluate:" + ex.getClass() + ":" + ex.getMessage() + "\n" + sb, ex); + return null; + } + } + + private static void defineAtoB(ScriptEngine js) throws ScriptException { + js.eval("atob = function(s) { return new String(org.apidesign.vm4brwsr.ResourcesTest.parseBase64Binary(s)); }"); + } Object loadClass(String loadClass, String name) throws ScriptException, NoSuchMethodException { return code.invokeMethod(bck2brwsr, "loadClass", Exceptions.class.getName()); diff -r 5171ac3b4232 -r ae8575329e1b rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/VMinVM.java Tue May 06 17:46:47 2014 +0200 @@ -43,9 +43,4 @@ @Override protected void requireScript(String resourcePath) { } - - @Override - String getVMObject() { - return "global"; - } } diff -r 5171ac3b4232 -r ae8575329e1b rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Wed Apr 30 09:26:28 2014 +0200 +++ b/rt/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Tue May 06 17:46:47 2014 +0200 @@ -43,6 +43,7 @@ private final boolean fail; private final HtmlFragment html; private final Http.Resource[] http; + private final InvocationContext c; Object value; Bck2BrwsrCase(Method m, String type, Launcher l, boolean fail, HtmlFragment html, Http.Resource[] http) { @@ -52,12 +53,12 @@ this.fail = fail; this.html = html; this.http = http; + this.c = l != null ? l.createInvocation(m.getDeclaringClass(), m.getName()) : null; } @Test(groups = "run") public void executeCode() throws Throwable { if (l != null) { - InvocationContext c = l.createInvocation(m.getDeclaringClass(), m.getName()); if (html != null) { c.setHtmlFragment(html.value()); }