Source code: com/chaoswg/xtc4y/AbstractClassImplementor.java
1 //$Header: /cvsroot/xtc4y/xtc4y/src/com/chaoswg/xtc4y/AbstractClassImplementor.java,v 1.1.1.1 2003/08/07 13:40:26 toggm Exp $
2 /******************************************************************************
3 * XTC4y - eXtreme Testing Collection 4 you *
4 * -------------------------------------------------------------------------- *
5 * URL: http://www.chaoswg.com/xtc4y *
6 * Author: Mike Toggweiler (2.dog@gmx.ch) *
7 * *
8 * Last Updated: $Date: 2003/08/07 13:40:26 $, by $Author: toggm $ *
9 * Version: $Revision: 1.1.1.1 $ *
10 * -------------------------------------------------------------------------- *
11 * COPYRIGHT: (c) 2003 by Mike Toggweiler *
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 *****************************************************************************/
18 package com.chaoswg.xtc4y;
19
20 import java.io.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.DataInputStream;
23 import java.io.DataOutputStream;
24 import java.io.FileOutputStream;
25 import java.io.InputStream;
26 import java.io.IOException;
27
28 import java.lang.reflect.Method;
29 import java.lang.reflect.Modifier;
30 import java.lang.reflect.Constructor;
31
32 import java.util.Vector;
33
34 import com.chaoswg.util.ExceptionPrinter;
35
36 import com.chaoswg.xtc4y.classloaders.ByteArrayClassLoader;
37 import com.chaoswg.xtc4y.classdesc.AttributeInfo;
38 import com.chaoswg.xtc4y.classdesc.Attribute;
39 import com.chaoswg.xtc4y.classdesc.SourceFileAttribute;
40 import com.chaoswg.xtc4y.classdesc.BaseType;
41 import com.chaoswg.xtc4y.classdesc.ClassCPEntry;
42 import com.chaoswg.xtc4y.classdesc.ClassDescriptor;
43 import com.chaoswg.xtc4y.classdesc.ClassAccessFlags;
44 import com.chaoswg.xtc4y.classdesc.CodeAttribute;
45 import com.chaoswg.xtc4y.classdesc.MethodAccessFlags;
46 import com.chaoswg.xtc4y.classdesc.MethodInfo;
47 import com.chaoswg.xtc4y.classdesc.NameAndTypeCPEntry;
48 import com.chaoswg.xtc4y.classdesc.MethodrefCPEntry;
49 import com.chaoswg.xtc4y.classdesc.code.Code;
50 import com.chaoswg.xtc4y.classdesc.code.instructions.UnaryInstruction;
51 import com.chaoswg.xtc4y.classdesc.code.instructions.Instruction;
52 import com.chaoswg.xtc4y.classdesc.code.instructions.InvokeSpecial;
53
54 /**
55 * This class is used to implement an abstract class for testing purposes.
56 * It creates a subclass of it which implements all abstract methods
57 * @author Mike Toggweiler
58 **/
59 public class AbstractClassImplementor {
60
61 private boolean writeGeneratedFile = false;
62 private ByteArrayClassLoader loader = new ByteArrayClassLoader();
63
64 /**
65 * Construct a new AbstractClassLoad
66 **/
67 public AbstractClassImplementor() {
68 }
69
70 /**
71 * Construct a new AbstractClassImplementor
72 * @param writeOutput true if generated output file should be written in
73 * a class file
74 **/
75 public AbstractClassImplementor(boolean writeOutput) {
76 writeGeneratedFile = writeOutput;
77 }
78
79 /**
80 * Get an implementation of the abstract class referenced by the name
81 * @param name the name of the class
82 * @return a class if found
83 * @exception ClassNotFoundException
84 **/
85 public Class getImplementedClass(String name)
86 throws ClassNotFoundException {
87 return getImplementedClass(Class.forName(name));
88 }
89
90 /**
91 * Get an implementation of the abstract class referenced by the class
92 * object
93 * @param orig the original class to implement
94 * @return a class if found
95 * @exception ClassNotFoundException
96 **/
97 public Class getImplementedClass(Class orig)
98 throws ClassNotFoundException {
99 try {
100 if (orig.isInterface()) {
101 //TBD, implement support for interfaces
102 //return getImplementedIfcClass(orig);
103 return null;
104 }
105
106 int mod = orig.getModifiers();
107 if (!Modifier.isAbstract(mod)) {
108 return orig;
109 }
110
111
112
113 ClassDescriptor descriptor = new ClassDescriptor();
114
115 ClassAccessFlags classAccess = new ClassAccessFlags((short)0);
116 classAccess.setIsPublic(Modifier.isPublic(mod));
117 classAccess.setIsAbstract(false);
118 descriptor.setClassAccess(classAccess);
119
120 //set class structure
121 String name = orig.getName();
122 name = name.replace('.','/');
123 ClassCPEntry superCl = new ClassCPEntry(name);
124 descriptor.setSuperClass(superCl);
125 name += "_impl";
126 descriptor.setThisClass(new ClassCPEntry(name));
127
128 //implement all abstract methods
129 Method[] methods = orig.getMethods();
130 int mmod;
131 for (int i=0; i<methods.length; ++i) {
132 mmod = methods[i].getModifiers();
133 if (Modifier.isAbstract(mmod)) {
134 MethodInfo meth = getMethodImplementation(methods[i]);
135 System.out.println("Add method:"+meth);
136 descriptor.addMethod(meth);
137 }
138 }
139
140 //add all declared constructors
141 Constructor[] constr = orig.getDeclaredConstructors();
142 for (int i=0; i<constr.length; ++i) {
143 MethodInfo cons = getConstructorImplementation(constr[i],
144 superCl);
145 if (cons != null) {
146 System.out.println("Add constructor:"+cons);
147 //null if constructor was private
148 descriptor.addMethod(cons);
149 }
150 }
151
152 //print new class into byte array
153 ByteArrayOutputStream baos = new ByteArrayOutputStream();
154 DataOutputStream dos = new DataOutputStream(baos);
155
156 //write new class
157 descriptor.writeClass(dos);
158
159 byte[] bytes = baos.toByteArray();
160
161 if (writeGeneratedFile) {
162 writeClassFile(bytes, name);
163 }
164
165 dos.close();
166 baos.close();
167
168 System.out.println("Define class:"+name);
169 return loader.defineClass1(name, bytes, 0, bytes.length);
170 }
171 catch (IOException ioe) {
172 System.out.println("Couldn't load class data:"+
173 ExceptionPrinter.getStackTrace(ioe));
174 }
175 throw new ClassNotFoundException();
176 }
177
178 /**
179 * @return a descriptor concatenated of an array of classes and the
180 * return type
181 **/
182 private String getDescriptor(Class[] params, Class ret) {
183 String desc = "(";
184 for (int i=0; i<params.length; ++i) {
185 desc += getBaseType(params[i]);
186 }
187
188 desc += ")";
189 //add return type
190 if (ret != null) {
191 desc += getBaseType(ret);
192 }
193
194 return desc;
195 }
196
197 /**
198 * @return the string representation of the basetype of a given class
199 **/
200 private String getBaseType(Class cl) {
201 System.out.println("Check class:"+cl+":"+cl.isPrimitive());
202 if (cl.isPrimitive()) {
203 if (Integer.TYPE.isAssignableFrom(cl)) {
204 return BaseType.INT;
205 }
206 else if (Short.TYPE.isAssignableFrom(cl)) {
207 return BaseType.SHORT;
208 }
209 else if (Byte.TYPE.isAssignableFrom(cl)) {
210 return BaseType.BYTE;
211 }
212 else if (Character.TYPE.isAssignableFrom(cl)) {
213 return BaseType.CHAR;
214 }
215 else if (Long.TYPE.isAssignableFrom(cl)) {
216 return BaseType.LONG;
217 }
218 else if (Float.TYPE.isAssignableFrom(cl)) {
219 return BaseType.FLOAT;
220 }
221 else if (Double.TYPE.isAssignableFrom(cl)) {
222 return BaseType.DOUBLE;
223 }
224 else if (Boolean.TYPE.isAssignableFrom(cl)) {
225 return BaseType.BOOLEAN;
226 }
227 else if (Void.TYPE.isAssignableFrom(cl)) {
228 return BaseType.VOID;
229 }
230 return null;
231 }
232 else if (cl.isArray()) {
233 return cl.getName();
234 }
235 System.out.println("Class is wheter an array nor primitive:"+cl);
236 //in all other cases assuem that the class is an object
237 String name = cl.getName();
238 name = name.replace('.', '/');
239 return BaseType.REFERENCE + name + ";";
240 }
241
242 /**
243 * @return a constructor implementation with a call to the super
244 * constructor
245 **/
246 private MethodInfo getConstructorImplementation(Constructor cons,
247 ClassCPEntry superCl) {
248 int mod = cons.getModifiers();
249 MethodAccessFlags access = new MethodAccessFlags((short)0);
250 if (Modifier.isPrivate(mod)) {
251 return null;
252 }
253 else if (Modifier.isPublic(mod)) {
254 access.setPublic();
255 }
256 else if (Modifier.isProtected(mod)) {
257 access.setProtected();
258 }
259 access.setIsSynchronized(Modifier.isSynchronized(mod));
260 //TBD: check if other flags are allowed on a constructor
261
262 String desc = getDescriptor(cons.getParameterTypes(), Void.TYPE);
263
264 MethodInfo consImpl = new MethodInfo(access,
265 MethodInfo.CONSTRUCTOR_NAME,
266 desc);
267
268 //add code implementation which calls super constructor
269 Code code = new Code();
270 Class[] params = cons.getParameterTypes();
271 int index = 0;
272 //load instructions
273 code.addInstruction(getLoadInstruction(Object.class, index++));
274 for (int i=0; i<params.length; ++i) {
275 code.addInstruction(getLoadInstruction(params[i], index++));
276 }
277 //invokespecial instruction
278 NameAndTypeCPEntry nat =
279 new NameAndTypeCPEntry(MethodInfo.CONSTRUCTOR_NAME, desc);
280
281 MethodrefCPEntry methodr = new MethodrefCPEntry(superCl, nat);
282 code.addInstruction(new InvokeSpecial(methodr));
283 //add a return depending on the return type
284 code.addInstruction(getReturnInstruction(Void.TYPE));
285
286 consImpl.addAttribute(new AttributeInfo(new CodeAttribute(code)));
287
288 //add exception attributes
289
290 return consImpl;
291 }
292
293 /**
294 * @return a load instruction dependant on the basetype
295 **/
296 private Instruction getLoadInstruction(Class type, int index) {
297 if (!type.isPrimitive() && !type.isArray()) {
298 return new UnaryInstruction((byte)
299 (UnaryInstruction.ALOAD_0+index));
300 }
301 else if (type.isArray()) {
302 return getArrayLoadInstruction(type);
303 }
304 else if (Double.TYPE.isAssignableFrom(type)) {
305 return new UnaryInstruction((byte)
306 (UnaryInstruction.DLOAD_0+index));
307 }
308 else if (Float.TYPE.isAssignableFrom(type)) {
309 return new UnaryInstruction((byte)
310 (UnaryInstruction.FLOAD_0+index));
311 }
312 else if (Long.TYPE.isAssignableFrom(type)) {
313 return new UnaryInstruction((byte)
314 (UnaryInstruction.LLOAD_0+index));
315 }
316 else {
317 return new UnaryInstruction((byte)
318 (UnaryInstruction.ILOAD_0+index));
319 }
320 }
321
322 /**
323 * @return a load instruction dependant on the basetype array
324 **/
325 private Instruction getArrayLoadInstruction(Class type) {
326 if (!type.isPrimitive()) {
327 return new UnaryInstruction(UnaryInstruction.AALOAD);
328 }
329 else if (Double.TYPE.isAssignableFrom(type)) {
330 return new UnaryInstruction(UnaryInstruction.DALOAD);
331 }
332 else if (Float.TYPE.isAssignableFrom(type)) {
333 return new UnaryInstruction(UnaryInstruction.FALOAD);
334 }
335 else if (Long.TYPE.isAssignableFrom(type)) {
336 return new UnaryInstruction(UnaryInstruction.LALOAD);
337 }
338 else if (Integer.TYPE.isAssignableFrom(type)) {
339 return new UnaryInstruction(UnaryInstruction.IALOAD);
340 }
341 else if (Short.TYPE.isAssignableFrom(type)) {
342 return new UnaryInstruction(UnaryInstruction.SALOAD);
343 }
344 else if (Byte.TYPE.isAssignableFrom(type)) {
345 return new UnaryInstruction(UnaryInstruction.BALOAD);
346 }
347 else if (Character.TYPE.isAssignableFrom(type)) {
348 return new UnaryInstruction(UnaryInstruction.CALOAD);
349 }
350 else if (Boolean.TYPE.isAssignableFrom(type)) {
351 return new UnaryInstruction(UnaryInstruction.IALOAD);
352 }
353 else {
354 throw new ClassFormatError("Array Descriptor type unknown:"+type);
355 }
356 }
357
358 /**
359 * @return a return instruction depending on the return type
360 **/
361 private Instruction getReturnInstruction(Class ret) {
362 if (ret.isArray() || !ret.isPrimitive()) {
363 return new UnaryInstruction(UnaryInstruction.ARETURN);
364 }
365 else if (Void.TYPE.isAssignableFrom(ret)) {
366 return new UnaryInstruction(UnaryInstruction.RETURN);
367 }
368 else if (Double.TYPE.isAssignableFrom(ret)) {
369 return new UnaryInstruction(UnaryInstruction.DRETURN);
370 }
371 else if (Float.TYPE.isAssignableFrom(ret)) {
372 return new UnaryInstruction(UnaryInstruction.FRETURN);
373 }
374 else if (Long.TYPE.isAssignableFrom(ret)) {
375 return new UnaryInstruction(UnaryInstruction.LRETURN);
376 }
377 else {
378 return new UnaryInstruction(UnaryInstruction.IRETURN);
379 }
380 }
381
382 /**
383 * @return a default implementation for an abstract method
384 **/
385 private MethodInfo getMethodImplementation(Method method) {
386
387 String desc = getDescriptor(method.getParameterTypes(),
388 method.getReturnType());
389
390 MethodAccessFlags access = new MethodAccessFlags((short)0);
391 access.setIsAbstract(false);
392 int mmod = method.getModifiers();
393 if (Modifier.isPublic(mmod)) {
394 access.setPublic();
395 }
396 else if (Modifier.isProtected(mmod)) {
397 access.setProtected();
398 }
399 //all other flags are not allowed when declared abstract
400
401 System.out.println("IMplement methodS:"+method.getName());
402 MethodInfo methodImpl = new MethodInfo(access, method.getName(), desc);
403
404 //all thrown exception where ignored, because no
405 //implementation throws an exception
406
407 //add code attribute
408 methodImpl.
409 addAttribute(new
410 AttributeInfo(getDefaultAttribute(method.
411 getReturnType())));
412
413 return methodImpl;
414 }
415
416 /**
417 * @return the default implementation of a codeattribute. This
418 * method should be averwritten to change the default implementation
419 * of abstract methods
420 * @param retType string of the basetype of the return type (see §4.3.2)
421 * @return a CodeAttribute
422 **/
423 protected CodeAttribute getDefaultAttribute(Class ret) {
424 Code code = new Code();
425 if (ret.isArray() || !ret.isPrimitive()) {
426 //it is an object or an array, return just null
427 //push null onto the stack
428 //print areturn
429 code.
430 addInstruction(new
431 UnaryInstruction(UnaryInstruction.ACONST_NULL));
432 }
433 else if (Void.TYPE.isAssignableFrom(ret)) {
434 //void return type
435 //print return
436 }
437 else if (Double.TYPE.isAssignableFrom(ret)) {
438 //double return 0
439 //push 0 onto the stack (dconst_0)
440 //print dreturn
441 code.addInstruction(new
442 UnaryInstruction(UnaryInstruction.DCONST_0));
443 }
444 else if (Float.TYPE.isAssignableFrom(ret)) {
445 //float return 0
446 //push 0 onto the stack (fconst_0)
447 //print freturn
448 code.addInstruction(new
449 UnaryInstruction(UnaryInstruction.FCONST_0));
450 }
451 else if (Long.TYPE.isAssignableFrom(ret)) {
452 //long return 0
453 //push 0 onto the stack (lconst_0)
454 //print lreturn
455 code.addInstruction(new
456 UnaryInstruction(UnaryInstruction.LCONST_0));
457 }
458 else {
459 //boolean, char, short, int, byte
460 //return 0
461 //push 0 onto the stack (iconst_0)
462 //print ireturn
463 code.addInstruction(new
464 UnaryInstruction(UnaryInstruction.ICONST_0));
465 }
466
467 code.addInstruction(getReturnInstruction(ret));
468 return new CodeAttribute(code);
469 }
470
471 /**
472 * Write an array of bytes into an output file
473 * @param bytes the byte array to write
474 * @param name the classname
475 **/
476 private void writeClassFile(byte[] bytes, String name) throws IOException {
477 System.out.println("Write output file to:"+name);
478 FileOutputStream fileoutputstream =
479 new FileOutputStream(name + ".class");
480 fileoutputstream.write(bytes);
481 fileoutputstream.close();
482 }
483 }