blob: 44608b1bdbf0c02d73a89071c03bb25a02ffbdc7 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Dynamic Runtime Environment"
*
* Copyright 2009, 2019 Oliver Frank and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0 *
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Oliver Frank - Initial API and implementation
* Stephan Herrmann - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otredyn.bytecode.asm;
import org.eclipse.objectteams.otredyn.bytecode.Method;
import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
import org.eclipse.objectteams.otredyn.util.SMAPConstants;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* This class creates and adds the instructions, that are needed
* to call the method callAllBindings to a method.<br/> <br/>
* The instructions looks as follows:<br/>
* <code>
* Object[] args = {args1, ..., argsn};<br/>
* this.callAllBindings(boundMethodId, args);
* </code>
* @author Oliver Frank
*/
public class CreateCallAllBindingsCallInOrgMethod extends
AbstractTransformableClassNode {
private Method orgMethod;
private int boundMethodId;
public CreateCallAllBindingsCallInOrgMethod(Method orgMethod,
int boundMethodId) {
this.orgMethod = orgMethod;
this.boundMethodId = boundMethodId;
}
@Override
public boolean transform() {
MethodNode method = getMethod(orgMethod);
if (method == null || (method.access & Opcodes.ACC_ABSTRACT) != 0) return false;
// start of try-block:
InsnList newInstructions = new InsnList();
LabelNode start = new LabelNode();
newInstructions.add(start);
Type[] args = Type.getArgumentTypes(method.desc);
{
if (orgMethod.getName().equals("<init>")) {
// keep instructions, find insertion points:
int last = method.instructions.size();
LabelNode callAll = new LabelNode();
boolean hasGenerated = true;
for (int i=0; i<last; i++) {
AbstractInsnNode returnCandidate = method.instructions.get(i);
if (returnCandidate.getOpcode() == Opcodes.RETURN) {
method.instructions.set(returnCandidate, callAll);
generateInvocation(method, args, callAll, newInstructions);
hasGenerated = true;
}
}
if (!hasGenerated)
throw new IllegalStateException("Insertion point for weaving into ctor not found!!!");
} else {
int startLine = peekFirstLineNumber(method.instructions);
if (startLine == -1)
startLine = 1;
method.instructions.clear();
addLineNumber(newInstructions, startLine);
generateInvocation(method, args, null, newInstructions);
}
}
// catch and unwrap SneakyException:
addCatchSneakyException(method, start);
int localSlots = 0;
int maxArgSize = 1;
for (Type type : args) {
int size = type.getSize();
localSlots += size;
if (size == 2)
maxArgSize = 2;
}
method.maxStack = args.length > 0 ? 5+maxArgSize : 3;
method.maxLocals = localSlots+1;
return true;
}
private void generateInvocation(MethodNode method, Type[] args, AbstractInsnNode insertBefore,
InsnList newInstructions) {
// put this on the stack
newInstructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// put boundMethodId on the stack
if(method.name.equals("<init>")) { // set bit 0x8000000 to signal the ctor
newInstructions.add(createLoadIntConstant(0x8000_0000 | boundMethodId));
} else {
newInstructions.add(createLoadIntConstant(boundMethodId));
}
// box the arguments
newInstructions.add(getBoxingInstructions(args, false));
// this.callAllBindings(boundMethodId, args);
addLineNumber(newInstructions, SMAPConstants.STEP_INTO_LINENUMBER);
newInstructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
this.name, ConstantMembers.callAllBindingsClient.getName(),
ConstantMembers.callAllBindingsClient.getSignature(), false));
Type returnType = Type.getReturnType(method.desc);
newInstructions
.add(getUnboxingInstructionsForReturnValue(returnType));
addLineNumber(newInstructions, SMAPConstants.STEP_OVER_LINENUMBER);
if (insertBefore != null) {
method.instructions.insertBefore(insertBefore, newInstructions);
method.instructions.remove(insertBefore); // remove extra RETURN
} else {
method.instructions.add(newInstructions);
}
}
void addCatchSneakyException(MethodNode method, LabelNode start) {
method.tryCatchBlocks.add(getCatchBlock(method.instructions, start, orgMethod));
}
TryCatchBlockNode getCatchBlock(InsnList instructions, LabelNode start, Method method) {
// end (exclusive) of try-block
LabelNode end = new LabelNode();
instructions.add(end);
// catch (SneakyException e) { e.rethrow(); }
LabelNode catchSneaky = new LabelNode();
instructions.add(catchSneaky);
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, ClassNames.SNEAKY_EXCEPTION_SLASH, ClassNames.RETHROW_SELECTOR, ClassNames.RETHROW_SIGNATURE, false));
// never reached, just to please the verifier:
Type returnType = Type.getReturnType(method.getSignature());
instructions.add(getReturnInsn(returnType));
return new TryCatchBlockNode(start, end, catchSneaky, ClassNames.SNEAKY_EXCEPTION_SLASH);
}
protected InsnList getReturnInsn(Type returnType) {
InsnList instructions = new InsnList();
switch (returnType.getSort()) {
case Type.VOID:
instructions.add(new InsnNode(Opcodes.RETURN));
break;
case Type.ARRAY:
case Type.OBJECT:
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
instructions.add(new InsnNode(Opcodes.ARETURN));
break;
case Type.BOOLEAN:
case Type.CHAR:
case Type.BYTE:
case Type.INT:
case Type.SHORT:
case Type.LONG:
instructions.add(new InsnNode(Opcodes.ICONST_0));
instructions.add(new InsnNode(Opcodes.IRETURN));
break;
case Type.DOUBLE:
instructions.add(new InsnNode(Opcodes.DCONST_0));
instructions.add(new InsnNode(Opcodes.DRETURN));
break;
case Type.FLOAT:
instructions.add(new InsnNode(Opcodes.FCONST_0));
instructions.add(new InsnNode(Opcodes.FRETURN));
break;
}
return instructions;
}
}