org.eclipse.objectteams.otredyn.bytecode.asm.CreateCallAllBindingsCallInOrgMethod.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.objectteams.otredyn.bytecode.asm.CreateCallAllBindingsCallInOrgMethod.java

Source

/**********************************************************************
 * This file is part of "Object Teams Dynamic Runtime Environment"
 * 
 * Copyright 2009, 2014 Oliver Frank and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * 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.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.IntInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;

/**
 * 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.access & Opcodes.ACC_ABSTRACT) != 0)
            return false;

        //      if (method.name.equals("<init>")) {
        //         int size = method.instructions.size();
        //         for (int i = 0; i < size; i++) {
        //            AbstractInsnNode insn = method.instructions.get(i);
        //            System.out.println(insn+" "+insn.getOpcode());
        //         }
        //      }
        //      System.out.println("================");

        AbstractInsnNode insertBefore = null;
        if (orgMethod.getName().equals("<init>")) {
            // keep instructions, find insertion point:
            int last = method.instructions.size();
            LabelNode callAll = new LabelNode();
            while (--last >= 0) {
                if (method.instructions.get(last).getOpcode() == Opcodes.RETURN) {
                    AbstractInsnNode ret = method.instructions.get(last);
                    method.instructions.set(ret, callAll);
                    insertBefore = callAll;
                    break;
                }
            }
            if (insertBefore == null)
                throw new IllegalStateException("Insertion point for weaving into ctor not found!!!");

            // FIXME: triggers NPE in MethodVisitor.visitMaxs
            //         // replace RETURN with GOTO
            //         for (int i=0; i<last; i++) {
            //            AbstractInsnNode current = method.instructions.get(i);
            //            if (current.getOpcode() == Opcodes.RETURN)
            //               method.instructions.set(current, new JumpInsnNode(Opcodes.GOTO, callAll));
            //         }

        } else {
            method.instructions.clear();
        }

        // start of try-block:
        InsnList newInstructions = new InsnList();
        LabelNode start = new LabelNode();
        newInstructions.add(start);

        // put this on the stack
        newInstructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
        // put boundMethodId on the stack
        newInstructions.add(createLoadIntConstant(boundMethodId));
        Type[] args = Type.getArgumentTypes(method.desc);
        // box the arguments
        newInstructions.add(getBoxingInstructions(args, false));

        // this.callAllBindings(boundMethodId, args);
        newInstructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, this.name,
                ConstantMembers.callAllBindingsClient.getName(),
                ConstantMembers.callAllBindingsClient.getSignature()));
        Type returnType = Type.getReturnType(method.desc);
        newInstructions.add(getUnboxingInstructionsForReturnValue(returnType));

        if (insertBefore != null) {
            method.instructions.insertBefore(insertBefore, newInstructions);
            method.instructions.remove(insertBefore); // remove extra RETURN
        } else {
            method.instructions.add(newInstructions);
        }

        //      if (method.name.equals("<init>")) {
        //         int size = method.instructions.size();
        //         for (int i = 0; i < size; i++) {
        //            AbstractInsnNode insn = method.instructions.get(i);
        //            System.out.println(insn+" "+insn.getOpcode());
        //         }
        //      }

        // 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;
    }

    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;
    }
}