pt.minha.kernel.instrument.SyncToMonitorClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for pt.minha.kernel.instrument.SyncToMonitorClassVisitor.java

Source

/*
 * Minha.pt: middleware testing platform.
 * Copyright (c) 2011-2014, Universidade do Minho.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package pt.minha.kernel.instrument;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

public class SyncToMonitorClassVisitor extends ClassVisitor {
    private String clz;
    private boolean hasClinit;
    private int access;
    private Translation trans;

    public SyncToMonitorClassVisitor(ClassVisitor visitor, Translation trans) {
        super(Opcodes.ASM5, visitor);
        this.trans = trans;
    }

    public void visit(int version, int access, String name, String signature, String superName,
            String[] interfaces) {
        if (trans.isSynchronized()) {
            clz = name;
            this.access = access;
            if ((access & Opcodes.ACC_INTERFACE) == 0) {
                FieldVisitor fv = visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "_fake_class",
                        "L" + ClassConfig.fake_prefix + "java/lang/Object;", null, null);
                fv.visitEnd();
            }
        }
        super.visit(version, access, name, signature, superName, interfaces);
    }

    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (trans.isSynchronized()) {
            if (name.equals("<clinit>")) {
                hasClinit = true;
                return new ClinitVisitor(super.visitMethod(access, name, desc, signature, exceptions));
            }

            if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
                makeStub(access & ~Opcodes.ACC_SYNCHRONIZED, name, desc, signature, exceptions);

                return super.visitMethod(
                        (access & ~(Opcodes.ACC_SYNCHRONIZED | Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)
                                | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PRIVATE),
                        "_" + name, desc, signature, exceptions);
            }
        }
        return super.visitMethod(access, name, desc, signature, exceptions);
    }

    public void visitEnd() {
        if (trans.isSynchronized()) {
            if (!hasClinit && (access & Opcodes.ACC_INTERFACE) == 0)
                mkClinit();
        }
        super.visitEnd();
    }

    private void mkClinit() {
        MethodVisitor mv = visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
        mv.visitCode();
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }

    private class ClinitVisitor extends MethodVisitor {
        public ClinitVisitor(MethodVisitor arg0) {
            super(Opcodes.ASM5, arg0);
        }

        public void visitCode() {
            super.visitCode();
            if ((access & Opcodes.ACC_INTERFACE) != 0)
                return;
            mv.visitTypeInsn(Opcodes.NEW, ClassConfig.fake_prefix + "java/lang/Object");
            mv.visitInsn(Opcodes.DUP);
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, ClassConfig.fake_prefix + "java/lang/Object", "<init>", "()V",
                    false);
            mv.visitFieldInsn(Opcodes.PUTSTATIC, clz, "_fake_class",
                    "L" + ClassConfig.fake_prefix + "java/lang/Object;");
        }
    }

    public void makeStub(int access, String name, String desc, String signature, String[] exceptions) {
        Method m = new Method(name, desc);

        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        mv.visitCode();

        Label begin = new Label();
        Label pre_invoke = new Label();
        Label pos_leave = new Label();
        Label in_catch = new Label();
        Label pre_rethrow = new Label();
        Label end = new Label();

        mv.visitTryCatchBlock(pre_invoke, pos_leave, in_catch, null);
        mv.visitTryCatchBlock(in_catch, pre_rethrow, in_catch, null);

        mv.visitLabel(begin);

        int offset;
        if ((access & Opcodes.ACC_STATIC) == 0) {
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            offset = 1;
        } else {
            mv.visitFieldInsn(Opcodes.GETSTATIC, clz, "_fake_class",
                    "L" + ClassConfig.fake_prefix + "java/lang/Object;");
            offset = 0;
        }

        int length = 0;
        for (Type t : m.getArgumentTypes())
            length += t.getSize();

        mv.visitInsn(Opcodes.DUP);
        mv.visitVarInsn(Opcodes.ASTORE, offset + length);
        mv.visitInsn(Opcodes.MONITORENTER);

        mv.visitLabel(pre_invoke);

        if ((access & Opcodes.ACC_STATIC) == 0)
            mv.visitVarInsn(Opcodes.ALOAD, 0);

        int i = offset;
        for (Type t : m.getArgumentTypes()) {
            // t.getOpcode() should work for long and double too... :-( 
            if (t.getClassName().equals("long"))
                mv.visitVarInsn(Opcodes.LLOAD, i);
            else if (t.getClassName().equals("double"))
                mv.visitVarInsn(Opcodes.DLOAD, i);
            else
                mv.visitVarInsn(t.getOpcode(Opcodes.ILOAD), i);
            i += t.getSize();
        }

        boolean itf = (access & Opcodes.ACC_INTERFACE) != 0;
        if ((access & Opcodes.ACC_STATIC) == 0)
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, clz, "_" + name, desc, itf);
        else
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, clz, "_" + name, desc, itf);

        mv.visitVarInsn(Opcodes.ALOAD, offset + length);
        mv.visitInsn(Opcodes.MONITOREXIT);

        mv.visitLabel(pos_leave);

        if (m.getReturnType().equals(Type.VOID_TYPE))
            mv.visitInsn(Opcodes.RETURN);
        else
            mv.visitInsn(m.getReturnType().getOpcode(Opcodes.IRETURN));

        mv.visitLabel(in_catch);

        mv.visitVarInsn(Opcodes.ALOAD, offset + length);
        mv.visitInsn(Opcodes.MONITOREXIT);

        mv.visitLabel(pre_rethrow);
        mv.visitInsn(Opcodes.ATHROW);

        mv.visitLabel(end);

        i = 0;
        if ((access & Opcodes.ACC_STATIC) == 0)
            mv.visitLocalVariable("this", "L" + clz + ";", null, begin, end, i++);
        for (Type t : m.getArgumentTypes())
            mv.visitLocalVariable("arg" + i, t.getDescriptor(), null, begin, end, i++);

        mv.visitMaxs(0, 0);
        mv.visitEnd();
    }
}