All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.SootResolver.orig Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
/* Soot - a J*va Optimization Framework
 * Copyright (C) 2000 Patrice Pominville
 * Copyright (C) 2004 Ondrej Lhotak, Ganesh Sittampalam
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the Sable Research Group and others 1997-1999.  
 * See the 'credits' file distributed with Soot for the complete list of
 * contributors.  (Soot is distributed at http://www.sable.mcgill.ca/soot)
 */

package soot;

import java.io.IOException;
import java.io.InputStream;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.JastAddJ.BytecodeParser;
import soot.JastAddJ.CompilationUnit;
import soot.JastAddJ.JastAddJavaParser;
import soot.JastAddJ.JavaParser;
import soot.JastAddJ.Program;
import soot.javaToJimple.IInitialResolver.Dependencies;
import soot.options.Options;
import soot.util.ConcurrentHashMultiMap;
import soot.util.MultiMap;

/**
 * Loads symbols for SootClasses from either class files or jimple files.
 */
public class SootResolver {
<<<<<<< HEAD
    /**
     * Maps each resolved class to a list of all references in it.
     */
    protected MultiMap classToTypesSignature = new ConcurrentHashMultiMap();

    /**
     * Maps each resolved class to a list of all references in it.
     */
    protected MultiMap classToTypesHierarchy = new ConcurrentHashMultiMap();

    /**
     * SootClasses waiting to be resolved.
     */
    @SuppressWarnings("unchecked")
    private final Deque[] worklist = new Deque[4];

    private Program program = null;

    public SootResolver(Singletons.Global g) {
        worklist[SootClass.HIERARCHY] = new ArrayDeque();
        worklist[SootClass.SIGNATURES] = new ArrayDeque();
        worklist[SootClass.BODIES] = new ArrayDeque();
    }

    protected void initializeProgram() {
        if (Options.v().src_prec() != Options.src_prec_apk_c_j) {
            program = new Program();
            program.state().reset();

            program.initBytecodeReader(new BytecodeParser());
            program.initJavaParser(new JavaParser() {
                @Override
                public CompilationUnit parse(InputStream is, String fileName)
                        throws IOException, beaver.Parser.Exception {
                    return new JastAddJavaParser().parse(is, fileName);
                }
            });

            program.options().initOptions();
            program.options().addKeyValueOption("-classpath");
            program.options().setValueForOption(Scene.v().getSootClassPath(), "-classpath");
            if (Options.v().src_prec() == Options.src_prec_java)
                program.setSrcPrec(Program.SRC_PREC_JAVA);
            else if (Options.v().src_prec() == Options.src_prec_class)
                program.setSrcPrec(Program.SRC_PREC_CLASS);
            else if (Options.v().src_prec() == Options.src_prec_only_class)
                program.setSrcPrec(Program.SRC_PREC_CLASS);
            program.initPaths();
        }
    }

    public static SootResolver v() {
        if (ModuleUtil.module_mode())
            return G.v().soot_SootModuleResolver();
        return G.v().soot_SootResolver();
    }

    /**
     * Returns true if we are resolving all class refs recursively.
     */
    protected boolean resolveEverything() {
        if (Options.v().on_the_fly())
            return false;
        return (Options.v().whole_program() || Options.v().whole_shimple() || Options.v().full_resolver()
                || Options.v().output_format() == Options.output_format_dava);
    }

    /**
     * Returns a (possibly not yet resolved) SootClass to be used in references
     * to a class. If/when the class is resolved, it will be resolved into this
     * SootClass.
     */
    public SootClass makeClassRef(String className) {
        if (Scene.v().containsClass(className))
            return Scene.v().getSootClass(className);

        SootClass newClass;
        if (className.endsWith(SootModuleInfo.MODULE_INFO)) {
            newClass = new SootModuleInfo(className, null);
        } else {

            newClass = new SootClass(className);
        }
        newClass.setResolvingLevel(SootClass.DANGLING);
        Scene.v().addClass(newClass);

        return newClass;
    }

