# HG changeset patch # User Jaroslav Tulach # Date 1355734642 -3600 # Node ID 4928b51565b21d7e573bf66764dbfd1d63a7d2bf # Parent 6949044415df81de34179efd7344ee29dcc05180# Parent 7579a0ee92fb5bef2dad5816ef78b51569818ed7 Merging in URLStreamHandler diff -r 6949044415df -r 4928b51565b2 emul/src/main/java/java/lang/Character.java --- a/emul/src/main/java/java/lang/Character.java Sun Dec 16 20:11:18 2012 +0100 +++ b/emul/src/main/java/java/lang/Character.java Mon Dec 17 09:57:22 2012 +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 6949044415df -r 4928b51565b2 emul/src/main/java/java/lang/Object.java --- a/emul/src/main/java/java/lang/Object.java Sun Dec 16 20:11:18 2012 +0100 +++ b/emul/src/main/java/java/lang/Object.java Mon Dec 17 09:57:22 2012 +0100 @@ -104,6 +104,11 @@ * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */ + @JavaScriptBody(args = "self", body = + "if (self.$hashCode) return self.$hashCode;\n" + + "var h = Math.random() * Math.pow(2, 32);\n" + + "return self.$hashCode = h & h;" + ) public native int hashCode(); /** diff -r 6949044415df -r 4928b51565b2 emul/src/main/java/java/lang/String.java --- a/emul/src/main/java/java/lang/String.java Sun Dec 16 20:11:18 2012 +0100 +++ b/emul/src/main/java/java/lang/String.java Mon Dec 17 09:57:22 2012 +0100 @@ -2143,6 +2143,12 @@ * @since 1.4 * @spec JSR-51 */ + @JavaScriptBody(args = { "self", "regex" }, body = + "self = self.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 +2561,7 @@ * @return the String, converted to lowercase. * @see java.lang.String#toLowerCase(Locale) */ + @JavaScriptBody(args = "self", body = "return self.toLowerCase();") public String toLowerCase() { throw new UnsupportedOperationException("Should be supported but without connection to locale"); } @@ -2720,6 +2727,7 @@ * @return the String, converted to uppercase. * @see java.lang.String#toUpperCase(Locale) */ + @JavaScriptBody(args = "self", body = "return self.toUpperCase();") public String toUpperCase() { throw new UnsupportedOperationException(); } diff -r 6949044415df -r 4928b51565b2 emul/src/main/java/java/net/URL.java --- a/emul/src/main/java/java/net/URL.java Sun Dec 16 20:11:18 2012 +0100 +++ b/emul/src/main/java/java/net/URL.java Mon Dec 17 09:57:22 2012 +0100 @@ -196,6 +196,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 +319,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 +398,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 +492,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 +545,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 +574,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 +598,7 @@ } } -// handler.parseURL(this, spec, start, limit); + handler.parseURL(this, spec, start, limit); } catch(MalformedURLException e) { throw e; @@ -557,7 +608,7 @@ throw exception; } } - + /* * Returns true if specified string is a valid protocol name. */ @@ -601,6 +652,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 +691,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 +750,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 +839,7 @@ return false; URL u2 = (URL)obj; - // return handler.equals(this, u2); - return u2 == this; + return handler.equals(this, u2); } /** @@ -789,7 +854,7 @@ if (hashCode != -1) return hashCode; - // hashCode = handler.hashCode(this); + hashCode = handler.hashCode(this); return hashCode; } @@ -805,8 +870,7 @@ * false otherwise. */ public boolean sameFile(URL other) { -// return handler.sameFile(this, other); - throw new UnsupportedOperationException(); + return handler.sameFile(this, other); } /** @@ -834,8 +898,7 @@ * @see java.net.URLStreamHandler#toExternalForm(java.net.URL) */ public String toExternalForm() { - throw new UnsupportedOperationException(); -// return handler.toExternalForm(this); + return handler.toExternalForm(this); } /** @@ -925,6 +988,11 @@ // return openConnection().getContent(classes); } + static URLStreamHandler getURLStreamHandler(String protocol) { + Class c = URLStreamHandler.class; // XXX only here to pre-initialize URLStreamHandler + URLStreamHandler universal = new URLStreamHandler() {}; + return universal; + } } diff -r 6949044415df -r 4928b51565b2 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 Mon Dec 17 09:57:22 2012 +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 6949044415df -r 4928b51565b2 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Sun Dec 16 20:11:18 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Dec 17 09:57:22 2012 +0100 @@ -19,7 +19,6 @@ import java.io.IOException; import java.io.InputStream; -import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.javap.AnnotationParser; import org.apidesign.javap.ClassData; import org.apidesign.javap.FieldData; @@ -76,6 +75,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", @@ -632,7 +636,7 @@ case opc_i2b: case opc_i2c: case opc_i2s: - out.append("/* number conversion */"); + out.append("{ /* number conversion */ }"); break; case opc_aconst_null: emit(out, "@1 = null;", smapper.pushA()); diff -r 6949044415df -r 4928b51565b2 vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Sun Dec 16 20:11:18 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Mon Dec 17 09:57:22 2012 +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); @@ -64,7 +64,7 @@ new Gen(this, out).compile(new ByteArrayInputStream(arr)); String code = out.toString().toString(); String under = name.replace('.', '_'); - return applyCode(loader, under, code); + return applyCode(loader, under, code, instance); } /* possibly not needed: @@ -76,15 +76,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 vm[name](instance);\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 { @@ -104,7 +104,8 @@ + "\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(lazy, dot, instance);" + "\n};" + "\nreturn true;") @Override diff -r 6949044415df -r 4928b51565b2 vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Sun Dec 16 20:11:18 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Mon Dec 17 09:57:22 2012 +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 6949044415df -r 4928b51565b2 vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Sun Dec 16 20:11:18 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Mon Dec 17 09:57:22 2012 +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 6949044415df -r 4928b51565b2 vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java Sun Dec 16 20:11:18 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareHashTest.java Mon Dec 17 09:57:22 2012 +0100 @@ -30,6 +30,11 @@ return "Ahoj".hashCode(); } + @Compare public int hashRemainsYieldsZero() { + Object o = new Object(); + return o.hashCode() - o.hashCode(); + } + @Factory public static Object[] create() { return CompareVMs.create(CompareHashTest.class); diff -r 6949044415df -r 4928b51565b2 vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java Sun Dec 16 20:11:18 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java Mon Dec 17 09:57:22 2012 +0100 @@ -17,7 +17,8 @@ */ package org.apidesign.vm4brwsr.tck; -import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; import org.apidesign.vm4brwsr.Compare; import org.apidesign.vm4brwsr.CompareVMs; import org.testng.annotations.Factory; @@ -27,6 +28,10 @@ * @author Jaroslav Tulach */ public class CompareStringsTest { + @Compare public static Object compareURLs() throws MalformedURLException { + return new URL("http://apidesign.org:8080/wiki/").toExternalForm().toString(); + } + @Compare public String deleteLastTwoCharacters() { StringBuilder sb = new StringBuilder(); sb.append("453.0"); @@ -43,9 +48,63 @@ @Compare public String nameOfArrayClass() throws Exception { return Class.forName("org.apidesign.vm4brwsr.Array").getName(); } + + @Compare public String lowerHello() { + return "HeLlO".toLowerCase(); + } + + @Compare public String lowerA() { + return String.valueOf(Character.toLowerCase('A')).toString(); + } + @Compare public String upperHello() { + return "hello".toUpperCase(); + } + + @Compare public String upperA() { + return String.valueOf(Character.toUpperCase('a')).toString(); + } + + @Compare public boolean matchRegExp() throws Exception { + return "58038503".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExp() throws Exception { + return "58038503GH".matches("\\d*"); + } + + @Compare public boolean doesNotMatchRegExpFully() throws Exception { + return "Hello".matches("Hell"); + } + + @Compare public String variousCharacterTests() throws Exception { + StringBuilder sb = new StringBuilder(); + sb.append(Character.isUpperCase('a')); + sb.append(Character.isUpperCase('A')); + sb.append(Character.isLowerCase('a')); + sb.append(Character.isLowerCase('A')); + + sb.append(Character.isLetter('A')); + sb.append(Character.isLetterOrDigit('9')); + sb.append(Character.isLetterOrDigit('A')); + sb.append(Character.isLetter('0')); + + return sb.toString().toString(); + } + + @Compare + public String nullFieldInitialized() { + NullField nf = new NullField(); + return ("" + nf.name).toString(); + } + @Factory public static Object[] create() { return CompareVMs.create(CompareStringsTest.class); } + + private static final class NullField { + + String name; + } }