de.zib.sfs.instrument.RandomAccessFileAdapter.java Source code

Java tutorial

Introduction

Here is the source code for de.zib.sfs.instrument.RandomAccessFileAdapter.java

Source

/*
 * Copyright (c) 2016 by Robert Schmidtke,
 *               Zuse Institute Berlin
 *
 * Licensed under the BSD License, see LICENSE file for details.
 *
 */
package de.zib.sfs.instrument;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Set;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import de.zib.sfs.instrument.statistics.OperationCategory;

/**
 * Class adapter that instruments {@link java.io.RandomAccessFile}.
 * 
 * @author robert
 *
 */
public class RandomAccessFileAdapter extends AbstractSfsAdapter {

    public RandomAccessFileAdapter(ClassVisitor cv, String methodPrefix, Set<OperationCategory> skip)
            throws SecurityException {
        super(cv, RandomAccessFile.class, RandomAccessFileCallback.class, methodPrefix, skip);
    }

    @Override
    protected boolean wrapMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return isOpenMethod(access, name, desc, signature, exceptions)
                || isReadMethod(access, name, desc, signature, exceptions)
                || isReadFullyMethod(access, name, desc, signature, exceptions)
                || isReadStringMethod(access, name, desc, signature, exceptions)
                || isWriteMethod(access, name, desc, signature, exceptions)
                || isWriteStringMethod(access, name, desc, signature, exceptions);
    }

    @Override
    protected void appendWrappedMethods(ClassVisitor visitor) {
        ResultPasser resultDiscarder = new DiscardResultPasser();

        wrapMethod(Opcodes.ACC_PRIVATE, "open", Type.VOID_TYPE,
                new Type[] { Type.getType(String.class), Type.INT_TYPE }, null,
                new String[] { Type.getInternalName(FileNotFoundException.class) }, "openCallback",
                Type.getType(String.class), new ParameterResultPasser(1));

        // for all read methods pass the read result to the callback
        ReturnResultPasser resultPasser = new ReturnResultPasser();

        // invokes .length on an array
        ResultPasser arrayLengthPasser = new ParameterResultPasser(1) {
            @Override
            public void passResult(MethodVisitor mv) {
                mv.visitInsn(Opcodes.ARRAYLENGTH);
            }
        };

        // invokes .length(); on the current local variable
        ResultPasser stringLengthPasser = new ReturnResultPasser() {
            @Override
            public void passResult(MethodVisitor mv) {
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(String.class), "length",
                        Type.getMethodDescriptor(Type.INT_TYPE), false);
            }
        };

        if (!skipReads()) {
            wrapMethod(Opcodes.ACC_PUBLIC, "read", Type.INT_TYPE, null, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readCallback", Type.INT_TYPE,
                    resultPasser);

            wrapMethod(Opcodes.ACC_PUBLIC, "read", Type.INT_TYPE, new Type[] { Type.getType(byte[].class) }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readBytesCallback", Type.INT_TYPE,
                    resultPasser);

            wrapMethod(Opcodes.ACC_PUBLIC, "read", Type.INT_TYPE,
                    new Type[] { Type.getType(byte[].class), Type.INT_TYPE, Type.INT_TYPE }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readBytesCallback", Type.INT_TYPE,
                    resultPasser);

            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "readFully", Type.VOID_TYPE,
                    new Type[] { Type.getType(byte[].class) }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readBytesCallback", Type.INT_TYPE,
                    arrayLengthPasser);

            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "readFully", Type.VOID_TYPE,
                    new Type[] { Type.getType(byte[].class), Type.INT_TYPE, Type.INT_TYPE }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readBytesCallback", Type.INT_TYPE,
                    new ParameterResultPasser(3));

            // record length of read string
            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "readLine", Type.getType(String.class), null, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readBytesCallback", Type.INT_TYPE,
                    stringLengthPasser);

            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "readUTF", Type.getType(String.class), null, null,
                    new String[] { Type.getInternalName(IOException.class) }, "readBytesCallback", Type.INT_TYPE,
                    stringLengthPasser);
        }

        // take length of parameter string instead of return value
        stringLengthPasser = new ParameterResultPasser(1) {
            @Override
            public void passResult(MethodVisitor mv) {
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(String.class), "length",
                        Type.getMethodDescriptor(Type.INT_TYPE), false);
            }
        };

        if (!skipWrites()) {
            // 1 byte write, no result needed
            wrapMethod(Opcodes.ACC_PUBLIC, "write", Type.VOID_TYPE, new Type[] { Type.INT_TYPE }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "writeCallback", null,
                    resultDiscarder);

            // have the byte array put on top of the stack, then pass its length
            // to
            // the callback
            wrapMethod(Opcodes.ACC_PUBLIC, "write", Type.VOID_TYPE, new Type[] { Type.getType(byte[].class) }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "writeBytesCallback", Type.INT_TYPE,
                    arrayLengthPasser);

            // have the len parameter put on top of the stack
            wrapMethod(Opcodes.ACC_PUBLIC, "write", Type.VOID_TYPE,
                    new Type[] { Type.getType(byte[].class), Type.INT_TYPE, Type.INT_TYPE }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "writeBytesCallback", Type.INT_TYPE,
                    new ParameterResultPasser(3));

            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "writeBytes", Type.VOID_TYPE,
                    new Type[] { Type.getType(String.class) }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "writeBytesCallback", Type.INT_TYPE,
                    stringLengthPasser);

            // twice the data if written as characters
            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "writeChars", Type.VOID_TYPE,
                    new Type[] { Type.getType(String.class) }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "writeBytesCallback", Type.INT_TYPE,
                    new ParameterResultPasser(1) {
                        @Override
                        public void passResult(MethodVisitor mv) {
                            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(String.class), "length",
                                    Type.getMethodDescriptor(Type.INT_TYPE), false);
                            mv.visitInsn(Opcodes.ICONST_2);
                            mv.visitInsn(Opcodes.IMUL);
                        }
                    });

            wrapMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "writeUTF", Type.VOID_TYPE,
                    new Type[] { Type.getType(String.class) }, null,
                    new String[] { Type.getInternalName(IOException.class) }, "writeBytesCallback", Type.INT_TYPE,
                    stringLengthPasser);
        }

    }

    private static boolean isOpenMethod(int access, String name, String desc, String signature,
            String[] exceptions) {
        return access == Opcodes.ACC_PRIVATE && "open".equals(name)
                && Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class), Type.INT_TYPE).equals(desc)
                && null == signature && exceptions != null && exceptions.length == 1
                && Type.getInternalName(FileNotFoundException.class).equals(exceptions[0]);
    }

    private boolean isReadMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return access == Opcodes.ACC_PUBLIC && "read".equals(name)
                && (Type.getMethodDescriptor(Type.INT_TYPE).equals(desc)
                        || Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class)).equals(desc)
                        || Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(byte[].class), Type.INT_TYPE,
                                Type.INT_TYPE).equals(desc))
                && null == signature && exceptions != null && exceptions.length == 1
                && Type.getInternalName(IOException.class).equals(exceptions[0]) && !skipReads();
    }

    private boolean isReadFullyMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return access == (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL) && "readFully".equals(name)
                && (Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(byte[].class)).equals(desc)
                        || Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(byte[].class), Type.INT_TYPE,
                                Type.INT_TYPE).equals(desc))
                && null == signature && exceptions != null && exceptions.length == 1
                && Type.getInternalName(IOException.class).equals(exceptions[0]) && !skipReads();
    }

    private boolean isReadStringMethod(int access, String name, String desc, String signature,
            String[] exceptions) {
        return access == (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL)
                && ("readLine".equals(name) || "readUTF".equals(name))
                && Type.getMethodDescriptor(Type.getType(String.class)).equals(desc) && null == signature
                && exceptions != null && exceptions.length == 1
                && Type.getInternalName(IOException.class).equals(exceptions[0]) && !skipReads();
    }

    private boolean isWriteMethod(int access, String name, String desc, String signature, String[] exceptions) {
        return access == Opcodes.ACC_PUBLIC && "write".equals(name)
                && (Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE).equals(desc)
                        || Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(byte[].class)).equals(desc)
                        || Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(byte[].class), Type.INT_TYPE,
                                Type.INT_TYPE).equals(desc))
                && null == signature && exceptions != null && exceptions.length == 1
                && Type.getInternalName(IOException.class).equals(exceptions[0]) && !skipWrites();
    }

    private boolean isWriteStringMethod(int access, String name, String desc, String signature,
            String[] exceptions) {
        return access == (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL)
                && ("writeBytes".equals(name) || "writeChars".equals(name) || "writeUTF".equals(name))
                && Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)).equals(desc)
                && null == signature && exceptions != null && exceptions.length == 1
                && Type.getInternalName(IOException.class).equals(exceptions[0]) && !skipWrites();
    }
}