KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > flow > java > ContinuationClassLoader


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.flow.java;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.Vector JavaDoc;
20
21 import org.apache.bcel.Constants;
22 import org.apache.bcel.Repository;
23 import org.apache.bcel.classfile.ConstantCP;
24 import org.apache.bcel.classfile.ConstantNameAndType;
25 import org.apache.bcel.classfile.ConstantPool;
26 import org.apache.bcel.classfile.ConstantUtf8;
27 import org.apache.bcel.classfile.JavaClass;
28 import org.apache.bcel.classfile.Method;
29 import org.apache.bcel.generic.ACONST_NULL;
30 import org.apache.bcel.generic.BasicType;
31 import org.apache.bcel.generic.ClassGen;
32 import org.apache.bcel.generic.ConstantPoolGen;
33 import org.apache.bcel.generic.GOTO;
34 import org.apache.bcel.generic.IFEQ;
35 import org.apache.bcel.generic.IFNONNULL;
36 import org.apache.bcel.generic.IFNULL;
37 import org.apache.bcel.generic.INVOKESTATIC;
38 import org.apache.bcel.generic.InstructionConstants;
39 import org.apache.bcel.generic.InstructionFactory;
40 import org.apache.bcel.generic.InstructionHandle;
41 import org.apache.bcel.generic.InstructionList;
42 import org.apache.bcel.generic.InstructionTargeter;
43 import org.apache.bcel.generic.InvokeInstruction;
44 import org.apache.bcel.generic.MethodGen;
45 import org.apache.bcel.generic.ObjectType;
46 import org.apache.bcel.generic.PUSH;
47 import org.apache.bcel.generic.RET;
48 import org.apache.bcel.generic.ReferenceType;
49 import org.apache.bcel.generic.ReturnaddressType;
50 import org.apache.bcel.generic.SWAP;
51 import org.apache.bcel.generic.TABLESWITCH;
52 import org.apache.bcel.generic.TargetLostException;
53 import org.apache.bcel.generic.Type;
54 import org.apache.bcel.util.ClassLoaderRepository;
55 import org.apache.bcel.verifier.exc.AssertionViolatedException;
56 import org.apache.bcel.verifier.structurals.ControlFlowGraph;
57 import org.apache.bcel.verifier.structurals.ExceptionHandler;
58 import org.apache.bcel.verifier.structurals.ExecutionVisitor;
59 import org.apache.bcel.verifier.structurals.Frame;
60 import org.apache.bcel.verifier.structurals.InstConstraintVisitor;
61 import org.apache.bcel.verifier.structurals.InstructionContext;
62 import org.apache.bcel.verifier.structurals.LocalVariables;
63 import org.apache.bcel.verifier.structurals.OperandStack;
64 import org.apache.bcel.verifier.structurals.UninitializedObjectType;
65
66 /**
67  * The classloader breakes the methods of the classes into pieces and
68  * add intercepting code to suspend the execution of the method.
69  *
70  * This code is based on the original idea of the BRAKES project.
71  * (http://www.cs.kuleuven.ac.be/~eddy/BRAKES/brakes.html).
72  *
73  * @author <a HREF="mailto:stephan@apache.org">Stephan Michels</a>
74  * @author <a HREF="mailto:tcurdt@apache.org">Torsten Curdt</a>
75  * @version CVS $Id: ContinuationClassLoader.java 30932 2004-07-29 17:35:38Z vgritsenko $
76  */

