# HG changeset patch # User Lubomir Nerad # Date 1358515642 -3600 # Node ID 95b68e21a53f7f445b7b29fdd151be878bb53746 # Parent 1a73c6e08cc52fe77522f4cd27254de14ff1bb5a# Parent a2871a3fd4c5e0450ee50cdb7e507721fa4d9d65 Merge with trunk diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/jsTestDriver.conf --- a/benchmarks/matrix-multiplication/jsTestDriver.conf Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -server: http://localhost:9876 - -load: - - target/classes/org/apidesign/benchmark/matrixmul/*.js - diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/nbactions.xml --- a/benchmarks/matrix-multiplication/nbactions.xml Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ - - - - - run - - process-classes - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec - - - diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/pom.xml --- a/benchmarks/matrix-multiplication/pom.xml Fri Jan 18 14:23:18 2013 +0100 +++ b/benchmarks/matrix-multiplication/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -25,62 +25,9 @@ 1.7 - - - org.apidesign.bck2brwsr - mojo - 0.3-SNAPSHOT - - - - j2js - - - - - - - - run-benchmarks - - bck2brwsr.runBenchmarks.browsers - - - - - - - com.googlecode.jstd-maven-plugin - jstd-maven-plugin - 1.3.2.5 - - 9876 - ${bck2brwsr.runBenchmarks.browsers} - 720 - all - jsTestDriver.conf - true - ${basedir}/target/testOutput - - - - - run-tests - - test - - - - - - - - - - org.apidesign.bck2brwsr @@ -88,24 +35,22 @@ 0.3-SNAPSHOT - com.googlecode.jstd-maven-plugin - jstd-maven-plugin - 1.3.2.5 - test + org.testng + testng + 6.5.2 + test + + + junit + junit + + + + + org.apidesign.bck2brwsr + vmtest + 0.3-SNAPSHOT + test - - - - jstd-maven-plugin google code repo - http://jstd-maven-plugin.googlecode.com/svn/maven2 - - - - - jstd-maven-plugin google code repo - http://jstd-maven-plugin.googlecode.com/svn/maven2 - - - diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Main.java --- a/benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Main.java Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.benchmark.matrixmul; - -public class Main { - - public static final int ITERATION_COUNT = 100000; - - public static void main(String[] args) { - Matrix m1 = new Matrix(5); - Matrix m2 = new Matrix(5); - - m1.generateData(); - m2.generateData(); - - //m1.printOn(System.out); - //System.out.println("x"); - //m2.printOn(System.out); - - for (int i = 0; i < ITERATION_COUNT; i++) { - m1.multiply(m2); - } - - //System.out.println("="); - //m1.printOn(System.out); - } -} diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Matrix.java --- a/benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Matrix.java Fri Jan 18 14:23:18 2013 +0100 +++ b/benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Matrix.java Fri Jan 18 14:27:22 2013 +0100 @@ -17,17 +17,20 @@ */ package org.apidesign.benchmark.matrixmul; -//import java.io.PrintStream; -//import java.util.Random; +import java.io.IOException; +import java.util.Arrays; public class Matrix { - private final int rank; - private float data[][]; + private final float data[][]; public Matrix(int r) { - rank = r; - data = new float[r][r]; + this(r, new float[r][r]); + } + + private Matrix(int r, float[][] data) { + this.rank = r; + this.data = data; } public void setElement(int i, int j, float value) { @@ -62,20 +65,44 @@ res[i][j] = ij; } } - data = res; - - return this; + return new Matrix(rank, res); } - /* - public void printOn(PrintStream s) { + public void printOn(Appendable s) throws IOException { for (int i = 0; i < rank; i++) { + String sep = ""; for (int j = 0; j < rank; j++) { - s.printf("%f ", data[i][j]); + s.append(sep + data[i][j]); + sep = " "; } - s.println(); + s.append("\n"); } } - */ - + + @Override + public boolean equals(Object obj) { + if (obj instanceof Matrix) { + Matrix snd = (Matrix)obj; + if (snd.rank != rank) { + return false; + } + for (int i = 0; i < rank; i++) { + for (int j = 0; j < rank; j++) { + if (data[i][j] != snd.data[i][j]) { + return false; + } + } + } + return true; + } + return false; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 97 * hash + this.rank; + hash = 97 * hash + Arrays.deepHashCode(this.data); + return hash; + } } diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/src/main/resources/org/apidesign/benchmark/matrixmul/multiplication-test.js --- a/benchmarks/matrix-multiplication/src/main/resources/org/apidesign/benchmark/matrixmul/multiplication-test.js Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +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. - */ -MatrixTest = TestCase("MatrixTest"); - - -MatrixTest.prototype.testMultiplication = function() { - var vm = new bck2brwsr(); - var x = vm.loadClass("org.apidesign.benchmark.matrixmul.Main"); - x.main__V_3Ljava_lang_String_2(null); -}; diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/matrix-multiplication/src/test/java/org/apidesign/benchmark/matrixmul/MatrixTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/src/test/java/org/apidesign/benchmark/matrixmul/MatrixTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,61 @@ +/** + * 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.benchmark.matrixmul; + +import java.io.IOException; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class MatrixTest { + public static final int ITERATION_COUNT = 10; + + public MatrixTest() { + } + + @Compare public String tenThousandIterations() throws IOException { + + Matrix m1 = new Matrix(5); + Matrix m2 = new Matrix(5); + + m1.generateData(); + m2.generateData(); + + Matrix res = null; + for (int i = 0; i < ITERATION_COUNT; i++) { + Matrix m = m1.multiply(m2); + if (res != null && !res.equals(m)) { + return "different"; + } + res = m; + } + + StringBuilder sb = new StringBuilder(); + res.printOn(sb); + return sb.toString(); + } + + @Factory + public static Object[] create() { + return VMTest.create(MatrixTest.class); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f benchmarks/run-firefox.sh --- a/benchmarks/run-firefox.sh Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#!/bin/bash -# -# 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. -# - -/usr/bin/firefox --display=`echo $DISPLAY` "$@" diff -r 1a73c6e08cc5 -r 95b68e21a53f core/pom.xml --- a/core/pom.xml Fri Jan 18 14:23:18 2013 +0100 +++ b/core/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -19,8 +19,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 @@ -35,6 +35,10 @@ 3.8.1 test + + org.netbeans.api + org-openide-util-lookup + Contains esential annotations for associating JavaScript code with methods and classes. diff -r 1a73c6e08cc5 -r 95b68e21a53f core/src/main/java/org/apidesign/bck2brwsr/core/impl/JavaScriptProcesor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/src/main/java/org/apidesign/bck2brwsr/core/impl/JavaScriptProcesor.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,92 @@ +/** + * 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.impl; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Completion; +import javax.annotation.processing.Completions; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.tools.Diagnostic; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Jaroslav Tulach + */ +@ServiceProvider(service = Processor.class) +public final class JavaScriptProcesor extends AbstractProcessor { + @Override + public Set getSupportedAnnotationTypes() { + Set set = new HashSet<>(); + set.add(JavaScriptBody.class.getName()); + return set; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(JavaScriptBody.class)) { + if (e.getKind() != ElementKind.METHOD && e.getKind() != ElementKind.CONSTRUCTOR) { + continue; + } + ExecutableElement ee = (ExecutableElement)e; + List params = ee.getParameters(); + + JavaScriptBody jsb = e.getAnnotation(JavaScriptBody.class); + String[] arr = jsb.args(); + if (params.size() != arr.length) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Number of args arguments does not match real arguments!", e); + } + } + return true; + } + + @Override + public Iterable getCompletions(Element e, + AnnotationMirror annotation, ExecutableElement member, String userText + ) { + StringBuilder sb = new StringBuilder(); + if (e.getKind() == ElementKind.METHOD && member.getSimpleName().contentEquals("args")) { + ExecutableElement ee = (ExecutableElement) e; + String sep = ""; + sb.append("{ "); + for (VariableElement ve : ee.getParameters()) { + sb.append(sep).append('"').append(ve.getSimpleName()) + .append('"'); + sep = ", "; + } + sb.append(" }"); + return Collections.nCopies(1, Completions.of(sb.toString())); + } + return null; + } + + +} diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/AbstractStringBuilder.java --- a/emul/src/main/java/java/lang/AbstractStringBuilder.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/AbstractStringBuilder.java Fri Jan 18 14:27:22 2013 +0100 @@ -1271,8 +1271,7 @@ * null. */ public int indexOf(String str, int fromIndex) { - return String.indexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return toString().indexOf(str, fromIndex); } /** diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/Character.java --- a/emul/src/main/java/java/lang/Character.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/Character.java Fri Jan 18 14:27:22 2013 +0100 @@ -25,6 +25,8 @@ package java.lang; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + /** * The {@code Character} class wraps a value of the primitive * type {@code char} in an object. An object of type @@ -1525,7 +1527,7 @@ * @see Character#getType(char) */ public static boolean isLowerCase(char ch) { - throw new UnsupportedOperationException(); + return ch == toLowerCase(ch); } /** @@ -1560,7 +1562,7 @@ * @since 1.0 */ public static boolean isUpperCase(char ch) { - throw new UnsupportedOperationException(); + return ch == toUpperCase(ch); } /** @@ -1676,7 +1678,7 @@ * @see Character#getType(char) */ public static boolean isDigit(char ch) { - return isDigit((int)ch); + return String.valueOf(ch).matches("\\d"); } /** @@ -1710,8 +1712,11 @@ * @since 1.5 */ public static boolean isDigit(int codePoint) { - return getType(codePoint) == Character.DECIMAL_DIGIT_NUMBER; + return fromCodeChars(codePoint).matches("\\d"); } + + @JavaScriptBody(args = "c", body = "return String.fromCharCode(c);") + private native static String fromCodeChars(int codePoint); /** * Determines if a character is defined in Unicode. @@ -1802,7 +1807,7 @@ * @see Character#isUpperCase(char) */ public static boolean isLetter(char ch) { - return isLetter((int)ch); + return String.valueOf(ch).matches("\\w") && !isDigit(ch); } /** @@ -1835,12 +1840,7 @@ * @since 1.5 */ public static boolean isLetter(int codePoint) { - return ((((1 << Character.UPPERCASE_LETTER) | - (1 << Character.LOWERCASE_LETTER) | - (1 << Character.TITLECASE_LETTER) | - (1 << Character.MODIFIER_LETTER) | - (1 << Character.OTHER_LETTER)) >> getType(codePoint)) & 1) - != 0; + return fromCodeChars(codePoint).matches("\\w") && !isDigit(codePoint); } /** @@ -1868,7 +1868,7 @@ * @since 1.0.2 */ public static boolean isLetterOrDigit(char ch) { - return isLetterOrDigit((int)ch); + return String.valueOf(ch).matches("\\w"); } /** @@ -1889,13 +1889,7 @@ * @since 1.5 */ public static boolean isLetterOrDigit(int codePoint) { - return ((((1 << Character.UPPERCASE_LETTER) | - (1 << Character.LOWERCASE_LETTER) | - (1 << Character.TITLECASE_LETTER) | - (1 << Character.MODIFIER_LETTER) | - (1 << Character.OTHER_LETTER) | - (1 << Character.DECIMAL_DIGIT_NUMBER)) >> getType(codePoint)) & 1) - != 0; + return fromCodeChars(codePoint).matches("\\w"); } static int getType(int x) { @@ -1930,7 +1924,7 @@ * @see String#toLowerCase() */ public static char toLowerCase(char ch) { - throw new UnsupportedOperationException(); + return String.valueOf(ch).toLowerCase().charAt(0); } /** @@ -1961,7 +1955,7 @@ * @see String#toUpperCase() */ public static char toUpperCase(char ch) { - throw new UnsupportedOperationException(); + return String.valueOf(ch).toUpperCase().charAt(0); } /** diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/Class.java --- a/emul/src/main/java/java/lang/Class.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/Class.java Fri Jan 18 14:27:22 2013 +0100 @@ -25,6 +25,7 @@ package java.lang; +import java.io.ByteArrayInputStream; import org.apidesign.bck2brwsr.emul.AnnotationImpl; import java.io.InputStream; import java.lang.annotation.Annotation; @@ -32,6 +33,7 @@ import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.emul.MethodImpl; /** * Instances of the class {@code Class} represent classes and @@ -143,9 +145,31 @@ * @exception ClassNotFoundException if the class cannot be located */ public static Class forName(String className) - throws ClassNotFoundException { - throw new UnsupportedOperationException(); + throws ClassNotFoundException { + if (className.startsWith("[")) { + Class arrType = defineArray(className); + Class c = arrType; + while (c != null && c.isArray()) { + c = c.getComponentType0(); // verify component type is sane + } + return arrType; + } + Class c = loadCls(className, className.replace('.', '_')); + if (c == null) { + throw new ClassNotFoundException(className); + } + return c; } + + @JavaScriptBody(args = {"n", "c" }, body = + "if (vm[c]) return vm[c].$class;\n" + + "if (vm.loadClass) {\n" + + " vm.loadClass(n);\n" + + " if (vm[c]) return vm[c].$class;\n" + + "}\n" + + "return null;" + ) + private static native Class loadCls(String n, String c); /** @@ -195,15 +219,32 @@ * * */ - @JavaScriptBody(args = "self", body = - "var inst = self.cnstr();" - + "inst.cons__V(inst);" - + "return inst;" + @JavaScriptBody(args = { "self", "illegal" }, body = + "\nvar c = self.cnstr;" + + "\nif (c['cons__V']) {" + + "\n if ((c.cons__V.access & 0x1) != 0) {" + + "\n var inst = c();" + + "\n c.cons__V.call(inst);" + + "\n return inst;" + + "\n }" + + "\n return illegal;" + + "\n}" + + "\nreturn null;" ) + private static native Object newInstance0(Class self, Object illegal); + public T newInstance() throws InstantiationException, IllegalAccessException { - throw new UnsupportedOperationException(); + Object illegal = new Object(); + Object inst = newInstance0(this, illegal); + if (inst == null) { + throw new InstantiationException(getName()); + } + if (inst == illegal) { + throw new IllegalAccessException(); + } + return (T)inst; } /** @@ -236,7 +277,15 @@ * * @since JDK1.1 */ - public native boolean isInstance(Object obj); + public boolean isInstance(Object obj) { + String prop = "$instOf_" + getName().replace('.', '_'); + return hasProperty(obj, prop); + } + + @JavaScriptBody(args = { "who", "prop" }, body = + "if (who[prop]) return true; else return false;" + ) + private static native boolean hasProperty(Object who, String prop); /** @@ -273,7 +322,12 @@ * @return {@code true} if this object represents an interface; * {@code false} otherwise. */ - public native boolean isInterface(); + public boolean isInterface() { + return (getAccess() & 0x200) != 0; + } + + @JavaScriptBody(args = {}, body = "return this.access;") + private native int getAccess(); /** @@ -284,7 +338,7 @@ * @since JDK1.1 */ public boolean isArray() { - return false; + return hasProperty(this, "array"); // NOI18N } @@ -316,6 +370,10 @@ * @see java.lang.Void#TYPE * @since JDK1.1 */ + @JavaScriptBody(args = {}, body = + "if (this.primitive) return true;" + + "else return false;" + ) public native boolean isPrimitive(); /** @@ -397,7 +455,7 @@ return jvmName().replace('/', '.'); } - @JavaScriptBody(args = "self", body = "return self.jvmName;") + @JavaScriptBody(args = {}, body = "return this.jvmName;") private native String jvmName(); @@ -431,7 +489,7 @@ * * @return the superclass of the class represented by this object. */ - @JavaScriptBody(args = "self", body = "return self.superclass;") + @JavaScriptBody(args = {}, body = "return this.superclass;") public native Class getSuperclass(); /** @@ -617,7 +675,7 @@ * @since JDK1.1 */ public Method[] getMethods() throws SecurityException { - return Method.findMethods(this); + return MethodImpl.findMethods(this, 0x01); } /** @@ -747,10 +805,18 @@ * @since JDK1.1 */ public Method getMethod(String name, Class... parameterTypes) - throws SecurityException { - Method m = Method.findMethod(this, name, parameterTypes); + throws SecurityException, NoSuchMethodException { + Method m = MethodImpl.findMethod(this, name, parameterTypes); if (m == null) { - throw new SecurityException(); // XXX: NoSuchMethodException + StringBuilder sb = new StringBuilder(); + sb.append(getName()).append('.').append(name).append('('); + String sep = ""; + for (int i = 0; i < parameterTypes.length; i++) { + sb.append(sep).append(parameterTypes[i].getName()); + sep = ", "; + } + sb.append(')'); + throw new NoSuchMethodException(sb.toString()); } return m; } @@ -832,13 +898,14 @@ */ public InputStream getResourceAsStream(String name) { name = resolveName(name); - ClassLoader cl = getClassLoader0(); - if (cl==null) { - // A system class. - return ClassLoader.getSystemResourceAsStream(name); - } - return cl.getResourceAsStream(name); - } + byte[] arr = getResourceAsStream0(name); + return arr == null ? null : new ByteArrayInputStream(arr); + } + + @JavaScriptBody(args = "name", body = + "return (vm.loadBytes) ? vm.loadBytes(name) : null;" + ) + private static native byte[] getResourceAsStream0(String name); /** * Finds a resource with a given name. The rules for searching resources @@ -876,7 +943,7 @@ */ public java.net.URL getResource(String name) { name = resolveName(name); - ClassLoader cl = getClassLoader0(); + ClassLoader cl = null; if (cl==null) { // A system class. return ClassLoader.getSystemResource(name); @@ -940,9 +1007,6 @@ throw new SecurityException(); } - // Package-private to allow ClassLoader access - native ClassLoader getClassLoader0(); - /** * Returns the {@code Class} representing the component type of an * array. If this class does not represent an array class this method @@ -954,9 +1018,59 @@ * @since JDK1.1 */ public Class getComponentType() { + if (isArray()) { + try { + return getComponentType0(); + } catch (ClassNotFoundException cnfe) { + throw new IllegalStateException(cnfe); + } + } return null; } + private Class getComponentType0() throws ClassNotFoundException { + String n = getName().substring(1); + switch (n.charAt(0)) { + case 'L': + n = n.substring(1, n.length() - 1); + return Class.forName(n); + case 'I': + return Integer.TYPE; + case 'J': + return Long.TYPE; + case 'D': + return Double.TYPE; + case 'F': + return Float.TYPE; + case 'B': + return Byte.TYPE; + case 'Z': + return Boolean.TYPE; + case 'S': + return Short.TYPE; + case 'V': + return Void.TYPE; + case 'C': + return Character.TYPE; + case '[': + return defineArray(n); + default: + throw new ClassNotFoundException("Unknown component type of " + getName()); + } + } + + @JavaScriptBody(args = { "sig" }, body = + "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" + + "return c;" + ) + private static native Class defineArray(String sig); + /** * Returns true if and only if this class was declared as an enum in the * source code. @@ -1022,10 +1136,10 @@ throw new ClassCastException(this.toString()); } - @JavaScriptBody(args = { "self", "ac" }, + @JavaScriptBody(args = { "ac" }, body = - "if (self.anno) {" - + " return self.anno['L' + ac.jvmName + ';'];" + "if (this.anno) {" + + " return this.anno['L' + ac.jvmName + ';'];" + "} else return null;" ) private Object getAnnotationData(Class annotationClass) { @@ -1044,8 +1158,8 @@ * @throws NullPointerException {@inheritDoc} * @since 1.5 */ - @JavaScriptBody(args = { "self", "ac" }, - body = "if (self.anno && self.anno['L' + ac.jvmName + ';']) { return true; }" + @JavaScriptBody(args = { "ac" }, + body = "if (this.anno && this.anno['L' + ac.jvmName + ';']) { return true; }" + "else return false;" ) public boolean isAnnotationPresent( @@ -1056,7 +1170,7 @@ return getAnnotation(annotationClass) != null; } - @JavaScriptBody(args = "self", body = "return self.anno;") + @JavaScriptBody(args = {}, body = "return this.anno;") private Object getAnnotationData() { throw new UnsupportedOperationException(); } @@ -1076,10 +1190,13 @@ throw new UnsupportedOperationException(); } - static Class getPrimitiveClass(String type) { - // XXX - return Object.class; - } + @JavaScriptBody(args = "type", body = "" + + "var c = vm.java_lang_Class(true);" + + "c.jvmName = type;" + + "c.primitive = true;" + + "return c;" + ) + native static Class getPrimitiveClass(String type); public boolean desiredAssertionStatus() { return false; diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/NoSuchMethodException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/src/main/java/java/lang/NoSuchMethodException.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown when a particular method cannot be found. + * + * @author unascribed + * @since JDK1.0 + */ +public +class NoSuchMethodException extends ReflectiveOperationException { + private static final long serialVersionUID = 5034388446362600923L; + + /** + * Constructs a NoSuchMethodException without a detail message. + */ + public NoSuchMethodException() { + super(); + } + + /** + * Constructs a NoSuchMethodException with a detail message. + * + * @param s the detail message. + */ + public NoSuchMethodException(String s) { + super(s); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/Number.java --- a/emul/src/main/java/java/lang/Number.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/Number.java Fri Jan 18 14:27:22 2013 +0100 @@ -25,6 +25,8 @@ package java.lang; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; + /** * The abstract class Number is the superclass of classes * BigDecimal, BigInteger, @@ -46,6 +48,10 @@ * @see java.lang.Short * @since JDK1.0 */ +@ExtraJavaScript( + resource="/org/apidesign/vm4brwsr/emul/java_lang_Number.js", + processByteCode=true +) public abstract class Number implements java.io.Serializable { /** * Returns the value of the specified number as an int. diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/Object.java --- a/emul/src/main/java/java/lang/Object.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/Object.java Fri Jan 18 14:27:22 2013 +0100 @@ -66,7 +66,7 @@ * @see Class Literals, section 15.8.2 of * The Java™ Language Specification. */ - @JavaScriptBody(args="self", body="return self.constructor.$class;") + @JavaScriptBody(args={}, body="return this.constructor.$class;") public final native Class getClass(); /** @@ -104,8 +104,16 @@ * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ + @JavaScriptBody(args = {}, body = + "if (this.$hashCode) return this.$hashCode;\n" + + "var h = this.computeHashCode__I();\n" + + "return this.$hashCode = h & h;" + ) public native int hashCode(); + @JavaScriptBody(args = {}, body = "Math.random() * Math.pow(2, 32);") + native int computeHashCode(); + /** * Indicates whether some other object is "equal to" this one. *

