You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AjState.java 80KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * Andy Clement overhauled
  12. * ******************************************************************/
  13. package org.aspectj.ajdt.internal.core.builder;
  14. import java.io.File;
  15. import java.io.FilenameFilter;
  16. import java.io.IOException;
  17. import java.lang.ref.ReferenceQueue;
  18. import java.lang.ref.SoftReference;
  19. import java.util.AbstractMap;
  20. import java.util.ArrayList;
  21. import java.util.Collection;
  22. import java.util.Collections;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.Hashtable;
  26. import java.util.Iterator;
  27. import java.util.LinkedList;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.Set;
  31. import org.aspectj.ajdt.internal.compiler.CompilationResultDestinationManager;
  32. import org.aspectj.ajdt.internal.compiler.InterimCompilationResult;
  33. import org.aspectj.ajdt.internal.core.builder.AjBuildConfig.BinarySourceFile;
  34. import org.aspectj.apache.bcel.classfile.ClassParser;
  35. import org.aspectj.asm.AsmManager;
  36. import org.aspectj.bridge.IMessage;
  37. import org.aspectj.bridge.Message;
  38. import org.aspectj.bridge.SourceLocation;
  39. import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
  40. import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
  41. import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
  42. import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
  43. import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
  44. import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryField;
  45. import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
  46. import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryNestedType;
  47. import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryType;
  48. import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment;
  49. import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
  50. import org.aspectj.org.eclipse.jdt.internal.core.builder.ReferenceCollection;
  51. import org.aspectj.org.eclipse.jdt.internal.core.builder.StringSet;
  52. import org.aspectj.util.FileUtil;
  53. import org.aspectj.weaver.BCException;
  54. import org.aspectj.weaver.CompressingDataOutputStream;
  55. import org.aspectj.weaver.ReferenceType;
  56. import org.aspectj.weaver.ReferenceTypeDelegate;
  57. import org.aspectj.weaver.ResolvedType;
  58. import org.aspectj.weaver.bcel.BcelWeaver;
  59. import org.aspectj.weaver.bcel.BcelWorld;
  60. import org.aspectj.weaver.bcel.TypeDelegateResolver;
  61. import org.aspectj.weaver.bcel.UnwovenClassFile;
  62. /**
  63. * Maintains state needed for incremental compilation
  64. */
  65. public class AjState implements CompilerConfigurationChangeFlags, TypeDelegateResolver {
  66. // SECRETAPI configures whether we use state instead of lastModTime - see pr245566
  67. public static boolean CHECK_STATE_FIRST = true;
  68. // SECRETAPI static so beware of multi-threading bugs...
  69. public static IStateListener stateListener = null;
  70. public static boolean FORCE_INCREMENTAL_DURING_TESTING = false;
  71. static int PATHID_CLASSPATH = 0;
  72. static int PATHID_ASPECTPATH = 1;
  73. static int PATHID_INPATH = 2;
  74. private static int CLASS_FILE_NO_CHANGES = 0;
  75. private static int CLASS_FILE_CHANGED_THAT_NEEDS_INCREMENTAL_BUILD = 1;
  76. private static int CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD = 2;
  77. private static final char[][] EMPTY_CHAR_ARRAY = new char[0][];
  78. // now follows non static, but transient state - no need to write out, doesn't need reinitializing
  79. // State recreated for each build:
  80. /**
  81. * When looking at changes on the classpath, this set accumulates files in our state instance that affected by those changes.
  82. * Then if we can do an incremental build - these must be compiled.
  83. */
  84. private final Set<File> affectedFiles = new HashSet<File>();
  85. // these are references created on a particular compile run - when looping round in
  86. // addAffectedSourceFiles(), if some have been created then we look at which source files
  87. // touch upon those and get them recompiled.
  88. private StringSet qualifiedStrings = new StringSet(3);
  89. private StringSet simpleStrings = new StringSet(3);
  90. private Set<File> addedFiles;
  91. private Set<File> deletedFiles;
  92. private Set<BinarySourceFile> addedBinaryFiles;
  93. private Set<BinarySourceFile> deletedBinaryFiles;
  94. // For a particular build run, this set records the changes to classesFromName
  95. public final Set<String> deltaAddedClasses = new HashSet<String>();
  96. // now follows non static, but transient state - no need to write out, DOES need reinitializing when read AjState instance
  97. // reloaded
  98. private final AjBuildManager buildManager;
  99. private INameEnvironment nameEnvironment;
  100. // now follows normal state that must be written out
  101. private boolean couldBeSubsequentIncrementalBuild = false;
  102. private boolean batchBuildRequiredThisTime = false;
  103. private AjBuildConfig buildConfig;
  104. private long lastSuccessfulFullBuildTime = -1;
  105. private final Hashtable<String, Long> structuralChangesSinceLastFullBuild = new Hashtable<String, Long>();
  106. private long lastSuccessfulBuildTime = -1;
  107. private long currentBuildTime = -1;
  108. private AsmManager structureModel;
  109. /**
  110. * For a given source file, records the ClassFiles (which contain a fully qualified name and a file name) that were created when
  111. * the source file was compiled. Populated in noteResult and used in addDependentsOf(File)
  112. */
  113. private final Map<File, List<ClassFile>> fullyQualifiedTypeNamesResultingFromCompilationUnit = new HashMap<File, List<ClassFile>>();
  114. /**
  115. * Source files defining aspects Populated in noteResult and used in processDeletedFiles
  116. */
  117. private final Set<File> sourceFilesDefiningAspects = new HashSet<File>();
  118. /**
  119. * Populated in noteResult to record the set of types that should be recompiled if the given file is modified or deleted.
  120. * Referred to during addAffectedSourceFiles when calculating incremental compilation set.
  121. */
  122. private final Map<File, ReferenceCollection> references = new HashMap<File, ReferenceCollection>();
  123. /**
  124. * Holds UnwovenClassFiles (byte[]s) originating from the given file source. This could be a jar file, a directory, or an
  125. * individual .class file. This is an *expensive* map. It is cleared immediately following a batch build, and the cheaper
  126. * inputClassFilesBySource map is kept for processing of any subsequent incremental builds.
  127. *
  128. * Populated during AjBuildManager.initBcelWorld().
  129. *
  130. * Passed into AjCompiler adapter as the set of binary input files to reweave if the weaver determines a full weave is required.
  131. *
  132. * Cleared during initBcelWorld prior to repopulation.
  133. *
  134. * Used when a file is deleted during incremental compilation to delete all of the class files in the output directory that
  135. * resulted from the weaving of File.
  136. *
  137. * Used during getBinaryFilesToCompile when compiling incrementally to determine which files should be recompiled if a given
  138. * input file has changed.
  139. *
  140. */
  141. private Map<String, List<UnwovenClassFile>> binarySourceFiles = new HashMap<String, List<UnwovenClassFile>>();
  142. /**
  143. * Initially a duplicate of the information held in binarySourceFiles, with the key difference that the values are ClassFiles
  144. * (type name, File) not UnwovenClassFiles (which also have all the byte code in them). After a batch build, binarySourceFiles
  145. * is cleared, leaving just this much lighter weight map to use in processing subsequent incremental builds.
  146. */
  147. private final Map<String, List<ClassFile>> inputClassFilesBySource = new HashMap<String, List<ClassFile>>();
  148. /**
  149. * A list of the .class files created by this state that contain aspects.
  150. */
  151. private final List<String> aspectClassFiles = new ArrayList<String>();
  152. /**
  153. * Holds structure information on types as they were at the end of the last build. It would be nice to get rid of this too, but
  154. * can't see an easy way to do that right now.
  155. */
  156. private final Map<String, CompactTypeStructureRepresentation> resolvedTypeStructuresFromLastBuild = new HashMap<String, CompactTypeStructureRepresentation>();
  157. /**
  158. * Populated in noteResult to record the set of UnwovenClassFiles (intermediate results) that originated from compilation of the
  159. * class with the given fully-qualified name.
  160. *
  161. * Used in removeAllResultsOfLastBuild to remove .class files from output directory.
  162. *
  163. * Passed into StatefulNameEnvironment during incremental compilation to support findType lookups.
  164. */
  165. private final Map<String, File> classesFromName = new HashMap<String, File>();
  166. /**
  167. * Populated by AjBuildManager to record the aspects with the file name in which they're contained. This is later used when
  168. * writing the outxml file in AjBuildManager. Need to record the file name because want to write an outxml file for each of the
  169. * output directories and in order to ask the OutputLocationManager for the output location for a given aspect we need the file
  170. * in which it is contained.
  171. */
  172. private Map<String, char[]> aspectsFromFileNames;
  173. private Set<File> compiledSourceFiles = new HashSet<File>();
  174. private final Map<String, File> resources = new HashMap<String, File>();
  175. SoftHashMap/* <baseDir,SoftHashMap<theFile,className>> */fileToClassNameMap = new SoftHashMap();
  176. private BcelWeaver weaver;
  177. private BcelWorld world;
  178. // --- below here is unsorted state
  179. // ---
  180. public AjState(AjBuildManager buildManager) {
  181. this.buildManager = buildManager;
  182. }
  183. public void setCouldBeSubsequentIncrementalBuild(boolean yesThereCould) {
  184. this.couldBeSubsequentIncrementalBuild = yesThereCould;
  185. }
  186. void successfulCompile(AjBuildConfig config, boolean wasFullBuild) {
  187. buildConfig = config;
  188. lastSuccessfulBuildTime = currentBuildTime;
  189. if (stateListener != null) {
  190. stateListener.buildSuccessful(wasFullBuild);
  191. }
  192. if (wasFullBuild) {
  193. lastSuccessfulFullBuildTime = currentBuildTime;
  194. }
  195. }
  196. /**
  197. * Returns false if a batch build is needed.
  198. */
  199. public boolean prepareForNextBuild(AjBuildConfig newBuildConfig) {
  200. currentBuildTime = System.currentTimeMillis();
  201. if (!maybeIncremental()) {
  202. if (listenerDefined()) {
  203. getListener().recordDecision(
  204. "Preparing for build: not going to be incremental because either not in AJDT or incremental deactivated");
  205. }
  206. return false;
  207. }
  208. if (this.batchBuildRequiredThisTime) {
  209. this.batchBuildRequiredThisTime = false;
  210. if (listenerDefined()) {
  211. getListener().recordDecision(
  212. "Preparing for build: not going to be incremental this time because batch build explicitly forced");
  213. }
  214. return false;
  215. }
  216. if (lastSuccessfulBuildTime == -1 || buildConfig == null) {
  217. structuralChangesSinceLastFullBuild.clear();
  218. if (listenerDefined()) {
  219. getListener().recordDecision(
  220. "Preparing for build: not going to be incremental because no successful previous full build");
  221. }
  222. return false;
  223. }
  224. // we don't support incremental with an outjar yet
  225. if (newBuildConfig.getOutputJar() != null) {
  226. structuralChangesSinceLastFullBuild.clear();
  227. if (listenerDefined()) {
  228. getListener().recordDecision("Preparing for build: not going to be incremental because outjar being used");
  229. }
  230. return false;
  231. }
  232. affectedFiles.clear();
  233. // we can't do an incremental build if one of our paths
  234. // has changed, or a jar on a path has been modified
  235. if (pathChange(buildConfig, newBuildConfig)) {
  236. // last time we built, .class files and resource files from jars on the
  237. // inpath will have been copied to the output directory.
  238. // these all need to be deleted in preparation for the clean build that is
  239. // coming - otherwise a file that has been deleted from an inpath jar
  240. // since the last build will not be deleted from the output directory.
  241. removeAllResultsOfLastBuild();
  242. if (stateListener != null) {
  243. stateListener.pathChangeDetected();
  244. }
  245. structuralChangesSinceLastFullBuild.clear();
  246. if (listenerDefined()) {
  247. getListener()
  248. .recordDecision(
  249. "Preparing for build: not going to be incremental because path change detected (one of classpath/aspectpath/inpath/injars)");
  250. }
  251. return false;
  252. }
  253. if (simpleStrings.elementSize > 20) {
  254. simpleStrings = new StringSet(3);
  255. } else {
  256. simpleStrings.clear();
  257. }
  258. if (qualifiedStrings.elementSize > 20) {
  259. qualifiedStrings = new StringSet(3);
  260. } else {
  261. qualifiedStrings.clear();
  262. }
  263. if ((newBuildConfig.getChanged() & PROJECTSOURCEFILES_CHANGED) == 0) {
  264. addedFiles = Collections.emptySet();
  265. deletedFiles = Collections.emptySet();
  266. } else {
  267. Set<File> oldFiles = new HashSet<File>(buildConfig.getFiles());
  268. Set<File> newFiles = new HashSet<File>(newBuildConfig.getFiles());
  269. addedFiles = new HashSet<File>(newFiles);
  270. addedFiles.removeAll(oldFiles);
  271. deletedFiles = new HashSet<File>(oldFiles);
  272. deletedFiles.removeAll(newFiles);
  273. }
  274. Set<BinarySourceFile> oldBinaryFiles = new HashSet<BinarySourceFile>(buildConfig.getBinaryFiles());
  275. Set<BinarySourceFile> newBinaryFiles = new HashSet<BinarySourceFile>(newBuildConfig.getBinaryFiles());
  276. addedBinaryFiles = new HashSet<BinarySourceFile>(newBinaryFiles);
  277. addedBinaryFiles.removeAll(oldBinaryFiles);
  278. deletedBinaryFiles = new HashSet<BinarySourceFile>(oldBinaryFiles);
  279. deletedBinaryFiles.removeAll(newBinaryFiles);
  280. boolean couldStillBeIncremental = processDeletedFiles(deletedFiles);
  281. if (!couldStillBeIncremental) {
  282. if (listenerDefined()) {
  283. getListener().recordDecision("Preparing for build: not going to be incremental because an aspect was deleted");
  284. }
  285. return false;
  286. }
  287. if (listenerDefined()) {
  288. getListener().recordDecision("Preparing for build: planning to be an incremental build");
  289. }
  290. return true;
  291. }
  292. /**
  293. * Checks if any of the files in the set passed in contains an aspect declaration. If one is found then we start the process of
  294. * batch building, i.e. we remove all the results of the last build, call any registered listener to tell them whats happened
  295. * and return false.
  296. *
  297. * @return false if we discovered an aspect declaration
  298. */
  299. private boolean processDeletedFiles(Set<File> deletedFiles) {
  300. for (File deletedFile : deletedFiles) {
  301. if (this.sourceFilesDefiningAspects.contains(deletedFile)) {
  302. removeAllResultsOfLastBuild();
  303. if (stateListener != null) {
  304. stateListener.detectedAspectDeleted(deletedFile);
  305. }
  306. return false;
  307. }
  308. List<ClassFile> classes = fullyQualifiedTypeNamesResultingFromCompilationUnit.get(deletedFile);
  309. if (classes != null) {
  310. for (ClassFile cf : classes) {
  311. resolvedTypeStructuresFromLastBuild.remove(cf.fullyQualifiedTypeName);
  312. }
  313. }
  314. }
  315. return true;
  316. }
  317. private Collection<File> getModifiedFiles() {
  318. return getModifiedFiles(lastSuccessfulBuildTime);
  319. }
  320. Collection<File> getModifiedFiles(long lastBuildTime) {
  321. Set<File> ret = new HashSet<File>();
  322. // Check if the build configuration knows what files have changed...
  323. List<File> modifiedFiles = buildConfig.getModifiedFiles();
  324. if (modifiedFiles == null) {
  325. // do not know, so need to go looking
  326. // not our job to account for new and deleted files
  327. for (Iterator<File> i = buildConfig.getFiles().iterator(); i.hasNext();) {
  328. File file = i.next();
  329. if (!file.exists()) {
  330. continue;
  331. }
  332. long modTime = file.lastModified();
  333. // System.out.println("check: " + file + " mod " + modTime + " build " + lastBuildTime);
  334. // need to add 1000 since lastModTime is only accurate to a second on some (all?) platforms
  335. if (modTime + 1000 > lastBuildTime) {
  336. ret.add(file);
  337. }
  338. }
  339. } else {
  340. ret.addAll(modifiedFiles);
  341. }
  342. ret.addAll(affectedFiles);
  343. return ret;
  344. }
  345. private Collection<BinarySourceFile> getModifiedBinaryFiles() {
  346. return getModifiedBinaryFiles(lastSuccessfulBuildTime);
  347. }
  348. Collection<BinarySourceFile> getModifiedBinaryFiles(long lastBuildTime) {
  349. List<BinarySourceFile> ret = new ArrayList<BinarySourceFile>();
  350. // not our job to account for new and deleted files
  351. for (Iterator<BinarySourceFile> i = buildConfig.getBinaryFiles().iterator(); i.hasNext();) {
  352. AjBuildConfig.BinarySourceFile bsfile = i.next();
  353. File file = bsfile.binSrc;
  354. if (!file.exists()) {
  355. continue;
  356. }
  357. long modTime = file.lastModified();
  358. // System.out.println("check: " + file + " mod " + modTime + " build " + lastBuildTime);
  359. // need to add 1000 since lastModTime is only accurate to a second on some (all?) platforms
  360. if (modTime + 1000 >= lastBuildTime) {
  361. ret.add(bsfile);
  362. }
  363. }
  364. return ret;
  365. }
  366. private void recordDecision(String decision) {
  367. getListener().recordDecision(decision);
  368. }
  369. /**
  370. * Analyse .class files in the directory specified, if they have changed since the last successful build then see if we can
  371. * determine which source files in our project depend on the change. If we can then we can still do an incremental build, if we
  372. * can't then we have to do a full build.
  373. *
  374. */
  375. private int classFileChangedInDirSinceLastBuildRequiringFullBuild(File dir, int pathid) {
  376. if (!dir.isDirectory()) {
  377. if (listenerDefined()) {
  378. recordDecision("ClassFileChangeChecking: not a directory so forcing full build: '" + dir.getPath() + "'");
  379. }
  380. return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
  381. }
  382. // Are we managing that output directory?
  383. AjState state = IncrementalStateManager.findStateManagingOutputLocation(dir);
  384. if (listenerDefined()) {
  385. if (state != null) {
  386. recordDecision("ClassFileChangeChecking: found state instance managing output location : " + dir);
  387. } else {
  388. recordDecision("ClassFileChangeChecking: failed to find a state instance managing output location : " + dir);
  389. }
  390. }
  391. // pr268827 - this guard will cause us to exit quickly if the state says there really is
  392. // nothing of interest. This will not catch the case where a user modifies the .class files outside of
  393. // eclipse because the state will not be aware of it. But that seems an unlikely scenario and
  394. // we are paying a heavy price to check it
  395. if (state != null && !state.hasAnyStructuralChangesSince(lastSuccessfulBuildTime)) {
  396. if (listenerDefined()) {
  397. getListener().recordDecision("ClassFileChangeChecking: no reported changes in that state");
  398. }
  399. return CLASS_FILE_NO_CHANGES;
  400. }
  401. if (state == null) {
  402. // This may be because the directory is the output path of a Java project upon which we depend
  403. // we need to call back into AJDT to ask about that projects state.
  404. CompilationResultDestinationManager crdm = buildConfig.getCompilationResultDestinationManager();
  405. if (crdm != null) {
  406. int i = crdm.discoverChangesSince(dir, lastSuccessfulBuildTime);
  407. // 0 = dontknow if it has changed
  408. // 1 = definetly not changed at all
  409. // further numbers can determine more granular changes
  410. if (i == 1) {
  411. if (listenerDefined()) {
  412. getListener().recordDecision(
  413. "ClassFileChangeChecking: queried JDT and '" + dir
  414. + "' is apparently unchanged so not performing timestamp check");
  415. }
  416. return CLASS_FILE_NO_CHANGES;
  417. }
  418. }
  419. }
  420. List<File> classFiles = FileUtil.listClassFiles(dir);
  421. for (Iterator<File> iterator = classFiles.iterator(); iterator.hasNext();) {
  422. File classFile = iterator.next();
  423. if (CHECK_STATE_FIRST && state != null) {
  424. // Next section reworked based on bug 270033:
  425. // if it is an aspect we may or may not be in trouble depending on whether (a) we depend on it (b) it is on the
  426. // classpath or the aspectpath
  427. if (state.isAspect(classFile)) {
  428. boolean hasStructuralChanges = state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime);
  429. if (hasStructuralChanges || isTypeWeReferTo(classFile)) {
  430. if (hasStructuralChanges) {
  431. if (listenerDefined()) {
  432. getListener().recordDecision(
  433. "ClassFileChangeChecking: aspect found that has structurally changed : " + classFile);
  434. }
  435. return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
  436. } else {
  437. // must be 'isTypeWeReferTo()'
  438. if (pathid == PATHID_CLASSPATH) {
  439. if (listenerDefined()) {
  440. getListener().recordDecision(
  441. "ClassFileChangeChecking: aspect found that this project refers to : " + classFile
  442. + " but only referred to via classpath");
  443. }
  444. } else {
  445. if (listenerDefined()) {
  446. getListener().recordDecision(
  447. "ClassFileChangeChecking: aspect found that this project refers to : " + classFile
  448. + " from either inpath/aspectpath, switching to full build");
  449. }
  450. return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
  451. }
  452. }
  453. } else {
  454. // it is an aspect but we don't refer to it:
  455. // - for CLASSPATH I think this is OK, we can continue and try an
  456. // incremental build
  457. // - for ASPECTPATH we don't know what else might be touched in this project
  458. // and must rebuild
  459. if (pathid == PATHID_CLASSPATH) {
  460. if (listenerDefined()) {
  461. getListener()
  462. .recordDecision(
  463. "ClassFileChangeChecking: found aspect on classpath but this project doesn't reference it, continuing to try for incremental build : "
  464. + classFile);
  465. }
  466. } else {
  467. if (listenerDefined()) {
  468. getListener().recordDecision(
  469. "ClassFileChangeChecking: found aspect on aspectpath/inpath - can't determine if this project is affected, must full build: "
  470. + classFile);
  471. }
  472. return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
  473. }
  474. }
  475. }
  476. if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime)) {
  477. if (listenerDefined()) {
  478. getListener().recordDecision("ClassFileChangeChecking: structural change detected in : " + classFile);
  479. }
  480. isTypeWeReferTo(classFile);
  481. }
  482. } else {
  483. long modTime = classFile.lastModified();
  484. if ((modTime + 1000) >= lastSuccessfulBuildTime) {
  485. // so the class on disk has changed since the last successful build for this state object
  486. // BUG? we stop on the first change that leads us to an incremental build, surely we need to continue and look
  487. // at all files incase another change means we need to incremental a bit more stuff?
  488. // To work out if it is a real change we should ask any state
  489. // object managing the output location whether the file has
  490. // structurally changed or not
  491. if (state != null) {
  492. if (state.isAspect(classFile)) {
  493. if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime) || isTypeWeReferTo(classFile)) {
  494. // further improvements possible
  495. if (listenerDefined()) {
  496. getListener().recordDecision(
  497. "ClassFileChangeChecking: aspect found that has structurally changed or that this project depends upon : "
  498. + classFile);
  499. }
  500. return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
  501. } else {
  502. // it is an aspect but we don't refer to it:
  503. // - for CLASSPATH I think this is OK, we can continue and try an
  504. // incremental build
  505. // - for ASPECTPATH we don't know what else might be touched in this project
  506. // and must rebuild
  507. if (pathid == PATHID_CLASSPATH) {
  508. if (listenerDefined()) {
  509. getListener()
  510. .recordDecision(
  511. "ClassFileChangeChecking: found aspect on classpath but this project doesn't reference it, continuing to try for incremental build : "
  512. + classFile);
  513. }
  514. } else {
  515. if (listenerDefined()) {
  516. getListener().recordDecision(
  517. "ClassFileChangeChecking: found aspect on aspectpath/inpath - can't determine if this project is affected, must full build: "
  518. + classFile);
  519. }
  520. return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
  521. }
  522. }
  523. }
  524. if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime)) {
  525. if (listenerDefined()) {
  526. getListener().recordDecision(
  527. "ClassFileChangeChecking: structural change detected in : " + classFile);
  528. }
  529. isTypeWeReferTo(classFile);
  530. } else {
  531. if (listenerDefined()) {
  532. getListener().recordDecision(
  533. "ClassFileChangeChecking: change detected in " + classFile + " but it is not structural");
  534. }
  535. }
  536. } else {
  537. // No state object to ask, so it only matters if we know which type depends on this file
  538. if (isTypeWeReferTo(classFile)) {
  539. return CLASS_FILE_CHANGED_THAT_NEEDS_INCREMENTAL_BUILD;
  540. } else {
  541. return CLASS_FILE_NO_CHANGES;
  542. }
  543. }
  544. }
  545. }
  546. }
  547. return CLASS_FILE_NO_CHANGES;
  548. }
  549. private boolean isAspect(File file) {
  550. return aspectClassFiles.contains(file.getAbsolutePath());
  551. }
  552. @SuppressWarnings("rawtypes")
  553. public static class SoftHashMap extends AbstractMap {
  554. private final Map map;
  555. private final ReferenceQueue rq = new ReferenceQueue();
  556. public SoftHashMap(Map map) {
  557. this.map = map;
  558. }
  559. public SoftHashMap() {
  560. this(new HashMap());
  561. }
  562. public SoftHashMap(Map map, boolean b) {
  563. this(map);
  564. }
  565. class SoftReferenceKnownKey extends SoftReference {
  566. private final Object key;
  567. @SuppressWarnings("unchecked")
  568. SoftReferenceKnownKey(Object k, Object v) {
  569. super(v, rq);
  570. this.key = k;
  571. }
  572. }
  573. private void processQueue() {
  574. SoftReferenceKnownKey sv = null;
  575. while ((sv = (SoftReferenceKnownKey) rq.poll()) != null) {
  576. map.remove(sv.key);
  577. }
  578. }
  579. public Object get(Object key) {
  580. SoftReferenceKnownKey value = (SoftReferenceKnownKey) map.get(key);
  581. if (value == null) {
  582. return null;
  583. }
  584. if (value.get() == null) {
  585. // it got GC'd
  586. map.remove(value.key);
  587. return null;
  588. } else {
  589. return value.get();
  590. }
  591. }
  592. public Object put(Object k, Object v) {
  593. processQueue();
  594. return map.put(k, new SoftReferenceKnownKey(k, v));
  595. }
  596. public Set entrySet() {
  597. return map.entrySet();
  598. }
  599. public void clear() {
  600. processQueue();
  601. map.clear();
  602. }
  603. public int size() {
  604. processQueue();
  605. return map.size();
  606. }
  607. public Object remove(Object k) {
  608. processQueue();
  609. SoftReferenceKnownKey value = (SoftReferenceKnownKey) map.remove(k);
  610. if (value == null) {
  611. return null;
  612. }
  613. if (value.get() != null) {
  614. return value.get();
  615. }
  616. return null;
  617. }
  618. }
  619. /**
  620. * If a class file has changed in a path on our classpath, it may not be for a type that any of our source files care about.
  621. * This method checks if any of our source files have a dependency on the class in question and if not, we don't consider it an
  622. * interesting change.
  623. */
  624. private boolean isTypeWeReferTo(File file) {
  625. String fpath = file.getAbsolutePath();
  626. int finalSeparator = fpath.lastIndexOf(File.separator);
  627. String baseDir = fpath.substring(0, finalSeparator);
  628. String theFile = fpath.substring(finalSeparator + 1);
  629. SoftHashMap classNames = (SoftHashMap) fileToClassNameMap.get(baseDir);
  630. if (classNames == null) {
  631. classNames = new SoftHashMap();
  632. fileToClassNameMap.put(baseDir, classNames);
  633. }
  634. char[] className = (char[]) classNames.get(theFile);
  635. if (className == null) {
  636. // if (listenerDefined())
  637. // getListener().recordDecision("Cache miss, looking up classname for : " + fpath);
  638. ClassFileReader cfr;
  639. try {
  640. cfr = ClassFileReader.read(file);
  641. } catch (ClassFormatException e) {
  642. return true;
  643. } catch (IOException e) {
  644. return true;
  645. }
  646. className = cfr.getName();
  647. classNames.put(theFile, className);
  648. // } else {
  649. // if (listenerDefined())
  650. // getListener().recordDecision("Cache hit, looking up classname for : " + fpath);
  651. }
  652. char[][][] qualifiedNames = null;
  653. char[][] simpleNames = null;
  654. if (CharOperation.indexOf('/', className) != -1) {
  655. qualifiedNames = new char[1][][];
  656. qualifiedNames[0] = CharOperation.splitOn('/', className);
  657. qualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedNames);
  658. } else {
  659. simpleNames = new char[1][];
  660. simpleNames[0] = className;
  661. simpleNames = ReferenceCollection.internSimpleNames(simpleNames, true);
  662. }
  663. int newlyAffectedFiles = 0;
  664. for (Iterator<Map.Entry<File, ReferenceCollection>> i = references.entrySet().iterator(); i.hasNext();) {
  665. Map.Entry<File, ReferenceCollection> entry = i.next();
  666. ReferenceCollection refs = entry.getValue();
  667. if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
  668. if (listenerDefined()) {
  669. getListener().recordDecision(
  670. toString() + ": type " + new String(className) + " is depended upon by '" + entry.getKey() + "'");
  671. }
  672. newlyAffectedFiles++;
  673. // possibly the beginnings of addressing the second point in 270033 comment 3
  674. // List/*ClassFile*/ cfs = (List)this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(entry.getKey());
  675. affectedFiles.add(entry.getKey());
  676. }
  677. }
  678. if (newlyAffectedFiles > 0) {
  679. return true;
  680. }
  681. if (listenerDefined()) {
  682. getListener().recordDecision(toString() + ": type " + new String(className) + " is not depended upon by this state");
  683. }
  684. return false;
  685. }
  686. // /**
  687. // * For a given class file, determine which source file it came from. This will only succeed if the class file is from a source
  688. // * file within this project.
  689. // */
  690. // private File getSourceFileForClassFile(File classfile) {
  691. // Set sourceFiles = fullyQualifiedTypeNamesResultingFromCompilationUnit.keySet();
  692. // for (Iterator sourceFileIterator = sourceFiles.iterator(); sourceFileIterator.hasNext();) {
  693. // File sourceFile = (File) sourceFileIterator.next();
  694. // List/* ClassFile */classesFromSourceFile = (List/* ClassFile */) fullyQualifiedTypeNamesResultingFromCompilationUnit
  695. // .get(sourceFile);
  696. // for (int i = 0; i < classesFromSourceFile.size(); i++) {
  697. // if (((ClassFile) classesFromSourceFile.get(i)).locationOnDisk.equals(classfile))
  698. // return sourceFile;
  699. // }
  700. // }
  701. // return null;
  702. // }
  703. public String toString() {
  704. StringBuffer sb = new StringBuffer();
  705. // null config means failed build i think as it is only set on successful full build?
  706. sb.append("AjState(").append((buildConfig == null ? "NULLCONFIG" : buildConfig.getConfigFile().toString())).append(")");
  707. return sb.toString();
  708. }
  709. /**
  710. * Determine if a file has changed since a given time, using the local information recorded in the structural changes data
  711. * structure.
  712. *
  713. * @param file the file we are wondering about
  714. * @param lastSuccessfulBuildTime the last build time for the state asking the question
  715. */
  716. private boolean hasStructuralChangedSince(File file, long lastSuccessfulBuildTime) {
  717. // long lastModTime = file.lastModified();
  718. Long l = structuralChangesSinceLastFullBuild.get(file.getAbsolutePath());
  719. long strucModTime = -1;
  720. if (l != null) {
  721. strucModTime = l.longValue();
  722. } else {
  723. strucModTime = this.lastSuccessfulFullBuildTime;
  724. }
  725. // we now have:
  726. // 'strucModTime'-> the last time the class was structurally changed
  727. return (strucModTime > lastSuccessfulBuildTime);
  728. }
  729. /**
  730. * Determine if anything has changed since a given time.
  731. */
  732. private boolean hasAnyStructuralChangesSince(long lastSuccessfulBuildTime) {
  733. Set<Map.Entry<String, Long>> entries = structuralChangesSinceLastFullBuild.entrySet();
  734. for (Iterator<Map.Entry<String, Long>> iterator = entries.iterator(); iterator.hasNext();) {
  735. Map.Entry<String, Long> entry = iterator.next();
  736. Long l = entry.getValue();
  737. if (l != null) {
  738. long lvalue = l.longValue();
  739. if (lvalue > lastSuccessfulBuildTime) {
  740. if (listenerDefined()) {
  741. getListener().recordDecision(
  742. "Seems this has changed " + entry.getKey() + "modtime=" + lvalue + " lsbt="
  743. + this.lastSuccessfulFullBuildTime + " incoming check value=" + lastSuccessfulBuildTime);
  744. }
  745. return true;
  746. }
  747. }
  748. }
  749. return (this.lastSuccessfulFullBuildTime > lastSuccessfulBuildTime);
  750. }
  751. /**
  752. * Determine if something has changed on the classpath/inpath/aspectpath and a full build is required rather than an incremental
  753. * one.
  754. *
  755. * @param previousConfig the previous configuration used
  756. * @param newConfig the new configuration being used
  757. * @return true if full build required
  758. */
  759. private boolean pathChange(AjBuildConfig previousConfig, AjBuildConfig newConfig) {
  760. int changes = newConfig.getChanged();
  761. if ((changes & (CLASSPATH_CHANGED | ASPECTPATH_CHANGED | INPATH_CHANGED | OUTPUTDESTINATIONS_CHANGED | INJARS_CHANGED)) != 0) {
  762. List<File> oldOutputLocs = getOutputLocations(previousConfig);
  763. Set<String> alreadyAnalysedPaths = new HashSet<String>();
  764. List<String> oldClasspath = previousConfig.getClasspath();
  765. List<String> newClasspath = newConfig.getClasspath();
  766. if (stateListener != null) {
  767. stateListener.aboutToCompareClasspaths(oldClasspath, newClasspath);
  768. }
  769. if (classpathChangedAndNeedsFullBuild(oldClasspath, newClasspath, true, oldOutputLocs, alreadyAnalysedPaths)) {
  770. return true;
  771. }
  772. List<File> oldAspectpath = previousConfig.getAspectpath();
  773. List<File> newAspectpath = newConfig.getAspectpath();
  774. if (changedAndNeedsFullBuild(oldAspectpath, newAspectpath, true, oldOutputLocs, alreadyAnalysedPaths, PATHID_ASPECTPATH)) {
  775. return true;
  776. }
  777. List<File> oldInPath = previousConfig.getInpath();
  778. List<File> newInPath = newConfig.getInpath();
  779. if (changedAndNeedsFullBuild(oldInPath, newInPath, false, oldOutputLocs, alreadyAnalysedPaths, PATHID_INPATH)) {
  780. return true;
  781. }
  782. List<File> oldInJars = previousConfig.getInJars();
  783. List<File> newInJars = newConfig.getInJars();
  784. if (changedAndNeedsFullBuild(oldInJars, newInJars, false, oldOutputLocs, alreadyAnalysedPaths, PATHID_INPATH)) {
  785. return true;
  786. }
  787. } else if (newConfig.getClasspathElementsWithModifiedContents() != null) {
  788. // Although the classpath entries themselves are the same as before, the contents of one of the
  789. // directories on the classpath has changed - rather than go digging around to find it, let's ask
  790. // the compiler configuration. This will allow for projects with long classpaths where classpaths
  791. // are also capturing project dependencies - when a project we depend on is rebuilt, we can just check
  792. // it as a standalone element on our classpath rather than going through them all
  793. List<String> modifiedCpElements = newConfig.getClasspathElementsWithModifiedContents();
  794. for (Iterator<String> iterator = modifiedCpElements.iterator(); iterator.hasNext();) {
  795. File cpElement = new File(iterator.next());
  796. if (cpElement.exists() && !cpElement.isDirectory()) {
  797. if (cpElement.lastModified() > lastSuccessfulBuildTime) {
  798. return true;
  799. }
  800. } else {
  801. int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(cpElement, PATHID_CLASSPATH);
  802. if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
  803. return true;
  804. }
  805. }
  806. }
  807. }
  808. return false;
  809. }
  810. /**
  811. * Return a list of the output locations - this includes any 'default' output location and then any known by a registered
  812. * CompilationResultDestinationManager.
  813. *
  814. * @param config the build configuration for which the output locations should be determined
  815. * @return a list of file objects
  816. */
  817. private List<File> getOutputLocations(AjBuildConfig config) {
  818. List<File> outputLocs = new ArrayList<File>();
  819. // Is there a default location?
  820. if (config.getOutputDir() != null) {
  821. try {
  822. outputLocs.add(config.getOutputDir().getCanonicalFile());
  823. } catch (IOException e) {
  824. }
  825. }
  826. if (config.getCompilationResultDestinationManager() != null) {
  827. List<File> dirs = config.getCompilationResultDestinationManager().getAllOutputLocations();
  828. for (Iterator<File> iterator = dirs.iterator(); iterator.hasNext();) {
  829. File f = iterator.next();
  830. try {
  831. File cf = f.getCanonicalFile();
  832. if (!outputLocs.contains(cf)) {
  833. outputLocs.add(cf);
  834. }
  835. } catch (IOException e) {
  836. }
  837. }
  838. }
  839. return outputLocs;
  840. }
  841. private File getOutputLocationFor(AjBuildConfig config, File aResourceFile) {
  842. if (config.getCompilationResultDestinationManager() != null) {
  843. File outputLoc = config.getCompilationResultDestinationManager().getOutputLocationForResource(aResourceFile);
  844. if (outputLoc != null) {
  845. return outputLoc;
  846. }
  847. }
  848. // Is there a default location?
  849. if (config.getOutputDir() != null) {
  850. return config.getOutputDir();
  851. }
  852. return null;
  853. }
  854. /**
  855. * Check the old and new paths, if they vary by length or individual elements then that is considered a change. Or if the last
  856. * modified time of a path entry has changed (or last modified time of a classfile in that path entry has changed) then return
  857. * true. The outputlocations are supplied so they can be 'ignored' in the comparison.
  858. *
  859. * @param oldPath
  860. * @param newPath
  861. * @param checkClassFiles whether to examine individual class files within directories
  862. * @param outputLocs the output locations that should be ignored if they occur on the paths being compared
  863. * @return true if a change is detected that requires a full build
  864. */
  865. private boolean changedAndNeedsFullBuild(List oldPath, List newPath, boolean checkClassFiles, List<File> outputLocs,
  866. Set<String> alreadyAnalysedPaths, int pathid) {
  867. if (oldPath.size() != newPath.size()) {
  868. return true;
  869. }
  870. for (int i = 0; i < oldPath.size(); i++) {
  871. if (!oldPath.get(i).equals(newPath.get(i))) {
  872. return true;
  873. }
  874. Object o = oldPath.get(i); // String on classpath, File on other paths
  875. File f = null;
  876. if (o instanceof String) {
  877. f = new File((String) o);
  878. } else {
  879. f = (File) o;
  880. }
  881. if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
  882. return true;
  883. }
  884. if (checkClassFiles && f.exists() && f.isDirectory()) {
  885. // We should use here a list/set of directories we know have or have not changed - some kind of
  886. // List<File> buildConfig.getClasspathEntriesWithChangedContents()
  887. // and then only proceed to look inside directories if it is one of these, ignoring others -
  888. // that should save a massive amount of processing for incremental builds in a multi project scenario
  889. boolean foundMatch = false;
  890. for (Iterator<File> iterator = outputLocs.iterator(); !foundMatch && iterator.hasNext();) {
  891. File dir = iterator.next();
  892. if (f.equals(dir)) {
  893. foundMatch = true;
  894. }
  895. }
  896. if (!foundMatch) {
  897. if (!alreadyAnalysedPaths.contains(f.getAbsolutePath())) { // Do not check paths more than once
  898. alreadyAnalysedPaths.add(f.getAbsolutePath());
  899. int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(f, pathid);
  900. if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
  901. return true;
  902. }
  903. }
  904. }
  905. }
  906. }
  907. return false;
  908. }
  909. /**
  910. * Check the old and new paths, if they vary by length or individual elements then that is considered a change. Or if the last
  911. * modified time of a path entry has changed (or last modified time of a classfile in that path entry has changed) then return
  912. * true. The outputlocations are supplied so they can be 'ignored' in the comparison.
  913. *
  914. * @param oldPath
  915. * @param newPath
  916. * @param checkClassFiles whether to examine individual class files within directories
  917. * @param outputLocs the output locations that should be ignored if they occur on the paths being compared
  918. * @return true if a change is detected that requires a full build
  919. */
  920. private boolean classpathChangedAndNeedsFullBuild(List<String> oldPath, List<String> newPath, boolean checkClassFiles,
  921. List<File> outputLocs, Set<String> alreadyAnalysedPaths) {
  922. if (oldPath.size() != newPath.size()) {
  923. return true;
  924. }
  925. for (int i = 0; i < oldPath.size(); i++) {
  926. if (!oldPath.get(i).equals(newPath.get(i))) {
  927. return true;
  928. }
  929. File f = new File(oldPath.get(i));
  930. if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
  931. return true;
  932. }
  933. if (checkClassFiles && f.exists() && f.isDirectory()) {
  934. // We should use here a list/set of directories we know have or have not changed - some kind of
  935. // List<File> buildConfig.getClasspathEntriesWithChangedContents()
  936. // and then only proceed to look inside directories if it is one of these, ignoring others -
  937. // that should save a massive amount of processing for incremental builds in a multi project scenario
  938. boolean foundMatch = false;
  939. for (Iterator<File> iterator = outputLocs.iterator(); !foundMatch && iterator.hasNext();) {
  940. File dir = iterator.next();
  941. if (f.equals(dir)) {
  942. foundMatch = true;
  943. }
  944. }
  945. if (!foundMatch) {
  946. if (!alreadyAnalysedPaths.contains(f.getAbsolutePath())) { // Do not check paths more than once
  947. alreadyAnalysedPaths.add(f.getAbsolutePath());
  948. int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(f, PATHID_CLASSPATH);
  949. if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
  950. return true;
  951. }
  952. }
  953. }
  954. }
  955. }
  956. return false;
  957. }
  958. public Set<File> getFilesToCompile(boolean firstPass) {
  959. Set<File> thisTime = new HashSet<File>();
  960. if (firstPass) {
  961. compiledSourceFiles = new HashSet<File>();
  962. Collection<File> modifiedFiles = getModifiedFiles();
  963. // System.out.println("modified: " + modifiedFiles);
  964. thisTime.addAll(modifiedFiles);
  965. // ??? eclipse IncrementalImageBuilder appears to do this
  966. // for (Iterator i = modifiedFiles.iterator(); i.hasNext();) {
  967. // File file = (File) i.next();
  968. // addDependentsOf(file);
  969. // }
  970. if (addedFiles != null) {
  971. for (Iterator<File> fIter = addedFiles.iterator(); fIter.hasNext();) {
  972. File o = fIter.next();
  973. // TODO isn't it a set?? why do this
  974. if (!thisTime.contains(o)) {
  975. thisTime.add(o);
  976. }
  977. }
  978. // thisTime.addAll(addedFiles);
  979. }
  980. deleteClassFiles();
  981. // Do not delete resources on incremental build, AJDT will handle
  982. // copying updates to the output folder. AspectJ only does a copy
  983. // of them on full build (see copyResourcesToDestination() call
  984. // in AjBuildManager)
  985. // deleteResources();
  986. addAffectedSourceFiles(thisTime, thisTime);
  987. } else {
  988. addAffectedSourceFiles(thisTime, compiledSourceFiles);
  989. }
  990. compiledSourceFiles = thisTime;
  991. return thisTime;
  992. }
  993. private boolean maybeIncremental() {
  994. return (FORCE_INCREMENTAL_DURING_TESTING || this.couldBeSubsequentIncrementalBuild);
  995. }
  996. public Map<String, List<UnwovenClassFile>> getBinaryFilesToCompile(boolean firstTime) {
  997. if (lastSuccessfulBuildTime == -1 || buildConfig == null || !maybeIncremental()) {
  998. return binarySourceFiles;
  999. }
  1000. // else incremental...
  1001. Map<String, List<UnwovenClassFile>> toWeave = new HashMap<String, List<UnwovenClassFile>>();
  1002. if (firstTime) {
  1003. List<BinarySourceFile> addedOrModified = new ArrayList<BinarySourceFile>();
  1004. addedOrModified.addAll(addedBinaryFiles);
  1005. addedOrModified.addAll(getModifiedBinaryFiles());
  1006. for (Iterator<BinarySourceFile> iter = addedOrModified.iterator(); iter.hasNext();) {
  1007. AjBuildConfig.BinarySourceFile bsf = iter.next();
  1008. UnwovenClassFile ucf = createUnwovenClassFile(bsf);
  1009. if (ucf == null) {
  1010. continue;
  1011. }
  1012. List<UnwovenClassFile> ucfs = new ArrayList<UnwovenClassFile>();
  1013. ucfs.add(ucf);
  1014. recordTypeChanged(ucf.getClassName());
  1015. binarySourceFiles.put(bsf.binSrc.getPath(), ucfs);
  1016. List<ClassFile> cfs = new ArrayList<ClassFile>(1);
  1017. cfs.add(getClassFileFor(ucf));
  1018. this.inputClassFilesBySource.put(bsf.binSrc.getPath(), cfs);
  1019. toWeave.put(bsf.binSrc.getPath(), ucfs);
  1020. }
  1021. deleteBinaryClassFiles();
  1022. } else {
  1023. // return empty set... we've already done our bit.
  1024. }
  1025. return toWeave;
  1026. }
  1027. /**
  1028. * Called when a path change is about to trigger a full build, but we haven't cleaned up from the last incremental build...
  1029. */
  1030. private void removeAllResultsOfLastBuild() {
  1031. // remove all binarySourceFiles, and all classesFromName...
  1032. for (Iterator<List<ClassFile>> iter = this.inputClassFilesBySource.values().iterator(); iter.hasNext();) {
  1033. List<ClassFile> cfs = iter.next();
  1034. for (ClassFile cf : cfs) {
  1035. cf.deleteFromFileSystem(buildConfig);
  1036. }
  1037. }
  1038. for (Iterator<File> iterator = classesFromName.values().iterator(); iterator.hasNext();) {
  1039. File f = iterator.next();
  1040. new ClassFile("", f).deleteFromFileSystem(buildConfig);
  1041. }
  1042. Set<Map.Entry<String, File>> resourceEntries = resources.entrySet();
  1043. for (Iterator<Map.Entry<String, File>> iter = resourceEntries.iterator(); iter.hasNext();) {
  1044. Map.Entry<String, File> resourcePair = iter.next();
  1045. File sourcePath = resourcePair.getValue();
  1046. File outputLoc = getOutputLocationFor(buildConfig, sourcePath);
  1047. if (outputLoc != null) {
  1048. outputLoc = new File(outputLoc, resourcePair.getKey());
  1049. if (!outputLoc.getPath().equals(sourcePath.getPath()) && outputLoc.exists()) {
  1050. outputLoc.delete();
  1051. if (buildConfig.getCompilationResultDestinationManager() != null) {
  1052. buildConfig.getCompilationResultDestinationManager().reportFileRemove(outputLoc.getPath(),
  1053. CompilationResultDestinationManager.FILETYPE_RESOURCE);
  1054. }
  1055. }
  1056. }
  1057. }
  1058. }
  1059. private void deleteClassFiles() {
  1060. if (deletedFiles == null) {
  1061. return;
  1062. }
  1063. for (File deletedFile : deletedFiles) {
  1064. addDependentsOf(deletedFile);
  1065. List<ClassFile> cfs = this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(deletedFile);
  1066. this.fullyQualifiedTypeNamesResultingFromCompilationUnit.remove(deletedFile);
  1067. if (cfs != null) {
  1068. for (ClassFile cf : cfs) {
  1069. deleteClassFile(cf);
  1070. }
  1071. }
  1072. }
  1073. }
  1074. private void deleteBinaryClassFiles() {
  1075. // range of bsf is ucfs, domain is files (.class and jars) in inpath/jars
  1076. for (BinarySourceFile deletedFile : deletedBinaryFiles) {
  1077. List<ClassFile> cfs = this.inputClassFilesBySource.get(deletedFile.binSrc.getPath());
  1078. for (Iterator<ClassFile> iterator = cfs.iterator(); iterator.hasNext();) {
  1079. deleteClassFile(iterator.next());
  1080. }
  1081. this.inputClassFilesBySource.remove(deletedFile.binSrc.getPath());
  1082. }
  1083. }
  1084. // private void deleteResources() {
  1085. // List oldResources = new ArrayList();
  1086. // oldResources.addAll(resources);
  1087. //
  1088. // // note - this deliberately ignores resources in jars as we don't yet handle jar changes
  1089. // // with incremental compilation
  1090. // for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext();) {
  1091. // File inPathElement = (File) i.next();
  1092. // if (inPathElement.isDirectory() && AjBuildManager.COPY_INPATH_DIR_RESOURCES) {
  1093. // deleteResourcesFromDirectory(inPathElement, oldResources);
  1094. // }
  1095. // }
  1096. //
  1097. // if (buildConfig.getSourcePathResources() != null) {
  1098. // for (Iterator i = buildConfig.getSourcePathResources().keySet().iterator(); i.hasNext();) {
  1099. // String resource = (String) i.next();
  1100. // maybeDeleteResource(resource, oldResources);
  1101. // }
  1102. // }
  1103. //
  1104. // // oldResources need to be deleted...
  1105. // for (Iterator iter = oldResources.iterator(); iter.hasNext();) {
  1106. // String victim = (String) iter.next();
  1107. // List outputDirs = getOutputLocations(buildConfig);
  1108. // for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
  1109. // File dir = (File) iterator.next();
  1110. // File f = new File(dir, victim);
  1111. // if (f.exists()) {
  1112. // f.delete();
  1113. // }
  1114. // resources.remove(victim);
  1115. // }
  1116. // }
  1117. // }
  1118. // private void maybeDeleteResource(String resName, List oldResources) {
  1119. // if (resources.contains(resName)) {
  1120. // oldResources.remove(resName);
  1121. // List outputDirs = getOutputLocations(buildConfig);
  1122. // for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
  1123. // File dir = (File) iterator.next();
  1124. // File source = new File(dir, resName);
  1125. // if (source.exists() && (source.lastModified() >= lastSuccessfulBuildTime)) {
  1126. // resources.remove(resName); // will ensure it is re-copied
  1127. // }
  1128. // }
  1129. // }
  1130. // }
  1131. // private void deleteResourcesFromDirectory(File dir, List oldResources) {
  1132. // File[] files = FileUtil.listFiles(dir, new FileFilter() {
  1133. // public boolean accept(File f) {
  1134. // boolean accept = !(f.isDirectory() || f.getName().endsWith(".class"));
  1135. // return accept;
  1136. // }
  1137. // });
  1138. //
  1139. // // For each file, add it either as a real .class file or as a resource
  1140. // for (int i = 0; i < files.length; i++) {
  1141. // // ASSERT: files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath()
  1142. // // or we are in trouble...
  1143. // String filename = null;
  1144. // try {
  1145. // filename = files[i].getCanonicalPath().substring(dir.getCanonicalPath().length() + 1);
  1146. // } catch (IOException e) {
  1147. // // we are in trouble if this happens...
  1148. // IMessage msg = new Message("call to getCanonicalPath() failed for file " + files[i] + " with: " + e.getMessage(),
  1149. // new SourceLocation(files[i], 0), false);
  1150. // buildManager.handler.handleMessage(msg);
  1151. // filename = files[i].getAbsolutePath().substring(dir.getAbsolutePath().length() + 1);
  1152. // }
  1153. //
  1154. // maybeDeleteResource(filename, oldResources);
  1155. // }
  1156. // }
  1157. private void deleteClassFile(ClassFile cf) {
  1158. classesFromName.remove(cf.fullyQualifiedTypeName);
  1159. weaver.deleteClassFile(cf.fullyQualifiedTypeName);
  1160. cf.deleteFromFileSystem(buildConfig);
  1161. }
  1162. private UnwovenClassFile createUnwovenClassFile(AjBuildConfig.BinarySourceFile bsf) {
  1163. UnwovenClassFile ucf = null;
  1164. try {
  1165. File outputDir = buildConfig.getOutputDir();
  1166. if (buildConfig.getCompilationResultDestinationManager() != null) {
  1167. // createUnwovenClassFile is called only for classes that are on the inpath,
  1168. // all inpath classes are put in the defaultOutputLocation, therefore,
  1169. // this is the output dir
  1170. outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
  1171. }
  1172. ucf = weaver.addClassFile(bsf.binSrc, bsf.fromInPathDirectory, outputDir);
  1173. } catch (IOException ex) {
  1174. IMessage msg = new Message("can't read class file " + bsf.binSrc.getPath(), new SourceLocation(bsf.binSrc, 0), false);
  1175. buildManager.handler.handleMessage(msg);
  1176. }
  1177. return ucf;
  1178. }
  1179. public void noteResult(InterimCompilationResult result) {
  1180. if (!maybeIncremental()) {
  1181. return;
  1182. }
  1183. File sourceFile = new File(result.fileName());
  1184. CompilationResult cr = result.result();
  1185. references.put(sourceFile, new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
  1186. UnwovenClassFile[] unwovenClassFiles = result.unwovenClassFiles();
  1187. for (int i = 0; i < unwovenClassFiles.length; i++) {
  1188. File lastTimeRound = classesFromName.get(unwovenClassFiles[i].getClassName());
  1189. recordClassFile(unwovenClassFiles[i], lastTimeRound);
  1190. String name = unwovenClassFiles[i].getClassName();
  1191. if (lastTimeRound == null) {
  1192. deltaAddedClasses.add(name);
  1193. }
  1194. classesFromName.put(name, new File(unwovenClassFiles[i].getFilename()));
  1195. }
  1196. // need to do this before types are deleted from the World...
  1197. recordWhetherCompilationUnitDefinedAspect(sourceFile, cr);
  1198. deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(sourceFile, unwovenClassFiles);
  1199. recordFQNsResultingFromCompilationUnit(sourceFile, result);
  1200. }
  1201. public void noteNewResult(CompilationResult cr) {
  1202. // if (!maybeIncremental()) {
  1203. // return;
  1204. // }
  1205. //
  1206. // // File sourceFile = new File(result.fileName());
  1207. // // CompilationResult cr = result.result();
  1208. // if (new String(cr.getFileName()).indexOf("C") != -1) {
  1209. // cr.references.put(new String(cr.getFileName()),
  1210. // new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
  1211. // int stop = 1;
  1212. // }
  1213. // references.put(sourceFile, new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
  1214. //
  1215. // UnwovenClassFile[] unwovenClassFiles = cr.unwovenClassFiles();
  1216. // for (int i = 0; i < unwovenClassFiles.length; i++) {
  1217. // File lastTimeRound = (File) classesFromName.get(unwovenClassFiles[i].getClassName());
  1218. // recordClassFile(unwovenClassFiles[i], lastTimeRound);
  1219. // classesFromName.put(unwovenClassFiles[i].getClassName(), new File(unwovenClassFiles[i].getFilename()));
  1220. // }
  1221. // need to do this before types are deleted from the World...
  1222. // recordWhetherCompilationUnitDefinedAspect(sourceFile, cr);
  1223. // deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(sourceFile, unwovenClassFiles);
  1224. //
  1225. // recordFQNsResultingFromCompilationUnit(sourceFile, result);
  1226. }
  1227. /**
  1228. * @param sourceFile
  1229. * @param unwovenClassFiles
  1230. */
  1231. private void deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(File sourceFile,
  1232. UnwovenClassFile[] unwovenClassFiles) {
  1233. List<ClassFile> classFiles = this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(sourceFile);
  1234. if (classFiles != null) {
  1235. for (int i = 0; i < unwovenClassFiles.length; i++) {
  1236. // deleting also deletes types from the weaver... don't do this if they are
  1237. // still present this time around...
  1238. removeFromClassFilesIfPresent(unwovenClassFiles[i].getClassName(), classFiles);
  1239. }
  1240. for (ClassFile cf : classFiles) {
  1241. recordTypeChanged(cf.fullyQualifiedTypeName);
  1242. resolvedTypeStructuresFromLastBuild.remove(cf.fullyQualifiedTypeName);
  1243. // }
  1244. // for (ClassFile cf : classFiles) {
  1245. deleteClassFile(cf);
  1246. }
  1247. }
  1248. }
  1249. private void removeFromClassFilesIfPresent(String className, List<ClassFile> classFiles) {
  1250. ClassFile victim = null;
  1251. for (ClassFile cf : classFiles) {
  1252. if (cf.fullyQualifiedTypeName.equals(className)) {
  1253. victim = cf;
  1254. break;
  1255. }
  1256. }
  1257. if (victim != null) {
  1258. classFiles.remove(victim);
  1259. }
  1260. }
  1261. /**
  1262. * Record the fully-qualified names of the types that were declared in the given source file.
  1263. *
  1264. * @param sourceFile, the compilation unit
  1265. * @param icr, the CompilationResult from compiling it
  1266. */
  1267. private void recordFQNsResultingFromCompilationUnit(File sourceFile, InterimCompilationResult icr) {
  1268. List<ClassFile> classFiles = new ArrayList<ClassFile>();
  1269. UnwovenClassFile[] types = icr.unwovenClassFiles();
  1270. for (int i = 0; i < types.length; i++) {
  1271. classFiles.add(new ClassFile(types[i].getClassName(), new File(types[i].getFilename())));
  1272. }
  1273. this.fullyQualifiedTypeNamesResultingFromCompilationUnit.put(sourceFile, classFiles);
  1274. }
  1275. /**
  1276. * If this compilation unit defined an aspect, we need to know in case it is modified in a future increment.
  1277. *
  1278. * @param sourceFile
  1279. * @param cr
  1280. */
  1281. private void recordWhetherCompilationUnitDefinedAspect(File sourceFile, CompilationResult cr) {
  1282. this.sourceFilesDefiningAspects.remove(sourceFile);
  1283. if (cr != null) {
  1284. Map compiledTypes = cr.compiledTypes;
  1285. if (compiledTypes != null) {
  1286. for (Iterator<char[]> iterator = compiledTypes.keySet().iterator(); iterator.hasNext();) {
  1287. char[] className = iterator.next();
  1288. String typeName = new String(className).replace('/', '.');
  1289. if (typeName.indexOf(BcelWeaver.SYNTHETIC_CLASS_POSTFIX) == -1) {
  1290. ResolvedType rt = world.resolve(typeName);
  1291. if (rt.isMissing()) {
  1292. // This can happen in a case where another problem has occurred that prevented it being
  1293. // correctly added to the world. Eg. pr148285. Duplicate types
  1294. // throw new IllegalStateException("Type '" + rt.getSignature() + "' not found in world!");
  1295. } else if (rt.isAspect()) {
  1296. this.sourceFilesDefiningAspects.add(sourceFile);
  1297. break;
  1298. }
  1299. }
  1300. }
  1301. }
  1302. }
  1303. }
  1304. // private UnwovenClassFile removeFromPreviousIfPresent(UnwovenClassFile cf, InterimCompilationResult previous) {
  1305. // if (previous == null)
  1306. // return null;
  1307. // UnwovenClassFile[] unwovenClassFiles = previous.unwovenClassFiles();
  1308. // for (int i = 0; i < unwovenClassFiles.length; i++) {
  1309. // UnwovenClassFile candidate = unwovenClassFiles[i];
  1310. // if ((candidate != null) && candidate.getFilename().equals(cf.getFilename())) {
  1311. // unwovenClassFiles[i] = null;
  1312. // return candidate;
  1313. // }
  1314. // }
  1315. // return null;
  1316. // }
  1317. private void recordClassFile(UnwovenClassFile thisTime, File lastTime) {
  1318. if (simpleStrings == null) {
  1319. // batch build
  1320. // record resolved type for structural comparisons in future increments
  1321. // this records a second reference to a structure already held in memory
  1322. // by the world.
  1323. ResolvedType rType = world.resolve(thisTime.getClassName());
  1324. if (!rType.isMissing()) {
  1325. try {
  1326. ClassFileReader reader = new ClassFileReader(thisTime.getBytes(), null);
  1327. boolean isAspect = false;
  1328. if (rType instanceof ReferenceType && ((ReferenceType) rType).getDelegate() != null) {
  1329. isAspect = ((ReferenceType) rType).isAspect();
  1330. }
  1331. this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(), new CompactTypeStructureRepresentation(
  1332. reader, isAspect));
  1333. } catch (ClassFormatException cfe) {
  1334. throw new BCException("Unexpected problem processing class", cfe);
  1335. }
  1336. }
  1337. return;
  1338. }
  1339. CompactTypeStructureRepresentation existingStructure = this.resolvedTypeStructuresFromLastBuild
  1340. .get(thisTime.getClassName());
  1341. ResolvedType newResolvedType = world.resolve(thisTime.getClassName());
  1342. if (!newResolvedType.isMissing()) {
  1343. try {
  1344. ClassFileReader reader = new ClassFileReader(thisTime.getBytes(), null);
  1345. boolean isAspect = false;
  1346. if (newResolvedType instanceof ReferenceType && ((ReferenceType) newResolvedType).getDelegate() != null) {
  1347. isAspect = ((ReferenceType) newResolvedType).isAspect();
  1348. }
  1349. this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(), new CompactTypeStructureRepresentation(
  1350. reader, isAspect));
  1351. } catch (ClassFormatException cfe) {
  1352. throw new BCException("Unexpected problem processing class", cfe);
  1353. }
  1354. }
  1355. if (lastTime == null) {
  1356. recordTypeChanged(thisTime.getClassName());
  1357. return;
  1358. }
  1359. if (newResolvedType.isMissing()) {
  1360. return;
  1361. }
  1362. world.ensureAdvancedConfigurationProcessed();
  1363. byte[] newBytes = thisTime.getBytes();
  1364. try {
  1365. ClassFileReader reader = new ClassFileReader(newBytes, lastTime.getAbsolutePath().toCharArray());
  1366. // ignore local types since they're only visible inside a single method
  1367. if (!(reader.isLocal() || reader.isAnonymous())) {
  1368. if (hasStructuralChanges(reader, existingStructure)) {
  1369. if (world.forDEBUG_structuralChangesCode) {
  1370. System.err.println("Detected a structural change in " + thisTime.getFilename());
  1371. }
  1372. structuralChangesSinceLastFullBuild.put(thisTime.getFilename(), new Long(currentBuildTime));
  1373. recordTypeChanged(new String(reader.getName()).replace('/', '.'));
  1374. }
  1375. }
  1376. } catch (ClassFormatException e) {
  1377. recordTypeChanged(thisTime.getClassName());
  1378. }
  1379. }
  1380. /**
  1381. * Compare the class structure of the new intermediate (unwoven) class with the existingResolvedType of the same class that we
  1382. * have in the world, looking for any structural differences (and ignoring aj members resulting from weaving....)
  1383. *
  1384. * Some notes from Andy... lot of problems here, which I've eventually resolved by building the compactstructure based on a
  1385. * classfilereader, rather than on a ResolvedType. There are accessors for inner types and funky fields that the compiler
  1386. * creates to support the language - for non-static inner types it also mangles ctors to be prefixed with an instance of the
  1387. * surrounding type.
  1388. *
  1389. * @param reader
  1390. * @param existingType
  1391. * @return
  1392. */
  1393. private boolean hasStructuralChanges(ClassFileReader reader, CompactTypeStructureRepresentation existingType) {
  1394. if (existingType == null) {
  1395. return true;
  1396. }
  1397. // modifiers
  1398. if (!modifiersEqual(reader.getModifiers(), existingType.modifiers)) {
  1399. return true;
  1400. }
  1401. // generic signature
  1402. if (!CharOperation.equals(reader.getGenericSignature(), existingType.genericSignature)) {
  1403. return true;
  1404. }
  1405. // superclass name
  1406. if (!CharOperation.equals(reader.getSuperclassName(), existingType.superclassName)) {
  1407. return true;
  1408. }
  1409. // have annotations changed on the type?
  1410. IBinaryAnnotation[] newAnnos = reader.getAnnotations();
  1411. if (newAnnos == null || newAnnos.length == 0) {
  1412. if (existingType.annotations != null && existingType.annotations.length != 0) {
  1413. return true;
  1414. }
  1415. } else {
  1416. IBinaryAnnotation[] existingAnnos = existingType.annotations;
  1417. if (existingAnnos == null || existingAnnos.length != newAnnos.length) {
  1418. return true;
  1419. }
  1420. // Does not allow for an order switch
  1421. // Does not cope with a change in values set on the annotation (hard to create a testcase where this is a problem tho)
  1422. for (int i = 0; i < newAnnos.length; i++) {
  1423. if (!CharOperation.equals(newAnnos[i].getTypeName(), existingAnnos[i].getTypeName())) {
  1424. return true;
  1425. }
  1426. }
  1427. }
  1428. // interfaces
  1429. char[][] existingIfs = existingType.interfaces;
  1430. char[][] newIfsAsChars = reader.getInterfaceNames();
  1431. if (newIfsAsChars == null) {
  1432. newIfsAsChars = EMPTY_CHAR_ARRAY;
  1433. } // damn I'm lazy...
  1434. if (existingIfs == null) {
  1435. existingIfs = EMPTY_CHAR_ARRAY;
  1436. }
  1437. if (existingIfs.length != newIfsAsChars.length) {
  1438. return true;
  1439. }
  1440. new_interface_loop: for (int i = 0; i < newIfsAsChars.length; i++) {
  1441. for (int j = 0; j < existingIfs.length; j++) {
  1442. if (CharOperation.equals(existingIfs[j], newIfsAsChars[i])) {
  1443. continue new_interface_loop;
  1444. }
  1445. }
  1446. return true;
  1447. }
  1448. // fields
  1449. // CompactMemberStructureRepresentation[] existingFields = existingType.fields;
  1450. IBinaryField[] newFields = reader.getFields();
  1451. if (newFields == null) {
  1452. newFields = CompactTypeStructureRepresentation.NoField;
  1453. }
  1454. // all redundant for now ... could be an optimization at some point...
  1455. // remove any ajc$XXX fields from those we compare with
  1456. // the existing fields - bug 129163
  1457. // List nonGenFields = new ArrayList();
  1458. // for (int i = 0; i < newFields.length; i++) {
  1459. // IBinaryField field = newFields[i];
  1460. // //if (!CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,field.getName())) { // this would skip ajc$ fields
  1461. // //if ((field.getModifiers()&0x1000)==0) // 0x1000 => synthetic - this will skip synthetic fields (eg. this$0)
  1462. // nonGenFields.add(field);
  1463. // //}
  1464. // }
  1465. IBinaryField[] existingFs = existingType.binFields;
  1466. if (newFields.length != existingFs.length) {
  1467. return true;
  1468. }
  1469. new_field_loop: for (int i = 0; i < newFields.length; i++) {
  1470. IBinaryField field = newFields[i];
  1471. char[] fieldName = field.getName();
  1472. for (int j = 0; j < existingFs.length; j++) {
  1473. if (CharOperation.equals(existingFs[j].getName(), fieldName)) {
  1474. IBinaryField existing = existingFs[j];
  1475. if (!modifiersEqual(field.getModifiers(), existing.getModifiers())) {
  1476. return true;
  1477. }
  1478. if (!CharOperation.equals(existing.getTypeName(), field.getTypeName())) {
  1479. return true;
  1480. }
  1481. char[] existingGSig = existing.getGenericSignature();
  1482. char[] fieldGSig = field.getGenericSignature();
  1483. if ((existingGSig == null && fieldGSig != null) || (existingGSig != null && fieldGSig == null)) {
  1484. return true;
  1485. }
  1486. if (existingGSig != null) {
  1487. if (!CharOperation.equals(existingGSig, fieldGSig)) {
  1488. return true;
  1489. }
  1490. }
  1491. continue new_field_loop;
  1492. }
  1493. }
  1494. return true;
  1495. }
  1496. // methods
  1497. // CompactMemberStructureRepresentation[] existingMethods = existingType.methods;
  1498. IBinaryMethod[] newMethods = reader.getMethods();
  1499. if (newMethods == null) {
  1500. newMethods = CompactTypeStructureRepresentation.NoMethod;
  1501. }
  1502. // all redundant for now ... could be an optimization at some point...
  1503. // Ctors in a non-static inner type have an 'extra parameter' of the enclosing type.
  1504. // If skippableDescriptorPrefix gets set here then it is set to the descriptor portion
  1505. // for this 'extra parameter'. For an inner class of pkg.Foo the skippable descriptor
  1506. // prefix will be '(Lpkg/Foo;' - so later when comparing <init> methods we know what to
  1507. // compare.
  1508. // IF THIS CODE NEEDS TO GET MORE COMPLICATED, I THINK ITS WORTH RIPPING IT ALL OUT AND
  1509. // CREATING THE STRUCTURAL CHANGES OBJECT BASED ON CLASSREADER OUTPUT RATHER THAN
  1510. // THE RESOLVEDTYPE - THEN THERE WOULD BE NO NEED TO TREAT SOME METHODS IN A PECULIAR
  1511. // WAY.
  1512. // char[] skippableDescriptorPrefix = null;
  1513. // char[] enclosingTypeName = reader.getEnclosingTypeName();
  1514. // boolean isStaticType = Modifier.isStatic(reader.getModifiers());
  1515. // if (!isStaticType && enclosingTypeName!=null) {
  1516. // StringBuffer sb = new StringBuffer();
  1517. // sb.append("(L").append(new String(enclosingTypeName)).append(";");
  1518. // skippableDescriptorPrefix = sb.toString().toCharArray();
  1519. // }
  1520. //
  1521. //
  1522. // // remove the aspectOf, hasAspect, clinit and ajc$XXX methods
  1523. // // from those we compare with the existing methods - bug 129163
  1524. // List nonGenMethods = new ArrayList();
  1525. // for (int i = 0; i < newMethods.length; i++) {
  1526. // IBinaryMethod method = newMethods[i];
  1527. // // if ((method.getModifiers() & 0x1000)!=0) continue; // 0x1000 => synthetic - will cause us to skip access$0 - is this
  1528. // always safe?
  1529. // char[] methodName = method.getSelector();
  1530. // // if (!CharOperation.equals(methodName,NameMangler.METHOD_ASPECTOF) &&
  1531. // // !CharOperation.equals(methodName,NameMangler.METHOD_HASASPECT) &&
  1532. // // !CharOperation.equals(methodName,NameMangler.STATIC_INITIALIZER) &&
  1533. // // !CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,methodName) &&
  1534. // // !CharOperation.prefixEquals(NameMangler.CLINIT,methodName)) {
  1535. // nonGenMethods.add(method);
  1536. // // }
  1537. // }
  1538. IBinaryMethod[] existingMs = existingType.binMethods;
  1539. if (newMethods.length != existingMs.length) {
  1540. return true;
  1541. }
  1542. new_method_loop: for (int i = 0; i < newMethods.length; i++) {
  1543. IBinaryMethod method = newMethods[i];
  1544. char[] methodName = method.getSelector();
  1545. for (int j = 0; j < existingMs.length; j++) {
  1546. if (CharOperation.equals(existingMs[j].getSelector(), methodName)) {
  1547. // candidate match
  1548. if (!CharOperation.equals(method.getMethodDescriptor(), existingMs[j].getMethodDescriptor())) {
  1549. // ok, the descriptors don't match, but is this a funky ctor on a non-static inner
  1550. // type?
  1551. // boolean mightBeOK =
  1552. // skippableDescriptorPrefix!=null && // set for inner types
  1553. // CharOperation.equals(methodName,NameMangler.INIT) && // ctor
  1554. // CharOperation.prefixEquals(skippableDescriptorPrefix,method.getMethodDescriptor()); // checking for
  1555. // prefix on the descriptor
  1556. // if (mightBeOK) {
  1557. // // OK, so the descriptor starts something like '(Lpkg/Foo;' - we now may need to look at the rest of the
  1558. // // descriptor if it takes >1 parameter.
  1559. // // eg. could be (Lpkg/C;Ljava/lang/String;) where the skippablePrefix is (Lpkg/C;
  1560. // char [] md = method.getMethodDescriptor();
  1561. // char[] remainder = CharOperation.subarray(md, skippableDescriptorPrefix.length, md.length);
  1562. // if (CharOperation.equals(remainder,BRACKET_V)) continue new_method_loop; // no other parameters to worry
  1563. // about
  1564. // char[] comparableSig = CharOperation.subarray(existingMethods[j].signature, 1,
  1565. // existingMethods[j].signature.length);
  1566. // boolean match = CharOperation.equals(comparableSig, remainder);
  1567. // if (match) continue new_method_loop;
  1568. // }
  1569. continue; // might be overloading
  1570. } else {
  1571. // matching sigs
  1572. IBinaryMethod existing = existingMs[j];
  1573. if (!modifiersEqual(method.getModifiers(), existing.getModifiers())) {
  1574. return true;
  1575. }
  1576. if (exceptionClausesDiffer(existing, method)) {
  1577. return true;
  1578. }
  1579. char[] existingGSig = existing.getGenericSignature();
  1580. char[] methodGSig = method.getGenericSignature();
  1581. if ((existingGSig == null && methodGSig != null) || (existingGSig != null && methodGSig == null)) {
  1582. return true;
  1583. }
  1584. if (existingGSig != null) {
  1585. if (!CharOperation.equals(existingGSig, methodGSig)) {
  1586. return true;
  1587. }
  1588. }
  1589. continue new_method_loop;
  1590. }
  1591. }
  1592. }
  1593. return true; // (no match found)
  1594. }
  1595. // check for differences in inner types
  1596. // TODO could make order insensitive
  1597. IBinaryNestedType[] binaryNestedTypes = reader.getMemberTypes();
  1598. IBinaryNestedType[] existingBinaryNestedTypes = existingType.getMemberTypes();
  1599. if ((binaryNestedTypes == null && existingBinaryNestedTypes != null)
  1600. || (binaryNestedTypes != null && existingBinaryNestedTypes == null)) {
  1601. return true;
  1602. }
  1603. if (binaryNestedTypes != null) {
  1604. int bnLength = binaryNestedTypes.length;
  1605. if (existingBinaryNestedTypes.length != bnLength) {
  1606. return true;
  1607. }
  1608. for (int m = 0; m < bnLength; m++) {
  1609. IBinaryNestedType bnt = binaryNestedTypes[m];
  1610. IBinaryNestedType existingBnt = existingBinaryNestedTypes[m];
  1611. if (!CharOperation.equals(bnt.getName(), existingBnt.getName())) {
  1612. return true;
  1613. }
  1614. }
  1615. }
  1616. return false;
  1617. }
  1618. /**
  1619. * For two methods, discover if there has been a change in the exception types specified.
  1620. *
  1621. * @return true if the exception types have changed
  1622. */
  1623. private boolean exceptionClausesDiffer(IBinaryMethod lastMethod, IBinaryMethod newMethod) {
  1624. char[][] previousExceptionTypeNames = lastMethod.getExceptionTypeNames();
  1625. char[][] newExceptionTypeNames = newMethod.getExceptionTypeNames();
  1626. int pLength = previousExceptionTypeNames.length;
  1627. int nLength = newExceptionTypeNames.length;
  1628. if (pLength != nLength) {
  1629. return true;
  1630. }
  1631. if (pLength == 0) {
  1632. return false;
  1633. }
  1634. // TODO could be insensitive to an order change
  1635. for (int i = 0; i < pLength; i++) {
  1636. if (!CharOperation.equals(previousExceptionTypeNames[i], newExceptionTypeNames[i])) {
  1637. return true;
  1638. }
  1639. }
  1640. return false;
  1641. }
  1642. private boolean modifiersEqual(int eclipseModifiers, int resolvedTypeModifiers) {
  1643. resolvedTypeModifiers = resolvedTypeModifiers & ExtraCompilerModifiers.AccJustFlag;
  1644. eclipseModifiers = eclipseModifiers & ExtraCompilerModifiers.AccJustFlag;
  1645. // if ((eclipseModifiers & CompilerModifiers.AccSuper) != 0) {
  1646. // eclipseModifiers -= CompilerModifiers.AccSuper;
  1647. // }
  1648. return (eclipseModifiers == resolvedTypeModifiers);
  1649. }
  1650. // private static StringSet makeStringSet(List strings) {
  1651. // StringSet ret = new StringSet(strings.size());
  1652. // for (Iterator iter = strings.iterator(); iter.hasNext();) {
  1653. // String element = (String) iter.next();
  1654. // ret.add(element);
  1655. // }
  1656. // return ret;
  1657. // }
  1658. private String stringifySet(Set<?> l) {
  1659. StringBuffer sb = new StringBuffer();
  1660. sb.append("{");
  1661. for (Iterator<?> iter = l.iterator(); iter.hasNext();) {
  1662. Object el = iter.next();
  1663. sb.append(el);
  1664. if (iter.hasNext()) {
  1665. sb.append(",");
  1666. }
  1667. }
  1668. sb.append("}");
  1669. return sb.toString();
  1670. }
  1671. protected void addAffectedSourceFiles(Set<File> addTo, Set<File> lastTimeSources) {
  1672. if (qualifiedStrings.elementSize == 0 && simpleStrings.elementSize == 0) {
  1673. return;
  1674. }
  1675. if (listenerDefined()) {
  1676. getListener().recordDecision(
  1677. "Examining whether any other files now need compilation based on just compiling: '"
  1678. + stringifySet(lastTimeSources) + "'");
  1679. }
  1680. // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
  1681. char[][][] qualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedStrings);
  1682. // if a well known qualified name was found then we can skip over these
  1683. if (qualifiedNames.length < qualifiedStrings.elementSize) {
  1684. qualifiedNames = null;
  1685. }
  1686. char[][] simpleNames = ReferenceCollection.internSimpleNames(simpleStrings);
  1687. // if a well known name was found then we can skip over these
  1688. if (simpleNames.length < simpleStrings.elementSize) {
  1689. simpleNames = null;
  1690. }
  1691. // System.err.println("simple: " + simpleStrings);
  1692. // System.err.println("qualif: " + qualifiedStrings);
  1693. for (Iterator<Map.Entry<File, ReferenceCollection>> i = references.entrySet().iterator(); i.hasNext();) {
  1694. Map.Entry<File, ReferenceCollection> entry = i.next();
  1695. ReferenceCollection refs = entry.getValue();
  1696. if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
  1697. File file = entry.getKey();
  1698. if (file.exists()) {
  1699. if (!lastTimeSources.contains(file)) { // ??? O(n**2)
  1700. if (listenerDefined()) {
  1701. getListener().recordDecision("Need to recompile '" + file.getName().toString() + "'");
  1702. }
  1703. addTo.add(file);
  1704. }
  1705. }
  1706. }
  1707. }
  1708. // add in the things we compiled previously - I know that seems crap but otherwise we may pull woven
  1709. // stuff off disk (since we no longer have UnwovenClassFile objects) in order to satisfy references
  1710. // in the new files we are about to compile (see pr133532)
  1711. if (addTo.size() > 0) {
  1712. addTo.addAll(lastTimeSources);
  1713. }
  1714. // // XXX Promote addTo to a Set - then we don't need this rubbish? but does it need to be ordered?
  1715. // if (addTo.size()>0) {
  1716. // for (Iterator iter = lastTimeSources.iterator(); iter.hasNext();) {
  1717. // Object element = (Object) iter.next();
  1718. // if (!addTo.contains(element)) addTo.add(element);
  1719. // }
  1720. // }
  1721. qualifiedStrings.clear();
  1722. simpleStrings.clear();
  1723. }
  1724. /**
  1725. * Record that a particular type has been touched during a compilation run. Information is used to ensure any types depending
  1726. * upon this one are also recompiled.
  1727. *
  1728. * @param typename (possibly qualified) type name
  1729. */
  1730. protected void recordTypeChanged(String typename) {
  1731. int lastDot = typename.lastIndexOf('.');
  1732. String typeName;
  1733. if (lastDot != -1) {
  1734. String packageName = typename.substring(0, lastDot).replace('.', '/');
  1735. qualifiedStrings.add(packageName);
  1736. typeName = typename.substring(lastDot + 1);
  1737. } else {
  1738. qualifiedStrings.add("");
  1739. typeName = typename;
  1740. }
  1741. int memberIndex = typeName.indexOf('$');
  1742. if (memberIndex > 0) {
  1743. typeName = typeName.substring(0, memberIndex);
  1744. }
  1745. simpleStrings.add(typeName);
  1746. }
  1747. /**
  1748. * Record some additional dependencies between types. When any of the types specified in fullyQualifiedTypeNames changes, we
  1749. * need to recompile the file named in the CompilationResult. This method patches that information into the existing data
  1750. * structures.
  1751. */
  1752. public boolean recordDependencies(File file, String[] typeNameDependencies) {
  1753. try {
  1754. File sourceFile = new File(new String(file.getCanonicalPath()));
  1755. ReferenceCollection existingCollection = references.get(sourceFile);
  1756. if (existingCollection != null) {
  1757. existingCollection.addDependencies(typeNameDependencies);
  1758. return true;
  1759. } else {
  1760. ReferenceCollection rc = new ReferenceCollection(null, null);
  1761. rc.addDependencies(typeNameDependencies);
  1762. references.put(sourceFile, rc);
  1763. return true;
  1764. }
  1765. } catch (IOException ioe) {
  1766. ioe.printStackTrace();
  1767. }
  1768. return false;
  1769. }
  1770. protected void addDependentsOf(File sourceFile) {
  1771. List<ClassFile> cfs = this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(sourceFile);
  1772. if (cfs != null) {
  1773. for (ClassFile cf : cfs) {
  1774. recordTypeChanged(cf.fullyQualifiedTypeName);
  1775. }
  1776. }
  1777. }
  1778. public void setStructureModel(AsmManager structureModel) {
  1779. this.structureModel = structureModel;
  1780. }
  1781. public AsmManager getStructureModel() {
  1782. return structureModel;
  1783. }
  1784. public void setWeaver(BcelWeaver bw) {
  1785. weaver = bw;
  1786. }
  1787. public BcelWeaver getWeaver() {
  1788. return weaver;
  1789. }
  1790. public void setWorld(BcelWorld bw) {
  1791. world = bw;
  1792. world.addTypeDelegateResolver(this);
  1793. }
  1794. public BcelWorld getBcelWorld() {
  1795. return world;
  1796. }
  1797. //
  1798. // public void setRelationshipMap(IRelationshipMap irm) {
  1799. // relmap = irm;
  1800. // }
  1801. //
  1802. // public IRelationshipMap getRelationshipMap() {
  1803. // return relmap;
  1804. // }
  1805. public int getNumberOfStructuralChangesSinceLastFullBuild() {
  1806. return structuralChangesSinceLastFullBuild.size();
  1807. }
  1808. /** Returns last time we did a full or incremental build. */
  1809. public long getLastBuildTime() {
  1810. return lastSuccessfulBuildTime;
  1811. }
  1812. /** Returns last time we did a full build */
  1813. public long getLastFullBuildTime() {
  1814. return lastSuccessfulFullBuildTime;
  1815. }
  1816. /**
  1817. * @return Returns the buildConfig.
  1818. */
  1819. public AjBuildConfig getBuildConfig() {
  1820. return this.buildConfig;
  1821. }
  1822. public void clearBinarySourceFiles() {
  1823. this.binarySourceFiles = new HashMap<String, List<UnwovenClassFile>>();
  1824. }
  1825. public void recordBinarySource(String fromPathName, List<UnwovenClassFile> unwovenClassFiles) {
  1826. this.binarySourceFiles.put(fromPathName, unwovenClassFiles);
  1827. if (this.maybeIncremental()) {
  1828. List<ClassFile> simpleClassFiles = new LinkedList<ClassFile>();
  1829. for (UnwovenClassFile ucf : unwovenClassFiles) {
  1830. ClassFile cf = getClassFileFor(ucf);
  1831. simpleClassFiles.add(cf);
  1832. }
  1833. this.inputClassFilesBySource.put(fromPathName, simpleClassFiles);
  1834. }
  1835. }
  1836. /**
  1837. * @param ucf
  1838. * @return
  1839. */
  1840. private ClassFile getClassFileFor(UnwovenClassFile ucf) {
  1841. return new ClassFile(ucf.getClassName(), new File(ucf.getFilename()));
  1842. }
  1843. public Map<String, List<UnwovenClassFile>> getBinarySourceMap() {
  1844. return this.binarySourceFiles;
  1845. }
  1846. public Map<String, File> getClassNameToFileMap() {
  1847. return this.classesFromName;
  1848. }
  1849. public boolean hasResource(String resourceName) {
  1850. return this.resources.keySet().contains(resourceName);
  1851. }
  1852. public void recordResource(String resourceName, File resourceSourceLocation) {
  1853. this.resources.put(resourceName, resourceSourceLocation);
  1854. }
  1855. /**
  1856. * @return Returns the addedFiles.
  1857. */
  1858. public Set<File> getAddedFiles() {
  1859. return this.addedFiles;
  1860. }
  1861. /**
  1862. * @return Returns the deletedFiles.
  1863. */
  1864. public Set<File> getDeletedFiles() {
  1865. return this.deletedFiles;
  1866. }
  1867. public void forceBatchBuildNextTimeAround() {
  1868. this.batchBuildRequiredThisTime = true;
  1869. }
  1870. public boolean requiresFullBatchBuild() {
  1871. return this.batchBuildRequiredThisTime;
  1872. }
  1873. private static class ClassFile {
  1874. public String fullyQualifiedTypeName;
  1875. public File locationOnDisk;
  1876. public ClassFile(String fqn, File location) {
  1877. this.fullyQualifiedTypeName = fqn;
  1878. this.locationOnDisk = location;
  1879. }
  1880. public String toString() {
  1881. StringBuilder s = new StringBuilder();
  1882. s.append("ClassFile(type=").append(fullyQualifiedTypeName).append(",location=").append(locationOnDisk).append(")");
  1883. return s.toString();
  1884. }
  1885. public void deleteFromFileSystem(AjBuildConfig buildConfig) {
  1886. String namePrefix = locationOnDisk.getName();
  1887. namePrefix = namePrefix.substring(0, namePrefix.lastIndexOf('.'));
  1888. final String targetPrefix = namePrefix + BcelWeaver.CLOSURE_CLASS_PREFIX;
  1889. File dir = locationOnDisk.getParentFile();
  1890. if (dir != null) {
  1891. File[] weaverGenerated = dir.listFiles(new FilenameFilter() {
  1892. public boolean accept(File dir, String name) {
  1893. return name.startsWith(targetPrefix);
  1894. }
  1895. });
  1896. if (weaverGenerated != null) {
  1897. for (int i = 0; i < weaverGenerated.length; i++) {
  1898. weaverGenerated[i].delete();
  1899. if (buildConfig != null && buildConfig.getCompilationResultDestinationManager() != null) {
  1900. buildConfig.getCompilationResultDestinationManager().reportFileRemove(weaverGenerated[i].getPath(),
  1901. CompilationResultDestinationManager.FILETYPE_CLASS);
  1902. }
  1903. }
  1904. }
  1905. }
  1906. locationOnDisk.delete();
  1907. if (buildConfig != null && buildConfig.getCompilationResultDestinationManager() != null) {
  1908. buildConfig.getCompilationResultDestinationManager().reportFileRemove(locationOnDisk.getPath(),
  1909. CompilationResultDestinationManager.FILETYPE_CLASS);
  1910. }
  1911. }
  1912. }
  1913. public void wipeAllKnowledge() {
  1914. buildManager.state = null;
  1915. // buildManager.setStructureModel(null);
  1916. }
  1917. public Map<String, char[]> getAspectNamesToFileNameMap() {
  1918. return aspectsFromFileNames;
  1919. }
  1920. public void initializeAspectNamesToFileNameMap() {
  1921. this.aspectsFromFileNames = new HashMap<String, char[]>();
  1922. }
  1923. // Will allow us to record decisions made during incremental processing, hopefully aid in debugging
  1924. public boolean listenerDefined() {
  1925. return stateListener != null;
  1926. }
  1927. public IStateListener getListener() {
  1928. return stateListener;
  1929. }
  1930. public IBinaryType checkPreviousBuild(String name) {
  1931. return resolvedTypeStructuresFromLastBuild.get(name);
  1932. }
  1933. public AjBuildManager getAjBuildManager() {
  1934. return buildManager;
  1935. }
  1936. public INameEnvironment getNameEnvironment() {
  1937. return this.nameEnvironment;
  1938. }
  1939. public void setNameEnvironment(INameEnvironment nameEnvironment) {
  1940. this.nameEnvironment = nameEnvironment;
  1941. }
  1942. /**
  1943. * Record an aspect that came in on the aspect path. When a .class file changes on the aspect path we can then recognize it as
  1944. * an aspect and know to do more than just a tiny incremental build. <br>
  1945. * TODO but this doesn't allow for a new aspect created on the aspectpath?
  1946. *
  1947. * @param aspectFile path to the file, eg. c:/temp/foo/Fred.class
  1948. */
  1949. public void recordAspectClassFile(String aspectFile) {
  1950. aspectClassFiles.add(aspectFile);
  1951. }
  1952. public void write(CompressingDataOutputStream dos) throws IOException {
  1953. // weaver
  1954. weaver.write(dos);
  1955. // world
  1956. // model
  1957. // local state
  1958. }
  1959. /**
  1960. * See if we can create a delegate from a CompactTypeStructure - TODO better comment
  1961. */
  1962. public ReferenceTypeDelegate getDelegate(ReferenceType referenceType) {
  1963. File f = classesFromName.get(referenceType.getName());
  1964. if (f == null) {
  1965. return null; // not heard of it
  1966. }
  1967. try {
  1968. ClassParser parser = new ClassParser(f.toString());
  1969. return world.buildBcelDelegate(referenceType, parser.parse(), true, false);
  1970. } catch (IOException e) {
  1971. System.err.println("Failed to recover " + referenceType);
  1972. e.printStackTrace();
  1973. }
  1974. return null;
  1975. }
  1976. }