1 |
771 |
jeremybenn |
/* Proxy.java -- build a proxy class that implements reflected interfaces
|
2 |
|
|
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
3 |
|
|
|
4 |
|
|
This file is part of GNU Classpath.
|
5 |
|
|
|
6 |
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
7 |
|
|
it under the terms of the GNU General Public License as published by
|
8 |
|
|
the Free Software Foundation; either version 2, or (at your option)
|
9 |
|
|
any later version.
|
10 |
|
|
|
11 |
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
12 |
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 |
|
|
General Public License for more details.
|
15 |
|
|
|
16 |
|
|
You should have received a copy of the GNU General Public License
|
17 |
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
18 |
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 |
|
|
02110-1301 USA.
|
20 |
|
|
|
21 |
|
|
Linking this library statically or dynamically with other modules is
|
22 |
|
|
making a combined work based on this library. Thus, the terms and
|
23 |
|
|
conditions of the GNU General Public License cover the whole
|
24 |
|
|
combination.
|
25 |
|
|
|
26 |
|
|
As a special exception, the copyright holders of this library give you
|
27 |
|
|
permission to link this library with independent modules to produce an
|
28 |
|
|
executable, regardless of the license terms of these independent
|
29 |
|
|
modules, and to copy and distribute the resulting executable under
|
30 |
|
|
terms of your choice, provided that you also meet, for each linked
|
31 |
|
|
independent module, the terms and conditions of the license of that
|
32 |
|
|
module. An independent module is a module which is not derived from
|
33 |
|
|
or based on this library. If you modify this library, you may extend
|
34 |
|
|
this exception to your version of the library, but you are not
|
35 |
|
|
obligated to do so. If you do not wish to do so, delete this
|
36 |
|
|
exception statement from your version. */
|
37 |
|
|
|
38 |
|
|
|
39 |
|
|
package java.lang.reflect;
|
40 |
|
|
|
41 |
|
|
import gnu.java.lang.CPStringBuilder;
|
42 |
|
|
|
43 |
|
|
import gnu.java.lang.reflect.TypeSignature;
|
44 |
|
|
|
45 |
|
|
import java.io.Serializable;
|
46 |
|
|
import java.security.ProtectionDomain;
|
47 |
|
|
import java.util.Arrays;
|
48 |
|
|
import java.util.HashMap;
|
49 |
|
|
import java.util.HashSet;
|
50 |
|
|
import java.util.Iterator;
|
51 |
|
|
import java.util.Map;
|
52 |
|
|
import java.util.Set;
|
53 |
|
|
|
54 |
|
|
/**
|
55 |
|
|
* This class allows you to dynamically create an instance of any (or
|
56 |
|
|
* even multiple) interfaces by reflection, and decide at runtime
|
57 |
|
|
* how that instance will behave by giving it an appropriate
|
58 |
|
|
* {@link InvocationHandler}. Proxy classes serialize specially, so
|
59 |
|
|
* that the proxy object can be reused between VMs, without requiring
|
60 |
|
|
* a persistent copy of the generated class code.
|
61 |
|
|
*
|
62 |
|
|
* <h3>Creation</h3>
|
63 |
|
|
* To create a proxy for some interface Foo:
|
64 |
|
|
*
|
65 |
|
|
* <pre>
|
66 |
|
|
* InvocationHandler handler = new MyInvocationHandler(...);
|
67 |
|
|
* Class proxyClass = Proxy.getProxyClass(
|
68 |
|
|
* Foo.class.getClassLoader(), new Class[] { Foo.class });
|
69 |
|
|
* Foo f = (Foo) proxyClass
|
70 |
|
|
* .getConstructor(new Class[] { InvocationHandler.class })
|
71 |
|
|
* .newInstance(new Object[] { handler });
|
72 |
|
|
* </pre>
|
73 |
|
|
* or more simply:
|
74 |
|
|
* <pre>
|
75 |
|
|
* Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
|
76 |
|
|
* new Class[] { Foo.class },
|
77 |
|
|
* handler);
|
78 |
|
|
* </pre>
|
79 |
|
|
*
|
80 |
|
|
* <h3>Dynamic Proxy Classes</h3>
|
81 |
|
|
* A dynamic proxy class is created at runtime, and has the following
|
82 |
|
|
* properties:
|
83 |
|
|
* <ul>
|
84 |
|
|
* <li>The class is <code>public</code> and <code>final</code>,
|
85 |
|
|
* and is neither <code>abstract</code> nor an inner class.</li>
|
86 |
|
|
* <li>The class has no canonical name (there is no formula you can use
|
87 |
|
|
* to determine or generate its name), but begins with the
|
88 |
|
|
* sequence "$Proxy". Abuse this knowledge at your own peril.
|
89 |
|
|
* (For now, '$' in user identifiers is legal, but it may not
|
90 |
|
|
* be that way forever. You weren't using '$' in your
|
91 |
|
|
* identifiers, were you?)</li>
|
92 |
|
|
* <li>The class extends Proxy, and explicitly implements all the
|
93 |
|
|
* interfaces specified at creation, in order (this is important
|
94 |
|
|
* for determining how method invocation is resolved). Note that
|
95 |
|
|
* a proxy class implements {@link Serializable}, at least
|
96 |
|
|
* implicitly, since Proxy does, but true serial behavior
|
97 |
|
|
* depends on using a serializable invocation handler as well.</li>
|
98 |
|
|
* <li>If at least one interface is non-public, the proxy class
|
99 |
|
|
* will be in the same package. Otherwise, the package is
|
100 |
|
|
* unspecified. This will work even if the package is sealed
|
101 |
|
|
* from user-generated classes, because Proxy classes are
|
102 |
|
|
* generated by a trusted source. Meanwhile, the proxy class
|
103 |
|
|
* belongs to the classloader you designated.</li>
|
104 |
|
|
* <li>Reflection works as expected: {@link Class#getInterfaces()} and
|
105 |
|
|
* {@link Class#getMethods()} work as they do on normal classes.</li>
|
106 |
|
|
* <li>The method {@link #isProxyClass(Class)} will distinguish between
|
107 |
|
|
* true proxy classes and user extensions of this class. It only
|
108 |
|
|
* returns true for classes created by {@link #getProxyClass}.</li>
|
109 |
|
|
* <li>The {@link ProtectionDomain} of a proxy class is the same as for
|
110 |
|
|
* bootstrap classes, such as Object or Proxy, since it is created by
|
111 |
|
|
* a trusted source. This protection domain will typically be granted
|
112 |
|
|
* {@link java.security.AllPermission}. But this is not a security
|
113 |
|
|
* risk, since there are adequate permissions on reflection, which is
|
114 |
|
|
* the only way to create an instance of the proxy class.</li>
|
115 |
|
|
* <li>The proxy class contains a single constructor, which takes as
|
116 |
|
|
* its only argument an {@link InvocationHandler}. The method
|
117 |
|
|
* {@link #newProxyInstance(ClassLoader, Class[], InvocationHandler)}
|
118 |
|
|
* is shorthand to do the necessary reflection.</li>
|
119 |
|
|
* </ul>
|
120 |
|
|
*
|
121 |
|
|
* <h3>Proxy Instances</h3>
|
122 |
|
|
* A proxy instance is an instance of a proxy class. It has the
|
123 |
|
|
* following properties, many of which follow from the properties of a
|
124 |
|
|
* proxy class listed above:
|
125 |
|
|
* <ul>
|
126 |
|
|
* <li>For a proxy class with Foo listed as one of its interfaces, the
|
127 |
|
|
* expression <code>proxy instanceof Foo</code> will return true,
|
128 |
|
|
* and the expression <code>(Foo) proxy</code> will succeed without
|
129 |
|
|
* a {@link ClassCastException}.</li>
|
130 |
|
|
* <li>Each proxy instance has an invocation handler, which can be
|
131 |
|
|
* accessed by {@link #getInvocationHandler(Object)}. Any call
|
132 |
|
|
* to an interface method, including {@link Object#hashCode()},
|
133 |
|
|
* {@link Object#equals(Object)}, or {@link Object#toString()},
|
134 |
|
|
* but excluding the public final methods of Object, will be
|
135 |
|
|
* encoded and passed to the {@link InvocationHandler#invoke}
|
136 |
|
|
* method of this handler.</li>
|
137 |
|
|
* </ul>
|
138 |
|
|
*
|
139 |
|
|
* <h3>Inheritance Issues</h3>
|
140 |
|
|
* A proxy class may inherit a method from more than one interface.
|
141 |
|
|
* The order in which interfaces are listed matters, because it determines
|
142 |
|
|
* which reflected {@link Method} object will be passed to the invocation
|
143 |
|
|
* handler. This means that the dynamically generated class cannot
|
144 |
|
|
* determine through which interface a method is being invoked.<p>
|
145 |
|
|
*
|
146 |
|
|
* In short, if a method is declared in Object (namely, hashCode,
|
147 |
|
|
* equals, or toString), then Object will be used; otherwise, the
|
148 |
|
|
* leftmost interface that inherits or declares a method will be used,
|
149 |
|
|
* even if it has a more permissive throws clause than what the proxy
|
150 |
|
|
* class is allowed. Thus, in the invocation handler, it is not always
|
151 |
|
|
* safe to assume that every class listed in the throws clause of the
|
152 |
|
|
* passed Method object can safely be thrown; fortunately, the Proxy
|
153 |
|
|
* instance is robust enough to wrap all illegal checked exceptions in
|
154 |
|
|
* {@link UndeclaredThrowableException}.
|
155 |
|
|
*
|
156 |
|
|
* @see InvocationHandler
|
157 |
|
|
* @see UndeclaredThrowableException
|
158 |
|
|
* @see Class
|
159 |
|
|
* @author Eric Blake (ebb9@email.byu.edu)
|
160 |
|
|
* @since 1.3
|
161 |
|
|
* @status updated to 1.5, except for the use of ProtectionDomain
|
162 |
|
|
*/
|
163 |
|
|
public class Proxy implements Serializable
|
164 |
|
|
{
|
165 |
|
|
/**
|
166 |
|
|
* Compatible with JDK 1.3+.
|
167 |
|
|
*/
|
168 |
|
|
private static final long serialVersionUID = -2222568056686623797L;
|
169 |
|
|
|
170 |
|
|
/**
|
171 |
|
|
* Map of ProxyType to proxy class.
|
172 |
|
|
*
|
173 |
|
|
* @XXX This prevents proxy classes from being garbage collected.
|
174 |
|
|
* java.util.WeakHashSet is not appropriate, because that collects the
|
175 |
|
|
* keys, but we are interested in collecting the elements.
|
176 |
|
|
*/
|
177 |
|
|
private static final Map proxyClasses = new HashMap();
|
178 |
|
|
|
179 |
|
|
/**
|
180 |
|
|
* The invocation handler for this proxy instance. For Proxy, this
|
181 |
|
|
* field is unused, but it appears here in order to be serialized in all
|
182 |
|
|
* proxy classes.
|
183 |
|
|
*
|
184 |
|
|
* <em>NOTE</em>: This implementation is more secure for proxy classes
|
185 |
|
|
* than what Sun specifies. Sun does not require h to be immutable, but
|
186 |
|
|
* this means you could change h after the fact by reflection. However,
|
187 |
|
|
* by making h immutable, we may break non-proxy classes which extend
|
188 |
|
|
* Proxy.
|
189 |
|
|
* @serial invocation handler associated with this proxy instance
|
190 |
|
|
*/
|
191 |
|
|
protected InvocationHandler h;
|
192 |
|
|
|
193 |
|
|
/**
|
194 |
|
|
* Constructs a new Proxy from a subclass (usually a proxy class),
|
195 |
|
|
* with the specified invocation handler.
|
196 |
|
|
*
|
197 |
|
|
* <em>NOTE</em>: This throws a NullPointerException if you attempt
|
198 |
|
|
* to create a proxy instance with a null handler using reflection.
|
199 |
|
|
* This behavior is not yet specified by Sun; see Sun Bug 4487672.
|
200 |
|
|
*
|
201 |
|
|
* @param handler the invocation handler, may be null if the subclass
|
202 |
|
|
* is not a proxy class
|
203 |
|
|
* @throws NullPointerException if handler is null and this is a proxy
|
204 |
|
|
* instance
|
205 |
|
|
*/
|
206 |
|
|
protected Proxy(InvocationHandler handler)
|
207 |
|
|
{
|
208 |
|
|
if (handler == null && isProxyClass(getClass()))
|
209 |
|
|
throw new NullPointerException("invalid handler");
|
210 |
|
|
h = handler;
|
211 |
|
|
}
|
212 |
|
|
|
213 |
|
|
/**
|
214 |
|
|
* Returns the proxy {@link Class} for the given ClassLoader and array
|
215 |
|
|
* of interfaces, dynamically generating it if necessary.
|
216 |
|
|
*
|
217 |
|
|
* <p>There are several restrictions on this method, the violation of
|
218 |
|
|
* which will result in an IllegalArgumentException or
|
219 |
|
|
* NullPointerException:</p>
|
220 |
|
|
*
|
221 |
|
|
* <ul>
|
222 |
|
|
* <li>All objects in `interfaces' must represent distinct interfaces.
|
223 |
|
|
* Classes, primitive types, null, and duplicates are forbidden.</li>
|
224 |
|
|
* <li>The interfaces must be visible in the specified ClassLoader.
|
225 |
|
|
* In other words, for each interface i:
|
226 |
|
|
* <code>Class.forName(i.getName(), false, loader) == i</code>
|
227 |
|
|
* must be true.</li>
|
228 |
|
|
* <li>All non-public interfaces (if any) must reside in the same
|
229 |
|
|
* package, or the proxy class would be non-instantiable. If
|
230 |
|
|
* there are no non-public interfaces, the package of the proxy
|
231 |
|
|
* class is unspecified.</li>
|
232 |
|
|
* <li>All interfaces must be compatible - if two declare a method
|
233 |
|
|
* with the same name and parameters, the return type must be
|
234 |
|
|
* the same and the throws clause of the proxy class will be
|
235 |
|
|
* the maximal subset of subclasses of the throws clauses for
|
236 |
|
|
* each method that is overridden.</li>
|
237 |
|
|
* <li>VM constraints limit the number of interfaces a proxy class
|
238 |
|
|
* may directly implement (however, the indirect inheritance
|
239 |
|
|
* of {@link Serializable} does not count against this limit).
|
240 |
|
|
* Even though most VMs can theoretically have 65535
|
241 |
|
|
* superinterfaces for a class, the actual limit is smaller
|
242 |
|
|
* because a class's constant pool is limited to 65535 entries,
|
243 |
|
|
* and not all entries can be interfaces.</li>
|
244 |
|
|
* </ul>
|
245 |
|
|
*
|
246 |
|
|
* <p>Note that different orders of interfaces produce distinct classes.</p>
|
247 |
|
|
*
|
248 |
|
|
* @param loader the class loader to define the proxy class in; null
|
249 |
|
|
* implies the bootstrap class loader
|
250 |
|
|
* @param interfaces the array of interfaces the proxy class implements,
|
251 |
|
|
* may be empty, but not null
|
252 |
|
|
* @return the Class object of the proxy class
|
253 |
|
|
* @throws IllegalArgumentException if the constraints above were
|
254 |
|
|
* violated, except for problems with null
|
255 |
|
|
* @throws NullPointerException if `interfaces' is null or contains
|
256 |
|
|
* a null entry
|
257 |
|
|
*/
|
258 |
|
|
// synchronized so that we aren't trying to build the same class
|
259 |
|
|
// simultaneously in two threads
|
260 |
|
|
public static synchronized Class<?> getProxyClass(ClassLoader loader,
|
261 |
|
|
Class<?>... interfaces)
|
262 |
|
|
{
|
263 |
|
|
interfaces = (Class[]) interfaces.clone();
|
264 |
|
|
ProxyType pt = new ProxyType(loader, interfaces);
|
265 |
|
|
Class clazz = (Class) proxyClasses.get(pt);
|
266 |
|
|
if (clazz == null)
|
267 |
|
|
{
|
268 |
|
|
if (VMProxy.HAVE_NATIVE_GET_PROXY_CLASS)
|
269 |
|
|
clazz = VMProxy.getProxyClass(loader, interfaces);
|
270 |
|
|
else
|
271 |
|
|
{
|
272 |
|
|
ProxyData data = (VMProxy.HAVE_NATIVE_GET_PROXY_DATA
|
273 |
|
|
? VMProxy.getProxyData(loader, interfaces)
|
274 |
|
|
: ProxyData.getProxyData(pt));
|
275 |
|
|
|
276 |
|
|
clazz = (VMProxy.HAVE_NATIVE_GENERATE_PROXY_CLASS
|
277 |
|
|
? VMProxy.generateProxyClass(loader, data)
|
278 |
|
|
: new ClassFactory(data).generate(loader));
|
279 |
|
|
}
|
280 |
|
|
|
281 |
|
|
Object check = proxyClasses.put(pt, clazz);
|
282 |
|
|
// assert check == null && clazz != null;
|
283 |
|
|
if (check != null || clazz == null)
|
284 |
|
|
throw new InternalError(/*"Fatal flaw in getProxyClass"*/);
|
285 |
|
|
}
|
286 |
|
|
return clazz;
|
287 |
|
|
}
|
288 |
|
|
|
289 |
|
|
/**
|
290 |
|
|
* Combines several methods into one. This is equivalent to:
|
291 |
|
|
* <pre>
|
292 |
|
|
* Proxy.getProxyClass(loader, interfaces)
|
293 |
|
|
* .getConstructor(new Class[] {InvocationHandler.class})
|
294 |
|
|
* .newInstance(new Object[] {handler});
|
295 |
|
|
* </pre>
|
296 |
|
|
* except that it will not fail with the normal problems caused
|
297 |
|
|
* by reflection. It can still fail for the same reasons documented
|
298 |
|
|
* in getProxyClass, or if handler is null.
|
299 |
|
|
*
|
300 |
|
|
* @param loader the class loader to define the proxy class in; null
|
301 |
|
|
* implies the bootstrap class loader
|
302 |
|
|
* @param interfaces the array of interfaces the proxy class implements,
|
303 |
|
|
* may be empty, but not null
|
304 |
|
|
* @param handler the invocation handler, may not be null
|
305 |
|
|
* @return a proxy instance implementing the specified interfaces
|
306 |
|
|
* @throws IllegalArgumentException if the constraints for getProxyClass
|
307 |
|
|
* were violated, except for problems with null
|
308 |
|
|
* @throws NullPointerException if `interfaces' is null or contains
|
309 |
|
|
* a null entry, or if handler is null
|
310 |
|
|
* @see #getProxyClass(ClassLoader, Class[])
|
311 |
|
|
* @see Class#getConstructor(Class[])
|
312 |
|
|
* @see Constructor#newInstance(Object[])
|
313 |
|
|
*/
|
314 |
|
|
public static Object newProxyInstance(ClassLoader loader,
|
315 |
|
|
Class<?>[] interfaces,
|
316 |
|
|
InvocationHandler handler)
|
317 |
|
|
{
|
318 |
|
|
try
|
319 |
|
|
{
|
320 |
|
|
// getProxyClass() and Proxy() throw the necessary exceptions
|
321 |
|
|
return getProxyClass(loader, interfaces)
|
322 |
|
|
.getConstructor(new Class[] {InvocationHandler.class})
|
323 |
|
|
.newInstance(new Object[] {handler});
|
324 |
|
|
}
|
325 |
|
|
catch (RuntimeException e)
|
326 |
|
|
{
|
327 |
|
|
// Let IllegalArgumentException, NullPointerException escape.
|
328 |
|
|
// assert e instanceof IllegalArgumentException
|
329 |
|
|
// || e instanceof NullPointerException;
|
330 |
|
|
throw e;
|
331 |
|
|
}
|
332 |
|
|
catch (InvocationTargetException e)
|
333 |
|
|
{
|
334 |
|
|
// Let wrapped NullPointerException escape.
|
335 |
|
|
// assert e.getTargetException() instanceof NullPointerException
|
336 |
|
|
throw (NullPointerException) e.getCause();
|
337 |
|
|
}
|
338 |
|
|
catch (Exception e)
|
339 |
|
|
{
|
340 |
|
|
// Covers InstantiationException, IllegalAccessException,
|
341 |
|
|
// NoSuchMethodException, none of which should be generated
|
342 |
|
|
// if the proxy class was generated correctly.
|
343 |
|
|
// assert false;
|
344 |
|
|
throw (Error) new InternalError("Unexpected: " + e).initCause(e);
|
345 |
|
|
}
|
346 |
|
|
}
|
347 |
|
|
|
348 |
|
|
/**
|
349 |
|
|
* Returns true if and only if the Class object is a dynamically created
|
350 |
|
|
* proxy class (created by <code>getProxyClass</code> or by the
|
351 |
|
|
* syntactic sugar of <code>newProxyInstance</code>).
|
352 |
|
|
*
|
353 |
|
|
* <p>This check is secure (in other words, it is not simply
|
354 |
|
|
* <code>clazz.getSuperclass() == Proxy.class</code>), it will not
|
355 |
|
|
* be spoofed by non-proxy classes that extend Proxy.
|
356 |
|
|
*
|
357 |
|
|
* @param clazz the class to check, must not be null
|
358 |
|
|
* @return true if the class represents a proxy class
|
359 |
|
|
* @throws NullPointerException if clazz is null
|
360 |
|
|
*/
|
361 |
|
|
// This is synchronized on the off chance that another thread is
|
362 |
|
|
// trying to add a class to the map at the same time we read it.
|
363 |
|
|
public static synchronized boolean isProxyClass(Class<?> clazz)
|
364 |
|
|
{
|
365 |
|
|
if (! Proxy.class.isAssignableFrom(clazz))
|
366 |
|
|
return false;
|
367 |
|
|
// This is a linear search, even though we could do an O(1) search
|
368 |
|
|
// using new ProxyType(clazz.getClassLoader(), clazz.getInterfaces()).
|
369 |
|
|
return proxyClasses.containsValue(clazz);
|
370 |
|
|
}
|
371 |
|
|
|
372 |
|
|
/**
|
373 |
|
|
* Returns the invocation handler for the given proxy instance.<p>
|
374 |
|
|
*
|
375 |
|
|
* <em>NOTE</em>: We guarantee a non-null result if successful,
|
376 |
|
|
* but Sun allows the creation of a proxy instance with a null
|
377 |
|
|
* handler. See the comments for {@link #Proxy(InvocationHandler)}.
|
378 |
|
|
*
|
379 |
|
|
* @param proxy the proxy instance, must not be null
|
380 |
|
|
* @return the invocation handler, guaranteed non-null.
|
381 |
|
|
* @throws IllegalArgumentException if
|
382 |
|
|
* <code>Proxy.isProxyClass(proxy.getClass())</code> returns false.
|
383 |
|
|
* @throws NullPointerException if proxy is null
|
384 |
|
|
*/
|
385 |
|
|
public static InvocationHandler getInvocationHandler(Object proxy)
|
386 |
|
|
{
|
387 |
|
|
if (! isProxyClass(proxy.getClass()))
|
388 |
|
|
throw new IllegalArgumentException("not a proxy instance");
|
389 |
|
|
return ((Proxy) proxy).h;
|
390 |
|
|
}
|
391 |
|
|
|
392 |
|
|
/**
|
393 |
|
|
* Helper class for mapping unique ClassLoader and interface combinations
|
394 |
|
|
* to proxy classes.
|
395 |
|
|
*
|
396 |
|
|
* @author Eric Blake (ebb9@email.byu.edu)
|
397 |
|
|
*/
|
398 |
|
|
private static final class ProxyType
|
399 |
|
|
{
|
400 |
|
|
/**
|
401 |
|
|
* Store the class loader (may be null)
|
402 |
|
|
*/
|
403 |
|
|
final ClassLoader loader;
|
404 |
|
|
|
405 |
|
|
/**
|
406 |
|
|
* Store the interfaces (never null, all elements are interfaces)
|
407 |
|
|
*/
|
408 |
|
|
final Class[] interfaces;
|
409 |
|
|
|
410 |
|
|
/**
|
411 |
|
|
* Construct the helper object.
|
412 |
|
|
*
|
413 |
|
|
* @param loader the class loader to define the proxy class in; null
|
414 |
|
|
* implies the bootstrap class loader
|
415 |
|
|
* @param interfaces an array of interfaces
|
416 |
|
|
*/
|
417 |
|
|
ProxyType(ClassLoader loader, Class[] interfaces)
|
418 |
|
|
{
|
419 |
|
|
this.loader = loader;
|
420 |
|
|
this.interfaces = interfaces;
|
421 |
|
|
}
|
422 |
|
|
|
423 |
|
|
/**
|
424 |
|
|
* Calculates the hash code.
|
425 |
|
|
*
|
426 |
|
|
* @return a combination of the classloader and interfaces hashcodes.
|
427 |
|
|
*/
|
428 |
|
|
public int hashCode()
|
429 |
|
|
{
|
430 |
|
|
int hash = loader == null ? 0 : loader.hashCode();
|
431 |
|
|
for (int i = 0; i < interfaces.length; i++)
|
432 |
|
|
hash = hash * 31 + interfaces[i].hashCode();
|
433 |
|
|
return hash;
|
434 |
|
|
}
|
435 |
|
|
|
436 |
|
|
/**
|
437 |
|
|
* Calculates equality.
|
438 |
|
|
*
|
439 |
|
|
* @param other object to compare to
|
440 |
|
|
* @return true if it is a ProxyType with same data
|
441 |
|
|
*/
|
442 |
|
|
public boolean equals(Object other)
|
443 |
|
|
{
|
444 |
|
|
ProxyType pt = (ProxyType) other;
|
445 |
|
|
if (loader != pt.loader || interfaces.length != pt.interfaces.length)
|
446 |
|
|
return false;
|
447 |
|
|
for (int i = 0; i < interfaces.length; i++)
|
448 |
|
|
if (interfaces[i] != pt.interfaces[i])
|
449 |
|
|
return false;
|
450 |
|
|
return true;
|
451 |
|
|
}
|
452 |
|
|
} // class ProxyType
|
453 |
|
|
|
454 |
|
|
/**
|
455 |
|
|
* Helper class which allows hashing of a method name and signature
|
456 |
|
|
* without worrying about return type, declaring class, or throws clause,
|
457 |
|
|
* and which reduces the maximally common throws clause between two methods
|
458 |
|
|
*
|
459 |
|
|
* @author Eric Blake (ebb9@email.byu.edu)
|
460 |
|
|
*/
|
461 |
|
|
private static final class ProxySignature
|
462 |
|
|
{
|
463 |
|
|
/**
|
464 |
|
|
* The core signatures which all Proxy instances handle.
|
465 |
|
|
*/
|
466 |
|
|
static final HashMap coreMethods = new HashMap();
|
467 |
|
|
static
|
468 |
|
|
{
|
469 |
|
|
try
|
470 |
|
|
{
|
471 |
|
|
ProxySignature sig
|
472 |
|
|
= new ProxySignature(Object.class
|
473 |
|
|
.getMethod("equals",
|
474 |
|
|
new Class[] {Object.class}));
|
475 |
|
|
coreMethods.put(sig, sig);
|
476 |
|
|
sig = new ProxySignature(Object.class.getMethod("hashCode"));
|
477 |
|
|
coreMethods.put(sig, sig);
|
478 |
|
|
sig = new ProxySignature(Object.class.getMethod("toString"));
|
479 |
|
|
coreMethods.put(sig, sig);
|
480 |
|
|
}
|
481 |
|
|
catch (Exception e)
|
482 |
|
|
{
|
483 |
|
|
// assert false;
|
484 |
|
|
throw (Error) new InternalError("Unexpected: " + e).initCause(e);
|
485 |
|
|
}
|
486 |
|
|
}
|
487 |
|
|
|
488 |
|
|
/**
|
489 |
|
|
* The underlying Method object, never null
|
490 |
|
|
*/
|
491 |
|
|
final Method method;
|
492 |
|
|
|
493 |
|
|
/**
|
494 |
|
|
* The set of compatible thrown exceptions, may be empty
|
495 |
|
|
*/
|
496 |
|
|
final Set exceptions = new HashSet();
|
497 |
|
|
|
498 |
|
|
/**
|
499 |
|
|
* Construct a signature
|
500 |
|
|
*
|
501 |
|
|
* @param method the Method this signature is based on, never null
|
502 |
|
|
*/
|
503 |
|
|
ProxySignature(Method method)
|
504 |
|
|
{
|
505 |
|
|
this.method = method;
|
506 |
|
|
Class[] exc = method.getExceptionTypes();
|
507 |
|
|
int i = exc.length;
|
508 |
|
|
while (--i >= 0)
|
509 |
|
|
{
|
510 |
|
|
// discard unchecked exceptions
|
511 |
|
|
if (Error.class.isAssignableFrom(exc[i])
|
512 |
|
|
|| RuntimeException.class.isAssignableFrom(exc[i]))
|
513 |
|
|
continue;
|
514 |
|
|
exceptions.add(exc[i]);
|
515 |
|
|
}
|
516 |
|
|
}
|
517 |
|
|
|
518 |
|
|
/**
|
519 |
|
|
* Given a method, make sure it's return type is identical
|
520 |
|
|
* to this, and adjust this signature's throws clause appropriately
|
521 |
|
|
*
|
522 |
|
|
* @param other the signature to merge in
|
523 |
|
|
* @throws IllegalArgumentException if the return types conflict
|
524 |
|
|
*/
|
525 |
|
|
void checkCompatibility(ProxySignature other)
|
526 |
|
|
{
|
527 |
|
|
if (method.getReturnType() != other.method.getReturnType())
|
528 |
|
|
throw new IllegalArgumentException("incompatible return types: "
|
529 |
|
|
+ method + ", " + other.method);
|
530 |
|
|
|
531 |
|
|
// if you can think of a more efficient way than this O(n^2) search,
|
532 |
|
|
// implement it!
|
533 |
|
|
int size1 = exceptions.size();
|
534 |
|
|
int size2 = other.exceptions.size();
|
535 |
|
|
boolean[] valid1 = new boolean[size1];
|
536 |
|
|
boolean[] valid2 = new boolean[size2];
|
537 |
|
|
Iterator itr = exceptions.iterator();
|
538 |
|
|
int pos = size1;
|
539 |
|
|
while (--pos >= 0)
|
540 |
|
|
{
|
541 |
|
|
Class c1 = (Class) itr.next();
|
542 |
|
|
Iterator itr2 = other.exceptions.iterator();
|
543 |
|
|
int pos2 = size2;
|
544 |
|
|
while (--pos2 >= 0)
|
545 |
|
|
{
|
546 |
|
|
Class c2 = (Class) itr2.next();
|
547 |
|
|
if (c2.isAssignableFrom(c1))
|
548 |
|
|
valid1[pos] = true;
|
549 |
|
|
if (c1.isAssignableFrom(c2))
|
550 |
|
|
valid2[pos2] = true;
|
551 |
|
|
}
|
552 |
|
|
}
|
553 |
|
|
pos = size1;
|
554 |
|
|
itr = exceptions.iterator();
|
555 |
|
|
while (--pos >= 0)
|
556 |
|
|
{
|
557 |
|
|
itr.next();
|
558 |
|
|
if (! valid1[pos])
|
559 |
|
|
itr.remove();
|
560 |
|
|
}
|
561 |
|
|
pos = size2;
|
562 |
|
|
itr = other.exceptions.iterator();
|
563 |
|
|
while (--pos >= 0)
|
564 |
|
|
{
|
565 |
|
|
itr.next();
|
566 |
|
|
if (! valid2[pos])
|
567 |
|
|
itr.remove();
|
568 |
|
|
}
|
569 |
|
|
exceptions.addAll(other.exceptions);
|
570 |
|
|
}
|
571 |
|
|
|
572 |
|
|
/**
|
573 |
|
|
* Calculates the hash code.
|
574 |
|
|
*
|
575 |
|
|
* @return a combination of name and parameter types
|
576 |
|
|
*/
|
577 |
|
|
public int hashCode()
|
578 |
|
|
{
|
579 |
|
|
int hash = method.getName().hashCode();
|
580 |
|
|
Class[] types = method.getParameterTypes();
|
581 |
|
|
for (int i = 0; i < types.length; i++)
|
582 |
|
|
hash = hash * 31 + types[i].hashCode();
|
583 |
|
|
return hash;
|
584 |
|
|
}
|
585 |
|
|
|
586 |
|
|
/**
|
587 |
|
|
* Calculates equality.
|
588 |
|
|
*
|
589 |
|
|
* @param other object to compare to
|
590 |
|
|
* @return true if it is a ProxySignature with same data
|
591 |
|
|
*/
|
592 |
|
|
public boolean equals(Object other)
|
593 |
|
|
{
|
594 |
|
|
ProxySignature ps = (ProxySignature) other;
|
595 |
|
|
Class[] types1 = method.getParameterTypes();
|
596 |
|
|
Class[] types2 = ps.method.getParameterTypes();
|
597 |
|
|
if (! method.getName().equals(ps.method.getName())
|
598 |
|
|
|| types1.length != types2.length)
|
599 |
|
|
return false;
|
600 |
|
|
int i = types1.length;
|
601 |
|
|
while (--i >= 0)
|
602 |
|
|
if (types1[i] != types2[i])
|
603 |
|
|
return false;
|
604 |
|
|
return true;
|
605 |
|
|
}
|
606 |
|
|
} // class ProxySignature
|
607 |
|
|
|
608 |
|
|
/**
|
609 |
|
|
* A flat representation of all data needed to generate bytecode/instantiate
|
610 |
|
|
* a proxy class. This is basically a struct.
|
611 |
|
|
*
|
612 |
|
|
* @author Eric Blake (ebb9@email.byu.edu)
|
613 |
|
|
*/
|
614 |
|
|
static final class ProxyData
|
615 |
|
|
{
|
616 |
|
|
/**
|
617 |
|
|
* The package this class is in <b>including the trailing dot</b>
|
618 |
|
|
* or an empty string for the unnamed (aka default) package.
|
619 |
|
|
*/
|
620 |
|
|
String pack = "";
|
621 |
|
|
|
622 |
|
|
/**
|
623 |
|
|
* The interfaces this class implements. Non-null, but possibly empty.
|
624 |
|
|
*/
|
625 |
|
|
Class[] interfaces;
|
626 |
|
|
|
627 |
|
|
/**
|
628 |
|
|
* The Method objects this class must pass as the second argument to
|
629 |
|
|
* invoke (also useful for determining what methods this class has).
|
630 |
|
|
* Non-null, non-empty (includes at least Object.hashCode, Object.equals,
|
631 |
|
|
* and Object.toString).
|
632 |
|
|
*/
|
633 |
|
|
Method[] methods;
|
634 |
|
|
|
635 |
|
|
/**
|
636 |
|
|
* The exceptions that do not need to be wrapped in
|
637 |
|
|
* UndeclaredThrowableException. exceptions[i] is the same as, or a
|
638 |
|
|
* subset of subclasses, of methods[i].getExceptionTypes(), depending on
|
639 |
|
|
* compatible throws clauses with multiple inheritance. It is unspecified
|
640 |
|
|
* if these lists include or exclude subclasses of Error and
|
641 |
|
|
* RuntimeException, but excluding them is harmless and generates a
|
642 |
|
|
* smaller class.
|
643 |
|
|
*/
|
644 |
|
|
Class[][] exceptions;
|
645 |
|
|
|
646 |
|
|
/**
|
647 |
|
|
* For unique id's
|
648 |
|
|
*/
|
649 |
|
|
private static int count;
|
650 |
|
|
|
651 |
|
|
/**
|
652 |
|
|
* The id of this proxy class
|
653 |
|
|
*/
|
654 |
|
|
final int id = count++;
|
655 |
|
|
|
656 |
|
|
/**
|
657 |
|
|
* Construct a ProxyData with uninitialized data members.
|
658 |
|
|
*/
|
659 |
|
|
ProxyData()
|
660 |
|
|
{
|
661 |
|
|
}
|
662 |
|
|
|
663 |
|
|
/**
|
664 |
|
|
* Return the name of a package (including the trailing dot)
|
665 |
|
|
* given the name of a class.
|
666 |
|
|
* Returns an empty string if no package. We use this in preference to
|
667 |
|
|
* using Class.getPackage() to avoid problems with ClassLoaders
|
668 |
|
|
* that don't set the package.
|
669 |
|
|
*/
|
670 |
|
|
private static String getPackage(Class k)
|
671 |
|
|
{
|
672 |
|
|
String name = k.getName();
|
673 |
|
|
int idx = name.lastIndexOf('.');
|
674 |
|
|
return name.substring(0, idx + 1);
|
675 |
|
|
}
|
676 |
|
|
|
677 |
|
|
/**
|
678 |
|
|
* Verifies that the arguments are legal, and sets up remaining data
|
679 |
|
|
* This should only be called when a class must be generated, as
|
680 |
|
|
* it is expensive.
|
681 |
|
|
*
|
682 |
|
|
* @param pt the ProxyType to convert to ProxyData
|
683 |
|
|
* @return the flattened, verified ProxyData structure for use in
|
684 |
|
|
* class generation
|
685 |
|
|
* @throws IllegalArgumentException if `interfaces' contains
|
686 |
|
|
* non-interfaces or incompatible combinations, and verify is true
|
687 |
|
|
* @throws NullPointerException if interfaces is null or contains null
|
688 |
|
|
*/
|
689 |
|
|
static ProxyData getProxyData(ProxyType pt)
|
690 |
|
|
{
|
691 |
|
|
Map method_set = (Map) ProxySignature.coreMethods.clone();
|
692 |
|
|
boolean in_package = false; // true if we encounter non-public interface
|
693 |
|
|
|
694 |
|
|
ProxyData data = new ProxyData();
|
695 |
|
|
data.interfaces = pt.interfaces;
|
696 |
|
|
|
697 |
|
|
// if interfaces is too large, we croak later on when the constant
|
698 |
|
|
// pool overflows
|
699 |
|
|
int i = data.interfaces.length;
|
700 |
|
|
while (--i >= 0)
|
701 |
|
|
{
|
702 |
|
|
Class inter = data.interfaces[i];
|
703 |
|
|
if (! inter.isInterface())
|
704 |
|
|
throw new IllegalArgumentException("not an interface: " + inter);
|
705 |
|
|
try
|
706 |
|
|
{
|
707 |
|
|
if (Class.forName(inter.getName(), false, pt.loader) != inter)
|
708 |
|
|
throw new IllegalArgumentException("not accessible in "
|
709 |
|
|
+ "classloader: " + inter);
|
710 |
|
|
}
|
711 |
|
|
catch (ClassNotFoundException e)
|
712 |
|
|
{
|
713 |
|
|
throw new IllegalArgumentException("not accessible in "
|
714 |
|
|
+ "classloader: " + inter);
|
715 |
|
|
}
|
716 |
|
|
if (! Modifier.isPublic(inter.getModifiers()))
|
717 |
|
|
if (in_package)
|
718 |
|
|
{
|
719 |
|
|
String p = getPackage(inter);
|
720 |
|
|
if (! data.pack.equals(p))
|
721 |
|
|
throw new IllegalArgumentException("non-public interfaces "
|
722 |
|
|
+ "from different "
|
723 |
|
|
+ "packages");
|
724 |
|
|
}
|
725 |
|
|
else
|
726 |
|
|
{
|
727 |
|
|
in_package = true;
|
728 |
|
|
data.pack = getPackage(inter);
|
729 |
|
|
}
|
730 |
|
|
for (int j = i-1; j >= 0; j--)
|
731 |
|
|
if (data.interfaces[j] == inter)
|
732 |
|
|
throw new IllegalArgumentException("duplicate interface: "
|
733 |
|
|
+ inter);
|
734 |
|
|
Method[] methods = inter.getMethods();
|
735 |
|
|
int j = methods.length;
|
736 |
|
|
while (--j >= 0)
|
737 |
|
|
{
|
738 |
|
|
if (isCoreObjectMethod(methods[j]))
|
739 |
|
|
{
|
740 |
|
|
// In the case of an attempt to redefine a public non-final
|
741 |
|
|
// method of Object, we must skip it
|
742 |
|
|
continue;
|
743 |
|
|
}
|
744 |
|
|
ProxySignature sig = new ProxySignature(methods[j]);
|
745 |
|
|
ProxySignature old = (ProxySignature) method_set.put(sig, sig);
|
746 |
|
|
if (old != null)
|
747 |
|
|
sig.checkCompatibility(old);
|
748 |
|
|
}
|
749 |
|
|
}
|
750 |
|
|
|
751 |
|
|
i = method_set.size();
|
752 |
|
|
data.methods = new Method[i];
|
753 |
|
|
data.exceptions = new Class[i][];
|
754 |
|
|
Iterator itr = method_set.values().iterator();
|
755 |
|
|
while (--i >= 0)
|
756 |
|
|
{
|
757 |
|
|
ProxySignature sig = (ProxySignature) itr.next();
|
758 |
|
|
data.methods[i] = sig.method;
|
759 |
|
|
data.exceptions[i] = (Class[]) sig.exceptions
|
760 |
|
|
.toArray(new Class[sig.exceptions.size()]);
|
761 |
|
|
}
|
762 |
|
|
return data;
|
763 |
|
|
}
|
764 |
|
|
|
765 |
|
|
/**
|
766 |
|
|
* Checks whether the method is similar to a public non-final method of
|
767 |
|
|
* Object or not (i.e. with the same name and parameter types). Note that we
|
768 |
|
|
* can't rely, directly or indirectly (via Collection.contains) on
|
769 |
|
|
* Method.equals as it would also check the declaring class, what we do not
|
770 |
|
|
* want. We only want to check that the given method have the same signature
|
771 |
|
|
* as a core method (same name and parameter types)
|
772 |
|
|
*
|
773 |
|
|
* @param method the method to check
|
774 |
|
|
* @return whether the method has the same name and parameter types as
|
775 |
|
|
* Object.equals, Object.hashCode or Object.toString
|
776 |
|
|
* @see java.lang.Object#equals(Object)
|
777 |
|
|
* @see java.lang.Object#hashCode()
|
778 |
|
|
* @see java.lang.Object#toString()
|
779 |
|
|
*/
|
780 |
|
|
private static boolean isCoreObjectMethod(Method method)
|
781 |
|
|
{
|
782 |
|
|
String methodName = method.getName();
|
783 |
|
|
if (methodName.equals("equals"))
|
784 |
|
|
{
|
785 |
|
|
return Arrays.equals(method.getParameterTypes(),
|
786 |
|
|
new Class[] { Object.class });
|
787 |
|
|
}
|
788 |
|
|
if (methodName.equals("hashCode"))
|
789 |
|
|
{
|
790 |
|
|
return method.getParameterTypes().length == 0;
|
791 |
|
|
}
|
792 |
|
|
if (methodName.equals("toString"))
|
793 |
|
|
{
|
794 |
|
|
return method.getParameterTypes().length == 0;
|
795 |
|
|
}
|
796 |
|
|
return false;
|
797 |
|
|
}
|
798 |
|
|
|
799 |
|
|
} // class ProxyData
|
800 |
|
|
|
801 |
|
|
/**
|
802 |
|
|
* Does all the work of building a class. By making this a nested class,
|
803 |
|
|
* this code is not loaded in memory if the VM has a native
|
804 |
|
|
* implementation instead.
|
805 |
|
|
*
|
806 |
|
|
* @author Eric Blake (ebb9@email.byu.edu)
|
807 |
|
|
*/
|
808 |
|
|
private static final class ClassFactory
|
809 |
|
|
{
|
810 |
|
|
/** Constants for assisting the compilation */
|
811 |
|
|
private static final byte FIELD = 1;
|
812 |
|
|
private static final byte METHOD = 2;
|
813 |
|
|
private static final byte INTERFACE = 3;
|
814 |
|
|
private static final String CTOR_SIG
|
815 |
|
|
= "(Ljava/lang/reflect/InvocationHandler;)V";
|
816 |
|
|
private static final String INVOKE_SIG = "(Ljava/lang/Object;"
|
817 |
|
|
+ "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;";
|
818 |
|
|
|
819 |
|
|
/** Bytecodes for insertion in the class definition byte[] */
|
820 |
|
|
private static final char ACONST_NULL = 1;
|
821 |
|
|
private static final char ICONST_0 = 3;
|
822 |
|
|
private static final char BIPUSH = 16;
|
823 |
|
|
private static final char SIPUSH = 17;
|
824 |
|
|
private static final char ILOAD = 21;
|
825 |
|
|
private static final char ILOAD_0 = 26;
|
826 |
|
|
private static final char ALOAD_0 = 42;
|
827 |
|
|
private static final char ALOAD_1 = 43;
|
828 |
|
|
private static final char AALOAD = 50;
|
829 |
|
|
private static final char AASTORE = 83;
|
830 |
|
|
private static final char DUP = 89;
|
831 |
|
|
private static final char DUP_X1 = 90;
|
832 |
|
|
private static final char SWAP = 95;
|
833 |
|
|
private static final char IRETURN = 172;
|
834 |
|
|
private static final char LRETURN = 173;
|
835 |
|
|
private static final char FRETURN = 174;
|
836 |
|
|
private static final char DRETURN = 175;
|
837 |
|
|
private static final char ARETURN = 176;
|
838 |
|
|
private static final char RETURN = 177;
|
839 |
|
|
private static final char GETSTATIC = 178;
|
840 |
|
|
private static final char GETFIELD = 180;
|
841 |
|
|
private static final char INVOKEVIRTUAL = 182;
|
842 |
|
|
private static final char INVOKESPECIAL = 183;
|
843 |
|
|
private static final char INVOKEINTERFACE = 185;
|
844 |
|
|
private static final char NEW = 187;
|
845 |
|
|
private static final char ANEWARRAY = 189;
|
846 |
|
|
private static final char ATHROW = 191;
|
847 |
|
|
private static final char CHECKCAST = 192;
|
848 |
|
|
|
849 |
|
|
// Implementation note: we use StringBuffers to hold the byte data, since
|
850 |
|
|
// they automatically grow. However, we only use the low 8 bits of
|
851 |
|
|
// every char in the array, so we are using twice the necessary memory
|
852 |
|
|
// for the ease StringBuffer provides.
|
853 |
|
|
|
854 |
|
|
/** The constant pool. */
|
855 |
|
|
private final StringBuffer pool = new StringBuffer();
|
856 |
|
|
/** The rest of the class data. */
|
857 |
|
|
private final StringBuffer stream = new StringBuffer();
|
858 |
|
|
|
859 |
|
|
/** Map of strings to byte sequences, to minimize size of pool. */
|
860 |
|
|
private final Map poolEntries = new HashMap();
|
861 |
|
|
|
862 |
|
|
/** The VM name of this proxy class. */
|
863 |
|
|
private final String qualName;
|
864 |
|
|
|
865 |
|
|
/**
|
866 |
|
|
* The Method objects the proxy class refers to when calling the
|
867 |
|
|
* invocation handler.
|
868 |
|
|
*/
|
869 |
|
|
private final Method[] methods;
|
870 |
|
|
|
871 |
|
|
/**
|
872 |
|
|
* Initializes the buffers with the bytecode contents for a proxy class.
|
873 |
|
|
*
|
874 |
|
|
* @param data the remainder of the class data
|
875 |
|
|
* @throws IllegalArgumentException if anything else goes wrong this
|
876 |
|
|
* late in the game; as far as I can tell, this will only happen
|
877 |
|
|
* if the constant pool overflows, which is possible even when
|
878 |
|
|
* the user doesn't exceed the 65535 interface limit
|
879 |
|
|
*/
|
880 |
|
|
ClassFactory(ProxyData data)
|
881 |
|
|
{
|
882 |
|
|
methods = data.methods;
|
883 |
|
|
|
884 |
|
|
// magic = 0xcafebabe
|
885 |
|
|
// minor_version = 0
|
886 |
|
|
// major_version = 46
|
887 |
|
|
// constant_pool_count: place-holder for now
|
888 |
|
|
pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0");
|
889 |
|
|
// constant_pool[], filled in as we go
|
890 |
|
|
|
891 |
|
|
// access_flags
|
892 |
|
|
putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC);
|
893 |
|
|
// this_class
|
894 |
|
|
qualName = (data.pack + "$Proxy" + data.id);
|
895 |
|
|
putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false)));
|
896 |
|
|
// super_class
|
897 |
|
|
putU2(classInfo("java/lang/reflect/Proxy"));
|
898 |
|
|
|
899 |
|
|
// interfaces_count
|
900 |
|
|
putU2(data.interfaces.length);
|
901 |
|
|
// interfaces[]
|
902 |
|
|
for (int i = 0; i < data.interfaces.length; i++)
|
903 |
|
|
putU2(classInfo(data.interfaces[i]));
|
904 |
|
|
|
905 |
|
|
// Recall that Proxy classes serialize specially, so we do not need
|
906 |
|
|
// to worry about a <clinit> method for this field. Instead, we
|
907 |
|
|
// just assign it by reflection after the class is successfully loaded.
|
908 |
|
|
// fields_count - private static Method[] m;
|
909 |
|
|
putU2(1);
|
910 |
|
|
// fields[]
|
911 |
|
|
// m.access_flags
|
912 |
|
|
putU2(Modifier.PRIVATE | Modifier.STATIC);
|
913 |
|
|
// m.name_index
|
914 |
|
|
putU2(utf8Info("m"));
|
915 |
|
|
// m.descriptor_index
|
916 |
|
|
putU2(utf8Info("[Ljava/lang/reflect/Method;"));
|
917 |
|
|
// m.attributes_count
|
918 |
|
|
putU2(0);
|
919 |
|
|
// m.attributes[]
|
920 |
|
|
|
921 |
|
|
// methods_count - # handler methods, plus <init>
|
922 |
|
|
putU2(methods.length + 1);
|
923 |
|
|
// methods[]
|
924 |
|
|
// <init>.access_flags
|
925 |
|
|
putU2(Modifier.PUBLIC);
|
926 |
|
|
// <init>.name_index
|
927 |
|
|
putU2(utf8Info("<init>"));
|
928 |
|
|
// <init>.descriptor_index
|
929 |
|
|
putU2(utf8Info(CTOR_SIG));
|
930 |
|
|
// <init>.attributes_count - only Code is needed
|
931 |
|
|
putU2(1);
|
932 |
|
|
// <init>.Code.attribute_name_index
|
933 |
|
|
putU2(utf8Info("Code"));
|
934 |
|
|
// <init>.Code.attribute_length = 18
|
935 |
|
|
// <init>.Code.info:
|
936 |
|
|
// $Proxynn(InvocationHandler h) { super(h); }
|
937 |
|
|
// <init>.Code.max_stack = 2
|
938 |
|
|
// <init>.Code.max_locals = 2
|
939 |
|
|
// <init>.Code.code_length = 6
|
940 |
|
|
// <init>.Code.code[]
|
941 |
|
|
stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1
|
942 |
|
|
+ INVOKESPECIAL);
|
943 |
|
|
putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "<init>", CTOR_SIG));
|
944 |
|
|
// <init>.Code.exception_table_length = 0
|
945 |
|
|
// <init>.Code.exception_table[]
|
946 |
|
|
// <init>.Code.attributes_count = 0
|
947 |
|
|
// <init>.Code.attributes[]
|
948 |
|
|
stream.append(RETURN + "\0\0\0\0");
|
949 |
|
|
|
950 |
|
|
for (int i = methods.length - 1; i >= 0; i--)
|
951 |
|
|
emitMethod(i, data.exceptions[i]);
|
952 |
|
|
|
953 |
|
|
// attributes_count
|
954 |
|
|
putU2(0);
|
955 |
|
|
// attributes[] - empty; omit SourceFile attribute
|
956 |
|
|
// XXX should we mark this with a Synthetic attribute?
|
957 |
|
|
}
|
958 |
|
|
|
959 |
|
|
/**
|
960 |
|
|
* Produce the bytecode for a single method.
|
961 |
|
|
*
|
962 |
|
|
* @param i the index of the method we are building
|
963 |
|
|
* @param e the exceptions possible for the method
|
964 |
|
|
*/
|
965 |
|
|
private void emitMethod(int i, Class[] e)
|
966 |
|
|
{
|
967 |
|
|
// First, we precalculate the method length and other information.
|
968 |
|
|
|
969 |
|
|
Method m = methods[i];
|
970 |
|
|
Class[] paramtypes = m.getParameterTypes();
|
971 |
|
|
int wrap_overhead = 0; // max words taken by wrapped primitive
|
972 |
|
|
int param_count = 1; // 1 for this
|
973 |
|
|
int code_length = 16; // aload_0, getfield, aload_0, getstatic, const,
|
974 |
|
|
// aaload, const/aconst_null, invokeinterface
|
975 |
|
|
if (i > 5)
|
976 |
|
|
{
|
977 |
|
|
if (i > Byte.MAX_VALUE)
|
978 |
|
|
code_length += 2; // sipush
|
979 |
|
|
else
|
980 |
|
|
code_length++; // bipush
|
981 |
|
|
}
|
982 |
|
|
if (paramtypes.length > 0)
|
983 |
|
|
{
|
984 |
|
|
code_length += 3; // anewarray
|
985 |
|
|
if (paramtypes.length > Byte.MAX_VALUE)
|
986 |
|
|
code_length += 2; // sipush
|
987 |
|
|
else if (paramtypes.length > 5)
|
988 |
|
|
code_length++; // bipush
|
989 |
|
|
for (int j = 0; j < paramtypes.length; j++)
|
990 |
|
|
{
|
991 |
|
|
code_length += 4; // dup, const, load, store
|
992 |
|
|
Class type = paramtypes[j];
|
993 |
|
|
if (j > 5)
|
994 |
|
|
{
|
995 |
|
|
if (j > Byte.MAX_VALUE)
|
996 |
|
|
code_length += 2; // sipush
|
997 |
|
|
else
|
998 |
|
|
code_length++; // bipush
|
999 |
|
|
}
|
1000 |
|
|
if (param_count >= 4)
|
1001 |
|
|
code_length++; // 2-byte load
|
1002 |
|
|
param_count++;
|
1003 |
|
|
if (type.isPrimitive())
|
1004 |
|
|
{
|
1005 |
|
|
code_length += 7; // new, dup, invokespecial
|
1006 |
|
|
if (type == long.class || type == double.class)
|
1007 |
|
|
{
|
1008 |
|
|
wrap_overhead = 3;
|
1009 |
|
|
param_count++;
|
1010 |
|
|
}
|
1011 |
|
|
else if (wrap_overhead < 2)
|
1012 |
|
|
wrap_overhead = 2;
|
1013 |
|
|
}
|
1014 |
|
|
}
|
1015 |
|
|
}
|
1016 |
|
|
int end_pc = code_length;
|
1017 |
|
|
Class ret_type = m.getReturnType();
|
1018 |
|
|
if (ret_type == void.class)
|
1019 |
|
|
code_length++; // return
|
1020 |
|
|
else if (ret_type.isPrimitive())
|
1021 |
|
|
code_length += 7; // cast, invokevirtual, return
|
1022 |
|
|
else
|
1023 |
|
|
code_length += 4; // cast, return
|
1024 |
|
|
int exception_count = 0;
|
1025 |
|
|
boolean throws_throwable = false;
|
1026 |
|
|
for (int j = 0; j < e.length; j++)
|
1027 |
|
|
if (e[j] == Throwable.class)
|
1028 |
|
|
{
|
1029 |
|
|
throws_throwable = true;
|
1030 |
|
|
break;
|
1031 |
|
|
}
|
1032 |
|
|
if (! throws_throwable)
|
1033 |
|
|
{
|
1034 |
|
|
exception_count = e.length + 3; // Throwable, Error, RuntimeException
|
1035 |
|
|
code_length += 9; // new, dup_x1, swap, invokespecial, athrow
|
1036 |
|
|
}
|
1037 |
|
|
int handler_pc = code_length - 1;
|
1038 |
|
|
CPStringBuilder signature = new CPStringBuilder("(");
|
1039 |
|
|
for (int j = 0; j < paramtypes.length; j++)
|
1040 |
|
|
signature.append(TypeSignature.getEncodingOfClass(paramtypes[j]));
|
1041 |
|
|
signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type));
|
1042 |
|
|
|
1043 |
|
|
// Now we have enough information to emit the method.
|
1044 |
|
|
|
1045 |
|
|
// handler.access_flags
|
1046 |
|
|
putU2(Modifier.PUBLIC | Modifier.FINAL);
|
1047 |
|
|
// handler.name_index
|
1048 |
|
|
putU2(utf8Info(m.getName()));
|
1049 |
|
|
// handler.descriptor_index
|
1050 |
|
|
putU2(utf8Info(signature.toString()));
|
1051 |
|
|
// handler.attributes_count - Code is necessary, Exceptions possible
|
1052 |
|
|
putU2(e.length > 0 ? 2 : 1);
|
1053 |
|
|
|
1054 |
|
|
// handler.Code.info:
|
1055 |
|
|
// type name(args) {
|
1056 |
|
|
// try {
|
1057 |
|
|
// return (type) h.invoke(this, methods[i], new Object[] {args});
|
1058 |
|
|
// } catch (<declared Exceptions> e) {
|
1059 |
|
|
// throw e;
|
1060 |
|
|
// } catch (Throwable t) {
|
1061 |
|
|
// throw new UndeclaredThrowableException(t);
|
1062 |
|
|
// }
|
1063 |
|
|
// }
|
1064 |
|
|
// Special cases:
|
1065 |
|
|
// if arg_n is primitive, wrap it
|
1066 |
|
|
// if method throws Throwable, try-catch is not needed
|
1067 |
|
|
// if method returns void, return statement not needed
|
1068 |
|
|
// if method returns primitive, unwrap it
|
1069 |
|
|
// save space by sharing code for all the declared handlers
|
1070 |
|
|
|
1071 |
|
|
// handler.Code.attribute_name_index
|
1072 |
|
|
putU2(utf8Info("Code"));
|
1073 |
|
|
// handler.Code.attribute_length
|
1074 |
|
|
putU4(12 + code_length + 8 * exception_count);
|
1075 |
|
|
// handler.Code.max_stack
|
1076 |
|
|
putU2(param_count == 1 ? 4 : 7 + wrap_overhead);
|
1077 |
|
|
// handler.Code.max_locals
|
1078 |
|
|
putU2(param_count);
|
1079 |
|
|
// handler.Code.code_length
|
1080 |
|
|
putU4(code_length);
|
1081 |
|
|
// handler.Code.code[]
|
1082 |
|
|
putU1(ALOAD_0);
|
1083 |
|
|
putU1(GETFIELD);
|
1084 |
|
|
putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h",
|
1085 |
|
|
"Ljava/lang/reflect/InvocationHandler;"));
|
1086 |
|
|
putU1(ALOAD_0);
|
1087 |
|
|
putU1(GETSTATIC);
|
1088 |
|
|
putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false),
|
1089 |
|
|
"m", "[Ljava/lang/reflect/Method;"));
|
1090 |
|
|
putConst(i);
|
1091 |
|
|
putU1(AALOAD);
|
1092 |
|
|
if (paramtypes.length > 0)
|
1093 |
|
|
{
|
1094 |
|
|
putConst(paramtypes.length);
|
1095 |
|
|
putU1(ANEWARRAY);
|
1096 |
|
|
putU2(classInfo("java/lang/Object"));
|
1097 |
|
|
param_count = 1;
|
1098 |
|
|
for (int j = 0; j < paramtypes.length; j++, param_count++)
|
1099 |
|
|
{
|
1100 |
|
|
putU1(DUP);
|
1101 |
|
|
putConst(j);
|
1102 |
|
|
if (paramtypes[j].isPrimitive())
|
1103 |
|
|
{
|
1104 |
|
|
putU1(NEW);
|
1105 |
|
|
putU2(classInfo(wrapper(paramtypes[j])));
|
1106 |
|
|
putU1(DUP);
|
1107 |
|
|
}
|
1108 |
|
|
putLoad(param_count, paramtypes[j]);
|
1109 |
|
|
if (paramtypes[j].isPrimitive())
|
1110 |
|
|
{
|
1111 |
|
|
putU1(INVOKESPECIAL);
|
1112 |
|
|
putU2(refInfo(METHOD, wrapper(paramtypes[j]), "<init>",
|
1113 |
|
|
'(' + (TypeSignature
|
1114 |
|
|
.getEncodingOfClass(paramtypes[j])
|
1115 |
|
|
+ ")V")));
|
1116 |
|
|
if (paramtypes[j] == long.class
|
1117 |
|
|
|| paramtypes[j] == double.class)
|
1118 |
|
|
param_count++;
|
1119 |
|
|
}
|
1120 |
|
|
putU1(AASTORE);
|
1121 |
|
|
}
|
1122 |
|
|
}
|
1123 |
|
|
else
|
1124 |
|
|
putU1(ACONST_NULL);
|
1125 |
|
|
putU1(INVOKEINTERFACE);
|
1126 |
|
|
putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler",
|
1127 |
|
|
"invoke", INVOKE_SIG));
|
1128 |
|
|
putU1(4); // InvocationHandler, this, Method, Object[]
|
1129 |
|
|
putU1(0);
|
1130 |
|
|
if (ret_type == void.class)
|
1131 |
|
|
putU1(RETURN);
|
1132 |
|
|
else if (ret_type.isPrimitive())
|
1133 |
|
|
{
|
1134 |
|
|
putU1(CHECKCAST);
|
1135 |
|
|
putU2(classInfo(wrapper(ret_type)));
|
1136 |
|
|
putU1(INVOKEVIRTUAL);
|
1137 |
|
|
putU2(refInfo(METHOD, wrapper(ret_type),
|
1138 |
|
|
ret_type.getName() + "Value",
|
1139 |
|
|
"()" + TypeSignature.getEncodingOfClass(ret_type)));
|
1140 |
|
|
if (ret_type == long.class)
|
1141 |
|
|
putU1(LRETURN);
|
1142 |
|
|
else if (ret_type == float.class)
|
1143 |
|
|
putU1(FRETURN);
|
1144 |
|
|
else if (ret_type == double.class)
|
1145 |
|
|
putU1(DRETURN);
|
1146 |
|
|
else
|
1147 |
|
|
putU1(IRETURN);
|
1148 |
|
|
}
|
1149 |
|
|
else
|
1150 |
|
|
{
|
1151 |
|
|
putU1(CHECKCAST);
|
1152 |
|
|
putU2(classInfo(ret_type));
|
1153 |
|
|
putU1(ARETURN);
|
1154 |
|
|
}
|
1155 |
|
|
if (! throws_throwable)
|
1156 |
|
|
{
|
1157 |
|
|
putU1(NEW);
|
1158 |
|
|
putU2(classInfo("java/lang/reflect/UndeclaredThrowableException"));
|
1159 |
|
|
putU1(DUP_X1);
|
1160 |
|
|
putU1(SWAP);
|
1161 |
|
|
putU1(INVOKESPECIAL);
|
1162 |
|
|
putU2(refInfo(METHOD,
|
1163 |
|
|
"java/lang/reflect/UndeclaredThrowableException",
|
1164 |
|
|
"<init>", "(Ljava/lang/Throwable;)V"));
|
1165 |
|
|
putU1(ATHROW);
|
1166 |
|
|
}
|
1167 |
|
|
|
1168 |
|
|
// handler.Code.exception_table_length
|
1169 |
|
|
putU2(exception_count);
|
1170 |
|
|
// handler.Code.exception_table[]
|
1171 |
|
|
if (! throws_throwable)
|
1172 |
|
|
{
|
1173 |
|
|
// handler.Code.exception_table.start_pc
|
1174 |
|
|
putU2(0);
|
1175 |
|
|
// handler.Code.exception_table.end_pc
|
1176 |
|
|
putU2(end_pc);
|
1177 |
|
|
// handler.Code.exception_table.handler_pc
|
1178 |
|
|
putU2(handler_pc);
|
1179 |
|
|
// handler.Code.exception_table.catch_type
|
1180 |
|
|
putU2(classInfo("java/lang/Error"));
|
1181 |
|
|
// handler.Code.exception_table.start_pc
|
1182 |
|
|
putU2(0);
|
1183 |
|
|
// handler.Code.exception_table.end_pc
|
1184 |
|
|
putU2(end_pc);
|
1185 |
|
|
// handler.Code.exception_table.handler_pc
|
1186 |
|
|
putU2(handler_pc);
|
1187 |
|
|
// handler.Code.exception_table.catch_type
|
1188 |
|
|
putU2(classInfo("java/lang/RuntimeException"));
|
1189 |
|
|
for (int j = 0; j < e.length; j++)
|
1190 |
|
|
{
|
1191 |
|
|
// handler.Code.exception_table.start_pc
|
1192 |
|
|
putU2(0);
|
1193 |
|
|
// handler.Code.exception_table.end_pc
|
1194 |
|
|
putU2(end_pc);
|
1195 |
|
|
// handler.Code.exception_table.handler_pc
|
1196 |
|
|
putU2(handler_pc);
|
1197 |
|
|
// handler.Code.exception_table.catch_type
|
1198 |
|
|
putU2(classInfo(e[j]));
|
1199 |
|
|
}
|
1200 |
|
|
// handler.Code.exception_table.start_pc
|
1201 |
|
|
putU2(0);
|
1202 |
|
|
// handler.Code.exception_table.end_pc
|
1203 |
|
|
putU2(end_pc);
|
1204 |
|
|
// handler.Code.exception_table.handler_pc -
|
1205 |
|
|
// -8 for undeclared handler, which falls thru to normal one
|
1206 |
|
|
putU2(handler_pc - 8);
|
1207 |
|
|
// handler.Code.exception_table.catch_type
|
1208 |
|
|
putU2(0);
|
1209 |
|
|
}
|
1210 |
|
|
// handler.Code.attributes_count
|
1211 |
|
|
putU2(0);
|
1212 |
|
|
// handler.Code.attributes[]
|
1213 |
|
|
|
1214 |
|
|
if (e.length > 0)
|
1215 |
|
|
{
|
1216 |
|
|
// handler.Exceptions.attribute_name_index
|
1217 |
|
|
putU2(utf8Info("Exceptions"));
|
1218 |
|
|
// handler.Exceptions.attribute_length
|
1219 |
|
|
putU4(2 * e.length + 2);
|
1220 |
|
|
// handler.Exceptions.number_of_exceptions
|
1221 |
|
|
putU2(e.length);
|
1222 |
|
|
// handler.Exceptions.exception_index_table[]
|
1223 |
|
|
for (int j = 0; j < e.length; j++)
|
1224 |
|
|
putU2(classInfo(e[j]));
|
1225 |
|
|
}
|
1226 |
|
|
}
|
1227 |
|
|
|
1228 |
|
|
/**
|
1229 |
|
|
* Creates the Class object that corresponds to the bytecode buffers
|
1230 |
|
|
* built when this object was constructed.
|
1231 |
|
|
*
|
1232 |
|
|
* @param loader the class loader to define the proxy class in; null
|
1233 |
|
|
* implies the bootstrap class loader
|
1234 |
|
|
* @return the proxy class Class object
|
1235 |
|
|
*/
|
1236 |
|
|
Class generate(ClassLoader loader)
|
1237 |
|
|
{
|
1238 |
|
|
byte[] bytecode = new byte[pool.length() + stream.length()];
|
1239 |
|
|
// More efficient to bypass calling charAt() repetitively.
|
1240 |
|
|
char[] c = pool.toString().toCharArray();
|
1241 |
|
|
int i = c.length;
|
1242 |
|
|
while (--i >= 0)
|
1243 |
|
|
bytecode[i] = (byte) c[i];
|
1244 |
|
|
c = stream.toString().toCharArray();
|
1245 |
|
|
i = c.length;
|
1246 |
|
|
int j = bytecode.length;
|
1247 |
|
|
while (i > 0)
|
1248 |
|
|
bytecode[--j] = (byte) c[--i];
|
1249 |
|
|
|
1250 |
|
|
// Patch the constant pool size, which we left at 0 earlier.
|
1251 |
|
|
int count = poolEntries.size() + 1;
|
1252 |
|
|
bytecode[8] = (byte) (count >> 8);
|
1253 |
|
|
bytecode[9] = (byte) count;
|
1254 |
|
|
|
1255 |
|
|
try
|
1256 |
|
|
{
|
1257 |
|
|
Class vmClassLoader = Class.forName("java.lang.VMClassLoader");
|
1258 |
|
|
Class[] types = {ClassLoader.class, String.class,
|
1259 |
|
|
byte[].class, int.class, int.class,
|
1260 |
|
|
ProtectionDomain.class };
|
1261 |
|
|
Method m = vmClassLoader.getDeclaredMethod("defineClass", types);
|
1262 |
|
|
// We can bypass the security check of setAccessible(true), since
|
1263 |
|
|
// we're in the same package.
|
1264 |
|
|
m.flag = true;
|
1265 |
|
|
|
1266 |
|
|
Object[] args = {loader, qualName, bytecode, Integer.valueOf(0),
|
1267 |
|
|
Integer.valueOf(bytecode.length),
|
1268 |
|
|
Object.class.getProtectionDomain() };
|
1269 |
|
|
Class clazz = (Class) m.invoke(null, args);
|
1270 |
|
|
|
1271 |
|
|
// Finally, initialize the m field of the proxy class, before
|
1272 |
|
|
// returning it.
|
1273 |
|
|
Field f = clazz.getDeclaredField("m");
|
1274 |
|
|
f.flag = true;
|
1275 |
|
|
// we can share the array, because it is not publicized
|
1276 |
|
|
f.set(null, methods);
|
1277 |
|
|
|
1278 |
|
|
return clazz;
|
1279 |
|
|
}
|
1280 |
|
|
catch (Exception e)
|
1281 |
|
|
{
|
1282 |
|
|
// assert false;
|
1283 |
|
|
throw (Error) new InternalError("Unexpected: " + e).initCause(e);
|
1284 |
|
|
}
|
1285 |
|
|
}
|
1286 |
|
|
|
1287 |
|
|
/**
|
1288 |
|
|
* Put a single byte on the stream.
|
1289 |
|
|
*
|
1290 |
|
|
* @param i the information to add (only lowest 8 bits are used)
|
1291 |
|
|
*/
|
1292 |
|
|
private void putU1(int i)
|
1293 |
|
|
{
|
1294 |
|
|
stream.append((char) i);
|
1295 |
|
|
}
|
1296 |
|
|
|
1297 |
|
|
/**
|
1298 |
|
|
* Put two bytes on the stream.
|
1299 |
|
|
*
|
1300 |
|
|
* @param i the information to add (only lowest 16 bits are used)
|
1301 |
|
|
*/
|
1302 |
|
|
private void putU2(int i)
|
1303 |
|
|
{
|
1304 |
|
|
stream.append((char) (i >> 8)).append((char) i);
|
1305 |
|
|
}
|
1306 |
|
|
|
1307 |
|
|
/**
|
1308 |
|
|
* Put four bytes on the stream.
|
1309 |
|
|
*
|
1310 |
|
|
* @param i the information to add (treated as unsigned)
|
1311 |
|
|
*/
|
1312 |
|
|
private void putU4(int i)
|
1313 |
|
|
{
|
1314 |
|
|
stream.append((char) (i >> 24)).append((char) (i >> 16));
|
1315 |
|
|
stream.append((char) (i >> 8)).append((char) i);
|
1316 |
|
|
}
|
1317 |
|
|
|
1318 |
|
|
/**
|
1319 |
|
|
* Put bytecode to load a constant integer on the stream. This only
|
1320 |
|
|
* needs to work for values less than Short.MAX_VALUE.
|
1321 |
|
|
*
|
1322 |
|
|
* @param i the int to add
|
1323 |
|
|
*/
|
1324 |
|
|
private void putConst(int i)
|
1325 |
|
|
{
|
1326 |
|
|
if (i >= -1 && i <= 5)
|
1327 |
|
|
putU1(ICONST_0 + i);
|
1328 |
|
|
else if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE)
|
1329 |
|
|
{
|
1330 |
|
|
putU1(BIPUSH);
|
1331 |
|
|
putU1(i);
|
1332 |
|
|
}
|
1333 |
|
|
else
|
1334 |
|
|
{
|
1335 |
|
|
putU1(SIPUSH);
|
1336 |
|
|
putU2(i);
|
1337 |
|
|
}
|
1338 |
|
|
}
|
1339 |
|
|
|
1340 |
|
|
/**
|
1341 |
|
|
* Put bytecode to load a given local variable on the stream.
|
1342 |
|
|
*
|
1343 |
|
|
* @param i the slot to load
|
1344 |
|
|
* @param type the base type of the load
|
1345 |
|
|
*/
|
1346 |
|
|
private void putLoad(int i, Class type)
|
1347 |
|
|
{
|
1348 |
|
|
int offset = 0;
|
1349 |
|
|
if (type == long.class)
|
1350 |
|
|
offset = 1;
|
1351 |
|
|
else if (type == float.class)
|
1352 |
|
|
offset = 2;
|
1353 |
|
|
else if (type == double.class)
|
1354 |
|
|
offset = 3;
|
1355 |
|
|
else if (! type.isPrimitive())
|
1356 |
|
|
offset = 4;
|
1357 |
|
|
if (i < 4)
|
1358 |
|
|
putU1(ILOAD_0 + 4 * offset + i);
|
1359 |
|
|
else
|
1360 |
|
|
{
|
1361 |
|
|
putU1(ILOAD + offset);
|
1362 |
|
|
putU1(i);
|
1363 |
|
|
}
|
1364 |
|
|
}
|
1365 |
|
|
|
1366 |
|
|
/**
|
1367 |
|
|
* Given a primitive type, return its wrapper class name.
|
1368 |
|
|
*
|
1369 |
|
|
* @param clazz the primitive type (but not void.class)
|
1370 |
|
|
* @return the internal form of the wrapper class name
|
1371 |
|
|
*/
|
1372 |
|
|
private String wrapper(Class clazz)
|
1373 |
|
|
{
|
1374 |
|
|
if (clazz == boolean.class)
|
1375 |
|
|
return "java/lang/Boolean";
|
1376 |
|
|
if (clazz == byte.class)
|
1377 |
|
|
return "java/lang/Byte";
|
1378 |
|
|
if (clazz == short.class)
|
1379 |
|
|
return "java/lang/Short";
|
1380 |
|
|
if (clazz == char.class)
|
1381 |
|
|
return "java/lang/Character";
|
1382 |
|
|
if (clazz == int.class)
|
1383 |
|
|
return "java/lang/Integer";
|
1384 |
|
|
if (clazz == long.class)
|
1385 |
|
|
return "java/lang/Long";
|
1386 |
|
|
if (clazz == float.class)
|
1387 |
|
|
return "java/lang/Float";
|
1388 |
|
|
if (clazz == double.class)
|
1389 |
|
|
return "java/lang/Double";
|
1390 |
|
|
// assert false;
|
1391 |
|
|
return null;
|
1392 |
|
|
}
|
1393 |
|
|
|
1394 |
|
|
/**
|
1395 |
|
|
* Returns the entry of this String in the Constant pool, adding it
|
1396 |
|
|
* if necessary.
|
1397 |
|
|
*
|
1398 |
|
|
* @param str the String to resolve
|
1399 |
|
|
* @return the index of the String in the constant pool
|
1400 |
|
|
*/
|
1401 |
|
|
private char utf8Info(String str)
|
1402 |
|
|
{
|
1403 |
|
|
String utf8 = toUtf8(str);
|
1404 |
|
|
int len = utf8.length();
|
1405 |
|
|
return poolIndex("\1" + (char) (len >> 8) + (char) (len & 0xff) + utf8);
|
1406 |
|
|
}
|
1407 |
|
|
|
1408 |
|
|
/**
|
1409 |
|
|
* Returns the entry of the appropriate class info structure in the
|
1410 |
|
|
* Constant pool, adding it if necessary.
|
1411 |
|
|
*
|
1412 |
|
|
* @param name the class name, in internal form
|
1413 |
|
|
* @return the index of the ClassInfo in the constant pool
|
1414 |
|
|
*/
|
1415 |
|
|
private char classInfo(String name)
|
1416 |
|
|
{
|
1417 |
|
|
char index = utf8Info(name);
|
1418 |
|
|
char[] c = {7, (char) (index >> 8), (char) (index & 0xff)};
|
1419 |
|
|
return poolIndex(new String(c));
|
1420 |
|
|
}
|
1421 |
|
|
|
1422 |
|
|
/**
|
1423 |
|
|
* Returns the entry of the appropriate class info structure in the
|
1424 |
|
|
* Constant pool, adding it if necessary.
|
1425 |
|
|
*
|
1426 |
|
|
* @param clazz the class type
|
1427 |
|
|
* @return the index of the ClassInfo in the constant pool
|
1428 |
|
|
*/
|
1429 |
|
|
private char classInfo(Class clazz)
|
1430 |
|
|
{
|
1431 |
|
|
return classInfo(TypeSignature.getEncodingOfClass(clazz.getName(),
|
1432 |
|
|
false));
|
1433 |
|
|
}
|
1434 |
|
|
|
1435 |
|
|
/**
|
1436 |
|
|
* Returns the entry of the appropriate fieldref, methodref, or
|
1437 |
|
|
* interfacemethodref info structure in the Constant pool, adding it
|
1438 |
|
|
* if necessary.
|
1439 |
|
|
*
|
1440 |
|
|
* @param structure FIELD, METHOD, or INTERFACE
|
1441 |
|
|
* @param clazz the class name, in internal form
|
1442 |
|
|
* @param name the simple reference name
|
1443 |
|
|
* @param type the type of the reference
|
1444 |
|
|
* @return the index of the appropriate Info structure in the constant pool
|
1445 |
|
|
*/
|
1446 |
|
|
private char refInfo(byte structure, String clazz, String name,
|
1447 |
|
|
String type)
|
1448 |
|
|
{
|
1449 |
|
|
char cindex = classInfo(clazz);
|
1450 |
|
|
char ntindex = nameAndTypeInfo(name, type);
|
1451 |
|
|
// relies on FIELD == 1, METHOD == 2, INTERFACE == 3
|
1452 |
|
|
char[] c = {(char) (structure + 8),
|
1453 |
|
|
(char) (cindex >> 8), (char) (cindex & 0xff),
|
1454 |
|
|
(char) (ntindex >> 8), (char) (ntindex & 0xff)};
|
1455 |
|
|
return poolIndex(new String(c));
|
1456 |
|
|
}
|
1457 |
|
|
|
1458 |
|
|
/**
|
1459 |
|
|
* Returns the entry of the appropriate nameAndTyperef info structure
|
1460 |
|
|
* in the Constant pool, adding it if necessary.
|
1461 |
|
|
*
|
1462 |
|
|
* @param name the simple name
|
1463 |
|
|
* @param type the reference type
|
1464 |
|
|
* @return the index of the NameAndTypeInfo structure in the constant pool
|
1465 |
|
|
*/
|
1466 |
|
|
private char nameAndTypeInfo(String name, String type)
|
1467 |
|
|
{
|
1468 |
|
|
char nindex = utf8Info(name);
|
1469 |
|
|
char tindex = utf8Info(type);
|
1470 |
|
|
char[] c = {12, (char) (nindex >> 8), (char) (nindex & 0xff),
|
1471 |
|
|
(char) (tindex >> 8), (char) (tindex & 0xff)};
|
1472 |
|
|
return poolIndex(new String(c));
|
1473 |
|
|
}
|
1474 |
|
|
|
1475 |
|
|
/**
|
1476 |
|
|
* Converts a regular string to a UTF8 string, where the upper byte
|
1477 |
|
|
* of every char is 0, and '\\u0000' is not in the string. This is
|
1478 |
|
|
* basically to use a String as a fancy byte[], and while it is less
|
1479 |
|
|
* efficient in memory use, it is easier for hashing.
|
1480 |
|
|
*
|
1481 |
|
|
* @param str the original, in straight unicode
|
1482 |
|
|
* @return a modified string, in UTF8 format in the low bytes
|
1483 |
|
|
*/
|
1484 |
|
|
private String toUtf8(String str)
|
1485 |
|
|
{
|
1486 |
|
|
final char[] ca = str.toCharArray();
|
1487 |
|
|
final int len = ca.length;
|
1488 |
|
|
|
1489 |
|
|
// Avoid object creation, if str is already fits UTF8.
|
1490 |
|
|
int i;
|
1491 |
|
|
for (i = 0; i < len; i++)
|
1492 |
|
|
if (ca[i] == 0 || ca[i] > '\u007f')
|
1493 |
|
|
break;
|
1494 |
|
|
if (i == len)
|
1495 |
|
|
return str;
|
1496 |
|
|
|
1497 |
|
|
final CPStringBuilder sb = new CPStringBuilder(str);
|
1498 |
|
|
sb.setLength(i);
|
1499 |
|
|
for ( ; i < len; i++)
|
1500 |
|
|
{
|
1501 |
|
|
final char c = ca[i];
|
1502 |
|
|
if (c > 0 && c <= '\u007f')
|
1503 |
|
|
sb.append(c);
|
1504 |
|
|
else if (c <= '\u07ff') // includes '\0'
|
1505 |
|
|
{
|
1506 |
|
|
sb.append((char) (0xc0 | (c >> 6)));
|
1507 |
|
|
sb.append((char) (0x80 | (c & 0x6f)));
|
1508 |
|
|
}
|
1509 |
|
|
else
|
1510 |
|
|
{
|
1511 |
|
|
sb.append((char) (0xe0 | (c >> 12)));
|
1512 |
|
|
sb.append((char) (0x80 | ((c >> 6) & 0x6f)));
|
1513 |
|
|
sb.append((char) (0x80 | (c & 0x6f)));
|
1514 |
|
|
}
|
1515 |
|
|
}
|
1516 |
|
|
return sb.toString();
|
1517 |
|
|
}
|
1518 |
|
|
|
1519 |
|
|
/**
|
1520 |
|
|
* Returns the location of a byte sequence (conveniently wrapped in
|
1521 |
|
|
* a String with all characters between \u0001 and \u00ff inclusive)
|
1522 |
|
|
* in the constant pool, adding it if necessary.
|
1523 |
|
|
*
|
1524 |
|
|
* @param sequence the byte sequence to look for
|
1525 |
|
|
* @return the index of the sequence
|
1526 |
|
|
* @throws IllegalArgumentException if this would make the constant
|
1527 |
|
|
* pool overflow
|
1528 |
|
|
*/
|
1529 |
|
|
private char poolIndex(String sequence)
|
1530 |
|
|
{
|
1531 |
|
|
Integer i = (Integer) poolEntries.get(sequence);
|
1532 |
|
|
if (i == null)
|
1533 |
|
|
{
|
1534 |
|
|
// pool starts at index 1
|
1535 |
|
|
int size = poolEntries.size() + 1;
|
1536 |
|
|
if (size >= 65535)
|
1537 |
|
|
throw new IllegalArgumentException("exceeds VM limitations");
|
1538 |
|
|
i = Integer.valueOf(size);
|
1539 |
|
|
poolEntries.put(sequence, i);
|
1540 |
|
|
pool.append(sequence);
|
1541 |
|
|
}
|
1542 |
|
|
return (char) i.intValue();
|
1543 |
|
|
}
|
1544 |
|
|
} // class ClassFactory
|
1545 |
|
|
}
|