| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.components.flow.java; |
| |
| import java.util.ArrayList; |
| import java.util.Vector; |
| |
| import org.apache.bcel.Constants; |
| import org.apache.bcel.Repository; |
| import org.apache.bcel.classfile.ConstantCP; |
| import org.apache.bcel.classfile.ConstantNameAndType; |
| import org.apache.bcel.classfile.ConstantPool; |
| import org.apache.bcel.classfile.ConstantUtf8; |
| import org.apache.bcel.classfile.JavaClass; |
| import org.apache.bcel.classfile.Method; |
| import org.apache.bcel.generic.ACONST_NULL; |
| import org.apache.bcel.generic.BasicType; |
| import org.apache.bcel.generic.ClassGen; |
| import org.apache.bcel.generic.ConstantPoolGen; |
| import org.apache.bcel.generic.GOTO; |
| import org.apache.bcel.generic.IFEQ; |
| import org.apache.bcel.generic.IFNONNULL; |
| import org.apache.bcel.generic.IFNULL; |
| import org.apache.bcel.generic.INVOKESTATIC; |
| import org.apache.bcel.generic.InstructionConstants; |
| import org.apache.bcel.generic.InstructionFactory; |
| import org.apache.bcel.generic.InstructionHandle; |
| import org.apache.bcel.generic.InstructionList; |
| import org.apache.bcel.generic.InstructionTargeter; |
| import org.apache.bcel.generic.InvokeInstruction; |
| import org.apache.bcel.generic.MethodGen; |
| import org.apache.bcel.generic.ObjectType; |
| import org.apache.bcel.generic.PUSH; |
| import org.apache.bcel.generic.RET; |
| import org.apache.bcel.generic.ReferenceType; |
| import org.apache.bcel.generic.ReturnaddressType; |
| import org.apache.bcel.generic.SWAP; |
| import org.apache.bcel.generic.TABLESWITCH; |
| import org.apache.bcel.generic.TargetLostException; |
| import org.apache.bcel.generic.Type; |
| import org.apache.bcel.util.ClassLoaderRepository; |
| import org.apache.bcel.verifier.exc.AssertionViolatedException; |
| import org.apache.bcel.verifier.structurals.ControlFlowGraph; |
| import org.apache.bcel.verifier.structurals.ExceptionHandler; |
| import org.apache.bcel.verifier.structurals.ExecutionVisitor; |
| import org.apache.bcel.verifier.structurals.Frame; |
| import org.apache.bcel.verifier.structurals.InstConstraintVisitor; |
| import org.apache.bcel.verifier.structurals.InstructionContext; |
| import org.apache.bcel.verifier.structurals.LocalVariables; |
| import org.apache.bcel.verifier.structurals.OperandStack; |
| import org.apache.bcel.verifier.structurals.UninitializedObjectType; |
| |
| /** |
| * The classloader breakes the methods of the classes into pieces and |
| * add intercepting code to suspend the execution of the method. |
| * |
| * This code is based on the original idea of the BRAKES project. |
| * (http://www.cs.kuleuven.ac.be/~eddy/BRAKES/brakes.html). |
| * |
| * @author <a href="mailto:stephan@apache.org">Stephan Michels</a> |
| * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a> |
| * @version CVS $Id$ |
| */ |
| public class ContinuationClassLoader extends ClassLoader { |
| |
| private static final String CONTINUATION_CLASS = Continuation.class.getName(); |
| private static final ObjectType CONTINUATION_TYPE = new ObjectType(CONTINUATION_CLASS); |
| |
| private static final String STACK_CLASS = ContinuationStack.class.getName(); |
| private static final ObjectType STACK_TYPE = new ObjectType(STACK_CLASS); |
| |
| private static final String CONTINUABLE_CLASS = Continuable.class.getName(); |
| |
| private static final String CONTINUATIONCAPABLE_CLASS = ContinuationCapable.class.getName(); |
| |
| private static final String CONTINUATION_METHOD = "currentContinuation"; |
| private static final String STACK_METHOD = "getStack"; |
| private static final String POP_METHOD = "pop"; |
| private static final String PUSH_METHOD = "push"; |
| private static final String RESTORING_METHOD = "isRestoring"; |
| private static final String CAPURING_METHOD = "isCapturing"; |
| |
| private static boolean currentMethodStatic; |
| |
| public ContinuationClassLoader(ClassLoader parent) { |
| super(parent); |
| Repository.setRepository(new ClassLoaderRepository(parent)); |
| } |
| |
| protected synchronized Class loadClass(String name, boolean resolve) |
| throws ClassNotFoundException { |
| // this finds also classes, which are already transformed, via findLoadedClass |
| Class c = super.loadClass(name, resolve); |
| |
| // transform class if class is continuable and not continuation capable |
| if ((Continuable.class.isAssignableFrom(c)) && |
| (!ContinuationCapable.class.isAssignableFrom(c)) && |
| (!c.isInterface())) { |
| JavaClass clazz = Repository.lookupClass(c); |
| |
| byte data[] = transform(clazz); |
| c = defineClass(name, data, 0, data.length); |
| } |
| if (c == null) { |
| throw new ClassNotFoundException(name); |
| } |
| if (resolve) { |
| resolveClass(c); |
| } |
| return c; |
| } |
| |
| private byte[] transform(JavaClass javaclazz) throws ClassNotFoundException { |
| // make all methods of java class continuable |
| ClassGen clazz = new ClassGen(javaclazz); |
| ConstantPoolGen cp = clazz.getConstantPool(); |
| // obsolete, but neccesary to execute the InvokeContext |
| InstConstraintVisitor icv = new InstConstraintVisitor(); |
| icv.setConstantPoolGen(cp); |
| // vistor to build the frame information |
| ExecutionVisitor ev = new ExecutionVisitor(); |
| ev.setConstantPoolGen(cp); |
| |
| Method[] methods = clazz.getMethods(); |
| for (int i = 0; i < methods.length; i++) { |
| MethodGen method = new MethodGen(methods[i], clazz.getClassName(), cp); |
| |
| currentMethodStatic = methods[i].isStatic(); |
| if (isValid(method)) { |
| // analyse the code of the method to create the frame |
| // information about every instruction |
| ControlFlowGraph cfg = new ControlFlowGraph(method); |
| analyse(clazz, method, cfg, icv, ev); |
| // add intercepting code |
| rewrite(method, cfg); |
| // make last optional check for consistency |
| clazz.replaceMethod(methods[i], method.getMethod()); |
| } |
| } |
| clazz.addInterface(CONTINUATIONCAPABLE_CLASS); |
| return clazz.getJavaClass().getBytes(); |
| } |
| |
| private boolean isValid(MethodGen m) { |
| if (m.getName().equals(Constants.CONSTRUCTOR_NAME) |
| || m.getName().equals(Constants.STATIC_INITIALIZER_NAME) |
| || m.isNative() |
| || m.isAbstract()) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| private void analyse(ClassGen clazz, MethodGen method, ControlFlowGraph cfg, |
| InstConstraintVisitor icv, ExecutionVisitor ev) { |
| // build the initial frame situation for this method. |
| Frame vanillaFrame = new Frame(method.getMaxLocals(), method.getMaxStack()); |
| if (!method.isStatic()) { |
| if (method.getName().equals(Constants.CONSTRUCTOR_NAME)) { |
| Frame._this = new UninitializedObjectType(new ObjectType(clazz.getClassName())); |
| vanillaFrame.getLocals().set(0, new UninitializedObjectType(new ObjectType(clazz.getClassName()))); |
| } else { |
| Frame._this = null; |
| vanillaFrame.getLocals().set(0, new ObjectType(clazz.getClassName())); |
| } |
| } |
| // fill local variables with parameter types |
| Type[] argtypes = method.getArgumentTypes(); |
| int twoslotoffset = 0; |
| for (int j = 0; j < argtypes.length; j++) { |
| if ((argtypes[j] == Type.SHORT) || |
| (argtypes[j] == Type.BYTE) || |
| (argtypes[j] == Type.CHAR) || |
| (argtypes[j] == Type.BOOLEAN)) { |
| argtypes[j] = Type.INT; |
| } |
| vanillaFrame.getLocals().set(twoslotoffset + j + (method.isStatic() ? 0 : 1), argtypes[j]); |
| if (argtypes[j].getSize() == 2) { |
| twoslotoffset++; |
| vanillaFrame.getLocals().set(twoslotoffset + j + (method.isStatic() ? 0 : 1), Type.UNKNOWN); |
| } |
| } |
| icv.setMethodGen(method); |
| |
| Vector ics = new Vector(); // Type: InstructionContext |
| Vector ecs = new Vector(); // Type: ArrayList (of InstructionContext) |
| |
| InstructionContext start = cfg.contextOf(method.getInstructionList().getStart()); |
| |
| start.execute(vanillaFrame, new ArrayList(), icv, ev); |
| // new ArrayList() <=> no Instruction was executed before |
| // => Top-Level routine (no jsr call before) |
| ics.add(start); |
| ecs.add(new ArrayList()); |
| |
| while (!ics.isEmpty()) { |
| InstructionContext u = (InstructionContext)ics.remove(0); |
| ArrayList ec = (ArrayList)ecs.remove(0); |
| |
| ArrayList oldchain = (ArrayList)(ec.clone()); |
| ArrayList newchain = (ArrayList)(ec.clone()); |
| newchain.add(u); |
| |
| if ((u.getInstruction().getInstruction()) instanceof RET) { |
| // We can only follow _one_ successor, the one after the |
| // JSR that was recently executed. |
| RET ret = (RET)u.getInstruction().getInstruction(); |
| ReturnaddressType t = (ReturnaddressType)u.getOutFrame(oldchain).getLocals().get(ret.getIndex()); |
| InstructionContext theSuccessor = cfg.contextOf(t.getTarget()); |
| |
| if (theSuccessor.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { |
| ics.add(theSuccessor); |
| ecs.add(newchain.clone()); |
| } |
| } else { // "not a ret" |
| // Normal successors. Add them to the queue of successors. |
| InstructionContext[] succs = u.getSuccessors(); |
| for (int s = 0; s < succs.length; s++) { |
| InstructionContext v = succs[s]; |
| if (v.execute(u.getOutFrame(oldchain), newchain, icv, ev)) { |
| ics.add(v); |
| ecs.add(newchain.clone()); |
| } |
| } |
| } |
| // Exception Handlers. Add them to the queue of successors. |
| ExceptionHandler[] exc_hds = u.getExceptionHandlers(); |
| for (int s = 0; s < exc_hds.length; s++) { |
| InstructionContext v = cfg.contextOf(exc_hds[s].getHandlerStart()); |
| // TODO: the "oldchain" and "newchain" is used to determine the subroutine |
| // we're in (by searching for the last JSR) by the InstructionContext |
| // implementation. Therefore, we should not use this chain mechanism |
| // when dealing with exception handlers. |
| |
| LocalVariables newLocals = u.getOutFrame(oldchain).getLocals(); |
| OperandStack newStack = new OperandStack( |
| u.getOutFrame(oldchain).getStack().maxStack(), |
| (exc_hds[s].getExceptionType() == null |
| ? Type.THROWABLE |
| : exc_hds[s].getExceptionType())); |
| Frame newFrame = new Frame(newLocals, newStack); |
| |
| if (v.execute(newFrame, new ArrayList(), icv, ev)) { |
| ics.add(v); |
| ecs.add(new ArrayList()); |
| } |
| } |
| } |
| } |
| |
| private void rewrite(MethodGen method, ControlFlowGraph cfg) |
| throws ClassNotFoundException { |
| InstructionFactory insFactory = new InstructionFactory(method.getConstantPool()); |
| Vector invokeIns = new Vector(); |
| int count = 0; |
| InstructionList insList = method.getInstructionList(); |
| InstructionHandle ins = insList.getStart(); |
| InstructionList restorer = new InstructionList(); |
| while (ins != null) { |
| InstructionHandle next = ins.getNext(); |
| |
| // if not traversed by the analyser, then don't rewrite |
| InstructionContext context = null; |
| Frame frame = null; |
| try { |
| context = cfg.contextOf(ins); |
| frame = context.getOutFrame(new ArrayList()); |
| } catch (AssertionViolatedException ave) { |
| // empty |
| } |
| if (frame != null) { |
| if (rewriteable(method, ins)) { |
| // Add frame saver and restorer for the current breakpoint |
| |
| // determine type of object for the method invocation |
| InvokeInstruction invoke = (InvokeInstruction)ins.getInstruction(); |
| Type[] arguments = invoke.getArgumentTypes(method.getConstantPool()); |
| ObjectType objecttype = null; |
| if (!(invoke instanceof INVOKESTATIC)) { |
| objecttype = (ObjectType)context.getInFrame().getStack().peek(arguments.length); |
| } |
| InstructionList rList = restoreFrame(method, ins, insFactory, frame, objecttype); |
| insList.append(ins, saveFrame(method, ins, count++, insFactory, frame)); |
| invokeIns.addElement(rList.getStart()); |
| restorer.append(rList); |
| } |
| // remove all new's |
| if (ins.getInstruction().getOpcode() == Constants.NEW) { |
| try { |
| // remove additional dup's |
| while (next != null && next.getInstruction().getOpcode() == Constants.DUP) { |
| context = cfg.contextOf(next); |
| frame = context.getOutFrame(new ArrayList()); |
| InstructionHandle newnext = next.getNext(); |
| insList.delete(next); |
| next = newnext; |
| } |
| InstructionTargeter[] targeter = ins.getTargeters(); |
| if (targeter != null) { |
| InstructionHandle newnext = ins.getNext(); |
| for (int i = 0; i < targeter.length; i++) { |
| targeter[i].updateTarget(ins, newnext); |
| } |
| } |
| insList.delete(ins); |
| } catch (TargetLostException tle) { |
| throw new ClassNotFoundException(tle.getMessage(), tle); |
| } |
| } else if (ins.getInstruction().getOpcode() == Constants.INVOKESPECIAL) { |
| // duplicate stack before invokespecial to insert uninitialized object |
| frame = context.getInFrame(); |
| InvokeInstruction invoke = (InvokeInstruction)ins.getInstruction(); |
| Type[] arguments = invoke.getArgumentTypes(method.getConstantPool()); |
| |
| OperandStack os = frame.getStack(); |
| Type type = os.peek(arguments.length); |
| if (type instanceof UninitializedObjectType) { |
| ObjectType objecttype = ((UninitializedObjectType) type).getInitialized(); |
| InstructionList duplicator = duplicateStack(method, invoke, objecttype); |
| InstructionTargeter[] targeter = ins.getTargeters(); |
| |
| if (targeter!=null) { |
| InstructionHandle newnext = duplicator.getStart(); |
| for(int i=0; i < targeter.length; i++) { |
| targeter[i].updateTarget(ins, newnext); |
| } |
| } |
| insList.insert(ins, duplicator); |
| } |
| } |
| } |
| ins = next; |
| } |
| InstructionHandle firstIns = insList.getStart(); |
| if (count > 0) { |
| InstructionHandle[] tableTargets = new InstructionHandle[count]; |
| int[] match = new int[count]; |
| for (int i = 0; i < count; i++) { |
| match[i] = i; |
| } |
| invokeIns.copyInto(tableTargets); |
| insList.insert(restorer); |
| |
| // select frame restorer |
| insList.insert(new TABLESWITCH(match, tableTargets, firstIns)); |
| insList.insert(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.INT), Type.INT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| insList.insert(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| |
| // test if the continuation should be restored |
| insList.insert(new IFEQ(firstIns)); |
| insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, RESTORING_METHOD, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals())); |
| } |
| |
| // get stack from current continuation and store in the last local variable |
| insList.insert(InstructionFactory.createStore(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, STACK_METHOD, STACK_TYPE, |
| Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| InstructionHandle restore_handle = insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals())); |
| |
| // if not continuation exists, create empty stack |
| insList.insert(new GOTO(firstIns)); |
| insList.insert(InstructionFactory.createStore(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.insert(insFactory.createInvoke(STACK_CLASS, Constants.CONSTRUCTOR_NAME, Type.VOID, Type.NO_ARGS, Constants. INVOKESPECIAL)); |
| insList.insert(InstructionFactory.createDup(STACK_TYPE.getSize())); |
| insList.insert(insFactory.createNew(STACK_TYPE)); |
| |
| // test if no current continuation exists |
| insList.insert(new IFNONNULL(restore_handle)); |
| insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals())); |
| |
| // get current continuation and store in the next to last local variable |
| insList.insert(InstructionFactory.createStore(CONTINUATION_TYPE, method.getMaxLocals())); |
| insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, CONTINUATION_METHOD, CONTINUATION_TYPE, |
| Type.NO_ARGS, Constants.INVOKESTATIC)); |
| |
| // make room for additional objects |
| method.setMaxLocals(method.getMaxLocals() + 2); |
| method.setMaxStack(method.getMaxStack() + 2); |
| } |
| |
| private InstructionList duplicateStack(MethodGen method, InvokeInstruction invoke, |
| ObjectType objecttype) throws ClassNotFoundException { |
| // reconstruction of an uninitialed object to call the constructor. |
| InstructionFactory insFactory = new InstructionFactory(method.getConstantPool()); |
| InstructionList insList = new InstructionList(); |
| |
| Type[] arguments = invoke.getArgumentTypes(method.getConstantPool()); |
| // pop all arguments for the constructor from the stack |
| for (int i = arguments.length - 1; i >= 0; i--) { |
| Type type = arguments[i]; |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(new SWAP()); |
| if (type instanceof BasicType) { |
| if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
| type = Type.INT; |
| } |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(type), Type.VOID, new Type[]{type}, Constants.INVOKEVIRTUAL)); |
| } else if (type instanceof ReferenceType) { |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL)); |
| } |
| } |
| // create uninitialzed object |
| insList.append(insFactory.createNew(objecttype)); |
| insList.append(InstructionFactory.createDup(objecttype.getSize())); |
| // return the arguments into the stack |
| for (int i = 0; i < arguments.length; i++) { |
| Type type = arguments[i]; |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| if (type instanceof BasicType) { |
| if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
| type = Type.INT; |
| } |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| } else if (type instanceof ReferenceType) { |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| if (!type.equals(Type.OBJECT)) { |
| insList.append(insFactory.createCast(Type.OBJECT, type)); |
| } |
| } |
| } |
| return insList; |
| } |
| |
| private boolean rewriteable(MethodGen method, InstructionHandle handle) |
| throws ClassNotFoundException { |
| // check in the invocation can be a breakpoint. |
| int opcode = handle.getInstruction().getOpcode(); |
| boolean invokeSpecialSuper = false; |
| if (opcode == Constants.INVOKESPECIAL) { |
| InvokeInstruction ivs = (InvokeInstruction) handle.getInstruction(); |
| String mName = ivs.getMethodName(method.getConstantPool()); |
| invokeSpecialSuper = !mName.equals(Constants.CONSTRUCTOR_NAME); |
| } |
| |
| if (opcode == Constants.INVOKEVIRTUAL || |
| opcode == Constants.INVOKESTATIC || |
| opcode == Constants.INVOKEINTERFACE || |
| invokeSpecialSuper) { |
| |
| int index = ((InvokeInstruction) handle.getInstruction()).getIndex(); |
| String classname = getObjectType(method.getConstantPool().getConstantPool(), index).getClassName(); |
| |
| // rewrite invocation if object is continuable or a continuation object |
| return Repository.implementationOf(classname, CONTINUABLE_CLASS) || |
| Repository.instanceOf(classname, CONTINUATION_CLASS); |
| } |
| return false; |
| } |
| |
| private InstructionList saveFrame(MethodGen method, InstructionHandle handle, int pc, |
| InstructionFactory insFactory, Frame frame) { |
| InstructionList insList = new InstructionList(); |
| |
| // Remove needless return type from stack |
| InvokeInstruction inv = (InvokeInstruction) handle.getInstruction(); |
| Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex()); |
| if (returnType.getSize() > 0) { |
| insList.insert(InstructionFactory.createPop(returnType.getSize())); |
| } |
| boolean skipFirst = returnType.getSize() > 0; |
| |
| // save stack |
| OperandStack os = frame.getStack(); |
| for (int i = skipFirst ? 1 : 0; i < os.size(); i++) { |
| Type type = os.peek(i); |
| if (type instanceof BasicType) { |
| if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
| type = Type.INT; |
| } |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(new SWAP()); // TODO: check for types with two words on stack |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(type), Type.VOID, new Type[]{type}, Constants.INVOKEVIRTUAL)); |
| } else if (type == null) { |
| insList.append(InstructionConstants.POP); |
| } else if (type instanceof UninitializedObjectType) { |
| // After the remove of new, there shouldn't be a |
| // uninitialized object on the stack |
| } else if (type instanceof ReferenceType) { |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(new SWAP()); |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL)); |
| } |
| } |
| // add isCapturing test |
| insList.insert(new IFEQ(handle.getNext())); |
| // test if the continuation should be captured after the invocation |
| insList.insert(insFactory.createInvoke(CONTINUATION_CLASS, CAPURING_METHOD, Type.BOOLEAN, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals())); |
| // test if continuation exists |
| insList.insert(new IFNULL(handle.getNext())); |
| insList.insert(InstructionFactory.createLoad(CONTINUATION_TYPE, method.getMaxLocals())); |
| // save local variables |
| LocalVariables lvs = frame.getLocals(); |
| for (int i = 0; i < lvs.maxLocals(); i++) { |
| Type type = lvs.get(i); |
| if (type instanceof BasicType) { |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(InstructionFactory.createLoad(type, i)); |
| if (type.getSize() < 2 && !type.equals(Type.FLOAT)) |
| type = Type.INT; |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(type), Type.VOID, new Type[]{type}, Constants.INVOKEVIRTUAL)); |
| } else if (type == null) { |
| // no need to save null |
| } else if (type instanceof UninitializedObjectType) { |
| // no need to save uninitialized objects |
| } else if (type instanceof ReferenceType) { |
| if (i == 0 && !currentMethodStatic) { |
| // remember current object |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(InstructionFactory.createLoad(type, i)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, PUSH_METHOD + "Reference", Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL)); |
| } |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(InstructionFactory.createLoad(type, i)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.OBJECT), Type.VOID, new Type[]{Type.OBJECT}, Constants.INVOKEVIRTUAL)); |
| } |
| } |
| // save programcounter |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(new PUSH(method.getConstantPool(), pc)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPushMethod(Type.INT), Type.VOID, new Type[]{Type.INT}, Constants.INVOKEVIRTUAL)); |
| // return NULL result |
| insList.append(InstructionFactory.createNull(method.getReturnType())); |
| insList.append(InstructionFactory.createReturn(method.getReturnType())); |
| return insList; |
| } |
| |
| private InstructionList restoreFrame(MethodGen method, InstructionHandle handle, |
| InstructionFactory insFactory, Frame frame, ObjectType objecttype) { |
| InstructionList insList = new InstructionList(); |
| // restore local variables |
| LocalVariables lvs = frame.getLocals(); |
| for (int i = lvs.maxLocals() - 1; i >= 0; i--) { |
| Type type = lvs.get(i); |
| if (type instanceof BasicType) { |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
| type = Type.INT; |
| } |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| insList.append(InstructionFactory.createStore(type, i)); |
| } else if (type == null) { |
| insList.append(new ACONST_NULL()); |
| insList.append(InstructionFactory.createStore(new ObjectType("<null object>"), i)); |
| } else if (type instanceof UninitializedObjectType) { |
| // No uninitilaized objects should be found |
| // in the local variables. |
| } else if (type instanceof ReferenceType) { |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| if (!type.equals(Type.OBJECT) && (!type.equals(Type.NULL))) { |
| insList.append(insFactory.createCast(Type.OBJECT, type)); |
| } |
| insList.append(InstructionFactory.createStore(type, i)); |
| } |
| } |
| |
| InvokeInstruction inv = (InvokeInstruction) handle.getInstruction(); |
| Type returnType = getReturnType(method.getConstantPool().getConstantPool(), inv.getIndex()); |
| boolean skipFirst = returnType.getSize() > 0; |
| |
| // restore stack |
| OperandStack os = frame.getStack(); |
| for (int i = os.size() - 1; i >= (skipFirst ? 1 : 0); i--) { |
| Type type = os.peek(i); |
| if (type instanceof BasicType) { |
| if (type.getSize() < 2 && !type.equals(Type.FLOAT)) { |
| type = Type.INT; |
| } |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(type), type, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| } else if (type == null) { |
| insList.append(new ACONST_NULL()); |
| } else if (type instanceof UninitializedObjectType) { |
| // After the remove of new, there shouldn't be a |
| // uninitialized object on the stack |
| } else if (type instanceof ReferenceType) { |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, getPopMethod(Type.OBJECT), Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| if (!type.equals(Type.OBJECT)) |
| insList.append(insFactory.createCast(Type.OBJECT, type)); |
| } |
| } |
| // retrieve current object |
| if (!(inv instanceof INVOKESTATIC)) { |
| insList.append(InstructionFactory.createLoad(STACK_TYPE, method.getMaxLocals()+1)); |
| insList.append(insFactory.createInvoke(STACK_CLASS, POP_METHOD + "Reference", Type.OBJECT, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); |
| insList.append(insFactory.createCast(Type.OBJECT, objecttype)); |
| } |
| // Create null types for the parameters of the method invocation |
| Type[] paramTypes = getParamTypes(method.getConstantPool().getConstantPool(), inv.getIndex()); |
| for (int j = 0; j < paramTypes.length; j++) { |
| insList.append(InstructionFactory.createNull(paramTypes[j])); |
| } |
| // go to last invocation |
| insList.append(new GOTO(handle)); |
| return insList; |
| } |
| |
| private ObjectType getObjectType(ConstantPool cp, int index) { |
| ConstantCP cmr = (ConstantCP) cp.getConstant(index); |
| String sig = cp.getConstantString(cmr.getClassIndex(), Constants.CONSTANT_Class); |
| return new ObjectType(sig.replace('/', '.')); |
| } |
| |
| private Type[] getParamTypes(ConstantPool cp, int index) { |
| ConstantCP cmr = (ConstantCP) cp.getConstant(index); |
| ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); |
| String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes(); |
| return Type.getArgumentTypes(sig); |
| } |
| |
| private Type getReturnType(ConstantPool cp, int index) { |
| ConstantCP cmr = (ConstantCP) cp.getConstant(index); |
| ConstantNameAndType cnat = (ConstantNameAndType) cp.getConstant(cmr.getNameAndTypeIndex()); |
| String sig = ((ConstantUtf8) cp.getConstant(cnat.getSignatureIndex())).getBytes(); |
| return Type.getReturnType(sig); |
| } |
| |
| private String getPopMethod(Type type) { |
| return POP_METHOD + getTypeSuffix(type); |
| } |
| |
| private String getPushMethod(Type type) { |
| return PUSH_METHOD + getTypeSuffix(type); |
| } |
| |
| private String getTypeSuffix(Type type) { |
| if (type.equals(Type.BOOLEAN)) |
| return "Int"; |
| else if (type.equals(Type.CHAR)) |
| return "Int"; |
| else if (type.equals(Type.FLOAT)) |
| return "Float"; |
| else if (type.equals(Type.DOUBLE)) |
| return "Double"; |
| else if (type.equals(Type.BYTE)) |
| return "Int"; |
| else if (type.equals(Type.SHORT)) |
| return "Int"; |
| else if (type.equals(Type.INT)) |
| return "Int"; |
| else if (type.equals(Type.LONG)) |
| return "Long"; |
| // VOID and OBJECT are "Object" |
| return "Object"; |
| } |
| } |