/*******************************************************************************
* 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);
}
}
}