    /**
     * Resolves the given class. Depending on the resolver settings, may decide
     * to resolve other classes as well. If the class has already been resolved,
     * just returns the class that was already resolved.
     */
    public SootClass resolveClass(String className, int desiredLevel) {
        SootClass resolvedClass = null;
        try {
            resolvedClass = makeClassRef(className);
            addToResolveWorklist(resolvedClass, desiredLevel);
            processResolveWorklist();
            return resolvedClass;
        } catch (SootClassNotFoundException e) {
            // remove unresolved class and rethrow
            if (resolvedClass != null) {
                assert resolvedClass.resolvingLevel() == SootClass.DANGLING;
                Scene.v().removeClass(resolvedClass);
            }
            throw e;
        }
    }

    /**
     * Resolve all classes on toResolveWorklist.
     */
    protected void processResolveWorklist() {
        for (int i = SootClass.BODIES; i >= SootClass.HIERARCHY; i--) {
            while (!worklist[i].isEmpty()) {
                SootClass sc = worklist[i].pop();
                if (resolveEverything()) { // Whole program mode
                    boolean onlySignatures = sc.isPhantom() || (Options.v().no_bodies_for_excluded()
                            && Scene.v().isExcluded(sc) && !Scene.v().getBasicClasses().contains(sc.getName()));
                    if (onlySignatures) {
                        bringToSignatures(sc);
                        sc.setPhantomClass();
                        for (SootMethod m : sc.getMethods()) {
                            m.setPhantom(true);
                        }
                        for (SootField f : sc.getFields()) {
                            f.setPhantom(true);
                        }
                    } else
                        bringToBodies(sc);
                } else { // No transitive
                    switch (i) {
                        case SootClass.BODIES:
                            bringToBodies(sc);
                            break;
                        case SootClass.SIGNATURES:
                            bringToSignatures(sc);
                            break;
                        case SootClass.HIERARCHY:
                            bringToHierarchy(sc);
                            break;
                    }
                }
            }
        }
    }

    protected void addToResolveWorklist(Type type, int level) {
        // We go from Type -> SootClass directly, since RefType.getSootClass
        // calls makeClassRef anyway
        if (type instanceof RefType)
            addToResolveWorklist(((RefType) type).getSootClass(), level);
        else if (type instanceof ArrayType)
            addToResolveWorklist(((ArrayType) type).baseType, level);
        // Other types ignored
    }

    protected void addToResolveWorklist(SootClass sc, int desiredLevel) {
        if (sc.resolvingLevel() >= desiredLevel)
            return;
        worklist[desiredLevel].add(sc);
    }

    /**
     * Hierarchy - we know the hierarchy of the class and that's it requires at
     * least Hierarchy for all supertypes and enclosing types.
     */
    protected void bringToHierarchy(SootClass sc) {
        if (sc.resolvingLevel() >= SootClass.HIERARCHY)
            return;
        if (Options.v().debug_resolver())
            G.v().out.println("bringing to HIERARCHY: " + sc);
        sc.setResolvingLevel(SootClass.HIERARCHY);

        bringToHierarchyUnchecked(sc);
    }

    protected void bringToHierarchyUnchecked(SootClass sc) {
        String className = sc.getName();
        ClassSource is;
        if (ModuleUtil.module_mode()) {
            is = ModulePathSourceLocator.v().getClassSource(className, com.google.common.base.Optional.fromNullable(sc.moduleName));
        } else {
            is = SourceLocator.v().getClassSource(className);
        }
        try {
            boolean modelAsPhantomRef = is == null;
            if (modelAsPhantomRef) {
                if (!Scene.v().allowsPhantomRefs()) {
                    String suffix = "";
                    if (className.equals("java.lang.Object")) {
                        suffix = " Try adding rt.jar to Soot's classpath, e.g.:\n"
                                + "java -cp sootclasses.jar soot.Main -cp "
                                + ".:/path/to/jdk/jre/lib/rt.jar ";
                    } else if (className.equals("javax.crypto.Cipher")) {
                        suffix = " Try adding jce.jar to Soot's classpath, e.g.:\n"
                                + "java -cp sootclasses.jar soot.Main -cp "
                                + ".:/path/to/jdk/jre/lib/rt.jar:/path/to/jdk/jre/lib/jce.jar ";
                    }
                    throw new SootClassNotFoundException(
                            "couldn't find class: " + className + " (is your soot-class-path set properly?)" + suffix);
                } else {
                    // G.v().out.println("Warning: " + className + " is a
                    // phantom class!");
                    sc.setPhantomClass();
                }
            } else {
                Dependencies dependencies = is.resolve(sc);
                if (!dependencies.typesToSignature.isEmpty())
                    classToTypesSignature.putAll(sc, dependencies.typesToSignature);
                if (!dependencies.typesToHierarchy.isEmpty())
                    classToTypesHierarchy.putAll(sc, dependencies.typesToHierarchy);
            }
        } finally {
            if (is != null)
                is.close();
        }
        reResolveHierarchy(sc);
    }

