1 /*
2 * The Apache Software License, Version 1.1
3 *
4 * Copyright (c) 2002 The Apache Software Foundation. All rights
5 * reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
25 *
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
30 *
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
34 *
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
53 */
54 package net.sf.cglib.proxy;
55
56 import java.io.ObjectStreamException;
57 import java.lang.reflect.*;
58 import java.util.*;
59 import net.sf.cglib.core.*;
60 import org.objectweb.asm.ClassVisitor;
61 import org.objectweb.asm.Label;
62 import org.objectweb.asm.Type;
63
64 class EnhancerEmitter extends ClassEmitter {
65 private static final String CONSTRUCTED_FIELD = "CGLIB$CONSTRUCTED";
66 private static final String THREAD_CALLBACKS_FIELD = "CGLIB$THREAD_CALLBACKS";
67
68 private static final String SET_THREAD_CALLBACKS_NAME = "CGLIB$SET_THREAD_CALLBACKS";
69
70 private static final Type ILLEGAL_STATE_EXCEPTION =
71 TypeUtils.parseType("IllegalStateException");
72 private static final Type ILLEGAL_ARGUMENT_EXCEPTION =
73 TypeUtils.parseType("IllegalArgumentException");
74 private static final Type THREAD_LOCAL =
75 TypeUtils.parseType("ThreadLocal");
76 private static final Type FACTORY =
77 TypeUtils.parseType("net.sf.cglib.proxy.Factory");
78 private static final Type CALLBACK =
79 TypeUtils.parseType("net.sf.cglib.proxy.Callback");
80 private static final Type CALLBACK_ARRAY =
81 TypeUtils.parseType("net.sf.cglib.proxy.Callback[]");
82
83 private static final Signature CSTRUCT_NULL =
84 TypeUtils.parseConstructor("");
85 private static final Signature SET_THREAD_CALLBACKS =
86 TypeUtils.parseSignature("void " + SET_THREAD_CALLBACKS_NAME + "(net.sf.cglib.proxy.Callback[])");
87 private static final Signature NEW_INSTANCE =
88 TypeUtils.parseSignature("Object newInstance(net.sf.cglib.proxy.Callback[])");
89 private static final Signature MULTIARG_NEW_INSTANCE =
90 TypeUtils.parseSignature("Object newInstance(Class[], Object[], net.sf.cglib.proxy.Callback[])");
91 private static final Signature SINGLE_NEW_INSTANCE =
92 TypeUtils.parseSignature("Object newInstance(net.sf.cglib.proxy.Callback)");
93 private static final Signature SET_CALLBACK =
94 TypeUtils.parseSignature("void setCallback(int, net.sf.cglib.proxy.Callback)");
95 private static final Signature GET_CALLBACK =
96 TypeUtils.parseSignature("net.sf.cglib.proxy.Callback getCallback(int)");
97 private static final Signature SET_CALLBACKS =
98 TypeUtils.parseSignature("void setCallbacks(net.sf.cglib.proxy.Callback[])");
99 private static final Signature GET_CALLBACKS =
100 TypeUtils.parseSignature("net.sf.cglib.proxy.Callback[] getCallbacks()");
101
102 private static final Signature THREAD_LOCAL_GET =
103 TypeUtils.parseSignature("Object get()");
104 private static final Signature THREAD_LOCAL_SET =
105 TypeUtils.parseSignature("void set(Object)");
106
107 private Class[] callbackTypes;
108
109 public EnhancerEmitter(ClassVisitor v,
110 String className,
111 Class superclass,
112 Class[] interfaces,
113 CallbackFilter filter,
114 Class[] callbackTypes,
115 boolean useFactory) throws Exception {
116 super(v);
117 if (superclass == null) {
118 superclass = Object.class;
119 }
120 this.callbackTypes = callbackTypes;
121
122 begin_class(Constants.ACC_PUBLIC,
123 className,
124 Type.getType(superclass),
125 (useFactory ?
126 TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
127 TypeUtils.getTypes(interfaces)),
128 Constants.SOURCE_FILE);
129
130 if (Modifier.isFinal(superclass.getModifiers()))
131 throw new IllegalArgumentException("Cannot subclass final class " + superclass);
132
133 List clist = new ArrayList(Arrays.asList(superclass.getDeclaredConstructors()));
134 CollectionUtils.filter(clist, new VisibilityPredicate(superclass, true));
135 if (clist.size() == 0) {
136 throw new IllegalArgumentException("No visible constructors in " + superclass);
137 }
138 Constructor[] constructors = (Constructor[])clist.toArray(new Constructor[clist.size()]);
139
140 // Order is very important: must add superclass, then
141 // its superclass chain, then each interface and
142 // its superinterfaces.
143 List methods = new ArrayList();
144 ReflectUtils.addAllMethods(superclass, methods);
145
146 List interfaceMethods = new ArrayList();
147 if (interfaces != null) {
148 for (int i = 0; i < interfaces.length; i++) {
149 if (interfaces[i] != Factory.class) {
150 ReflectUtils.addAllMethods(interfaces[i], interfaceMethods);
151 }
152 }
153 }
154 Set forcePublic = MethodWrapper.createSet(interfaceMethods);
155 methods.addAll(interfaceMethods);
156 CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.STATIC));
157 CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));
158 CollectionUtils.filter(methods, new DuplicatesPredicate());
159 CollectionUtils.filter(methods, new RejectModifierPredicate(Modifier.FINAL));
160
161 Map groups = new HashMap();
162 Map indexes = new HashMap();
163 for (Iterator it = methods.iterator(); it.hasNext();) {
164 Method method = (Method)it.next();
165 int index = filter.accept(method);
166 if (index >= callbackTypes.length) {
167 throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
168 }
169 indexes.put(method, new Integer(index));
170 Object gen = CallbackUtils.getGenerator(callbackTypes[index]);
171 List group = (List)groups.get(gen);
172 if (group == null) {
173 groups.put(gen, group = new ArrayList(methods.size()));
174 }
175 group.add(method);
176 }
177
178 emitMethods(groups, indexes, forcePublic);
179 emitConstructors(constructors);
180 emitSetThreadCallbacks();
181
182 if (useFactory) {
183 int[] keys = getCallbackKeys();
184 emitNewInstanceCallbacks();
185 emitNewInstanceCallback();
186 emitNewInstanceMultiarg(constructors);
187 emitGetCallback(keys);
188 emitSetCallback(keys);
189 emitGetCallbacks();
190 emitSetCallbacks();
191 }
192 end_class();
193 }
194
195 static void setThreadCallbacks(Class type, Callback[] callbacks) {
196 // TODO: optimize
197 try {
198 Method setter = type.getDeclaredMethod(SET_THREAD_CALLBACKS_NAME, new Class[]{ Callback[].class });
199 setter.invoke(null, new Object[]{ callbacks });
200 } catch (NoSuchMethodException e) {
201 throw new IllegalArgumentException(type + " is not an enhanced class");
202 } catch (IllegalAccessException e) {
203 throw new CodeGenerationException(e);
204 } catch (InvocationTargetException e) {
205 throw new CodeGenerationException(e);
206 }
207 }
208
209 private void emitConstructors(Constructor[] constructors) {
210 for (int i = 0; i < constructors.length; i++) {
211 Signature sig = ReflectUtils.getSignature(constructors[i]);
212 CodeEmitter e = begin_method(Constants.ACC_PUBLIC,
213 sig,
214 ReflectUtils.getExceptionTypes(constructors[i]),
215 null);
216 e.load_this();
217 e.dup();
218 e.load_args();
219 e.super_invoke_constructor(sig);
220 e.push(1);
221 e.putfield(CONSTRUCTED_FIELD);
222
223 e.load_this();
224 e.getfield(THREAD_CALLBACKS_FIELD);
225 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
226 e.dup();
227 Label skip = e.make_label();
228 e.ifnull(skip);
229
230 e.checkcast(CALLBACK_ARRAY);
231 for (int j = 0; j < callbackTypes.length; j++) {
232 e.dup2();
233 e.aaload(j);
234 e.checkcast(Type.getType(callbackTypes[j]));
235 e.putfield(getCallbackField(j));
236 }
237 e.mark(skip);
238
239 // clear thread-locals
240 e.getfield(THREAD_CALLBACKS_FIELD);
241 e.aconst_null();
242 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
243
244 e.return_value();
245 e.end_method();
246 }
247 }
248
249 private int[] getCallbackKeys() {
250 int[] keys = new int[callbackTypes.length];
251 for (int i = 0; i < callbackTypes.length; i++) {
252 keys[i] = i;
253 }
254 return keys;
255 }
256
257 private void emitGetCallback(int[] keys) {
258 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_CALLBACK, null, null);
259 e.load_this();
260 e.load_arg(0);
261 e.process_switch(keys, new ProcessSwitchCallback() {
262 public void processCase(int key, Label end) {
263 e.getfield(getCallbackField(key));
264 e.goTo(end);
265 }
266 public void processDefault() {
267 e.pop(); // stack height
268 e.aconst_null();
269 }
270 });
271 e.return_value();
272 e.end_method();
273 }
274
275 private void emitSetCallback(int[] keys) {
276 final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_CALLBACK, null, null);
277 e.load_this();
278 e.load_arg(1);
279 e.load_arg(0);
280 e.process_switch(keys, new ProcessSwitchCallback() {
281 public void processCase(int key, Label end) {
282 e.checkcast(Type.getType(callbackTypes[key]));
283 e.putfield(getCallbackField(key));
284 e.goTo(end);
285 }
286 public void processDefault() {
287 // TODO: error?
288 // stack height
289 e.pop2();
290 }
291 });
292 e.return_value();
293 e.end_method();
294 }
295
296 private void emitSetCallbacks() {
297 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SET_CALLBACKS, null, null);
298 e.load_this();
299 e.load_arg(0);
300 for (int i = 0; i < callbackTypes.length; i++) {
301 e.dup2();
302 e.aaload(i);
303 e.checkcast(Type.getType(callbackTypes[i]));
304 e.putfield(getCallbackField(i));
305 }
306 e.return_value();
307 e.end_method();
308 }
309
310 private void emitGetCallbacks() {
311 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_CALLBACKS, null, null);
312 e.load_this();
313 e.push(callbackTypes.length);
314 e.newarray(CALLBACK);
315 for (int i = 0; i < callbackTypes.length; i++) {
316 e.dup();
317 e.push(i);
318 e.load_this();
319 e.getfield(getCallbackField(i));
320 e.aastore();
321 }
322 e.return_value();
323 e.end_method();
324 }
325
326 private void emitNewInstanceCallbacks() {
327 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, NEW_INSTANCE, null, null);
328 e.load_arg(0);
329 e.invoke_static_this(SET_THREAD_CALLBACKS);
330 emitCommonNewInstance(e);
331 }
332
333 private void emitCommonNewInstance(CodeEmitter e) {
334 e.new_instance_this();
335 e.dup();
336 e.invoke_constructor_this();
337 e.return_value();
338 e.end_method();
339 }
340
341 private void emitNewInstanceCallback() {
342 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, SINGLE_NEW_INSTANCE, null, null);
343 switch (callbackTypes.length) {
344 case 0:
345 // TODO: make sure Callback is null
346 break;
347 case 1:
348 // for now just make a new array; TODO: optimize
349 e.push(1);
350 e.newarray(CALLBACK);
351 e.dup();
352 e.push(0);
353 e.load_arg(0);
354 e.aastore();
355 e.invoke_static_this(SET_THREAD_CALLBACKS);
356 break;
357 default:
358 e.throw_exception(ILLEGAL_STATE_EXCEPTION, "More than one callback object required");
359 }
360 emitCommonNewInstance(e);
361 }
362
363 private void emitNewInstanceMultiarg(Constructor[] constructors) {
364 CodeEmitter e = begin_method(Constants.ACC_PUBLIC, MULTIARG_NEW_INSTANCE, null, null);
365 e.load_arg(2);
366 e.invoke_static_this(SET_THREAD_CALLBACKS);
367 emitCommonMultiarg(constructors, e);
368 }
369
370 private void emitCommonMultiarg(Constructor[] constructors, final CodeEmitter e) {
371 e.new_instance_this();
372 e.dup();
373 e.load_arg(0);
374 EmitUtils.constructor_switch(e, constructors, new ObjectSwitchCallback() {
375 public void processCase(Object key, Label end) {
376 Constructor constructor = (Constructor)key;
377 Type types[] = TypeUtils.getTypes(constructor.getParameterTypes());
378 for (int i = 0; i < types.length; i++) {
379 e.load_arg(1);
380 e.push(i);
381 e.aaload();
382 e.unbox(types[i]);
383 }
384 e.invoke_constructor_this(ReflectUtils.getSignature(constructor));
385 e.goTo(end);
386 }
387 public void processDefault() {
388 e.throw_exception(ILLEGAL_ARGUMENT_EXCEPTION, "Constructor not found");
389 }
390 });
391 e.return_value();
392 e.end_method();
393 }
394
395 private void emitMethods(Map groups, final Map indexes, final Set forcePublic) throws Exception {
396 declare_field(Constants.ACC_PRIVATE, CONSTRUCTED_FIELD, Type.BOOLEAN_TYPE, null, null);
397 declare_field(Constants.PRIVATE_FINAL_STATIC, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null, null);
398
399 for (int i = 0; i < callbackTypes.length; i++) {
400 declare_field(Constants.ACC_PRIVATE, getCallbackField(i), Type.getType(callbackTypes[i]), null, null);
401 }
402
403 Set seenGen = new HashSet();
404 CodeEmitter e = begin_static();
405 e.new_instance(THREAD_LOCAL);
406 e.dup();
407 e.invoke_constructor(THREAD_LOCAL, CSTRUCT_NULL);
408 e.putfield(THREAD_CALLBACKS_FIELD);
409
410 for (int i = 0; i < callbackTypes.length; i++) {
411 CallbackGenerator gen = CallbackUtils.getGenerator(callbackTypes[i]);
412 if (!seenGen.contains(gen)) {
413 seenGen.add(gen);
414 final List fmethods = (List)groups.get(gen);
415 if (fmethods != null) {
416 CallbackGenerator.Context context = new CallbackGenerator.Context() {
417 public Iterator getMethods() {
418 return fmethods.iterator();
419 }
420 public int getIndex(Method method) {
421 return ((Integer)indexes.get(method)).intValue();
422 }
423 public void emitCallback(CodeEmitter e, int index) {
424 emitCurrentCallback(e, index);
425 }
426 public int getModifiers(Method method) {
427 int modifiers = Constants.ACC_FINAL
428 | (method.getModifiers()
429 & ~Constants.ACC_ABSTRACT
430 & ~Constants.ACC_NATIVE
431 & ~Constants.ACC_SYNCHRONIZED);
432 if (forcePublic.contains(MethodWrapper.create(method))) {
433 modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
434 }
435 return modifiers;
436 }
437 // TODO: this is probably slow
438 public String getUniqueName(Method method) {
439 return method.getName() + "_" + fmethods.indexOf(method);
440 }
441 };
442 gen.generate(this, context);
443 gen.generateStatic(e, context);
444 }
445 }
446 }
447 e.return_value();
448 e.end_method();
449 }
450
451 private void emitSetThreadCallbacks() {
452 CodeEmitter e = begin_method(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
453 SET_THREAD_CALLBACKS,
454 null,
455 null);
456 e.getfield(THREAD_CALLBACKS_FIELD);
457 e.load_arg(0);
458 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_SET);
459 e.return_value();
460 e.end_method();
461 }
462
463 private void emitCurrentCallback(CodeEmitter e, int index) {
464 e.load_this();
465 e.getfield(getCallbackField(index));
466 e.dup();
467 Label end = e.make_label();
468 e.ifnonnull(end);
469 e.load_this();
470 e.getfield(CONSTRUCTED_FIELD);
471 e.if_jump(e.NE, end);
472 e.pop();
473 e.getfield(THREAD_CALLBACKS_FIELD);
474 e.invoke_virtual(THREAD_LOCAL, THREAD_LOCAL_GET);
475 e.checkcast(CALLBACK_ARRAY);
476 e.push(index);
477 e.aaload();
478 e.checkcast(Type.getType(callbackTypes[index]));
479 e.mark(end);
480 }
481
482 private static String getCallbackField(int index) {
483 return "CGLIB$CALLBACK_" + index;
484 }
485 }
This page was automatically generated by Maven