1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300 |
- /* *******************************************************************
- * Copyright (c) 2003 Contributors.
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Mik Kersten initial implementation
- * Andy Clement incremental support and switch on/off state
- * ******************************************************************/
-
- package org.aspectj.asm;
-
- import java.io.BufferedWriter;
- import java.io.EOFException;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Writer;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Hashtable;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import java.util.Set;
-
- import org.aspectj.asm.internal.AspectJElementHierarchy;
- import org.aspectj.asm.internal.HandleProviderDelimiter;
- import org.aspectj.asm.internal.JDTLikeHandleProvider;
- import org.aspectj.asm.internal.RelationshipMap;
- import org.aspectj.bridge.ISourceLocation;
- import org.aspectj.util.IStructureModel;
-
- /**
- * The Abstract Structure Model (ASM) represents the containment hierarchy and crosscutting structure map for AspectJ programs. It
- * is used by IDE views such as the document outline, and by other tools such as ajdoc to show both AspectJ declarations and
- * crosscutting links, such as which advice affects which join point shadows.
- *
- * @author Mik Kersten
- * @author Andy Clement
- */
- public class AsmManager implements IStructureModel {
-
- // For testing ONLY
- public static boolean recordingLastActiveStructureModel = true;
- public static AsmManager lastActiveStructureModel;
- public static boolean forceSingletonBehaviour = false;
-
- // SECRETAPI asc pull the secret options together into a system API you lazy fool
- public static boolean attemptIncrementalModelRepairs = false;
- // Dumping the model is expensive
- public static boolean dumpModelPostBuild = false;
- // For offline debugging, you can now ask for the AsmManager to
- // dump the model - see the method setReporting()
- private static boolean dumpModel = false;
- private static boolean dumpRelationships = false;
- private static boolean dumpDeltaProcessing = false;
- private static IModelFilter modelFilter = null;
- private static String dumpFilename = "";
- private static boolean reporting = false;
-
- private static boolean completingTypeBindings = false;
-
- private final List<IHierarchyListener> structureListeners = new ArrayList<>();
-
- // The model is 'manipulated' by the AjBuildManager.setupModel() code which
- // trashes all the
- // fields when setting up a new model for a batch build.
- // Due to the requirements of incremental compilation we need to tie some of
- // the info
- // below to the AjState for a compilation and recover it if switching
- // between projects.
- protected IHierarchy hierarchy;
-
- /*
- * Map from String > String - it maps absolute paths for inpath dirs/jars to workspace relative paths suitable for handle
- * inclusion
- */
- protected Map<File, String> inpathMap;
- private IRelationshipMap mapper;
- private IElementHandleProvider handleProvider;
-
- private final CanonicalFilePathMap canonicalFilePathMap = new CanonicalFilePathMap();
- // Record the Set<File> for which the model has been modified during the
- // last incremental build
- private final Set<File> lastBuildChanges = new HashSet<>();
-
- // Record the Set<File> of aspects that wove the files listed in lastBuildChanges
- final Set<File> aspectsWeavingInLastBuild = new HashSet<>();
-
- // static {
- // setReporting("c:/model.nfo",true,true,true,true);
- // }
-
- private AsmManager() {
- }
-
- public static AsmManager createNewStructureModel(Map<File, String> inpathMap) {
- if (forceSingletonBehaviour && lastActiveStructureModel != null) {
- return lastActiveStructureModel;
- }
- AsmManager asm = new AsmManager();
- asm.inpathMap = inpathMap;
- asm.hierarchy = new AspectJElementHierarchy(asm);
- asm.mapper = new RelationshipMap();
- asm.handleProvider = new JDTLikeHandleProvider(asm);
- // call initialize on the handleProvider when we create a new ASM
- // to give handleProviders the chance to reset any state
- asm.handleProvider.initialize();
- asm.resetDeltaProcessing();
- setLastActiveStructureModel(asm);
- return asm;
- }
-
- public IHierarchy getHierarchy() {
- return hierarchy;
- }
-
- public IRelationshipMap getRelationshipMap() {
- return mapper;
- }
-
- public void fireModelUpdated() {
- notifyListeners();
- if (dumpModelPostBuild && hierarchy.getConfigFile() != null) {
- writeStructureModel(hierarchy.getConfigFile());
- }
- }
-
- /**
- * Constructs map each time it's called.
- */
- public HashMap<Integer, List<IProgramElement>> getInlineAnnotations(String sourceFile, boolean showSubMember,
- boolean showMemberAndType) {
-
- if (!hierarchy.isValid()) {
- return null;
- }
-
- HashMap<Integer, List<IProgramElement>> annotations = new HashMap<>();
- IProgramElement node = hierarchy.findElementForSourceFile(sourceFile);
- if (node == IHierarchy.NO_STRUCTURE) {
- return null;
- } else {
- IProgramElement fileNode = node;
- ArrayList<IProgramElement> peNodes = new ArrayList<>();
- getAllStructureChildren(fileNode, peNodes, showSubMember, showMemberAndType);
- for (IProgramElement peNode : peNodes) {
- List<IProgramElement> entries = new ArrayList<>();
- entries.add(peNode);
- ISourceLocation sourceLoc = peNode.getSourceLocation();
- if (null != sourceLoc) {
- Integer hash = sourceLoc.getLine();
- List<IProgramElement> existingEntry = annotations.get(hash);
- if (existingEntry != null) {
- entries.addAll(existingEntry);
- }
- annotations.put(hash, entries);
- }
- }
- return annotations;
- }
- }
-
- private void getAllStructureChildren(IProgramElement node, List<IProgramElement> result, boolean showSubMember,
- boolean showMemberAndType) {
- List<IProgramElement> children = node.getChildren();
- if (node.getChildren() == null) {
- return;
- }
- for (IProgramElement next : children) {
- List<IRelationship> rels = mapper.get(next);
- if (next != null
- && ((next.getKind() == IProgramElement.Kind.CODE && showSubMember) || (next.getKind() != IProgramElement.Kind.CODE && showMemberAndType))
- && rels != null && rels.size() > 0) {
- result.add(next);
- }
- getAllStructureChildren(next, result, showSubMember, showMemberAndType);
- }
- }
-
- public void addListener(IHierarchyListener listener) {
- structureListeners.add(listener);
- }
-
- public void removeStructureListener(IHierarchyListener listener) {
- structureListeners.remove(listener);
- }
-
- // this shouldn't be needed - but none of the people that add listeners
- // in the test suite ever remove them. AMC added this to be called in
- // setup() so that the test cases would cease leaking listeners and go
- // back to executing at a reasonable speed.
- public void removeAllListeners() {
- structureListeners.clear();
- }
-
- private void notifyListeners() {
- for (IHierarchyListener listener : structureListeners) {
- listener.elementsUpdated(hierarchy);
- }
- }
-
- public IElementHandleProvider getHandleProvider() {
- return handleProvider;
- }
-
- public void setHandleProvider(IElementHandleProvider handleProvider) {
- this.handleProvider = handleProvider;
- }
-
- public void writeStructureModel(String configFilePath) {
- try {
- String filePath = genExternFilePath(configFilePath);
- FileOutputStream fos = new FileOutputStream(filePath);
- ObjectOutputStream s = new ObjectOutputStream(fos);
- s.writeObject(hierarchy); // Store the program element tree
- s.writeObject(mapper); // Store the relationships
- s.flush();
- fos.flush();
- fos.close();
- s.close();
- } catch (IOException e) {
- // System.err.println("AsmManager: Unable to write structure model: "
- // +configFilePath+" because of:");
- // e.printStackTrace();
- }
- }
-
- /**
- * @param configFilePath path to an ".lst" file
- */
- public void readStructureModel(String configFilePath) {
- boolean hierarchyReadOK = false;
- try {
- if (configFilePath == null) {
- hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
- } else {
- String filePath = genExternFilePath(configFilePath);
- FileInputStream in = new FileInputStream(filePath);
- ObjectInputStream s = new ObjectInputStream(in);
- hierarchy = (AspectJElementHierarchy) s.readObject();
- ((AspectJElementHierarchy) hierarchy).setAsmManager(this);
- hierarchyReadOK = true;
- mapper = (RelationshipMap) s.readObject();
- s.close();
- }
- } catch (FileNotFoundException fnfe) {
- // That is OK
- hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
- } catch (EOFException eofe) {
- // Might be an old format sym file that is missing its relationships
- if (!hierarchyReadOK) {
- System.err.println("AsmManager: Unable to read structure model: " + configFilePath + " because of:");
- eofe.printStackTrace();
- hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
- }
- } catch (Exception e) {
- // System.err.println("AsmManager: Unable to read structure model: "+
- // configFilePath+" because of:");
- // e.printStackTrace();
- hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
- } finally {
- notifyListeners();
- }
- }
-
- private String genExternFilePath(String configFilePath) {
- // sometimes don't have ".lst"
- if (configFilePath.lastIndexOf(".lst") != -1) {
- configFilePath = configFilePath.substring(0, configFilePath.lastIndexOf(".lst"));
- }
- return configFilePath + ".ajsym";
- }
-
- public String getCanonicalFilePath(File f) {
- return canonicalFilePathMap.get(f);
- }
-
- public CanonicalFilePathMap getCanonicalFilePathMap() {
- return canonicalFilePathMap;
- }
-
- private static class CanonicalFilePathMap {
- private static final int MAX_SIZE = 4000;
-
- private final Map<String, String> pathMap = new HashMap<>(20);
-
- // // guards to ensure correctness and liveness
- // private boolean cacheInUse = false;
- // private boolean stopRequested = false;
- //
- // private synchronized boolean isCacheInUse() {
- // return cacheInUse;
- // }
- //
- // private synchronized void setCacheInUse(boolean val) {
- // cacheInUse = val;
- // if (val) {
- // notifyAll();
- // }
- // }
- //
- // private synchronized boolean isStopRequested() {
- // return stopRequested;
- // }
- //
- // private synchronized void requestStop() {
- // stopRequested = true;
- // }
- //
- // /**
- // * Begin prepopulating the map by adding an entry from
- // * file.getPath -> file.getCanonicalPath for each file in
- // * the list. Do this on a background thread.
- // * @param files
- // */
- // public void prepopulate(final List files) {
- // stopRequested = false;
- // setCacheInUse(false);
- // if (pathMap.size() > MAX_SIZE) {
- // pathMap.clear();
- // }
- // new Thread() {
- // public void run() {
- // System.out.println("Starting cache population: " +
- // System.currentTimeMillis());
- // Iterator it = files.iterator();
- // while (!isStopRequested() && it.hasNext()) {
- // File f = (File)it.next();
- // if (pathMap.get(f.getPath()) == null) {
- // // may reuse cache across compiles from ides...
- // try {
- // pathMap.put(f.getPath(),f.getCanonicalPath());
- // } catch (IOException ex) {
- // pathMap.put(f.getPath(),f.getPath());
- // }
- // }
- // }
- // System.out.println("Cached " + files.size());
- // setCacheInUse(true);
- // System.out.println("Cache populated: " + System.currentTimeMillis());
- // }
- // }.start();
- // }
- //
- // /**
- // * Stop pre-populating the cache - our customers are ready to use it.
- // * If there are any cache misses from this point on, we'll populate
- // the
- // * cache as we go.
- // * The handover is done this way to ensure that only one thread is
- // ever
- // * accessing the cache, and that we minimize synchronization.
- // */
- // public synchronized void handover() {
- // if (!isCacheInUse()) {
- // requestStop();
- // try {
- // while (!isCacheInUse()) wait();
- // } catch (InterruptedException intEx) { } // just continue
- // }
- // }
-
- public String get(File f) {
- // if (!cacheInUse) { // unsynchronized test - should never be
- // parallel
- // // threads at this point
- // throw new IllegalStateException(
- // "Must take ownership of cache before using by calling " +
- // "handover()");
- // }
- String ret = pathMap.get(f.getPath());
- if (ret == null) {
- try {
- ret = f.getCanonicalPath();
- } catch (IOException ioEx) {
- ret = f.getPath();
- }
- pathMap.put(f.getPath(), ret);
- if (pathMap.size() > MAX_SIZE) {
- pathMap.clear();
- }
- }
- return ret;
- }
- }
-
- // SECRETAPI
- public static void setReporting(String filename, boolean dModel, boolean dRels, boolean dDeltaProcessing, boolean deletefile) {
- reporting = true;
- dumpModel = dModel;
- dumpRelationships = dRels;
- dumpDeltaProcessing = dDeltaProcessing;
- if (deletefile) {
- new File(filename).delete();
- }
- dumpFilename = filename;
- }
-
- public static void setReporting(String filename, boolean dModel, boolean dRels, boolean dDeltaProcessing, boolean deletefile,
- IModelFilter aFilter) {
- setReporting(filename, dModel, dRels, dDeltaProcessing, deletefile);
- modelFilter = aFilter;
- }
-
- public static boolean isReporting() {
- return reporting;
- }
-
- public static void setDontReport() {
- reporting = false;
- dumpDeltaProcessing = false;
- dumpModel = false;
- dumpRelationships = false;
- }
-
- // NB. If the format of this report changes then the model tests
- // (@see org.aspectj.systemtest.model.ModelTestCase) will fail in
- // their comparison. The tests are assuming that both the model
- // and relationship map are reported and as a consequence single
- // testcases test that both the model and relationship map are correct.
- public void reportModelInfo(String reasonForReport) {
- if (!dumpModel && !dumpRelationships) {
- return;
- }
- try {
- FileWriter fw = new FileWriter(dumpFilename, true);
- BufferedWriter bw = new BufferedWriter(fw);
- if (dumpModel) {
- bw.write("=== MODEL STATUS REPORT ========= " + reasonForReport + "\n");
- dumptree(bw, hierarchy.getRoot(), 0);
-
- bw.write("=== END OF MODEL REPORT =========\n");
- }
- if (dumpRelationships) {
- bw.write("=== RELATIONSHIPS REPORT ========= " + reasonForReport + "\n");
- dumprels(bw);
- bw.write("=== END OF RELATIONSHIPS REPORT ==\n");
- }
- Properties p = summarizeModel().getProperties();
- Enumeration<Object> pkeyenum = p.keys();
- bw.write("=== Properties of the model and relationships map =====\n");
- while (pkeyenum.hasMoreElements()) {
- String pkey = (String) pkeyenum.nextElement();
- bw.write(pkey + "=" + p.getProperty(pkey) + "\n");
- }
- bw.flush();
- fw.close();
- } catch (IOException e) {
- System.err.println("InternalError: Unable to report model information:");
- e.printStackTrace();
- }
- }
-
- public static void dumptree(Writer w, IProgramElement node, int indent) throws IOException {
- for (int i = 0; i < indent; i++) {
- w.write(" ");
- }
- String loc = "";
- if (node != null) {
- if (node.getSourceLocation() != null) {
- loc = node.getSourceLocation().toString();
- if (modelFilter != null) {
- loc = modelFilter.processFilelocation(loc);
- }
- }
- }
- w.write(node + " [" + (node == null ? "null" : node.getKind().toString()) + "] " + loc + "\n");
- if (node != null) {
- for (IProgramElement child : node.getChildren()) {
- dumptree(w, child, indent + 2);
- }
- }
- }
-
- public static void dumptree(IProgramElement node, int indent) throws IOException {
- for (int i = 0; i < indent; i++) {
- System.out.print(" ");
- }
- String loc = "";
- if (node != null) {
- if (node.getSourceLocation() != null) {
- loc = node.getSourceLocation().toString();
- }
- }
- System.out.println(node + " [" + (node == null ? "null" : node.getKind().toString()) + "] " + loc);
- if (node != null) {
- for (IProgramElement child : node.getChildren()) {
- dumptree(child, indent + 2);
- }
- }
- }
-
- public void dumprels(Writer w) throws IOException {
- int ctr = 1;
- Set<String> entries = mapper.getEntries();
- for (String hid : entries) {
- List<IRelationship> rels = mapper.get(hid);
- for (IRelationship ir : rels) {
- List<String> targets = ir.getTargets();
- for (String thid : targets) {
- StringBuffer sb = new StringBuffer();
- if (modelFilter == null || modelFilter.wantsHandleIds()) {
- sb.append("Hid:" + (ctr++) + ":");
- }
- sb.append("(targets=" + targets.size() + ") " + hid + " (" + ir.getName() + ") " + thid + "\n");
- w.write(sb.toString());
- }
- }
- }
- }
-
- private void dumprelsStderr(String key) {
- System.err.println("Relationships dump follows: " + key);
- int ctr = 1;
- Set<String> entries = mapper.getEntries();
- for (String hid : entries) {
- for (IRelationship ir : mapper.get(hid)) {
- List<String> targets = ir.getTargets();
- for (String thid : targets) {
- System.err.println("Hid:" + (ctr++) + ":(targets=" + targets.size() + ") " + hid + " (" + ir.getName() + ") "
- + thid);
- }
- }
- }
- System.err.println("End of relationships dump for: " + key);
- }
-
- // ===================== DELTA PROCESSING CODE ============== start
- // ==========//
-
- /**
- * Removes the hierarchy structure for the specified files from the structure model. Returns true if it deleted anything
- */
- public boolean removeStructureModelForFiles(Writer fw, Collection<File> files) throws IOException {
-
- boolean modelModified = false;
-
- Set<String> deletedNodes = new HashSet<>();
- for (File fileForCompilation : files) {
- String correctedPath = getCanonicalFilePath(fileForCompilation);
- IProgramElement progElem = (IProgramElement) hierarchy.findInFileMap(correctedPath);
- if (progElem != null) {
- // Found it, let's remove it
- if (dumpDeltaProcessing) {
- fw.write("Deleting " + progElem + " node for file " + fileForCompilation + "\n");
- }
- removeNode(progElem);
- lastBuildChanges.add(fileForCompilation);
- deletedNodes.add(getCanonicalFilePath(progElem.getSourceLocation().getSourceFile()));
- if (!hierarchy.removeFromFileMap(correctedPath)) {
- throw new RuntimeException("Whilst repairing model, couldn't remove entry for file: " + correctedPath
- + " from the filemap");
- }
- modelModified = true;
- }
- }
- if (modelModified) {
- hierarchy.updateHandleMap(deletedNodes);
- }
- return modelModified;
- }
-
- public void processDelta(Collection<File> files_tobecompiled, Set<File> files_added, Set<File> files_deleted) {
-
- try {
- Writer fw = null;
-
- // Are we recording this ?
- if (dumpDeltaProcessing) {
- FileWriter filew = new FileWriter(dumpFilename, true);
- fw = new BufferedWriter(filew);
- fw.write("=== Processing delta changes for the model ===\n");
- fw.write("Files for compilation:#" + files_tobecompiled.size() + ":" + files_tobecompiled + "\n");
- fw.write("Files added :#" + files_added.size() + ":" + files_added + "\n");
- fw.write("Files deleted :#" + files_deleted.size() + ":" + files_deleted + "\n");
- }
-
- long stime = System.currentTimeMillis();
-
- // Let's remove all the files that are deleted on this compile
- removeStructureModelForFiles(fw, files_deleted);
- long etime1 = System.currentTimeMillis(); // etime1-stime = time to
- // fix up the model
-
- repairRelationships(fw);
- long etime2 = System.currentTimeMillis(); // etime2-stime = time to
- // repair the
- // relationship map
-
- removeStructureModelForFiles(fw, files_tobecompiled);
-
- if (dumpDeltaProcessing) {
- fw.write("===== Delta Processing timing ==========\n");
- fw.write("Hierarchy=" + (etime1 - stime) + "ms Relationshipmap=" + (etime2 - etime1) + "ms\n");
- fw.write("===== Traversal ========================\n");
- // fw.write("Source handles processed="+srchandlecounter+"\n");
- // fw.write("Target handles processed="+tgthandlecounter+"\n");
- fw.write("========================================\n");
- fw.flush();
- fw.close();
-
- }
- reportModelInfo("After delta processing");
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-
- private String getTypeNameFromHandle(String handle, Map<String, String> cache) {
- try {
- String typename = cache.get(handle);
- if (typename != null) {
- return typename;
- }
- // inpath handle - but for which type?
- // let's do it the slow way, we can optimize this with a cache perhaps
- int hasPackage = handle.indexOf(HandleProviderDelimiter.PACKAGEFRAGMENT.getDelimiter());
- int typeLocation = handle.indexOf(HandleProviderDelimiter.TYPE.getDelimiter());
- if (typeLocation == -1) {
- typeLocation = handle.indexOf(HandleProviderDelimiter.ASPECT_TYPE.getDelimiter());
- }
- if (typeLocation == -1) {
- // unexpected - time to give up
- return "";
- }
- StringBuffer qualifiedTypeNameFromHandle = new StringBuffer();
- if (hasPackage != -1) {
- int classfileLoc = handle.indexOf(HandleProviderDelimiter.CLASSFILE.getDelimiter(), hasPackage);
- qualifiedTypeNameFromHandle.append(handle.substring(hasPackage + 1, classfileLoc));
- qualifiedTypeNameFromHandle.append('.');
- }
- qualifiedTypeNameFromHandle.append(handle.substring(typeLocation + 1));
- typename = qualifiedTypeNameFromHandle.toString();
- cache.put(handle, typename);
- return typename;
- } catch (StringIndexOutOfBoundsException sioobe) {
- // debug for 330170
- System.err.println("Handle processing problem, the handle is: " + handle);
- sioobe.printStackTrace(System.err);
- return "";
- }
- }
-
- /**
- * two kinds of relationships
- *
- * A affects B B affectedBy A
- *
- * Both of these relationships are added when 'B' is modified. Concrete examples are 'advises/advisedby' or
- * 'annotates/annotatedby'.
- *
- * What we need to do is when 'B' is going to be woven, remove all relationships that may reoccur when it is woven. So - remove
- * 'affects' relationships where the target is 'B', remove all 'affectedBy' relationships where the source is 'B'.
- *
- */
- public void removeRelationshipsTargettingThisType(String typename) {
- boolean debug = false;
- if (debug) {
- System.err.println(">>removeRelationshipsTargettingThisType " + typename);
- }
- String pkg = null;
- String type = typename;
- int lastSep = typename.lastIndexOf('.');
- if (lastSep != -1) {
- pkg = typename.substring(0, lastSep);
- type = typename.substring(lastSep + 1);
- }
- boolean didsomething = false;
- IProgramElement typeNode = hierarchy.findElementForType(pkg, type);
-
- // Reasons for that being null:
- // 1. the file has fundamental errors and so doesn't exist in the model
- // (-proceedOnError probably forced us to weave)
- if (typeNode == null) {
- return;
- }
-
- Set<String> sourcesToRemove = new HashSet<>();
- Map<String, String> handleToTypenameCache = new HashMap<>();
- // Iterate over the source handles in the relationships map, the aim
- // here is to remove any 'affected by'
- // relationships where the source of the relationship is the specified
- // type (since it will be readded
- // when the type is woven)
- Set<String> sourcehandlesSet = mapper.getEntries();
- List<IRelationship> relationshipsToRemove = new ArrayList<>();
- for (String hid : sourcehandlesSet) {
- if (isPhantomHandle(hid)) {
- // inpath handle - but for which type?
- // TODO promote cache for reuse during one whole model update
- if (!getTypeNameFromHandle(hid, handleToTypenameCache).equals(typename)) {
- continue;
- }
- }
- IProgramElement sourceElement = hierarchy.getElement(hid);
- if (sourceElement == null || sameType(hid, sourceElement, typeNode)) {
- // worth continuing as there may be a relationship to remove
- relationshipsToRemove.clear();
- List<IRelationship> relationships = mapper.get(hid);
- for (IRelationship relationship : relationships) {
- if (relationship.getKind() == IRelationship.Kind.USES_POINTCUT) {
- continue; // these relationships are added at compile
- }
- // time, argh
- if (relationship.isAffects()) {
- continue; // we want 'affected by' relationships - (e.g.
- }
- // advised by)
- relationshipsToRemove.add(relationship); // all the relationships can
- // be removed, regardless of
- // the target(s)
- }
- // Now, were any relationships emptied during that processing
- // and so need removing for this source handle
- if (relationshipsToRemove.size() > 0) {
- didsomething = true;
- if (relationshipsToRemove.size() == relationships.size()) {
- sourcesToRemove.add(hid);
- } else {
- for (IRelationship iRelationship : relationshipsToRemove) {
- relationships.remove(iRelationship);
- }
- }
- }
- }
- }
- // Remove sources that have no valid relationships any more
- for (String hid : sourcesToRemove) {
- // System.err.println(
- // " source handle: all relationships have gone for "+hid);
- mapper.removeAll(hid);
- IProgramElement ipe = hierarchy.getElement(hid);
- if (ipe != null) {
- // If the relationship was hanging off a 'code' node, delete it.
- if (ipe.getKind().equals(IProgramElement.Kind.CODE)) {
- if (debug) {
- System.err.println(" source handle: it was code node, removing that as well... code=" + ipe + " parent="
- + ipe.getParent());
- }
- removeSingleNode(ipe);
- }
- }
- }
-
- if (debug) {
- dumprelsStderr("after processing 'affectedby'");
- }
- if (didsomething) { // did we do anything?
- sourcesToRemove.clear();
- // removing 'affects' relationships
- if (debug) {
- dumprelsStderr("before processing 'affects'");
- }
- // Iterate over the source handles in the relationships map
- sourcehandlesSet = mapper.getEntries();
- for (String hid : sourcehandlesSet) {
- relationshipsToRemove.clear();
- List<IRelationship> relationships = mapper.get(hid);
- for (IRelationship rel : relationships) {
- if (rel.getKind() == IRelationship.Kind.USES_POINTCUT) {
- continue; // these relationships are added at compile
- }
- // time, argh
- if (!rel.isAffects()) {
- continue;
- }
- List<String> targets = rel.getTargets();
- List<String> targetsToRemove = new ArrayList<>();
-
- // find targets that target the type we are interested in,
- // they need removing
- for (String targethid : targets) {
- if (isPhantomHandle(hid) && !getTypeNameFromHandle(hid, handleToTypenameCache).equals(typename)) {
- continue;
- }
- // Does this point to the same type?
- IProgramElement existingTarget = hierarchy.getElement(targethid);
- if (existingTarget == null || sameType(targethid, existingTarget, typeNode)) {
- targetsToRemove.add(targethid);
- }
- }
-
- if (targetsToRemove.size() != 0) {
- if (targetsToRemove.size() == targets.size()) {
- relationshipsToRemove.add(rel);
- } else {
- // Remove all the targets that are no longer valid
- for (String togo : targetsToRemove) {
- targets.remove(togo);
- }
- }
- }
- }
- // Now, were any relationships emptied during that processing
- // and so need removing for this source handle
- if (relationshipsToRemove.size() > 0) {
- // Are we removing *all* of the relationships for this
- // source handle?
- if (relationshipsToRemove.size() == relationships.size()) {
- sourcesToRemove.add(hid);
- } else {
- for (IRelationship iRelationship : relationshipsToRemove) {
- relationships.remove(iRelationship);
- }
- }
- }
- }
- // Remove sources that have no valid relationships any more
- for (String hid : sourcesToRemove) {
- // System.err.println(
- // " source handle: all relationships have gone for "+hid);
- mapper.removeAll(hid);
- IProgramElement ipe = hierarchy.getElement(hid);
- if (ipe != null) {
- // If the relationship was hanging off a 'code' node, delete
- // it.
- if (ipe.getKind().equals(IProgramElement.Kind.CODE)) {
- if (debug) {
- System.err.println(" source handle: it was code node, removing that as well... code=" + ipe
- + " parent=" + ipe.getParent());
- }
- removeSingleNode(ipe);
- }
- }
- }
- if (debug) {
- dumprelsStderr("after processing 'affects'");
- }
- }
-
- if (debug) {
- System.err.println("<<removeRelationshipsTargettingThisFile");
- }
- }
-
- /**
- * Return true if the target element is in the type specified.
- */
- private boolean sameType(String hid, IProgramElement target, IProgramElement type) {
- IProgramElement containingType = target;
- if (target == null) {
- throw new RuntimeException("target can't be null!");
- }
- if (type == null) {
- throw new RuntimeException("type can't be null!");
- }
- if (target.getKind().isSourceFile() || target.getKind().isFile()) { // isFile() covers pr263487
- // @AJ aspect with broken relationship endpoint - we couldn't find
- // the real
- // endpoint (the declare parents or ITD or similar) so defaulted to
- // the
- // first line of the source file...
-
- // FRAGILE
- // Let's assume the worst, and that it is the same type if the
- // source files
- // are the same. This will break for multiple top level types in a
- // file...
- if (target.getSourceLocation() == null) {
- return false; // these four possibilities should really be FIXED
- }
- // so we don't have this situation
- if (type.getSourceLocation() == null) {
- return false;
- }
- if (target.getSourceLocation().getSourceFile() == null) {
- return false;
- }
- if (type.getSourceLocation().getSourceFile() == null) {
- return false;
- }
- return (target.getSourceLocation().getSourceFile().equals(type.getSourceLocation().getSourceFile()));
- }
- try {
- while (!containingType.getKind().isType()) {
- containingType = containingType.getParent();
- }
- } catch (Throwable t) {
- // Example:
- // java.lang.RuntimeException: Exception whilst walking up from target X.class kind=(file)
- // hid=(=importProb/binaries<x(X.class)
- throw new RuntimeException("Exception whilst walking up from target " + target.toLabelString() + " kind=("
- + target.getKind() + ") hid=(" + target.getHandleIdentifier() + ")", t);
- }
- return (type.equals(containingType));
- }
-
- /**
- * @param handle a JDT like handle, following the form described in AsmRelationshipProvider.findOrFakeUpNode
- * @return true if the handle contains ';' - the char indicating that it is a phantom handle
- */
- private boolean isPhantomHandle(String handle) {
- int phantomMarker = handle.indexOf(HandleProviderDelimiter.PHANTOM.getDelimiter());
- return phantomMarker != -1
- && handle.charAt(phantomMarker - 1) == HandleProviderDelimiter.PACKAGEFRAGMENTROOT.getDelimiter();
- }
-
- /**
- * Go through all the relationships in the model, if any endpoints no longer exist (the node it points to has been deleted from
- * the model) then delete the relationship.
- */
- private void repairRelationships(Writer fw) {
- try {
- // IHierarchy model = AsmManager.getDefault().getHierarchy();
- // TODO Speed this code up by making this assumption:
- // the only piece of the handle that is interesting is the file
- // name. We are working at file granularity, if the
- // file does not exist (i.e. its not in the filemap) then any handle
- // inside that file cannot exist.
- if (dumpDeltaProcessing) {
- fw.write("Repairing relationships map:\n");
- }
-
- // Now sort out the relationships map
- // IRelationshipMap irm = AsmManager.getDefault().getRelationshipMap();
- Set<String> sourcesToRemove = new HashSet<>();
- Set<String> nonExistingHandles = new HashSet<>(); // Cache of handles that we
- // *know* are invalid
- // int srchandlecounter = 0;
- // int tgthandlecounter = 0;
-
- // Iterate over the source handles in the relationships map
- Set<String> keyset = mapper.getEntries(); // These are source handles
- for (String hid : keyset) {
- // srchandlecounter++;
-
- // Do we already know this handle points to nowhere?
- if (nonExistingHandles.contains(hid)) {
- sourcesToRemove.add(hid);
- } else if (!isPhantomHandle(hid)) {
- // We better check if it actually exists
- IProgramElement existingElement = hierarchy.getElement(hid);
- if (dumpDeltaProcessing) {
- fw.write("Looking for handle [" + hid + "] in model, found: " + existingElement + "\n");
- }
- // Did we find it?
- if (existingElement == null) {
- // No, so delete this relationship
- sourcesToRemove.add(hid);
- nonExistingHandles.add(hid); // Speed up a bit you swine
- } else {
- // Ok, so the source is valid, what about the targets?
- List<IRelationship> relationships = mapper.get(hid);
- List<IRelationship> relationshipsToRemove = new ArrayList<>();
- // Iterate through the relationships against this source
- // handle
- for (IRelationship rel : relationships) {
- List<String> targets = rel.getTargets();
- List<String> targetsToRemove = new ArrayList<>();
-
- // Iterate through the targets for this relationship
- for (String targethid : targets) {
- // tgthandlecounter++;
- // Do we already know it doesn't exist?
- if (nonExistingHandles.contains(targethid)) {
- if (dumpDeltaProcessing) {
- fw.write("Target handle [" + targethid + "] for srchid[" + hid + "]rel[" + rel.getName()
- + "] does not exist\n");
- }
- targetsToRemove.add(targethid);
- } else if (!isPhantomHandle(targethid)) {
- // We better check
- IProgramElement existingTarget = hierarchy.getElement(targethid);
- if (existingTarget == null) {
- if (dumpDeltaProcessing) {
- fw.write("Target handle [" + targethid + "] for srchid[" + hid + "]rel["
- + rel.getName() + "] does not exist\n");
- }
- targetsToRemove.add(targethid);
- nonExistingHandles.add(targethid);
- }
- }
- }
-
- // Do we have some targets that need removing?
- if (targetsToRemove.size() != 0) {
- // Are we removing *all* of the targets for this
- // relationship (i.e. removing the relationship)
- if (targetsToRemove.size() == targets.size()) {
- if (dumpDeltaProcessing) {
- fw.write("No targets remain for srchid[" + hid + "] rel[" + rel.getName()
- + "]: removing it\n");
- }
- relationshipsToRemove.add(rel);
- } else {
- // Remove all the targets that are no longer
- // valid
- for (String togo : targetsToRemove) {
- targets.remove(togo);
- }
- // Should have already been caught above,
- // but lets double check ...
- if (targets.size() == 0) {
- if (dumpDeltaProcessing) {
- fw.write("No targets remain for srchid[" + hid + "] rel[" + rel.getName()
- + "]: removing it\n");
- }
- relationshipsToRemove.add(rel); // TODO
- // Should
- // only
- // remove
- // this
- // relationship
- // for
- // the
- // srchid
- // ?
- }
- }
- }
- }
- // Now, were any relationships emptied during that
- // processing and so need removing for this source
- // handle
- if (relationshipsToRemove.size() > 0) {
- // Are we removing *all* of the relationships for
- // this source handle?
- if (relationshipsToRemove.size() == relationships.size()) {
- // We know they are all going to go, so just
- // delete the source handle.
- sourcesToRemove.add(hid);
- } else {
- // MEMORY LEAK - we don't remove the
- // relationships !!
- for (IRelationship irel : relationshipsToRemove) {
- verifyAssumption(mapper.remove(hid, irel), "Failed to remove relationship " + irel.getName()
- + " for shid " + hid);
- }
- List<IRelationship> rels = mapper.get(hid);
- if (rels == null || rels.size() == 0) {
- sourcesToRemove.add(hid);
- }
- }
- }
- }
- }
- }
- // Remove sources that have no valid relationships any more
- for (String hid : sourcesToRemove) {
- mapper.removeAll(hid);
- IProgramElement ipe = hierarchy.getElement(hid);
- if (ipe != null) {
- // If the relationship was hanging off a 'code' node, delete
- // it.
- if (ipe.getKind().equals(IProgramElement.Kind.CODE)) {
- // System.err.println("Deleting code node");
- removeSingleNode(ipe);
- }
- }
- }
- } catch (IOException ioe) {
- System.err.println("Failed to repair relationships:");
- ioe.printStackTrace();
- }
- }
-
- /**
- * Removes a specified program element from the structure model. We go to the parent of the program element, ask for all its
- * children and remove the node we want to delete from the list of children.
- */
- private void removeSingleNode(IProgramElement progElem) {
- if (progElem == null) {
- throw new IllegalStateException("AsmManager.removeNode(): programElement unexpectedly null");
- }
- boolean deleteOK = false;
- IProgramElement parent = progElem.getParent();
- List<IProgramElement> kids = parent.getChildren();
- for (int i = 0, max = kids.size(); i < max; i++) {
- if (kids.get(i).equals(progElem)) {
- kids.remove(i);
- deleteOK = true;
- break;
- }
- }
- if (!deleteOK) {
- System.err.println("unexpectedly failed to delete node from model. hid=" + progElem.getHandleIdentifier());
- }
- }
-
- /**
- * Removes a specified program element from the structure model. Two processing stages:
- * <p>
- * First: We go to the parent of the program element, ask for all its children and remove the node we want to delete from the
- * list of children.
- * <p>
- * Second:We check if that parent has any other children. If it has no other children and it is either a CODE node or a PACKAGE
- * node, we delete it too.
- */
- private void removeNode(IProgramElement progElem) {
-
- // StringBuffer flightrecorder = new StringBuffer();
- try {
- // flightrecorder.append("In removeNode, about to chuck away: "+
- // progElem+"\n");
- if (progElem == null) {
- throw new IllegalStateException("AsmManager.removeNode(): programElement unexpectedly null");
- }
- // boolean deleteOK = false;
- IProgramElement parent = progElem.getParent();
- // flightrecorder.append("Parent of it is "+parent+"\n");
- List<IProgramElement> kids = parent.getChildren();
- // flightrecorder.append("Which has "+kids.size()+" kids\n");
- for (int i = 0; i < kids.size(); i++) {
- // flightrecorder.append("Comparing with "+kids.get(i)+"\n");
- if (kids.get(i).equals(progElem)) {
- kids.remove(i);
- // flightrecorder.append("Removing it\n");
- // deleteOK=true;
- break;
- }
- }
- // verifyAssumption(deleteOK,flightrecorder.toString());
- // Are there any kids left for this node?
- if (parent.getChildren().size() == 0
- && parent.getParent() != null
- && (parent.getKind().equals(IProgramElement.Kind.CODE) || parent.getKind().equals(IProgramElement.Kind.PACKAGE))) {
- // This node is on its own, we should trim it too *as long as
- // its not a structural node* which we currently check by
- // making sure its a code node
- // We should trim if it
- // System.err.println("Deleting parent:"+parent);
- removeNode(parent);
- }
- } catch (NullPointerException npe) {
- // Occurred when commenting out other 2 ras classes in wsif??
- // reproducable?
- // System.err.println(flightrecorder.toString());
- npe.printStackTrace();
- }
- }
-
- public static void verifyAssumption(boolean b, String info) {
- if (!b) {
- System.err.println("=========== ASSERTION IS NOT TRUE =========v");
- System.err.println(info);
- Thread.dumpStack();
- System.err.println("=========== ASSERTION IS NOT TRUE =========^");
- throw new RuntimeException("Assertion is false");
- }
- }
-
- public static void verifyAssumption(boolean b) {
- if (!b) {
- Thread.dumpStack();
- throw new RuntimeException("Assertion is false");
- }
- }
-
- // ===================== DELTA PROCESSING CODE ============== end
- // ==========//
-
- /**
- * A ModelInfo object captures basic information about the structure model. It is used for testing and producing debug info.
- */
- public static class ModelInfo {
- private final Hashtable<String, Integer> nodeTypeCount = new Hashtable<>();
- private final Properties extraProperties = new Properties();
-
- private ModelInfo(IHierarchy hierarchy, IRelationshipMap relationshipMap) {
- IProgramElement ipe = hierarchy.getRoot();
- walkModel(ipe);
- recordStat("FileMapSize", new Integer(hierarchy.getFileMapEntrySet().size()).toString());
- recordStat("RelationshipMapSize", new Integer(relationshipMap.getEntries().size()).toString());
- }
-
- private void walkModel(IProgramElement ipe) {
- countNode(ipe);
- for (IProgramElement child : ipe.getChildren()) {
- walkModel(child);
- }
- }
-
- private void countNode(IProgramElement ipe) {
- String node = ipe.getKind().toString();
- Integer ctr = nodeTypeCount.get(node);
- if (ctr == null) {
- nodeTypeCount.put(node, 1);
- } else {
- ctr = ctr + 1;
- nodeTypeCount.put(node, ctr);
- }
- }
-
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("Model node summary:\n");
- Enumeration<String> nodeKeys = nodeTypeCount.keys();
- while (nodeKeys.hasMoreElements()) {
- String key = nodeKeys.nextElement();
- Integer ct = nodeTypeCount.get(key);
- sb.append(key + "=" + ct + "\n");
- }
- sb.append("Model stats:\n");
- Enumeration<Object> ks = extraProperties.keys();
- while (ks.hasMoreElements()) {
- String k = (String) ks.nextElement();
- String v = extraProperties.getProperty(k);
- sb.append(k + "=" + v + "\n");
- }
- return sb.toString();
- }
-
- public Properties getProperties() {
- Properties p = new Properties();
- for (Map.Entry<String, Integer> entry : nodeTypeCount.entrySet()) {
- p.setProperty(entry.getKey(), entry.getValue().toString());
- }
- p.putAll(extraProperties);
- return p;
- }
-
- public void recordStat(String string, String string2) {
- extraProperties.setProperty(string, string2);
- }
-
- }
-
- public ModelInfo summarizeModel() {
- return new ModelInfo(getHierarchy(), getRelationshipMap());
- }
-
- /**
- * Set to indicate whether we are currently building a structure model, should be set up front.
- */
- // public static void setCreatingModel(boolean b) {
- // creatingModel = b;
- // }
- //
- // /**
- // * returns true if we are currently generating a structure model, enables guarding of expensive operations on an empty/null
- // * model.
- // */
- // public static boolean isCreatingModel() {
- // return creatingModel;
- // }
- public static void setCompletingTypeBindings(boolean b) {
- completingTypeBindings = b;
- }
-
- public static boolean isCompletingTypeBindings() {
- return completingTypeBindings;
- }
-
- // public void setRelationshipMap(IRelationshipMap irm) {
- // mapper = irm;
- // }
- //
- // public void setHierarchy(IHierarchy ih) {
- // hierarchy = ih;
- // }
-
- public void resetDeltaProcessing() {
- lastBuildChanges.clear();
- aspectsWeavingInLastBuild.clear();
- }
-
- /**
- * @return the Set of files for which the structure model was modified (they may have been removed or otherwise rebuilt). Set is
- * empty for a full build.
- */
- public Set<File> getModelChangesOnLastBuild() {
- return lastBuildChanges;
- }
-
- /**
- * @return the Set of aspects that wove files on the last build (either incremental or full build)
- */
- public Set<File> getAspectsWeavingFilesOnLastBuild() {
- return aspectsWeavingInLastBuild;
- }
-
- public void addAspectInEffectThisBuild(File f) {
- aspectsWeavingInLastBuild.add(f);
- }
-
- public static void setLastActiveStructureModel(AsmManager structureModel) {
- if (recordingLastActiveStructureModel) {
- lastActiveStructureModel = structureModel;
- }
- }
-
- public String getHandleElementForInpath(String binaryPath) {
- return inpathMap.get(new File(binaryPath));
- }
- }
|