diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 2cf68de05eed..4b0cc0fcd244 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,26 @@ +2002-09-29 Anthony Green + + * java/lang/reflect/UndeclaredThrowableException.java: New file. + Imported from GNU Classpath. + * java/lang/reflect/natProxy.cc: New file. + * java/lang/reflect/InvocationHandler.java: New file. Imported + from GNU Classpath. + * java/lang/reflect/Proxy.java: New file. Imported from GNU + Classpath. + * gnu/java/lang/reflect/TypeSignature.java: Refresh from GNU + Classpath. + * gnu/classpath/Configuration.java.in (HAVE_NATIVE_GET_PROXY_DATA, + HAVE_NATIVE_GET_PROXY_CLASS, HAVE_NATIVE_GENERATE_PROXY_CLASS): + New statics. + * gcj/javaprims.h ("Java"): Add new classes. + * java/lang/reflect/Proxy.java: Fix check for duplicate interfaces. + * Makefile.am (java/lang/reflect/Proxy$$ProxyData.h): Create this. + java/lang/reflect/Proxy$$ProxyType.h): And this. + (inner_nat_headers): Add these new headers. + (ordinary_java_source_files): Add new files. + (nat_source_files): Add new file. + * Makefile.in: Rebuilt. + 2002-09-28 Richard Earnshaw * configure.host: Handle arm*-elf, strongarm*-elf and xscale*-elf with diff --git a/libjava/Makefile.am b/libjava/Makefile.am index 7e55a78f7316..5d2a18209a06 100644 --- a/libjava/Makefile.am +++ b/libjava/Makefile.am @@ -249,7 +249,9 @@ ordinary_nat_headers = $(ordinary_java_source_files:.java=.h) \ $(built_java_source_files:.java=.h) inner_nat_headers = java/io/ObjectOutputStream$$PutField.h \ - java/io/ObjectInputStream$$GetField.h + java/io/ObjectInputStream$$GetField.h \ + java/lang/reflect/Proxy$$ProxyData.h \ + java/lang/reflect/Proxy$$ProxyType.h nat_headers = $(ordinary_nat_headers) $(inner_nat_headers) @@ -309,6 +311,18 @@ java/lang/reflect/Method.h: java/lang/reflect/Method.class -friend 'jobject _Jv_JNI_ToReflectedMethod (_Jv_JNIEnv *, jclass, jmethodID, jboolean);' \ $(basename $<) +java/lang/reflect/Proxy.h: java/lang/reflect/Proxy.class + $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ + $(basename $<) + +java/lang/reflect/Proxy$$ProxyData.h: java/lang/reflect/Proxy.class + $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ + 'java/lang/reflect/Proxy$$ProxyData' + +java/lang/reflect/Proxy$$ProxyType.h: java/lang/reflect/Proxy.class + $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ + 'java/lang/reflect/Proxy$$ProxyType' + gnu/gcj/runtime/VMClassLoader.h: gnu/gcj/runtime/VMClassLoader.class $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ -friend 'class ::java::lang::ClassLoader;' \ @@ -1873,11 +1887,14 @@ java/lang/reflect/AccessibleObject.java \ java/lang/reflect/Array.java \ java/lang/reflect/Constructor.java \ java/lang/reflect/Field.java \ +java/lang/reflect/InvocationHandler.java \ java/lang/reflect/InvocationTargetException.java \ java/lang/reflect/Member.java \ java/lang/reflect/Method.java \ java/lang/reflect/Modifier.java \ +java/lang/reflect/Proxy.java \ java/lang/reflect/ReflectPermission.java \ +java/lang/reflect/UndeclaredThrowableException.java \ java/math/BigDecimal.java \ java/math/BigInteger.java \ java/net/Authenticator.java \ @@ -2242,6 +2259,7 @@ java/lang/reflect/natArray.cc \ java/lang/reflect/natConstructor.cc \ java/lang/reflect/natField.cc \ java/lang/reflect/natMethod.cc \ +java/lang/reflect/natProxy.cc \ java/net/natNetworkInterface.cc \ java/net/natInetAddress.cc \ java/net/natPlainDatagramSocketImpl.cc \ diff --git a/libjava/Makefile.in b/libjava/Makefile.in index 239aa3715486..a0f6851409b4 100644 --- a/libjava/Makefile.in +++ b/libjava/Makefile.in @@ -1,6 +1,6 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -246,7 +246,9 @@ ordinary_nat_headers = $(ordinary_java_source_files:.java=.h) \ inner_nat_headers = java/io/ObjectOutputStream$$PutField.h \ - java/io/ObjectInputStream$$GetField.h + java/io/ObjectInputStream$$GetField.h \ + java/lang/reflect/Proxy$$ProxyData.h \ + java/lang/reflect/Proxy$$ProxyType.h nat_headers = $(ordinary_nat_headers) $(inner_nat_headers) @@ -1635,11 +1637,14 @@ java/lang/reflect/AccessibleObject.java \ java/lang/reflect/Array.java \ java/lang/reflect/Constructor.java \ java/lang/reflect/Field.java \ +java/lang/reflect/InvocationHandler.java \ java/lang/reflect/InvocationTargetException.java \ java/lang/reflect/Member.java \ java/lang/reflect/Method.java \ java/lang/reflect/Modifier.java \ +java/lang/reflect/Proxy.java \ java/lang/reflect/ReflectPermission.java \ +java/lang/reflect/UndeclaredThrowableException.java \ java/math/BigDecimal.java \ java/math/BigInteger.java \ java/net/Authenticator.java \ @@ -2003,6 +2008,7 @@ java/lang/reflect/natArray.cc \ java/lang/reflect/natConstructor.cc \ java/lang/reflect/natField.cc \ java/lang/reflect/natMethod.cc \ +java/lang/reflect/natProxy.cc \ java/net/natNetworkInterface.cc \ java/net/natInetAddress.cc \ java/net/natPlainDatagramSocketImpl.cc \ @@ -2157,11 +2163,11 @@ java/lang/natStringBuffer.lo java/lang/natSystem.lo \ java/lang/natThread.lo java/lang/natVMThrowable.lo \ java/lang/ref/natReference.lo java/lang/reflect/natArray.lo \ java/lang/reflect/natConstructor.lo java/lang/reflect/natField.lo \ -java/lang/reflect/natMethod.lo java/net/natNetworkInterface.lo \ -java/net/natInetAddress.lo java/net/natPlainDatagramSocketImpl.lo \ -java/net/natPlainSocketImpl.lo java/text/natCollator.lo \ -java/util/natTimeZone.lo java/util/zip/natDeflater.lo \ -java/util/zip/natInflater.lo +java/lang/reflect/natMethod.lo java/lang/reflect/natProxy.lo \ +java/net/natNetworkInterface.lo java/net/natInetAddress.lo \ +java/net/natPlainDatagramSocketImpl.lo java/net/natPlainSocketImpl.lo \ +java/text/natCollator.lo java/util/natTimeZone.lo \ +java/util/zip/natDeflater.lo java/util/zip/natInflater.lo libgcjx_la_OBJECTS = gnu/gcj/xlib/natClip.lo \ gnu/gcj/xlib/natColormap.lo gnu/gcj/xlib/natDisplay.lo \ gnu/gcj/xlib/natDrawable.lo gnu/gcj/xlib/natFont.lo \ @@ -2854,29 +2860,32 @@ DEP_FILES = .deps/$(srcdir)/$(CONVERT_DIR)/gen-from-JIS.P \ .deps/java/lang/reflect/AccessibleObject.P \ .deps/java/lang/reflect/Array.P .deps/java/lang/reflect/Constructor.P \ .deps/java/lang/reflect/Field.P \ +.deps/java/lang/reflect/InvocationHandler.P \ .deps/java/lang/reflect/InvocationTargetException.P \ .deps/java/lang/reflect/Member.P .deps/java/lang/reflect/Method.P \ -.deps/java/lang/reflect/Modifier.P \ +.deps/java/lang/reflect/Modifier.P .deps/java/lang/reflect/Proxy.P \ .deps/java/lang/reflect/ReflectPermission.P \ +.deps/java/lang/reflect/UndeclaredThrowableException.P \ .deps/java/lang/reflect/natArray.P \ .deps/java/lang/reflect/natConstructor.P \ .deps/java/lang/reflect/natField.P .deps/java/lang/reflect/natMethod.P \ -.deps/java/lang/s_atan.P .deps/java/lang/s_ceil.P \ -.deps/java/lang/s_copysign.P .deps/java/lang/s_cos.P \ -.deps/java/lang/s_fabs.P .deps/java/lang/s_floor.P \ -.deps/java/lang/s_rint.P .deps/java/lang/s_scalbn.P \ -.deps/java/lang/s_sin.P .deps/java/lang/s_tan.P \ -.deps/java/lang/sf_fabs.P .deps/java/lang/sf_rint.P \ -.deps/java/lang/strtod.P .deps/java/lang/w_acos.P \ -.deps/java/lang/w_asin.P .deps/java/lang/w_atan2.P \ -.deps/java/lang/w_exp.P .deps/java/lang/w_fmod.P \ -.deps/java/lang/w_log.P .deps/java/lang/w_pow.P \ -.deps/java/lang/w_remainder.P .deps/java/lang/w_sqrt.P \ -.deps/java/math/BigDecimal.P .deps/java/math/BigInteger.P \ -.deps/java/net/Authenticator.P .deps/java/net/BindException.P \ -.deps/java/net/ConnectException.P .deps/java/net/ContentHandler.P \ -.deps/java/net/ContentHandlerFactory.P .deps/java/net/DatagramPacket.P \ -.deps/java/net/DatagramSocket.P .deps/java/net/DatagramSocketImpl.P \ +.deps/java/lang/reflect/natProxy.P .deps/java/lang/s_atan.P \ +.deps/java/lang/s_ceil.P .deps/java/lang/s_copysign.P \ +.deps/java/lang/s_cos.P .deps/java/lang/s_fabs.P \ +.deps/java/lang/s_floor.P .deps/java/lang/s_rint.P \ +.deps/java/lang/s_scalbn.P .deps/java/lang/s_sin.P \ +.deps/java/lang/s_tan.P .deps/java/lang/sf_fabs.P \ +.deps/java/lang/sf_rint.P .deps/java/lang/strtod.P \ +.deps/java/lang/w_acos.P .deps/java/lang/w_asin.P \ +.deps/java/lang/w_atan2.P .deps/java/lang/w_exp.P \ +.deps/java/lang/w_fmod.P .deps/java/lang/w_log.P \ +.deps/java/lang/w_pow.P .deps/java/lang/w_remainder.P \ +.deps/java/lang/w_sqrt.P .deps/java/math/BigDecimal.P \ +.deps/java/math/BigInteger.P .deps/java/net/Authenticator.P \ +.deps/java/net/BindException.P .deps/java/net/ConnectException.P \ +.deps/java/net/ContentHandler.P .deps/java/net/ContentHandlerFactory.P \ +.deps/java/net/DatagramPacket.P .deps/java/net/DatagramSocket.P \ +.deps/java/net/DatagramSocketImpl.P \ .deps/java/net/DatagramSocketImplFactory.P .deps/java/net/FileNameMap.P \ .deps/java/net/HttpURLConnection.P .deps/java/net/InetAddress.P \ .deps/java/net/InetSocketAddress.P .deps/java/net/JarURLConnection.P \ @@ -3776,7 +3785,7 @@ maintainer-clean-recursive: dot_seen=no; \ rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ rev="$$subdir $$rev"; \ - test "$$subdir" = "." && dot_seen=yes; \ + test "$$subdir" != "." || dot_seen=yes; \ done; \ test "$$dot_seen" = "no" && rev=". $$rev"; \ target=`echo $@ | sed s/-recursive//`; \ @@ -4162,6 +4171,18 @@ java/lang/reflect/Method.h: java/lang/reflect/Method.class -friend 'jobject _Jv_JNI_ToReflectedMethod (_Jv_JNIEnv *, jclass, jmethodID, jboolean);' \ $(basename $<) +java/lang/reflect/Proxy.h: java/lang/reflect/Proxy.class + $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ + $(basename $<) + +java/lang/reflect/Proxy$$ProxyData.h: java/lang/reflect/Proxy.class + $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ + 'java/lang/reflect/Proxy$$ProxyData' + +java/lang/reflect/Proxy$$ProxyType.h: java/lang/reflect/Proxy.class + $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ + 'java/lang/reflect/Proxy$$ProxyType' + gnu/gcj/runtime/VMClassLoader.h: gnu/gcj/runtime/VMClassLoader.class $(GCJH) -classpath '' -bootclasspath $(top_builddir) \ -friend 'class ::java::lang::ClassLoader;' \ diff --git a/libjava/gcj/Makefile.in b/libjava/gcj/Makefile.in index 201722f80ca2..9ec51a6906df 100644 --- a/libjava/gcj/Makefile.in +++ b/libjava/gcj/Makefile.in @@ -1,6 +1,6 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. diff --git a/libjava/gcj/javaprims.h b/libjava/gcj/javaprims.h index 3378a9faf668..8ed649432a21 100644 --- a/libjava/gcj/javaprims.h +++ b/libjava/gcj/javaprims.h @@ -230,11 +230,16 @@ extern "Java" class Array; class Constructor; class Field; + class InvocationHandler; class InvocationTargetException; class Member; class Method; class Modifier; + class Proxy; + class Proxy$ProxyData; + class Proxy$ProxyType; class ReflectPermission; + class UndeclaredThrowableException; }; }; diff --git a/libjava/gnu/classpath/Configuration.java.in b/libjava/gnu/classpath/Configuration.java.in index 4ea692357d15..1002b62bd8bf 100644 --- a/libjava/gnu/classpath/Configuration.java.in +++ b/libjava/gnu/classpath/Configuration.java.in @@ -51,4 +51,9 @@ public interface Configuration // For libgcj we never load the JNI libraries. boolean INIT_LOAD_LIBRARY = false; + + // For libgcj we have native methods for proxy support.... + boolean HAVE_NATIVE_GET_PROXY_DATA = false; + boolean HAVE_NATIVE_GET_PROXY_CLASS = false; + boolean HAVE_NATIVE_GENERATE_PROXY_CLASS = false; } diff --git a/libjava/gnu/java/lang/reflect/TypeSignature.java b/libjava/gnu/java/lang/reflect/TypeSignature.java index 0344ca3a4af9..e9c8861c611f 100644 --- a/libjava/gnu/java/lang/reflect/TypeSignature.java +++ b/libjava/gnu/java/lang/reflect/TypeSignature.java @@ -1,5 +1,5 @@ /* TypeSignature.java -- Class used to compute type signatures - Copyright (C) 1998 Free Software Foundation, Inc. + Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -7,7 +7,7 @@ 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 @@ -44,230 +44,218 @@ 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. -*/ + * 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 - CLAZZ. 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  -> "[" + type-encoding of component type
-     object  -> "L"
-                 + fully qualified class name with "."'s replaced by "/"'s
-                 + ";"
- */ - public static String getEncodingOfClass( Class clazz ) + * 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( clazz.isPrimitive() ) - { - if( clazz == Boolean.TYPE ) - return "Z"; - if( clazz == Byte.TYPE ) - return "B"; - if( clazz == Character.TYPE ) - return "C"; - if( clazz == Double.TYPE ) - return "D"; - if( clazz == Float.TYPE ) - return "F"; - if( clazz == Integer.TYPE ) - return "I"; - if( clazz == Long.TYPE ) - return "J"; - if( clazz == Short.TYPE ) - return "S"; - if( clazz == Void.TYPE ) - return "V"; - else - throw new RuntimeException( "Unknown primitive class " + clazz ); - } - else if( clazz.isArray() ) - { - return '[' + getEncodingOfClass( clazz.getComponentType() ); - } - else - { - String classname = clazz.getName(); - int name_len = classname.length(); - char[] buf = new char[ name_len + 2 ]; - buf[0] = 'L'; - classname.getChars( 0, name_len, buf, 1 ); - - int i; - for( i=1; i <= name_len; i++ ) - { - if( buf[i] == '.' ) - buf[i] = '/'; - } - - buf[i] = ';'; - return new String( buf ); - } + 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('.', '/') + ';'; } - /** - This function is the inverse of getEncodingOfClass. + * 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); + } - @see getEncodingOfClass + /** + * 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); + } - @exception ClassNotFoundException If class encoded as type_code - cannot be located. - */ - public static Class getClassForEncoding( String type_code ) + + /** + * 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( type_code.equals( "B" ) ) - return Byte.TYPE; - if( type_code.equals( "C" ) ) - return Character.TYPE; - if( type_code.equals( "D" ) ) - return Double.TYPE; - if( type_code.equals( "F" ) ) - return Float.TYPE; - if( type_code.equals( "I" ) ) - return Integer.TYPE; - if( type_code.equals( "J" ) ) - return Long.TYPE; - if( type_code.equals( "S" ) ) - return Short.TYPE; - if( type_code.equals( "Z" ) ) - return Boolean.TYPE; - if( type_code.charAt( 0 ) == 'L' ) - { - return Class.forName( - type_code.substring( 1, type_code.length() - 1 ).replace( '/', '.' )); - } - if( type_code.charAt( 0 ) == '[' ) - { - int last_bracket = type_code.lastIndexOf( '[' ); - String brackets = type_code.substring( 0, last_bracket + 1 ); - String component = type_code.substring( last_bracket + 1 ); - -// ??? This is what the Classpath implementation did, but I don't -// think that it's correct. The JLS says that Class.forName takes the -// classname of an array element in fully qualified form, whereas this -// code is tring to strip off the punctuation. - -// if( component.charAt( 0 ) == 'L' ) -// component = -// component.substring( 1, component.length() - 1 ).replace('/', '.'); - - if( component.charAt( 0 ) == 'L' ) - component = component.replace('/', '.'); - - return Class.forName( brackets + component ); - } - else - throw new ClassNotFoundException( "Type code cannot be parsed as a valid class name" ); + 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('/', '.')); } - /** - Returns a String representing the type-encoding of - M. The type-encoding of a method is: - - "(" + type-encodings of parameter types + ")" - + type-encoding of return type - */ - public static String getEncodingOfMethod( Method m ) + * 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) { - String returnEncoding = getEncodingOfClass( m.getReturnType() ); Class[] paramTypes = m.getParameterTypes(); - String[] paramEncodings = new String[ paramTypes.length ]; - - String paramEncoding; - int size = 2; // make room for parens - for( int i=0; i < paramTypes.length; i++ ) - { - paramEncoding = getEncodingOfClass( paramTypes[i] ); - size += paramEncoding.length(); - paramEncodings[i] = paramEncoding; - } - - size += returnEncoding.length(); - - StringBuffer buf = new StringBuffer( size ); - buf.append( '(' ); - - for( int i=0; i < paramTypes.length; i++ ) - { - buf.append( paramEncodings[i] ); - } - - buf.append( ')' ); - buf.append( returnEncoding ); - + 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 - C. The type-encoding of a method is: - - "(" + type-encodings of parameter types + ")V" - */ - public static String getEncodingOfConstructor( Constructor c ) + * 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(); - String[] paramEncodings = new String[ paramTypes.length ]; - - String paramEncoding; - int size = 3; // make room for parens and V for return type - for( int i=0; i < paramTypes.length; i++ ) - { - paramEncoding = getEncodingOfClass( paramTypes[i] ); - size += paramEncoding.length(); - paramEncodings[i] = paramEncoding; - } - - StringBuffer buf = new StringBuffer( size ); - buf.append( '(' ); - - for( int i=0; i < paramTypes.length; i++ ) - { - buf.append( paramEncodings[i] ); - } - - buf.append( ")V" ); - + 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 - MEM. Constructors are handled by - getEncodingOfConstructor. Methods are - handled by getEncodingOfMethod. Fields - are handled by returning the encoding of the type of the - Field. - */ - public static String getEncodingOfMember( Member mem ) + * 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 ); + if (mem instanceof Constructor) + return getEncodingOfConstructor((Constructor) mem); + if (mem instanceof Method) + return getEncodingOfMethod((Method) mem); else // Field - return getEncodingOfClass( ((Field)mem).getType() ); + return getEncodingOfClass(((Field) mem).getType().getName(), true); } -} +} // class TypeSignature diff --git a/libjava/include/Makefile.in b/libjava/include/Makefile.in index 1deb2b46dc3c..ebcd4250ab00 100644 --- a/libjava/include/Makefile.in +++ b/libjava/include/Makefile.in @@ -1,6 +1,6 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. diff --git a/libjava/java/lang/VMClassLoader.java b/libjava/java/lang/VMClassLoader.java index 445272b1bbac..11b75548e53c 100644 --- a/libjava/java/lang/VMClassLoader.java +++ b/libjava/java/lang/VMClassLoader.java @@ -43,10 +43,10 @@ class VMClassLoader { * @return the class that was defined. * @exception ClassFormatError if the byte array is not in proper classfile format. */ - // Not yet needed for libgcj. -// final static native Class defineClass(ClassLoader cl, String name, -// byte[] data, int offset, int len) throws ClassFormatError; - + final static native Class defineClass(ClassLoader cl, String name, + byte[] data, int offset, int len) + throws ClassFormatError; + /** * Helper to resolve all references to other classes from this class. * @param c the class to resolve. diff --git a/libjava/java/lang/natClassLoader.cc b/libjava/java/lang/natClassLoader.cc index d33614dbf032..c229943ecc5f 100644 --- a/libjava/java/lang/natClassLoader.cc +++ b/libjava/java/lang/natClassLoader.cc @@ -170,6 +170,16 @@ java::lang::ClassLoader::markClassErrorState0 (java::lang::Class *klass) klass->notifyAll (); } +jclass +java::lang::VMClassLoader::defineClass (java::lang::ClassLoader *cl, + jstring name, + jbyteArray data, + jint offset, + jint length) +{ + return cl->defineClass (name, data, offset, length); +} + jclass java::lang::VMClassLoader::getPrimitiveClass (jchar type) { @@ -269,6 +279,7 @@ _Jv_PrepareCompiledClass (jclass klass) else if (pool->tags[index] == JV_CONSTANT_String) { jstring str; + str = _Jv_NewStringUtf8Const (pool->data[index].utf8); pool->data[index].o = str; pool->tags[index] |= JV_CONSTANT_ResolvedFlag; diff --git a/libjava/java/lang/reflect/InvocationHandler.java b/libjava/java/lang/reflect/InvocationHandler.java new file mode 100644 index 000000000000..91907e280c7f --- /dev/null +++ b/libjava/java/lang/reflect/InvocationHandler.java @@ -0,0 +1,136 @@ +/* java.lang.reflect.InvocationHandler - dynamically executes methods in + proxy instances + Copyright (C) 2001 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; + +/** + * This interface defines an invocation handler. Suppose you are using + * reflection, and found a method that requires that its parameter + * be an object of a given interface. You want to call this method, + * but have no idea what classes implement that interface. So, you can + * create a {@link Proxy} instance, a convenient way to dynamically + * generate a class that meets all the necessary properties of that + * interface. But in order for the proxy instance to do any good, it + * needs to know what to do when interface methods are invoked! So, + * this interface is basically a cool wrapper that provides runtime + * code generation needed by proxy instances.

+ * + * While this interface was designed for use by Proxy, it will also + * work on any object in general.

+ * + * Hints for implementing this class:
+ *

    + *
  • Don't forget that Object.equals, Object.hashCode, and + * Object.toString will call this handler. In particular, + * a naive call to proxy.equals, proxy.hashCode, or proxy.toString + * will put you in an infinite loop. And remember that string + * concatenation also invokes toString.
  • + *
  • Obey the contract of the Method object you are handling, or + * the proxy instance will be forced to throw a + * {@link NullPointerException}, {@link ClassCastException}, + * or {@link UndeclaredThrowableException}.
  • + *
  • Be prepared to wrap/unwrap primitives as necessary.
  • + *
  • The Method object may be owned by a different interface than + * what was actually used as the qualifying type of the method + * invocation in the Java source code. This means that it might + * not always be safe to throw an exception listed as belonging + * to the method's throws clause.
  • + *
+ * + *

For a fun time, create an InvocationHandler that handles the + * methods of a proxy instance of the InvocationHandler interface! + * + * @see Proxy + * @see UndeclaredThrowableException + * + * @author Eric Blake + * @since 1.3 + * @status updated to 1.4 + */ +public interface InvocationHandler +{ + /** + * When a method is invoked on a proxy instance, it is wrapped and + * this method is called instead, so that you may decide at runtime + * how the original method should behave. + * + * @param proxy the instance that the wrapped method should be + * invoked on. When this method is called by a Proxy object, + * `proxy' will be an instance of {@link Proxy}, and oddly enough, + * Proxy.getInvocationHandler(proxy) will return + * this! + * @param method the reflected method to invoke on the proxy. + * When this method is called by a Proxy object, 'method' + * will be the reflection object owned by the declaring + * class or interface, which may be a supertype of the + * interfaces the proxy directly implements. + * @param args the arguments passed to the original method, or + * null if the method takes no arguments. + * (But also be prepared to handle a 0-length array). + * Arguments of primitive type, such as boolean + * or int, are wrapped in the appropriate + * class such as {@link Boolean} or {@link Integer}. + * @return whatever is necessary to return from the wrapped method. + * If the wrapped method is void, the proxy + * instance will ignore it. If the wrapped method returns + * a primitive, this must be the correct wrapper type whose value + * is exactly assignable to the appropriate type (no widening + * will be performed); a null object in this case causes a + * {@link NullPointerException}. In all remaining cases, if + * the returned object is not assignment compatible to the + * declared type of the original method, the proxy instance + * will generate a {@link ClassCastException}. + * @throws Throwable this interface is listed as throwing anything, + * but the implementation should only throw unchecked + * exceptions and exceptions listed in the throws clause of + * all methods being overridden by the proxy instance. If + * something is thrown that is not compatible with the throws + * clause of all overridden methods, the proxy instance will + * wrap the exception in an UndeclaredThrowableException. + * Note that an exception listed in the throws clause of the + * `method' parameter might not be declared in additional + * interfaces also implemented by the proxy object. + * + * @see Proxy + * @see UndeclaredThrowableException + */ + Object invoke(Object proxy, Method method, Object[] args) + throws Throwable; + +} diff --git a/libjava/java/lang/reflect/Proxy.java b/libjava/java/lang/reflect/Proxy.java new file mode 100644 index 000000000000..972ac19c37ac --- /dev/null +++ b/libjava/java/lang/reflect/Proxy.java @@ -0,0 +1,1586 @@ +/* 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);
+ * 
+ * + *

Dynamic Proxy Classes

+ * A dynamic proxy class is created at runtime, and has the following + * properties: + *
    + *
  • The class is public and final, + * and is neither abstract nor an inner class.
  • + *
  • The class has no canonical name (there is no formula you can use + * to determine or generate its name), but begins with the + * sequence "$Proxy". Abuse this knowledge at your own peril. + * (For now, '$' in user identifiers is legal, but it may not + * be that way forever. You weren't using '$' in your + * identifiers, were you?)
  • + *
  • The class extends Proxy, and explicitly implements all the + * interfaces specified at creation, in order (this is important + * for determining how method invocation is resolved). Note that + * a proxy class implements {@link Serializable}, at least + * implicitly, since Proxy does, but true serial behavior + * depends on using a serializable invocation handler as well.
  • + *
  • If at least one interface is non-public, the proxy class + * will be in the same package. Otherwise, the package is + * unspecified. This will work even if the package is sealed + * from user-generated classes, because Proxy classes are + * generated by a trusted source. Meanwhile, the proxy class + * belongs to the classloader you designated.
  • + *
  • Reflection works as expected: {@link Class#getInterfaces()} and + * {@link Class#getMethods()} work as they do on normal classes.
  • + *
  • The method {@link #isProxyClass()} will distinguish between + * true proxy classes and user extensions of this class. It only + * returns true for classes created by {@link #getProxyClass}.
  • + *
  • The {@link ProtectionDomain} of a proxy class is the same as for + * bootstrap classes, such as Object or Proxy, since it is created by + * a trusted source. This protection domain will typically be granted + * {@link java.security.AllPermission}. But this is not a security + * risk, since there are adequate permissions on reflection, which is + * the only way to create an instance of the proxy class.
  • + *
  • The proxy class contains a single constructor, which takes as + * its only argument an {@link InvocationHandler}. The method + * {@link #newInstance} is shorthand to do the necessary + * reflection.
  • + *
+ * + *

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: + *
    + *
  • For a proxy class with Foo listed as one of its interfaces, the + * expression proxy instanceof Foo will return true, + * and the expression (Foo) proxy will succeed without + * a {@link ClassCastException}.
  • + *
  • Each proxy instance has an invocation handler, which can be + * accessed by {@link #getInvocationHandler(Object)}. Any call + * to an interface method, including {@link Object#hashcode()}, + * {@link Object#equals(Object)}, or {@link Object#toString()}, + * but excluding the public final methods of Object, will be + * encoded and passed to the {@link InvocationHandler#invoke} + * method of this handler.
  • + *
+ * + *

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; + + /** + * 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(); + + /** + * 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 final 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: + *

    + *
  • All objects in `interfaces' must represent distinct interfaces. + * Classes, primitive types, null, and duplicates are forbidden.
  • + *
  • The interfaces must be visible in the specified ClassLoader. + * In other words, for each interface i: + * Class.forName(i.getName(), false, loader) == i + * must be true.
  • + *
  • All non-public interfaces (if any) must reside in the same + * package, or the proxy class would be non-instantiable. If + * there are no non-public interfaces, the package of the proxy + * class is unspecified.
  • + *
  • All interfaces must be compatible - if two declare a method + * with the same name and parameters, the return type must be + * the same and the throws clause of the proxy class will be + * the maximal subset of subclasses of the throws clauses for + * each method that is overridden.
  • + *
  • VM constraints limit the number of interfaces a proxy class + * may directly implement (however, the indirect inheritance + * of {@link Serializable} does not count against this limit). + * Even though most VMs can theoretically have 65535 + * superinterfaces for a class, the actual limit is smaller + * because a class's constant pool is limited to 65535 entries, + * and not all entries can be interfaces.
  • + *

+ * + * 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) + { + this.loader = loader; + this.interfaces = interfaces; + } + + /** + * Calculates the hash code. + * + * @return a combination of the classloader and interfaces hashcodes. + */ + public int hashCode() + { + int hash = (loader == null) ? 0 : loader.hashCode(); + for (int i = 0; i < interfaces.length; i++) + hash = hash * 31 + interfaces[i].hashCode(); + return hash; + } + + /** + * 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; + int i = interfaces.length; + while (--i >= 0) + if (interfaces[i] != pt.interfaces[i]) + return false; + return true; + } + } // 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; + + /** + * 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]); + } + } + + /** + * 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) + { + 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); + } + + /** + * Calculates the hash code. + * + * @return a combination of name and parameter types + */ + public int hashCode() + { + 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; + } + + /** + * Calculates equality. + * + * @param the object to compare to + * @return true if it is a ProxySignature with same data + */ + public boolean equals(Object other) + { + 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.VMClassLoader"); + Class[] types = {ClassLoader.class, 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.flag = true; + Object[] args = {loader, qualName, bytecode, new Integer(0), + new Integer(bytecode.length), + /* Object.class.getProtectionDomain() */ }; + Class clazz = (Class) m.invoke(null, args); + m.flag = 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.flag = true; + // we can share the array, because it is not publicized + f.set(null, methods); + f.flag = 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(); + } + + /** + * 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) + { + 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 --git a/libjava/java/lang/reflect/UndeclaredThrowableException.java b/libjava/java/lang/reflect/UndeclaredThrowableException.java new file mode 100644 index 000000000000..d9596922bebe --- /dev/null +++ b/libjava/java/lang/reflect/UndeclaredThrowableException.java @@ -0,0 +1,128 @@ +/* UndeclaredThrowableException.java -- wraps an undeclared checked exception + thrown by a Proxy invocation handler + 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; + +/** + * This exception class is thrown by a {@link Proxy} instance if + * the {@link InvocationHandler#invoke(Object, Method, Object[]) invoke} + * method of that instance's InvocationHandler attempts to throw an + * exception that not declared by the throws clauses of all of the + * interface methods that the proxy instance is implementing. + * + *

When thrown by Proxy, this class will always wrap a checked + * exception, never {@link Error} or {@link RuntimeException}, + * which are unchecked. + * + * @author Eric Blake + * @see Proxy + * @see InvocationHandler + * @since 1.3 + * @status updated to 1.4 + */ +public class UndeclaredThrowableException extends RuntimeException +{ + /** + * Compatible with JDK 1.3+. + */ + private static final long serialVersionUID = 330127114055056639L; + + /** + * The immutable exception that this wraps. This field is redundant + * with {@link Throwable#cause}, but is necessary for serial compatibility. + * + * @serial the chained exception + */ + private final Throwable undeclaredThrowable; + + /** + * Wraps the given checked exception into a RuntimeException, with no + * detail message. {@link Throwable#initCause(Throwable)} will fail + * on this instance. + * + * @param cause the undeclared throwable that caused this exception, + * may be null + */ + public UndeclaredThrowableException(Throwable cause) + { + this(cause, null); + } + + /** + * Wraps the given checked exception into a RuntimeException, with the + * specified detail message. {@link Throwable#initCause(Throwable)} will + * fail on this instance. + * + * @param cause the undeclared throwable that caused this exception, + * may be null + * @param message the message, may be null + */ + public UndeclaredThrowableException(Throwable cause, String message) + { + super(message, cause); + undeclaredThrowable = cause; + } + + /** + * Returns the cause of this exception. If this exception was created + * by a {@link Proxy} instance, it will be a non-null checked + * exception. This method pre-dates exception chaining, and is now + * simply a longer way to call getCause(). + * + * @return the cause of this exception, may be null + * @see #getCause() + */ + public Throwable getUndeclaredThrowable() + { + return undeclaredThrowable; + } + + /** + * Returns the cause of this exception. If this exception was created + * by a {@link Proxy} instance, it will be a non-null checked + * exception. + * + * @return the cause of this exception, may be null + * @since 1.4 + */ + public Throwable getCause() + { + return undeclaredThrowable; + } +} diff --git a/libjava/java/lang/reflect/natProxy.cc b/libjava/java/lang/reflect/natProxy.cc new file mode 100644 index 000000000000..1a24858fa784 --- /dev/null +++ b/libjava/java/lang/reflect/natProxy.cc @@ -0,0 +1,38 @@ +// natProxy.cc - Native code for Proxy class. + +/* Copyright (C) 2002 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +#include + +#include +#include + +#include +#include +#include +#include + +::java::lang::Class * +java::lang::reflect::Proxy::getProxyClass0 (::java::lang::ClassLoader *, JArray< ::java::lang::Class *> *) +{ + return 0; +} + +::java::lang::reflect::Proxy$ProxyData * +java::lang::reflect::Proxy::getProxyData0 (::java::lang::ClassLoader *, JArray< ::java::lang::Class *> *) +{ + return 0; +} + +::java::lang::Class * +java::lang::reflect::Proxy::generateProxyClass0 (::java::lang::ClassLoader *, + ::java::lang::reflect::Proxy$ProxyData *) +{ + return 0; +} diff --git a/libjava/testsuite/ChangeLog b/libjava/testsuite/ChangeLog index 5915177d7f56..adb0e1e246b3 100644 --- a/libjava/testsuite/ChangeLog +++ b/libjava/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2002-09-29 Anthony Green + + * libjava.lang/TestProxy.java: New file. + * libjava.lang/TestProxy.out: Ditto. + +2002-09-29 Anthony Green + + * libjava.lang/utf8concat.java: New file. + * libjava.lang/utf8concat.out: Ditto. + 2002-08-26 Tom Tromey * libjava.compile/narrow_case.java: New file. diff --git a/libjava/testsuite/Makefile.in b/libjava/testsuite/Makefile.in index e5ed97687638..46247a6c279f 100644 --- a/libjava/testsuite/Makefile.in +++ b/libjava/testsuite/Makefile.in @@ -1,6 +1,6 @@ -# Makefile.in generated automatically by automake 1.4 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am -# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. diff --git a/libjava/testsuite/libjava.lang/TestProxy.java b/libjava/testsuite/libjava.lang/TestProxy.java new file mode 100644 index 000000000000..d1411a6f0a89 --- /dev/null +++ b/libjava/testsuite/libjava.lang/TestProxy.java @@ -0,0 +1,34 @@ +import java.lang.reflect.*; +import java.net.*; + +public class TestProxy +{ + public class MyInvocationHandler implements InvocationHandler + { + public Object invoke (Object proxy, + Method method, + Object[] args) + throws Throwable + { + System.out.println (args[0]); + return null; + } + } + + public static void main (String[] args) + { + try { + InvocationHandler ih = new MyInvocationHandler(); + + SocketOptions c = (SocketOptions) + Proxy.newProxyInstance (SocketOptions.class.getClassLoader(), + new Class[]{SocketOptions.class}, + ih); + + c.getOption (555); + + } catch (Exception e) { + e.printStackTrace (); + } + } +} diff --git a/libjava/testsuite/libjava.lang/TestProxy.out b/libjava/testsuite/libjava.lang/TestProxy.out new file mode 100644 index 000000000000..3749383ded24 --- /dev/null +++ b/libjava/testsuite/libjava.lang/TestProxy.out @@ -0,0 +1 @@ +555 diff --git a/libjava/testsuite/libjava.lang/utf8concat.java b/libjava/testsuite/libjava.lang/utf8concat.java new file mode 100644 index 000000000000..8b8f47ee1a0c --- /dev/null +++ b/libjava/testsuite/libjava.lang/utf8concat.java @@ -0,0 +1,11 @@ +public class utf8concat +{ + private static String s; + + public static void main (String[] args) + { + // This causes a crash at runtime because the compiler is + // producing an invalid UTF-8 string literal. + s = "abc" + (char)183; + } +} diff --git a/libjava/testsuite/libjava.lang/utf8concat.out b/libjava/testsuite/libjava.lang/utf8concat.out new file mode 100644 index 000000000000..e69de29bb2d1