com.drgarbage.bytecode.BytecodeUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.drgarbage.bytecode.BytecodeUtils.java

Source

/**
 * Copyright (c) 2008-2012, Dr. Garbage Community
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.drgarbage.bytecode;

import java.io.IOException;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.util.ConstantPoolEntry;

import com.drgarbage.asm.MethodVisitor;
import com.drgarbage.asm.Opcodes;
import com.drgarbage.asm.render.intf.IInstructionLine;
import com.drgarbage.asm.render.intf.ILocalVariableTable;
import com.drgarbage.bytecode.constant_pool.AbstractConstantPoolEntry;
import com.drgarbage.bytecode.constant_pool.ConstantClassInfo;
import com.drgarbage.bytecode.constant_pool.ConstantUtf8Info;
import com.drgarbage.javasrc.JavaKeywords;
import com.drgarbage.javasrc.JavaLexicalConstants;

public class BytecodeUtils {
    public static int getMinor(int classFileFormatVersion) {
        return classFileFormatVersion >>> 16;
    }

    public static int getMajor(int classFileFormatVersion) {
        return classFileFormatVersion & 0xFFFF;
    }

    public static String getLowestJavaPlatformVersion(int major, int minor) {
        if (major < 45) {
            return null;
        } else if (major == 45 && minor >= 0 && minor <= 3) {
            return "1.0.2";
        } else if (major == 45 && minor >= 0 && minor <= 65535) {
            return "1.1";
        } else {
            StringBuilder sb = new StringBuilder();
            sb.append("1.");
            sb.append(major - 44);
            return sb.toString();
        }
    }

    public static String toJavaSrcName(String byteCodeName) {
        return byteCodeName.replace(ByteCodeConstants.CLASS_NAME_SLASH, JavaLexicalConstants.DOT)
                .replace(ByteCodeConstants.CLASS_NAME_DOLLAR, JavaLexicalConstants.DOT);
    }

    public static String getSimpleName(String byteCodeName) {
        if (byteCodeName != null) {
            int i = byteCodeName.lastIndexOf(ByteCodeConstants.CLASS_NAME_SLASH);
            if (i >= 0) {
                return byteCodeName.substring(i + 1);
            }
        }
        return byteCodeName;
    }

    public static String getPackage(String thisClassName) {
        if (thisClassName != null) {
            int i = thisClassName.lastIndexOf(ByteCodeConstants.CLASS_NAME_SLASH);
            if (i >= 0) {
                return thisClassName.substring(0, i);
            }
        }
        return null;
    }

    /**
     * @see MethodVisitor#visitIntInsn(int, int)
     * 
     * @param operand
     * @return java keyword as a string
     */
    public static String getJavaTypeName(int operand) {
        switch (operand) {
        case Opcodes.T_BOOLEAN:
            return JavaKeywords.BOOLEAN;
        case Opcodes.T_CHAR:
            return JavaKeywords.CHAR;
        case Opcodes.T_FLOAT:
            return JavaKeywords.FLOAT;
        case Opcodes.T_DOUBLE:
            return JavaKeywords.DOUBLE;
        case Opcodes.T_BYTE:
            return JavaKeywords.BYTE;
        case Opcodes.T_SHORT:
            return JavaKeywords.SHORT;
        case Opcodes.T_INT:
            return JavaKeywords.INT;
        case Opcodes.T_LONG:
            return JavaKeywords.LONG;
        default:
            throw new IllegalArgumentException("Unexpected option '" + operand + "';");
        }
    }

    public static int appendMethodDescriptor(String methodName, boolean isConstructor, boolean isStatic,
            String descr, int offset, ILocalVariableTable localVariableTable,
            AbstractConstantPoolEntry[] constantPool, Appendable doc) throws IOException {

        /*
         * parse the method arguments first and save them in a temporarary
         * StringBuilder
         */
        StringBuilder sb = new StringBuilder();
        int i = offset;
        switch (descr.charAt(i++)) {
        case ByteCodeConstants.METHOD_DESCRIPTOR_LEFT_PARENTHESIS:
            break;
        default:
            i--;
            throw new IllegalArgumentException("could not parse the descriptor '" + descr
                    + "'. '(' expected, found '" + descr.charAt(i) + "' at the position " + i + ".");
        }
        int argi = 0;
        while ((descr.charAt(i)) != ByteCodeConstants.METHOD_DESCRIPTOR_RIGHT_PARENTHESIS) {
            if (argi != 0) {
                sb.append(JavaLexicalConstants.COMMA);
                sb.append(JavaLexicalConstants.SPACE);
            }
            i = appendFieldDescriptor(descr, i, sb);
            sb.append(JavaLexicalConstants.SPACE);

            String argName = null;

            if (localVariableTable != null) {
                argName = localVariableTable.findArgName(isStatic ? argi : argi + 1, 0, isConstructor, isStatic);
            }
            if (argName != null) {
                sb.append(argName);
            } else {
                sb.append(ByteCodeConstants.ARG);
                sb.append(argi);
            }
            argi++;
        }
        /* consume the right parenthesis */
        i++;

        /*
         * now parse the return type; and append it directly to the doc
         */
        if (!isConstructor) {
            /* constructors do not have return types */
            i = appendFieldDescriptor(descr, i, doc);
            doc.append(JavaLexicalConstants.SPACE);
        }

        doc.append(methodName);
        doc.append(JavaLexicalConstants.LEFT_PARENTHESIS);

        /* append the arguments stored in the sb */
        doc.append(sb.toString());
        doc.append(JavaLexicalConstants.RIGHT_PARENTHESIS);

        return i;
    }

    public static int appendArrayDescriptor(String descr, int offset, Appendable doc) throws IOException {
        int i = offset;
        switch (descr.charAt(i++)) {
        case ByteCodeConstants.ARRAY_BEGINNING_BRACKET:
            int dims = 1;
            while (descr.charAt(i) == ByteCodeConstants.ARRAY_BEGINNING_BRACKET) {
                i++;
                dims++;
            }
            i = appendFieldDescriptor(descr, i, doc);
            for (int j = 0; j < dims; j++) {
                doc.append(JavaLexicalConstants.LEFT_SQUARE_BRACKET);
                doc.append(JavaLexicalConstants.RIGHT_SQUARE_BRACKET);
            }
            break;
        default:
            i--;
            throw new IllegalArgumentException("could not parse the descriptor '" + descr
                    + "'. Unexpected character '" + descr.charAt(i) + "' at the position " + i);
        }

        return i;
    }

    public static int appendFieldDescriptor(String descr, int offset, Appendable doc) throws IOException {

        int i = offset;
        switch (descr.charAt(i++)) {
        case ByteCodeConstants.ARRAY_BEGINNING_BRACKET:
            i = appendArrayDescriptor(descr, i - 1, doc);
            break;
        case ByteCodeConstants.L_REFERENCE:
            char c;
            while ((c = descr.charAt(i++)) != ByteCodeConstants.REFERENCE_TERMINATING_SEMICOLON) {
                if (c == ByteCodeConstants.CLASS_NAME_SLASH) {
                    doc.append(JavaLexicalConstants.DOT);
                } else {
                    doc.append(c);
                }
            }
            break;
        case ByteCodeConstants.B_BYTE:
            doc.append(JavaKeywords.BYTE);
            break;
        case ByteCodeConstants.C_CHAR:
            doc.append(JavaKeywords.CHAR);
            break;
        case ByteCodeConstants.D_DOUBLE:
            doc.append(JavaKeywords.DOUBLE);
            break;
        case ByteCodeConstants.F_FLOAT:
            doc.append(JavaKeywords.FLOAT);
            break;
        case ByteCodeConstants.I_INT:
            doc.append(JavaKeywords.INT);
            break;
        case ByteCodeConstants.J_LONG:
            doc.append(JavaKeywords.LONG);
            break;
        case ByteCodeConstants.S_SHORT:
            doc.append(JavaKeywords.SHORT);
            break;
        case ByteCodeConstants.Z_BOOLEAN:
            doc.append(JavaKeywords.BOOLEAN);
            break;
        case ByteCodeConstants.V_VOID:
            doc.append(JavaKeywords.VOID);
            break;
        default:
            i--;
            throw new IllegalArgumentException("could not parse the descriptor '" + descr
                    + "'. Unexpected character '" + descr.charAt(i) + "' at the position " + i);
        }

        return i;

    }

    public static void padd(String s, int length, char ch, Appendable sb) throws IOException {
        int toPadd = length - s.length();
        while (toPadd-- > 0) {
            sb.append(ch);
        }
        sb.append(s);
    }

    /**
     * Appends a quoted string to a given buffer.
     * 
     * @param buf
     *            the buffer where the string must be added.
     * @param s
     *            the string to be added.
     */
    public static void appendString(final StringBuffer buf, final String s) {
        buf.append(JavaLexicalConstants.QUOTE);
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            if (c == '\n') {
                buf.append("\\n");
            } else if (c == '\r') {
                buf.append("\\r");
            } else if (c == '\\') {
                buf.append("\\\\");
            } else if (c == '"') {
                buf.append("\\\"");
            } else if (c < 0x20 || c > 0x7f) {
                buf.append("\\u");
                if (c < 0x10) {
                    buf.append("000");
                } else if (c < 0x100) {
                    buf.append("00");
                } else if (c < 0x1000) {
                    buf.append('0');
                }
                buf.append(Integer.toString(c, 16));
            } else {
                buf.append(c);
            }
        }
        buf.append(JavaLexicalConstants.QUOTE);
    }

    public static boolean isSourceOf(IResource srcFile, IClassFile classFile) {

        if (srcFile == null || classFile == null) {
            return false;
        }

        IResource resource = classFile.getResource();
        if (resource == null) {
            return false;
        }

        String classFileTypeName = toFullyQualifiedTypeName(resource);

        if (classFileTypeName == null) {
            return false;
        }

        String srcPath = srcFile.getFullPath().removeFileExtension().toString();
        srcPath = toJavaSrcName(srcPath);

        return srcPath.endsWith(classFileTypeName);
    }

    /**
     * Returns the fully qualified type name for a given {@link IResource}. 
     * For resources which are not included in the project's build path 
     * returns always <code>null</code>.
     * 
     * @param classFileResource
     * @return the fully qualified name as a string or <code>null</code>
     */
    public static String toFullyQualifiedTypeName(IResource classFileResource) {
        IProject project = classFileResource.getProject();

        /* create java project */
        IJavaProject javaProject = JavaCore.create(project);

        IPath wspacePath = classFileResource.getFullPath();
        IPath outputDir = null;

        try {
            outputDir = javaProject.getOutputLocation();
        } catch (JavaModelException e) {
            throw new RuntimeException(e);
        }

        IPath classToFind = null;
        if (outputDir.matchingFirstSegments(wspacePath) == outputDir.segmentCount()) {
            /*
             * if we are in the output directory strip the output directory as
             * we want the project relative path
             */
            classToFind = wspacePath.removeFirstSegments(outputDir.segmentCount());
        }

        if (classToFind != null) {
            classToFind = classToFind.removeFileExtension();
            String typeToFind = classToFind.toString();

            typeToFind = toJavaSrcName(typeToFind);

            return typeToFind;

        }
        return null;
    }

    /**
     * returns the type name of the given {@link ConstantClassInfo}
     * @param constantClassInfo the {@link ConstantClassInfo} to check
     * @param constantPool the associated ConstantPool
     * @return type name as a string
     */
    public static String resolveConstantPoolTypeName(ConstantClassInfo constantClassInfo,
            AbstractConstantPoolEntry[] constantPool) {

        return ((ConstantUtf8Info) constantPool[constantClassInfo.getNameIndex()]).getString();
    }

}