    public void reResolveHierarchy(SootClass sc) {
        // Bring superclasses to hierarchy
        if (sc.hasSuperclass())
            addToResolveWorklist(sc.getSuperclass(), SootClass.HIERARCHY);
        if (sc.hasOuterClass())
            addToResolveWorklist(sc.getOuterClass(), SootClass.HIERARCHY);
        for (SootClass iface : sc.getInterfaces()) {
            addToResolveWorklist(iface, SootClass.HIERARCHY);
        }
    }

    /**
     * Signatures - we know the signatures of all methods and fields requires at
     * least Hierarchy for all referred to types in these signatures.
     */
    protected void bringToSignatures(SootClass sc) {
        if (sc.resolvingLevel() >= SootClass.SIGNATURES)
            return;
        bringToHierarchy(sc);
        if (Options.v().debug_resolver())
            G.v().out.println("bringing to SIGNATURES: " + sc);
        sc.setResolvingLevel(SootClass.SIGNATURES);

        bringToSignaturesUnchecked(sc);
    }

    protected void bringToSignaturesUnchecked(SootClass sc) {
        for (SootField f : sc.getFields()) {
            addToResolveWorklist(f.getType(), SootClass.HIERARCHY);
        }
        for (SootMethod m : sc.getMethods()) {
            addToResolveWorklist(m.getReturnType(), SootClass.HIERARCHY);
            for (Type ptype : m.getParameterTypes()) {
                addToResolveWorklist(ptype, SootClass.HIERARCHY);
            }
            List exceptions = m.getExceptionsUnsafe();
            if (exceptions != null) {
                for (SootClass exception : exceptions) {
                    addToResolveWorklist(exception, SootClass.HIERARCHY);
                }
            }
        }

        // Bring superclasses to signatures
        if (sc.hasSuperclass())
            addToResolveWorklist(sc.getSuperclass(), SootClass.SIGNATURES);
        for (SootClass iface : sc.getInterfaces()) {
            addToResolveWorklist(iface, SootClass.SIGNATURES);
        }
    }

    /**
     * Bodies - we can now start loading the bodies of methods for all referred
     * to methods and fields in the bodies, requires signatures for the method
     * receiver and field container, and hierarchy for all other classes
     * referenced in method references. Current implementation does not
     * distinguish between the receiver and other references. Therefore, it is
     * conservative and brings all of them to signatures. But this could/should
     * be improved.
     */
    protected void bringToBodies(SootClass sc) {
        if (sc.resolvingLevel() >= SootClass.BODIES)
            return;
        bringToSignatures(sc);
        if (Options.v().debug_resolver())
            G.v().out.println("bringing to BODIES: " + sc);
        sc.setResolvingLevel(SootClass.BODIES);

        bringToBodiesUnchecked(sc);
    }

    protected void bringToBodiesUnchecked(SootClass sc) {
        {
            Collection references = classToTypesHierarchy.get(sc);
            if (references != null) {
                // This must be an iterator, not a for-all since the underlying
                // collection may change as we go
                Iterator it = references.iterator();
                while (it.hasNext()) {
                    final Type t = it.next();
                    addToResolveWorklist(t, SootClass.HIERARCHY);
                }
            }
        }

        {
            Collection references = classToTypesSignature.get(sc);
            if (references != null) {
                // This must be an iterator, not a for-all since the underlying
                // collection may change as we go
                Iterator it = references.iterator();
                while (it.hasNext()) {
                    final Type t = it.next();
                    addToResolveWorklist(t, SootClass.SIGNATURES);
                }
            }
        }
    }