77 public class ContinuationClassLoader extends ClassLoader JavaDoc {
78
79     private static final String JavaDoc CONTINUATION_CLASS = Continuation.class.getName();
80     private static final ObjectType CONTINUATION_TYPE = new ObjectType(CONTINUATION_CLASS);
81
82     private static final String JavaDoc STACK_CLASS = ContinuationStack.class.getName();
83     private static final ObjectType STACK_TYPE = new ObjectType(STACK_CLASS);
84
85     private static final String JavaDoc CONTINUABLE_CLASS = Continuable.class.getName();
86
87     private static final String JavaDoc CONTINUATIONCAPABLE_CLASS = ContinuationCapable.class.getName();
88
89     private static final String JavaDoc CONTINUATION_METHOD = "currentContinuation";
90     private static final String JavaDoc STACK_METHOD = "getStack";
91     private static final String JavaDoc POP_METHOD = "pop";
92     private static final String JavaDoc PUSH_METHOD = "push";
93     private static final String JavaDoc RESTORING_METHOD = "isRestoring";
94     private static final String JavaDoc CAPURING_METHOD = "isCapturing";
95
96     private static boolean currentMethodStatic;
97
98     public ContinuationClassLoader(ClassLoader JavaDoc parent) {
99         super(parent);
100         Repository.setRepository(new ClassLoaderRepository(parent));
101     }
102
103     protected synchronized Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
104             throws ClassNotFoundException JavaDoc {
105         // this finds also classes, which are already transformed, via findLoadedClass
106
Class JavaDoc c = super.loadClass(name, resolve);
107
108         // transform class if class is continuable and not continuation capable
109
if ((Continuable.class.isAssignableFrom(c)) &&
110             (!ContinuationCapable.class.isAssignableFrom(c)) &&
111             (!c.isInterface())) {
112             JavaClass clazz = Repository.lookupClass(c);
113
114             byte data[] = transform(clazz);
115             c = defineClass(name, data, 0, data.length);
116         }
117         if (c == null) {
118             throw new ClassNotFoundException JavaDoc(name);
119         }
120         if (resolve) {
121             resolveClass(c);
122         }
123         return c;
124     }
125
126     private byte[] transform(JavaClass javaclazz) throws ClassNotFoundException JavaDoc {
127         // make all methods of java class continuable
128
ClassGen clazz = new ClassGen(javaclazz);
129         ConstantPoolGen cp = clazz.getConstantPool();
130         // obsolete, but neccesary to execute the InvokeContext
131
InstConstraintVisitor icv = new InstConstraintVisitor();
132         icv.setConstantPoolGen(cp);
133         // vistor to build the frame information
134
ExecutionVisitor ev = new ExecutionVisitor();
135         ev.setConstantPoolGen(cp);
136
137         Method[] methods = clazz.getMethods();
138         for (int i = 0; i < methods.length; i++) {
139             MethodGen method = new MethodGen(methods[i], clazz.getClassName(), cp);
140
141             currentMethodStatic = methods[i].isStatic();
142             if (isValid(method)) {
143                 // analyse the code of the method to create the frame
144
// information about every instruction
145
ControlFlowGraph cfg = new ControlFlowGraph(method);
146                 analyse(clazz, method, cfg, icv, ev);
147                 // add intercepting code
148
rewrite(method, cfg);
149                 // make last optional check for consistency
150
clazz.replaceMethod(methods[i], method.getMethod());
151             }
152         }
153         clazz.addInterface(CONTINUATIONCAPABLE_CLASS);
154         return clazz.getJavaClass().getBytes();
155     }
156
157     private boolean isValid(MethodGen m) {
158         if (m.getName().equals(Constants.CONSTRUCTOR_NAME)
159                 || m.getName().equals(Constants.STATIC_INITIALIZER_NAME)
160                 || m.isNative()
161                 || m.isAbstract()) {
162             return false;
163         } else {
164             return true;
165         }
166     }
167
168     private void analyse(ClassGen clazz, MethodGen method, ControlFlowGraph cfg,
169                          InstConstraintVisitor icv, ExecutionVisitor ev) {
170         // build the initial frame situation for this method.
171
Frame vanillaFrame = new Frame(method.getMaxLocals(), method.getMaxStack());
172         if (!method.isStatic()) {
173             if (method.getName().equals(Constants.CONSTRUCTOR_NAME)) {
174                 Frame._this = new UninitializedObjectType(new ObjectType(clazz.getClassName()));
175                 vanillaFrame.getLocals().set(0, new UninitializedObjectType(new ObjectType(clazz.getClassName())));
176             } else {
177                 Frame._this = null;
178                 vanillaFrame.getLocals().set(0, new ObjectType(clazz.getClassName()));
179             }
180         }
181         // fill local variables with parameter types
182
Type[] argtypes = method.getArgumentTypes();
183         int twoslotoffset = 0;
184         for (int j = 0; j < argtypes.length; j++) {
185             if ((argtypes[j] == Type.SHORT) ||
186                 (argtypes[j] == Type.BYTE) ||
187                 (argtypes[j] == Type.CHAR) ||
188                 (argtypes[j] == Type.BOOLEAN)) {
189                 argtypes[j] = Type.INT;
190             }
191             vanillaFrame.getLocals().set(twoslotoffset + j + (method.isStatic() ? 0 : 1), argtypes[j]);
192             if (argtypes[j].getSize() == 2) {
193                 twoslotoffset++;
194                 vanillaFrame.getLocals().set(twoslotoffset + j + (method.isStatic() ? 0 : 1), Type.UNKNOWN);
195             }
196         }
197         icv.setMethodGen(method);
198
199         Vector JavaDoc ics = new Vector JavaDoc(); // Type: InstructionContext
200
Vector JavaDoc ecs = new Vector JavaDoc(); // Type: ArrayList (of InstructionContext)
201

202         InstructionContext start = cfg.contextOf(method.getInstructionList().getStart());
203
204         start.execute(vanillaFrame, new ArrayList JavaDoc(), icv, ev);
205         // new ArrayList() <=> no Instruction was executed before
206
// => Top-Level routine (no jsr call before)
207
ics.add(start);
208         ecs.add(new ArrayList JavaDoc());
209
210         while (!ics.isEmpty()) {
211             InstructionContext u = (InstructionContext)ics.remove(0);
212             ArrayList JavaDoc ec = (ArrayList JavaDoc)ecs.remove(0);
213
214             ArrayList JavaDoc oldchain = (ArrayList JavaDoc)(ec.clone());
215             ArrayList JavaDoc newchain = (ArrayList JavaDoc)(ec.clone());
216             newchain.add(u);
217
218             if ((u.getInstruction().getInstruction()) instanceof RET) {
219                 // We can only follow _one_ successor, the one after the
220
// JSR that was recently executed.
221
RET ret = (RET)u.getInstruction().getInstruction();
222                 ReturnaddressType t = (ReturnaddressType)u.getOutFrame(oldchain).getLocals().get(ret.getIndex());
223                 InstructionContext theSuccessor = cfg.contextOf(t.getTarget());
224
225                 if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)) {
226                     ics.add(theSuccessor);
227                     ecs.add(newchain.clone());
228                 }
229             } else { // "not a ret"
230
// Normal successors. Add them to the queue of successors.
231
InstructionContext[] succs = u.getSuccessors();
232                 for (int s = 0; s < succs.length; s++) {
233                     InstructionContext v = succs[s];
234                     if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) {
235                         ics.add(v);
236                         ecs.add(newchain.clone());
237                     }
238                 }
239             }
240             // Exception Handlers. Add them to the queue of successors.
241
ExceptionHandler[] exc_hds = u.getExceptionHandlers();
242             for (int s = 0; s < exc_hds.length; s++) {
243                 InstructionContext v = cfg.contextOf(exc_hds[s].getHandlerStart());
244                 // TODO: the "oldchain" and "newchain" is used to determine the subroutine
245
// we're in (by searching for the last JSR) by the InstructionContext
246
// implementation. Therefore, we should not use this chain mechanism
247
// when dealing with exception handlers.
248

249                 LocalVariables newLocals = u.getOutFrame(oldchain).getLocals();
250                 OperandStack newStack = new OperandStack(
251                         u.getOutFrame(oldchain).getStack().maxStack(),
252                         (exc_hds[s].getExceptionType() == null
253                         ? Type.THROWABLE
254                         : exc_hds[s].getExceptionType()));
255                 Frame newFrame = new Frame(newLocals, newStack);
256
257                 if (v.execute(newFrame, new ArrayList JavaDoc(), icv, ev)) {
258                     ics.add(v);
259                     ecs.add(new ArrayList JavaDoc());
260                 }
261             }
262         }
263     }
264
265     private void rewrite(MethodGen method, ControlFlowGraph cfg)
266             throws ClassNotFoundException JavaDoc {
267         InstructionFactory insFactory = new InstructionFactory(method.getConstantPool());
268         Vector JavaDoc invokeIns = new Vector JavaDoc();
269         int count = 0;
270         InstructionList insList = method.getInstructionList();
271         InstructionHandle ins = insList.getStart();
272         InstructionList restorer = new InstructionList();
273         while (ins != null) {
274             InstructionHandle next = ins.getNext();
275
276             // if not traversed by the analyser, then don't rewrite
277
InstructionContext context = null;
278             Frame frame = null;
279             try {
280                 context = cfg.contextOf(ins);
281                 frame = context.getOutFrame(new ArrayList JavaDoc());
282             } catch (AssertionViolatedException ave) {
283                 // empty
284
}
285             if (frame != null) {
286                 if (rewriteable(method, ins)) {
287                     // Add frame saver and restorer for the current breakpoint
288

289                     // determine type of object for the method invocation
290
InvokeInstruction invoke = (InvokeInstruction)ins.getInstruction();
291                     Type[] arguments = invoke.getArgumentTypes(method.getConstantPool());
292                     ObjectType objecttype = null;
293                     if (!(invoke instanceof INVOKESTATIC)) {
294                         objecttype = (ObjectType)context.getInFrame().getStack().peek(arguments.length);
295                     }
296                     InstructionList rList = restoreFrame(method, ins, insFactory, frame, objecttype);
297                     insList.append(ins, saveFrame(method, ins, count++, insFactory, frame));
298                     invokeIns.addElement(rList.getStart());
299                     restorer.append(rList);
300                 }
301                 // remove all new's
302
if (ins.getInstruction().getOpcode() == Constants.NEW) {
303                     try {
304                         // remove additional dup's
305
while (next != null && next.getInstruction().getOpcode() == Constants.DUP) {
306                             context = cfg.contextOf(next);
307                             frame = context.getOutFrame(new ArrayList JavaDoc());
308                             InstructionHandle newnext = next.getNext();
309                             insList.delete(next);
310                             next = newnext;
311                         }
312                         InstructionTargeter[] targeter = ins.getTargeters();
313                         if (targeter != null) {
314                             InstructionHandle newnext = ins.getNext();
315                             for (int i = 0; i < targeter.length; i++) {
316                                 targeter[i].updateTarget(ins, newnext);
317                             }
318                         }
319                         insList.delete(ins);
320                     } catch (TargetLostException tle) {
321                         throw new ClassNotFoundException JavaDoc(tle.getMessage(), tle);
322                     }
323                 } else if (ins.getInstruction().getOpcode() == Constants.INVOKESPECIAL) {
324                     // duplicate stack before invokespecial to insert uninitialized object
325
frame = context.getInFrame();
326                     InvokeInstruction invoke = (InvokeInstruction)ins.getInstruction();
327                     Type[] arguments = invoke.getArgumentTypes(method.getConstantPool());
328     
329                     OperandStack os = frame.getStack();
330                     Type type = os.peek(arguments.length);
331                     if (type instanceof UninitializedObjectType) {
332                         ObjectType objecttype = ((UninitializedObjectType) type).getInitialized();
333                         InstructionList duplicator = duplicateStack(method, invoke, objecttype);
334                         InstructionTargeter[] targeter = ins.getTargeters();
335
336                         if (targeter!=null) {
337                             InstructionHandle newnext = duplicator.getStart();
338                             for(int i=0; i < targeter.length; i++) {
339                                 targeter[i].updateTarget(ins, newnext);
340                             }
341                         }
342                         insList.insert(ins, duplicator);
343                     }
344                 }
345             }
346             ins = next;
347         }
348         InstructionHandle firstIns = insList.getStart();
349         if (count > 0) {
350             InstructionHandle[] tableTargets = new InstructionHandle[count];
351             int[] match = new int[count];
352             for (int i = 0; i < count; i++) {
353                 match[i] = i;
354             }
355             invokeIns.copyInto(tableTargets);
356             insList.insert(restorer);
357
358             // select frame restorer
359
insList.insert(new TABLESWITCH(match, tableTargets, firstIns));
360             insList.insert(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.INT), Type.INT, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
361             insList.insert(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
362
363             // test if the continuation should be restored
364
insList.insert(new IFEQ(firstIns));
365             insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, RESTORING_METHOD, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
366             insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
367         }
368
369         // get stack from current continuation and store in the last local variable
370
insList.insert(InstructionFactory.createStore(STACK_TYPE, method.getMaxLocals()+1));
371         insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, STACK_METHOD, STACK_TYPE,
372                        Type.NO_ARGS, Constants.INVOKEVIRTUAL));
373         InstructionHandle restore_handle = insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
374
375         // if not continuation exists, create empty stack
376
insList.insert(new GOTO(firstIns));
377         insList.insert(InstructionFactory.createStore(STACK_TYPE, method.getMaxLocals()+1));
378         insList.insert(insFactory.createInvoke(STACK_CLASS, Constants.CONSTRUCTOR_NAME, Type.VOID, Type.NO_ARGS, Constants. INVOKESPECIAL));
379         insList.insert(InstructionFactory.createDup(STACK_TYPE.getSize()));
380         insList.insert(insFactory.createNew(STACK_TYPE));
381
382         // test if no current continuation exists
383
insList.insert(new IFNONNULL(restore_handle));
384         insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
385
386         // get current continuation and store in the next to last local variable
387
insList.insert(InstructionFactory.createStore(CONTINUATION_TYPE, method.getMaxLocals()));
388         insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, CONTINUATION_METHOD, CONTINUATION_TYPE,
389                        Type.NO_ARGS, Constants.INVOKESTATIC));
390
391         // make room for additional objects
392
method.setMaxLocals(method.getMaxLocals() + 2);
393         method.setMaxStack(method.getMaxStack() + 2);
394     }
395
396     private InstructionList duplicateStack(MethodGen method, InvokeInstruction invoke,
397             ObjectType objecttype) throws ClassNotFoundException JavaDoc {
398         // reconstruction of an uninitialed object to call the constructor.
399
InstructionFactory insFactory = new InstructionFactory(method.getConstantPool());
400         InstructionList insList = new InstructionList();
401
402         Type[] arguments = invoke.getArgumentTypes(method.getConstantPool());
403         // pop all arguments for the constructor from the stack
404
for (int i = arguments.length - 1; i >= 0; i--) {
405             Type type = arguments[i];
406             insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
407             insList.append(new SWAP());
408             if (type instanceof BasicType) {
409                 if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
410                     type = Type.INT;
411                 }
412                 insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(type), Type.VOID, new Type[]{type}, Constants.INVOKEVIRTUAL));
413             } else if (type instanceof ReferenceType) {
414                 insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL));
415             }
416         }
417         // create uninitialzed object
418
insList.append(insFactory.createNew(objecttype));
419         insList.append(InstructionFactory.createDup(objecttype.getSize()));
420         // return the arguments into the stack
421
for (int i = 0; i < arguments.length; i++) {
422             Type type = arguments[i];
423             insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
424             if (type instanceof BasicType) {
425                 if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
426                     type = Type.INT;
427                 }
428                 insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
429             } else if (type instanceof ReferenceType) {
430                 insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
431                 if (!type.equals(Type.OBJECT)) {
432                     insList.append(insFactory.createCast(Type.OBJECT, type));
433                 }
434             }
435         }
436         return insList;
437     }
438
439     private boolean rewriteable(MethodGen method, InstructionHandle handle)
440             throws ClassNotFoundException JavaDoc {
441         // check in the invocation can be a breakpoint.
442
int opcode = handle.getInstruction().getOpcode();
443         boolean invokeSpecialSuper = false;
444         if (opcode == Constants.INVOKESPECIAL) {
445             InvokeInstruction ivs = (InvokeInstruction) handle.getInstruction();
446             String JavaDoc mName = ivs.getMethodName(method.getConstantPool());
447             invokeSpecialSuper = !mName.equals(Constants.CONSTRUCTOR_NAME);
448         }
449
450         if (opcode == Constants.INVOKEVIRTUAL ||
451             opcode == Constants.INVOKESTATIC ||
452             opcode == Constants.INVOKEINTERFACE ||
453             invokeSpecialSuper) {
454
455             int index = ((InvokeInstruction) handle.getInstruction()).getIndex();
456             String JavaDoc classname = getObjectType(method.getConstantPool().getConstantPool(), index).getClassName();
457
458             // rewrite invocation if object is continuable or a continuation object
459
return Repository.implementationOf(classname, CONTINUABLE_CLASS) ||
460                    Repository.instanceOf(classname, CONTINUATION_CLASS);
461         }
462         return false;
463     }
464
465     private InstructionList saveFrame(MethodGen method, InstructionHandle handle, int pc,
466                                       InstructionFactory insFactory, Frame frame) {
467         InstructionList insList = new InstructionList();
468
469         // Remove needless return type from stack
470
InvokeInstruction inv = (InvokeInstruction) handle.getInstruction();
471         Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex());
472         if (returnType.getSize() > 0) {
473             insList.insert(InstructionFactory.createPop(returnType.getSize()));
474         }
475         boolean skipFirst = returnType.getSize() > 0;
476
477         // save stack
478
OperandStack os = frame.getStack();
479         for (int i = skipFirst ? 1 : 0; i < os.size(); i++) {
480             Type type = os.peek(i);
481             if (type instanceof BasicType) {
482                 if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
483                     type = Type.INT;
484                 }
485                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
486                 insList.append(new SWAP()); // TODO: check for types with two words on stack
487
insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(type), Type.VOID, new Type[]{type}, Constants.INVOKEVIRTUAL));
488             } else if (type == null) {
489                 insList.append(InstructionConstants.POP);
490             } else if (type instanceof UninitializedObjectType) {
491                 // After the remove of new, there shouldn't be a
492
// uninitialized object on the stack
493
} else if (type instanceof ReferenceType) {
494                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
495                 insList.append(new SWAP());
496                 insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL));
497             }
498         }
499         // add isCapturing test
500
insList.insert(new IFEQ(handle.getNext()));
501         // test if the continuation should be captured after the invocation
502
insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, CAPURING_METHOD, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
503         insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
504         // test if continuation exists
505
insList.insert(new IFNULL(handle.getNext()));
506         insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals()));
507         // save local variables
508
LocalVariables lvs = frame.getLocals();
509         for (int i = 0; i < lvs.maxLocals(); i++) {
510             Type type = lvs.get(i);
511             if (type instanceof BasicType) {
512                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
513                 insList.append(InstructionFactory.createLoad(type, i));
514                 if (type.getSize() < 2 && !type.equals(Type.FLOAT))
515                     type = Type.INT;
516                 insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(type), Type.VOID, new Type[]{type}, Constants.INVOKEVIRTUAL));
517             } else if (type == null) {
518                 // no need to save null
519
} else if (type instanceof UninitializedObjectType) {
520                 // no need to save uninitialized objects
521
} else if (type instanceof ReferenceType) {
522                 if (i == 0 && !currentMethodStatic) {
523                     // remember current object
524
insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
525                     insList.append(InstructionFactory.createLoad(type, i));
526                     insList.append(insFactory.createInvoke(STACK_CLASS, PUSH_METHOD + "Reference", Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL));
527                 }
528                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
529                 insList.append(InstructionFactory.createLoad(type, i));
530                 insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL));
531             }
532         }
533         // save programcounter
534
insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
535         insList.append(new PUSH(method.getConstantPool(), pc));
536         insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.INT), Type.VOID, new Type[]{Type.INT}, Constants.INVOKEVIRTUAL));
537         // return NULL result
538
insList.append(InstructionFactory.createNull(method.getReturnType()));
539         insList.append(InstructionFactory.createReturn(method.getReturnType()));
540         return insList;
541     }
542
543     private InstructionList restoreFrame(MethodGen method, InstructionHandle handle,
544             InstructionFactory insFactory, Frame frame, ObjectType objecttype) {
545         InstructionList insList = new InstructionList();
546         // restore local variables
547
LocalVariables lvs = frame.getLocals();
548         for (int i = lvs.maxLocals() - 1; i >= 0; i--) {
549             Type type = lvs.get(i);
550             if (type instanceof BasicType) {
551                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
552                 if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
553                     type = Type.INT;
554                 }
555                 insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
556                 insList.append(InstructionFactory.createStore(type, i));
557             } else if (type == null) {
558                 insList.append(new ACONST_NULL());
559                 insList.append(InstructionFactory.createStore(new ObjectType("<null object>"), i));
560             } else if (type instanceof UninitializedObjectType) {
561                 // No uninitilaized objects should be found
562
// in the local variables.
563
} else if (type instanceof ReferenceType) {
564                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
565                 insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
566                 if (!type.equals(Type.OBJECT) && (!type.equals(Type.NULL))) {
567                     insList.append(insFactory.createCast(Type.OBJECT, type));
568                 }
569                 insList.append(InstructionFactory.createStore(type, i));
570             }
571         }
572
573         InvokeInstruction inv = (InvokeInstruction) handle.getInstruction();
574         Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex());
575         boolean skipFirst = returnType.getSize() > 0;
576
577         // restore stack
578
OperandStack os = frame.getStack();
579         for (int i = os.size() - 1; i >= (skipFirst ? 1 : 0); i--) {
580             Type type = os.peek(i);
581             if (type instanceof BasicType) {
582                 if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
583                     type = Type.INT;
584                 }
585                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
586                 insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
587             } else if (type == null) {
588                 insList.append(new ACONST_NULL());
589             } else if (type instanceof UninitializedObjectType) {
590                 // After the remove of new, there shouldn't be a
591
// uninitialized object on the stack
592
} else if (type instanceof ReferenceType) {
593                 insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
594                 insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
595                 if (!type.equals(Type.OBJECT))
596                     insList.append(insFactory.createCast(Type.OBJECT, type));
597             }
598         }
599         // retrieve current object
600
if (!(inv instanceof INVOKESTATIC)) {
601             insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1));
602             insList.append(insFactory.createInvoke(STACK_CLASS, POP_METHOD + "Reference", Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
603             insList.append(insFactory.createCast(Type.OBJECT, objecttype));
604         }
605         // Create null types for the parameters of the method invocation
606
Type[] paramTypes = getParamTypes(method.getConstantPool().getConstantPool(), inv.getIndex());
607         for (int j = 0; j < paramTypes.length; j++) {
608             insList.append(InstructionFactory.createNull(paramTypes[j]));
609         }
610         // go to last invocation
611
insList.append(new GOTO(handle));
612         return insList;
613     }
614
615     private ObjectType getObjectType(ConstantPool cp, int index) {
616         ConstantCP cmr = (ConstantCP) cp.getConstant(index);
617         String JavaDoc sig = cp.getConstantString(cmr.getClassIndex(), Constants.CONSTANT_Class);
618         return new ObjectType(sig.replace('/', '.'));
619     }
620
621     private Type[] getParamTypes(ConstantPool cp, int index) {
622         ConstantCP cmr = (ConstantCP) cp.getConstant(index);
623         ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex());
624         String JavaDoc sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
625         return Type.getArgumentTypes(sig);
626     }
627
628     private Type getReturnType(ConstantPool cp, int index) {
629         ConstantCP cmr = (ConstantCP) cp.getConstant(index);
630         ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex());
631         String JavaDoc sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes();
632         return Type.getReturnType(sig);
633     }
634
635     private String JavaDoc getPopMethod(Type type) {
636         return POP_METHOD + getTypeSuffix(type);
637     }
638     
639     private String JavaDoc getPushMethod(Type type) {
640            return PUSH_METHOD + getTypeSuffix(type);
641     }
642
643     private String JavaDoc getTypeSuffix(Type type) {
644         if (type.equals(Type.BOOLEAN))
645             return "Int";
646         else if (type.equals(Type.CHAR))
647             return "Int";
648         else if (type.equals(Type.FLOAT))
649             return "Float";
650         else if (type.equals(Type.DOUBLE))
651             return "Double";
652         else if (type.equals(Type.BYTE))
653             return "Int";
654         else if (type.equals(Type.SHORT))
655             return "Int";
656         else if (type.equals(Type.INT))
657             return "Int";
658         else if (type.equals(Type.LONG))
659             return "Long";
660         // VOID and OBJECT are "Object"
661
return "Object";
662     }
663 }
664
Popular Tags