/*******************************************************************************
* Copyright 2014,
* Luis Pina <luis@luispina.me>,
* Michael Hicks <mwh@cs.umd.edu>
*
* This file is part of Rubah.
*
* Rubah 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 3 of the License, or
* (at your option) any later version.
*
* Rubah 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 Rubah. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package rubah.bytecode.transformers;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.javatuples.Pair;
import rubah.framework.Clazz;
import rubah.framework.Method;
import rubah.framework.Namespace;
import rubah.framework.Type;
import rubah.runtime.Version;
public class UpdatableClassInfoGatherer extends BasicClassInfoGatherer {
public static HashMap<Clazz, List<Clazz>> computeSubClasses(Namespace namespace) {
HashMap<Clazz, Set<Clazz>> tmp = new HashMap<Clazz, Set<Clazz>>();
for (Clazz c : namespace.getDefinedClasses()) {
tmp.put(c, new HashSet<Clazz>());
}
for (Clazz c : namespace.getDefinedClasses()) {
if (c.getParent().getNamespace().equals(namespace)) {
tmp.get(c.getParent()).add(c);
}
}
HashMap<Clazz, List<Clazz>> ret = new HashMap<Clazz, List<Clazz>>();
for (Entry<Clazz, Set<Clazz>> entry : tmp.entrySet()) {
LinkedList<Clazz> lst = new LinkedList<>(entry.getValue());
Collections.sort(lst, new Comparator<Clazz>() {
@Override
public int compare(Clazz o1, Clazz o2) {
return o1.getFqn().compareTo(o2.getFqn());
}
});
ret.put(entry.getKey(), lst);
}
return ret;
}
public static Map<Clazz, Set<Clazz>> computeInterfaces(Namespace namespace) {
HashMap<Clazz, Set<Clazz>> ret = new HashMap<Clazz, Set<Clazz>>();
for (Clazz c : namespace.getDefinedClasses()) {
ret.put(c, new HashSet<Clazz>());
}
for (Clazz c : namespace.getDefinedClasses()) {
for (Clazz iface : c.getInterfaces()) {
if (iface.getNamespace().equals(namespace)) {
ret.get(iface).add(c);
}
}
}
return ret;
}
private Map<Pair<Clazz, Method>, Integer> overloads =
new HashMap<Pair<Clazz,Method>, Integer>();
private Version version;
public UpdatableClassInfoGatherer(Version version) {
super(version.getNamespace());
this.version = version;
}
public Map<Pair<Clazz, Method>, Integer> getOverloads() {
return this.overloads;
}
public void computeOverloads() {
this.computeOverloads(computeSubClasses(this.namespace));
}
private void computeOverloads(HashMap<Clazz, List<Clazz>> subClasses) {
Map<Class<?>, Set<Method>> cache = new HashMap<Class<?>, Set<Method>>();
for (Clazz c : this.namespace.getDefinedClasses()) {
if (!c.getParent().getNamespace().equals(this.namespace)) {
Map<Method, List<Method>> map = new HashMap<Method, List<Method>>();
try {
for (Method m : this.getAllInheritedMethodsUsingReflection(Class.forName(c.getFqn(), false, UpdatableClassRenamer.class.getClassLoader()), cache)) {
List<Method> list = new ArrayList<Method>();
list.add(m);
map.put(m, list);
}
} catch (ClassNotFoundException e) {
throw new Error(e);
}
this.computeOverloadesRecur(map, c, subClasses);
}
}
Iterator<Entry<Pair<Clazz, Method>, Integer>> iterator =
this.getOverloads().entrySet().iterator();
while (iterator.hasNext()) {
Entry<Pair<Clazz, Method>, Integer> entry = iterator.next();
if (entry.getValue() == 0) {
iterator.remove();
}
}
}
private Set<Method> getAllInheritedMethodsUsingReflection(Class<?> c,
Map<Class<?>, Set<Method>> cache) {
Set<Method> ret = cache.get(c);
if (ret != null) {
return ret;
} else {
ret = new HashSet<Method>();
}
while (c != null) {
for (Class<?> iface : c.getInterfaces()) {
ret.addAll(this.getAllInheritedMethodsUsingReflection(iface, cache));
}
for (java.lang.reflect.Method m : c.getDeclaredMethods()) {
ret.add(
new Method(
m.getModifiers(),
m.getName(),
Type.getMethodDescriptor(m),
this.namespace));
}
for (Constructor<?> m : c.getDeclaredConstructors()) {
ret.add(
new Method(
m.getModifiers(),
"<init>",
Type.getMethodDescriptor(m),
this.namespace));
}
c = c.getSuperclass();
}
cache.put(c, ret);
return ret;
}
private void computeOverloadesRecur(
Map<Method, List<Method>> map,
Clazz c, HashMap<Clazz, List<Clazz>> subclasses) {
List<Method> methodsAndConstructores = new LinkedList<Method>();
methodsAndConstructores.addAll(c.getMethods());
Collections.sort(methodsAndConstructores, new Comparator<Method>() {
@Override
public int compare(Method arg0, Method arg1) {
int ret = arg0.getName().compareTo(arg1.getName());
if (ret == 0) {
ret = arg0.getASMDesc().compareTo(arg1.getASMDesc());
}
return ret;
}
});
for(Method m : methodsAndConstructores) {
Method afterM = this.version.eraseUpdatableTypes(m);
List<Method> secondMap;
if (map.containsKey(afterM)) {
secondMap = map.get(afterM);
}
else {
secondMap = new ArrayList<Method>();
map.put(afterM, secondMap);
}
int idx = secondMap.indexOf(m);
if (idx >= 0) {
this.getOverloads().put(new Pair<Clazz, Method>(c, m), idx);
}
else {
this.getOverloads().put(new Pair<Clazz, Method>(c, m), secondMap.size());
secondMap.add(m);
}
}
for(Clazz child : subclasses.get(c)) {
this.computeOverloadesRecur(
new HashMap<Method, List<Method>>(map), child, subclasses);
}
}
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
}
}