    public void reResolve(SootClass cl, int newResolvingLevel) {
        int resolvingLevel = cl.resolvingLevel();
        if (resolvingLevel >= newResolvingLevel)
            return;
        reResolveHierarchy(cl);
        cl.setResolvingLevel(newResolvingLevel);
        addToResolveWorklist(cl, resolvingLevel);
        processResolveWorklist();
    }

    public void reResolve(SootClass cl) {
        reResolve(cl, SootClass.HIERARCHY);
    }

    public Program getProgram() {
        if (program == null)
            initializeProgram();
        return program;
    }

    protected class SootClassNotFoundException extends RuntimeException {
        /**
         *
         */
        private static final long serialVersionUID = 1563461446590293827L;

        private SootClassNotFoundException(String s) {
            super(s);
        }
    }
=======
	private static final Logger logger = LoggerFactory.getLogger(SootResolver.class);
	/** Maps each resolved class to a list of all references in it. */
	protected MultiMap classToTypesSignature = new ConcurrentHashMultiMap();

	/** Maps each resolved class to a list of all references in it. */
	protected MultiMap classToTypesHierarchy = new ConcurrentHashMultiMap();

	/** SootClasses waiting to be resolved. */
	@SuppressWarnings("unchecked")
	private final Deque[] worklist = new Deque[4];

	private Program program = null;

	public SootResolver(Singletons.Global g) {
		worklist[SootClass.HIERARCHY] = new ArrayDeque();
		worklist[SootClass.SIGNATURES] = new ArrayDeque();
		worklist[SootClass.BODIES] = new ArrayDeque();
	}

	protected void initializeProgram() {
		if (Options.v().src_prec() != Options.src_prec_apk_c_j) {
			program = new Program();
			program.state().reset();

			program.initBytecodeReader(new BytecodeParser());
			program.initJavaParser(new JavaParser() {
				@Override
				public CompilationUnit parse(InputStream is, String fileName)
						throws IOException, beaver.Parser.Exception {
					return new JastAddJavaParser().parse(is, fileName);
				}
			});

			program.options().initOptions();
			program.options().addKeyValueOption("-classpath");
			program.options().setValueForOption(Scene.v().getSootClassPath(), "-classpath");
			if (Options.v().src_prec() == Options.src_prec_java)
				program.setSrcPrec(Program.SRC_PREC_JAVA);
			else if (Options.v().src_prec() == Options.src_prec_class)
				program.setSrcPrec(Program.SRC_PREC_CLASS);
			else if (Options.v().src_prec() == Options.src_prec_only_class)
				program.setSrcPrec(Program.SRC_PREC_CLASS);
			program.initPaths();
		}
	}

	public static SootResolver v() {
		return G.v().soot_SootResolver();
	}

	/** Returns true if we are resolving all class refs recursively. */
	protected boolean resolveEverything() {
		if (Options.v().on_the_fly())
			return false;
		return (Options.v().whole_program() || Options.v().whole_shimple() || Options.v().full_resolver()
				|| Options.v().output_format() == Options.output_format_dava);
	}

	/**
	 * Returns a (possibly not yet resolved) SootClass to be used in references to a
	 * class. If/when the class is resolved, it will be resolved into this
	 * SootClass.
	 */
	public SootClass makeClassRef(String className) {
		if (Scene.v().containsClass(className))
			return Scene.v().getSootClass(className);

		SootClass newClass;
		newClass = new SootClass(className);
		newClass.setResolvingLevel(SootClass.DANGLING);
		Scene.v().addClass(newClass);

		return newClass;
	}

	/**
	 * Resolves the given class. Depending on the resolver settings, may decide to
	 * resolve other classes as well. If the class has already been resolved, just
	 * returns the class that was already resolved.
	 */
	public SootClass resolveClass(String className, int desiredLevel) {
		SootClass resolvedClass = null;
		try {
			resolvedClass = makeClassRef(className);
			addToResolveWorklist(resolvedClass, desiredLevel);
			processResolveWorklist();
			return resolvedClass;
		} catch (SootClassNotFoundException e) {
			// remove unresolved class and rethrow
			if (resolvedClass != null) {
				assert resolvedClass.resolvingLevel() == SootClass.DANGLING;
				Scene.v().removeClass(resolvedClass);
			}
			throw e;
		}
	}

