use JDT again
[eclipsethinslicer.git] / Svelte / src / edu / berkeley / cs / bodik / svelte / SvelteAnalysisEngine.java
blob3480e16a718ec5eab7d9bff78bc832c6fb123957
1 /*******************************************************************************
2 * This program and the accompanying materials
3 * are made available under the terms of the Eclipse Public License v1.0
4 * which accompanies this distribution, and is available at
5 * http://www.eclipse.org/legal/epl-v10.html.
6 *
7 * This file is a derivative of code released by the University of
8 * California under the terms listed below.
10 * Refinement Analysis Tools is Copyright ©2007 The Regents of the
11 * University of California (Regents). Provided that this notice and
12 * the following two paragraphs are included in any distribution of
13 * Refinement Analysis Tools or its derivative work, Regents agrees
14 * not to assert any of Regents' copyright rights in Refinement
15 * Analysis Tools against recipient for recipient’s reproduction,
16 * preparation of derivative works, public display, public
17 * performance, distribution or sublicensing of Refinement Analysis
18 * Tools and derivative works, in source code and object code form.
19 * This agreement not to assert does not confer, by implication,
20 * estoppel, or otherwise any license or rights in any intellectual
21 * property of Regents, including, but not limited to, any patents
22 * of Regents or Regents’ employees.
24 * IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT,
25 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
26 * INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE
27 * AND ITS DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE AND FURTHER DISCLAIMS ANY STATUTORY
33 * WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE AND ACCOMPANYING
34 * DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS
35 * IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
36 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
38 package edu.berkeley.cs.bodik.svelte;
40 import java.io.File;
41 import java.io.FileNotFoundException;
42 import java.io.IOException;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.LinkedList;
46 import java.util.List;
47 import java.util.Properties;
48 import java.util.jar.JarFile;
50 import org.eclipse.core.resources.IFile;
51 import org.eclipse.core.resources.IProject;
52 import org.eclipse.core.resources.ResourcesPlugin;
53 import org.eclipse.core.runtime.IPath;
54 import org.eclipse.jdt.core.IClasspathEntry;
55 import org.eclipse.jdt.core.IJavaProject;
56 import org.eclipse.jdt.core.JavaCore;
57 import org.eclipse.jdt.core.JavaModelException;
59 import com.ibm.wala.cast.java.client.JavaSourceAnalysisEngine;
60 import com.ibm.wala.cast.java.client.JdtJavaSourceAnalysisEngine;
61 import com.ibm.wala.cast.java.client.impl.ZeroOneContainerCFABuilderFactory;
62 import com.ibm.wala.classLoader.BinaryDirectoryTreeModule;
63 import com.ibm.wala.classLoader.EclipseSourceFileModule;
64 import com.ibm.wala.classLoader.JarFileModule;
65 import com.ibm.wala.classLoader.Module;
66 import com.ibm.wala.classLoader.SourceDirectoryTreeModule;
67 import com.ibm.wala.classLoader.SourceFileModule;
68 import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
69 import com.ibm.wala.eclipse.util.CancelException;
70 import com.ibm.wala.eclipse.util.EclipseProjectPath;
71 import com.ibm.wala.ipa.callgraph.AnalysisCache;
72 import com.ibm.wala.ipa.callgraph.AnalysisOptions;
73 import com.ibm.wala.ipa.callgraph.AnalysisScope;
74 import com.ibm.wala.ipa.callgraph.CallGraph;
75 import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
76 import com.ibm.wala.ipa.callgraph.Entrypoint;
77 import com.ibm.wala.ipa.callgraph.impl.Util;
78 import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
79 import com.ibm.wala.ipa.cha.IClassHierarchy;
80 import com.ibm.wala.properties.WalaProperties;
81 import com.ibm.wala.util.debug.Assertions;
82 import com.ibm.wala.util.debug.UnimplementedError;
84 import edu.berkeley.cs.bodik.svelte.plugin.EclipseJdtUtils;
86 /**
87 * A SvelteAnalysisEngine simplifies the process of setting up the analysis scope and getting the
88 * call graph and pointer analysis. To get a CallGraph and PointerAnalysis, add the files you wish
89 * to analyze (e.g. with <code>addSourceDir()</code> and
90 * <code>addJar()<code>, and define the entrypoints. You should probably
91 * also add <code>setExclusionsFile()</code>. You may then <code>getCallGraph()</code> and
92 * <code>getPointerAnalysis()</code>, which implicitly build the call graph if it has not
93 * been build with <code>build()</code>.
95 * @author evan
98 public class SvelteAnalysisEngine extends JdtJavaSourceAnalysisEngine {
100 // /////////////////////////////////////////
101 // /// RT JAR FINDER -- SHOULD REPLACE /////
102 // /////////////////////////////////////////
104 // TODO: why does this throw NullPointerException ? ...
105 // TODO: simplify. all we need is rtJar, get rid of special WALA stuff
106 // All this does is figure out the Java System libraries... what a waste...
107 protected static String javaHomePath;
108 public static List<String> rtJar;
109 static {
110 boolean found = false;
111 try {
112 rtJar = new LinkedList<String>();
114 Properties p = WalaProperties.loadProperties();
115 javaHomePath = p.getProperty(WalaProperties.J2SE_DIR);
117 if (new File(javaHomePath).isDirectory()) {
118 if ("Mac OS X".equals(System.getProperty("os.name"))) { // nick
120 * todo: {@link WalaProperties#getJ2SEJarFiles()}
122 rtJar.add(javaHomePath + "/Classes/classes.jar");
123 rtJar.add(javaHomePath + "/Classes/ui.jar");
124 } else {
125 rtJar.add(javaHomePath + File.separator + "classes.jar");
126 rtJar.add(javaHomePath + File.separator + "rt.jar");
127 rtJar.add(javaHomePath + File.separator + "core.jar");
128 rtJar.add(javaHomePath + File.separator + "vm.jar");
130 found = true;
132 } catch (Exception e) {
133 // no properties
136 if (!found) {
137 javaHomePath = System.getProperty("java.home");
138 if ("Mac OS X".equals(System.getProperty("os.name"))) { // nick
139 rtJar.add(javaHomePath + "/../Classes/classes.jar");
140 rtJar.add(javaHomePath + "/../Classes/ui.jar");
141 } else {
142 rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "rt.jar");
143 rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "core.jar");
144 rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "vm.jar");
145 rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "classes.jar");
150 // //////////////////////////////////////////////////////
151 // /// CORE -- CONTRUCTOR AND ANALSYSIS SCOPE SETUP /////
152 // //////////////////////////////////////////////////////
154 protected boolean built = false;
155 protected CallGraph cg;
157 // entry point stuff
158 protected String mainClassDescriptors[] = null;
161 * Default constructor. By default, only system jars are added to analysis scope.
163 public SvelteAnalysisEngine() {
164 setExclusionsFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS);
165 // setExclusionsFile("dat/Java60RegressionExclusions.txt");
168 public void addRtJar() {
169 for (String lib : rtJar) {
170 File libFile = new File(lib);
171 if (libFile.exists()) {
172 try {
173 addSystemModule(new JarFileModule(new JarFile(libFile)));
174 } catch (IOException e) {
175 e.printStackTrace();
181 // called by some WALA thing when making call graph
182 @Override
183 protected Iterable<Entrypoint> makeDefaultEntrypoints(AnalysisScope scope, IClassHierarchy cha) {
184 if (mainClassDescriptors == null)
185 throw new UnimplementedError(
186 "Cannot use SvelteAnalysisEngine without defining entrypoints!");
187 return Util.makeMainEntrypoints(EclipseProjectPath.SOURCE_REF, cha, mainClassDescriptors);
191 * Tell the analysis engine the entrypoints for the call graph. Given a fully qualified
192 * classname, this function will look for a method
193 * <code>public static void main(String[])</code> in the classname and use that as the sole
194 * entrypoint.
196 * @param packageAndClass
197 * A fully qualified class name, such as "com.example.mypackage.MyClass"
198 * @return
200 public void findEntrypointFromClassnameOfMain(String packageAndClass) {
201 mainClassDescriptors = new String[] { "L" + packageAndClass.replace('.', '/') };
205 * Tell the analysis engine the entrypoints for the call graph. Given a fully qualified
206 * classname, this function will look for a method
207 * <code>public static void main(String[])</code> in the classname and use that as the sole
208 * entrypoint.
210 * @param packageAndClass
211 * A fully qualified class name, such as "com.example.mypackage.MyClass"
212 * @return
214 public void findEntrypointFromClassnamesOfMain(Collection<String> packagesAndClasses) {
215 mainClassDescriptors = new String[packagesAndClasses.size()];
216 int i = 0;
217 for (String s : packagesAndClasses) {
218 mainClassDescriptors[i++] = "L" + s.replace('.', '/');
222 public boolean isBuilt() {
223 return built;
226 public void build() {
227 if (!built)
228 rebuild();
231 public void rebuild() {
232 try {
233 cg = buildDefaultCallGraph();
234 built = true;
235 } catch (CancelException e) {
236 // TODO probably should just throw this.
237 e.printStackTrace();
238 } catch (IllegalArgumentException e) {
239 // TODO what to do here and below?
240 e.printStackTrace();
241 } catch (IOException e) {
242 e.printStackTrace();
246 @Override
247 public CallGraph getCallGraph() {
248 if (!built)
249 rebuild();
250 return cg;
253 @Override
254 public PointerAnalysis getPointerAnalysis() {
255 if (!built)
256 rebuild();
257 return super.getPointerAnalysis();
260 // ////////////////////////////////////////////////////////
261 // /// ADD SOURCE & BINARY CODE TO THE ANALYSIS SCOPE /////
262 // ////////////////////////////////////////////////////////
265 * Add the specified jar file to the analysis scope with addCompiledModule()
267 * @param path
268 * @throws IOException
270 public void addJar(String path) throws IOException {
271 File f = new File(path);
272 if (!f.exists())
273 throw new FileNotFoundException("SvelteAnalysisEnginge.addJar: Couldn't find jar "
274 + path);
275 addCompiledModule(new JarFileModule(new JarFile(f)));
279 * Add the specified jar file to the analysis scope with addSystemModule()
281 * @param path
282 * @throws IOException
284 public void addSystemJar(String path) throws IOException {
285 File f = new File(path);
286 if (!f.exists())
287 throw new FileNotFoundException("SvelteAnalysisEnginge.addJar: Couldn't find jar "
288 + path);
289 addSystemModule(new JarFileModule(new JarFile(f)));
293 * Add the specified directory of binary .class files to the analysis scope.
295 * @param path
296 * Directory name should be the root of a package hierarchy, i.e.
297 * com.example.mypackage.MyClass should be found in <code>path</code>/com/example/mypackage/MyClass.class
299 * @throws FileNotFoundException
301 public void addClassDir(String path) throws IOException {
302 File f = new File(path);
303 if (!f.exists())
304 throw new FileNotFoundException();
305 addCompiledModule(new BinaryDirectoryTreeModule(f));
309 * Add the specified directory full of .java files to the analysis scope. All .java files in the
310 * directory tree will be analyzed with WALA CAst (Polyglot)
312 * @param path
313 * @throws FileNotFoundException
315 public void addJavaDir(String path) throws IOException {
316 File f = new File(path);
317 if (!f.exists())
318 throw new FileNotFoundException();
319 addSourceModule(new SourceDirectoryTreeModule(f));
322 public void addJavaDir(IPath path) throws IOException {
323 addJavaDir(path.toOSString());
327 * Add the Java directory referenced by <code>path</code> to the analysis scope, but do not
328 * include any Java files referenced in <code>exceptions</code>, referenced relatively
330 * @param path
331 * The path of the directory containing *.java source files.
333 * @param exceptions
334 * Java files to specifically not add in the analysis scope. They should by relative
335 * paths, relative to the directory path, e.g. if I want to add
336 * /home/evan/myproj/src, but not /home/evan/myproj/src/foo/Foo.java, an exception
337 * string would be "foo/Foo.java"
339 * @throws IOException
341 public void addJavaDirExcept(final String path, final String exceptions[]) throws IOException {
342 File f = new File(path);
343 if (!f.exists())
344 throw new FileNotFoundException();
345 SourceDirectoryTreeModule sdtm = new SourceDirectoryTreeModule(f) {
346 @Override
347 protected boolean includeFile(File file) {
348 if (!super.includeFile(file))
349 return false;
350 for (int i = 0; i < exceptions.length; i++) {
351 if (file.compareTo(new File(path + exceptions[i])) == 0)
352 return false;
354 return true;
357 addSourceModule(sdtm);
361 * Add the specified .java file to the analysis scope.
363 * @param path
364 * @throws FileNotFoundException
366 public void addJavaFile(String path) throws IOException {
367 File f = new File(path);
368 if (!f.exists())
369 throw new FileNotFoundException();
371 addSourceModule(new SourceFileModule(f, f.getName()));
375 * Add the specified .jar files to the analysis scope.
377 * @param path
378 * @throws FileNotFoundException
379 * @see addJar()
381 public void addJars(String[] jarfiles) throws IOException {
382 String failedjars = "";
383 for (String jf : jarfiles)
384 try {
385 addJar(jf);
386 } catch (IOException e) {
387 failedjars += " " + jf;
389 if (!failedjars.equals(""))
390 throw new FileNotFoundException("Couldn't find jars: " + failedjars);
394 * Add the specified .jar files to the analysis scope.
396 * @param path
397 * @throws FileNotFoundException
398 * @see addJar()
400 public void addJars(Iterable<String> jarfiles) throws IOException {
401 String failedjars = "";
402 for (String jf : jarfiles)
403 try {
404 addJar(jf);
405 } catch (IOException e) {
406 failedjars += " " + jf;
408 if (!failedjars.equals(""))
409 throw new FileNotFoundException("Couldn't find jars: " + failedjars);
412 // ////////////////////////////////////////////////////////////
413 // /// ECLIPSE-PROJECT-BASED ADDITION OF SOURCES/BINARIES /////
414 // ////////////////////////////////////////////////////////////
417 protected IPath makeAbsolute(IPath p) {
418 if (p.toFile().exists()) {
419 return p;
421 return EclipseJdtUtils.getAbsolutePath(p); // workspace path
424 public void addEclipseProjectSource(IJavaProject javaProject) {
425 ArrayList<IFile> files;
426 try {
427 files = EclipseJdtUtils.getProjectCompilationUnits(javaProject);
428 for (IFile file: files)
429 addSourceModule(new EclipseSourceFileModule(file));
430 } catch (JavaModelException e) {
431 e.printStackTrace();
432 Assertions.UNREACHABLE("SvelteAnalysisEngine.addProjectSource");
437 public void addClasspathEntry(IClasspathEntry cpe, boolean usePolyglot) throws IOException {
438 boolean pathAbsolute = cpe.getPath().toFile().exists();
440 if (cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
441 // System.out.println("DEBUG: adding CPE_SOURCE " +
442 // makeAbsolute(cpe.getPath()).toOSString());
444 if ( usePolyglot )
445 addJavaDir(EclipseJdtUtils.getAbsolutePath(cpe)); // done in add EclipseClassPth for jdt
446 } else if (cpe.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
447 // System.out.println("DEBUG: adding CPE_LIBRARY " +
448 // makeAbsolute(cpe.getPath()).toOSString());
450 File file = makeAbsolute(cpe.getPath()).toFile();
451 Module m = null;
452 if (file.isDirectory())
453 m = new BinaryDirectoryTreeModule(file);
454 else {
455 m = new JarFileModule(new JarFile(file));
458 // libraries with absolute paths are assumed to be "system" jars
459 if (pathAbsolute) {
460 // System.out.println("(as system)");
461 addSystemModule(m);
462 } else {
463 // System.out.println("(as compiled)");
464 addCompiledModule(m);
466 } else if (cpe.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
467 // TODO -- add as source if not using shrike, maybe.
468 // addClassDir(makeAbsolute(cpe.getPath()).toOSString()); // could
469 // add as source
471 IProject project = (IProject) ResourcesPlugin.getWorkspace().getRoot().findMember(
472 cpe.getPath());
473 addEclipseClasspaths(JavaCore.create(project), true, usePolyglot, true);
478 public void addEclipseClasspaths(IJavaProject project, boolean useShrike) {
479 addEclipseClasspaths(project, useShrike, false, false);
481 public void addEclipseClasspaths(IJavaProject project, boolean useShrike, boolean usePolyglot) {
482 addEclipseClasspaths(project, useShrike, usePolyglot, false);
485 public void addEclipseClasspaths(IJavaProject project, boolean useShrike, boolean usePolyglot, boolean onlyExported) {
486 if ( !useShrike && !usePolyglot )
487 addEclipseProjectSource(project);
488 try {
489 IClasspathEntry[] classpaths = project.getResolvedClasspath(true);
490 for (IClasspathEntry cpe : classpaths) {
491 try {
492 if (!(cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE && useShrike))
493 if (cpe.isExported() || !onlyExported)
494 addClasspathEntry(cpe, usePolyglot);
495 } catch (IOException e) {
496 e.printStackTrace();
499 if (useShrike)
500 try {
501 // System.out.println("adding binary directory " +
502 // makeAbsolute(project.getOutputLocation()).toOSString());
503 addClassDir(makeAbsolute(project.getOutputLocation()).toOSString());
504 } catch (IOException e) {
505 e.printStackTrace();
507 } catch (JavaModelException e) {
508 e.printStackTrace();
513 @Override
514 protected CallGraphBuilder getCallGraphBuilder(IClassHierarchy cha, AnalysisOptions options,
515 AnalysisCache cache) {
516 return new ZeroOneContainerCFABuilderFactory().make(options, cache, cha, scope, false);