001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.flow.java;
018:
019: import java.util.ArrayList;
020: import java.util.Vector;
021:
022: import org.apache.bcel.Constants;
023: import org.apache.bcel.Repository;
024: import org.apache.bcel.classfile.ConstantCP;
025: import org.apache.bcel.classfile.ConstantNameAndType;
026: import org.apache.bcel.classfile.ConstantPool;
027: import org.apache.bcel.classfile.ConstantUtf8;
028: import org.apache.bcel.classfile.JavaClass;
029: import org.apache.bcel.classfile.Method;
030: import org.apache.bcel.generic.ACONST_NULL;
031: import org.apache.bcel.generic.BasicType;
032: import org.apache.bcel.generic.ClassGen;
033: import org.apache.bcel.generic.ConstantPoolGen;
034: import org.apache.bcel.generic.GOTO;
035: import org.apache.bcel.generic.IFEQ;
036: import org.apache.bcel.generic.IFNONNULL;
037: import org.apache.bcel.generic.IFNULL;
038: import org.apache.bcel.generic.INVOKESTATIC;
039: import org.apache.bcel.generic.InstructionConstants;
040: import org.apache.bcel.generic.InstructionFactory;
041: import org.apache.bcel.generic.InstructionHandle;
042: import org.apache.bcel.generic.InstructionList;
043: import org.apache.bcel.generic.InstructionTargeter;
044: import org.apache.bcel.generic.InvokeInstruction;
045: import org.apache.bcel.generic.MethodGen;
046: import org.apache.bcel.generic.ObjectType;
047: import org.apache.bcel.generic.PUSH;
048: import org.apache.bcel.generic.RET;
049: import org.apache.bcel.generic.ReferenceType;
050: import org.apache.bcel.generic.ReturnaddressType;
051: import org.apache.bcel.generic.SWAP;
052: import org.apache.bcel.generic.TABLESWITCH;
053: import org.apache.bcel.generic.TargetLostException;
054: import org.apache.bcel.generic.Type;
055: import org.apache.bcel.util.ClassLoaderRepository;
056: import org.apache.bcel.verifier.exc.AssertionViolatedException;
057: import org.apache.bcel.verifier.structurals.ControlFlowGraph;
058: import org.apache.bcel.verifier.structurals.ExceptionHandler;
059: import org.apache.bcel.verifier.structurals.ExecutionVisitor;
060: import org.apache.bcel.verifier.structurals.Frame;
061: import org.apache.bcel.verifier.structurals.InstConstraintVisitor;
062: import org.apache.bcel.verifier.structurals.InstructionContext;
063: import org.apache.bcel.verifier.structurals.LocalVariables;
064: import org.apache.bcel.verifier.structurals.OperandStack;
065: import org.apache.bcel.verifier.structurals.UninitializedObjectType;
066:
067: /**
068: * The classloader breakes the methods of the classes into pieces and
069: * add intercepting code to suspend the execution of the method.
070: *
071: * This code is based on the original idea of the BRAKES project.
072: * (http://www.cs.kuleuven.ac.be/~eddy/BRAKES/brakes.html).
073: *
074: * @author <a href="mailto:stephan@apache.org">Stephan Michels</a>
075: * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
076: * @version CVS $Id: ContinuationClassLoader.java 433543 2006-08-22 06:22:54Z crossley $
077: */
078: public class ContinuationClassLoader extends ClassLoader {
079:
080: private static final String CONTINUATION_CLASS = Continuation.class
081: .getName();
082: private static final ObjectType CONTINUATION_TYPE = new ObjectType(
083: CONTINUATION_CLASS);
084:
085: private static final String STACK_CLASS = ContinuationStack.class
086: .getName();
087: private static final ObjectType STACK_TYPE = new ObjectType(
088: STACK_CLASS);
089:
090: private static final String CONTINUABLE_CLASS = Continuable.class
091: .getName();
092:
093: private static final String CONTINUATIONCAPABLE_CLASS = ContinuationCapable.class
094: .getName();
095:
096: private static final String CONTINUATION_METHOD = "currentContinuation";
097: private static final String STACK_METHOD = "getStack";
098: private static final String POP_METHOD = "pop";
099: private static final String PUSH_METHOD = "push";
100: private static final String RESTORING_METHOD = "isRestoring";
101: private static final String CAPURING_METHOD = "isCapturing";
102:
103: private static boolean currentMethodStatic;
104:
105: public ContinuationClassLoader(ClassLoader parent) {
106: super (parent);
107: Repository.setRepository(new ClassLoaderRepository(parent));
108: }
109:
110: protected synchronized Class loadClass(String name, boolean resolve)
111: throws ClassNotFoundException {
112: // this finds also classes, which are already transformed, via findLoadedClass
113: Class c = super .loadClass(name, resolve);
114:
115: // transform class if class is continuable and not continuation capable
116: if ((Continuable.class.isAssignableFrom(c))
117: && (!ContinuationCapable.class.isAssignableFrom(c))
118: && (!c.isInterface())) {
119: JavaClass clazz = Repository.lookupClass(c);
120:
121: byte data[] = transform(clazz);
122: c = defineClass(name, data, 0, data.length);
123: }
124: if (c == null) {
125: throw new ClassNotFoundException(name);
126: }
127: if (resolve) {
128: resolveClass(c);
129: }
130: return c;
131: }
132:
133: private byte[] transform(JavaClass javaclazz)
134: throws ClassNotFoundException {
135: // make all methods of java class continuable
136: ClassGen clazz = new ClassGen(javaclazz);
137: ConstantPoolGen cp = clazz.getConstantPool();
138: // obsolete, but neccesary to execute the InvokeContext
139: InstConstraintVisitor icv = new InstConstraintVisitor();
140: icv.setConstantPoolGen(cp);
141: // vistor to build the frame information
142: ExecutionVisitor ev = new ExecutionVisitor();
143: ev.setConstantPoolGen(cp);
144:
145: Method[] methods = clazz.getMethods();
146: for (int i = 0; i < methods.length; i++) {
147: MethodGen method = new MethodGen(methods[i], clazz
148: .getClassName(), cp);
149:
150: currentMethodStatic = methods[i].isStatic();
151: if (isValid(method)) {
152: // analyse the code of the method to create the frame
153: // information about every instruction
154: ControlFlowGraph cfg = new ControlFlowGraph(method);
155: analyse(clazz, method, cfg, icv, ev);
156: // add intercepting code
157: rewrite(method, cfg);
158: // make last optional check for consistency
159: clazz.replaceMethod(methods[i], method.getMethod());
160: }
161: }
162: clazz.addInterface(CONTINUATIONCAPABLE_CLASS);
163: return clazz.getJavaClass().getBytes();
164: }
165:
166: private boolean isValid(MethodGen m) {
167: if (m.getName().equals(Constants.CONSTRUCTOR_NAME)
168: || m.getName()
169: .equals(Constants.STATIC_INITIALIZER_NAME)
170: || m.isNative() || m.isAbstract()) {
171: return false;
172: } else {
173: return true;
174: }
175: }
176:
177: private void analyse(ClassGen clazz, MethodGen method,
178: ControlFlowGraph cfg, InstConstraintVisitor icv,
179: ExecutionVisitor ev) {
180: // build the initial frame situation for this method.
181: Frame vanillaFrame = new Frame(method.getMaxLocals(), method
182: .getMaxStack());
183: if (!method.isStatic()) {
184: if (method.getName().equals(Constants.CONSTRUCTOR_NAME)) {
185: Frame._this = new UninitializedObjectType(
186: new ObjectType(clazz.getClassName()));
187: vanillaFrame.getLocals().set(
188: 0,
189: new UninitializedObjectType(new ObjectType(
190: clazz.getClassName())));
191: } else {
192: Frame._this = null;
193: vanillaFrame.getLocals().set(0,
194: new ObjectType(clazz.getClassName()));
195: }
196: }
197: // fill local variables with parameter types
198: Type[] argtypes = method.getArgumentTypes();
199: int twoslotoffset = 0;
200: for (int j = 0; j < argtypes.length; j++) {
201: if ((argtypes[j] == Type.SHORT)
202: || (argtypes[j] == Type.BYTE)
203: || (argtypes[j] == Type.CHAR)
204: || (argtypes[j] == Type.BOOLEAN)) {
205: argtypes[j] = Type.INT;
206: }
207: vanillaFrame.getLocals().set(
208: twoslotoffset + j + (method.isStatic() ? 0 : 1),
209: argtypes[j]);
210: if (argtypes[j].getSize() == 2) {
211: twoslotoffset++;
212: vanillaFrame.getLocals()
213: .set(
214: twoslotoffset + j
215: + (method.isStatic() ? 0 : 1),
216: Type.UNKNOWN);
217: }
218: }
219: icv.setMethodGen(method);
220:
221: Vector ics = new Vector(); // Type: InstructionContext
222: Vector ecs = new Vector(); // Type: ArrayList (of InstructionContext)
223:
224: InstructionContext start = cfg.contextOf(method
225: .getInstructionList().getStart());
226:
227: start.execute(vanillaFrame, new ArrayList(), icv, ev);
228: // new ArrayList() <=> no Instruction was executed before
229: // => Top-Level routine (no jsr call before)
230: ics.add(start);
231: ecs.add(new ArrayList());
232:
233: while (!ics.isEmpty()) {
234: InstructionContext u = (InstructionContext) ics.remove(0);
235: ArrayList ec = (ArrayList) ecs.remove(0);
236:
237: ArrayList oldchain = (ArrayList) (ec.clone());
238: ArrayList newchain = (ArrayList) (ec.clone());
239: newchain.add(u);
240:
241: if ((u.getInstruction().getInstruction()) instanceof RET) {
242: // We can only follow _one_ successor, the one after the
243: // JSR that was recently executed.
244: RET ret = (RET) u.getInstruction().getInstruction();
245: ReturnaddressType t = (ReturnaddressType) u
246: .getOutFrame(oldchain).getLocals().get(
247: ret.getIndex());
248: InstructionContext theSuccessor = cfg.contextOf(t
249: .getTarget());
250:
251: if (theSuccessor.execute(u.getOutFrame(oldchain),
252: newchain, icv, ev)) {
253: ics.add(theSuccessor);
254: ecs.add(newchain.clone());
255: }
256: } else { // "not a ret"
257: // Normal successors. Add them to the queue of successors.
258: InstructionContext[] succs = u.getSuccessors();
259: for (int s = 0; s < succs.length; s++) {
260: InstructionContext v = succs[s];
261: if (v.execute(u.getOutFrame(oldchain), newchain,
262: icv, ev)) {
263: ics.add(v);
264: ecs.add(newchain.clone());
265: }
266: }
267: }
268: // Exception Handlers. Add them to the queue of successors.
269: ExceptionHandler[] exc_hds = u.getExceptionHandlers();
270: for (int s = 0; s < exc_hds.length; s++) {
271: InstructionContext v = cfg.contextOf(exc_hds[s]
272: .getHandlerStart());
273: // TODO: the "oldchain" and "newchain" is used to determine the subroutine
274: // we're in (by searching for the last JSR) by the InstructionContext
275: // implementation. Therefore, we should not use this chain mechanism
276: // when dealing with exception handlers.
277:
278: LocalVariables newLocals = u.getOutFrame(oldchain)
279: .getLocals();
280: OperandStack newStack = new OperandStack(u.getOutFrame(
281: oldchain).getStack().maxStack(), (exc_hds[s]
282: .getExceptionType() == null ? Type.THROWABLE
283: : exc_hds[s].getExceptionType()));
284: Frame newFrame = new Frame(newLocals, newStack);
285:
286: if (v.execute(newFrame, new ArrayList(), icv, ev)) {
287: ics.add(v);
288: ecs.add(new ArrayList());
289: }
290: }
291: }
292: }
293:
294: private void rewrite(MethodGen method, ControlFlowGraph cfg)
295: throws ClassNotFoundException {
296: InstructionFactory insFactory = new InstructionFactory(method
297: .getConstantPool());
298: Vector invokeIns = new Vector();
299: int count = 0;
300: InstructionList insList = method.getInstructionList();
301: InstructionHandle ins = insList.getStart();
302: InstructionList restorer = new InstructionList();
303: while (ins != null) {
304: InstructionHandle next = ins.getNext();
305:
306: // if not traversed by the analyser, then don't rewrite
307: InstructionContext context = null;
308: Frame frame = null;
309: try {
310: context = cfg.contextOf(ins);
311: frame = context.getOutFrame(new ArrayList());
312: } catch (AssertionViolatedException ave) {
313: // empty
314: }
315: if (frame != null) {
316: if (rewriteable(method, ins)) {
317: // Add frame saver and restorer for the current breakpoint
318:
319: // determine type of object for the method invocation
320: InvokeInstruction invoke = (InvokeInstruction) ins
321: .getInstruction();
322: Type[] arguments = invoke.getArgumentTypes(method
323: .getConstantPool());
324: ObjectType objecttype = null;
325: if (!(invoke instanceof INVOKESTATIC)) {
326: objecttype = (ObjectType) context.getInFrame()
327: .getStack().peek(arguments.length);
328: }
329: InstructionList rList = restoreFrame(method, ins,
330: insFactory, frame, objecttype);
331: insList.append(ins, saveFrame(method, ins, count++,
332: insFactory, frame));
333: invokeIns.addElement(rList.getStart());
334: restorer.append(rList);
335: }
336: // remove all new's
337: if (ins.getInstruction().getOpcode() == Constants.NEW) {
338: try {
339: // remove additional dup's
340: while (next != null
341: && next.getInstruction().getOpcode() == Constants.DUP) {
342: context = cfg.contextOf(next);
343: frame = context
344: .getOutFrame(new ArrayList());
345: InstructionHandle newnext = next.getNext();
346: insList.delete(next);
347: next = newnext;
348: }
349: InstructionTargeter[] targeter = ins
350: .getTargeters();
351: if (targeter != null) {
352: InstructionHandle newnext = ins.getNext();
353: for (int i = 0; i < targeter.length; i++) {
354: targeter[i].updateTarget(ins, newnext);
355: }
356: }
357: insList.delete(ins);
358: } catch (TargetLostException tle) {
359: throw new ClassNotFoundException(tle
360: .getMessage(), tle);
361: }
362: } else if (ins.getInstruction().getOpcode() == Constants.INVOKESPECIAL) {
363: // duplicate stack before invokespecial to insert uninitialized object
364: frame = context.getInFrame();
365: InvokeInstruction invoke = (InvokeInstruction) ins
366: .getInstruction();
367: Type[] arguments = invoke.getArgumentTypes(method
368: .getConstantPool());
369:
370: OperandStack os = frame.getStack();
371: Type type = os.peek(arguments.length);
372: if (type instanceof UninitializedObjectType) {
373: ObjectType objecttype = ((UninitializedObjectType) type)
374: .getInitialized();
375: InstructionList duplicator = duplicateStack(
376: method, invoke, objecttype);
377: InstructionTargeter[] targeter = ins
378: .getTargeters();
379:
380: if (targeter != null) {
381: InstructionHandle newnext = duplicator
382: .getStart();
383: for (int i = 0; i < targeter.length; i++) {
384: targeter[i].updateTarget(ins, newnext);
385: }
386: }
387: insList.insert(ins, duplicator);
388: }
389: }
390: }
391: ins = next;
392: }
393: InstructionHandle firstIns = insList.getStart();
394: if (count > 0) {
395: InstructionHandle[] tableTargets = new InstructionHandle[count];
396: int[] match = new int[count];
397: for (int i = 0; i < count; i++) {
398: match[i] = i;
399: }
400: invokeIns.copyInto(tableTargets);
401: insList.insert(restorer);
402:
403: // select frame restorer
404: insList.insert(new TABLESWITCH(match, tableTargets,
405: firstIns));
406: insList.insert(insFactory.createInvoke(STACK_CLASS,
407: getPopMethod(Type.INT), Type.INT, Type.NO_ARGS,
408: Constants.INVOKEVIRTUAL));
409: insList.insert(InstructionFactory.createLoad(STACK_TYPE,
410: method.getMaxLocals() + 1));
411:
412: // test if the continuation should be restored
413: insList.insert(new IFEQ(firstIns));
414: insList.insert(insFactory.createInvoke(CONTINUATION_CLASS,
415: RESTORING_METHOD, Type.BOOLEAN, Type.NO_ARGS,
416: Constants.INVOKEVIRTUAL));
417: insList.insert(InstructionFactory.createLoad(
418: CONTINUATION_TYPE, method.getMaxLocals()));
419: }
420:
421: // get stack from current continuation and store in the last local variable
422: insList.insert(InstructionFactory.createStore(STACK_TYPE,
423: method.getMaxLocals() + 1));
424: insList.insert(insFactory.createInvoke(CONTINUATION_CLASS,
425: STACK_METHOD, STACK_TYPE, Type.NO_ARGS,
426: Constants.INVOKEVIRTUAL));
427: InstructionHandle restore_handle = insList
428: .insert(InstructionFactory.createLoad(
429: CONTINUATION_TYPE, method.getMaxLocals()));
430:
431: // if not continuation exists, create empty stack
432: insList.insert(new GOTO(firstIns));
433: insList.insert(InstructionFactory.createStore(STACK_TYPE,
434: method.getMaxLocals() + 1));
435: insList.insert(insFactory.createInvoke(STACK_CLASS,
436: Constants.CONSTRUCTOR_NAME, Type.VOID, Type.NO_ARGS,
437: Constants.INVOKESPECIAL));
438: insList.insert(InstructionFactory.createDup(STACK_TYPE
439: .getSize()));
440: insList.insert(insFactory.createNew(STACK_TYPE));
441:
442: // test if no current continuation exists
443: insList.insert(new IFNONNULL(restore_handle));
444: insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE,
445: method.getMaxLocals()));
446:
447: // get current continuation and store in the next to last local variable
448: insList.insert(InstructionFactory.createStore(
449: CONTINUATION_TYPE, method.getMaxLocals()));
450: insList.insert(insFactory.createInvoke(CONTINUATION_CLASS,
451: CONTINUATION_METHOD, CONTINUATION_TYPE, Type.NO_ARGS,
452: Constants.INVOKESTATIC));
453:
454: // make room for additional objects
455: method.setMaxLocals(method.getMaxLocals() + 2);
456: method.setMaxStack(method.getMaxStack() + 2);
457: }
458:
459: private InstructionList duplicateStack(MethodGen method,
460: InvokeInstruction invoke, ObjectType objecttype)
461: throws ClassNotFoundException {
462: // reconstruction of an uninitialed object to call the constructor.
463: InstructionFactory insFactory = new InstructionFactory(method
464: .getConstantPool());
465: InstructionList insList = new InstructionList();
466:
467: Type[] arguments = invoke.getArgumentTypes(method
468: .getConstantPool());
469: // pop all arguments for the constructor from the stack
470: for (int i = arguments.length - 1; i >= 0; i--) {
471: Type type = arguments[i];
472: insList.append(InstructionFactory.createLoad(STACK_TYPE,
473: method.getMaxLocals() + 1));
474: insList.append(new SWAP());
475: if (type instanceof BasicType) {
476: if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
477: type = Type.INT;
478: }
479: insList.append(insFactory.createInvoke(STACK_CLASS,
480: getPushMethod(type), Type.VOID,
481: new Type[] { type }, Constants.INVOKEVIRTUAL));
482: } else if (type instanceof ReferenceType) {
483: insList.append(insFactory.createInvoke(STACK_CLASS,
484: getPushMethod(Type.OBJECT), Type.VOID,
485: new Type[] { Type.OBJECT },
486: Constants.INVOKEVIRTUAL));
487: }
488: }
489: // create uninitialzed object
490: insList.append(insFactory.createNew(objecttype));
491: insList.append(InstructionFactory.createDup(objecttype
492: .getSize()));
493: // return the arguments into the stack
494: for (int i = 0; i < arguments.length; i++) {
495: Type type = arguments[i];
496: insList.append(InstructionFactory.createLoad(STACK_TYPE,
497: method.getMaxLocals() + 1));
498: if (type instanceof BasicType) {
499: if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
500: type = Type.INT;
501: }
502: insList.append(insFactory.createInvoke(STACK_CLASS,
503: getPopMethod(type), type, Type.NO_ARGS,
504: Constants.INVOKEVIRTUAL));
505: } else if (type instanceof ReferenceType) {
506: insList.append(insFactory.createInvoke(STACK_CLASS,
507: getPopMethod(Type.OBJECT), Type.OBJECT,
508: Type.NO_ARGS, Constants.INVOKEVIRTUAL));
509: if (!type.equals(Type.OBJECT)) {
510: insList.append(insFactory.createCast(Type.OBJECT,
511: type));
512: }
513: }
514: }
515: return insList;
516: }
517:
518: private boolean rewriteable(MethodGen method,
519: InstructionHandle handle) throws ClassNotFoundException {
520: // check in the invocation can be a breakpoint.
521: int opcode = handle.getInstruction().getOpcode();
522: boolean invokeSpecialSuper = false;
523: if (opcode == Constants.INVOKESPECIAL) {
524: InvokeInstruction ivs = (InvokeInstruction) handle
525: .getInstruction();
526: String mName = ivs.getMethodName(method.getConstantPool());
527: invokeSpecialSuper = !mName
528: .equals(Constants.CONSTRUCTOR_NAME);
529: }
530:
531: if (opcode == Constants.INVOKEVIRTUAL
532: || opcode == Constants.INVOKESTATIC
533: || opcode == Constants.INVOKEINTERFACE
534: || invokeSpecialSuper) {
535:
536: int index = ((InvokeInstruction) handle.getInstruction())
537: .getIndex();
538: String classname = getObjectType(
539: method.getConstantPool().getConstantPool(), index)
540: .getClassName();
541:
542: // rewrite invocation if object is continuable or a continuation object
543: return Repository.implementationOf(classname,
544: CONTINUABLE_CLASS)
545: || Repository.instanceOf(classname,
546: CONTINUATION_CLASS);
547: }
548: return false;
549: }
550:
551: private InstructionList saveFrame(MethodGen method,
552: InstructionHandle handle, int pc,
553: InstructionFactory insFactory, Frame frame) {
554: InstructionList insList = new InstructionList();
555:
556: // Remove needless return type from stack
557: InvokeInstruction inv = (InvokeInstruction) handle
558: .getInstruction();
559: Type returnType = getReturnType(method.getConstantPool()
560: .getConstantPool(), inv.getIndex());
561: if (returnType.getSize() > 0) {
562: insList.insert(InstructionFactory.createPop(returnType
563: .getSize()));
564: }
565: boolean skipFirst = returnType.getSize() > 0;
566:
567: // save stack
568: OperandStack os = frame.getStack();
569: for (int i = skipFirst ? 1 : 0; i < os.size(); i++) {
570: Type type = os.peek(i);
571: if (type instanceof BasicType) {
572: if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
573: type = Type.INT;
574: }
575: insList.append(InstructionFactory.createLoad(
576: STACK_TYPE, method.getMaxLocals() + 1));
577: insList.append(new SWAP()); // TODO: check for types with two words on stack
578: insList.append(insFactory.createInvoke(STACK_CLASS,
579: getPushMethod(type), Type.VOID,
580: new Type[] { type }, Constants.INVOKEVIRTUAL));
581: } else if (type == null) {
582: insList.append(InstructionConstants.POP);
583: } else if (type instanceof UninitializedObjectType) {
584: // After the remove of new, there shouldn't be a
585: // uninitialized object on the stack
586: } else if (type instanceof ReferenceType) {
587: insList.append(InstructionFactory.createLoad(
588: STACK_TYPE, method.getMaxLocals() + 1));
589: insList.append(new SWAP());
590: insList.append(insFactory.createInvoke(STACK_CLASS,
591: getPushMethod(Type.OBJECT), Type.VOID,
592: new Type[] { Type.OBJECT },
593: Constants.INVOKEVIRTUAL));
594: }
595: }
596: // add isCapturing test
597: insList.insert(new IFEQ(handle.getNext()));
598: // test if the continuation should be captured after the invocation
599: insList.insert(insFactory.createInvoke(CONTINUATION_CLASS,
600: CAPURING_METHOD, Type.BOOLEAN, Type.NO_ARGS,
601: Constants.INVOKEVIRTUAL));
602: insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE,
603: method.getMaxLocals()));
604: // test if continuation exists
605: insList.insert(new IFNULL(handle.getNext()));
606: insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE,
607: method.getMaxLocals()));
608: // save local variables
609: LocalVariables lvs = frame.getLocals();
610: for (int i = 0; i < lvs.maxLocals(); i++) {
611: Type type = lvs.get(i);
612: if (type instanceof BasicType) {
613: insList.append(InstructionFactory.createLoad(
614: STACK_TYPE, method.getMaxLocals() + 1));
615: insList.append(InstructionFactory.createLoad(type, i));
616: if (type.getSize() < 2 && !type.equals(Type.FLOAT))
617: type = Type.INT;
618: insList.append(insFactory.createInvoke(STACK_CLASS,
619: getPushMethod(type), Type.VOID,
620: new Type[] { type }, Constants.INVOKEVIRTUAL));
621: } else if (type == null) {
622: // no need to save null
623: } else if (type instanceof UninitializedObjectType) {
624: // no need to save uninitialized objects
625: } else if (type instanceof ReferenceType) {
626: if (i == 0 && !currentMethodStatic) {
627: // remember current object
628: insList.append(InstructionFactory.createLoad(
629: STACK_TYPE, method.getMaxLocals() + 1));
630: insList.append(InstructionFactory.createLoad(type,
631: i));
632: insList.append(insFactory.createInvoke(STACK_CLASS,
633: PUSH_METHOD + "Reference", Type.VOID,
634: new Type[] { Type.OBJECT },
635: Constants.INVOKEVIRTUAL));
636: }
637: insList.append(InstructionFactory.createLoad(
638: STACK_TYPE, method.getMaxLocals() + 1));
639: insList.append(InstructionFactory.createLoad(type, i));
640: insList.append(insFactory.createInvoke(STACK_CLASS,
641: getPushMethod(Type.OBJECT), Type.VOID,
642: new Type[] { Type.OBJECT },
643: Constants.INVOKEVIRTUAL));
644: }
645: }
646: // save programcounter
647: insList.append(InstructionFactory.createLoad(STACK_TYPE, method
648: .getMaxLocals() + 1));
649: insList.append(new PUSH(method.getConstantPool(), pc));
650: insList.append(insFactory.createInvoke(STACK_CLASS,
651: getPushMethod(Type.INT), Type.VOID,
652: new Type[] { Type.INT }, Constants.INVOKEVIRTUAL));
653: // return NULL result
654: insList.append(InstructionFactory.createNull(method
655: .getReturnType()));
656: insList.append(InstructionFactory.createReturn(method
657: .getReturnType()));
658: return insList;
659: }
660:
661: private InstructionList restoreFrame(MethodGen method,
662: InstructionHandle handle, InstructionFactory insFactory,
663: Frame frame, ObjectType objecttype) {
664: InstructionList insList = new InstructionList();
665: // restore local variables
666: LocalVariables lvs = frame.getLocals();
667: for (int i = lvs.maxLocals() - 1; i >= 0; i--) {
668: Type type = lvs.get(i);
669: if (type instanceof BasicType) {
670: insList.append(InstructionFactory.createLoad(
671: STACK_TYPE, method.getMaxLocals() + 1));
672: if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
673: type = Type.INT;
674: }
675: insList.append(insFactory.createInvoke(STACK_CLASS,
676: getPopMethod(type), type, Type.NO_ARGS,
677: Constants.INVOKEVIRTUAL));
678: insList.append(InstructionFactory.createStore(type, i));
679: } else if (type == null) {
680: insList.append(new ACONST_NULL());
681: insList.append(InstructionFactory.createStore(
682: new ObjectType("<null object>"), i));
683: } else if (type instanceof UninitializedObjectType) {
684: // No uninitilaized objects should be found
685: // in the local variables.
686: } else if (type instanceof ReferenceType) {
687: insList.append(InstructionFactory.createLoad(
688: STACK_TYPE, method.getMaxLocals() + 1));
689: insList.append(insFactory.createInvoke(STACK_CLASS,
690: getPopMethod(Type.OBJECT), Type.OBJECT,
691: Type.NO_ARGS, Constants.INVOKEVIRTUAL));
692: if (!type.equals(Type.OBJECT)
693: && (!type.equals(Type.NULL))) {
694: insList.append(insFactory.createCast(Type.OBJECT,
695: type));
696: }
697: insList.append(InstructionFactory.createStore(type, i));
698: }
699: }
700:
701: InvokeInstruction inv = (InvokeInstruction) handle
702: .getInstruction();
703: Type returnType = getReturnType(method.getConstantPool()
704: .getConstantPool(), inv.getIndex());
705: boolean skipFirst = returnType.getSize() > 0;
706:
707: // restore stack
708: OperandStack os = frame.getStack();
709: for (int i = os.size() - 1; i >= (skipFirst ? 1 : 0); i--) {
710: Type type = os.peek(i);
711: if (type instanceof BasicType) {
712: if (type.getSize() < 2 && !type.equals(Type.FLOAT)) {
713: type = Type.INT;
714: }
715: insList.append(InstructionFactory.createLoad(
716: STACK_TYPE, method.getMaxLocals() + 1));
717: insList.append(insFactory.createInvoke(STACK_CLASS,
718: getPopMethod(type), type, Type.NO_ARGS,
719: Constants.INVOKEVIRTUAL));
720: } else if (type == null) {
721: insList.append(new ACONST_NULL());
722: } else if (type instanceof UninitializedObjectType) {
723: // After the remove of new, there shouldn't be a
724: // uninitialized object on the stack
725: } else if (type instanceof ReferenceType) {
726: insList.append(InstructionFactory.createLoad(
727: STACK_TYPE, method.getMaxLocals() + 1));
728: insList.append(insFactory.createInvoke(STACK_CLASS,
729: getPopMethod(Type.OBJECT), Type.OBJECT,
730: Type.NO_ARGS, Constants.INVOKEVIRTUAL));
731: if (!type.equals(Type.OBJECT))
732: insList.append(insFactory.createCast(Type.OBJECT,
733: type));
734: }
735: }
736: // retrieve current object
737: if (!(inv instanceof INVOKESTATIC)) {
738: insList.append(InstructionFactory.createLoad(STACK_TYPE,
739: method.getMaxLocals() + 1));
740: insList.append(insFactory.createInvoke(STACK_CLASS,
741: POP_METHOD + "Reference", Type.OBJECT,
742: Type.NO_ARGS, Constants.INVOKEVIRTUAL));
743: insList.append(insFactory.createCast(Type.OBJECT,
744: objecttype));
745: }
746: // Create null types for the parameters of the method invocation
747: Type[] paramTypes = getParamTypes(method.getConstantPool()
748: .getConstantPool(), inv.getIndex());
749: for (int j = 0; j < paramTypes.length; j++) {
750: insList
751: .append(InstructionFactory
752: .createNull(paramTypes[j]));
753: }
754: // go to last invocation
755: insList.append(new GOTO(handle));
756: return insList;
757: }
758:
759: private ObjectType getObjectType(ConstantPool cp, int index) {
760: ConstantCP cmr = (ConstantCP) cp.getConstant(index);
761: String sig = cp.getConstantString(cmr.getClassIndex(),
762: Constants.CONSTANT_Class);
763: return new ObjectType(sig.replace('/', '.'));
764: }
765:
766: private Type[] getParamTypes(ConstantPool cp, int index) {
767: ConstantCP cmr = (ConstantCP) cp.getConstant(index);
768: ConstantNameAndType cnat = (ConstantNameAndType) cp
769: .getConstant(cmr.getNameAndTypeIndex());
770: String sig = ((ConstantUtf8) cp.getConstant(cnat
771: .getSignatureIndex())).getBytes();
772: return Type.getArgumentTypes(sig);
773: }
774:
775: private Type getReturnType(ConstantPool cp, int index) {
776: ConstantCP cmr = (ConstantCP) cp.getConstant(index);
777: ConstantNameAndType cnat = (ConstantNameAndType) cp
778: .getConstant(cmr.getNameAndTypeIndex());
779: String sig = ((ConstantUtf8) cp.getConstant(cnat
780: .getSignatureIndex())).getBytes();
781: return Type.getReturnType(sig);
782: }
783:
784: private String getPopMethod(Type type) {
785: return POP_METHOD + getTypeSuffix(type);
786: }
787:
788: private String getPushMethod(Type type) {
789: return PUSH_METHOD + getTypeSuffix(type);
790: }
791:
792: private String getTypeSuffix(Type type) {
793: if (type.equals(Type.BOOLEAN))
794: return "Int";
795: else if (type.equals(Type.CHAR))
796: return "Int";
797: else if (type.equals(Type.FLOAT))
798: return "Float";
799: else if (type.equals(Type.DOUBLE))
800: return "Double";
801: else if (type.equals(Type.BYTE))
802: return "Int";
803: else if (type.equals(Type.SHORT))
804: return "Int";
805: else if (type.equals(Type.INT))
806: return "Int";
807: else if (type.equals(Type.LONG))
808: return "Long";
809: // VOID and OBJECT are "Object"
810: return "Object";
811: }
812: }
|