	/** Resolve all classes on toResolveWorklist. */
	protected void processResolveWorklist() {
		for (int i = SootClass.BODIES; i >= SootClass.HIERARCHY; i--) {
			while (!worklist[i].isEmpty()) {
				SootClass sc = worklist[i].pop();
				if (resolveEverything()) { // Whole program mode
					boolean onlySignatures = sc.isPhantom() || (Options.v().no_bodies_for_excluded()
							&& Scene.v().isExcluded(sc) && !Scene.v().getBasicClasses().contains(sc.getName()));
					if (onlySignatures) {
						bringToSignatures(sc);
						sc.setPhantomClass();
						for (SootMethod m : sc.getMethods()) {
							m.setPhantom(true);
						}
						for (SootField f : sc.getFields()) {
							f.setPhantom(true);
						}
					} else
						bringToBodies(sc);
				} else { // No transitive
					switch (i) {
					case SootClass.BODIES:
						bringToBodies(sc);
						break;
					case SootClass.SIGNATURES:
						bringToSignatures(sc);
						break;
					case SootClass.HIERARCHY:
						bringToHierarchy(sc);
						break;
					}
				}
			}
		}
	}

	protected void addToResolveWorklist(Type type, int level) {
		// We go from Type -> SootClass directly, since RefType.getSootClass
		// calls makeClassRef anyway
		if (type instanceof RefType)
			addToResolveWorklist(((RefType) type).getSootClass(), level);
		else if (type instanceof ArrayType)
			addToResolveWorklist(((ArrayType) type).baseType, level);
		// Other types ignored
	}

	protected void addToResolveWorklist(SootClass sc, int desiredLevel) {
		if (sc.resolvingLevel() >= desiredLevel)
			return;
		worklist[desiredLevel].add(sc);
	}

	/**
	 * Hierarchy - we know the hierarchy of the class and that's it requires at
	 * least Hierarchy for all supertypes and enclosing types.
	 */
	protected void bringToHierarchy(SootClass sc) {
		if (sc.resolvingLevel() >= SootClass.HIERARCHY)
			return;
		if (Options.v().debug_resolver())
			logger.debug("bringing to HIERARCHY: " + sc);
		sc.setResolvingLevel(SootClass.HIERARCHY);

		bringToHierarchyUnchecked(sc);
	}

	protected void bringToHierarchyUnchecked(SootClass sc) {
		String className = sc.getName();
		ClassSource is = SourceLocator.v().getClassSource(className);
		try {
			boolean modelAsPhantomRef = is == null;
			if (modelAsPhantomRef) {
				if (!Scene.v().allowsPhantomRefs()) {
					String suffix = "";
					if (className.equals("java.lang.Object")) {
						suffix = " Try adding rt.jar to Soot's classpath, e.g.:\n"
								+ "java -cp sootclasses.jar soot.Main -cp "
								+ ".:/path/to/jdk/jre/lib/rt.jar ";
					} else if (className.equals("javax.crypto.Cipher")) {
						suffix = " Try adding jce.jar to Soot's classpath, e.g.:\n"
								+ "java -cp sootclasses.jar soot.Main -cp "
								+ ".:/path/to/jdk/jre/lib/rt.jar:/path/to/jdk/jre/lib/jce.jar ";
					}
					throw new SootClassNotFoundException(
							"couldn't find class: " + className + " (is your soot-class-path set properly?)" + suffix);
				} else {
					// logger.warn("" + className + " is a
					// phantom class!");
					sc.setPhantomClass();
				}
			} else {
				Dependencies dependencies = is.resolve(sc);
				if (!dependencies.typesToSignature.isEmpty())
					classToTypesSignature.putAll(sc, dependencies.typesToSignature);
				if (!dependencies.typesToHierarchy.isEmpty())
					classToTypesHierarchy.putAll(sc, dependencies.typesToHierarchy);
			}
		} finally {
			if (is != null)
				is.close();
		}
		reResolveHierarchy(sc);
	}

