org.robovm.compiler.clazz.Clazzes Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 RoboVM AB
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program. If not, see .
*/
package org.robovm.compiler.clazz;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.robovm.compiler.config.Config;
import soot.Scene;
import soot.SootClass;
import soot.SourceLocator;
import soot.options.Options;
/**
*
* @version $Id$
*/
public class Clazzes {
private final Config config;
private final List bootclasspathPaths = new ArrayList<>();
private final List classpathPaths = new ArrayList<>();
private final List paths = new ArrayList<>();
private final Map cache = new HashMap<>();
private final List allClasses = new ArrayList<>();
private boolean sootInitialized = false;
// disposed state
private boolean sootDisposed = false;
private boolean contentDisposed = false;
public Clazzes(Config config, List bootclasspath, List classpath) throws IOException {
this.config = config;
Set seen = new HashSet<>();
addPaths(bootclasspath, bootclasspathPaths, seen, true);
addPaths(classpath, classpathPaths, seen, false);
paths.addAll(bootclasspathPaths);
paths.addAll(classpathPaths);
populateCache();
}
Config getConfig() {
return config;
}
private static boolean isArchive(File f) {
String name = f.getName().toLowerCase();
return name.endsWith(".zip") || name.endsWith(".jar");
}
private void addPaths(List files, List cp, Set seen, boolean inBootclasspath) throws IOException {
for (File file : files) {
if (SourceLocator.DUMMY_CLASSPATH_JDK9_FS.equals(file.getPath()) && !seen.contains(file)) {
// java9 runtime: add special mark to allow recognize it latter when providing soot CP
Path p = new Java9RuntimePath(file, this, cp.size(), inBootclasspath);
cp.add(p);
seen.add(file);
continue;
}
if (!file.exists()) {
config.getLogger().warn("Classpath entry %s does not exist", file);
continue;
}
if (file.isFile() && !isArchive(file)) {
throw new IOException("File is not an archive file: " + file.getAbsolutePath());
}
if (file.isDirectory() && isEmpty(file)) {
continue;
}
if (!seen.contains(file)) {
Path p = createPath(file, cp, inBootclasspath);
cp.add(p);
seen.add(file);
}
}
}
private Path createPath(File file, List cp, boolean inBootclasspath) throws IOException {
return file.isDirectory()
? new DirectoryPath(file, this, cp.size(), inBootclasspath)
: new ZipFilePath(file, this, cp.size(), inBootclasspath);
}
public Path createResourcesBootclasspathPath(File file) throws IOException {
return createPath(file, bootclasspathPaths, true);
}
public Path createResourcesClasspathPath(File file) throws IOException {
return createPath(file, classpathPaths, false);
}
private boolean isEmpty(File dir) {
for (File f : dir.listFiles()) {
if (f.isFile()) {
return false;
}
if (!isEmpty(f)) {
return false;
}
}
return true;
}
private void populateCache() {
requiresContent();
for (Path p : paths) {
for (Clazz clazz : p.listClasses()) {
if (!cache.containsKey(clazz.getInternalName())) {
cache.put(clazz.getInternalName(), clazz);
allClasses.add(clazz);
}
}
}
}
public Clazz load(String internalName) {
requiresContent();
Clazz clazz = cache.get(internalName);
if (clazz == null) {
// Could be a generated class
for (Path p : paths) {
clazz = p.loadGeneratedClass(internalName);
if (clazz != null) {
break;
}
}
}
return clazz;
}
public SootClass loadSootClass(String internalName) {
Clazz clazz = load(internalName);
return clazz != null ? clazz.getSootClass() : null;
}
public List loadResources(String resource) throws IOException {
List arr = new ArrayList<>();
for (Path p : getPaths()) {
if (p.contains(resource)) {
arr.add(p.open(resource));
}
}
return arr;
}
public List getBootclasspathPaths() {
return Collections.unmodifiableList(bootclasspathPaths);
}
public List getClasspathPaths() {
return Collections.unmodifiableList(classpathPaths);
}
public List getPaths() {
return Collections.unmodifiableList(paths);
}
public List listClasses() {
requiresContent();
return Collections.unmodifiableList(allClasses);
}
SootClass getSootClass(Clazz clazz) {
requiresSoot();
if (!sootInitialized) {
initializeSoot(this);
sootInitialized = true;
}
return Scene.v().loadClassAndSupport(clazz.getClassName());
}
private static String getSootClasspath(Clazzes clazzes) {
StringBuilder sb = new StringBuilder();
for (Path path : clazzes.getPaths()) {
if (sb.length() > 0) {
sb.append(File.pathSeparator);
}
if (path instanceof Java9RuntimePath) {
// special hack for specifying Java9+ RT
sb.append(path.getFile().getPath());
// skipping generatedClassDir here as support is only for UT and it will not produce any native code
} else {
try {
sb.append(path.getFile().getCanonicalPath());
sb.append(File.pathSeparator);
sb.append(clazzes.config.getGeneratedClassDir(path).getCanonicalPath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return sb.toString();
}
/**
* sanity: checks if soot was disposed
*/
private void requiresSoot() {
if (sootDisposed)
throw new IllegalStateException("Soot has been disposed !");
}
/**
* releases soot resources, singletons once these are not required anymore to reduce memory pressure
* after this point soot is not usable anymore
*/
public void disposeSoot() {
requiresSoot();
soot.G.reset();
sootDisposed = true;
}
private void requiresContent() {
if (contentDisposed)
throw new IllegalStateException("Content has been disposed !");
}
/**
* drops caches and all classes data. after this point this data is not accessible
*/
public void disposeData() {
requiresContent();
cache.clear();
allClasses.clear();
for (Path p : paths)
p.disposeBuildData();
contentDisposed = true;
}
private static void initializeSoot(Clazzes clazzes) {
soot.G.reset();
Options.v().set_output_format(Options.output_format_jimple);
Options.v().set_include_all(true);
Options.v().set_print_tags_in_output(true);
Options.v().set_allow_phantom_refs(true);
Options.v().set_keep_line_number(true);
Options.v().set_soot_classpath(getSootClasspath(clazzes));
/*
* Enable the use-original-names phase to merge local variables and
* verbose logging for debugging purposes only.
*/
// Options.v().set_verbose(true);
// Options.v().setPhaseOption("jb", "use-original-names:true");
/*
* Disable the jb.dae phase (DeadAssignmentEliminator) since it removes
* LDC instructions which would have thrown a NoClassDefFoundError.
* TODO: Report this to soot as a bug?
*/
Options.v().setPhaseOption("jb.dae", "enabled:false");
/*
* Disable the jb.uce phase (UnreachableCodeEliminator) since it seems
* to remove try-catch blocks which catches a non-existing Throwable
* class. This should generate a NoClassDefFoundError at runtime but
* with the UCE in place no exception is thrown.
*/
Options.v().setPhaseOption("jb.uce", "enabled:false");
/*
* Enable jap.npc (NullPointerChecker) and jap.abc (ArrayBoundsChecker)
* phases in the annotation pack. The annotation pack is enabled by
* default but all its phases are disabled by default.
*/
Options.v().setPhaseOption("jap.npc", "enabled:true");
Options.v().setPhaseOption("jap.abc", "enabled:true");
/*
* Enable the jop (Jimple optimization) pack but disable all phases for
* now.
*/
Options.v().setPhaseOption("jop", "enabled:true");
Options.v().setPhaseOption("jop.cse", "enabled:false");
Options.v().setPhaseOption("jop.bcm", "enabled:false");
Options.v().setPhaseOption("jop.lcm", "enabled:false");
Options.v().setPhaseOption("jop.cp", "enabled:false");
Options.v().setPhaseOption("jop.cpf", "enabled:false");
Options.v().setPhaseOption("jop.cbf", "enabled:false");
Options.v().setPhaseOption("jop.dae", "enabled:false");
Options.v().setPhaseOption("jop.nce", "enabled:false");
Options.v().setPhaseOption("jop.uce1", "enabled:false");
Options.v().setPhaseOption("jop.ubf1", "enabled:false");
Options.v().setPhaseOption("jop.uce2", "enabled:false");
Options.v().setPhaseOption("jop.ubf2", "enabled:false");
Options.v().setPhaseOption("jop.ule", "enabled:false");
Scene.v().loadNecessaryClasses();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy