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

Java tutorial

Introduction

Here is the source code for org.eclipse.objectteams.otredyn.bytecode.asm.CreateMethodAccessAdapter.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.ConstantMembers;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
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;

/**
 * Creates and adds the instructions,
 * that are needed to access a not visible method from the team
 * in the method access or accessStatic as follows:<br/> <br/>
 * case (memberId) { // generated by CreateSwitchForAccessAdapter <br/>
 *     return orgMethod(args[0], ..., args[args.length]); <br/>
 * }
 * 
 * @author Oliver Frank
 */
public class CreateMethodAccessAdapter extends AbstractTransformableClassNode {
    private Method method;
    private int accessId;
    private Method access;
    private int firstArgIndex;
    private boolean isConstructor;

    public CreateMethodAccessAdapter(Method method, int accessId) {
        this.method = method;
        this.accessId = accessId;
        isConstructor = method.getName().equals("<init>");
        if (method.isStatic() || isConstructor) {
            access = ConstantMembers.accessStatic;
            firstArgIndex = 0;
        } else {
            access = ConstantMembers.access;
            firstArgIndex = 1;
        }
    }

    @Override
    public boolean transform() {
        MethodNode methodNode = getMethod(method);
        InsnList instructions = new InsnList();

        if (isConstructor) {
            // create empty object for constructor invocation:
            instructions.add(new TypeInsnNode(Opcodes.NEW, name));
            instructions.add(new InsnNode(Opcodes.DUP));
        } else if (!method.isStatic()) {
            //put "this" on the stack for a non-static method
            instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
        }

        //Unbox arguments
        Type[] args = Type.getArgumentTypes(methodNode.desc);

        if (args.length > 0) {

            for (int i = 0; i < args.length; i++) {
                instructions.add(new IntInsnNode(Opcodes.ALOAD, firstArgIndex + 2));
                instructions.add(createLoadIntConstant(i));
                instructions.add(new InsnNode(Opcodes.AALOAD));
                Type arg = args[i];
                if (arg.getSort() != Type.ARRAY && arg.getSort() != Type.OBJECT) {
                    String objectType = AsmTypeHelper.getObjectType(arg);
                    instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, objectType));
                    instructions.add(AsmTypeHelper.getUnboxingInstructionForType(arg, objectType));
                } else {
                    instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, arg.getInternalName()));
                }
            }
        }

        //call original method
        int opcode = Opcodes.INVOKEVIRTUAL;
        if (method.isStatic()) {
            opcode = Opcodes.INVOKESTATIC;
        } else if (isConstructor) {
            opcode = Opcodes.INVOKESPECIAL;
        }
        instructions.add(new MethodInsnNode(opcode, name, method.getName(), method.getSignature()));

        //box return value
        Type returnType = Type.getReturnType(methodNode.desc);

        if (returnType.getSort() != Type.OBJECT && returnType.getSort() != Type.ARRAY
                && returnType.getSort() != Type.VOID) {

            instructions.add(AsmTypeHelper.getBoxingInstructionForType(returnType));
            instructions.add(new InsnNode(Opcodes.ARETURN));
        } else if (returnType.getSort() == Type.VOID && !isConstructor) {
            instructions.add(new InsnNode(Opcodes.ACONST_NULL));
            instructions.add(new InsnNode(Opcodes.ARETURN));
        } else {
            instructions.add(new InsnNode(Opcodes.ARETURN));
        }

        //add the instructions to a new label in the existing switch
        MethodNode access = getMethod(this.access);
        addNewLabelToSwitch(access.instructions, instructions, accessId);

        return true;
    }

}