	public void reResolveHierarchy(SootClass sc) {
		// Bring superclasses to hierarchy
		SootClass superClass = sc.getSuperclassUnsafe();
		if (superClass != null)
			addToResolveWorklist(superClass, SootClass.HIERARCHY);
		SootClass outerClass = sc.getOuterClassUnsafe();
		if (outerClass != null)
			addToResolveWorklist(outerClass, SootClass.HIERARCHY);
		for (SootClass iface : sc.getInterfaces()) {
			addToResolveWorklist(iface, SootClass.HIERARCHY);
		}
	}

	/**
	 * Signatures - we know the signatures of all methods and fields requires at
	 * least Hierarchy for all referred to types in these signatures.
	 */
	protected void bringToSignatures(SootClass sc) {
		if (sc.resolvingLevel() >= SootClass.SIGNATURES)
			return;
		bringToHierarchy(sc);
		if (Options.v().debug_resolver())
			logger.debug("bringing to SIGNATURES: " + sc);
		sc.setResolvingLevel(SootClass.SIGNATURES);

		bringToSignaturesUnchecked(sc);
	}

	protected void bringToSignaturesUnchecked(SootClass sc) {
		for (SootField f : sc.getFields()) {
			addToResolveWorklist(f.getType(), SootClass.HIERARCHY);
		}
		for (SootMethod m : sc.getMethods()) {
			addToResolveWorklist(m.getReturnType(), SootClass.HIERARCHY);
			for (Type ptype : m.getParameterTypes()) {
				addToResolveWorklist(ptype, SootClass.HIERARCHY);
			}
			List exceptions = m.getExceptionsUnsafe();
			if (exceptions != null) {
				for (SootClass exception : exceptions) {
					addToResolveWorklist(exception, SootClass.HIERARCHY);
				}
			}
		}

		// Bring superclasses to signatures
		reResolveHierarchy(sc);
	}

	/**
	 * Bodies - we can now start loading the bodies of methods for all referred to
	 * methods and fields in the bodies, requires signatures for the method receiver
	 * and field container, and hierarchy for all other classes referenced in method
	 * references. Current implementation does not distinguish between the receiver
	 * and other references. Therefore, it is conservative and brings all of them to
	 * signatures. But this could/should be improved.
	 */
	protected void bringToBodies(SootClass sc) {
		if (sc.resolvingLevel() >= SootClass.BODIES)
			return;
		bringToSignatures(sc);
		if (Options.v().debug_resolver())
			logger.debug("bringing to BODIES: " + sc);
		sc.setResolvingLevel(SootClass.BODIES);

		bringToBodiesUnchecked(sc);
	}

	protected void bringToBodiesUnchecked(SootClass sc) {
		{
			Collection references = classToTypesHierarchy.get(sc);
			if (references != null) {
				// This must be an iterator, not a for-all since the underlying
				// collection may change as we go
				Iterator it = references.iterator();
				while (it.hasNext()) {
					final Type t = it.next();
					addToResolveWorklist(t, SootClass.HIERARCHY);
				}
			}
		}

		{
			Collection references = classToTypesSignature.get(sc);
			if (references != null) {
				// This must be an iterator, not a for-all since the underlying
				// collection may change as we go
				Iterator it = references.iterator();
				while (it.hasNext()) {
					final Type t = it.next();
					addToResolveWorklist(t, SootClass.SIGNATURES);
				}
			}
		}
	}

	public void reResolve(SootClass cl, int newResolvingLevel) {
		int resolvingLevel = cl.resolvingLevel();
		if (resolvingLevel >= newResolvingLevel)
			return;
		reResolveHierarchy(cl);
		cl.setResolvingLevel(newResolvingLevel);
		addToResolveWorklist(cl, resolvingLevel);
		processResolveWorklist();
	}

	public void reResolve(SootClass cl) {
		reResolve(cl, SootClass.HIERARCHY);
	}

	public Program getProgram() {
		if (program == null)
			initializeProgram();
		return program;
	}

	public class SootClassNotFoundException extends RuntimeException {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1563461446590293827L;

		private SootClassNotFoundException(String s) {
			super(s);
		}
	}
>>>>>>> origin/develop
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy