org.rascalmpl.library.lang.java.m3.internal.JarConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.rascalmpl.library.lang.java.m3.internal.JarConverter.java

Source

/*******************************************************************************
 * Copyright (c) 2009-2013 CWI
 * 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
 *
 * Contributors:
 *   * Ashim Shahi -  - CWI
 *   * Ferry Rietveld - - UvA 
 *   * Chiel Peters - - UvA
 *   * Omar Pakker - - UvA
 *   * Maria Gouseti - - UvA
 *   
 * This code was developed in the Software Evolution course of the Software Engineering master.
 * 
*******************************************************************************/
package org.rascalmpl.library.lang.java.m3.internal;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.core.Signature;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.type.TypeStore;

public class JarConverter extends M3Converter {
    private final int CLASSE = 0;
    private final int METHODE = 1;
    private final int FIELDE = 2;

    private String jarFile;
    private String ClassFile;
    private String LogPath;
    private String classScheme;
    private String className;
    private boolean classIsEnum;

    JarConverter(TypeStore typeStore, Map<String, ISourceLocation> cache) {
        super(typeStore, cache);
    }

    private String extractJarName(ISourceLocation jarLoc) {
        String tmp = jarLoc.getPath().substring(0, jarLoc.getPath().indexOf("!"));
        return tmp.substring(tmp.lastIndexOf("/") + 1);
    }

    private String extractClassName(ISourceLocation jarLoc) {
        return jarLoc.getPath().substring(jarLoc.getPath().indexOf("!") + 1);
    }

    @SuppressWarnings("unchecked")
    public void convert(ISourceLocation jarLoc, IEvaluatorContext ctx) {

        this.loc = jarLoc;
        this.jarFile = extractJarName(jarLoc);
        this.ClassFile = extractClassName(jarLoc);
        this.LogPath = this.ClassFile.replace(".class", "");
        String packageName;

        // System.out.println(this.ClassFile);
        packageName = LogPath.substring(0, LogPath.lastIndexOf("/"));
        if (this.LogPath.contains("$")) {
            this.LogPath = LogPath.replace("$", "/");
        }
        try {
            ClassReader cr = new ClassReader(URIResolverRegistry.getInstance().getInputStream(jarLoc));
            ClassNode cn = new ClassNode();

            cr.accept(cn, ClassReader.SKIP_DEBUG);

            this.className = cn.name.replace("$", "/");
            classIsEnum = false;
            if ((cn.access & Opcodes.ACC_INTERFACE) != 0)
                classScheme = "java+interface";
            else if ((cn.access & Opcodes.ACC_ENUM) != 0) {
                classScheme = "java+enum";
                classIsEnum = true;
            } else
                this.classScheme = "java+class";

            this.insert(this.containment, values.sourceLocation(classScheme, "", "/" + className),
                    values.sourceLocation("java+compilationUnit", "", "/jar:///" + jarFile));
            this.insert(this.containment, values.sourceLocation("java+package", "", "/" + packageName),
                    values.sourceLocation("java+compilationUnit", "", "/jar:///" + jarFile));
            this.insert(this.containment, values.sourceLocation("java+compilationUnit", "", "/jar:///" + jarFile),
                    values.sourceLocation("java+class", "", "/" + LogPath));

            // <|java+package:///Main|,|java+compilationUnit:///src/Main/BaseInt.java|>,

            this.insert(this.declarations, values.sourceLocation(classScheme, "", "/" + className),
                    values.sourceLocation(jarFile + "!" + ClassFile));

            if (cn.superName != null && !(cn.superName.equalsIgnoreCase("java/lang/Object")
                    || cn.superName.equalsIgnoreCase("java/lang/Enum"))) {
                this.insert(this.extendsRelations, values.sourceLocation(classScheme, "", "/" + className),
                        values.sourceLocation(classScheme, "", cn.superName));
            }

            for (int fs = 0; fs < 15; fs++) {
                if ((cn.access & (0x0001 << fs)) != 0) {
                    IConstructor cons = mapFieldAccesCode(0x0001 << fs, CLASSE);
                    if (cons != null)
                        this.insert(this.modifiers, values.sourceLocation(classScheme, "", "/" + className), cons);
                }
            }

            // Deprecated method emit type annotation dependency Deprecated.
            if ((cn.access & 0x20000) == 0x20000)
                this.insert(this.annotations, values.sourceLocation(classScheme, "", "/" + className),
                        values.sourceLocation("java+interface", "", "/java/lang/Deprecated"));

            // @implements={<|java+class:///m3startv2/viaInterface|,|java+interface:///m3startv2/m3Interface|>},
            for (int i = 0; i < cn.interfaces.size(); ++i) {
                String iface = (String) cn.interfaces.get(i);
                // System.out.println(iface);
                this.insert(this.implementsRelations, values.sourceLocation(classScheme, "", "/" + className),
                        values.sourceLocation("java+interface", "", "/" + iface));
            }

            for (int fs = 0; fs < cn.innerClasses.size(); fs++) {
                InnerClassNode a = (InnerClassNode) cn.innerClasses.get(fs);
                String parsedName = a.name.replace("$", "/");
                this.insert(this.containment, values.sourceLocation(classScheme, "", "/" + className),
                        values.sourceLocation(classScheme, "", "/" + parsedName));
            }

            emitMethods(cn.methods);
            emitFields(cn.fields);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            throw new RuntimeException("Should not happen", e);
        }
    }

    private void emitMethods(List<MethodNode> methods) {
        try {
            for (int i = 0; i < methods.size(); ++i) {
                MethodNode method = methods.get(i);
                // System.out.println(new String("Signature :") + className + " " + method.name + " " +
                // method.signature + "  " + method.desc);

                if (classIsEnum
                        && (method.name.equalsIgnoreCase("values") || method.name.equalsIgnoreCase("valueOf"))) {
                    continue;
                }

                if (method.name.contains("<")) {
                    String name = LogPath.substring(LogPath.lastIndexOf("/"));
                    insertDeclMethod("java+constructor", method.signature, eliminateOutterClass(method.desc), name,
                            method.access);
                } else {
                    insertDeclMethod("java+method", method.signature, method.desc, method.name, method.access);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void insertDeclMethod(String type, String signature, String desc, String name, int access)
            throws URISyntaxException {
        String sig;
        if (signature != null) {
            sig = extractSignature(signature);
            // TypeVariables
            SignatureReader sr = new SignatureReader(signature);
            sr.accept(new SigVisitor(Opcodes.ASM4));
        } else {
            sig = extractSignature(desc);
        }
        // Typedepency methods
        String TypeSig = sig;
        // Loop over all parameters in the signature
        String[] params;

        if ((TypeSig != null) && (!TypeSig.equals(""))) {
            params = TypeSig.split(",");
            for (int i = 0; i < params.length; i++) {
                this.insert(this.typeDependency,
                        values.sourceLocation("java+parameter", "",
                                LogPath + "/" + name + "(" + sig + ")" + "/" + params[i] + i),
                        values.sourceLocation(printParameterType(params[i]), "", params[i]));
            }
        }

        // Return type
        if (type.equals("java+constructor")) {
            this.insert(this.typeDependency,
                    values.sourceLocation("java+constructor", "", LogPath + "/" + name + "(" + sig + ")"),
                    values.sourceLocation("java+class", "", LogPath));
        } else {
            String rType = null;
            if (signature != null) {
                rType = Signature.toString(signature);
            } else {
                rType = Signature.toString(desc);
            }
            rType = rType.substring(0, rType.indexOf(' '));
            this.insert(this.typeDependency,
                    values.sourceLocation("java+method", "", LogPath + "/" + name + "(" + sig + ")"),
                    values.sourceLocation(printParameterType(rType), "", rType));
        }

        this.insert(this.declarations, values.sourceLocation(type, "", LogPath + "/" + name + "(" + sig + ")"),
                values.sourceLocation(jarFile + "!" + ClassFile));
        for (int fs = 0; fs < 15; fs++) {
            if ((access & (0x0001 << fs)) != 0) {
                this.insert(this.modifiers, values.sourceLocation(type, "", LogPath + "/" + name + "(" + sig + ")"),
                        mapFieldAccesCode(0x0001 << fs, METHODE));
            }
        }

        // Containment of methods.
        this.insert(this.containment, values.sourceLocation(classScheme, "", LogPath),
                values.sourceLocation(type, "", LogPath + "/" + name + "(" + sig + ")"));

        // Deprecated method emit type annotation dependency Deprecated.
        if ((access & 0x20000) == 0x20000)
            this.insert(this.annotations,
                    values.sourceLocation("java+method", "", LogPath + "/" + name + "(" + sig + ")"),
                    values.sourceLocation("java+interface", "", "/java/lang/Deprecated"));
        // <|java+method:///Main/Main/FindMe(java.lang.String)|,|java+interface:///java/lang/Deprecated|>,

    }

    private String eliminateOutterClass(String desc) {
        // Find the end of the first class argument
        int semi = desc.indexOf(';');
        String outter = null;

        // Create the possible path
        if (semi > 0) {
            outter = desc.substring(desc.indexOf('(') + 2, semi) + "$";
        }

        // if the first argument is contained in the class path, remove it
        if ((outter != null) && ClassFile.contains(outter))
            return "(" + desc.substring(semi + 1);
        else
            return desc;
    }

    private String printParameterType(String t) {
        if (t != null) {
            switch (t) {
            case "void":
            case "boolean":
            case "char":
            case "byte":
            case "short":
            case "int":
            case "float":
            case "long":
            case "double":
                return "java+primitiveType";
            default:
                return "java+class";
            }
        }
        throw new RuntimeException("This should not happen, because i know everything");
    }

    private String extractSignature(String sig) {
        String args = Signature.toString(sig);
        args = args.substring(args.indexOf("(") + 1, args.indexOf(")"));
        args = args.replaceAll("\\s+", "");
        args = args.replaceAll("/", ".");
        return args;
    }

    private IConstructor mapFieldAccesCode(int code, int where) {
        // Check the original M3 implementation for possible IConstructor types.
        switch (code) {
        case Opcodes.ACC_PUBLIC:
            return constructModifierNode("public");
        case Opcodes.ACC_PRIVATE:
            return constructModifierNode("private");
        case Opcodes.ACC_PROTECTED:
            return constructModifierNode("protected");
        case Opcodes.ACC_STATIC:
            return constructModifierNode("static");
        case Opcodes.ACC_FINAL:
            return constructModifierNode("final");
        case Opcodes.ACC_SYNCHRONIZED:
            if (where == CLASSE)
                return null;
            return constructModifierNode("synchronized");
        case Opcodes.ACC_ABSTRACT:
            return constructModifierNode("abstract");
        case Opcodes.ACC_VOLATILE:
            return constructModifierNode("volatile");
        case Opcodes.ACC_TRANSIENT:
            return constructModifierNode("transient");
        case Opcodes.ACC_NATIVE:
            return constructModifierNode("native");

        // TODO: GIT PULL/MERGE ORIGINAL RASCAL VERSION < 2013-11-30 (Shahin commit)
        // case Opcodes.ACC_DEPRECATED:
        // return constructModifierNode("deprecated");

        default:
            return null;
        }
    }

    // <|java+field:///m3startv2/Main/intField|,|project://m3startv2/src/m3startv2/Main.java|(54,13,<5,12>,<5,25>)>,
    private void emitFields(List<FieldNode> fields) {
        try {
            for (int i = 0; i < fields.size(); ++i) {
                FieldNode field = fields.get(i);

                if ((field.access & Opcodes.ACC_SYNTHETIC) != 0)
                    continue;

                if (field.name.startsWith("this$")) {
                    if ((field.desc.length() > 0) && (className
                            .contains(field.desc.substring(1, field.desc.length() - 1).replace('$', '/') + "/")))

                        break;
                }

                boolean isEnum = (field.access & Opcodes.ACC_ENUM) != 0;
                String fieldScheme = isEnum ? "java+enumConstant" : "java+field";

                // System.out.println("Debug......." + field.name);
                this.insert(this.declarations, values.sourceLocation(fieldScheme, "", LogPath + "/" + field.name),
                        values.sourceLocation(jarFile + "!" + ClassFile));

                // Containment of fields.
                this.insert(this.containment, values.sourceLocation(classScheme, "", LogPath),
                        values.sourceLocation(fieldScheme, "", LogPath + "/" + field.name));

                if (!isEnum) {
                    // The jvm acces codes specify 15 different modifiers (more then in the Java language
                    // itself)
                    for (int fs = 0; fs < 15; fs++) {
                        if ((field.access & (0x0001 << fs)) != 0) {
                            this.insert(this.modifiers,
                                    values.sourceLocation("java+field", "", LogPath + "/" + field.name),
                                    mapFieldAccesCode(1 << fs, FIELDE));
                        }
                    }
                }
                // Put deprecated field in the annotations anno.
                if ((field.access & 0x20000) == 0x20000)
                    this.insert(this.annotations,
                            values.sourceLocation("java+field", "", LogPath + "/" + field.name),
                            values.sourceLocation("java+interface", "", "/java/lang/Deprecated"));
                // <|java+method:///Main/Main/FindMe(java.lang.String)|,|java+interface:///java/lang/Deprecated|>,

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class SigVisitor extends SignatureVisitor {

        public SigVisitor(int api) {
            super(api);
            // TODO Auto-generated constructor stub
        }

        public void visitFormalTypeParameter(String name) {
            try {
                // System.out.println(name);
                JarConverter.this.insert(JarConverter.this.declarations,
                        values.sourceLocation("java+typeVariable", "", LogPath + "/" + name),
                        values.sourceLocation(jarFile + "!" + ClassFile));
            } catch (URISyntaxException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void visitBaseType(char descriptor) {
            // System.out.println(descriptor);
        }

    }
}