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

Java tutorial

Introduction

Here is the source code for org.eclipse.objectteams.otredyn.bytecode.asm.MoveCodeToCallOrigAdapter.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 java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import org.eclipse.objectteams.otredyn.bytecode.Method;
import org.eclipse.objectteams.otredyn.transformer.IWeavingContext;
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.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;

/**
 * This class moves the code of a method to callOrig. 
 * @author Oliver Frank
 */
public class MoveCodeToCallOrigAdapter extends AbstractTransformableClassNode {
    private Method method;
    private int boundMethodId;
    private int firstArgIndex; // slot index of the first argument (0 (static) or 1 (non-static))
    private int argOffset; // used to skip synth args if the callOrig method itself is a statid role method
    private Method callOrig;
    private boolean superIsWeavable = true;

    public MoveCodeToCallOrigAdapter(AsmWritableBoundClass clazz, Method method, int boundMethodId,
            IWeavingContext weavingContext) {
        this.method = method;
        this.boundMethodId = boundMethodId;
        if (method.isStatic()) {
            firstArgIndex = 0;
            argOffset = clazz.isRole() ? 2 : 0;
            callOrig = clazz.getCallOrigStatic();
        } else {
            firstArgIndex = 1;
            callOrig = ConstantMembers.callOrig;
        }
        if (weavingContext != null)
            superIsWeavable = weavingContext.isWeavable(clazz.getSuperClassName());
    }

    public boolean transform() {
        MethodNode orgMethod = getMethod(method);
        if ((orgMethod.access & Opcodes.ACC_ABSTRACT) != 0)
            return false;

        MethodNode callOrig = getMethod(this.callOrig);

        Type returnType = Type.getReturnType(orgMethod.desc);

        InsnList newInstructions = new InsnList();

        //Unboxing arguments
        Type[] args = Type.getArgumentTypes(orgMethod.desc);

        int boundMethodIdSlot = firstArgIndex;

        if (args.length > 0) {
            // move boundMethodId to a higher slot, to make lower slots available for original locals
            newInstructions.add(new IntInsnNode(Opcodes.ILOAD, boundMethodIdSlot));
            boundMethodIdSlot = callOrig.maxLocals + 1;
            newInstructions.add(new IntInsnNode(Opcodes.ISTORE, boundMethodIdSlot));

            newInstructions.add(new IntInsnNode(Opcodes.ALOAD, firstArgIndex + argOffset + 1));

            int slot = firstArgIndex + argOffset;
            for (int i = argOffset; i < args.length; i++) {
                if (i < args.length - 1) {
                    newInstructions.add(new InsnNode(Opcodes.DUP));
                }
                newInstructions.add(createLoadIntConstant(i));
                newInstructions.add(new InsnNode(Opcodes.AALOAD));
                Type arg = args[i];
                if (arg.getSort() != Type.ARRAY && arg.getSort() != Type.OBJECT) {
                    String objectType = AsmTypeHelper.getObjectType(arg);
                    newInstructions.add(new TypeInsnNode(Opcodes.CHECKCAST, objectType));
                    newInstructions.add(AsmTypeHelper.getUnboxingInstructionForType(arg, objectType));
                } else {
                    newInstructions.add(new TypeInsnNode(Opcodes.CHECKCAST, arg.getInternalName()));
                }

                newInstructions.add(new IntInsnNode(args[i].getOpcode(Opcodes.ISTORE), slot));
                slot += arg.getSize();
            }
        }

        if (superIsWeavable)
            adjustSuperCalls(orgMethod.instructions, orgMethod.name, args, returnType, boundMethodIdSlot);

        // replace return of the original method with areturn and box the result value if needed
        replaceReturn(orgMethod.instructions, returnType);

        newInstructions.add(orgMethod.instructions);

        addNewLabelToSwitch(callOrig.instructions, newInstructions, boundMethodId);

        // a minimum stacksize of 3 is needed to box the arguments
        callOrig.maxStack = Math.max(Math.max(callOrig.maxStack, orgMethod.maxStack), 3);

        // we have to increment the max. stack size, because we have to put NULL on the stack
        if (returnType.getSort() == Type.VOID) {
            callOrig.maxStack += 1;
        }
        callOrig.maxLocals = Math.max(callOrig.maxLocals, orgMethod.maxLocals);
        return true;
    }

    /** To avoid infinite recursion, calls super.m(a1, a2) must be translated to super.callOrig(boundMethodId, new Object[] {a1, a2}). */
    private void adjustSuperCalls(InsnList instructions, String selector, Type[] args, Type returnType,
            int boundMethodIdSlot) {
        // search:
        List<MethodInsnNode> toReplace = new ArrayList<MethodInsnNode>();
        ListIterator<AbstractInsnNode> orgMethodIter = instructions.iterator();
        while (orgMethodIter.hasNext()) {
            AbstractInsnNode orgMethodNode = orgMethodIter.next();
            if (orgMethodNode.getOpcode() == Opcodes.INVOKESPECIAL
                    && ((MethodInsnNode) orgMethodNode).name.equals(selector))
                toReplace.add((MethodInsnNode) orgMethodNode);
        }
        if (toReplace.isEmpty())
            return;
        // replace:
        for (MethodInsnNode oldNode : toReplace) {
            // we need to insert into the loading sequence before the invocation, find the insertion points:
            AbstractInsnNode[] insertionPoints = StackBalanceAnalyzer.findInsertionPointsBefore(oldNode, args);
            AbstractInsnNode firstInsert = insertionPoints.length > 0 ? insertionPoints[0] : oldNode;

            // push first arg to _OT$callOrig():
            instructions.insertBefore(firstInsert, new IntInsnNode(Opcodes.ILOAD, boundMethodIdSlot));

            // prepare array as second arg to _OT$callOrig():
            instructions.insertBefore(firstInsert, new IntInsnNode(Opcodes.BIPUSH, args.length));
            instructions.insertBefore(firstInsert, new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object"));

            for (int i = 0; i < insertionPoints.length; i++) {
                // NB: each iteration has an even stack balance, where the top is the Object[].
                instructions.insertBefore(insertionPoints[i], new InsnNode(Opcodes.DUP));
                instructions.insertBefore(insertionPoints[i], new IntInsnNode(Opcodes.BIPUSH, i));
                // leave the original loading sequence in tact and continue at the next point:
                AbstractInsnNode insertAt = (i + 1 < insertionPoints.length) ? insertionPoints[i + 1] : oldNode;
                instructions.insertBefore(insertAt, AsmTypeHelper.getBoxingInstructionForType(args[i]));
                instructions.insertBefore(insertAt, new InsnNode(Opcodes.AASTORE));
            }

            if (returnType == Type.VOID_TYPE)
                instructions.insert(oldNode, new InsnNode(Opcodes.POP));
            else
                instructions.insert(oldNode, AsmTypeHelper.getUnboxingInstructionForType(returnType));

            instructions.set(oldNode, new MethodInsnNode(Opcodes.INVOKESPECIAL, ((MethodInsnNode) oldNode).owner,
                    callOrig.getName(), callOrig.getSignature()));
        }
    }
}