@@ -216,7 +224,28 @@ * be cloned. * @see java.lang.Cloneable */ - protected native Object clone() throws CloneNotSupportedException; + protected Object clone() throws CloneNotSupportedException { + Object ret = clone(this); + if (ret == null) { + throw new CloneNotSupportedException(getClass().getName()); + } + return ret; + } + + @JavaScriptBody(args = "self", body = + "\nif (!self.$instOf_java_lang_Cloneable) {" + + "\n return null;" + + "\n} else {" + + "\n var clone = self.constructor(true);" + + "\n var props = Object.getOwnPropertyNames(self);" + + "\n for (var i = 0; i < props.length; i++) {" + + "\n var p = props[i];" + + "\n clone[p] = self[p];" + + "\n };" + + "\n return clone;" + + "\n}" + ) + private static native Object clone(Object self) throws CloneNotSupportedException; /** * Returns a string representation of the object. In general, the diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/String.java --- a/emul/src/main/java/java/lang/String.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/String.java Fri Jan 18 14:27:22 2013 +0100 @@ -108,10 +108,6 @@ public final class String implements java.io.Serializable, Comparable, CharSequence { - @JavaScriptOnly - /** Cache the hash code for the string */ - private int hash; // Default to 0 - /** real string to delegate to */ private Object r; @@ -173,11 +169,11 @@ * @param value * The initial value of the string */ - @JavaScriptBody(args = { "self", "charArr" }, body= + @JavaScriptBody(args = { "charArr" }, body= "for (var i = 0; i < charArr.length; i++) {\n" + " if (typeof charArr[i] === 'number') charArr[i] = String.fromCharCode(charArr[i]);\n" + "}\n" - + "self.fld_r = charArr.join('');\n" + + "this.fld_r = charArr.join('');\n" ) public String(char value[]) { } @@ -203,12 +199,12 @@ * If the {@code offset} and {@code count} arguments index * characters outside the bounds of the {@code value} array */ - @JavaScriptBody(args = { "self", "charArr", "off", "cnt" }, body = + @JavaScriptBody(args = { "charArr", "off", "cnt" }, body = "var up = off + cnt;\n" + "for (var i = off; i < up; i++) {\n" + " if (typeof charArr[i] === 'number') charArr[i] = String.fromCharCode(charArr[i]);\n" + "}\n" + - "self.fld_r = charArr.slice(off, up).join(\"\");\n" + "this.fld_r = charArr.slice(off, up).join(\"\");\n" ) public String(char value[], int offset, int count) { } @@ -622,7 +618,7 @@ * @return the length of the sequence of characters represented by this * object. */ - @JavaScriptBody(args = "self", body = "return self.toString().length;") + @JavaScriptBody(args = {}, body = "return this.toString().length;") public int length() { throw new UnsupportedOperationException(); } @@ -635,7 +631,7 @@ * * @since 1.6 */ - @JavaScriptBody(args = "self", body="return self.toString().length === 0;") + @JavaScriptBody(args = {}, body="return this.toString().length === 0;") public boolean isEmpty() { return length() == 0; } @@ -658,8 +654,8 @@ * argument is negative or not less than the length of this * string. */ - @JavaScriptBody(args = { "self", "index" }, - body = "return self.toString().charCodeAt(index);" + @JavaScriptBody(args = { "index" }, + body = "return this.toString().charCodeAt(index);" ) public char charAt(int index) { throw new UnsupportedOperationException(); @@ -784,8 +780,8 @@ * Copy characters from this string into dst starting at dstBegin. * This method doesn't perform any range checking. */ - @JavaScriptBody(args = { "self", "arr", "to" }, body = - "var s = self.toString();\n" + + @JavaScriptBody(args = { "arr", "to" }, body = + "var s = this.toString();\n" + "for (var i = 0; i < s.length; i++) {\n" + " arr[to++] = s[i];\n" + "}" @@ -824,8 +820,8 @@ *

  • dstBegin+(srcEnd-srcBegin) is larger than * dst.length */ - @JavaScriptBody(args = { "self", "beg", "end", "arr", "dst" }, body= - "var s = self.toString();\n" + + @JavaScriptBody(args = { "beg", "end", "arr", "dst" }, body= + "var s = this.toString();\n" + "while (beg < end) {\n" + " arr[dst++] = s[beg++];\n" + "}\n" @@ -997,9 +993,9 @@ * @see #compareTo(String) * @see #equalsIgnoreCase(String) */ - @JavaScriptBody(args = { "self", "obj" }, body = + @JavaScriptBody(args = { "obj" }, body = "return obj.$instOf_java_lang_String && " - + "self.toString() === obj.toString();" + + "this.toString() === obj.toString();" ) public boolean equals(Object anObject) { if (this == anObject) { @@ -1424,9 +1420,9 @@ * this.substring(toffset).startsWith(prefix) * */ - @JavaScriptBody(args = { "self", "find", "from" }, body= + @JavaScriptBody(args = { "find", "from" }, body= "find = find.toString();\n" + - "return self.toString().substring(from, from + find.length) === find;\n" + "return this.toString().substring(from, from + find.length) === find;\n" ) public boolean startsWith(String prefix, int toffset) { char ta[] = toCharArray(); @@ -1491,26 +1487,18 @@ * * @return a hash code value for this object. */ - @JavaScriptBody(args = "self", body = - "var h = 0;\n" + - "var s = self.toString();\n" + - "for (var i = 0; i < s.length; i++) {\n" + - " var high = (h >> 16) & 0xffff, low = h & 0xffff;\n" + - " h = (((((31 * high) & 0xffff) << 16) >>> 0) + (31 * low) + s.charCodeAt(i)) & 0xffffffff;\n" + - "}\n" + - "return h;\n" - ) public int hashCode() { - int h = hash; + return super.hashCode(); + } + int computeHashCode() { + int h = 0; if (h == 0 && length() > 0) { int off = offset(); - char val[] = toCharArray(); int len = length(); for (int i = 0; i < len; i++) { - h = 31*h + val[off++]; + h = 31*h + charAt(off++); } - hash = h; } return h; } @@ -1582,9 +1570,9 @@ * than or equal to fromIndex, or -1 * if the character does not occur. */ - @JavaScriptBody(args = { "self", "ch", "from" }, body = + @JavaScriptBody(args = { "ch", "from" }, body = "if (typeof ch === 'number') ch = String.fromCharCode(ch);\n" + - "return self.toString().indexOf(ch, from);\n" + "return this.toString().indexOf(ch, from);\n" ) public int indexOf(int ch, int fromIndex) { if (fromIndex < 0) { @@ -1691,9 +1679,9 @@ * than or equal to fromIndex, or -1 * if the character does not occur before that point. */ - @JavaScriptBody(args = { "self", "ch", "from" }, body = + @JavaScriptBody(args = { "ch", "from" }, body = "if (typeof ch === 'number') ch = String.fromCharCode(ch);\n" + - "return self.toString().lastIndexOf(ch, from);" + "return this.toString().lastIndexOf(ch, from);" ) public int lastIndexOf(int ch, int fromIndex) { if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { @@ -1766,63 +1754,10 @@ * starting at the specified index, * or {@code -1} if there is no such occurrence. */ - @JavaScriptBody(args = { "self", "str", "fromIndex" }, body = - "return self.toString().indexOf(str.toString(), fromIndex);" + @JavaScriptBody(args = { "str", "fromIndex" }, body = + "return this.toString().indexOf(str.toString(), fromIndex);" ) - public int indexOf(String str, int fromIndex) { - return indexOf(toCharArray(), offset(), length(), str.toCharArray(), str.offset(), str.length(), fromIndex); - } - - /** - * Code shared by String and StringBuffer to do searches. The - * source is the character array being searched, and the target - * is the string being searched for. - * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param targetOffset offset of the target string. - * @param targetCount count of the target string. - * @param fromIndex the index to begin searching from. - */ - static int indexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { - if (fromIndex >= sourceCount) { - return (targetCount == 0 ? sourceCount : -1); - } - if (fromIndex < 0) { - fromIndex = 0; - } - if (targetCount == 0) { - return fromIndex; - } - - char first = target[targetOffset]; - int max = sourceOffset + (sourceCount - targetCount); - - for (int i = sourceOffset + fromIndex; i <= max; i++) { - /* Look for first character. */ - if (source[i] != first) { - while (++i <= max && source[i] != first); - } - - /* Found first character, now look at the rest of v2 */ - if (i <= max) { - int j = i + 1; - int end = j + targetCount - 1; - for (int k = targetOffset + 1; j < end && source[j] == - target[k]; j++, k++); - - if (j == end) { - /* Found whole string. */ - return i - sourceOffset; - } - } - } - return -1; - } + public native int indexOf(String str, int fromIndex); /** * Returns the index within this string of the last occurrence of the @@ -1859,8 +1794,8 @@ * searching backward from the specified index, * or {@code -1} if there is no such occurrence. */ - @JavaScriptBody(args = { "self", "s", "from" }, body = - "return self.toString().lastIndexOf(s.toString(), from);" + @JavaScriptBody(args = { "s", "from" }, body = + "return this.toString().lastIndexOf(s.toString(), from);" ) public int lastIndexOf(String str, int fromIndex) { return lastIndexOf(toCharArray(), offset(), length(), str.toCharArray(), str.offset(), str.length(), fromIndex); @@ -1968,8 +1903,8 @@ * beginIndex is larger than * endIndex. */ - @JavaScriptBody(args = { "self", "beginIndex", "endIndex" }, body = - "return self.toString().substring(beginIndex, endIndex);" + @JavaScriptBody(args = { "beginIndex", "endIndex" }, body = + "return this.toString().substring(beginIndex, endIndex);" ) public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { @@ -2077,10 +2012,10 @@ * @return a string derived from this string by replacing every * occurrence of oldChar with newChar. */ - @JavaScriptBody(args = { "self", "arg1", "arg2" }, body = + @JavaScriptBody(args = { "arg1", "arg2" }, body = "if (typeof arg1 === 'number') arg1 = String.fromCharCode(arg1);\n" + "if (typeof arg2 === 'number') arg2 = String.fromCharCode(arg2);\n" + - "var s = self.toString();\n" + + "var s = this.toString();\n" + "for (;;) {\n" + " var ret = s.replace(arg1, arg2);\n" + " if (ret === s) {\n" + @@ -2143,6 +2078,12 @@ * @since 1.4 * @spec JSR-51 */ + @JavaScriptBody(args = { "regex" }, body = + "var self = this.toString();\n" + + "var re = new RegExp(regex.toString());\n" + + "var r = re.exec(self);\n" + + "return r != null && r.length > 0 && self.length == r[0].length;" + ) public boolean matches(String regex) { throw new UnsupportedOperationException(); } @@ -2555,6 +2496,7 @@ * @return the String, converted to lowercase. * @see java.lang.String#toLowerCase(Locale) */ + @JavaScriptBody(args = {}, body = "return this.toLowerCase();") public String toLowerCase() { throw new UnsupportedOperationException("Should be supported but without connection to locale"); } @@ -2720,6 +2662,7 @@ * @return the String, converted to uppercase. * @see java.lang.String#toUpperCase(Locale) */ + @JavaScriptBody(args = {}, body = "return this.toUpperCase();") public String toUpperCase() { throw new UnsupportedOperationException(); } @@ -2775,7 +2718,7 @@ * * @return the string itself. */ - @JavaScriptBody(args = "self", body = "return self.toString();") + @JavaScriptBody(args = {}, body = "return this.toString();") public String toString() { return this; } @@ -2787,7 +2730,6 @@ * of this string and whose contents are initialized to contain * the character sequence represented by this string. */ - @JavaScriptBody(args = "self", body = "return self.toString().split('');") public char[] toCharArray() { char result[] = new char[length()]; getChars(0, length(), result, 0); diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/StringBuffer.java --- a/emul/src/main/java/java/lang/StringBuffer.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/StringBuffer.java Fri Jan 18 14:27:22 2013 +0100 @@ -527,8 +527,7 @@ * @since 1.4 */ public synchronized int indexOf(String str, int fromIndex) { - return String.indexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return super.indexOf(str, fromIndex); } /** diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/StringBuilder.java --- a/emul/src/main/java/java/lang/StringBuilder.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/StringBuilder.java Fri Jan 18 14:27:22 2013 +0100 @@ -376,8 +376,7 @@ * @throws NullPointerException {@inheritDoc} */ public int indexOf(String str, int fromIndex) { - return String.indexOf(value, 0, count, - str.toCharArray(), 0, str.length(), fromIndex); + return super.indexOf(str, fromIndex); } /** diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/Throwable.java --- a/emul/src/main/java/java/lang/Throwable.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/Throwable.java Fri Jan 18 14:27:22 2013 +0100 @@ -783,7 +783,7 @@ return this; } - @JavaScriptBody(args = { "self", "dummy" }, body = "") + @JavaScriptBody(args = { "dummy" }, body = "") private native Throwable fillInStackTrace(int dummy); /** diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/Void.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/src/main/java/java/lang/Void.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * The {@code Void} class is an uninstantiable placeholder class to hold a + * reference to the {@code Class} object representing the Java keyword + * void. + * + * @author unascribed + * @since JDK1.1 + */ +public final +class Void { + + /** + * The {@code Class} object representing the pseudo-type corresponding to + * the keyword {@code void}. + */ + public static final Class TYPE = Class.getPrimitiveClass("void"); + + /* + * The Void class cannot be instantiated. + */ + private Void() {} +} diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/lang/reflect/Method.java --- a/emul/src/main/java/java/lang/reflect/Method.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/lang/reflect/Method.java Fri Jan 18 14:27:22 2013 +0100 @@ -26,8 +26,10 @@ package java.lang.reflect; import java.lang.annotation.Annotation; +import java.util.Enumeration; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.emul.AnnotationImpl; +import org.apidesign.bck2brwsr.emul.MethodImpl; /** * A {@code Method} provides information about, and access to, a single method @@ -56,7 +58,6 @@ private final String name; private final Object data; private final String sig; - private int modifiers; // Generics infrastructure @@ -108,9 +109,12 @@ * @see Modifier */ public int getModifiers() { - return modifiers; + return getAccess(data); } - + + @JavaScriptBody(args = "self", body = "return self.access;") + private static native int getAccess(Object self); + /** * Returns an array of {@code TypeVariable} objects that represent the * type variables declared by the generic declaration represented by this @@ -137,7 +141,7 @@ * @return the return type for the method this object represents */ public Class getReturnType() { - throw new UnsupportedOperationException(); + return MethodImpl.signatureParser(sig).nextElement(); } /** @@ -179,8 +183,13 @@ * represents */ public Class[] getParameterTypes() { - throw new UnsupportedOperationException(); - //return (Class[]) parameterTypes.clone(); + Class[] arr = new Class[MethodImpl.signatureElements(sig) - 1]; + Enumeration en = MethodImpl.signatureParser(sig); + en.nextElement(); // return type + for (int i = 0; i < arr.length; i++) { + arr[i] = en.nextElement(); + } + return arr; } /** @@ -311,14 +320,14 @@ sb.append(Field.getTypeName(getReturnType())).append(' '); sb.append(Field.getTypeName(getDeclaringClass())).append('.'); sb.append(getName()).append('('); - /* - Class[] params = parameterTypes; // avoid clone + Class[] params = getParameterTypes(); // avoid clone for (int j = 0; j < params.length; j++) { sb.append(Field.getTypeName(params[j])); if (j < (params.length - 1)) sb.append(','); } sb.append(')'); + /* Class[] exceptions = exceptionTypes; // avoid clone if (exceptions.length > 0) { sb.append(" throws "); @@ -488,17 +497,118 @@ * @exception ExceptionInInitializerError if the initialization * provoked by this method fails. */ - @JavaScriptBody(args = { "method", "self", "args" }, body = - "if (args.length > 0) throw 'unsupported now';" - + "return method.fld_data(self);" - ) public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { - throw new UnsupportedOperationException(); + final boolean isStatic = (getModifiers() & Modifier.STATIC) == 0; + if (isStatic && obj == null) { + throw new NullPointerException(); + } + Class[] types = getParameterTypes(); + if (types.length != args.length) { + throw new IllegalArgumentException("Types len " + types.length + " args: " + args.length); + } else { + args = args.clone(); + for (int i = 0; i < types.length; i++) { + Class c = types[i]; + if (c.isPrimitive()) { + args[i] = toPrimitive(c, args[i]); + } + } + } + Object res = invoke0(isStatic, this, obj, args); + if (getReturnType().isPrimitive()) { + res = fromPrimitive(getReturnType(), res); + } + return res; } + + @JavaScriptBody(args = { "st", "method", "self", "args" }, body = + "var p;\n" + + "if (st) {\n" + + " p = new Array(1);\n" + + " p[0] = self;\n" + + " p = p.concat(args);\n" + + "} else {\n" + + " p = args;\n" + + "}\n" + + "return method.fld_data.apply(self, p);\n" + ) + private static native Object invoke0(boolean isStatic, Method m, Object self, Object[] args); + private static Object fromPrimitive(Class type, Object o) { + if (type == Integer.TYPE) { + return fromRaw(Integer.class, "valueOf__Ljava_lang_Integer_2I", o); + } + if (type == Long.TYPE) { + return fromRaw(Long.class, "valueOf__Ljava_lang_Long_2J", o); + } + if (type == Double.TYPE) { + return fromRaw(Double.class, "valueOf__Ljava_lang_Double_2D", o); + } + if (type == Float.TYPE) { + return fromRaw(Float.class, "valueOf__Ljava_lang_Float_2F", o); + } + if (type == Byte.TYPE) { + return fromRaw(Byte.class, "valueOf__Ljava_lang_Byte_2B", o); + } + if (type == Boolean.TYPE) { + return fromRaw(Boolean.class, "valueOf__Ljava_lang_Boolean_2Z", o); + } + if (type == Short.TYPE) { + return fromRaw(Short.class, "valueOf__Ljava_lang_Short_2S", o); + } + if (type == Character.TYPE) { + return fromRaw(Character.class, "valueOf__Ljava_lang_Character_2C", o); + } + if (type.getName().equals("void")) { + return null; + } + throw new IllegalStateException("Can't convert " + o); + } + + @JavaScriptBody(args = { "cls", "m", "o" }, + body = "return cls.cnstr(false)[m](o);" + ) + private static native Integer fromRaw(Class cls, String m, Object o); + + private static Object toPrimitive(Class type, Object o) { + if (type == Integer.TYPE) { + return toRaw("intValue__I", o); + } + if (type == Long.TYPE) { + return toRaw("longValue__J", o); + } + if (type == Double.TYPE) { + return toRaw("doubleValue__D", o); + } + if (type == Float.TYPE) { + return toRaw("floatValue__F", o); + } + if (type == Byte.TYPE) { + return toRaw("byteValue__B", o); + } + if (type == Boolean.TYPE) { + return toRaw("booleanValue__Z", o); + } + if (type == Short.TYPE) { + return toRaw("shortValue__S", o); + } + if (type == Character.TYPE) { + return toRaw("charValue__C", o); + } + if (type.getName().equals("void")) { + return o; + } + throw new IllegalStateException("Can't convert " + o); + } + + @JavaScriptBody(args = { "m", "o" }, + body = "return o[m](o);" + ) + private static native Object toRaw(String m, Object o); + /** * Returns {@code true} if this method is a bridge * method; returns {@code false} otherwise. @@ -536,10 +646,10 @@ return Modifier.isSynthetic(getModifiers()); } - @JavaScriptBody(args = { "self", "ac" }, + @JavaScriptBody(args = { "ac" }, body = - "if (self.fld_data.anno) {" - + " return self.fld_data.anno['L' + ac.jvmName + ';'];" + "if (this.fld_data.anno) {" + + " return this.fld_data.anno['L' + ac.jvmName + ';'];" + "} else return null;" ) private Object getAnnotationData(Class annotationClass) { @@ -599,57 +709,12 @@ public Annotation[][] getParameterAnnotations() { throw new UnsupportedOperationException(); } - - // - // bck2brwsr implementation - // - @JavaScriptBody(args = { "clazz", "prefix" }, - body = "" - + "var c = clazz.cnstr.prototype;" - + "var arr = new Array();\n" - + "for (m in c) {\n" - + " if (m.indexOf(prefix) === 0) {\n" - + " arr.push(m);\n" - + " arr.push(c[m]);\n" - + " }" - + "}\n" - + "return arr;" - ) - private static native Object[] findMethodData( - Class clazz, String prefix - ); - - // XXX should not be public - public static Method findMethod( - Class clazz, String name, Class... parameterTypes - ) { - Object[] data = findMethodData(clazz, name + "__"); - if (data.length == 0) { - return null; - } - String sig = ((String)data[0]).substring(name.length() + 2); - return new Method(clazz, name, data[1], sig); - } - - public static Method[] findMethods(Class clazz) { - Object[] namesAndData = findMethodData(clazz, ""); - int cnt = 0; - for (int i = 0; i < namesAndData.length; i += 2) { - String sig = (String) namesAndData[i]; - Object data = namesAndData[i + 1]; - int middle = sig.indexOf("__"); - if (middle == -1) { - continue; + static { + MethodImpl.INSTANCE = new MethodImpl() { + protected Method create(Class declaringClass, String name, Object data, String sig) { + return new Method(declaringClass, name, data, sig); } - String name = sig.substring(0, middle); - sig = sig.substring(middle + 2); - namesAndData[cnt++] = new Method(clazz, name, data, sig); - } - Method[] arr = new Method[cnt]; - for (int i = 0; i < cnt; i++) { - arr[i] = (Method)namesAndData[i]; - } - return arr; + }; } } diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/net/URL.java --- a/emul/src/main/java/java/net/URL.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/java/net/URL.java Fri Jan 18 14:27:22 2013 +0100 @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; /** * Class URL represents a Uniform Resource @@ -196,6 +197,17 @@ */ private String ref; + /** + * The host's IP address, used in equals and hashCode. + * Computed on demand. An uninitialized or unknown hostAddress is null. + */ + transient Object hostAddress; + + /** + * The URLStreamHandler for this URL. + */ + transient URLStreamHandler handler; + /* Our hash code. * @serial */ @@ -308,8 +320,47 @@ this(protocol, host, -1, file); } - private URL(String protocol, String host, int port, String file, - Object handler) throws MalformedURLException { + /** + * Creates a URL object from the specified + * protocol, host, port + * number, file, and handler. Specifying + * a port number of -1 indicates that + * the URL should use the default port for the protocol. Specifying + * a handler of null indicates that the URL + * should use a default stream handler for the protocol, as outlined + * for: + * java.net.URL#URL(java.lang.String, java.lang.String, int, + * java.lang.String) + * + *

    If the handler is not null and there is a security manager, + * the security manager's checkPermission + * method is called with a + * NetPermission("specifyStreamHandler") permission. + * This may result in a SecurityException. + * + * No validation of the inputs is performed by this constructor. + * + * @param protocol the name of the protocol to use. + * @param host the name of the host. + * @param port the port number on the host. + * @param file the file on the host + * @param handler the stream handler for the URL. + * @exception MalformedURLException if an unknown protocol is specified. + * @exception SecurityException + * if a security manager exists and its + * checkPermission method doesn't allow + * specifying a stream handler explicitly. + * @see java.lang.System#getProperty(java.lang.String) + * @see java.net.URL#setURLStreamHandlerFactory( + * java.net.URLStreamHandlerFactory) + * @see java.net.URLStreamHandler + * @see java.net.URLStreamHandlerFactory#createURLStreamHandler( + * java.lang.String) + * @see SecurityManager#checkPermission + * @see java.net.NetPermission + */ + public URL(String protocol, String host, int port, String file, + URLStreamHandler handler) throws MalformedURLException { if (handler != null) { throw new SecurityException(); } @@ -348,10 +399,11 @@ // Note: we don't do validation of the URL here. Too risky to change // right now, but worth considering for future reference. -br -// if (handler == null && -// (handler = getURLStreamHandler(protocol)) == null) { -// throw new MalformedURLException("unknown protocol: " + protocol); -// } + if (handler == null && + (handler = getURLStreamHandler(protocol)) == null) { + throw new MalformedURLException("unknown protocol: " + protocol); + } + this.handler = handler; } /** @@ -441,7 +493,7 @@ * @see java.net.URLStreamHandler#parseURL(java.net.URL, * java.lang.String, int, int) */ - private URL(URL context, String spec, Object handler) + public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException { String original = spec; @@ -494,9 +546,9 @@ newProtocol.equalsIgnoreCase(context.protocol))) { // inherit the protocol handler from the context // if not specified to the constructor -// if (handler == null) { -// handler = context.handler; -// } + if (handler == null) { + handler = context.handler; + } // If the context is a hierarchical URL scheme and the spec // contains a matching scheme then maintain backwards @@ -523,15 +575,15 @@ // Get the protocol handler if not specified or the protocol // of the context could not be used -// if (handler == null && -// (handler = getURLStreamHandler(protocol)) == null) { -// throw new MalformedURLException("unknown protocol: "+protocol); -// } - -// this.handler = handler; + if (handler == null && + (handler = getURLStreamHandler(protocol)) == null) { + throw new MalformedURLException("unknown protocol: "+protocol); + } + this.handler = handler; i = spec.indexOf('#', start); if (i >= 0) { +//thrw(protocol + " hnd: " + handler.getClass().getName() + " i: " + i); ref = spec.substring(i + 1, limit); limit = i; } @@ -547,7 +599,7 @@ } } -// handler.parseURL(this, spec, start, limit); + handler.parseURL(this, spec, start, limit); } catch(MalformedURLException e) { throw e; @@ -557,7 +609,7 @@ throw exception; } } - + /* * Returns true if specified string is a valid protocol name. */ @@ -601,6 +653,7 @@ /* This is very important. We must recompute this after the * URL has been changed. */ hashCode = -1; + hostAddress = null; int q = file.lastIndexOf('?'); if (q != -1) { query = file.substring(q+1); @@ -639,6 +692,7 @@ /* This is very important. We must recompute this after the * URL has been changed. */ hashCode = -1; + hostAddress = null; this.query = query; this.authority = authority; } @@ -697,6 +751,19 @@ } /** + * Gets the default port number of the protocol associated + * with this URL. If the URL scheme or the URLStreamHandler + * for the URL do not define a default port number, + * then -1 is returned. + * + * @return the port number + * @since 1.4 + */ + public int getDefaultPort() { + return handler.getDefaultPort(); + } + + /** * Gets the protocol name of this URL. * * @return the protocol of this URL. @@ -773,8 +840,7 @@ return false; URL u2 = (URL)obj; - // return handler.equals(this, u2); - return u2 == this; + return handler.equals(this, u2); } /** @@ -789,7 +855,7 @@ if (hashCode != -1) return hashCode; - // hashCode = handler.hashCode(this); + hashCode = handler.hashCode(this); return hashCode; } @@ -805,8 +871,7 @@ * false otherwise. */ public boolean sameFile(URL other) { -// return handler.sameFile(this, other); - throw new UnsupportedOperationException(); + return handler.sameFile(this, other); } /** @@ -834,8 +899,7 @@ * @see java.net.URLStreamHandler#toExternalForm(java.net.URL) */ public String toExternalForm() { - throw new UnsupportedOperationException(); -// return handler.toExternalForm(this); + return handler.toExternalForm(this); } /** @@ -901,9 +965,16 @@ * @see java.net.URLConnection#getContent() */ public final Object getContent() throws java.io.IOException { - throw new IOException(); -// return openConnection().getContent(); + return loadText(toExternalForm()); } + + @JavaScriptBody(args = "url", body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, false);\n" + + "request.send();\n" + + "return request.responseText;\n" + ) + private static native String loadText(String url) throws IOException; /** * Gets the contents of this URL. This method is a shorthand for: @@ -921,10 +992,18 @@ */ public final Object getContent(Class[] classes) throws java.io.IOException { - throw new IOException(); -// return openConnection().getContent(classes); + for (Class c : classes) { + if (c == String.class) { + return getContent(); + } + } + return null; } + static URLStreamHandler getURLStreamHandler(String protocol) { + URLStreamHandler universal = new URLStreamHandler() {}; + return universal; + } } diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/java/net/URLStreamHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/src/main/java/java/net/URLStreamHandler.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,568 @@ +/* + * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + + +/** + * The abstract class URLStreamHandler is the common + * superclass for all stream protocol handlers. A stream protocol + * handler knows how to make a connection for a particular protocol + * type, such as http, ftp, or + * gopher. + *

    + * In most cases, an instance of a URLStreamHandler + * subclass is not created directly by an application. Rather, the + * first time a protocol name is encountered when constructing a + * URL, the appropriate stream protocol handler is + * automatically loaded. + * + * @author James Gosling + * @see java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String) + * @since JDK1.0 + */ +public abstract class URLStreamHandler { + /** + * Opens a connection to the object referenced by the + * URL argument. + * This method should be overridden by a subclass. + * + *

    If for the handler's protocol (such as HTTP or JAR), there + * exists a public, specialized URLConnection subclass belonging + * to one of the following packages or one of their subpackages: + * java.lang, java.io, java.util, java.net, the connection + * returned will be of that subclass. For example, for HTTP an + * HttpURLConnection will be returned, and for JAR a + * JarURLConnection will be returned. + * + * @param u the URL that this connects to. + * @return a URLConnection object for the URL. + * @exception IOException if an I/O error occurs while opening the + * connection. + */ +// abstract protected URLConnection openConnection(URL u) throws IOException; + + /** + * Same as openConnection(URL), except that the connection will be + * made through the specified proxy; Protocol handlers that do not + * support proxying will ignore the proxy parameter and make a + * normal connection. + * + * Calling this method preempts the system's default ProxySelector + * settings. + * + * @param u the URL that this connects to. + * @param p the proxy through which the connection will be made. + * If direct connection is desired, Proxy.NO_PROXY + * should be specified. + * @return a URLConnection object for the URL. + * @exception IOException if an I/O error occurs while opening the + * connection. + * @exception IllegalArgumentException if either u or p is null, + * or p has the wrong type. + * @exception UnsupportedOperationException if the subclass that + * implements the protocol doesn't support this method. + * @since 1.5 + */ +// protected URLConnection openConnection(URL u, Proxy p) throws IOException { +// throw new UnsupportedOperationException("Method not implemented."); +// } + + /** + * Parses the string representation of a URL into a + * URL object. + *

    + * If there is any inherited context, then it has already been + * copied into the URL argument. + *

    + * The parseURL method of URLStreamHandler + * parses the string representation as if it were an + * http specification. Most URL protocol families have a + * similar parsing. A stream protocol handler for a protocol that has + * a different syntax must override this routine. + * + * @param u the URL to receive the result of parsing + * the spec. + * @param spec the String representing the URL that + * must be parsed. + * @param start the character index at which to begin parsing. This is + * just past the ':' (if there is one) that + * specifies the determination of the protocol name. + * @param limit the character position to stop parsing at. This is the + * end of the string or the position of the + * "#" character, if present. All information + * after the sharp sign indicates an anchor. + */ + protected void parseURL(URL u, String spec, int start, int limit) { + // These fields may receive context content if this was relative URL + String protocol = u.getProtocol(); + String authority = u.getAuthority(); + String userInfo = u.getUserInfo(); + String host = u.getHost(); + int port = u.getPort(); + String path = u.getPath(); + String query = u.getQuery(); + + // This field has already been parsed + String ref = u.getRef(); + + boolean isRelPath = false; + boolean queryOnly = false; + +// FIX: should not assume query if opaque + // Strip off the query part + if (start < limit) { + int queryStart = spec.indexOf('?'); + queryOnly = queryStart == start; + if ((queryStart != -1) && (queryStart < limit)) { + query = spec.substring(queryStart+1, limit); + if (limit > queryStart) + limit = queryStart; + spec = spec.substring(0, queryStart); + } + } + + int i = 0; + // Parse the authority part if any + boolean isUNCName = (start <= limit - 4) && + (spec.charAt(start) == '/') && + (spec.charAt(start + 1) == '/') && + (spec.charAt(start + 2) == '/') && + (spec.charAt(start + 3) == '/'); + if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') && + (spec.charAt(start + 1) == '/')) { + start += 2; + i = spec.indexOf('/', start); + if (i < 0) { + i = spec.indexOf('?', start); + if (i < 0) + i = limit; + } + + host = authority = spec.substring(start, i); + + int ind = authority.indexOf('@'); + if (ind != -1) { + userInfo = authority.substring(0, ind); + host = authority.substring(ind+1); + } else { + userInfo = null; + } + if (host != null) { + // If the host is surrounded by [ and ] then its an IPv6 + // literal address as specified in RFC2732 + if (host.length()>0 && (host.charAt(0) == '[')) { + if ((ind = host.indexOf(']')) > 2) { + + String nhost = host ; + host = nhost.substring(0,ind+1); +// if (!IPAddressUtil. +// isIPv6LiteralAddress(host.substring(1, ind))) { +// throw new IllegalArgumentException( +// "Invalid host: "+ host); +// } + + port = -1 ; + if (nhost.length() > ind+1) { + if (nhost.charAt(ind+1) == ':') { + ++ind ; + // port can be null according to RFC2396 + if (nhost.length() > (ind + 1)) { + port = Integer.parseInt(nhost.substring(ind+1)); + } + } else { + throw new IllegalArgumentException( + "Invalid authority field: " + authority); + } + } + } else { + throw new IllegalArgumentException( + "Invalid authority field: " + authority); + } + } else { + ind = host.indexOf(':'); + port = -1; + if (ind >= 0) { + // port can be null according to RFC2396 + if (host.length() > (ind + 1)) { + port = Integer.parseInt(host.substring(ind + 1)); + } + host = host.substring(0, ind); + } + } + } else { + host = ""; + } + if (port < -1) + throw new IllegalArgumentException("Invalid port number :" + + port); + start = i; + // If the authority is defined then the path is defined by the + // spec only; See RFC 2396 Section 5.2.4. + if (authority != null && authority.length() > 0) + path = ""; + } + + if (host == null) { + host = ""; + } + + // Parse the file path if any + if (start < limit) { + if (spec.charAt(start) == '/') { + path = spec.substring(start, limit); + } else if (path != null && path.length() > 0) { + isRelPath = true; + int ind = path.lastIndexOf('/'); + String seperator = ""; + if (ind == -1 && authority != null) + seperator = "/"; + path = path.substring(0, ind + 1) + seperator + + spec.substring(start, limit); + + } else { + String seperator = (authority != null) ? "/" : ""; + path = seperator + spec.substring(start, limit); + } + } else if (queryOnly && path != null) { + int ind = path.lastIndexOf('/'); + if (ind < 0) + ind = 0; + path = path.substring(0, ind) + "/"; + } + if (path == null) + path = ""; + + if (isRelPath) { + // Remove embedded /./ + while ((i = path.indexOf("/./")) >= 0) { + path = path.substring(0, i) + path.substring(i + 2); + } + // Remove embedded /../ if possible + i = 0; + while ((i = path.indexOf("/../", i)) >= 0) { + /* + * A "/../" will cancel the previous segment and itself, + * unless that segment is a "/../" itself + * i.e. "/a/b/../c" becomes "/a/c" + * but "/../../a" should stay unchanged + */ + if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 && + (path.indexOf("/../", limit) != 0)) { + path = path.substring(0, limit) + path.substring(i + 3); + i = 0; + } else { + i = i + 3; + } + } + // Remove trailing .. if possible + while (path.endsWith("/..")) { + i = path.indexOf("/.."); + if ((limit = path.lastIndexOf('/', i - 1)) >= 0) { + path = path.substring(0, limit+1); + } else { + break; + } + } + // Remove starting . + if (path.startsWith("./") && path.length() > 2) + path = path.substring(2); + + // Remove trailing . + if (path.endsWith("/.")) + path = path.substring(0, path.length() -1); + } + + setURL(u, protocol, host, port, authority, userInfo, path, query, ref); + } + + /** + * Returns the default port for a URL parsed by this handler. This method + * is meant to be overidden by handlers with default port numbers. + * @return the default port for a URL parsed by this handler. + * @since 1.3 + */ + protected int getDefaultPort() { + return -1; + } + + /** + * Provides the default equals calculation. May be overidden by handlers + * for other protocols that have different requirements for equals(). + * This method requires that none of its arguments is null. This is + * guaranteed by the fact that it is only called by java.net.URL class. + * @param u1 a URL object + * @param u2 a URL object + * @return true if the two urls are + * considered equal, ie. they refer to the same + * fragment in the same file. + * @since 1.3 + */ + protected boolean equals(URL u1, URL u2) { + String ref1 = u1.getRef(); + String ref2 = u2.getRef(); + return (ref1 == ref2 || (ref1 != null && ref1.equals(ref2))) && + sameFile(u1, u2); + } + + /** + * Provides the default hash calculation. May be overidden by handlers for + * other protocols that have different requirements for hashCode + * calculation. + * @param u a URL object + * @return an int suitable for hash table indexing + * @since 1.3 + */ + protected int hashCode(URL u) { + int h = 0; + + // Generate the protocol part. + String protocol = u.getProtocol(); + if (protocol != null) + h += protocol.hashCode(); + + // Generate the host part. + Object addr = getHostAddress(u); + if (addr != null) { + h += addr.hashCode(); + } else { + String host = u.getHost(); + if (host != null) + h += host.toLowerCase().hashCode(); + } + + // Generate the file part. + String file = u.getFile(); + if (file != null) + h += file.hashCode(); + + // Generate the port part. + if (u.getPort() == -1) + h += getDefaultPort(); + else + h += u.getPort(); + + // Generate the ref part. + String ref = u.getRef(); + if (ref != null) + h += ref.hashCode(); + + return h; + } + + /** + * Compare two urls to see whether they refer to the same file, + * i.e., having the same protocol, host, port, and path. + * This method requires that none of its arguments is null. This is + * guaranteed by the fact that it is only called indirectly + * by java.net.URL class. + * @param u1 a URL object + * @param u2 a URL object + * @return true if u1 and u2 refer to the same file + * @since 1.3 + */ + protected boolean sameFile(URL u1, URL u2) { + // Compare the protocols. + if (!((u1.getProtocol() == u2.getProtocol()) || + (u1.getProtocol() != null && + u1.getProtocol().equalsIgnoreCase(u2.getProtocol())))) + return false; + + // Compare the files. + if (!(u1.getFile() == u2.getFile() || + (u1.getFile() != null && u1.getFile().equals(u2.getFile())))) + return false; + + // Compare the ports. + int port1, port2; + port1 = (u1.getPort() != -1) ? u1.getPort() : u1.handler.getDefaultPort(); + port2 = (u2.getPort() != -1) ? u2.getPort() : u2.handler.getDefaultPort(); + if (port1 != port2) + return false; + + // Compare the hosts. + if (!hostsEqual(u1, u2)) + return false; + + return true; + } + + /** + * Get the IP address of our host. An empty host field or a DNS failure + * will result in a null return. + * + * @param u a URL object + * @return an InetAddress representing the host + * IP address. + * @since 1.3 + */ + private synchronized Object getHostAddress(URL u) { + return u.hostAddress; + } + + /** + * Compares the host components of two URLs. + * @param u1 the URL of the first host to compare + * @param u2 the URL of the second host to compare + * @return true if and only if they + * are equal, false otherwise. + * @since 1.3 + */ + protected boolean hostsEqual(URL u1, URL u2) { + Object a1 = getHostAddress(u1); + Object a2 = getHostAddress(u2); + // if we have internet address for both, compare them + if (a1 != null && a2 != null) { + return a1.equals(a2); + // else, if both have host names, compare them + } else if (u1.getHost() != null && u2.getHost() != null) + return u1.getHost().equalsIgnoreCase(u2.getHost()); + else + return u1.getHost() == null && u2.getHost() == null; + } + + /** + * Converts a URL of a specific protocol to a + * String. + * + * @param u the URL. + * @return a string representation of the URL argument. + */ + protected String toExternalForm(URL u) { + + // pre-compute length of StringBuffer + int len = u.getProtocol().length() + 1; + if (u.getAuthority() != null && u.getAuthority().length() > 0) + len += 2 + u.getAuthority().length(); + if (u.getPath() != null) { + len += u.getPath().length(); + } + if (u.getQuery() != null) { + len += 1 + u.getQuery().length(); + } + if (u.getRef() != null) + len += 1 + u.getRef().length(); + + StringBuffer result = new StringBuffer(len); + result.append(u.getProtocol()); + result.append(":"); + if (u.getAuthority() != null && u.getAuthority().length() > 0) { + result.append("//"); + result.append(u.getAuthority()); + } + if (u.getPath() != null) { + result.append(u.getPath()); + } + if (u.getQuery() != null) { + result.append('?'); + result.append(u.getQuery()); + } + if (u.getRef() != null) { + result.append("#"); + result.append(u.getRef()); + } + return result.toString(); + } + + /** + * Sets the fields of the URL argument to the indicated values. + * Only classes derived from URLStreamHandler are supposed to be able + * to call the set method on a URL. + * + * @param u the URL to modify. + * @param protocol the protocol name. + * @param host the remote host value for the URL. + * @param port the port on the remote machine. + * @param authority the authority part for the URL. + * @param userInfo the userInfo part of the URL. + * @param path the path component of the URL. + * @param query the query part for the URL. + * @param ref the reference. + * @exception SecurityException if the protocol handler of the URL is + * different from this one + * @see java.net.URL#set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String) + * @since 1.3 + */ + protected void setURL(URL u, String protocol, String host, int port, + String authority, String userInfo, String path, + String query, String ref) { + if (this != u.handler) { + throw new SecurityException("handler for url different from " + + "this handler"); + } + // ensure that no one can reset the protocol on a given URL. + u.set(u.getProtocol(), host, port, authority, userInfo, path, query, ref); + } + + /** + * Sets the fields of the URL argument to the indicated values. + * Only classes derived from URLStreamHandler are supposed to be able + * to call the set method on a URL. + * + * @param u the URL to modify. + * @param protocol the protocol name. This value is ignored since 1.2. + * @param host the remote host value for the URL. + * @param port the port on the remote machine. + * @param file the file. + * @param ref the reference. + * @exception SecurityException if the protocol handler of the URL is + * different from this one + * @deprecated Use setURL(URL, String, String, int, String, String, String, + * String); + */ + @Deprecated + protected void setURL(URL u, String protocol, String host, int port, + String file, String ref) { + /* + * Only old URL handlers call this, so assume that the host + * field might contain "user:passwd@host". Fix as necessary. + */ + String authority = null; + String userInfo = null; + if (host != null && host.length() != 0) { + authority = (port == -1) ? host : host + ":" + port; + int at = host.lastIndexOf('@'); + if (at != -1) { + userInfo = host.substring(0, at); + host = host.substring(at+1); + } + } + + /* + * Assume file might contain query part. Fix as necessary. + */ + String path = null; + String query = null; + if (file != null) { + int q = file.lastIndexOf('?'); + if (q != -1) { + query = file.substring(q+1); + path = file.substring(0, q); + } else + path = file; + } + setURL(u, protocol, host, port, authority, userInfo, path, query, ref); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/org/apidesign/bck2brwsr/emul/AnnotationImpl.java --- a/emul/src/main/java/org/apidesign/bck2brwsr/emul/AnnotationImpl.java Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/java/org/apidesign/bck2brwsr/emul/AnnotationImpl.java Fri Jan 18 14:27:22 2013 +0100 @@ -1,6 +1,19 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. +/** + * 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.emul; diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/java/org/apidesign/bck2brwsr/emul/MethodImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/src/main/java/org/apidesign/bck2brwsr/emul/MethodImpl.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,164 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.apidesign.bck2brwsr.emul; + +import java.lang.reflect.Method; +import java.util.Enumeration; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** Utilities to work on methods. + * + * @author Jaroslav Tulach + */ +public abstract class MethodImpl { + public static MethodImpl INSTANCE; + static { + try { + Class.forName(Method.class.getName()); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException(ex); + } + } + + protected abstract Method create(Class declaringClass, String name, Object data, String sig); + + + // + // bck2brwsr implementation + // + + @JavaScriptBody(args = {"clazz", "prefix"}, + body = "" + + "var c = clazz.cnstr.prototype;" + + "var arr = new Array();\n" + + "for (m in c) {\n" + + " if (m.indexOf(prefix) === 0) {\n" + + " arr.push(m);\n" + + " arr.push(c[m]);\n" + + " }" + + "}\n" + + "return arr;") + private static native Object[] findMethodData( + Class clazz, String prefix); + + public static Method findMethod( + Class clazz, String name, Class... parameterTypes) { + Object[] data = findMethodData(clazz, name + "__"); + BIG: for (int i = 0; i < data.length; i += 2) { + String sig = ((String) data[0]).substring(name.length() + 2); + Method tmp = INSTANCE.create(clazz, name, data[1], sig); + Class[] tmpParms = tmp.getParameterTypes(); + if (parameterTypes.length != tmpParms.length) { + continue; + } + for (int j = 0; j < tmpParms.length; j++) { + if (!parameterTypes[j].equals(tmpParms[j])) { + continue BIG; + } + } + return tmp; + } + return null; + } + + public static Method[] findMethods(Class clazz, int mask) { + Object[] namesAndData = findMethodData(clazz, ""); + int cnt = 0; + for (int i = 0; i < namesAndData.length; i += 2) { + String sig = (String) namesAndData[i]; + Object data = namesAndData[i + 1]; + int middle = sig.indexOf("__"); + if (middle == -1) { + continue; + } + String name = sig.substring(0, middle); + sig = sig.substring(middle + 2); + final Method m = INSTANCE.create(clazz, name, data, sig); + if ((m.getModifiers() & mask) == 0) { + continue; + } + namesAndData[cnt++] = m; + } + Method[] arr = new Method[cnt]; + for (int i = 0; i < cnt; i++) { + arr[i] = (Method) namesAndData[i]; + } + return arr; + } + + public static int signatureElements(String sig) { + Enumeration en = signatureParser(sig); + int cnt = 0; + while (en.hasMoreElements()) { + en.nextElement(); + cnt++; + } + return cnt; + } + + public static Enumeration signatureParser(final String sig) { + class E implements Enumeration { + int pos; + + public boolean hasMoreElements() { + return pos < sig.length(); + } + + public Class nextElement() { + switch (sig.charAt(pos++)) { + case 'I': + return Integer.TYPE; + case 'J': + return Long.TYPE; + case 'D': + return Double.TYPE; + case 'F': + return Float.TYPE; + case 'B': + return Byte.TYPE; + case 'Z': + return Boolean.TYPE; + case 'S': + return Short.TYPE; + case 'V': + return Void.TYPE; + case 'C': + return Character.TYPE; + case 'L': + try { + int up = sig.indexOf("_2"); + String type = sig.substring(1, up); + pos = up + 2; + return Class.forName(type); + } catch (ClassNotFoundException ex) { + // should not happen + } + } + throw new UnsupportedOperationException(sig + " at " + pos); + } + } + return new E(); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_Number.js Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,9 @@ +// empty line needed here +Number.prototype.add32 = function(x) { return (this + x) | 0; }; +Number.prototype.sub32 = function(x) { return (this - x) | 0; }; +Number.prototype.mul32 = function(x) { + return (((this * (x >> 16)) << 16) + this * (x & 0xFFFF)) | 0; +}; + +Number.prototype.toInt8 = function() { return (this << 24) >> 24; }; +Number.prototype.toInt16 = function() { return (this << 16) >> 16; }; \ No newline at end of file diff -r 1a73c6e08cc5 -r 95b68e21a53f emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js --- a/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js Fri Jan 18 14:23:18 2013 +0100 +++ b/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js Fri Jan 18 14:27:22 2013 +0100 @@ -2,8 +2,30 @@ vm.java_lang_String(false); // we need initialized arrays -Array.prototype.fillNulls = function() { - for(var i = 0; i < this.length; i++) this[i] = null; +Array.prototype.initWith = function(sig, value) { + for(var i = 0; i < this.length; i++) this[i] = value; + this.jvmName = sig; return this; }; - +Array.prototype.at = function(indx, value) { + if (indx < 0 || indx > this.length) { + var e = vm.java_lang_ArrayIndexOutOfBoundsException(true); + e.constructor.cons__VLjava_lang_String_2.call(e, indx.toString()); + throw e; + } + if (arguments.length === 2) { + this[indx] = value; + } + return this[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); +}; +Array.prototype.clone__Ljava_lang_Object_2 = function() { + var s = this.length; + var ret = new Array(s); + for (var i = 0; i < s; i++) { + ret[i] = this[i]; + } + return ret; +}; diff -r 1a73c6e08cc5 -r 95b68e21a53f javap/src/main/java/org/apidesign/javap/ClassData.java --- a/javap/src/main/java/org/apidesign/javap/ClassData.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/ClassData.java Fri Jan 18 14:27:22 2013 +0100 @@ -326,6 +326,10 @@ return res; // "#"+cpx+"// ERROR IN DISASSEMBLER"; } } + + public int getAccessFlags() { + return access; + } /** * Returns true if it is a class diff -r 1a73c6e08cc5 -r 95b68e21a53f javap/src/main/java/org/apidesign/javap/Hashtable.java --- a/javap/src/main/java/org/apidesign/javap/Hashtable.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/Hashtable.java Fri Jan 18 14:27:22 2013 +0100 @@ -1,11 +1,22 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. +/** + * 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.javap; -import org.apidesign.bck2brwsr.core.JavaScriptBody; - /** A JavaScript optimized replacement for Hashtable. * * @author Jaroslav Tulach @@ -25,9 +36,6 @@ Hashtable() { } - @JavaScriptBody(args = { "self", "key", "val" }, body = - "self[key] = val;" - ) synchronized void put(Object key, Object val) { int[] where = { -1, -1 }; Object found = get(key, where); @@ -61,9 +69,6 @@ } } - @JavaScriptBody(args = {"self", "key" }, body = - "return self[key];" - ) Object get(Object key) { return get(key, null); } diff -r 1a73c6e08cc5 -r 95b68e21a53f javap/src/main/java/org/apidesign/javap/MethodData.java --- a/javap/src/main/java/org/apidesign/javap/MethodData.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/MethodData.java Fri Jan 18 14:27:22 2013 +0100 @@ -25,9 +25,9 @@ package org.apidesign.javap; -import java.io.*; -import org.apidesign.bck2brwsr.core.JavaScriptBody; +import java.io.DataInputStream; +import java.io.IOException; import static org.apidesign.javap.RuntimeConstants.*; /** @@ -235,22 +235,8 @@ /** * Return access of the method. */ - public String[] getAccess(){ - - Vector v = new Vector(); - if ((access & ACC_PUBLIC) !=0) v.addElement("public"); - if ((access & ACC_PRIVATE) !=0) v.addElement("private"); - if ((access & ACC_PROTECTED) !=0) v.addElement("protected"); - if ((access & ACC_STATIC) !=0) v.addElement("static"); - if ((access & ACC_FINAL) !=0) v.addElement("final"); - if ((access & ACC_SYNCHRONIZED) !=0) v.addElement("synchronized"); - if ((access & ACC_NATIVE) !=0) v.addElement("native"); - if ((access & ACC_ABSTRACT) !=0) v.addElement("abstract"); - if ((access & ACC_STRICT) !=0) v.addElement("strictfp"); - - String[] accflags = new String[v.size()]; - v.copyInto(accflags); - return accflags; + public int getAccess(){ + return access; } /** @@ -357,10 +343,10 @@ /** * Return exception table in code attributre. */ - public Vector getexception_table(){ - return exception_table; + public TrapDataIterator getTrapDataIterator(){ + return new TrapDataIterator(exception_table); } - + /** * Return method attributes. @@ -401,4 +387,8 @@ attrs.copyInto(arr); return ClassData.findAttr(n, arr); } + + public boolean isConstructor() { + return "".equals(getName()); + } } diff -r 1a73c6e08cc5 -r 95b68e21a53f javap/src/main/java/org/apidesign/javap/TrapData.java --- a/javap/src/main/java/org/apidesign/javap/TrapData.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/TrapData.java Fri Jan 18 14:27:22 2013 +0100 @@ -26,7 +26,6 @@ package org.apidesign.javap; -import java.util.*; import java.io.*; /** @@ -34,15 +33,18 @@ * * @author Sucheta Dambalkar (Adopted code from jdis) */ -class TrapData { - short start_pc, end_pc, handler_pc, catch_cpx; - int num; +public final class TrapData { + public final short start_pc; + public final short end_pc; + public final short handler_pc; + public final short catch_cpx; + final int num; /** * Read and store exception table data in code attribute. */ - public TrapData(DataInputStream in, int num) throws IOException { + TrapData(DataInputStream in, int num) throws IOException { this.num=num; start_pc = in.readShort(); end_pc=in.readShort(); diff -r 1a73c6e08cc5 -r 95b68e21a53f javap/src/main/java/org/apidesign/javap/TrapDataIterator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javap/src/main/java/org/apidesign/javap/TrapDataIterator.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,114 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.javap; + +/** + * + * @author Jaroslav Tulach + */ +public final class TrapDataIterator { + private final Hashtable exStart = new Hashtable(); + private final Hashtable exStop = new Hashtable(); + private TrapData[] current = new TrapData[10]; + private int currentCount; + + TrapDataIterator(Vector exceptionTable) { + for (int i=0 ; i < exceptionTable.size(); i++) { + final TrapData td = (TrapData)exceptionTable.elementAt(i); + put(exStart, td.start_pc, td); + put(exStop, td.end_pc, td); + } + } + + private static void put(Hashtable h, short key, TrapData td) { + Short s = Short.valueOf((short)key); + Vector v = (Vector) h.get(s); + if (v == null) { + v = new Vector(1); + h.put(s, v); + } + v.add(td); + } + + private boolean processAll(Hashtable h, Short key, boolean add) { + boolean change = false; + Vector v = (Vector)h.get(key); + if (v != null) { + int s = v.size(); + for (int i = 0; i < s; i++) { + TrapData td = (TrapData)v.elementAt(i); + if (add) { + add(td); + change = true; + } else { + remove(td); + change = true; + } + } + } + return change; + } + + public boolean advanceTo(int i) { + Short s = Short.valueOf((short)i); + boolean ch1 = processAll(exStart, s, true); + boolean ch2 = processAll(exStop, s, false); + return ch1 || ch2; + } + + public boolean useTry() { + return currentCount > 0; + } + + public TrapData[] current() { + TrapData[] copy = new TrapData[currentCount]; + for (int i = 0; i < currentCount; i++) { + copy[i] = current[i]; + } + return copy; + } + + private void add(TrapData e) { + if (currentCount == current.length) { + TrapData[] data = new TrapData[currentCount * 2]; + for (int i = 0; i < currentCount; i++) { + data[i] = current[i]; + } + current = data; + } + current[currentCount++] = e; + } + + private void remove(TrapData e) { + if (currentCount == 0) { + return; + } + int from = 0; + while (from < currentCount) { + if (e == current[from++]) { + break; + } + } + while (from < currentCount) { + current[from - 1] = current[from]; + current[from] = null; + from++; + } + currentCount--; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f javap/src/main/java/org/apidesign/javap/Vector.java --- a/javap/src/main/java/org/apidesign/javap/Vector.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/Vector.java Fri Jan 18 14:27:22 2013 +0100 @@ -1,6 +1,19 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. +/** + * 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.javap; @@ -24,8 +37,8 @@ void add(Object objectType) { addElement(objectType); } - @JavaScriptBody(args = { "self", "obj" }, body = - "self.push(obj);" + @JavaScriptBody(args = { "obj" }, body = + "this.push(obj);" ) void addElement(Object obj) { final int s = size(); @@ -33,16 +46,16 @@ setElementAt(obj, s); } - @JavaScriptBody(args = { "self" }, body = - "return self.length;" + @JavaScriptBody(args = { }, body = + "return this.length;" ) int size() { return arr == null ? 0 : arr.length; } - @JavaScriptBody(args = { "self", "newArr" }, body = - "for (var i = 0; i < self.length; i++) {\n" - + " newArr[i] = self[i];\n" + @JavaScriptBody(args = { "newArr" }, body = + "for (var i = 0; i < this.length; i++) {\n" + + " newArr[i] = this[i];\n" + "}\n") void copyInto(Object[] newArr) { if (arr == null) { @@ -54,8 +67,8 @@ } } - @JavaScriptBody(args = { "self", "index" }, body = - "return self[index];" + @JavaScriptBody(args = { "index" }, body = + "return this[index];" ) Object elementAt(int index) { return arr[index]; @@ -67,8 +80,8 @@ arr = newArr; } - @JavaScriptBody(args = { "self", "val", "index" }, body = - "self[index] = val;" + @JavaScriptBody(args = { "val", "index" }, body = + "this[index] = val;" ) void setElementAt(Object val, int index) { arr[index] = val; diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/PageProcessor.java Fri Jan 18 14:27:22 2013 +0100 @@ -43,7 +43,7 @@ import javax.tools.Diagnostic; import javax.tools.FileObject; import javax.tools.StandardLocation; -import org.apidesign.bck2brwsr.htmlpage.api.OnClick; +import org.apidesign.bck2brwsr.htmlpage.api.On; import org.apidesign.bck2brwsr.htmlpage.api.Page; import org.openide.util.lookup.ServiceProvider; @@ -55,7 +55,7 @@ @ServiceProvider(service=Processor.class) @SupportedAnnotationTypes({ "org.apidesign.bck2brwsr.htmlpage.api.Page", - "org.apidesign.bck2brwsr.htmlpage.api.OnClick" + "org.apidesign.bck2brwsr.htmlpage.api.On" }) public final class PageProcessor extends AbstractProcessor { @Override @@ -146,11 +146,11 @@ } TypeElement type = (TypeElement)clazz; for (Element method : clazz.getEnclosedElements()) { - OnClick oc = method.getAnnotation(OnClick.class); + On oc = method.getAnnotation(On.class); if (oc != null) { for (String id : oc.id()) { if (pp.tagNameForId(id) == null) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + oc.id() + " does not exist in the HTML page. Found only " + pp.ids(), method); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "id = " + id + " does not exist in the HTML page. Found only " + pp.ids(), method); return false; } ExecutableElement ee = (ExecutableElement)method; @@ -159,21 +159,21 @@ hasParam = false; } else { if (ee.getParameters().size() != 1 || ee.getParameters().get(0).asType() != stringType) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method should either have no arguments or one String argument", ee); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method should either have no arguments or one String argument", ee); return false; } hasParam = true; } if (!ee.getModifiers().contains(Modifier.STATIC)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method has to be static", ee); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method has to be static", ee); return false; } if (ee.getModifiers().contains(Modifier.PRIVATE)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@OnClick method can't be private", ee); + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@On method can't be private", ee); return false; } - w.append(" ").append(cnstnt(id)). - append(".addOnClick(new Runnable() { public void run() {\n"); + w.append(" OnEvent." + oc.event()).append(".of(").append(cnstnt(id)). + append(").perform(new Runnable() { public void run() {\n"); w.append(" ").append(type.getSimpleName().toString()). append('.').append(ee.getSimpleName()).append("("); if (hasParam) { diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Element.java Fri Jan 18 14:27:22 2013 +0100 @@ -37,29 +37,45 @@ body="var e = window.document.getElementById(el.fld_id);\n" + "e[property] = value;\n" ) - static void setAttribute(Element el, String property, Object value) { - throw new UnsupportedOperationException("Needs JavaScript!"); - } + static native void setAttribute(Element el, String property, Object value); @JavaScriptBody( args={"el", "property"}, body="var e = window.document.getElementById(el.fld_id);\n" + "return e[property];\n" ) - static Object getAttribute(Element el, String property) { - throw new UnsupportedOperationException("Needs JavaScript!"); - } + static native Object getAttribute(Element el, String property); /** Executes given runnable when user performs a "click" on the given * element. * @param r the runnable to execute, never null */ @JavaScriptBody( - args={"el", "r"}, - body="var e = window.document.getElementById(el.fld_id);\n" - + "e.onclick = function() { r.run__V(); };\n" + args={ "ev", "r" }, + body="var e = window.document.getElementById(this.fld_id);\n" + + "e[ev.fld_id] = function() { r.run__V(); };\n" ) - public final void addOnClick(Runnable r) { - throw new UnsupportedOperationException("Needs JavaScript!"); + final native void on(OnEvent ev, Runnable r); + + /** Shows alert message dialog in a browser. + * @param msg the message to show + */ + @JavaScriptBody(args = "msg", body = "alert(msg);") + public static native void alert(String msg); + + /** Generic way to query any attribute of this element. + * @param property name of the attribute + */ + public final Object getAttribute(String property) { + return getAttribute(this, property); + } + + /** Generic way to change an attribute of this element. + * + * @param property name of the attribute + * @param value value to associate with the attribute + */ + public final void setAttribute(String property, Object value) { + setAttribute(this, property, value); } } diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/On.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/On.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,35 @@ +/** + * 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.htmlpage.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Adds an onClick handler to an element identified by given id. + * Apply on a public static void method with no arguments. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface On { + OnEvent event(); + String[] id(); +} diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java --- a/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnClick.java Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +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.bck2brwsr.htmlpage.api; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Adds an onClick handler to an element identified by given id. - * Apply on a public static void method with no arguments. - * - * @author Jaroslav Tulach - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.METHOD) -public @interface OnClick { - String[] id(); -} diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnController.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnController.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.htmlpage.api; + +/** Controller created via {@link OnEvent#of(org.apidesign.bck2brwsr.htmlpage.api.Element[])}. + * + * @author Jaroslav Tulach + */ +public final class OnController { + private final Element[] arr; + private final OnEvent event; + + OnController(OnEvent event, Element[] arr) { + this.event = event; + this.arr = arr; + } + + /** Registers a runnable to be performed on associated {@link OnEvent} + * and {@link Element}. + * + * @see OnEvent#of + */ + public void perform(Runnable r) { + for (Element e : arr) { + e.on(event, r); + } + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/OnEvent.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,95 @@ +/** + * 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.htmlpage.api; + +/** Type of events to use in connection with {@link On} annotation. + * + * @author Jaroslav Tulach + */ +public enum OnEvent { + ABORT("onabort"), + BLUR("onblur"), + CAN_PLAY("oncanplay"), + CAN_PLAY_THROUGH("oncanplaythrough"), + CLICK("onclick"), + CONTEXT_MENU("oncontextmenu"), + DBL_CLICK("ondblclick"), + DRAG("ondrag"), + DRAG_END("ondragend"), + DRAG_ENTER("ondragenter"), + DRAG_LEAVE("ondragleave"), + DRAG_OVER("ondragover"), + DRAG_START("ondragstart"), + DROP("ondrop"), + DURATION_CHANGE("ondurationchange"), + EMPTIED("onemptied"), + ENDED("onended"), + ERROR("onerror"), + FOCUS("onfocus"), + FORM_CHANGE("onformchange"), + FORM_INPUT("onforminput"), + INPUT("oninput"), + INVALID("oninvalid"), + KEY_DOWN("onkeydown"), + KEY_PRESS("onkeypress"), + KEY_UP("onkeyup"), + LOAD("onload"), + LOADED_DATA("onloadeddata"), + LOADED_META_DATA("onloadedmetadata"), + LOAD_START("onloadstart"), + MOUSE_DOWN("onmousedown"), + MOUSE_MOVE("onmousemove"), + MOUSE_OUT("onmouseout"), + MOUSE_OVER("onmouseover"), + MOUSE_UP("onmouseup"), + MOUSE_WHEEL("onmousewheel"), + PAUSE("onpause"), + PLAY("onplay"), + PLAYING("onplaying"), + PROGRESS("onprogress"), + RATE_CHANGE("onratechange"), + READY_STATE_CHANGE("onreadystatechange"), + SCROLL("onscroll"), + SEEKED("onseeked"), + SEEKING("onseeking"), + SELECT("onselect"), + SHOW("onshow"), + STALLED("onstalled"), + SUBMIT("onsubmit"), + SUSPEND("onsuspend"), + TIME_UPDATE("ontimeupdate"), + VOLUME_CHANGE("onvolumechange"), + WAITING("onwaiting"); + + final String id; + + private OnEvent(String id) { + this.id = id; + } + + /** What should happen when this even happen on one + * of associated elements. Continue by calling {@link OnController#perform(java.lang.Runnable)} + * method. + * + * @param elmnts one or more elements + * @return controller with perform method. + */ + public OnController of(Element... elmnts) { + return new OnController(this, elmnts); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Timer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/api/src/main/java/org/apidesign/bck2brwsr/htmlpage/api/Timer.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,59 @@ +/** + * 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.htmlpage.api; + +import java.io.Closeable; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +public class Timer implements Closeable { + private final Object t; + + private Timer(Object t) { + this.t = t; + } + + /** Creates a timer that invokes provided runnable on a fixed interval + * + * @param r the runnable to execute + * @param time milliseconds to invoke the timer periodically + */ + public static Timer create(Runnable r, int time) { + return new Timer(interval(r, time)); + } + + @JavaScriptBody(args = { "r", "time" }, body = + "return window.setInterval(function() { r.run__V(); }, time);" + ) + private static native Object interval(Runnable r, int time); + + @JavaScriptBody(args = { "self" }, body = + "window.clearInterval(self);" + ) + private static native void close(Object self); + + /** Cancels this timer. + */ + @Override + public void close() { + close(t); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/PageController.java Fri Jan 18 14:27:22 2013 +0100 @@ -17,7 +17,8 @@ */ package org.apidesign.bck2brwsr.htmlpage; -import org.apidesign.bck2brwsr.htmlpage.api.OnClick; +import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; +import org.apidesign.bck2brwsr.htmlpage.api.On; import org.apidesign.bck2brwsr.htmlpage.api.Page; /** Trivial demo for the bck2brwsr project. First of all start @@ -42,12 +43,12 @@ */ @Page(xhtml="TestPage.html") public class PageController { - @OnClick(id="pg.button") + @On(event = CLICK, id="pg.button") static void updateTitle() { TestPage.PG_TITLE.setText("You want this window to be named " + TestPage.PG_TEXT.getValue()); } - @OnClick(id={ "pg.title", "pg.text" }) + @On(event = CLICK, id={ "pg.title", "pg.text" }) static void click(String id) { if (!id.equals("pg.title")) { throw new IllegalStateException(); diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/demo-calculator-dynamic/nbactions.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/nbactions.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,29 @@ + + + + + run + + process-classes + org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/demo-calculator-dynamic/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,58 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + demo.calculator + 0.3-SNAPSHOT + jar + + JavaQuery Demo - Calculator + http://maven.apache.org + + + UTF-8 + + + + + org.apidesign.bck2brwsr + mojo + 0.3-SNAPSHOT + + + + brwsr + + + + + org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + + + + org.apidesign.bck2brwsr + emul + 0.3-SNAPSHOT + + + org.apidesign.bck2brwsr + javaquery.api + 0.3-SNAPSHOT + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,89 @@ +/** + * 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.mavenhtml; + +import org.apidesign.bck2brwsr.htmlpage.api.On; +import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; +import org.apidesign.bck2brwsr.htmlpage.api.Page; + +/** HTML5 & Java demo showing the power of + * annotation processors + * as well as other goodies. + * + * @author Jaroslav Tulach + */ +@Page(xhtml="Calculator.xhtml") +public class App { + private static double memory; + private static String operation; + + @On(event = CLICK, id="clear") + static void clear() { + memory = 0; + operation = null; + Calculator.DISPLAY.setValue("0"); + } + + @On(event = CLICK, id= { "plus", "minus", "mul", "div" }) + static void applyOp(String op) { + memory = getValue(); + operation = op; + Calculator.DISPLAY.setValue("0"); + } + + @On(event = CLICK, id="result") + static void computeTheValue() { + switch (operation) { + case "plus": setValue(memory + getValue()); break; + case "minus": setValue(memory - getValue()); break; + case "mul": setValue(memory * getValue()); break; + case "div": setValue(memory / getValue()); break; + default: throw new IllegalStateException(operation); + } + } + + @On(event = CLICK, id={"n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9"}) + static void addDigit(String digit) { + digit = digit.substring(1); + String v = Calculator.DISPLAY.getValue(); + if (getValue() == 0.0) { + Calculator.DISPLAY.setValue(digit); + } else { + Calculator.DISPLAY.setValue(v + digit); + } + } + + private static void setValue(double v) { + StringBuilder sb = new StringBuilder(); + sb.append(v); + if (sb.toString().endsWith(".0")) { + final int l = sb.length(); + sb.delete(l - 2, l); + } + Calculator.DISPLAY.setValue(sb.toString()); + } + + private static double getValue() { + try { + return Double.parseDouble(Calculator.DISPLAY.getValue()); + } catch (NumberFormatException ex) { + Calculator.DISPLAY.setValue("err"); + return 0.0; + } + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaquery/demo-calculator-dynamic/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,158 @@ + + + + + + Simple Calculator in HTML5 and Java + + + + +

    Java and HTML5 - Together at Last!

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + + + + +
    +
    +    package org.apidesign.bck2brwsr.mavenhtml;
    +
    +    import org.apidesign.bck2brwsr.htmlpage.api.OnClick;
    +    import org.apidesign.bck2brwsr.htmlpage.api.Page;
    +
    +    /** HTML5 & Java demo showing the power of annotation processors
    +     * as well as other goodies, including type-safe association between
    +     * an XHTML page and Java.
    +     * 
    +     * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    +     */
    +    @Page(xhtml="Calculator.xhtml")
    +    public class App {
    +        private static double memory;
    +        private static String operation;
    +
    +        @OnClick(id="clear")
    +        static void clear() {
    +            memory = 0;
    +            operation = null;
    +            Calculator.DISPLAY.setValue("0");
    +        }
    +
    +        @OnClick(id= { "plus", "minus", "mul", "div" })
    +        static void applyOp(String op) {
    +            memory = getValue();
    +            operation = op;
    +            Calculator.DISPLAY.setValue("0");
    +        }
    +
    +        @OnClick(id="result")
    +        static void computeTheValue() {
    +            switch (operation) {
    +                case "plus": setValue(memory + getValue()); break;
    +                case "minus": setValue(memory - getValue()); break;
    +                case "mul": setValue(memory * getValue()); break;
    +                case "div": setValue(memory / getValue()); break;
    +                default: throw new IllegalStateException(operation);
    +            }
    +        }
    +
    +        @OnClick(id={"n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9"}) 
    +        static void addDigit(String digit) {
    +            digit = digit.substring(1);
    +            String v = Calculator.DISPLAY.getValue();
    +            if (getValue() == 0.0) {
    +                Calculator.DISPLAY.setValue(digit);
    +            } else {
    +                Calculator.DISPLAY.setValue(v + digit);
    +            }
    +        }
    +
    +        private static void setValue(double v) {
    +            StringBuilder sb = new StringBuilder();
    +            sb.append(v);
    +            Calculator.DISPLAY.setValue(sb.toString());
    +        }
    +
    +        private static double getValue() {
    +            try {
    +                return Double.parseDouble(Calculator.DISPLAY.getValue());
    +            } catch (NumberFormatException ex) {
    +                Calculator.DISPLAY.setValue("err");
    +                return 0.0;
    +            }
    +        }
    +    }
    +
    +    
    + + diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/demo-calculator/pom.xml --- a/javaquery/demo-calculator/pom.xml Fri Jan 18 14:23:18 2013 +0100 +++ b/javaquery/demo-calculator/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -4,11 +4,11 @@ 4.0.0 org.apidesign.bck2brwsr - demo.calculator + demo.static.calculator 0.3-SNAPSHOT jar - JavaQuery Demo - Calculator + JavaQuery Demo - Calculator - Static Compilation http://maven.apache.org diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Fri Jan 18 14:23:18 2013 +0100 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Fri Jan 18 14:27:22 2013 +0100 @@ -17,7 +17,8 @@ */ package org.apidesign.bck2brwsr.mavenhtml; -import org.apidesign.bck2brwsr.htmlpage.api.OnClick; +import org.apidesign.bck2brwsr.htmlpage.api.On; +import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; import org.apidesign.bck2brwsr.htmlpage.api.Page; /** HTML5 & Java demo showing the power of @@ -31,21 +32,21 @@ private static double memory; private static String operation; - @OnClick(id="clear") + @On(event = CLICK, id="clear") static void clear() { memory = 0; operation = null; Calculator.DISPLAY.setValue("0"); } - @OnClick(id= { "plus", "minus", "mul", "div" }) + @On(event = CLICK, id= { "plus", "minus", "mul", "div" }) static void applyOp(String op) { memory = getValue(); operation = op; Calculator.DISPLAY.setValue("0"); } - @OnClick(id="result") + @On(event = CLICK, id="result") static void computeTheValue() { switch (operation) { case "plus": setValue(memory + getValue()); break; @@ -56,7 +57,7 @@ } } - @OnClick(id={"n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9"}) + @On(event = CLICK, id={"n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9"}) static void addDigit(String digit) { digit = digit.substring(1); String v = Calculator.DISPLAY.getValue(); diff -r 1a73c6e08cc5 -r 95b68e21a53f javaquery/pom.xml --- a/javaquery/pom.xml Fri Jan 18 14:23:18 2013 +0100 +++ b/javaquery/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -14,5 +14,6 @@ api demo-calculator + demo-calculator-dynamic diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,49 @@ + + + 4.0.0 + + org.apidesign + bck2brwsr + 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + launcher + 0.3-SNAPSHOT + Bck2Brwsr Launcher + http://maven.apache.org + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + + UTF-8 + + + + junit + junit + 3.8.1 + test + + + org.glassfish.grizzly + grizzly-http-server + 2.2.19 + + + ${project.groupId} + vm4brwsr + ${project.version} + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,455 @@ +/** + * 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.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apidesign.vm4brwsr.Bck2Brwsr; +import org.glassfish.grizzly.PortRange; +import org.glassfish.grizzly.http.server.HttpHandler; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.http.server.NetworkListener; +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.grizzly.http.server.ServerConfiguration; + +/** + * Lightweight server to launch Bck2Brwsr applications and tests. + * Supports execution in native browser as well as Java's internal + * execution engine. + */ +final class Bck2BrwsrLauncher extends Launcher implements Closeable { + private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName()); + private static final MethodInvocation END = new MethodInvocation(null, null); + private Set loaders = new LinkedHashSet<>(); + private BlockingQueue methods = new LinkedBlockingQueue<>(); + private long timeOut; + private final Res resources = new Res(); + private final String cmd; + private Object[] brwsr; + private HttpServer server; + private CountDownLatch wait; + + public Bck2BrwsrLauncher(String cmd) { + this.cmd = cmd; + } + + @Override + public MethodInvocation addMethod(Class clazz, String method) throws IOException { + loaders.add(clazz.getClassLoader()); + MethodInvocation c = new MethodInvocation(clazz.getName(), method); + methods.add(c); + try { + c.await(timeOut); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + return c; + } + + public void setTimeout(long ms) { + timeOut = ms; + } + + public void addClassLoader(ClassLoader url) { + this.loaders.add(url); + } + + public void showURL(String startpage) throws IOException { + if (!startpage.startsWith("/")) { + startpage = "/" + startpage; + } + HttpServer s = initServer(); + s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/"); + try { + launchServerAndBrwsr(s, startpage); + } catch (URISyntaxException | InterruptedException ex) { + throw new IOException(ex); + } + } + + @Override + public void initialize() throws IOException { + try { + executeInBrowser(); + } catch (InterruptedException ex) { + final InterruptedIOException iio = new InterruptedIOException(ex.getMessage()); + iio.initCause(ex); + throw iio; + } catch (Exception ex) { + if (ex instanceof IOException) { + throw (IOException)ex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + throw new IOException(ex); + } + } + + private HttpServer initServer() { + HttpServer s = HttpServer.createSimpleServer(".", new PortRange(8080, 65535)); + + final ServerConfiguration conf = s.getServerConfiguration(); + conf.addHttpHandler(new Page(resources, + "org/apidesign/bck2brwsr/launcher/console.xhtml", + "org.apidesign.bck2brwsr.launcher.Console", "welcome", "false" + ), "/console"); + conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); + conf.addHttpHandler(new VMInit(), "/vm.js"); + conf.addHttpHandler(new Classes(resources), "/classes/"); + return s; + } + + private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { + wait = new CountDownLatch(1); + server = initServer(); + ServerConfiguration conf = server.getServerConfiguration(); + conf.addHttpHandler(new Page(resources, + "org/apidesign/bck2brwsr/launcher/harness.xhtml" + ), "/execute"); + conf.addHttpHandler(new HttpHandler() { + int cnt; + List cases = new ArrayList<>(); + @Override + public void service(Request request, Response response) throws Exception { + String id = request.getParameter("request"); + String value = request.getParameter("result"); + + if (id != null && value != null) { + LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); + value = decodeURL(value); + cases.get(Integer.parseInt(id)).result(value, null); + } + + MethodInvocation mi = methods.take(); + if (mi == END) { + response.getWriter().write(""); + wait.countDown(); + cnt = 0; + LOG.log(Level.INFO, "End of data reached. Exiting."); + return; + } + + cases.add(mi); + final String cn = mi.className; + final String mn = mi.methodName; + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{cnt, cn, mn}); + response.getWriter().write("{" + + "className: '" + cn + "', " + + "methodName: '" + mn + "', " + + "request: " + cnt + + "}"); + cnt++; + } + }, "/data"); + + this.brwsr = launchServerAndBrwsr(server, "/execute"); + } + + @Override + public void shutdown() throws IOException { + methods.offer(END); + for (;;) { + int prev = methods.size(); + try { + if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) { + break; + } + } catch (InterruptedException ex) { + throw new IOException(ex); + } + if (prev == methods.size()) { + LOG.log( + Level.WARNING, + "Timeout and no test has been executed meanwhile (at {0}). Giving up.", + methods.size() + ); + break; + } + LOG.log(Level.INFO, + "Timeout, but tests got from {0} to {1}. Trying again.", + new Object[]{prev, methods.size()} + ); + } + stopServerAndBrwsr(server, brwsr); + } + + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { + for (;;) { + int ch = is.read(); + if (ch == -1) { + break; + } + if (ch == '$') { + int cnt = is.read() - '0'; + if (cnt == 'U' - '0') { + os.write(baseURL.getBytes()); + } + if (cnt < params.length) { + os.write(params[cnt].getBytes()); + } + } else { + os.write(ch); + } + } + } + + private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException { + server.start(); + NetworkListener listener = server.getListeners().iterator().next(); + int port = listener.getPort(); + + URI uri = new URI("http://localhost:" + port + page); + LOG.log(Level.INFO, "Showing {0}", uri); + if (cmd == null) { + try { + java.awt.Desktop.getDesktop().browse(uri); + LOG.log(Level.INFO, "Desktop.browse successfully finished"); + return null; + } catch (UnsupportedOperationException ex) { + LOG.log(Level.INFO, "Desktop.browse not supported", ex); + } + } + { + String cmdName = cmd == null ? "xdg-open" : cmd; + String[] cmdArr = { + cmdName, uri.toString() + }; + LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); + final Process process = Runtime.getRuntime().exec(cmdArr); + return new Object[] { process, null }; + } + } + + private static String decodeURL(String s) { + for (;;) { + int pos = s.indexOf('%'); + if (pos == -1) { + return s; + } + int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16); + s = s.substring(0, pos) + (char)i + s.substring(pos + 2); + } + } + + private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException { + if (brwsr == null) { + return; + } + Process process = (Process)brwsr[0]; + + server.stop(); + InputStream stdout = process.getInputStream(); + InputStream stderr = process.getErrorStream(); + drain("StdOut", stdout); + drain("StdErr", stderr); + process.destroy(); + int res; + try { + res = process.waitFor(); + } catch (InterruptedException ex) { + throw new IOException(ex); + } + LOG.log(Level.INFO, "Exit code: {0}", res); + + deleteTree((File)brwsr[1]); + } + + private static void drain(String name, InputStream is) throws IOException { + int av = is.available(); + if (av > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("v== ").append(name).append(" ==v\n"); + while (av-- > 0) { + sb.append((char)is.read()); + } + sb.append("\n^== ").append(name).append(" ==^"); + LOG.log(Level.INFO, sb.toString()); + } + } + + private void deleteTree(File file) { + if (file == null) { + return; + } + File[] arr = file.listFiles(); + if (arr != null) { + for (File s : arr) { + deleteTree(s); + } + } + file.delete(); + } + + @Override + public void close() throws IOException { + shutdown(); + } + + private class Res implements Bck2Brwsr.Resources { + @Override + public InputStream get(String resource) throws IOException { + for (ClassLoader l : loaders) { + URL u = null; + Enumeration en = l.getResources(resource); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u != null) { + return u.openStream(); + } + } + throw new IOException("Can't find " + resource); + } + } + + private static class Page extends HttpHandler { + private final String resource; + private final String[] args; + private final Res res; + + public Page(Res res, String resource, String... args) { + this.res = res; + this.resource = resource; + this.args = args; + } + + @Override + public void service(Request request, Response response) throws Exception { + String r = resource; + if (r == null) { + r = request.getHttpHandlerPath(); + if (r.startsWith("/")) { + r = r.substring(1); + } + } + if (r.endsWith(".html")) { + response.setContentType("text/html"); + LOG.info("Content type text/html"); + } + if (r.endsWith(".xhtml")) { + response.setContentType("application/xhtml+xml"); + LOG.info("Content type application/xhtml+xml"); + } + OutputStream os = response.getOutputStream(); + try (InputStream is = res.get(r)) { + copyStream(is, os, request.getRequestURL().toString(), args); + } catch (IOException ex) { + response.setDetailMessage(ex.getLocalizedMessage()); + response.setError(); + response.setStatus(404); + } + } + } + + private static class VM extends HttpHandler { + private final Res loader; + + public VM(Res loader) { + this.loader = loader; + } + + @Override + public void service(Request request, Response response) throws Exception { + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/javascript"); + Bck2Brwsr.generate(response.getWriter(), loader); + } + } + private static class VMInit extends HttpHandler { + public VMInit() { + } + + @Override + public void service(Request request, Response response) throws Exception { + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/javascript"); + response.getWriter().append( + "function ldCls(res) {\n" + + " var request = new XMLHttpRequest();\n" + + " request.open('GET', '/classes/' + res, false);\n" + + " request.send();\n" + + " var arr = eval('(' + request.responseText + ')');\n" + + " return arr;\n" + + "}\n" + + "var vm = new bck2brwsr(ldCls);\n"); + } + } + + private static class Classes extends HttpHandler { + private final Res loader; + + public Classes(Res loader) { + this.loader = loader; + } + + @Override + public void service(Request request, Response response) throws Exception { + String res = request.getHttpHandlerPath(); + if (res.startsWith("/")) { + res = res.substring(1); + } + try (InputStream is = loader.get(res)) { + response.setContentType("text/javascript"); + Writer w = response.getWriter(); + w.append("["); + for (int i = 0;; i++) { + int b = is.read(); + if (b == -1) { + break; + } + if (i > 0) { + w.append(", "); + } + if (i % 20 == 0) { + w.write("\n"); + } + if (b > 127) { + b = b - 256; + } + w.append(Integer.toString(b)); + } + w.append("\n]"); + } catch (IOException ex) { + response.setError(); + response.setDetailMessage(ex.getMessage()); + } + } + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,208 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Enumeration; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +public class Console { + public static String welcome() { + return "HellofromBck2Brwsr"; + } + public static String multiply() { + return String.valueOf(Integer.MAX_VALUE / 2 + Integer.MAX_VALUE); + } + + @JavaScriptBody(args = {"id", "attr"}, body = + "return window.document.getElementById(id)[attr].toString();") + private static native Object getAttr(String id, String attr); + + @JavaScriptBody(args = {"id", "attr", "value"}, body = + "window.document.getElementById(id)[attr] = value;") + private static native void setAttr(String id, String attr, Object value); + + @JavaScriptBody(args = {}, body = "return; window.close();") + private static native void closeWindow(); + + private static void log(String newText) { + String id = "result"; + String attr = "value"; + setAttr(id, attr, getAttr(id, attr) + "\n" + newText); + setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); + } + + public static void execute() throws Exception { + String clazz = (String) getAttr("clazz", "value"); + String method = (String) getAttr("method", "value"); + Object res = invokeMethod(clazz, method); + setAttr("result", "value", res); + } + + public static void harness(String url) { + log("Connecting to " + url); + try { + URL u = new URL(url); + for (;;) { + String data = (String) u.getContent(new Class[] { String.class }); + log("\nGot \"" + data + "\""); + if (data.isEmpty()) { + log("No data, exiting"); + closeWindow(); + break; + } + + Case c = Case.parseData(data); + log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); + + Object result = invokeMethod(c.getClassName(), c.getMethodName()); + + log("Result: " + result); + + result = encodeURL("" + result); + + log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); + u = new URL(url + "?request=" + c.getRequestId() + "&result=" + result); + } + + + } catch (Exception ex) { + log(ex.getMessage()); + } + } + + private static String encodeURL(String r) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < r.length(); i++) { + int ch = r.charAt(i); + if (ch < 32 || ch == '%' || ch == '+') { + sb.append("%").append(("0" + Integer.toHexString(ch)).substring(0, 2)); + } else { + if (ch == 32) { + sb.append("+"); + } else { + sb.append((char)ch); + } + } + } + return sb.toString(); + } + + static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { + final Object r = invokeMethod(clazz, method); + return r == null ? "null" : r.toString().toString(); + } + + /** Helper method that inspects the classpath and loads given resource + * (usually a class file). Used while running tests in Rhino. + * + * @param name resource name to find + * @return the array of bytes in the given resource + * @throws IOException I/O in case something goes wrong + */ + public static byte[] read(String name) throws IOException { + URL u = null; + Enumeration en = Console.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + try (InputStream is = u.openStream()) { + byte[] arr; + arr = new byte[is.available()]; + int offset = 0; + while (offset < arr.length) { + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + throw new IOException("Can't read " + name); + } + offset += len; + } + return arr; + } + } + + private static Object invokeMethod(String clazz, String method) + throws ClassNotFoundException, InvocationTargetException, + SecurityException, IllegalAccessException, IllegalArgumentException, + InstantiationException { + Method found = null; + Class c = Class.forName(clazz); + for (Method m : c.getMethods()) { + if (m.getName().equals(method)) { + found = m; + } + } + Object res; + if (found != null) { + try { + if ((found.getModifiers() & Modifier.STATIC) != 0) { + res = found.invoke(null); + } else { + res = found.invoke(c.newInstance()); + } + } catch (Exception ex) { + res = ex.getClass().getName() + ":" + ex.getMessage(); + } + } else { + res = "Can't find method " + method + " in " + clazz; + } + return res; + } + + private static final class Case { + private final Object data; + + private Case(Object data) { + this.data = data; + } + + public static Case parseData(String s) { + return new Case(toJSON(s)); + } + + public String getMethodName() { + return value("methodName", data); + } + + public String getClassName() { + return value("className", data); + } + + public String getRequestId() { + return value("request", data); + } + + @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") + private static native Object toJSON(String s); + + @JavaScriptBody(args = {"p", "d"}, body = "return d[p].toString();") + private static native String value(String p, Object d); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,127 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.logging.Logger; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** + * Tests execution in Java's internal scripting engine. + */ +final class JSLauncher extends Launcher { + private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName()); + private Set loaders = new LinkedHashSet<>(); + private final Res resources = new Res(); + private Invocable code; + private StringBuilder codeSeq; + private Object console; + + + @Override + public MethodInvocation addMethod(Class clazz, String method) { + loaders.add(clazz.getClassLoader()); + MethodInvocation mi = new MethodInvocation(clazz.getName(), method); + try { + mi.result(code.invokeMethod( + console, + "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2", + mi.className, mi.methodName).toString(), null); + } catch (ScriptException | NoSuchMethodException ex) { + mi.result(null, ex); + } + return mi; + } + + public void addClassLoader(ClassLoader url) { + this.loaders.add(url); + } + + @Override + public void initialize() throws IOException { + try { + initRhino(); + } catch (Exception ex) { + if (ex instanceof IOException) { + throw (IOException)ex; + } + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + throw new IOException(ex); + } + } + + private void initRhino() throws IOException, ScriptException, NoSuchMethodException { + StringBuilder sb = new StringBuilder(); + Bck2Brwsr.generate(sb, new Res()); + + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine mach = sem.getEngineByExtension("js"); + + sb.append( + "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.Console.read);" + + "\nfunction initVM() { return vm; };" + + "\n"); + + Object res = mach.eval(sb.toString()); + if (!(mach instanceof Invocable)) { + throw new IOException("It is invocable object: " + res); + } + code = (Invocable) mach; + codeSeq = sb; + + Object vm = code.invokeFunction("initVM"); + console = code.invokeMethod(vm, "loadClass", Console.class.getName()); + } + + @Override + public void shutdown() throws IOException { + } + + @Override + public String toString() { + return codeSeq.toString(); + } + + private class Res implements Bck2Brwsr.Resources { + @Override + public InputStream get(String resource) throws IOException { + for (ClassLoader l : loaders) { + URL u = null; + Enumeration en = l.getResources(resource); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u != null) { + return u.openStream(); + } + } + throw new IOException("Can't find " + resource); + } + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,63 @@ +/** + * 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.Closeable; +import java.io.IOException; +import java.net.URLClassLoader; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** An abstraction for executing tests in a Bck2Brwsr virtual machine. + * Either in JavaScript engine, or in external browser. + * + * @author Jaroslav Tulach + */ +public abstract class Launcher { + + Launcher() { + } + + abstract MethodInvocation addMethod(Class clazz, String method) throws IOException; + + public abstract void initialize() throws IOException; + public abstract void shutdown() throws IOException; + public MethodInvocation invokeMethod(Class clazz, String method) throws IOException { + return addMethod(clazz, method); + } + + + + public static Launcher createJavaScript() { + final JSLauncher l = new JSLauncher(); + l.addClassLoader(Bck2Brwsr.class.getClassLoader()); + return l; + } + + public static Launcher createBrowser(String cmd) { + final Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(cmd); + l.addClassLoader(Bck2Brwsr.class.getClassLoader()); + l.setTimeout(180000); + return l; + } + public static Closeable showURL(URLClassLoader classes, String startpage) throws IOException { + Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); + l.addClassLoader(classes); + l.showURL(startpage); + return l; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,57 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * + * @author Jaroslav Tulach + */ +public final class MethodInvocation { + final CountDownLatch wait = new CountDownLatch(1); + final String className; + final String methodName; + private String result; + private Throwable exception; + + MethodInvocation(String className, String methodName) { + this.className = className; + this.methodName = methodName; + } + + void await(long timeOut) throws InterruptedException { + wait.await(timeOut, TimeUnit.MILLISECONDS); + } + + void result(String r, Throwable e) { + this.result = r; + this.exception = e; + wait.countDown(); + } + + @Override + public String toString() { + if (exception != null) { + return exception.toString(); + } + return result; + } + +} diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/console.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/console.xhtml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,52 @@ + + + + + + Bck2Brwsr Launcher + + + + + +

    Bck2Browser Console Launcher

    + + Class Name: + +
    + Method Name: + + +
    + + + +
    + + + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,39 @@ + + + + + + Bck2Brwsr Harness + + + + + +

    Bck2Browser Execution Harness

    + + + + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/pom.xml --- a/mojo/pom.xml Fri Jan 18 14:23:18 2013 +0100 +++ b/mojo/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -11,7 +11,7 @@ mojo 0.3-SNAPSHOT maven-plugin - Maven Mojo to Compile to JavaScript + Bck2Brwsr Maven Project http://maven.apache.org @@ -77,5 +77,10 @@ 3.0.2 jar + + ${project.groupId} + launcher + ${project.version} + diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,89 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.mojo; + +import java.io.Closeable; +import org.apache.maven.plugin.AbstractMojo; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apidesign.bck2brwsr.launcher.Launcher; + +/** Executes given HTML page in a browser. */ +@Mojo(name="brwsr", defaultPhase=LifecyclePhase.DEPLOY) +public class BrswrMojo extends AbstractMojo { + public BrswrMojo() { + } + /** Resource to show as initial page */ + @Parameter + private String startpage; + + @Parameter(defaultValue="${project}") + private MavenProject prj; + + /** Root of the class files */ + @Parameter(defaultValue="${project.build.directory}/classes") + private File classes; + + @Override + public void execute() throws MojoExecutionException { + if (startpage == null) { + throw new MojoExecutionException("You have to provide a start page"); + } + + try { + URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); + + Closeable httpServer; + try { + httpServer = Launcher.showURL(url, startpage()); + } catch (Exception ex) { + throw new MojoExecutionException("Can't open " + startpage(), ex); + } + System.in.read(); + httpServer.close(); + } catch (IOException ex) { + throw new MojoExecutionException("Can't show the browser", ex); + } + } + + private String startpage() { + return startpage; + } + + private static URLClassLoader buildClassLoader(File root, Collection deps) throws MalformedURLException { + List arr = new ArrayList(); + arr.add(root.toURI().toURL()); + for (Artifact a : deps) { + arr.add(a.getFile().toURI().toURL()); + } + return new URLClassLoader(arr.toArray(new URL[0]), BrswrMojo.class.getClassLoader()); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/src/main/resources/META-INF/maven/archetype-metadata.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/META-INF/maven/archetype-metadata.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,42 @@ + + + + + + src/main/java + + **/*.java + + + + src/main/resources + + **/*.xhtml + + + + + + nbactions.xml + + + + \ No newline at end of file diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/src/main/resources/archetype-resources/nbactions.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/nbactions.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,10 @@ + + + + run + + process-classes + org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/src/main/resources/archetype-resources/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,57 @@ + + + 4.0.0 + + ${groupId} + ${artifactId} + ${version} + jar + + ${artifactId} + + + UTF-8 + + + + + org.apidesign.bck2brwsr + mojo + 0.3-SNAPSHOT + + + + brwsr + + + + + ${package.replace('.','/')}/index.xhtml + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + + + + org.apidesign.bck2brwsr + emul + 0.3-SNAPSHOT + + + org.apidesign.bck2brwsr + javaquery.api + 0.3-SNAPSHOT + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/src/main/resources/archetype-resources/src/main/java/App.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/src/main/java/App.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,17 @@ +package ${package}; + +import org.apidesign.bck2brwsr.htmlpage.api.*; +import static org.apidesign.bck2brwsr.htmlpage.api.OnEvent.*; +import org.apidesign.bck2brwsr.htmlpage.api.Page; + +/** Edit the index.xhtml file. Use 'id' to name certain HTML elements. + * Use this class to define behavior of the elements. + */ +@Page(xhtml="index.xhtml", className="Index") +public class App { + @On(event = CLICK, id="hello") + static void hello() { + Index.HELLO.setDisabled(true); + Element.alert("Hello World!"); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f mojo/src/main/resources/archetype-resources/src/main/resources/index.xhtml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mojo/src/main/resources/archetype-resources/src/main/resources/index.xhtml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,16 @@ + + + + + Bck2Brwsr's Hello World + + + + + + + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f pom.xml --- a/pom.xml Fri Jan 18 14:23:18 2013 +0100 +++ b/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -14,6 +14,8 @@ javaquery javap benchmarks + launcher + vmtest @@ -71,6 +73,8 @@ javap/** * .*/** + mojo/src/main/resources/archetype-resources/** + vmtest/src/test/resources/** @@ -108,4 +112,4 @@ COPYING - + \ No newline at end of file diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Jan 18 14:27:22 2013 +0100 @@ -37,7 +37,7 @@ * return null; // byte[] for the resource * }); * - * In this scenario, when a request for a unknown class is made, the loader + * In this scenario, when a request for an unknown class is made, the loader * function is asked for its byte code and the system dynamically transforms * it to JavaScript. * diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Jan 18 14:27:22 2013 +0100 @@ -26,6 +26,8 @@ import org.apidesign.javap.MethodData; import org.apidesign.javap.StackMapIterator; import static org.apidesign.javap.RuntimeConstants.*; +import org.apidesign.javap.TrapData; +import org.apidesign.javap.TrapDataIterator; /** Translator of the code inside class files to JavaScript. * @@ -64,6 +66,17 @@ /* protected */ String accessClass(String classOperation) { return classOperation; } + + /** Prints out a debug message. + * + * @param msg the message + * @return true if the message has been printed + * @throws IOException + */ + boolean debug(String msg) throws IOException { + out.append(msg); + return true; + } /** * Converts a given class file to a JavaScript version. @@ -76,6 +89,11 @@ public String compile(InputStream classFile) throws IOException { this.jc = new ClassData(classFile); + 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)." + ); + } byte[] arrData = jc.findAnnotationData(true); String[] arr = findAnnotation(arrData, jc, "org.apidesign.bck2brwsr.core.ExtraJavaScript", @@ -129,18 +147,27 @@ } continue; } + String prefix; String mn; if (m.isStatic()) { - mn = generateStaticMethod("\n c.", m, toInitilize); + prefix = "\n c."; + mn = generateStaticMethod(prefix, m, toInitilize); } else { - mn = generateInstanceMethod("\n c.", m); + if (m.isConstructor()) { + prefix = "\n CLS."; + mn = generateInstanceMethod(prefix, m); + } else { + prefix = "\n c."; + mn = generateInstanceMethod(prefix, m); + } } byte[] runAnno = m.findAnnotationData(false); if (runAnno != null) { - out.append("\n c.").append(mn).append(".anno = {"); + out.append(prefix).append(mn).append(".anno = {"); generateAnno(jc, out, runAnno); out.append("\n };"); } + out.append(prefix).append(mn).append(".access = " + m.getAccess()).append(";"); } out.append("\n c.constructor = CLS;"); out.append("\n c.$instOf_").append(className).append(" = true;"); @@ -151,6 +178,7 @@ out.append(accessClass("java_lang_Class(true);")); out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';"); out.append("\n CLS.$class.superclass = sprcls;"); + out.append("\n CLS.$class.access = ").append(jc.getAccessFlags()+";"); out.append("\n CLS.$class.cnstr = CLS;"); byte[] classAnno = jc.findAnnotationData(false); if (classAnno != null) { @@ -217,17 +245,19 @@ private void generateMethod(String prefix, String name, MethodData m) throws IOException { final StackMapIterator stackMapIterator = m.createStackMapIterator(); + TrapDataIterator trap = m.getTrapDataIterator(); final LocalsMapper lmapper = new LocalsMapper(stackMapIterator.getArguments()); out.append(prefix).append(name).append(" = function("); - lmapper.outputArguments(out); + lmapper.outputArguments(out, m.isStatic()); out.append(") {").append("\n"); final byte[] byteCodes = m.getCode(); if (byteCodes == null) { out.append(" throw 'no code found for ") - .append(m.getInternalSig()).append("';\n"); + .append(jc.getClassName()).append('.') + .append(m.getName()).append("';\n"); out.append("};"); return; } @@ -246,6 +276,9 @@ out.append(';'); } } + if (!m.isStatic()) { + out.append(" var ").append(" lcA0 = this;\n"); + } // maxStack includes two stack positions for every pushed long / double // so this might generate more stack variables than we need @@ -263,18 +296,31 @@ } int lastStackFrame = -1; - + TrapData[] previousTrap = null; + out.append("\n var gt = 0;\n for(;;) switch(gt) {\n"); for (int i = 0; i < byteCodes.length; i++) { int prev = i; stackMapIterator.advanceTo(i); + boolean changeInCatch = trap.advanceTo(i); + if (changeInCatch || lastStackFrame != stackMapIterator.getFrameIndex()) { + if (previousTrap != null) { + generateCatch(previousTrap); + previousTrap = null; + } + } if (lastStackFrame != stackMapIterator.getFrameIndex()) { lastStackFrame = stackMapIterator.getFrameIndex(); lmapper.syncWithFrameLocals(stackMapIterator.getFrameLocals()); smapper.syncWithFrameStack(stackMapIterator.getFrameStack()); - out.append(" case " + i).append(": "); + out.append(" case " + i).append(": "); + changeInCatch = true; } else { - out.append(" /* " + i).append(" */ "); + debug(" /* " + i + " */ "); + } + if (changeInCatch && trap.useTry()) { + out.append("try {"); + previousTrap = trap.current(); } final int c = readByte(byteCodes, i); switch (c) { @@ -449,7 +495,7 @@ emit(out, "@1 = @2;", lmapper.setD(3), smapper.popD()); break; case opc_iadd: - emit(out, "@1 += @2;", smapper.getI(1), smapper.popI()); + emit(out, "@1 = @1.add32(@2);", smapper.getI(1), smapper.popI()); break; case opc_ladd: emit(out, "@1 += @2;", smapper.getL(1), smapper.popL()); @@ -461,7 +507,7 @@ emit(out, "@1 += @2;", smapper.getD(1), smapper.popD()); break; case opc_isub: - emit(out, "@1 -= @2;", smapper.getI(1), smapper.popI()); + emit(out, "@1 = @1.sub32(@2);", smapper.getI(1), smapper.popI()); break; case opc_lsub: emit(out, "@1 -= @2;", smapper.getL(1), smapper.popL()); @@ -473,7 +519,7 @@ emit(out, "@1 -= @2;", smapper.getD(1), smapper.popD()); break; case opc_imul: - emit(out, "@1 *= @2;", smapper.getI(1), smapper.popI()); + emit(out, "@1 = @1.mul32(@2);", smapper.getI(1), smapper.popI()); break; case opc_lmul: emit(out, "@1 *= @2;", smapper.getL(1), smapper.popL()); @@ -630,9 +676,13 @@ smapper.popD(), smapper.pushL()); break; case opc_i2b: + emit(out, "@1 = @1.toInt8();", smapper.getI(0)); + break; case opc_i2c: + out.append("{ /* number conversion */ }"); + break; case opc_i2s: - out.append("/* number conversion */"); + emit(out, "@1 = @1.toInt16();", smapper.getI(0)); break; case opc_aconst_null: emit(out, "@1 = null;", smapper.pushA()); @@ -863,28 +913,53 @@ break; } case opc_newarray: - ++i; // skip type of array - emit(out, "@2 = new Array(@1).fillNulls();", - smapper.popI(), smapper.pushA()); + int atype = readByte(byteCodes, ++i); + String jvmType; + switch (atype) { + case 4: jvmType = "[Z"; break; + case 5: jvmType = "[C"; break; + case 6: jvmType = "[F"; break; + case 7: jvmType = "[D"; break; + case 8: jvmType = "[B"; break; + case 9: jvmType = "[S"; break; + case 10: jvmType = "[I"; break; + case 11: jvmType = "[J"; break; + default: throw new IllegalStateException("Array type: " + atype); + } + emit(out, "@2 = new Array(@1).initWith('@3', 0);", + smapper.popI(), smapper.pushA(), jvmType); break; - case opc_anewarray: - i += 2; // skip type of array - emit(out, "@2 = new Array(@1).fillNulls();", - smapper.popI(), smapper.pushA()); + case opc_anewarray: { + int type = readIntArg(byteCodes, i); + i += 2; + String typeName = jc.getClassName(type); + if (typeName.startsWith("[")) { + typeName = "[" + typeName; + } else { + typeName = "[L" + typeName + ";"; + } + emit(out, "@2 = new Array(@1).initWith('@3', null);", + smapper.popI(), smapper.pushA(), typeName); break; + } case opc_multianewarray: { + int type = readIntArg(byteCodes, i); i += 2; + String typeName = jc.getClassName(type); int dim = readByte(byteCodes, ++i); out.append("{ var a0 = new Array(").append(smapper.popI()) - .append(").fillNulls();"); + .append(").initWith('").append(typeName).append("', null);"); for (int d = 1; d < dim; d++) { + typeName = typeName.substring(1); out.append("\n var l" + d).append(" = ") .append(smapper.popI()).append(';'); out.append("\n for (var i" + d).append (" = 0; i" + d). append(" < a" + (d - 1)). append(".length; i" + d).append("++) {"); out.append("\n var a" + d). - append (" = new Array(l" + d).append(").fillNulls();"); + append (" = new Array(l" + d).append(").initWith('") + .append(typeName).append("', ") + .append(typeName.length() == 2 ? "0" : "null").append(");"); out.append("\n a" + (d - 1)).append("[i" + d).append("] = a" + d). append(";"); } @@ -898,55 +973,55 @@ emit(out, "@2 = @1.length;", smapper.popA(), smapper.pushI()); break; case opc_lastore: - emit(out, "@3[@2] = @1;", + emit(out, "@3.at(@2, @1);", smapper.popL(), smapper.popI(), smapper.popA()); break; case opc_fastore: - emit(out, "@3[@2] = @1;", + emit(out, "@3.at(@2, @1);", smapper.popF(), smapper.popI(), smapper.popA()); break; case opc_dastore: - emit(out, "@3[@2] = @1;", + emit(out, "@3.at(@2, @1);", smapper.popD(), smapper.popI(), smapper.popA()); break; case opc_aastore: - emit(out, "@3[@2] = @1;", + emit(out, "@3.at(@2, @1);", smapper.popA(), smapper.popI(), smapper.popA()); break; case opc_iastore: case opc_bastore: case opc_castore: case opc_sastore: - emit(out, "@3[@2] = @1;", + emit(out, "@3.at(@2, @1);", smapper.popI(), smapper.popI(), smapper.popA()); break; case opc_laload: - emit(out, "@3 = @2[@1];", + emit(out, "@3 = @2.at(@1);", smapper.popI(), smapper.popA(), smapper.pushL()); break; case opc_faload: - emit(out, "@3 = @2[@1];", + emit(out, "@3 = @2.at(@1);", smapper.popI(), smapper.popA(), smapper.pushF()); break; case opc_daload: - emit(out, "@3 = @2[@1];", + emit(out, "@3 = @2.at(@1);", smapper.popI(), smapper.popA(), smapper.pushD()); break; case opc_aaload: - emit(out, "@3 = @2[@1];", + emit(out, "@3 = @2.at(@1);", smapper.popI(), smapper.popA(), smapper.pushA()); break; case opc_iaload: case opc_baload: case opc_caload: case opc_saload: - emit(out, "@3 = @2[@1];", + emit(out, "@3 = @2.at(@1);", smapper.popI(), smapper.popA(), smapper.pushI()); break; case opc_pop: case opc_pop2: smapper.pop(1); - out.append("/* pop */"); + debug("/* pop */"); break; case opc_dup: { final Variable v = smapper.get(0); @@ -1024,7 +1099,7 @@ int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); - emit(out, "@1 = @2.@3;", + emit(out, "@1 = @2(false).constructor.@3;", smapper.pushT(type), accessClass(fi[0].replace('/', '_')), fi[1]); i += 2; @@ -1044,7 +1119,7 @@ int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); - emit(out, "@1.@2 = @3;", + emit(out, "@1(false).constructor.@2 = @3;", accessClass(fi[0].replace('/', '_')), fi[1], smapper.popT(type)); i += 2; @@ -1099,13 +1174,17 @@ Integer.toString(c)); } } - out.append(" //"); - for (int j = prev; j <= i; j++) { - out.append(" "); - final int cc = readByte(byteCodes, j); - out.append(Integer.toString(cc)); + if (debug(" //")) { + for (int j = prev; j <= i; j++) { + out.append(" "); + final int cc = readByte(byteCodes, j); + out.append(Integer.toString(cc)); + } } - out.append("\n"); + out.append("\n"); + } + if (previousTrap != null) { + generateCatch(previousTrap); } out.append(" }\n"); out.append("};"); @@ -1204,6 +1283,7 @@ returnType[0] = 'L'; } i = next + 1; + array = false; continue; case '[': array = true; @@ -1279,8 +1359,15 @@ final String in = mi[0]; out.append(accessClass(in.replace('/', '_'))); out.append("(false)."); + if (mn.startsWith("cons_")) { + out.append("constructor."); + } out.append(mn); - out.append('('); + if (isStatic) { + out.append('('); + } else { + out.append(".call("); + } if (numArguments > 0) { out.append(vars[0]); for (int j = 1; j < numArguments; ++j) { @@ -1316,10 +1403,11 @@ out.append(vars[0]).append('.'); out.append(mn); out.append('('); - out.append(vars[0]); + String sep = ""; for (int j = 1; j < numArguments; ++j) { - out.append(", "); + out.append(sep); out.append(vars[j]); + sep = ", "; } out.append(");"); i += 2; @@ -1328,7 +1416,7 @@ private void addReference(String cn) throws IOException { if (requireReference(cn)) { - out.append(" /* needs ").append(cn).append(" */"); + debug(" /* needs " + cn + " */"); } } @@ -1393,15 +1481,8 @@ final String mn = findMethodName(m, cnt); out.append(prefix).append(mn); out.append(" = function("); - String space; - int index; - if (!isStatic) { - space = outputArg(out, p.args, 0); - index = 1; - } else { - space = ""; - index = 0; - } + String space = ""; + int index = 0; for (int i = 0; i < cnt.length(); i++) { out.append(space); space = outputArg(out, p.args, index); @@ -1534,4 +1615,39 @@ out.append(format, processed, length); } + + private void generateCatch(TrapData[] traps) throws IOException { + out.append("} catch (e) {\n"); + int finallyPC = -1; + for (TrapData e : traps) { + if (e == null) { + break; + } + if (e.catch_cpx != 0) { //not finally + final String classInternalName = jc.getClassName(e.catch_cpx); + addReference(classInternalName); + if ("java/lang/Throwable".equals(classInternalName)) { + out.append("if (e.$instOf_java_lang_Throwable) {"); + out.append(" stA0 = e;"); + out.append("} else {"); + out.append(" stA0 = vm.java_lang_Throwable(true);"); + out.append(" vm.java_lang_Throwable.cons__VLjava_lang_String_2.call(stA0, e.toString());"); + out.append("}"); + out.append("gt=" + e.handler_pc + "; continue;"); + } else { + out.append("if (e.$instOf_" + classInternalName.replace('/', '_') + ") {"); + out.append("gt=" + e.handler_pc + "; stA0 = e; continue;"); + out.append("}\n"); + } + } else { + finallyPC = e.handler_pc; + } + } + if (finallyPC == -1) { + out.append("throw e;"); + } else { + out.append("gt=" + finallyPC + "; stA0 = e; continue;"); + } + out.append("\n}"); + } } diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/main/java/org/apidesign/vm4brwsr/LocalsMapper.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/LocalsMapper.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/LocalsMapper.java Fri Jan 18 14:27:22 2013 +0100 @@ -33,13 +33,14 @@ localTypeRecords = new TypeArray(initTypeRecords); } - public void outputArguments(final Appendable out) throws IOException { + public void outputArguments(final Appendable out, boolean isStatic) throws IOException { final int argRecordCount = argTypeRecords.getSize(); - if (argRecordCount > 0) { - Variable variable = getVariable(argTypeRecords, 0); + int first = isStatic ? 0 : 1; + if (argRecordCount > first) { + Variable variable = getVariable(argTypeRecords, first); out.append(variable); - int i = variable.isCategory2() ? 2 : 1; + int i = first + (variable.isCategory2() ? 2 : 1); while (i < argRecordCount) { variable = getVariable(argTypeRecords, i); out.append(", "); diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Jan 18 14:27:22 2013 +0100 @@ -33,6 +33,11 @@ // uses VMLazy to load dynamic classes VMLazy.init(); } + + @Override + boolean debug(String msg) throws IOException { + return false; + } static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException { new VM(out).doCompile(l, names); @@ -112,12 +117,19 @@ + " var loader = {};\n" + " loader.vm = vm;\n" + " loader.loadClass = function(name) {\n" - + " var attr = name.replace__Ljava_lang_String_2CC(name, '.','_');\n" + + " var attr = name.replace__Ljava_lang_String_2CC('.','_');\n" + " var fn = vm[attr];\n" + " if (fn) return fn(false);\n" + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" - + " load___3Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " load__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " }\n" + + " if (args[0]) {\n" + + " vm.loadClass = loader.loadClass;\n" + + " vm.loadBytes = function(name) {\n" + + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" + + " return args[0](name);\n" + + " }\n" + " }\n" + " return loader;\n" + " };\n"); diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Jan 18 14:27:22 2013 +0100 @@ -47,10 +47,10 @@ static Object load(Object loader, String name, Object[] arguments) throws IOException, ClassNotFoundException { - return new VMLazy(loader, arguments).load(name); + return new VMLazy(loader, arguments).load(name, false); } - private Object load(String name) + private Object load(String name, boolean instance) throws IOException, ClassNotFoundException { String res = name.replace('.', '/') + ".class"; byte[] arr = read(loader, res, args); @@ -61,10 +61,27 @@ StringBuilder out = new StringBuilder(); out.append("var loader = arguments[0];\n"); out.append("var vm = loader.vm;\n"); - new Gen(this, out).compile(new ByteArrayInputStream(arr)); + int prelude = out.length(); + String initCode = new Gen(this, out).compile(new ByteArrayInputStream(arr)); String code = out.toString().toString(); +// dump("Loading " + name); + dump(code); String under = name.replace('.', '_'); - return applyCode(loader, under, code); + Object fn = applyCode(loader, under, code, instance); + + if (!initCode.isEmpty()) { + out.setLength(prelude); + out.append(initCode); + code = out.toString().toString(); + dump(code); + applyCode(loader, null, code, false); + } + + return fn; + } + +// @JavaScriptBody(args = "s", body = "java.lang.System.out.println(s.toString());") + static void dump(String s) { } /* possibly not needed: @@ -76,15 +93,15 @@ */ - @JavaScriptBody(args = {"loader", "name", "script" }, body = + @JavaScriptBody(args = {"loader", "name", "script", "instance" }, body = "try {\n" + " new Function(script)(loader, name);\n" + "} catch (ex) {\n" + " throw 'Cannot compile ' + ex + ' line: ' + ex.lineNumber + ' script:\\n' + script;\n" + "}\n" + - "return vm[name](false);\n" + "return name != null ? vm[name](instance) : null;\n" ) - private static native Object applyCode(Object loader, String name, String script); + private static native Object applyCode(Object loader, String name, String script, boolean instance); private static final class Gen extends ByteCodeToJavaScript { @@ -95,16 +112,17 @@ this.lazy = vm; } - @JavaScriptBody(args = {"self", "n"}, + @JavaScriptBody(args = {"n"}, body = - "var cls = n.replace__Ljava_lang_String_2CC(n, '/','_').toString();" - + "\nvar dot = n.replace__Ljava_lang_String_2CC(n,'/','.').toString();" - + "\nvar lazy = self.fld_lazy;" + "var cls = n.replace__Ljava_lang_String_2CC('/','_').toString();" + + "\nvar dot = n.replace__Ljava_lang_String_2CC('/','.').toString();" + + "\nvar lazy = this.fld_lazy;" + "\nvar loader = lazy.fld_loader;" + "\nvar vm = loader.vm;" + "\nif (vm[cls]) return false;" + "\nvm[cls] = function() {" - + "\n return lazy.load__Ljava_lang_Object_2Ljava_lang_String_2(lazy, dot);" + + "\n var instance = arguments.length == 0 || arguments[0] === true;" + + "\n return lazy.load__Ljava_lang_Object_2Ljava_lang_String_2Z(dot, instance);" + "\n};" + "\nreturn true;") @Override diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/Array.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Array.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Array.java Fri Jan 18 14:27:22 2013 +0100 @@ -51,6 +51,16 @@ return doubles[4][0]; } + static double[][] dbls = new double[1][2]; + public static double twoDoubles() { + return dbls[0][0] + dbls[0][0]; + } + + static int[][] tints = new int[1][2]; + public static int twoInts() { + return tints[0][0] + tints[0][0]; + } + private static final Array[] ARR = { new Array(), new Array(), new Array() }; private static Array[][] arr() { @@ -82,16 +92,27 @@ } return sum; } - public static int simple() { - int[] arr = { 0, 1, 2, 3, 4, 5 }; + private static final int[] arr = { 0, 1, 2, 3, 4, 5 }; + public static int simple(boolean clone) { + int[] ar; + if (clone) { + ar = arr.clone(); + } else { + ar = arr; + } int sum = 0; - for (int a : arr) { + for (int a : ar) { sum += a; } return sum; } + public static int sum(int size) { + int[] arr = new int[size]; + return arr[0] + arr[1]; + } + static void arraycopy(char[] value, int srcBegin, char[] dst, int dstBegin, int count) { while (count-- > 0) { dst[dstBegin++] = value[srcBegin++]; diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ArrayTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -18,7 +18,6 @@ package org.apidesign.vm4brwsr; import javax.script.Invocable; -import javax.script.ScriptException; import static org.testng.Assert.*; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -28,9 +27,20 @@ * @author Jaroslav Tulach */ public class ArrayTest { + @Test public void intArrayShouldBeFilledWithZeroes() throws Exception { + assertExec("0 + 0", Array.class, "sum__II", + Double.valueOf(0), 2 + ); + } @Test public void verifySimpleIntOperation() throws Exception { - assertExec("CheckTheSum", Array.class, "simple__I", - Double.valueOf(15) + assertExec("CheckTheSum", Array.class, "simple__IZ", + Double.valueOf(15), false + ); + } + + @Test public void cloneOnArray() throws Exception { + assertExec("CheckTheSum on clone", Array.class, "simple__IZ", + Double.valueOf(15), true ); } @@ -43,6 +53,17 @@ Double.valueOf(105) ); } + + @Test public void twoDoubles() throws Exception { + assertExec("Elements are initialized", Array.class, "twoDoubles__D", + Double.valueOf(0) + ); + } + @Test public void twoInts() throws Exception { + assertExec("Elements are initialized", Array.class, "twoInts__I", + Double.valueOf(0) + ); + } @Test public void doesCopyArrayWork() throws Exception { assertExec("Returns 'a'", Array.class, "copyArray__C", Double.valueOf('a')); diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Fri Jan 18 14:27:22 2013 +0100 @@ -19,6 +19,8 @@ import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; import java.util.Set; import java.util.TreeSet; @@ -33,15 +35,7 @@ if (!requested.add(name)) { throw new IllegalStateException("Requested for second time: " + name); } - InputStream is = BytesLoader.class.getClassLoader().getResourceAsStream(name); - if (is == null) { - throw new IOException("Can't find " + name); - } - byte[] arr = new byte[is.available()]; - int len = is.read(arr); - if (len != arr.length) { - throw new IOException("Read only " + len + " wanting " + arr.length); - } + byte[] arr = readClass(name); /* System.err.print("loader['" + name + "'] = ["); for (int i = 0; i < arr.length; i++) { @@ -54,5 +48,29 @@ */ return arr; } + + static byte[] readClass(String name) throws IOException { + URL u = null; + Enumeration en = BytesLoader.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + try (InputStream is = u.openStream()) { + byte[] arr; + arr = new byte[is.available()]; + int offset = 0; + while (offset < arr.length) { + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + throw new IOException("Can't read " + name); + } + offset += len; + } + return arr; + } + } } diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -74,6 +74,16 @@ @Test public void jsNewInstance() throws Exception { assertExec("Check new instance", Classes.class, "newInstance__Z", Double.valueOf(1)); } + @Test public void javaNoNewInstance() throws Exception { + assertEquals("java.lang.InstantiationException:java.lang.Float", + Classes.newInstanceNoPubConstructor() + ); + } + @Test public void jsNoNewInstance() throws Exception { + assertExec("Check problems with new instance", Classes.class, "newInstanceNoPubConstructor__Ljava_lang_String_2", + "java.lang.InstantiationException:java.lang.Float" + ); + } @Test public void jsAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getMarker__I", Double.valueOf(10)); } @@ -92,6 +102,11 @@ "java.io.IOException", true, "name" ); } + @Test public void jsInvokeParamMethod() throws Exception { + assertExec("sums two numbers via reflection", Classes.class, + "reflectiveSum__III", Double.valueOf(5), 2, 3 + ); + } @Test public void javaFindMethod() throws Exception { assertEquals(Classes.reflectiveMethodCall(false, "name"), "java.io.IOException", "Calls the name() method via reflection"); } @@ -101,6 +116,18 @@ "java.io.IOException", false, "name" ); } + @Test public void primitiveReturnType() throws Exception { + assertExec("Tries to get an integer via reflection", Classes.class, + "primitiveType__Ljava_lang_String_2Ljava_lang_String_2", + Classes.primitiveType("primitive"), "primitive" + ); + } + @Test public void primitiveBoolReturnType() throws Exception { + assertExec("Tries to get an integer via reflection", Classes.class, + "primitiveType__Ljava_lang_String_2Ljava_lang_String_2", + Classes.primitiveType("primitiveB"), "primitiveB" + ); + } @Test public void javaAnnotatedMethod() throws Exception { assertEquals(Classes.reflectiveMethodCall(false, null), "java.io.IOException", "Calls the name() method via reflection"); } @@ -116,6 +143,26 @@ "java.io.IOException" ); } + @Test public void noInterface() throws Exception { + assertExec("Calls Class.isInterface", Classes.class, + "isInterface__ZLjava_lang_String_2", + 0.0, "java.lang.String" + ); + } + /* + @Test public void isInterface() throws Exception { + assertExec("Calls Class.isInterface", Classes.class, + "isInterface__ZLjava_lang_String_2", + 1.0, "java.lang.Runnable" + ); + } + */ + @Test public void integerType() throws Exception { + assertExec("Computes the type", Classes.class, + "intType__Ljava_lang_String_2", + Classes.intType() + ); + } private static CharSequence codeSeq; private static Invocable code; diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Fri Jan 18 14:27:22 2013 +0100 @@ -38,6 +38,10 @@ return c.getName(); } + public static boolean isInterface(String s) throws ClassNotFoundException { + return Class.forName(s).isInterface(); + } + public static boolean equalsClassesOfExceptions() { return MalformedURLException.class.getSuperclass() == IOException.class; } @@ -68,6 +72,14 @@ } throw new IllegalStateException("Not a subtype: " + ioe); } + public static String newInstanceNoPubConstructor() throws Exception { + try { + Float f = Float.class.newInstance(); + return "wrong, can't instantiate: " + f; + } catch (Exception ex) { + return (ex.getClass().getName() + ":" + ex.getMessage()).toString().toString(); + } + } public static int getMarker() { if (!Classes.class.isAnnotationPresent(ClassesMarker.class)) { return -2; @@ -88,6 +100,21 @@ return null; } + public static String intType() { + return Integer.TYPE.getName(); + } + + public static int primitive() { + return 1; + } + public static boolean primitiveB() { + return true; + } + + public static String primitiveType(String method) throws Exception { + return reflectiveMethodCall(false, method).getClass().getName(); + } + @JavaScriptBody(args = "msg", body = "throw msg;") private static native void thrw(String msg); @@ -119,4 +146,9 @@ } return find.invoke(null); } + + public static int reflectiveSum(int a, int b) throws Exception { + Method m = StaticMethod.class.getMethod("sum", int.class, int.class); + return (int) m.invoke(null, a, b); + } } diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/Compare.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Compare.java Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** Can be applied on a method that yields a return value. - * Together with {@link VMCompare#create} it can be used to write - * methods which are executed in real as well as JavaScript VMs and - * their results are compared. - * - * @author Jaroslav Tulach - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Compare { - -} diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr; - -import java.lang.reflect.Method; -import java.util.Map; -import java.util.WeakHashMap; -import javax.script.Invocable; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import org.testng.Assert; -import org.testng.ITest; -import org.testng.annotations.Factory; -import org.testng.annotations.Test; - -/** A TestNG {@link Factory} that seeks for {@link Compare} annotations - * in provided class and builds set of tests that compare the computations - * in real as well as JavaScript virtual machines. Use as:
    - * {@code @}{@link Factory} public static create() {
    - *   return @{link CompareVMs}.{@link #create(YourClass.class);
    - * }
    - * - * @author Jaroslav Tulach - */ -public final class CompareVMs implements ITest { - private final Run first, second; - private final Method m; - - private CompareVMs(Method m, Run first, Run second) { - this.first = first; - this.second = second; - this.m = m; - } - - public static Object[] create(Class clazz) { - Method[] arr = clazz.getMethods(); - Object[] ret = new Object[3 * arr.length]; - int cnt = 0; - for (Method m : arr) { - Compare c = m.getAnnotation(Compare.class); - if (c == null) { - continue; - } - final Run real = new Run(m, false); - final Run js = new Run(m, true); - ret[cnt++] = real; - ret[cnt++] = js; - ret[cnt++] = new CompareVMs(m, real, js); - } - Object[] r = new Object[cnt]; - for (int i = 0; i < cnt; i++) { - r[i] = ret[i]; - } - return r; - } - - @Test(dependsOnGroups = "run") public void compareResults() throws Throwable { - Object v1 = first.value; - Object v2 = second.value; - if (v1 instanceof Number) { - v1 = ((Number)v1).doubleValue(); - } - Assert.assertEquals(v2, v1, "Comparing results"); - } - - @Override - public String getTestName() { - return m.getName() + "[Compare]"; - } - - public static final class Run implements ITest { - private final Method m; - private final boolean js; - Object value; - private Invocable code; - private CharSequence codeSeq; - private static final Map compiled = new WeakHashMap(); - - private Run(Method m, boolean js) { - this.m = m; - this.js = js; - } - - private void compileTheCode(Class clazz) throws Exception { - final Object[] data = compiled.get(clazz); - if (data != null) { - code = (Invocable) data[0]; - codeSeq = (CharSequence) data[1]; - return; - } - StringBuilder sb = new StringBuilder(); - Bck2Brwsr.generate(sb, CompareVMs.class.getClassLoader()); - - ScriptEngineManager sem = new ScriptEngineManager(); - ScriptEngine js = sem.getEngineByExtension("js"); - js.getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE); - - sb.append("\nfunction initVM() {" - + "\n return bck2brwsr(" - + "\n function(name) { return loader.get(name);}" - + "\n );" - + "\n};"); - - Object res = js.eval(sb.toString()); - Assert.assertTrue(js instanceof Invocable, "It is invocable object: " + res); - code = (Invocable) js; - codeSeq = sb; - compiled.put(clazz, new Object[] { code, codeSeq }); - } - - @Test(groups = "run") public void executeCode() throws Throwable { - if (js) { - try { - compileTheCode(m.getDeclaringClass()); - Object vm = code.invokeFunction("initVM"); - Object inst = code.invokeMethod(vm, "loadClass", m.getDeclaringClass().getName()); - value = code.invokeMethod(inst, m.getName() + "__" + computeSignature(m)); - } catch (Exception ex) { - throw new AssertionError(StaticMethodTest.dumpJS(codeSeq)).initCause(ex); - } - } else { - value = m.invoke(m.getDeclaringClass().newInstance()); - } - } - @Override - public String getTestName() { - return m.getName() + (js ? "[JavaScript]" : "[Java]"); - } - - private static String computeSignature(Method m) { - StringBuilder sb = new StringBuilder(); - appendType(sb, m.getReturnType()); - for (Class c : m.getParameterTypes()) { - appendType(sb, c); - } - return sb.toString(); - } - - private static void appendType(StringBuilder sb, Class t) { - if (t == null) { - sb.append('V'); - return; - } - if (t.isPrimitive()) { - int ch = -1; - if (t == int.class) { - ch = 'I'; - } - if (t == short.class) { - ch = 'S'; - } - if (t == byte.class) { - ch = 'B'; - } - if (t == boolean.class) { - ch = 'Z'; - } - if (t == long.class) { - ch = 'J'; - } - if (t == float.class) { - ch = 'F'; - } - if (t == double.class) { - ch = 'D'; - } - assert ch != -1 : "Unknown primitive type " + t; - sb.append((char)ch); - return; - } - if (t.isArray()) { - sb.append("_3"); - appendType(sb, t.getComponentType()); - return; - } - sb.append('L'); - sb.append(t.getName().replace('.', '_')); - sb.append("_2"); - } - } -} diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/Exceptions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Exceptions.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,88 @@ +/** + * 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 org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author tom + */ +public class Exceptions { + private Exceptions() { + } + + public static int methodWithTryCatchNoThrow() { + int res = 0; + try { + res = 1; + } catch (IllegalArgumentException e) { + res = 2; + } + //join point + return res; + } + + public static int methodWithTryCatchThrow() { + int res = 0; + try { + res = 1; + throw new IllegalArgumentException(); + } catch (IllegalArgumentException e) { + res = 2; + } + //join point + return res; + } + + @JavaScriptBody(args = "msg", body = "throw msg;") + public static void thrw(String msg) {} + + public static String catchThrowableCatchesAll() { + try { + thrw("Hello!"); + return "Not here!"; + } catch (Throwable ex) { + return ex.getMessage(); + } + } + + public static String newInstance(String n) { + try { + Class c; + try { + c = Class.forName(n); + } catch (ClassNotFoundException ex) { + return ("CNFE:" + ex.getMessage()).toString(); + } + return c.newInstance().getClass().getName(); + } catch (InstantiationException | IllegalAccessException ex) { + return ex.getMessage(); + } + } + + private static int counter; + public static int readCounter(String n) throws ClassNotFoundException { + try { + Class.forName(n); + } finally { + counter++; + } + return counter; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ExceptionsTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,121 @@ +/** + * 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.Invocable; +import javax.script.ScriptException; +import static org.testng.Assert.*; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * + * @author Tomas Zezula + */ +public class ExceptionsTest { + @Test + public void verifyMethodWithTryCatchNoThrow() throws Exception { + assertExec( + "No throw", + Exceptions.class, + "methodWithTryCatchNoThrow__I", + new Double(1.0)); + } + + @Test + public void catchJavaScriptStringAsThrowable() throws Exception { + assertExec( + "Throw hello!", + Exceptions.class, + "catchThrowableCatchesAll__Ljava_lang_String_2", + "Hello!" + ); + } + + @Test + public void verifyMethodWithTryCatchThrow() throws Exception { + assertExec( + "Throw", + Exceptions.class, + "methodWithTryCatchThrow__I", + new Double(2.0)); + } + + @Test public void createObject() throws Exception { + assertExec("Object created", Exceptions.class, + "newInstance__Ljava_lang_String_2Ljava_lang_String_2", + "java.lang.Object", + "java.lang.Object" + ); + } + + @Test public void createFloatFails() throws Exception { + assertExec("Float not created", Exceptions.class, + "newInstance__Ljava_lang_String_2Ljava_lang_String_2", + "java.lang.Float", + "java.lang.Float" + ); + } + + @Test public void createUnknownFails() throws Exception { + assertExec("Object created", Exceptions.class, + "newInstance__Ljava_lang_String_2Ljava_lang_String_2", + "CNFE:org.apidesign.Unknown", + "org.apidesign.Unknown" + ); + } + + @Test public void testThreeCalls() throws Exception { + Object vm = code.invokeFunction("bck2brwsr"); + Object clazz = code.invokeMethod(vm, "loadClass", Exceptions.class.getName()); + + String method = "readCounter__ILjava_lang_String_2"; + + try { + Object ret = code.invokeMethod(clazz, method, "org.apidesign.Unknown"); + fail("We expect an CNFE!"); + } catch (ScriptException scriptException) { + // script exception should be OK + } + { + // 2nd invocation + Object ret = code.invokeMethod(clazz, method, "java.lang.String"); + assertEquals(ret, Double.valueOf(2)); + } + { + // 3rd invocation + Object ret = code.invokeMethod(clazz, method, "java.lang.Integer"); + assertEquals(ret, Double.valueOf(3)); + } + } + + private static CharSequence codeSeq; + private static Invocable code; + + @BeforeClass + public void compileTheCode() throws Exception { + StringBuilder sb = new StringBuilder(); + code = StaticMethodTest.compileClass(sb, + "org/apidesign/vm4brwsr/Exceptions" + ); + codeSeq = sb; + } + private static void assertExec(String msg, Class clazz, String method, Object expRes, Object... args) throws Exception { + StaticMethodTest.assertExec(code, codeSeq, msg, clazz, method, expRes, args); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/Instance.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Fri Jan 18 14:27:22 2013 +0100 @@ -125,4 +125,11 @@ public static boolean iofObject() { return jsObj() instanceof Object; } + + public static int jscall() { + return jsgetbytes(new Instance()); + } + + @JavaScriptBody(args = { "instance" }, body = "return instance.getByte__B();") + private static native int jsgetbytes(Instance instance); } diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -131,6 +131,14 @@ Double.valueOf(1) ); } + + @Test public void jsCallingConvention() throws Exception { + assertExec( + "Pointer to 'this' is passed automatically (and not as a first argument)", + Instance.class, "jscall__I", + Double.valueOf(31) + ); + } protected String startCompilationWith() { return "org/apidesign/vm4brwsr/Instance"; diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Fri Jan 18 14:23:18 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -20,7 +20,6 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; import static org.testng.Assert.*; import javax.script.Invocable; import org.testng.annotations.BeforeClass; @@ -36,13 +35,13 @@ private static Invocable code; @Test public void compareGeneratedCodeForArrayClass() throws Exception { - compareCode("/org/apidesign/vm4brwsr/Array.class"); + compareCode("org/apidesign/vm4brwsr/Array.class"); } @Test public void compareGeneratedCodeForClassesClass() throws Exception { - compareCode("/org/apidesign/vm4brwsr/Classes.class"); + compareCode("org/apidesign/vm4brwsr/Classes.class"); } - + @BeforeClass public void compileTheCode() throws Exception { StringBuilder sb = new StringBuilder(); @@ -52,20 +51,8 @@ codeSeq = sb; } - private static byte[] readClass(String res) throws IOException { - InputStream is1 = VMinVMTest.class.getResourceAsStream(res); - assertNotNull(is1, "Stream found"); - byte[] arr = new byte[is1.available()]; - int len = is1.read(arr); - is1.close(); - if (len != arr.length) { - throw new IOException("Wrong len " + len + " for arr: " + arr.length); - } - return arr; - } - private void compareCode(final String nm) throws Exception, IOException { - byte[] arr = readClass(nm); + byte[] arr = BytesLoader.readClass(nm); String ret1 = VMinVM.toJavaScript(arr); Object ret; diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr.tck; - -import org.apidesign.vm4brwsr.Compare; -import org.apidesign.vm4brwsr.CompareVMs; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareHashTest { - @Compare public int hashOfString() { - return "Ahoj".hashCode(); - } - - @Factory - public static Object[] create() { - return CompareVMs.create(CompareHashTest.class); - } -} diff -r 1a73c6e08cc5 -r 95b68e21a53f vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java Fri Jan 18 14:23:18 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr.tck; - -import org.apidesign.vm4brwsr.Compare; -import org.apidesign.vm4brwsr.CompareVMs; -import org.testng.annotations.Factory; - -/** - * - * @author Jaroslav Tulach - */ -public class CompareStringsTest { - @Compare public String deleteLastTwoCharacters() { - StringBuilder sb = new StringBuilder(); - sb.append("453.0"); - if (sb.toString().endsWith(".0")) { - final int l = sb.length(); - sb.delete(l - 2, l); - } - return sb.toString().toString(); - } - - @Factory - public static Object[] create() { - return CompareVMs.create(CompareStringsTest.class); - } -} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/pom.xml Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.apidesign + bck2brwsr + 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + vmtest + 0.3-SNAPSHOT + + VM Testing APIs + http://bck2brwsr.apidesign.org + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + + UTF-8 + + + + org.testng + testng + compile + + + junit + junit + + + + + ${project.groupId} + vm4brwsr + 0.3-SNAPSHOT + jar + + + ${project.groupId} + emul + 0.3-SNAPSHOT + + + ${project.groupId} + launcher + ${project.version} + + + diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/Compare.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Can be applied on a method that yields a return value. + * Together with {@link VMTest#create} it can be used to write + * methods which are executed in real as well as JavaScript VMs and + * their results are compared. + * + * @author Jaroslav Tulach + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Compare { + +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest; + +import org.apidesign.bck2brwsr.vmtest.impl.CompareCase; +import org.testng.annotations.Factory; + +/** A TestNG {@link Factory} that seeks for {@link Compare} annotations + * in provided class and builds set of tests that compare the computations + * in real as well as JavaScript virtual machines. Use as:
    + * {@code @}{@link Factory} public static create() {
    + *   return @{link VMTest}.{@link #create(YourClass.class);
    + * }
    + * + * @author Jaroslav Tulach + */ +public final class VMTest { + /** Inspects clazz and for each {@lik Compare} method creates + * instances of tests. Each instance runs the test in different virtual + * machine and at the end they compare the results. + * + * @param clazz the class to inspect + * @return the set of created tests + */ + public static Object[] create(Class clazz) { + return CompareCase.create(clazz); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Fri Jan 18 14:27:22 2013 +0100 @@ -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.bck2brwsr.vmtest.impl; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.WeakHashMap; +import org.apidesign.bck2brwsr.launcher.Launcher; +import org.apidesign.bck2brwsr.launcher.MethodInvocation; +import org.testng.ITest; +import org.testng.annotations.Test; + +/** + * + * @author Jaroslav Tulach + */ +public final class Bck2BrwsrCase implements ITest { + private final Method m; + private final Launcher l; + private final String type; + Object value; + private static final Map compiled = new WeakHashMap<>(); + private Object inst; + + Bck2BrwsrCase(Method m, String type, Launcher l) { + this.l = l; + this.m = m; + this.type = type; + } + + @Test(groups = "run") + public void executeCode() throws Throwable { + if (l != null) { + MethodInvocation c = l.invokeMethod(m.getDeclaringClass(), m.getName()); + value = c.toString(); + } else { + try { + value = m.invoke(m.getDeclaringClass().newInstance()); + } catch (InvocationTargetException ex) { + Throwable t = ex.getTargetException(); + value = t.getClass().getName() + ":" + t.getMessage(); + } + } + } + + @Override + public String getTestName() { + return m.getName() + "[" + typeName() + "]"; + } + + final String typeName() { + return type; + } + static void dumpJS(StringBuilder sb, Bck2BrwsrCase c) throws IOException { + File f = File.createTempFile(c.m.getName(), ".js"); + try (final FileWriter w = new FileWriter(f)) { + w.append(c.l.toString()); + } + sb.append("Path: ").append(f.getPath()); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/CompareCase.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,126 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import org.apidesign.bck2brwsr.vmtest.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import org.apidesign.bck2brwsr.launcher.Launcher; +import org.testng.Assert; +import org.testng.ITest; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +/** A TestNG {@link Factory} that seeks for {@link Compare} annotations + * in provided class and builds set of tests that compare the computations + * in real as well as JavaScript virtual machines. Use as:
    + * {@code @}{@link Factory} public static create() {
    + *   return @{link VMTest}.{@link #create(YourClass.class);
    + * }
    + * + * @author Jaroslav Tulach + */ +public final class CompareCase implements ITest { + private final Bck2BrwsrCase first, second; + private final Method m; + + private CompareCase(Method m, Bck2BrwsrCase first, Bck2BrwsrCase second) { + this.first = first; + this.second = second; + this.m = m; + } + + /** Inspects clazz and for each {@lik Compare} method creates + * instances of tests. Each instance runs the test in different virtual + * machine and at the end they compare the results. + * + * @param clazz the class to inspect + * @return the set of created tests + */ + public static Object[] create(Class clazz) { + Method[] arr = clazz.getMethods(); + List ret = new ArrayList<>(); + + final LaunchSetup l = LaunchSetup.INSTANCE; + ret.add(l); + + String[] brwsr; + { + String p = System.getProperty("vmtest.brwsrs"); + if (p != null) { + brwsr = p.split(","); + } else { + brwsr = new String[0]; + } + } + + for (Method m : arr) { + Compare c = m.getAnnotation(Compare.class); + if (c == null) { + continue; + } + final Bck2BrwsrCase real = new Bck2BrwsrCase(m, "Java", null); + final Bck2BrwsrCase js = new Bck2BrwsrCase(m, "JavaScript", l.javaScript()); + ret.add(real); + ret.add(js); + ret.add(new CompareCase(m, real, js)); + + for (String b : brwsr) { + final Launcher s = l.brwsr(b); + ret.add(s); + final Bck2BrwsrCase cse = new Bck2BrwsrCase(m, b, s); + ret.add(cse); + ret.add(new CompareCase(m, real, cse)); + } + } + return ret.toArray(); + } + + /** Test that compares the previous results. + * @throws Throwable + */ + @Test(dependsOnGroups = "run") public void compareResults() throws Throwable { + Object v1 = first.value; + Object v2 = second.value; + if (v1 != null) { + v1 = v1.toString(); + } else { + v1 = "null"; + } + try { + Assert.assertEquals(v2, v1, "Comparing results"); + } catch (AssertionError e) { + StringBuilder sb = new StringBuilder(); + sb.append(e.getMessage()); + Bck2BrwsrCase.dumpJS(sb, second); + throw new AssertionError(sb.toString()); + } + } + + /** Test name. + * @return name of the tested method followed by a suffix + */ + @Override + public String getTestName() { + return m.getName() + "[Compare " + second.typeName() + "]"; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/LaunchSetup.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/LaunchSetup.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,68 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; +import org.apidesign.bck2brwsr.launcher.Launcher; +import org.testng.annotations.AfterGroups; +import org.testng.annotations.BeforeGroups; + +/** + * + * @author Jaroslav Tulach + */ +public final class LaunchSetup { + static LaunchSetup INSTANCE = new LaunchSetup(); + + private final Launcher js = Launcher.createJavaScript(); + private final Map brwsrs = new LinkedHashMap<>(); + + private LaunchSetup() { + } + + public Launcher javaScript() { + return js; + } + + public synchronized Launcher brwsr(String cmd) { + Launcher s = brwsrs.get(cmd); + if (s == null) { + s = Launcher.createBrowser(cmd); + brwsrs.put(cmd, s); + } + return s; + } + + @BeforeGroups("run") + public void initializeLauncher() throws IOException { + js.initialize(); + for (Launcher launcher : brwsrs.values()) { + launcher.initialize(); + } + } + + @AfterGroups("run") + public void shutDownLauncher() throws IOException, InterruptedException { + js.shutdown(); + for (Launcher launcher : brwsrs.values()) { + launcher.shutdown(); + } + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ByteArithmeticTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,102 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ByteArithmeticTest { + + private static byte add(byte x, byte y) { + return (byte)(x + y); + } + + private static byte sub(byte x, byte y) { + return (byte)(x - y); + } + + private static byte mul(byte x, byte y) { + return (byte)(x * y); + } + + private static byte div(byte x, byte y) { + return (byte)(x / y); + } + + private static byte mod(byte x, byte y) { + return (byte)(x % y); + } + + @Compare public byte conversion() { + return (byte)123456; + } + + @Compare public byte addOverflow() { + return add(Byte.MAX_VALUE, (byte)1); + } + + @Compare public byte subUnderflow() { + return sub(Byte.MIN_VALUE, (byte)1); + } + + @Compare public byte addMaxByteAndMaxByte() { + return add(Byte.MAX_VALUE, Byte.MAX_VALUE); + } + + @Compare public byte subMinByteAndMinByte() { + return sub(Byte.MIN_VALUE, Byte.MIN_VALUE); + } + + @Compare public byte multiplyMaxByte() { + return mul(Byte.MAX_VALUE, (byte)2); + } + + @Compare public byte multiplyMaxByteAndMaxByte() { + return mul(Byte.MAX_VALUE, Byte.MAX_VALUE); + } + + @Compare public byte multiplyMinByte() { + return mul(Byte.MIN_VALUE, (byte)2); + } + + @Compare public byte multiplyMinByteAndMinByte() { + return mul(Byte.MIN_VALUE, Byte.MIN_VALUE); + } + + @Compare public byte multiplyPrecision() { + return mul((byte)17638, (byte)1103); + } + + @Compare public byte division() { + return div((byte)1, (byte)2); + } + + @Compare public byte divisionReminder() { + return mod((byte)1, (byte)2); + } + + @Factory + public static Object[] create() { + return VMTest.create(ByteArithmeticTest.class); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CloneTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,73 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CloneTest { + private int value; + + @Compare + public Object notSupported() throws CloneNotSupportedException { + return this.clone(); + } + + @Compare public String sameClass() throws CloneNotSupportedException { + return new Clnbl().clone().getClass().getName(); + } + + @Compare public boolean differentInstance() throws CloneNotSupportedException { + Clnbl orig = new Clnbl(); + return orig == orig.clone(); + } + + @Compare public int sameReference() throws CloneNotSupportedException { + CloneTest self = this; + Clnbl orig = new Clnbl(); + self.value = 33; + orig.ref = self; + return ((Clnbl)orig.clone()).ref.value; + } + + @Compare public int sameValue() throws CloneNotSupportedException { + Clnbl orig = new Clnbl(); + orig.value = 10; + return ((Clnbl)orig.clone()).value; + } + + @Factory + public static Object[] create() { + return VMTest.create(CloneTest.class); + } + + public static final class Clnbl implements Cloneable { + public CloneTest ref; + private int value; + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareByteArrayTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,93 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareByteArrayTest { + @Compare public int byteArraySum() { + byte[] arr = createArray(); + return sumByteArr(arr); + } + + @Compare public int countZeros() { + int zeros = 0; + for (Byte b : createArray()) { + if (b == 0) { + zeros++; + } + } + return zeros; + } + + private static int sumByteArr(byte[] arr) { + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + } + return sum; + } + + @Compare public String noOutOfBounds() { + return atIndex(1); + } + + @Compare public String outOfBounds() { + return atIndex(5); + } + + @Compare public String outOfBoundsMinus() { + return atIndex(-1); + } + + @Compare public String toOfBounds() { + return toIndex(5); + } + + @Compare public String toOfBoundsMinus() { + return toIndex(-1); + } + + private static final int[] arr = { 0, 1, 2 }; + public static String atIndex(int at) { + return "at@" + arr[at]; + } + public static String toIndex(int at) { + arr[at] = 10; + return "ok"; + } + + + @Factory + public static Object[] create() { + return VMTest.create(CompareByteArrayTest.class); + } + + private byte[] createArray() { + byte[] arr = new byte[10]; + arr[5] = 3; + arr[7] = 8; + return arr; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareHashTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,50 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareHashTest { + @Compare public int hashOfString() { + return "Ahoj".hashCode(); + } + + @Compare public int hashRemainsYieldsZero() { + Object o = new Object(); + return o.hashCode() - o.hashCode(); + } + + @Compare public int initializeInStatic() { + return StaticUse.NON_NULL.hashCode() - StaticUse.NON_NULL.hashCode(); + } + + @Compare public int hashOfInt() { + return Integer.valueOf(Integer.MAX_VALUE).hashCode(); + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareHashTest.class); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareIntArrayTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,63 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareIntArrayTest { + @Compare public int integerArraySum() { + int[] arr = createArray(); + return sumIntArr(arr); + } + + @Compare public int countZeros() { + int zeros = 0; + for (Integer i : createArray()) { + if (i == 0) { + zeros++; + } + } + return zeros; + } + + private static int sumIntArr(int[] arr) { + int sum = 0; + for (int i = 0; i < arr.length; i++) { + sum += arr[i]; + } + return sum; + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareIntArrayTest.class); + } + + private int[] createArray() { + int[] arr = new int[10]; + arr[5] = 3; + arr[7] = 8; + return arr; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/CompareStringsTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,129 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.net.MalformedURLException; +import java.net.URL; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareStringsTest { + @Compare public String firstChar() { + return "" + ("Hello".toCharArray()[0]); + } + + @Compare public String classCast() { + Object o = firstChar(); + return String.class.cast(o); + } + + @Compare public String classCastThrown() { + Object o = null; + return String.class.cast(o); + } + + @Compare public static Object compareURLs() throws MalformedURLException { + return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); + } + + @Compare public String deleteLastTwoCharacters() { + StringBuilder sb = new StringBuilder(); + sb.append("453.0"); + if (sb.toString().endsWith(".0")) { + final int l = sb.length(); + sb.delete(l - 2, l); + } + return sb.toString().toString(); + } + + @Compare public String nameOfStringClass() throws Exception { + return Class.forName("java.lang.String").getName(); + } + @Compare public String nameOfArrayClass() throws Exception { + return Class.forName("org.apidesign.bck2brwsr.tck.CompareHashTest").getName(); + } + + @Compare public String lowerHello() { + return "HeLlO".toLowerCase(); + } + + @Compare public String lowerA() { + return String.valueOf(Character.toLowerCase('A')).toString(); + } + @Compare public String upperHello() { + return "hello".toUpperCase(); + } + + @Compare public String upperA() { + return String.valueOf(Character.toUpperCase('a')).toString(); + } + + @Compare public boolean matchRegExp() throws Exception { + return "58038503".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExp() throws Exception { + return "58038503GH".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExpFully() throws Exception { + return "Hello".matches("Hell"); + } + + @Compare public String emptyCharArray() { + char[] arr = new char[10]; + return new String(arr); + } + + @Compare public String variousCharacterTests() throws Exception { + StringBuilder sb = new StringBuilder(); + + sb.append(Character.isUpperCase('a')); + sb.append(Character.isUpperCase('A')); + sb.append(Character.isLowerCase('a')); + sb.append(Character.isLowerCase('A')); + + sb.append(Character.isLetter('A')); + sb.append(Character.isLetterOrDigit('9')); + sb.append(Character.isLetterOrDigit('A')); + sb.append(Character.isLetter('0')); + + return sb.toString().toString(); + } + + @Compare + public String nullFieldInitialized() { + NullField nf = new NullField(); + return ("" + nf.name).toString(); + } + + @Factory + public static Object[] create() { + return VMTest.create(CompareStringsTest.class); + } + + private static final class NullField { + + String name; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/IntegerArithmeticTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,108 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class IntegerArithmeticTest { + + private static int add(int x, int y) { + return x + y; + } + + private static int sub(int x, int y) { + return x - y; + } + + private static int mul(int x, int y) { + return x * y; + } + + private static int div(int x, int y) { + return x / y; + } + + private static int mod(int x, int y) { + return x % y; + } + + @Compare public int addOverflow() { + return add(Integer.MAX_VALUE, 1); + } + + @Compare public int subUnderflow() { + return sub(Integer.MIN_VALUE, 1); + } + + @Compare public int addMaxIntAndMaxInt() { + return add(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Compare public int subMinIntAndMinInt() { + return sub(Integer.MIN_VALUE, Integer.MIN_VALUE); + } + + @Compare public int multiplyMaxInt() { + return mul(Integer.MAX_VALUE, 2); + } + + @Compare public int multiplyMaxIntAndMaxInt() { + return mul(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Compare public int multiplyMinInt() { + return mul(Integer.MIN_VALUE, 2); + } + + @Compare public int multiplyMinIntAndMinInt() { + return mul(Integer.MIN_VALUE, Integer.MIN_VALUE); + } + + @Compare public int multiplyPrecision() { + return mul(119106029, 1103515245); + } + + @Compare public int division() { + return div(1, 2); + } + + @Compare public int divisionReminder() { + return mod(1, 2); + } + + @Compare public int sumTwoDimensions() { + int[][] matrix = createMatrix(4, 3); + matrix[0][0] += 10; + return matrix[0][0]; + } + + static int[][] createMatrix(int x, int y) { + return new int[x][y]; + } + + @Factory + public static Object[] create() { + return VMTest.create(IntegerArithmeticTest.class); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,192 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ReflectionTest { + @Compare public boolean nonNullThis() { + return this == null; + } + + @Compare public String intType() { + return Integer.TYPE.toString(); + } + + @Compare public String voidType() throws Exception { + return void.class.toString(); + } + + @Compare public String longClass() { + return long.class.toString(); + } + + @Compare public String namesOfMethods() { + StringBuilder sb = new StringBuilder(); + String[] arr = new String[20]; + int i = 0; + for (Method m : StaticUse.class.getMethods()) { + arr[i++] = m.getName(); + } + for (String s : sort(arr, i)) { + sb.append(s).append("\n"); + } + return sb.toString(); + } + + @Compare public String cannotCallNonStaticMethodWithNull() throws Exception { + StaticUse.class.getMethod("instanceMethod").invoke(null); + return "should not happen"; + } + + @Compare public Object voidReturnType() throws Exception { + return StaticUse.class.getMethod("instanceMethod").getReturnType(); + } + + @Compare public String newInstanceFails() throws InstantiationException { + try { + return "success: " + StaticUse.class.newInstance(); + } catch (IllegalAccessException ex) { + return ex.getClass().getName(); + } + } + + @Compare public String paramTypes() throws Exception { + Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE); + final Class[] pt = plus.getParameterTypes(); + return pt[0].getName(); + } + @Compare public String paramTypesNotFound() throws Exception { + return StaticUse.class.getMethod("plus", int.class, double.class).toString(); + } + @Compare public int methodWithArgs() throws Exception { + Method plus = StaticUse.class.getMethod("plus", int.class, Integer.TYPE); + return (Integer)plus.invoke(null, 2, 3); + } + + @Compare public String classGetNameForByte() { + return byte.class.getName(); + } + @Compare public String classGetNameForBaseObject() { + return newObject().getClass().getName(); + } + @Compare public String classGetNameForJavaObject() { + return new Object().getClass().getName(); + } + @Compare public String classGetNameForObjectArray() { + return (new Object[3]).getClass().getName(); + } + @Compare public String classGetNameForSimpleIntArray() { + return (new int[3]).getClass().getName(); + } + @Compare public boolean sameClassGetNameForSimpleCharArray() { + return (new char[3]).getClass() == (new char[34]).getClass(); + } + @Compare public String classGetNameForMultiIntArray() { + return (new int[3][4][5][6][7][8][9]).getClass().getName(); + } + @Compare public String classGetNameForMultiIntArrayInner() { + final int[][][][][][][] arr = new int[3][4][5][6][7][8][9]; + int[][][][][][] subarr = arr[0]; + int[][][][][] subsubarr = subarr[0]; + return subsubarr.getClass().getName(); + } + @Compare public String classGetNameForMultiStringArray() { + return (new String[3][4][5][6][7][8][9]).getClass().getName(); + } + + @Compare public String classForByte() throws Exception { + return Class.forName("[Z").getName(); + } + + @Compare public String classForUnknownArray() { + try { + return Class.forName("[W").getName(); + } catch (Exception ex) { + return ex.getClass().getName(); + } + } + + @Compare public String classForUnknownDeepArray() { + try { + return Class.forName("[[[[[W").getName(); + } catch (Exception ex) { + return ex.getClass().getName(); + } + } + + @Compare public String componentGetNameForObjectArray() { + return (new Object[3]).getClass().getComponentType().getName(); + } + @Compare public boolean sameComponentGetNameForObjectArray() { + return (new Object[3]).getClass().getComponentType() == Object.class; + } + @Compare public String componentGetNameForSimpleIntArray() { + return (new int[3]).getClass().getComponentType().getName(); + } + @Compare public String componentGetNameForMultiIntArray() { + return (new int[3][4][5][6][7][8][9]).getClass().getComponentType().getName(); + } + @Compare public String componentGetNameForMultiStringArray() { + Class c = (new String[3][4][5][6][7][8][9]).getClass(); + StringBuilder sb = new StringBuilder(); + for (;;) { + sb.append(c.getName()).append("\n"); + c = c.getComponentType(); + if (c == null) { + break; + } + } + return sb.toString(); + } + + @Compare public boolean isArray() { + return new Object[0].getClass().isArray(); + } + + @JavaScriptBody(args = { "arr", "len" }, body="var a = arr.slice(0, len); a.sort(); return a;") + private static String[] sort(String[] arr, int len) { + List list = Arrays.asList(arr).subList(0, len); + Collections.sort(list); + return list.toArray(new String[0]); + } + + @JavaScriptBody(args = {}, body = "return new Object();") + private static Object newObject() { + return new Object(); + } + + @Factory + public static Object[] create() { + return VMTest.create(ReflectionTest.class); + } + +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ResourcesTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,45 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import java.io.InputStream; +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ResourcesTest { + + @Compare public String readResourceAsStream() throws Exception { + InputStream is = getClass().getResourceAsStream("Resources.txt"); + byte[] b = new byte[30]; + int len = is.read(b); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + sb.append((char)b[i]); + } + return sb.toString(); + } + + @Factory public static Object[] create() { + return VMTest.create(ResourcesTest.class); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/ShortArithmeticTest.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,102 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ShortArithmeticTest { + + private static short add(short x, short y) { + return (short)(x + y); + } + + private static short sub(short x, short y) { + return (short)(x - y); + } + + private static short mul(short x, short y) { + return (short)(x * y); + } + + private static short div(short x, short y) { + return (short)(x / y); + } + + private static short mod(short x, short y) { + return (short)(x % y); + } + + @Compare public short conversion() { + return (short)123456; + } + + @Compare public short addOverflow() { + return add(Short.MAX_VALUE, (short)1); + } + + @Compare public short subUnderflow() { + return sub(Short.MIN_VALUE, (short)1); + } + + @Compare public short addMaxShortAndMaxShort() { + return add(Short.MAX_VALUE, Short.MAX_VALUE); + } + + @Compare public short subMinShortAndMinShort() { + return sub(Short.MIN_VALUE, Short.MIN_VALUE); + } + + @Compare public short multiplyMaxShort() { + return mul(Short.MAX_VALUE, (short)2); + } + + @Compare public short multiplyMaxShortAndMaxShort() { + return mul(Short.MAX_VALUE, Short.MAX_VALUE); + } + + @Compare public short multiplyMinShort() { + return mul(Short.MIN_VALUE, (short)2); + } + + @Compare public short multiplyMinShortAndMinShort() { + return mul(Short.MIN_VALUE, Short.MIN_VALUE); + } + + @Compare public short multiplyPrecision() { + return mul((short)17638, (short)1103); + } + + @Compare public short division() { + return div((short)1, (short)2); + } + + @Compare public short divisionReminder() { + return mod((short)1, (short)2); + } + + @Factory + public static Object[] create() { + return VMTest.create(ShortArithmeticTest.class); + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/tck/StaticUse.java Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,31 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +class StaticUse { + public static final Object NON_NULL = new Object(); + private StaticUse() { + } + + public void instanceMethod() { + } + + public static int plus(int a, int b) { + return a + b; + } +} diff -r 1a73c6e08cc5 -r 95b68e21a53f vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/resources/org/apidesign/bck2brwsr/tck/Resources.txt Fri Jan 18 14:27:22 2013 +0100 @@ -0,0 +1,1 @@ +Ahoj