blob: a6915df82f084bdd7e18028addd4b215de184efa [file] [log] [blame]
/*
* 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";
}
}