package com.hellblazer.primeMover.soot.util;
import static java.util.Arrays.asList;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.jimple.toolkits.annotation.j5anno.AnnotationGenerator;
import soot.tagkit.AbstractHost;
import soot.tagkit.AnnotationArrayElem;
import soot.tagkit.AnnotationClassElem;
import soot.tagkit.AnnotationConstants;
import soot.tagkit.AnnotationElem;
import soot.tagkit.AnnotationStringElem;
import soot.tagkit.AnnotationTag;
import soot.tagkit.Tag;
import soot.tagkit.VisibilityAnnotationTag;
public class Utils {
private static Logger log = Logger.getLogger(Utils.class.getCanonicalName());
private static final String BLOCKING_CLASS_BINARY_NAME = "Lcom/hellblazer/primeMover/Blocking;";
private static final String NON_EVENT_CLASS_BINARY_NAME = "Lcom/hellblazer/primeMover/NonEvent;";
private static final String CONTINUABLE_CLASS_BINARY_NAME = "Lcom/hellblazer/primeMover/Continuable;";
private static final String TRANSFORMED_CLASS_BINARY_NAME = "Lcom/hellblazer/primeMover/Transformed;";
private static final String ENTITY_CLASS_BINARY_NAME = "Lcom/hellblazer/primeMover/Entity;";
private static SimpleDateFormat ISO8601FORMAT = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZ");
public static Collection<SootMethod> gatherImplementationsOf(SootMethod method,
SootClass base) {
ArrayList<SootMethod> implementations = new ArrayList<SootMethod>();
SootClass current = base;
String subSignature = method.getSubSignature();
while (current != null) {
SootMethod impl = current.getMethod(subSignature);
if (impl != null) {
implementations.add(impl);
}
current = current.hasSuperclass() ? current.getSuperclass() : null;
}
return implementations;
}
public static Collection<SootClass> getEntityInterfaces(SootClass base) {
Set<SootClass> interfaces = new HashSet<SootClass>();
SootClass current = base;
while (current != null) {
gatherEntityInterfacesOf(current, interfaces);
current = current.hasSuperclass() ? current.getSuperclass() : null;
}
return interfaces;
}
public static boolean isEntity(SootClass base) {
if (isMarkedEntity(base)) {
return true;
}
if (base.getName().endsWith("$gen")) {
return false;
}
if (inferEntity(base)) {
log.info(String.format("Inferred Entity status of %1s. Marking class as @Enity",
base));
markEntity(base);
markTransformed(base, "EntityInference", "Entity Status Inferred");
return true;
}
return false;
}
public static boolean isMarkedBlocking(SootMethod method) {
for (Tag tag : method.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
VisibilityAnnotationTag vTag = (VisibilityAnnotationTag) tag;
if (vTag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE) {
for (AnnotationTag aTag : vTag.getAnnotations()) {
if (aTag.getType().equals(BLOCKING_CLASS_BINARY_NAME)) {
return true;
}
}
}
}
}
return false;
}
public static boolean isMarkedContinuable(SootMethod method) {
for (Tag tag : method.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
VisibilityAnnotationTag vTag = (VisibilityAnnotationTag) tag;
if (vTag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE) {
for (AnnotationTag aTag : vTag.getAnnotations()) {
if (aTag.getType().equals(CONTINUABLE_CLASS_BINARY_NAME)) {
return true;
}
}
}
}
}
return false;
}
public static boolean isMarkedNonEvent(SootMethod method) {
for (Tag tag : method.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
VisibilityAnnotationTag vTag = (VisibilityAnnotationTag) tag;
if (vTag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE) {
for (AnnotationTag aTag : vTag.getAnnotations()) {
if (aTag.getType().equals(NON_EVENT_CLASS_BINARY_NAME)) {
return true;
}
}
}
}
}
return false;
}
@SuppressWarnings("unchecked")
public static void markBlocking(SootMethod method) {
AnnotationGenerator.v().annotate(method, BLOCKING_CLASS_BINARY_NAME,
AnnotationConstants.RUNTIME_VISIBLE,
Collections.EMPTY_LIST);
}
@SuppressWarnings("unchecked")
public static void markContinuable(SootMethod method) {
AnnotationGenerator.v().annotate(method, CONTINUABLE_CLASS_BINARY_NAME,
AnnotationConstants.RUNTIME_VISIBLE,
Collections.EMPTY_LIST);
}
@SuppressWarnings("unchecked")
public static void markEntity(SootClass clazz) {
AnnotationGenerator.v().annotate(clazz, ENTITY_CLASS_BINARY_NAME,
AnnotationConstants.RUNTIME_VISIBLE,
Collections.EMPTY_LIST);
}
public static void markTransformed(AbstractHost host, Object generator) {
markTransformed(host, generator, "");
}
public static void markTransformed(AbstractHost host, Object generator,
String comment) {
markTransformed(host, generator.getClass().getCanonicalName(), comment);
}
public static void markTransformed(AbstractHost host, String transformer,
String comment) {
List<AnnotationElem> elems = new ArrayList<AnnotationElem>(
asList(new AnnotationStringElem(
transformer,
's',
"value"),
new AnnotationStringElem(
comment,
's',
"comment"),
new AnnotationStringElem(
ISO8601FORMAT.format(new Date()),
's',
"date")));
AnnotationGenerator.v().annotate(host, TRANSFORMED_CLASS_BINARY_NAME,
AnnotationConstants.RUNTIME_VISIBLE,
elems);
}
public static boolean willContinue(SootMethod method) {
for (Tag tag : method.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
VisibilityAnnotationTag vTag = (VisibilityAnnotationTag) tag;
if (vTag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE) {
for (AnnotationTag aTag : vTag.getAnnotations()) {
String type = aTag.getType();
if (type.equals(CONTINUABLE_CLASS_BINARY_NAME)
|| type.equals(BLOCKING_CLASS_BINARY_NAME)) {
return true;
}
}
}
}
}
return false;
}
private static void gatherEntityInterfacesOf(SootClass base,
Set<SootClass> interfaces) {
for (Tag tag : base.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
VisibilityAnnotationTag vTag = (VisibilityAnnotationTag) tag;
if (vTag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE) {
for (AnnotationTag aTag : vTag.getAnnotations()) {
if (aTag.getType().equals(ENTITY_CLASS_BINARY_NAME)) {
if (aTag.getNumElems() != 0) {
AnnotationArrayElem values = (AnnotationArrayElem) aTag.getElemAt(0);
for (AnnotationElem element : values.getValues()) {
AnnotationClassElem classElement = (AnnotationClassElem) element;
String desc = classElement.getDesc();
String className = desc.substring(1,
desc.length() - 1).replace('/',
'.');
SootClass iFace = Scene.v().loadClassAndSupport(className);
if (interfaces.add(iFace)) {
gatherInterfaceExtensions(iFace,
interfaces);
}
}
}
}
}
}
}
}
}
private static void gatherInterfaceExtensions(SootClass base,
Set<SootClass> interfaces) {
for (SootClass extension : base.getInterfaces()) {
if (interfaces.add(extension)) {
gatherInterfaceExtensions(extension, interfaces);
}
}
}
static boolean inferEntity(SootClass base) {
if (base == null) {
return false;
}
if (base.equals(Scene.v().loadClass(Object.class.getCanonicalName(),
SootClass.SIGNATURES))) {
return false;
}
if (base.equals(Scene.v().loadClass("com.hellblazer.primeMover.runtime.EntityReference",
SootClass.SIGNATURES))) {
return false;
}
if (isMarkedEntity(base)) {
return true;
}
for (SootClass iFace : base.getInterfaces()) {
if (inferEntity(iFace)) {
return true;
}
}
return inferEntity(base.getSuperclass());
}
static boolean isMarkedEntity(SootClass base) {
for (Tag tag : base.getTags()) {
if (tag instanceof VisibilityAnnotationTag) {
VisibilityAnnotationTag vTag = (VisibilityAnnotationTag) tag;
if (vTag.getVisibility() == AnnotationConstants.RUNTIME_VISIBLE) {
for (AnnotationTag aTag : vTag.getAnnotations()) {
if (aTag.getType().equals(ENTITY_CLASS_BINARY_NAME)) {
return true;
}
}
}
}
}
return false;
}
}