diff -urN -x CVS /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/gnu/java/lang/reflect/TypeSignature.java kaffe/libraries/javalib/gnu/java/lang/reflect/TypeSignature.java --- /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/gnu/java/lang/reflect/TypeSignature.java Thu Jan 1 01:00:00 1970 +++ kaffe/libraries/javalib/gnu/java/lang/reflect/TypeSignature.java Tue May 20 16:48:13 2003 @@ -0,0 +1,261 @@ +/* TypeSignature.java -- Class used to compute type signatures + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.java.lang.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; + +/** + * This class provides static methods that can be used to compute + * type-signatures of Classs or Members. + * More specific methods are also provided for computing the + * type-signature of Constructors and + * Methods. Methods are also provided to go in the + * reverse direction. + * + * @author Eric Blake + */ +public class TypeSignature +{ + /** + * Returns a String representing the type-encoding of a class. + * The .class file format has different encodings for classes, depending + * on whether it must be disambiguated from primitive types or not; hence + * the descriptor parameter to choose between them. If you are planning + * on decoding primitive types along with classes, then descriptor should + * be true for correct results. Type-encodings are computed as follows: + * + *
+   * boolean -> "Z"
+   * byte    -> "B"
+   * char    -> "C"
+   * double  -> "D"
+   * float   -> "F"
+   * int     -> "I"
+   * long    -> "J"
+   * short   -> "S"
+   * void    -> "V"
+   * arrays  -> "[" + descriptor format of component type
+   * object  -> class format: fully qualified name with '.' replaced by '/'
+   *            descriptor format: "L" + class format + ";"
+   * 
+ * + * @param type the class name to encode + * @param descriptor true to return objects in descriptor format + * @return the class name, as it appears in bytecode constant pools + * @see #getClassForEncoding(String) + */ + public static String getEncodingOfClass(String type, boolean descriptor) + { + if (! descriptor || type.charAt(0) == '[') + return type.replace('.', '/'); + if (type.equals("boolean")) + return "Z"; + if (type.equals("byte")) + return "B"; + if (type.equals("short")) + return "S"; + if (type.equals("char")) + return "C"; + if (type.equals("int")) + return "I"; + if (type.equals("long")) + return "J"; + if (type.equals("float")) + return "F"; + if (type.equals("double")) + return "D"; + if (type.equals("void")) + return "V"; + return 'L' + type.replace('.', '/') + ';'; + } + + /** + * Gets the descriptor encoding for a class. + * + * @param clazz the class to encode + * @param descriptor true to return objects in descriptor format + * @return the class name, as it appears in bytecode constant pools + * @see #getEncodingOfClass(String, boolean) + */ + public static String getEncodingOfClass(Class clazz, boolean descriptor) + { + return getEncodingOfClass(clazz.getName(), descriptor); + } + + /** + * Gets the descriptor encoding for a class. + * + * @param clazz the class to encode + * @return the class name, as it appears in bytecode constant pools + * @see #getEncodingOfClass(String, boolean) + */ + public static String getEncodingOfClass(Class clazz) + { + return getEncodingOfClass(clazz.getName(), true); + } + + + /** + * This function is the inverse of getEncodingOfClass. This + * accepts both object and descriptor formats, but must know which style + * of string is being passed in (usually, descriptor should be true). In + * descriptor format, "I" is treated as int.class, in object format, it + * is treated as a class named I in the unnamed package. + * + * @param type_code the class name to decode + * @param descriptor if the string is in descriptor format + * @return the corresponding Class object + * @throws ClassNotFoundException if the class cannot be located + * @see #getEncodingOfClass(Class, boolean) + */ + public static Class getClassForEncoding(String type_code, boolean descriptor) + throws ClassNotFoundException + { + if (descriptor) + { + switch (type_code.charAt(0)) + { + case 'B': + return byte.class; + case 'C': + return char.class; + case 'D': + return double.class; + case 'F': + return float.class; + case 'I': + return int.class; + case 'J': + return long.class; + case 'S': + return short.class; + case 'V': + return void.class; + case 'Z': + return boolean.class; + default: + throw new ClassNotFoundException("Invalid class name: " + + type_code); + case 'L': + type_code = type_code.substring(1, type_code.length() - 1); + // Fallthrough. + case '[': + } + } + return Class.forName(type_code.replace('/', '.')); + } + + /** + * Gets the Class object for a type name. + * + * @param type_code the class name to decode + * @return the corresponding Class object + * @throws ClassNotFoundException if the class cannot be located + * @see #getClassForEncoding(String, boolean) + */ + public static Class getClassForEncoding(String type_code) + throws ClassNotFoundException + { + return getClassForEncoding(type_code, true); + } + + /** + * Returns a String representing the type-encoding of a + * method. The type-encoding of a method is: + * + * "(" + parameter type descriptors + ")" + return type descriptor + * + * XXX This could be faster if it were implemented natively. + * + * @param m the method to encode + * @return the encoding + */ + public static String getEncodingOfMethod(Method m) + { + Class[] paramTypes = m.getParameterTypes(); + StringBuffer buf = new StringBuffer().append('('); + for (int i = 0; i < paramTypes.length; i++) + buf.append(getEncodingOfClass(paramTypes[i].getName(), true)); + buf.append(')').append(getEncodingOfClass(m.getReturnType().getName(), + true)); + return buf.toString(); + } + + /** + * Returns a String representing the type-encoding of a + * constructor. The type-encoding of a method is: + * + * "(" + parameter type descriptors + ")V" + * + * XXX This could be faster if it were implemented natively. + * + * @param c the constructor to encode + * @return the encoding + */ + public static String getEncodingOfConstructor(Constructor c) + { + Class[] paramTypes = c.getParameterTypes(); + StringBuffer buf = new StringBuffer().append('('); + for (int i = 0; i < paramTypes.length; i++) + buf.append(getEncodingOfClass(paramTypes[i].getName(), true)); + buf.append(")V"); + return buf.toString(); + } + + /** + * Returns a String representing the type-encoding of a + * class member. This appropriately handles Constructors, Methods, and + * Fields. + * + * @param mem the member to encode + * @return the encoding + */ + public static String getEncodingOfMember(Member mem) + { + if (mem instanceof Constructor) + return getEncodingOfConstructor((Constructor) mem); + if (mem instanceof Method) + return getEncodingOfMethod((Method) mem); + else // Field + return getEncodingOfClass(((Field) mem).getType().getName(), true); + } +} // class TypeSignature diff -urN -x CVS /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/java/lang/reflect/Modifier.java kaffe/libraries/javalib/java/lang/reflect/Modifier.java --- /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/java/lang/reflect/Modifier.java Wed Apr 18 11:31:38 2001 +++ kaffe/libraries/javalib/java/lang/reflect/Modifier.java Tue May 20 16:50:34 2003 @@ -20,6 +20,14 @@ public static final int STATIC = 0x0008; public static final int FINAL = 0x0010; public static final int SYNCHRONIZED = 0x0020; + + /** + * Super - treat invokespecial as polymorphic so that super.foo() works + * according to the JLS. This is a reuse of the synchronized constant + * to patch a hole in JDK 1.0. *shudder*. + */ + static final int SUPER = 0x0020; + public static final int VOLATILE = 0x0040; public static final int TRANSIENT = 0x0080; public static final int NATIVE = 0x0100; diff -urN -x CVS /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/java/lang/reflect/Proxy.java kaffe/libraries/javalib/java/lang/reflect/Proxy.java --- /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/java/lang/reflect/Proxy.java Wed Apr 18 11:31:46 2001 +++ kaffe/libraries/javalib/java/lang/reflect/Proxy.java Tue May 20 17:34:39 2003 @@ -1,54 +1,1624 @@ -/* - * Java core library component. +/* Proxy.java -- build a proxy class that implements reflected interfaces + Copyright (C) 2001, 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath 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; either version 2, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.lang.reflect; + +import java.io.Serializable; +import java.security.ProtectionDomain; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import gnu.classpath.Configuration; +import gnu.java.lang.reflect.TypeSignature; + +/** + * This class allows you to dynamically create an instance of any (or + * even multiple) interfaces by reflection, and decide at runtime + * how that instance will behave by giving it an appropriate + * {@link InvocationHandler}. Proxy classes serialize specially, so + * that the proxy object can be reused between VMs, without requiring + * a persistent copy of the generated class code. + * + *

Creation

+ * To create a proxy for some interface Foo: + * + *
+ *   InvocationHandler handler = new MyInvocationHandler(...);
+ *   Class proxyClass = Proxy.getProxyClass(
+ *       Foo.class.getClassLoader(), new Class[] { Foo.class });
+ *   Foo f = (Foo) proxyClass
+ *       .getConstructor(new Class[] { InvocationHandler.class })
+ *       .newInstance(new Object[] { handler });
+ * 
+ * or more simply: + *
+ *   Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
+ *                                        new Class[] { Foo.class },
+ *                                        handler);
+ * 
* - * Copyright (c) 2001 - * Edouard G. Parmelan. All rights reserverd. - * Copyright (c) 2001 - * Transvirtual Technologies, Inc. All rights reserved. + *

Dynamic Proxy Classes

+ * A dynamic proxy class is created at runtime, and has the following + * properties: + * * - * See the file "license.terms" for information on usage and redistribution - * of this file. + *

Proxy Instances

+ * A proxy instance is an instance of a proxy class. It has the + * following properties, many of which follow from the properties of a + * proxy class listed above: + * * - * Author: Edouard G. Parmelan - * Checked Spec: JDK 1.3 - NotImplemented + *

Inheritance Issues

+ * A proxy class may inherit a method from more than one interface. + * The order in which interfaces are listed matters, because it determines + * which reflected {@link Method} object will be passed to the invocation + * handler. This means that the dynamically generated class cannot + * determine through which interface a method is being invoked.

+ * + * In short, if a method is declared in Object (namely, hashCode, + * equals, or toString), then Object will be used; otherwise, the + * leftmost interface that inherits or declares a method will be used, + * even if it has a more permissive throws clause than what the proxy + * class is allowed. Thus, in the invocation handler, it is not always + * safe to assume that every class listed in the throws clause of the + * passed Method object can safely be thrown; fortunately, the Proxy + * instance is robust enough to wrap all illegal checked exceptions in + * {@link UndeclaredThrowableException}. + * + * @see InvocationHandler + * @see UndeclaredThrowableException + * @see Class + * @author Eric Blake + * @since 1.3 + * @status updated to 1.4, except for the use of ProtectionDomain */ +public class Proxy implements Serializable +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = -2222568056686623797L; -package java.lang.reflect; + /** + * Map of ProxyType to proxy class. + * + * @XXX This prevents proxy classes from being garbage collected. + * java.util.WeakHashSet is not appropriate, because that collects the + * keys, but we are interested in collecting the elements. + */ + private static final Map proxyClasses = new HashMap(); -import java.io.Serializable; + /** + * The invocation handler for this proxy instance. For Proxy, this + * field is unused, but it appears here in order to be serialized in all + * proxy classes. + * + * NOTE: This implementation is more secure for proxy classes + * than what Sun specifies. Sun does not require h to be immutable, but + * this means you could change h after the fact by reflection. However, + * by making h immutable, we may break non-proxy classes which extend + * Proxy. + * @serial invocation handler associated with this proxy instance + */ + protected InvocationHandler h; -public class Proxy - implements Serializable -{ - protected InvocationHandler h; + /** + * Constructs a new Proxy from a subclass (usually a proxy class), + * with the specified invocation handler. + * + * NOTE: This throws a NullPointerException if you attempt + * to create a proxy instance with a null handler using reflection. + * This behavior is not yet specified by Sun; see Sun Bug 4487672. + * + * @param handler the invocation handler, may be null if the subclass + * is not a proxy class + * @throws NullPointerException if handler is null and this is a proxy + * instance + */ + protected Proxy(InvocationHandler handler) + { + if (handler == null && isProxyClass(getClass())) + throw new NullPointerException("invalid handler"); + h = handler; + } + + /** + * Returns the proxy {@link Class} for the given ClassLoader and array + * of interfaces, dynamically generating it if necessary. + * + * There are several restrictions on this method, the violation of + * which will result in an IllegalArgumentException or + * NullPointerException: + *

+ * + * Note that different orders of interfaces produce distinct classes. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the array of interfaces the proxy class implements, + * may be empty, but not null + * @return the Class object of the proxy class + * @throws IllegalArgumentException if the constraints above were + * violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry + */ + // synchronized so that we aren't trying to build the same class + // simultaneously in two threads + public static synchronized Class getProxyClass(ClassLoader loader, + Class[] interfaces) + { + interfaces = (Class[]) interfaces.clone(); + ProxyType pt = new ProxyType(loader, interfaces); + Class clazz = (Class) proxyClasses.get(pt); + if (clazz == null) + { + if (Configuration.HAVE_NATIVE_GET_PROXY_CLASS) + clazz = getProxyClass0(loader, interfaces); + else + { + ProxyData data = (Configuration.HAVE_NATIVE_GET_PROXY_DATA + ? getProxyData0(loader, interfaces) + : ProxyData.getProxyData(pt)); + + // FIXME workaround for bug in gcj 3.0.x + // Not needed with the latest gcj from cvs + //clazz = (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS + // ? generateProxyClass0(loader, data) + // : new ClassFactory(data).generate(loader)); + if (Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS) + clazz = generateProxyClass0(loader, data); + else + { + ClassFactory cf = new ClassFactory(data); + clazz = cf.generate(loader); + } + } + + Object check = proxyClasses.put(pt, clazz); + // assert check == null && clazz != null; + if (check != null || clazz == null) + throw new InternalError(/*"Fatal flaw in getProxyClass"*/); + } + return clazz; + } + + /** + * Combines several methods into one. This is equivalent to: + *

+   *   Proxy.getProxyClass(loader, interfaces)
+   *       .getConstructor(new Class[] {InvocationHandler.class})
+   *       .newInstance(new Object[] {handler});
+   * 
+ * except that it will not fail with the normal problems caused + * by reflection. It can still fail for the same reasons documented + * in getProxyClass, or if handler is null. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the array of interfaces the proxy class implements, + * may be empty, but not null + * @param handler the invocation handler, may not be null + * @return a proxy instance implementing the specified interfaces + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see #getProxyClass(ClassLoader, Class[]) + * @see Class#getConstructor(Class[]) + * @see Constructor#newInstance(Object[]) + */ + public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler handler) + { + try + { + // getProxyClass() and Proxy() throw the necessary exceptions + return getProxyClass(loader, interfaces) + .getConstructor(new Class[] {InvocationHandler.class}) + .newInstance(new Object[] {handler}); + } + catch (RuntimeException e) + { + // Let IllegalArgumentException, NullPointerException escape. + // assert e instanceof IllegalArgumentException + // || e instanceof NullPointerException; + throw e; + } + catch (InvocationTargetException e) + { + // Let wrapped NullPointerException escape. + // assert e.getTargetException() instanceof NullPointerException + throw (NullPointerException) e.getCause(); + } + catch (Exception e) + { + // Covers InstantiationException, IllegalAccessException, + // NoSuchMethodException, none of which should be generated + // if the proxy class was generated correctly. + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * Returns true if and only if the Class object is a dynamically created + * proxy class (created by getProxyClass or by the + * syntactic sugar of newProxyInstance). + * + *

This check is secure (in other words, it is not simply + * clazz.getSuperclass() == Proxy.class), it will not + * be spoofed by non-proxy classes that extend Proxy. + * + * @param clazz the class to check, must not be null + * @return true if the class represents a proxy class + * @throws NullPointerException if clazz is null + */ + // This is synchronized on the off chance that another thread is + // trying to add a class to the map at the same time we read it. + public static synchronized boolean isProxyClass(Class clazz) + { + if (! Proxy.class.isAssignableFrom(clazz)) + return false; + // This is a linear search, even though we could do an O(1) search + // using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()). + return proxyClasses.containsValue(clazz); + } + + /** + * Returns the invocation handler for the given proxy instance.

+ * + * NOTE: We guarantee a non-null result if successful, + * but Sun allows the creation of a proxy instance with a null + * handler. See the comments for {@link #Proxy(InvocationHandler)}. + * + * @param proxy the proxy instance, must not be null + * @return the invocation handler, guaranteed non-null. + * @throws IllegalArgumentException if + * Proxy.isProxyClass(proxy.getClass()) returns false. + * @throws NullPointerException if proxy is null + */ + public static InvocationHandler getInvocationHandler(Object proxy) + { + if (! isProxyClass(proxy.getClass())) + throw new IllegalArgumentException("not a proxy instance"); + return ((Proxy) proxy).h; + } + + /** + * Optional native method to replace (and speed up) the pure Java + * implementation of getProxyClass. Only needed if + * Configuration.HAVE_NATIVE_GET_PROXY_CLASS is true, this does the + * work of both getProxyData0 and generateProxyClass0 with no + * intermediate form in Java. The native code may safely assume that + * this class must be created, and does not already exist. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the interfaces the class will extend + * @return the generated proxy class + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see Configuration#HAVE_NATIVE_GET_PROXY_CLASS + * @see #getProxyClass(ClassLoader, Class[]) + * @see #getProxyData0(ClassLoader, Class[]) + * @see #generateProxyClass0(ProxyData) + */ + private static native Class getProxyClass0(ClassLoader loader, + Class[] interfaces); + + /** + * Optional native method to replace (and speed up) the pure Java + * implementation of getProxyData. Only needed if + * Configuration.HAVE_NATIVE_GET_PROXY_DATA is true. The native code + * may safely assume that a new ProxyData object must be created which + * does not duplicate any existing ones. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces the interfaces the class will extend + * @return all data that is required to make this proxy class + * @throws IllegalArgumentException if the constraints for getProxyClass + * were violated, except for problems with null + * @throws NullPointerException if `interfaces' is null or contains + * a null entry, or if handler is null + * @see Configuration.HAVE_NATIVE_GET_PROXY_DATA + * @see #getProxyClass(ClassLoader, Class[]) + * @see #getProxyClass0(ClassLoader, Class[]) + * @see ProxyType#getProxyData() + */ + private static native ProxyData getProxyData0(ClassLoader loader, + Class[] interfaces); + + /** + * Optional native method to replace (and speed up) the pure Java + * implementation of generateProxyClass. Only needed if + * Configuration.HAVE_NATIVE_GENERATE_PROXY_CLASS is true. The native + * code may safely assume that a new Class must be created, and that + * the ProxyData object does not describe any existing class. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param data the struct of information to convert to a Class. This + * has already been verified for all problems except exceeding + * VM limitations + * @return the newly generated class + * @throws IllegalArgumentException if VM limitations are exceeded + * @see #getProxyClass(ClassLoader, Class[]) + * @see #getProxyClass0(ClassLoader, Class[]) + * @see ProxyData#generateProxyClass(ClassLoader) + */ + private static native Class generateProxyClass0(ClassLoader loader, + ProxyData data); + + /** + * Helper class for mapping unique ClassLoader and interface combinations + * to proxy classes. + * + * @author Eric Blake + */ + private static final class ProxyType + { + /** + * Store the class loader (may be null) + */ + final ClassLoader loader; + + /** + * Store the interfaces (never null, all elements are interfaces) + */ + final Class[] interfaces; + + /** + * Construct the helper object. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @param interfaces an array of interfaces + */ + ProxyType(ClassLoader loader, Class[] interfaces) + { + if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + this.loader = loader; + this.interfaces = interfaces; + } + + /** + * Calculates the hash code. + * + * @return a combination of the classloader and interfaces hashcodes. + */ + public int hashCode() + { + //loader is always not null + int hash = loader.hashCode(); + for (int i = 0; i < interfaces.length; i++) + hash = hash * 31 + interfaces[i].hashCode(); + return hash; + } - private Proxy() { + // A more comprehensive comparison of two arrays, + // ignore array element order, and + // ignore redundant elements + private static boolean sameTypes(Class arr1[], Class arr2[]) { + if (arr1.length == 1 && arr2.length == 1) { + return arr1[0] == arr2[0]; + } + + // total occurrance of elements of arr1 in arr2 + int total_occ_of_arr1_in_arr2 = 0; + each_type: + for (int i = arr1.length; --i >= 0; ) + { + Class t = arr1[i]; + for (int j = i; --j >= 0; ) + { + if (t == arr1[j]) + { //found duplicate type + continue each_type; + } + } + + // count c(a unique element of arr1)'s + // occurrences in arr2 + int occ_in_arr2 = 0; + for (int j = arr2.length; --j >= 0; ) + { + if (t == arr2[j]) + { + ++occ_in_arr2; + } + } + if (occ_in_arr2 == 0) + { // t does not occur in arr2 + return false; + } + + total_occ_of_arr1_in_arr2 += occ_in_arr2; + } + // now, each element of arr2 must have been visited + return total_occ_of_arr1_in_arr2 == arr2.length; + } + + /** + * Calculates equality. + * + * @param the object to compare to + * @return true if it is a ProxyType with same data + */ + public boolean equals(Object other) + { + ProxyType pt = (ProxyType) other; + if (loader != pt.loader || interfaces.length != pt.interfaces.length) + return false; + return sameTypes(interfaces, pt.interfaces); } + } // class ProxyType + + /** + * Helper class which allows hashing of a method name and signature + * without worrying about return type, declaring class, or throws clause, + * and which reduces the maximally common throws clause between two methods + * + * @author Eric Blake + */ + private static final class ProxySignature + { + /** + * The core signatures which all Proxy instances handle. + */ + static final HashMap coreMethods = new HashMap(); + static + { + try + { + ProxySignature sig + = new ProxySignature(Object.class + .getMethod("equals", + new Class[] {Object.class})); + coreMethods.put(sig, sig); + sig = new ProxySignature(Object.class.getMethod("hashCode", null)); + coreMethods.put(sig, sig); + sig = new ProxySignature(Object.class.getMethod("toString", null)); + coreMethods.put(sig, sig); + } + catch (Exception e) + { + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * The underlying Method object, never null + */ + final Method method; - protected Proxy(InvocationHandler h) { - this.h = h; + /** + * The set of compatible thrown exceptions, may be empty + */ + final Set exceptions = new HashSet(); + + /** + * Construct a signature + * + * @param method the Method this signature is based on, never null + */ + ProxySignature(Method method) + { + this.method = method; + Class[] exc = method.getExceptionTypes(); + int i = exc.length; + while (--i >= 0) + { + // discard unchecked exceptions + if (Error.class.isAssignableFrom(exc[i]) + || RuntimeException.class.isAssignableFrom(exc[i])) + continue; + exceptions.add(exc[i]); + } } - public static Class getProxyClass(ClassLoader loader, Class[] interfaces) - throws IllegalArgumentException + /** + * Given a method, make sure it's return type is identical + * to this, and adjust this signature's throws clause appropriately + * + * @param other the signature to merge in + * @throws IllegalArgumentException if the return types conflict + */ + void checkCompatibility(ProxySignature other) { - throw new kaffe.util.NotImplemented("java.lang.reflect.Proxy"); + if (method.getReturnType() != other.method.getReturnType()) + throw new IllegalArgumentException("incompatible return types: " + + method + ", " + other.method); + + // if you can think of a more efficient way than this O(n^2) search, + // implement it! + int size1 = exceptions.size(); + int size2 = other.exceptions.size(); + boolean[] valid1 = new boolean[size1]; + boolean[] valid2 = new boolean[size2]; + Iterator itr = exceptions.iterator(); + int pos = size1; + while (--pos >= 0) + { + Class c1 = (Class) itr.next(); + Iterator itr2 = other.exceptions.iterator(); + int pos2 = size2; + while (--pos2 >= 0) + { + Class c2 = (Class) itr2.next(); + if (c2.isAssignableFrom(c1)) + valid1[pos] = true; + if (c1.isAssignableFrom(c2)) + valid2[pos2] = true; + } + } + pos = size1; + itr = exceptions.iterator(); + while (--pos >= 0) + { + itr.next(); + if (! valid1[pos]) + itr.remove(); + } + pos = size2; + itr = other.exceptions.iterator(); + while (--pos >= 0) + { + itr.next(); + if (! valid2[pos]) + itr.remove(); + } + exceptions.addAll(other.exceptions); } - public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) - throws IllegalArgumentException + /** + * Calculates the hash code. + * + * @return a combination of name and parameter types + */ + public int hashCode() { - throw new kaffe.util.NotImplemented("java.lang.reflect.Proxy"); + int hash = method.getName().hashCode(); + Class[] types = method.getParameterTypes(); + for (int i = 0; i < types.length; i++) + hash = hash * 31 + types[i].hashCode(); + return hash; } - public static boolean isProxyClass(Class cl) + /** + * Calculates equality. + * + * @param the object to compare to + * @return true if it is a ProxySignature with same data + */ + public boolean equals(Object other) { - throw new kaffe.util.NotImplemented("java.lang.reflect.Proxy"); + ProxySignature ps = (ProxySignature) other; + Class[] types1 = method.getParameterTypes(); + Class[] types2 = ps.method.getParameterTypes(); + if (! method.getName().equals(ps.method.getName()) + || types1.length != types2.length) + return false; + int i = types1.length; + while (--i >= 0) + if (types1[i] != types2[i]) + return false; + return true; + } + } // class ProxySignature + + /** + * A flat representation of all data needed to generate bytecode/instantiate + * a proxy class. This is basically a struct. + * + * @author Eric Blake + */ + private static final class ProxyData + { + /** + * The package this class is in. Possibly null, meaning the unnamed + * package. + */ + Package pack; + + /** + * The interfaces this class implements. Non-null, but possibly empty. + */ + Class[] interfaces; + + /** + * The Method objects this class must pass as the second argument to + * invoke (also useful for determining what methods this class has). + * Non-null, non-empty (includes at least Object.hashCode, Object.equals, + * and Object.toString). + */ + Method[] methods; + + /** + * The exceptions that do not need to be wrapped in + * UndeclaredThrowableException. exceptions[i] is the same as, or a + * subset of subclasses, of methods[i].getExceptionTypes(), depending on + * compatible throws clauses with multiple inheritance. It is unspecified + * if these lists include or exclude subclasses of Error and + * RuntimeException, but excluding them is harmless and generates a + * smaller class. + */ + Class[][] exceptions; + + /** + * For unique id's + */ + private static int count = 0; + + /** + * The id of this proxy class + */ + final int id = count++; + + /** + * Construct a ProxyData with uninitialized data members. + */ + ProxyData() + { + } + + /** + * Verifies that the arguments are legal, and sets up remaining data + * This should only be called when a class must be generated, as + * it is expensive. + * + * @param pt the ProxyType to convert to ProxyData + * @return the flattened, verified ProxyData structure for use in + * class generation + * @throws IllegalArgumentException if `interfaces' contains + * non-interfaces or incompatible combinations, and verify is true + * @throws NullPointerException if interfaces is null or contains null + */ + static ProxyData getProxyData(ProxyType pt) + { + Map method_set = (Map) ProxySignature.coreMethods.clone(); + boolean in_package = false; // true if we encounter non-public interface + + ProxyData data = new ProxyData(); + data.interfaces = pt.interfaces; + + // if interfaces is too large, we croak later on when the constant + // pool overflows + int i = data.interfaces.length; + while (--i >= 0) + { + Class inter = data.interfaces[i]; + if (! inter.isInterface()) + throw new IllegalArgumentException("not an interface: " + inter); + try + { + if (Class.forName(inter.getName(), false, pt.loader) != inter) + throw new IllegalArgumentException("not accessible in " + + "classloader: " + inter); + } + catch (ClassNotFoundException e) + { + throw new IllegalArgumentException("not accessible in " + + "classloader: " + inter); + } + if (! Modifier.isPublic(inter.getModifiers())) + if (in_package) + { + Package p = inter.getPackage(); + if (data.pack != inter.getPackage()) + throw new IllegalArgumentException("non-public interfaces " + + "from different " + + "packages"); + } + else + { + in_package = true; + data.pack = inter.getPackage(); + } + for (int j = i-1; j >= 0; j--) + if (data.interfaces[j] == inter) + throw new IllegalArgumentException("duplicate interface: " + + inter); + Method[] methods = inter.getMethods(); + int j = methods.length; + while (--j >= 0) + { + ProxySignature sig = new ProxySignature(methods[j]); + ProxySignature old = (ProxySignature) method_set.put(sig, sig); + if (old != null) + sig.checkCompatibility(old); + } + } + + i = method_set.size(); + data.methods = new Method[i]; + data.exceptions = new Class[i][]; + Iterator itr = method_set.values().iterator(); + while (--i >= 0) + { + ProxySignature sig = (ProxySignature) itr.next(); + data.methods[i] = sig.method; + data.exceptions[i] = (Class[]) sig.exceptions + .toArray(new Class[sig.exceptions.size()]); + } + return data; + } + } // class ProxyData + + /** + * Does all the work of building a class. By making this a nested class, + * this code is not loaded in memory if the VM has a native + * implementation instead. + * + * @author Eric Blake + */ + private static final class ClassFactory + { + /** Constants for assisting the compilation */ + private static final byte POOL = 0; + private static final byte FIELD = 1; + private static final byte METHOD = 2; + private static final byte INTERFACE = 3; + private static final String CTOR_SIG + = "(Ljava/lang/reflect/InvocationHandler;)V"; + private static final String INVOKE_SIG = "(Ljava/lang/Object;" + + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"; + + /** Bytecodes for insertion in the class definition byte[] */ + private static final char ACONST_NULL = 1; + private static final char ICONST_0 = 3; + private static final char BIPUSH = 16; + private static final char SIPUSH = 17; + private static final char ILOAD = 21; + private static final char ILOAD_0 = 26; + private static final char ALOAD_0 = 42; + private static final char ALOAD_1 = 43; + private static final char AALOAD = 50; + private static final char AASTORE = 83; + private static final char DUP = 89; + private static final char DUP_X1 = 90; + private static final char SWAP = 95; + private static final char IRETURN = 172; + private static final char LRETURN = 173; + private static final char FRETURN = 174; + private static final char DRETURN = 175; + private static final char ARETURN = 176; + private static final char RETURN = 177; + private static final char GETSTATIC = 178; + private static final char GETFIELD = 180; + private static final char INVOKEVIRTUAL = 182; + private static final char INVOKESPECIAL = 183; + private static final char INVOKESTATIC = 184; + private static final char INVOKEINTERFACE = 185; + private static final char NEW = 187; + private static final char ANEWARRAY = 189; + private static final char ATHROW = 191; + private static final char CHECKCAST = 192; + + // Implementation note: we use StringBuffers to hold the byte data, since + // they automatically grow. However, we only use the low 8 bits of + // every char in the array, so we are using twice the necessary memory + // for the ease StringBuffer provides. + + /** The constant pool. */ + private final StringBuffer pool = new StringBuffer(); + /** The rest of the class data. */ + private final StringBuffer stream = new StringBuffer(); + + /** Map of strings to byte sequences, to minimize size of pool. */ + private final Map poolEntries = new HashMap(); + + /** The VM name of this proxy class. */ + private final String qualName; + + /** + * The Method objects the proxy class refers to when calling the + * invocation handler. + */ + private final Method[] methods; + + /** + * Initializes the buffers with the bytecode contents for a proxy class. + * + * @param data the remainder of the class data + * @throws IllegalArgumentException if anything else goes wrong this + * late in the game; as far as I can tell, this will only happen + * if the constant pool overflows, which is possible even when + * the user doesn't exceed the 65535 interface limit + */ + ClassFactory(ProxyData data) + { + methods = data.methods; + + // magic = 0xcafebabe + // minor_version = 0 + // major_version = 46 + // constant_pool_count: place-holder for now + pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0"); + // constant_pool[], filled in as we go + + // access_flags + putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC); + // this_class + qualName = ((data.pack == null ? "" : data.pack.getName() + '.') + + "$Proxy" + data.id); + putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false))); + // super_class + putU2(classInfo("java/lang/reflect/Proxy")); + + // interfaces_count + putU2(data.interfaces.length); + // interfaces[] + for (int i = 0; i < data.interfaces.length; i++) + putU2(classInfo(data.interfaces[i])); + + // Recall that Proxy classes serialize specially, so we do not need + // to worry about a method for this field. Instead, we + // just assign it by reflection after the class is successfully loaded. + // fields_count - private static Method[] m; + putU2(1); + // fields[] + // m.access_flags + putU2(Modifier.PRIVATE | Modifier.STATIC); + // m.name_index + putU2(utf8Info("m")); + // m.descriptor_index + putU2(utf8Info("[Ljava/lang/reflect/Method;")); + // m.attributes_count + putU2(0); + // m.attributes[] + + // methods_count - # handler methods, plus + putU2(methods.length + 1); + // methods[] + // .access_flags + putU2(Modifier.PUBLIC); + // .name_index + putU2(utf8Info("")); + // .descriptor_index + putU2(utf8Info(CTOR_SIG)); + // .attributes_count - only Code is needed + putU2(1); + // .Code.attribute_name_index + putU2(utf8Info("Code")); + // .Code.attribute_length = 18 + // .Code.info: + // $Proxynn(InvocationHandler h) { super(h); } + // .Code.max_stack = 2 + // .Code.max_locals = 2 + // .Code.code_length = 6 + // .Code.code[] + stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1 + + INVOKESPECIAL); + putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "", CTOR_SIG)); + // .Code.exception_table_length = 0 + // .Code.exception_table[] + // .Code.attributes_count = 0 + // .Code.attributes[] + stream.append(RETURN + "\0\0\0\0"); + + for (int i = methods.length - 1; i >= 0; i--) + emitMethod(i, data.exceptions[i]); + + // attributes_count + putU2(0); + // attributes[] - empty; omit SourceFile attribute + // XXX should we mark this with a Synthetic attribute? + } + + /** + * Produce the bytecode for a single method. + * + * @param i the index of the method we are building + * @param e the exceptions possible for the method + */ + private void emitMethod(int i, Class[] e) + { + // First, we precalculate the method length and other information. + + Method m = methods[i]; + Class[] paramtypes = m.getParameterTypes(); + int wrap_overhead = 0; // max words taken by wrapped primitive + int param_count = 1; // 1 for this + int code_length = 16; // aload_0, getfield, aload_0, getstatic, const, + // aaload, const/aconst_null, invokeinterface + if (i > 5) + { + if (i > Byte.MAX_VALUE) + code_length += 2; // sipush + else + code_length++; // bipush + } + if (paramtypes.length > 0) + { + code_length += 3; // anewarray + if (paramtypes.length > Byte.MAX_VALUE) + code_length += 2; // sipush + else if (paramtypes.length > 5) + code_length++; // bipush + for (int j = 0; j < paramtypes.length; j++) + { + code_length += 4; // dup, const, load, store + Class type = paramtypes[j]; + if (j > 5) + { + if (j > Byte.MAX_VALUE) + code_length += 2; // sipush + else + code_length++; // bipush + } + if (param_count >= 4) + code_length++; // 2-byte load + param_count++; + if (type.isPrimitive()) + { + code_length += 7; // new, dup, invokespecial + if (type == long.class || type == double.class) + { + wrap_overhead = 3; + param_count++; + } + else if (wrap_overhead < 2) + wrap_overhead = 2; + } + } + } + int end_pc = code_length; + Class ret_type = m.getReturnType(); + if (ret_type == void.class) + code_length++; // return + else if (ret_type.isPrimitive()) + code_length += 7; // cast, invokevirtual, return + else + code_length += 4; // cast, return + int exception_count = 0; + boolean throws_throwable = false; + for (int j = 0; j < e.length; j++) + if (e[j] == Throwable.class) + { + throws_throwable = true; + break; + } + if (! throws_throwable) + { + exception_count = e.length + 3; // Throwable, Error, RuntimeException + code_length += 9; // new, dup_x1, swap, invokespecial, athrow + } + int handler_pc = code_length - 1; + StringBuffer signature = new StringBuffer("("); + for (int j = 0; j < paramtypes.length; j++) + signature.append(TypeSignature.getEncodingOfClass(paramtypes[j])); + signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type)); + + // Now we have enough information to emit the method. + + // handler.access_flags + putU2(Modifier.PUBLIC | Modifier.FINAL); + // handler.name_index + putU2(utf8Info(m.getName())); + // handler.descriptor_index + putU2(utf8Info(signature.toString())); + // handler.attributes_count - Code is necessary, Exceptions possible + putU2(e.length > 0 ? 2 : 1); + + // handler.Code.info: + // type name(args) { + // try { + // return (type) h.invoke(this, methods[i], new Object[] {args}); + // } catch ( e) { + // throw e; + // } catch (Throwable t) { + // throw new UndeclaredThrowableException(t); + // } + // } + // Special cases: + // if arg_n is primitive, wrap it + // if method throws Throwable, try-catch is not needed + // if method returns void, return statement not needed + // if method returns primitive, unwrap it + // save space by sharing code for all the declared handlers + + // handler.Code.attribute_name_index + putU2(utf8Info("Code")); + // handler.Code.attribute_length + putU4(12 + code_length + 8 * exception_count); + // handler.Code.max_stack + putU2(param_count == 1 ? 4 : 7 + wrap_overhead); + // handler.Code.max_locals + putU2(param_count); + // handler.Code.code_length + putU4(code_length); + // handler.Code.code[] + putU1(ALOAD_0); + putU1(GETFIELD); + putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h", + "Ljava/lang/reflect/InvocationHandler;")); + putU1(ALOAD_0); + putU1(GETSTATIC); + putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false), + "m", "[Ljava/lang/reflect/Method;")); + putConst(i); + putU1(AALOAD); + if (paramtypes.length > 0) + { + putConst(paramtypes.length); + putU1(ANEWARRAY); + putU2(classInfo("java/lang/Object")); + param_count = 1; + for (int j = 0; j < paramtypes.length; j++, param_count++) + { + putU1(DUP); + putConst(j); + if (paramtypes[j].isPrimitive()) + { + putU1(NEW); + putU2(classInfo(wrapper(paramtypes[j]))); + putU1(DUP); + } + putLoad(param_count, paramtypes[j]); + if (paramtypes[j].isPrimitive()) + { + putU1(INVOKESPECIAL); + putU2(refInfo(METHOD, wrapper(paramtypes[j]), "", + '(' + (TypeSignature + .getEncodingOfClass(paramtypes[j]) + + ")V"))); + if (paramtypes[j] == long.class + || paramtypes[j] == double.class) + param_count++; + } + putU1(AASTORE); + } + } + else + putU1(ACONST_NULL); + putU1(INVOKEINTERFACE); + putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler", + "invoke", INVOKE_SIG)); + putU1(4); // InvocationHandler, this, Method, Object[] + putU1(0); + if (ret_type == void.class) + putU1(RETURN); + else if (ret_type.isPrimitive()) + { + putU1(CHECKCAST); + putU2(classInfo(wrapper(ret_type))); + putU1(INVOKEVIRTUAL); + putU2(refInfo(METHOD, wrapper(ret_type), + ret_type.getName() + "Value", + "()" + TypeSignature.getEncodingOfClass(ret_type))); + if (ret_type == long.class) + putU1(LRETURN); + else if (ret_type == float.class) + putU1(FRETURN); + else if (ret_type == double.class) + putU1(DRETURN); + else + putU1(IRETURN); + } + else + { + putU1(CHECKCAST); + putU2(classInfo(ret_type)); + putU1(ARETURN); + } + if (! throws_throwable) + { + putU1(NEW); + putU2(classInfo("java/lang/reflect/UndeclaredThrowableException")); + putU1(DUP_X1); + putU1(SWAP); + putU1(INVOKESPECIAL); + putU2(refInfo(METHOD, + "java/lang/reflect/UndeclaredThrowableException", + "", "(Ljava/lang/Throwable;)V")); + putU1(ATHROW); + } + + // handler.Code.exception_table_length + putU2(exception_count); + // handler.Code.exception_table[] + if (! throws_throwable) + { + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo("java/lang/Error")); + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo("java/lang/RuntimeException")); + for (int j = 0; j < e.length; j++) + { + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc + putU2(handler_pc); + // handler.Code.exception_table.catch_type + putU2(classInfo(e[j])); + } + // handler.Code.exception_table.start_pc + putU2(0); + // handler.Code.exception_table.end_pc + putU2(end_pc); + // handler.Code.exception_table.handler_pc - + // -8 for undeclared handler, which falls thru to normal one + putU2(handler_pc - 8); + // handler.Code.exception_table.catch_type + putU2(0); + } + // handler.Code.attributes_count + putU2(0); + // handler.Code.attributes[] + + if (e.length > 0) + { + // handler.Exceptions.attribute_name_index + putU2(utf8Info("Exceptions")); + // handler.Exceptions.attribute_length + putU4(2 * e.length + 2); + // handler.Exceptions.number_of_exceptions + putU2(e.length); + // handler.Exceptions.exception_index_table[] + for (int j = 0; j < e.length; j++) + putU2(classInfo(e[j])); + } + } + + /** + * Creates the Class object that corresponds to the bytecode buffers + * built when this object was constructed. + * + * @param loader the class loader to define the proxy class in; null + * implies the bootstrap class loader + * @return the proxy class Class object + */ + final Class generate(ClassLoader loader) + { + byte[] bytecode = new byte[pool.length() + stream.length()]; + // More efficient to bypass calling charAt() repetitively. + char[] c = pool.toString().toCharArray(); + int i = c.length; + while (--i >= 0) + bytecode[i] = (byte) c[i]; + c = stream.toString().toCharArray(); + i = c.length; + int j = bytecode.length; + while (i > 0) + bytecode[--j] = (byte) c[--i]; + + // Patch the constant pool size, which we left at 0 earlier. + int count = poolEntries.size() + 1; + bytecode[8] = (byte) (count >> 8); + bytecode[9] = (byte) count; + + try + { + // XXX Do we require more native support here? + + // XXX Security hole - it is possible for another thread to grab the + // VMClassLoader.defineClass Method object, and abuse it while we + // have temporarily made it accessible. Do we need to add some + // synchronization lock to prevent user reflection while we use it? + + // XXX This is waiting on VM support for protection domains. + + Class vmClassLoader = Class.forName("java.lang.ClassLoader"); + Class[] types = {String.class, + byte[].class, int.class, int.class, + /* ProtectionDomain.class */ }; + Method m = vmClassLoader.getDeclaredMethod("defineClass", types); + + // Bypass the security check of setAccessible(true), since this + // is trusted code. But note the comment above about the security + // risk of doing this outside a synchronized block. + m.setAccessible(true); + Object[] args = {qualName, bytecode, new Integer(0), + new Integer(bytecode.length), + /* Object.class.getProtectionDomain() */ }; + Class clazz = (Class) m.invoke(loader, args); + m.setAccessible(false); + + // Finally, initialize the m field of the proxy class, before + // returning it. + + // No security risk here, since clazz has not been exposed yet, + // so user code cannot grab the same reflection object. + Field f = clazz.getDeclaredField("m"); + f.setAccessible(true); + // we can share the array, because it is not publicized + f.set(null, methods); + f.setAccessible(false); + + return clazz; + } + catch (Throwable e) + { + // assert false; + throw (Error) new InternalError("Unexpected: " + e).initCause(e); + } + } + + /** + * Put a single byte on the stream. + * + * @param i the information to add (only lowest 8 bits are used) + */ + private void putU1(int i) + { + stream.append((char) i); + } + + /** + * Put two bytes on the stream. + * + * @param i the information to add (only lowest 16 bits are used) + */ + private void putU2(int i) + { + stream.append((char) (i >> 8)).append((char) i); + } + + /** + * Put four bytes on the stream. + * + * @param i the information to add (treated as unsigned) + */ + private void putU4(int i) + { + stream.append((char) (i >> 24)).append((char) (i >> 16)); + stream.append((char) (i >> 8)).append((char) i); + } + + /** + * Put bytecode to load a constant integer on the stream. This only + * needs to work for values less than Short.MAX_VALUE. + * + * @param i the int to add + */ + private void putConst(int i) + { + if (i >= -1 && i <= 5) + putU1(ICONST_0 + i); + else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) + { + putU1(BIPUSH); + putU1(i); + } + else + { + putU1(SIPUSH); + putU2(i); + } + } + + /** + * Put bytecode to load a given local variable on the stream. + * + * @param i the slot to load + * @param type the base type of the load + */ + private void putLoad(int i, Class type) + { + int offset = 0; + if (type == long.class) + offset = 1; + else if (type == float.class) + offset = 2; + else if (type == double.class) + offset = 3; + else if (! type.isPrimitive()) + offset = 4; + if (i < 4) + putU1(ILOAD_0 + 4 * offset + i); + else + { + putU1(ILOAD + offset); + putU1(i); + } + } + + /** + * Given a primitive type, return its wrapper class name. + * + * @param clazz the primitive type (but not void.class) + * @return the internal form of the wrapper class name + */ + private String wrapper(Class clazz) + { + if (clazz == boolean.class) + return "java/lang/Boolean"; + if (clazz == byte.class) + return "java/lang/Byte"; + if (clazz == short.class) + return "java/lang/Short"; + if (clazz == char.class) + return "java/lang/Character"; + if (clazz == int.class) + return "java/lang/Integer"; + if (clazz == long.class) + return "java/lang/Long"; + if (clazz == float.class) + return "java/lang/Float"; + if (clazz == double.class) + return "java/lang/Double"; + // assert false; + return null; + } + + /** + * Returns the entry of this String in the Constant pool, adding it + * if necessary. + * + * @param str the String to resolve + * @return the index of the String in the constant pool + */ + private char utf8Info(String str) + { + String utf8 = toUtf8(str); + int len = utf8.length(); + return poolIndex("\1" + (char) (len >> 8) + (char) (len & 0xff) + utf8); + } + + /** + * Returns the entry of the appropriate class info structure in the + * Constant pool, adding it if necessary. + * + * @param name the class name, in internal form + * @return the index of the ClassInfo in the constant pool + */ + private char classInfo(String name) + { + char index = utf8Info(name); + char[] c = {7, (char) (index >> 8), (char) (index & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Returns the entry of the appropriate class info structure in the + * Constant pool, adding it if necessary. + * + * @param clazz the class type + * @return the index of the ClassInfo in the constant pool + */ + private char classInfo(Class clazz) + { + return classInfo(TypeSignature.getEncodingOfClass(clazz.getName(), + false)); + } + + /** + * Returns the entry of the appropriate fieldref, methodref, or + * interfacemethodref info structure in the Constant pool, adding it + * if necessary. + * + * @param structure FIELD, METHOD, or INTERFACE + * @param clazz the class name, in internal form + * @param name the simple reference name + * @param type the type of the reference + * @return the index of the appropriate Info structure in the constant pool + */ + private char refInfo(byte structure, String clazz, String name, + String type) + { + char cindex = classInfo(clazz); + char ntindex = nameAndTypeInfo(name, type); + // relies on FIELD == 1, METHOD == 2, INTERFACE == 3 + char[] c = {(char) (structure + 8), + (char) (cindex >> 8), (char) (cindex & 0xff), + (char) (ntindex >> 8), (char) (ntindex & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Returns the entry of the appropriate nameAndTyperef info structure + * in the Constant pool, adding it if necessary. + * + * @param name the simple name + * @param type the reference type + * @return the index of the NameAndTypeInfo structure in the constant pool + */ + private char nameAndTypeInfo(String name, String type) + { + char nindex = utf8Info(name); + char tindex = utf8Info(type); + char[] c = {12, (char) (nindex >> 8), (char) (nindex & 0xff), + (char) (tindex >> 8), (char) (tindex & 0xff)}; + return poolIndex(new String(c)); + } + + /** + * Converts a regular string to a UTF8 string, where the upper byte + * of every char is 0, and '\\u0000' is not in the string. This is + * basically to use a String as a fancy byte[], and while it is less + * efficient in memory use, it is easier for hashing. + * + * @param str the original, in straight unicode + * @return a modified string, in UTF8 format in the low bytes + */ + private String toUtf8(String str) + { + final char[] ca = str.toCharArray(); + final int len = ca.length; + + // Avoid object creation, if str is already fits UTF8. + int i; + for (i = 0; i < len; i++) + if (ca[i] == 0 || ca[i] > '\u007f') + break; + if (i == len) + return str; + + final StringBuffer sb = new StringBuffer(str); + sb.setLength(i); + for ( ; i < len; i++) + { + final char c = ca[i]; + if (c > 0 && c <= '\u007f') + sb.append(c); + else if (c <= '\u07ff') // includes '\0' + { + sb.append((char) (0xc0 | (c >> 6))); + sb.append((char) (0x80 | (c & 0x6f))); + } + else + { + sb.append((char) (0xe0 | (c >> 12))); + sb.append((char) (0x80 | ((c >> 6) & 0x6f))); + sb.append((char) (0x80 | (c & 0x6f))); + } + } + return sb.toString(); } - public static InvocationHandler getInvocationHandler(Object proxy) - throws IllegalArgumentException + /** + * Returns the location of a byte sequence (conveniently wrapped in + * a String with all characters between \u0001 and \u00ff inclusive) + * in the constant pool, adding it if necessary. + * + * @param sequence the byte sequence to look for + * @return the index of the sequence + * @throws IllegalArgumentException if this would make the constant + * pool overflow + */ + private char poolIndex(String sequence) { - throw new kaffe.util.NotImplemented("java.lang.reflect.Proxy"); + Integer i = (Integer) poolEntries.get(sequence); + if (i == null) + { + // pool starts at index 1 + int size = poolEntries.size() + 1; + if (size >= 65535) + throw new IllegalArgumentException("exceeds VM limitations"); + i = new Integer(size); + poolEntries.put(sequence, i); + pool.append(sequence); + } + return (char) i.intValue(); } + } // class ClassFactory } diff -urN -x CVS /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/java/net/URLClassLoader.java kaffe/libraries/javalib/java/net/URLClassLoader.java --- /HG/hiwis/topic/PROJECTS/kaffe/libraries/javalib/java/net/URLClassLoader.java Tue May 20 15:24:56 2003 +++ kaffe/libraries/javalib/java/net/URLClassLoader.java Tue May 20 16:44:01 2003 @@ -54,11 +54,15 @@ this.urls = new Vector(); this.factory = factory; for (int i = 0; i < urls.length; i++) { - addURL(urls[i]); + internalAddURL(urls[i]); } } protected void addURL(URL url) { + internalAddURL(url); + } + + private void internalAddURL(URL url) { urls.addElement(url); }