123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559 |
- /* *******************************************************************
- * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v 2.0
- * which accompanies this distribution and is available at
- * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
- *
- * Contributors:
- * PARC initial implementation
- * Andy Clement overhauled
- * ******************************************************************/
-
- package org.aspectj.ajdt.internal.core.builder;
-
- import java.io.DataOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.FilenameFilter;
- import java.io.IOException;
- import java.lang.ref.ReferenceQueue;
- import java.lang.ref.SoftReference;
- import java.util.AbstractMap;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Hashtable;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
-
- import org.aspectj.ajdt.internal.compiler.CompilationResultDestinationManager;
- import org.aspectj.ajdt.internal.compiler.InterimCompilationResult;
- import org.aspectj.ajdt.internal.core.builder.AjBuildConfig.BinarySourceFile;
- import org.aspectj.apache.bcel.classfile.ClassParser;
- import org.aspectj.asm.AsmManager;
- import org.aspectj.bridge.IMessage;
- import org.aspectj.bridge.Message;
- import org.aspectj.bridge.SourceLocation;
- import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
- import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
- import org.aspectj.org.eclipse.jdt.internal.compiler.batch.FileSystem;
- import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
- import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
- import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
- import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryField;
- import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
- import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryNestedType;
- import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryType;
- import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment;
- import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
- import org.aspectj.org.eclipse.jdt.internal.core.builder.ReferenceCollection;
- import org.aspectj.org.eclipse.jdt.internal.core.builder.StringSet;
- import org.aspectj.util.FileUtil;
- import org.aspectj.weaver.BCException;
- import org.aspectj.weaver.CompressingDataOutputStream;
- import org.aspectj.weaver.ReferenceType;
- import org.aspectj.weaver.ReferenceTypeDelegate;
- import org.aspectj.weaver.ResolvedType;
- import org.aspectj.weaver.bcel.BcelWeaver;
- import org.aspectj.weaver.bcel.BcelWorld;
- import org.aspectj.weaver.bcel.TypeDelegateResolver;
- import org.aspectj.weaver.bcel.UnwovenClassFile;
-
- /**
- * Maintains state needed for incremental compilation
- */
- public class AjState implements CompilerConfigurationChangeFlags, TypeDelegateResolver {
-
- // SECRETAPI configures whether we use state instead of lastModTime - see pr245566
- public static boolean CHECK_STATE_FIRST = true;
-
- // SECRETAPI static so beware of multi-threading bugs...
- public static IStateListener stateListener = null;
-
- public static boolean FORCE_INCREMENTAL_DURING_TESTING = false;
-
- static int PATHID_CLASSPATH = 0;
- static int PATHID_ASPECTPATH = 1;
- static int PATHID_INPATH = 2;
-
- private static int CLASS_FILE_NO_CHANGES = 0;
- private static int CLASS_FILE_CHANGED_THAT_NEEDS_INCREMENTAL_BUILD = 1;
- private static int CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD = 2;
-
- private static final char[][] EMPTY_CHAR_ARRAY = new char[0][];
-
- // now follows non static, but transient state - no need to write out, doesn't need reinitializing
- // State recreated for each build:
-
- /**
- * When looking at changes on the classpath, this set accumulates files in our state instance that affected by those changes.
- * Then if we can do an incremental build - these must be compiled.
- */
- private final Set<File> affectedFiles = new HashSet<>();
-
- // these are references created on a particular compile run - when looping round in
- // addAffectedSourceFiles(), if some have been created then we look at which source files
- // touch upon those and get them recompiled.
- private StringSet qualifiedStrings = new StringSet(3);
-
- private StringSet simpleStrings = new StringSet(3);
-
- private Set<File> addedFiles;
- private Set<File> deletedFiles;
- private Set<BinarySourceFile> addedBinaryFiles;
- private Set<BinarySourceFile> deletedBinaryFiles;
- // For a particular build run, this set records the changes to classesFromName
- public final Set<String> deltaAddedClasses = new HashSet<>();
-
- // now follows non static, but transient state - no need to write out, DOES need reinitializing when read AjState instance
- // reloaded
-
- private final AjBuildManager buildManager;
- private INameEnvironment nameEnvironment;
- private FileSystem fileSystem;
-
- // now follows normal state that must be written out
-
- private boolean couldBeSubsequentIncrementalBuild = false;
- private boolean batchBuildRequiredThisTime = false;
- private AjBuildConfig buildConfig;
- private long lastSuccessfulFullBuildTime = -1;
- private final Hashtable<String, Long> structuralChangesSinceLastFullBuild = new Hashtable<>();
- private long lastSuccessfulBuildTime = -1;
- private long currentBuildTime = -1;
- private AsmManager structureModel;
-
- /**
- * For a given source file, records the ClassFiles (which contain a fully qualified name and a file name) that were created when
- * the source file was compiled. Populated in noteResult and used in addDependentsOf(File)
- */
- private final Map<File, List<ClassFile>> fullyQualifiedTypeNamesResultingFromCompilationUnit = new HashMap<>();
-
- /**
- * Source files defining aspects Populated in noteResult and used in processDeletedFiles
- */
- private final Set<File> sourceFilesDefiningAspects = new HashSet<>();
-
- /**
- * Populated in noteResult to record the set of types that should be recompiled if the given file is modified or deleted.
- * Referred to during addAffectedSourceFiles when calculating incremental compilation set.
- */
- private final Map<File, ReferenceCollection> references = new HashMap<>();
-
- /**
- * Holds UnwovenClassFiles (byte[]s) originating from the given file source. This could be a jar file, a directory, or an
- * individual .class file. This is an *expensive* map. It is cleared immediately following a batch build, and the cheaper
- * inputClassFilesBySource map is kept for processing of any subsequent incremental builds.
- *
- * Populated during AjBuildManager.initBcelWorld().
- *
- * Passed into AjCompiler adapter as the set of binary input files to reweave if the weaver determines a full weave is required.
- *
- * Cleared during initBcelWorld prior to repopulation.
- *
- * Used when a file is deleted during incremental compilation to delete all of the class files in the output directory that
- * resulted from the weaving of File.
- *
- * Used during getBinaryFilesToCompile when compiling incrementally to determine which files should be recompiled if a given
- * input file has changed.
- *
- */
- private Map<String, List<UnwovenClassFile>> binarySourceFiles = new HashMap<>();
-
- /**
- * Initially a duplicate of the information held in binarySourceFiles, with the key difference that the values are ClassFiles
- * (type name, File) not UnwovenClassFiles (which also have all the byte code in them). After a batch build, binarySourceFiles
- * is cleared, leaving just this much lighter weight map to use in processing subsequent incremental builds.
- */
- private final Map<String, List<ClassFile>> inputClassFilesBySource = new HashMap<>();
-
- /**
- * A list of the .class files created by this state that contain aspects.
- */
- private final List<String> aspectClassFiles = new ArrayList<>();
-
- /**
- * 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
- * can't see an easy way to do that right now.
- */
- private final Map<String, CompactTypeStructureRepresentation> resolvedTypeStructuresFromLastBuild = new HashMap<>();
-
- /**
- * Populated in noteResult to record the set of UnwovenClassFiles (intermediate results) that originated from compilation of the
- * class with the given fully-qualified name.
- *
- * Used in removeAllResultsOfLastBuild to remove .class files from output directory.
- *
- * Passed into StatefulNameEnvironment during incremental compilation to support findType lookups.
- */
- private final Map<String, File> classesFromName = new HashMap<>();
-
- /**
- * Populated by AjBuildManager to record the aspects with the file name in which they're contained. This is later used when
- * writing the outxml file in AjBuildManager. Need to record the file name because want to write an outxml file for each of the
- * output directories and in order to ask the OutputLocationManager for the output location for a given aspect we need the file
- * in which it is contained.
- */
- private Map<String, char[]> aspectsFromFileNames;
-
- private Set<File> compiledSourceFiles = new HashSet<>();
- private final Map<String, File> resources = new HashMap<>();
-
- SoftHashMap/* <baseDir,SoftHashMap<theFile,className>> */fileToClassNameMap = new SoftHashMap();
-
- private BcelWeaver weaver;
- private BcelWorld world;
-
- // --- below here is unsorted state
-
- // ---
-
- public AjState(AjBuildManager buildManager) {
- this.buildManager = buildManager;
- }
-
- public void setCouldBeSubsequentIncrementalBuild(boolean yesThereCould) {
- this.couldBeSubsequentIncrementalBuild = yesThereCould;
- }
-
- void successfulCompile(AjBuildConfig config, boolean wasFullBuild) {
- buildConfig = config;
- lastSuccessfulBuildTime = currentBuildTime;
- if (stateListener != null) {
- stateListener.buildSuccessful(wasFullBuild);
- }
- if (wasFullBuild) {
- lastSuccessfulFullBuildTime = currentBuildTime;
- }
- }
-
- /**
- * Returns false if a batch build is needed.
- */
- public boolean prepareForNextBuild(AjBuildConfig newBuildConfig) {
- currentBuildTime = System.currentTimeMillis();
-
- if (!maybeIncremental()) {
- if (listenerDefined()) {
- getListener().recordDecision(
- "Preparing for build: not going to be incremental because either not in AJDT or incremental deactivated");
- }
- return false;
- }
-
- if (this.batchBuildRequiredThisTime) {
- this.batchBuildRequiredThisTime = false;
- if (listenerDefined()) {
- getListener().recordDecision(
- "Preparing for build: not going to be incremental this time because batch build explicitly forced");
- }
- return false;
- }
-
- if (lastSuccessfulBuildTime == -1 || buildConfig == null) {
- structuralChangesSinceLastFullBuild.clear();
- if (listenerDefined()) {
- getListener().recordDecision(
- "Preparing for build: not going to be incremental because no successful previous full build");
- }
- return false;
- }
-
- // we don't support incremental with an outjar yet
- if (newBuildConfig.getOutputJar() != null) {
- structuralChangesSinceLastFullBuild.clear();
- if (listenerDefined()) {
- getListener().recordDecision("Preparing for build: not going to be incremental because outjar being used");
- }
- return false;
- }
-
- affectedFiles.clear();
-
- // we can't do an incremental build if one of our paths
- // has changed, or a jar on a path has been modified
- if (pathChange(buildConfig, newBuildConfig)) {
- // last time we built, .class files and resource files from jars on the
- // inpath will have been copied to the output directory.
- // these all need to be deleted in preparation for the clean build that is
- // coming - otherwise a file that has been deleted from an inpath jar
- // since the last build will not be deleted from the output directory.
- removeAllResultsOfLastBuild();
- if (stateListener != null) {
- stateListener.pathChangeDetected();
- }
- structuralChangesSinceLastFullBuild.clear();
- if (listenerDefined()) {
- getListener()
- .recordDecision(
- "Preparing for build: not going to be incremental because path change detected (one of classpath/aspectpath/inpath/injars)");
- }
- return false;
- }
-
- if (simpleStrings.elementSize > 20) {
- simpleStrings = new StringSet(3);
- } else {
- simpleStrings.clear();
- }
- if (qualifiedStrings.elementSize > 20) {
- qualifiedStrings = new StringSet(3);
- } else {
- qualifiedStrings.clear();
- }
-
- if ((newBuildConfig.getChanged() & PROJECTSOURCEFILES_CHANGED) == 0) {
- addedFiles = Collections.emptySet();
- deletedFiles = Collections.emptySet();
- } else {
- Set<File> oldFiles = new HashSet<>(buildConfig.getFiles());
- Set<File> newFiles = new HashSet<>(newBuildConfig.getFiles());
-
- addedFiles = new HashSet<>(newFiles);
- addedFiles.removeAll(oldFiles);
- deletedFiles = new HashSet<>(oldFiles);
- deletedFiles.removeAll(newFiles);
- }
-
- Set<BinarySourceFile> oldBinaryFiles = new HashSet<>(buildConfig.getBinaryFiles());
- Set<BinarySourceFile> newBinaryFiles = new HashSet<>(newBuildConfig.getBinaryFiles());
-
- addedBinaryFiles = new HashSet<>(newBinaryFiles);
- addedBinaryFiles.removeAll(oldBinaryFiles);
- deletedBinaryFiles = new HashSet<>(oldBinaryFiles);
- deletedBinaryFiles.removeAll(newBinaryFiles);
-
- boolean couldStillBeIncremental = processDeletedFiles(deletedFiles);
-
- if (!couldStillBeIncremental) {
- if (listenerDefined()) {
- getListener().recordDecision("Preparing for build: not going to be incremental because an aspect was deleted");
- }
- return false;
- }
-
- if (listenerDefined()) {
- getListener().recordDecision("Preparing for build: planning to be an incremental build");
- }
- return true;
- }
-
- /**
- * 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
- * batch building, i.e. we remove all the results of the last build, call any registered listener to tell them whats happened
- * and return false.
- *
- * @return false if we discovered an aspect declaration
- */
- private boolean processDeletedFiles(Set<File> deletedFiles) {
- for (File deletedFile : deletedFiles) {
- if (this.sourceFilesDefiningAspects.contains(deletedFile)) {
- removeAllResultsOfLastBuild();
- if (stateListener != null) {
- stateListener.detectedAspectDeleted(deletedFile);
- }
- return false;
- }
- List<ClassFile> classes = fullyQualifiedTypeNamesResultingFromCompilationUnit.get(deletedFile);
- if (classes != null) {
- for (ClassFile cf : classes) {
- resolvedTypeStructuresFromLastBuild.remove(cf.fullyQualifiedTypeName);
- }
- }
- }
- return true;
- }
-
- private Collection<File> getModifiedFiles() {
- return getModifiedFiles(lastSuccessfulBuildTime);
- }
-
- Collection<File> getModifiedFiles(long lastBuildTime) {
- Set<File> ret = new HashSet<>();
-
- // Check if the build configuration knows what files have changed...
- List<File> modifiedFiles = buildConfig.getModifiedFiles();
-
- if (modifiedFiles == null) {
- // do not know, so need to go looking
- // not our job to account for new and deleted files
- for (File file : buildConfig.getFiles()) {
- if (!file.exists()) {
- continue;
- }
-
- long modTime = file.lastModified();
- // System.out.println("check: " + file + " mod " + modTime + " build " + lastBuildTime);
- // need to add 1000 since lastModTime is only accurate to a second on some (all?) platforms
- if (modTime + 1000 > lastBuildTime) {
- ret.add(file);
- }
- }
- } else {
- ret.addAll(modifiedFiles);
- }
- ret.addAll(affectedFiles);
- return ret;
- }
-
- private Collection<BinarySourceFile> getModifiedBinaryFiles() {
- return getModifiedBinaryFiles(lastSuccessfulBuildTime);
- }
-
- Collection<BinarySourceFile> getModifiedBinaryFiles(long lastBuildTime) {
- List<BinarySourceFile> ret = new ArrayList<>();
- // not our job to account for new and deleted files
- for (BinarySourceFile bsfile : buildConfig.getBinaryFiles()) {
- File file = bsfile.binSrc;
- if (!file.exists()) {
- continue;
- }
-
- long modTime = file.lastModified();
- // System.out.println("check: " + file + " mod " + modTime + " build " + lastBuildTime);
- // need to add 1000 since lastModTime is only accurate to a second on some (all?) platforms
- if (modTime + 1000 >= lastBuildTime) {
- ret.add(bsfile);
- }
- }
- return ret;
- }
-
- private void recordDecision(String decision) {
- getListener().recordDecision(decision);
- }
-
- /**
- * Analyse .class files in the directory specified, if they have changed since the last successful build then see if we can
- * determine which source files in our project depend on the change. If we can then we can still do an incremental build, if we
- * can't then we have to do a full build.
- *
- */
- private int classFileChangedInDirSinceLastBuildRequiringFullBuild(File dir, int pathid) {
-
- if (!dir.isDirectory()) {
- if (listenerDefined()) {
- recordDecision("ClassFileChangeChecking: not a directory so forcing full build: '" + dir.getPath() + "'");
- }
- return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
- }
-
- // Are we managing that output directory?
- AjState state = IncrementalStateManager.findStateManagingOutputLocation(dir);
- if (listenerDefined()) {
- if (state != null) {
- recordDecision("ClassFileChangeChecking: found state instance managing output location : " + dir);
- } else {
- recordDecision("ClassFileChangeChecking: failed to find a state instance managing output location : " + dir + " (could be getting managed by JDT)");
- }
- }
-
- // pr268827 - this guard will cause us to exit quickly if the state says there really is
- // nothing of interest. This will not catch the case where a user modifies the .class files outside of
- // eclipse because the state will not be aware of it. But that seems an unlikely scenario and
- // we are paying a heavy price to check it
- if (state != null && !state.hasAnyStructuralChangesSince(lastSuccessfulBuildTime)) {
- if (listenerDefined()) {
- getListener().recordDecision("ClassFileChangeChecking: no reported changes in that state");
- }
- return CLASS_FILE_NO_CHANGES;
- }
-
- if (state == null) {
- // This may be because the directory is the output path of a Java project upon which we depend
- // we need to call back into AJDT to ask about that projects state.
- CompilationResultDestinationManager crdm = buildConfig.getCompilationResultDestinationManager();
- if (crdm != null) {
- int i = crdm.discoverChangesSince(dir, lastSuccessfulBuildTime);
- // 0 = dontknow if it has changed
- // 1 = definetly not changed at all
- // further numbers can determine more granular changes
- if (i == 1) {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: queried JDT and '" + dir
- + "' is apparently unchanged so not performing timestamp check");
- }
- return CLASS_FILE_NO_CHANGES;
- }
- }
- }
-
- List<File> classFiles = FileUtil.listClassFiles(dir);
-
- for (File classFile : classFiles) {
- if (CHECK_STATE_FIRST && state != null) {
- // Next section reworked based on bug 270033:
- // 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
- // classpath or the aspectpath
- if (state.isAspect(classFile)) {
- boolean hasStructuralChanges = state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime);
- if (hasStructuralChanges || isTypeWeReferTo(classFile)) {
- if (hasStructuralChanges) {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: aspect found that has structurally changed : " + classFile);
- }
- return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
- } else {
- // must be 'isTypeWeReferTo()'
- if (pathid == PATHID_CLASSPATH) {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: aspect found that this project refers to : " + classFile
- + " but only referred to via classpath");
- }
- } else {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: aspect found that this project refers to : " + classFile
- + " from either inpath/aspectpath, switching to full build");
- }
- return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
- }
- }
-
- } else {
- // it is an aspect but we don't refer to it:
- // - for CLASSPATH I think this is OK, we can continue and try an
- // incremental build
- // - for ASPECTPATH we don't know what else might be touched in this project
- // and must rebuild
- if (pathid == PATHID_CLASSPATH) {
- if (listenerDefined()) {
- getListener()
- .recordDecision(
- "ClassFileChangeChecking: found aspect on classpath but this project doesn't reference it, continuing to try for incremental build : "
- + classFile);
- }
- } else {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: found aspect on aspectpath/inpath - can't determine if this project is affected, must full build: "
- + classFile);
- }
- return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
- }
- }
-
- }
- if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime)) {
- if (listenerDefined()) {
- getListener().recordDecision("ClassFileChangeChecking: structural change detected in : " + classFile);
- }
- isTypeWeReferTo(classFile);
- }
- } else {
- long modTime = classFile.lastModified();
- if ((modTime + 1000) >= lastSuccessfulBuildTime) {
- // so the class on disk has changed since the last successful build for this state object
-
- // BUG? we stop on the first change that leads us to an incremental build, surely we need to continue and look
- // at all files incase another change means we need to incremental a bit more stuff?
-
- // To work out if it is a real change we should ask any state
- // object managing the output location whether the file has
- // structurally changed or not
- if (state != null) {
- if (state.isAspect(classFile)) {
- if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime) || isTypeWeReferTo(classFile)) {
- // further improvements possible
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: aspect found that has structurally changed or that this project depends upon : "
- + classFile);
- }
- return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
- } else {
- // it is an aspect but we don't refer to it:
- // - for CLASSPATH I think this is OK, we can continue and try an
- // incremental build
- // - for ASPECTPATH we don't know what else might be touched in this project
- // and must rebuild
- if (pathid == PATHID_CLASSPATH) {
- if (listenerDefined()) {
- getListener()
- .recordDecision(
- "ClassFileChangeChecking: found aspect on classpath but this project doesn't reference it, continuing to try for incremental build : "
- + classFile);
- }
- } else {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: found aspect on aspectpath/inpath - can't determine if this project is affected, must full build: "
- + classFile);
- }
- return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
- }
- }
- }
- if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime)) {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: structural change detected in : " + classFile);
- }
- isTypeWeReferTo(classFile);
- } else {
- if (listenerDefined()) {
- getListener().recordDecision(
- "ClassFileChangeChecking: change detected in " + classFile + " but it is not structural");
- }
- }
- } else {
- // No state object to ask, so it only matters if we know which type depends on this file
- if (isTypeWeReferTo(classFile)) {
- return CLASS_FILE_CHANGED_THAT_NEEDS_INCREMENTAL_BUILD;
- } else {
- return CLASS_FILE_NO_CHANGES;
- }
- }
- }
- }
- }
- return CLASS_FILE_NO_CHANGES;
- }
-
- private boolean isAspect(File file) {
- return aspectClassFiles.contains(file.getAbsolutePath());
- }
-
- @SuppressWarnings("rawtypes")
- public static class SoftHashMap extends AbstractMap {
-
- private final Map map;
-
- private final ReferenceQueue rq = new ReferenceQueue();
-
- public SoftHashMap(Map map) {
- this.map = map;
- }
-
- public SoftHashMap() {
- this(new HashMap());
- }
-
- public SoftHashMap(Map map, boolean b) {
- this(map);
- }
-
- class SoftReferenceKnownKey extends SoftReference {
-
- private final Object key;
-
- @SuppressWarnings("unchecked")
- SoftReferenceKnownKey(Object k, Object v) {
- super(v, rq);
- this.key = k;
- }
- }
-
- private void processQueue() {
- SoftReferenceKnownKey sv = null;
- while ((sv = (SoftReferenceKnownKey) rq.poll()) != null) {
- map.remove(sv.key);
- }
- }
-
- @Override
- public Object get(Object key) {
- SoftReferenceKnownKey value = (SoftReferenceKnownKey) map.get(key);
- if (value == null) {
- return null;
- }
- if (value.get() == null) {
- // it got GC'd
- map.remove(value.key);
- return null;
- } else {
- return value.get();
- }
- }
-
- @Override
- public Object put(Object k, Object v) {
- processQueue();
- return map.put(k, new SoftReferenceKnownKey(k, v));
- }
-
- @Override
- public Set entrySet() {
- return map.entrySet();
- }
-
- @Override
- public void clear() {
- processQueue();
- map.clear();
- }
-
- @Override
- public int size() {
- processQueue();
- return map.size();
- }
-
- @Override
- public Object remove(Object k) {
- processQueue();
- SoftReferenceKnownKey value = (SoftReferenceKnownKey) map.remove(k);
- if (value == null) {
- return null;
- }
- if (value.get() != null) {
- return value.get();
- }
- return null;
- }
- }
-
- /**
- * 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.
- * 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
- * interesting change.
- */
- private boolean isTypeWeReferTo(File file) {
- String fpath = file.getAbsolutePath();
- int finalSeparator = fpath.lastIndexOf(File.separator);
- String baseDir = fpath.substring(0, finalSeparator);
- String theFile = fpath.substring(finalSeparator + 1);
- SoftHashMap classNames = (SoftHashMap) fileToClassNameMap.get(baseDir);
- if (classNames == null) {
- classNames = new SoftHashMap();
- fileToClassNameMap.put(baseDir, classNames);
- }
- char[] className = (char[]) classNames.get(theFile);
- if (className == null) {
- // if (listenerDefined())
- // getListener().recordDecision("Cache miss, looking up classname for : " + fpath);
-
- ClassFileReader cfr;
- try {
- cfr = ClassFileReader.read(file);
- } catch (ClassFormatException e) {
- return true;
- } catch (IOException e) {
- return true;
- }
- className = cfr.getName();
- classNames.put(theFile, className);
- // } else {
- // if (listenerDefined())
- // getListener().recordDecision("Cache hit, looking up classname for : " + fpath);
- }
-
- char[][][] qualifiedNames = null;
- char[][] simpleNames = null;
- if (CharOperation.indexOf('/', className) != -1) {
- qualifiedNames = new char[1][][];
- qualifiedNames[0] = CharOperation.splitOn('/', className);
- qualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedNames);
- } else {
- simpleNames = new char[1][];
- simpleNames[0] = className;
- simpleNames = ReferenceCollection.internSimpleNames(simpleNames, true);
- }
- int newlyAffectedFiles = 0;
- for (Map.Entry<File, ReferenceCollection> entry : references.entrySet()) {
- ReferenceCollection refs = entry.getValue();
- if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
- if (listenerDefined()) {
- getListener().recordDecision(
- toString() + ": type " + new String(className) + " is depended upon by '" + entry.getKey() + "'");
- }
- newlyAffectedFiles++;
- // possibly the beginnings of addressing the second point in 270033 comment 3
- // List/*ClassFile*/ cfs = (List)this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(entry.getKey());
- affectedFiles.add(entry.getKey());
- }
- }
- if (newlyAffectedFiles > 0) {
- return true;
- }
- if (listenerDefined()) {
- getListener().recordDecision(toString() + ": type " + new String(className) + " is not depended upon by this state");
- }
- return false;
- }
-
- // /**
- // * For a given class file, determine which source file it came from. This will only succeed if the class file is from a source
- // * file within this project.
- // */
- // private File getSourceFileForClassFile(File classfile) {
- // Set sourceFiles = fullyQualifiedTypeNamesResultingFromCompilationUnit.keySet();
- // for (Iterator sourceFileIterator = sourceFiles.iterator(); sourceFileIterator.hasNext();) {
- // File sourceFile = (File) sourceFileIterator.next();
- // List/* ClassFile */classesFromSourceFile = (List/* ClassFile */) fullyQualifiedTypeNamesResultingFromCompilationUnit
- // .get(sourceFile);
- // for (int i = 0; i < classesFromSourceFile.size(); i++) {
- // if (((ClassFile) classesFromSourceFile.get(i)).locationOnDisk.equals(classfile))
- // return sourceFile;
- // }
- // }
- // return null;
- // }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- // null config means failed build i think as it is only set on successful full build?
- sb.append("AjState(").append((buildConfig == null ? "NULLCONFIG" : buildConfig.getConfigFile().toString())).append(")");
- return sb.toString();
- }
-
- /**
- * Determine if a file has changed since a given time, using the local information recorded in the structural changes data
- * structure.
- *
- * @param file the file we are wondering about
- * @param lastSuccessfulBuildTime the last build time for the state asking the question
- */
- private boolean hasStructuralChangedSince(File file, long lastSuccessfulBuildTime) {
- // long lastModTime = file.lastModified();
- Long l = structuralChangesSinceLastFullBuild.get(file.getAbsolutePath());
- long strucModTime = -1;
- if (l != null) {
- strucModTime = l;
- } else {
- strucModTime = this.lastSuccessfulFullBuildTime;
- }
- // we now have:
- // 'strucModTime'-> the last time the class was structurally changed
- return (strucModTime > lastSuccessfulBuildTime);
- }
-
- /**
- * Determine if anything has changed since a given time.
- */
- private boolean hasAnyStructuralChangesSince(long lastSuccessfulBuildTime) {
- Set<Map.Entry<String, Long>> entries = structuralChangesSinceLastFullBuild.entrySet();
- for (Map.Entry<String, Long> entry : entries) {
- Long l = entry.getValue();
- if (l != null) {
- long lvalue = l;
- if (lvalue > lastSuccessfulBuildTime) {
- if (listenerDefined()) {
- getListener().recordDecision(
- "Seems this has changed " + entry.getKey() + "modtime=" + lvalue + " lsbt="
- + this.lastSuccessfulFullBuildTime + " incoming check value=" + lastSuccessfulBuildTime);
- }
- return true;
- }
- }
- }
- return (this.lastSuccessfulFullBuildTime > lastSuccessfulBuildTime);
- }
-
- /**
- * Determine if something has changed on the classpath/inpath/aspectpath and a full build is required rather than an incremental
- * one.
- *
- * @param previousConfig the previous configuration used
- * @param newConfig the new configuration being used
- * @return true if full build required
- */
- private boolean pathChange(AjBuildConfig previousConfig, AjBuildConfig newConfig) {
- int changes = newConfig.getChanged();
-
- if ((changes & (CLASSPATH_CHANGED | ASPECTPATH_CHANGED | INPATH_CHANGED | OUTPUTDESTINATIONS_CHANGED | INJARS_CHANGED)) != 0) {
- List<File> oldOutputLocs = getOutputLocations(previousConfig);
-
- Set<String> alreadyAnalysedPaths = new HashSet<>();
-
- List<String> oldClasspath = previousConfig.getClasspath();
- List<String> newClasspath = newConfig.getClasspath();
- if (stateListener != null) {
- stateListener.aboutToCompareClasspaths(oldClasspath, newClasspath);
- }
- if (classpathChangedAndNeedsFullBuild(oldClasspath, newClasspath, true, oldOutputLocs, alreadyAnalysedPaths)) {
- return true;
- }
-
- List<File> oldAspectpath = previousConfig.getAspectpath();
- List<File> newAspectpath = newConfig.getAspectpath();
- if (changedAndNeedsFullBuild(oldAspectpath, newAspectpath, true, oldOutputLocs, alreadyAnalysedPaths, PATHID_ASPECTPATH)) {
- return true;
- }
-
- List<File> oldInPath = previousConfig.getInpath();
- List<File> newInPath = newConfig.getInpath();
- if (changedAndNeedsFullBuild(oldInPath, newInPath, false, oldOutputLocs, alreadyAnalysedPaths, PATHID_INPATH)) {
- return true;
- }
-
- List<File> oldInJars = previousConfig.getInJars();
- List<File> newInJars = newConfig.getInJars();
- if (changedAndNeedsFullBuild(oldInJars, newInJars, false, oldOutputLocs, alreadyAnalysedPaths, PATHID_INPATH)) {
- return true;
- }
- } else if (newConfig.getClasspathElementsWithModifiedContents() != null) {
- // Although the classpath entries themselves are the same as before, the contents of one of the
- // directories on the classpath has changed - rather than go digging around to find it, let's ask
- // the compiler configuration. This will allow for projects with long classpaths where classpaths
- // are also capturing project dependencies - when a project we depend on is rebuilt, we can just check
- // it as a standalone element on our classpath rather than going through them all
- List<String> modifiedCpElements = newConfig.getClasspathElementsWithModifiedContents();
- for (String modifiedCpElement : modifiedCpElements) {
- File cpElement = new File(modifiedCpElement);
- if (cpElement.exists() && !cpElement.isDirectory()) {
- if (cpElement.lastModified() > lastSuccessfulBuildTime) {
- return true;
- }
- } else {
- int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(cpElement, PATHID_CLASSPATH);
- if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- /**
- * Return a list of the output locations - this includes any 'default' output location and then any known by a registered
- * CompilationResultDestinationManager.
- *
- * @param config the build configuration for which the output locations should be determined
- * @return a list of file objects
- */
- private List<File> getOutputLocations(AjBuildConfig config) {
- List<File> outputLocs = new ArrayList<>();
- // Is there a default location?
- if (config.getOutputDir() != null) {
- try {
- outputLocs.add(config.getOutputDir().getCanonicalFile());
- } catch (IOException e) {
- }
- }
- if (config.getCompilationResultDestinationManager() != null) {
- List<File> dirs = config.getCompilationResultDestinationManager().getAllOutputLocations();
- for (File f : dirs) {
- try {
- File cf = f.getCanonicalFile();
- if (!outputLocs.contains(cf)) {
- outputLocs.add(cf);
- }
- } catch (IOException e) {
- }
- }
- }
- return outputLocs;
- }
-
- private File getOutputLocationFor(AjBuildConfig config, File aResourceFile) {
- if (config.getCompilationResultDestinationManager() != null) {
- File outputLoc = config.getCompilationResultDestinationManager().getOutputLocationForResource(aResourceFile);
- if (outputLoc != null) {
- return outputLoc;
- }
- }
- // Is there a default location?
- if (config.getOutputDir() != null) {
- return config.getOutputDir();
- }
- return null;
- }
-
- /**
- * Check the old and new paths, if they vary by length or individual elements then that is considered a change. Or if the last
- * modified time of a path entry has changed (or last modified time of a classfile in that path entry has changed) then return
- * true. The outputlocations are supplied so they can be 'ignored' in the comparison.
- *
- * @param oldPath
- * @param newPath
- * @param checkClassFiles whether to examine individual class files within directories
- * @param outputLocs the output locations that should be ignored if they occur on the paths being compared
- * @return true if a change is detected that requires a full build
- */
- private boolean changedAndNeedsFullBuild(List<File> oldPath, List<File> newPath, boolean checkClassFiles, List<File> outputLocs,
- Set<String> alreadyAnalysedPaths, int pathid) {
- if (oldPath.size() != newPath.size()) {
- return true;
- }
- for (int i = 0; i < oldPath.size(); i++) {
- if (!oldPath.get(i).equals(newPath.get(i))) {
- return true;
- }
- File f = oldPath.get(i);
- if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
- return true;
- }
- if (checkClassFiles && f.isDirectory()) {
-
- // We should use here a list/set of directories we know have or have not changed - some kind of
- // List<File> buildConfig.getClasspathEntriesWithChangedContents()
- // and then only proceed to look inside directories if it is one of these, ignoring others -
- // that should save a massive amount of processing for incremental builds in a multi project scenario
-
- boolean foundMatch = false;
- for (Iterator<File> iterator = outputLocs.iterator(); !foundMatch && iterator.hasNext();) {
- File dir = iterator.next();
- if (f.equals(dir)) {
- foundMatch = true;
- }
- }
- if (!foundMatch) {
- if (!alreadyAnalysedPaths.contains(f.getAbsolutePath())) { // Do not check paths more than once
- alreadyAnalysedPaths.add(f.getAbsolutePath());
- int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(f, pathid);
- if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Check the old and new paths, if they vary by length or individual elements then that is considered a change. Or if the last
- * modified time of a path entry has changed (or last modified time of a classfile in that path entry has changed) then return
- * true. The outputlocations are supplied so they can be 'ignored' in the comparison.
- *
- * @param oldPath
- * @param newPath
- * @param checkClassFiles whether to examine individual class files within directories
- * @param outputLocs the output locations that should be ignored if they occur on the paths being compared
- * @return true if a change is detected that requires a full build
- */
- private boolean classpathChangedAndNeedsFullBuild(List<String> oldPath, List<String> newPath, boolean checkClassFiles,
- List<File> outputLocs, Set<String> alreadyAnalysedPaths) {
- if (oldPath.size() != newPath.size()) {
- return true;
- }
- for (int i = 0; i < oldPath.size(); i++) {
- if (!oldPath.get(i).equals(newPath.get(i))) {
- return true;
- }
- File f = new File(oldPath.get(i));
- if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
- return true;
- }
- if (checkClassFiles && f.isDirectory()) {
-
- // We should use here a list/set of directories we know have or have not changed - some kind of
- // List<File> buildConfig.getClasspathEntriesWithChangedContents()
- // and then only proceed to look inside directories if it is one of these, ignoring others -
- // that should save a massive amount of processing for incremental builds in a multi project scenario
-
- boolean foundMatch = false;
- for (Iterator<File> iterator = outputLocs.iterator(); !foundMatch && iterator.hasNext();) {
- File dir = iterator.next();
- if (f.equals(dir)) {
- foundMatch = true;
- }
- }
- if (!foundMatch) {
- if (!alreadyAnalysedPaths.contains(f.getAbsolutePath())) { // Do not check paths more than once
- alreadyAnalysedPaths.add(f.getAbsolutePath());
- int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(f, PATHID_CLASSPATH);
- if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
-
- public Set<File> getFilesToCompile(boolean firstPass) {
- Set<File> thisTime = new HashSet<>();
- if (firstPass) {
- compiledSourceFiles = new HashSet<>();
- Collection<File> modifiedFiles = getModifiedFiles();
- // System.out.println("modified: " + modifiedFiles);
- thisTime.addAll(modifiedFiles);
- // ??? eclipse IncrementalImageBuilder appears to do this
- // for (Iterator i = modifiedFiles.iterator(); i.hasNext();) {
- // File file = (File) i.next();
- // addDependentsOf(file);
- // }
-
- if (addedFiles != null) {
- for (File o : addedFiles) {
- // TODO isn't it a set?? why do this
- if (!thisTime.contains(o)) {
- thisTime.add(o);
- }
- }
- // thisTime.addAll(addedFiles);
- }
-
- deleteClassFiles();
- // Do not delete resources on incremental build, AJDT will handle
- // copying updates to the output folder. AspectJ only does a copy
- // of them on full build (see copyResourcesToDestination() call
- // in AjBuildManager)
- // deleteResources();
-
- addAffectedSourceFiles(thisTime, thisTime);
- } else {
- addAffectedSourceFiles(thisTime, compiledSourceFiles);
- }
- compiledSourceFiles = thisTime;
- return thisTime;
- }
-
- private boolean maybeIncremental() {
- return (FORCE_INCREMENTAL_DURING_TESTING || this.couldBeSubsequentIncrementalBuild);
- }
-
- public Map<String, List<UnwovenClassFile>> getBinaryFilesToCompile(boolean firstTime) {
- if (lastSuccessfulBuildTime == -1 || buildConfig == null || !maybeIncremental()) {
- return binarySourceFiles;
- }
- // else incremental...
- Map<String, List<UnwovenClassFile>> toWeave = new HashMap<>();
- if (firstTime) {
- List<BinarySourceFile> addedOrModified = new ArrayList<>();
- addedOrModified.addAll(addedBinaryFiles);
- addedOrModified.addAll(getModifiedBinaryFiles());
- for (BinarySourceFile bsf : addedOrModified) {
- UnwovenClassFile ucf = createUnwovenClassFile(bsf);
- if (ucf == null) {
- continue;
- }
- List<UnwovenClassFile> ucfs = new ArrayList<>();
- ucfs.add(ucf);
- recordTypeChanged(ucf.getClassName());
- binarySourceFiles.put(bsf.binSrc.getPath(), ucfs);
- List<ClassFile> cfs = new ArrayList<>(1);
- cfs.add(getClassFileFor(ucf));
- this.inputClassFilesBySource.put(bsf.binSrc.getPath(), cfs);
- toWeave.put(bsf.binSrc.getPath(), ucfs);
- }
- deleteBinaryClassFiles();
- } else {
- // return empty set... we've already done our bit.
- }
- return toWeave;
- }
-
- /**
- * Called when a path change is about to trigger a full build, but we haven't cleaned up from the last incremental build...
- */
- private void removeAllResultsOfLastBuild() {
- // remove all binarySourceFiles, and all classesFromName...
- for (List<ClassFile> cfs : this.inputClassFilesBySource.values()) {
- for (ClassFile cf : cfs) {
- cf.deleteFromFileSystem(buildConfig);
- }
- }
- for (File f : classesFromName.values()) {
- new ClassFile("", f).deleteFromFileSystem(buildConfig);
- }
- Set<Map.Entry<String, File>> resourceEntries = resources.entrySet();
- for (Map.Entry<String, File> resourcePair : resourceEntries) {
- File sourcePath = resourcePair.getValue();
- File outputLoc = getOutputLocationFor(buildConfig, sourcePath);
- if (outputLoc != null) {
- outputLoc = new File(outputLoc, resourcePair.getKey());
- if (!outputLoc.getPath().equals(sourcePath.getPath()) && outputLoc.exists()) {
- outputLoc.delete();
- if (buildConfig.getCompilationResultDestinationManager() != null) {
- buildConfig.getCompilationResultDestinationManager().reportFileRemove(outputLoc.getPath(),
- CompilationResultDestinationManager.FILETYPE_RESOURCE);
- }
- }
- }
- }
- }
-
- private void deleteClassFiles() {
- if (deletedFiles == null) {
- return;
- }
- for (File deletedFile : deletedFiles) {
- addDependentsOf(deletedFile);
-
- List<ClassFile> cfs = this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(deletedFile);
- this.fullyQualifiedTypeNamesResultingFromCompilationUnit.remove(deletedFile);
-
- if (cfs != null) {
- for (ClassFile cf : cfs) {
- deleteClassFile(cf);
- }
- }
-
- }
- }
-
- private void deleteBinaryClassFiles() {
- // range of bsf is ucfs, domain is files (.class and jars) in inpath/jars
- for (BinarySourceFile deletedFile : deletedBinaryFiles) {
- List<ClassFile> cfs = this.inputClassFilesBySource.get(deletedFile.binSrc.getPath());
- for (ClassFile cf : cfs) {
- deleteClassFile(cf);
- }
- this.inputClassFilesBySource.remove(deletedFile.binSrc.getPath());
- }
- }
-
- // private void deleteResources() {
- // List oldResources = new ArrayList();
- // oldResources.addAll(resources);
- //
- // // note - this deliberately ignores resources in jars as we don't yet handle jar changes
- // // with incremental compilation
- // for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext();) {
- // File inPathElement = (File) i.next();
- // if (inPathElement.isDirectory() && AjBuildManager.COPY_INPATH_DIR_RESOURCES) {
- // deleteResourcesFromDirectory(inPathElement, oldResources);
- // }
- // }
- //
- // if (buildConfig.getSourcePathResources() != null) {
- // for (Iterator i = buildConfig.getSourcePathResources().keySet().iterator(); i.hasNext();) {
- // String resource = (String) i.next();
- // maybeDeleteResource(resource, oldResources);
- // }
- // }
- //
- // // oldResources need to be deleted...
- // for (Iterator iter = oldResources.iterator(); iter.hasNext();) {
- // String victim = (String) iter.next();
- // List outputDirs = getOutputLocations(buildConfig);
- // for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
- // File dir = (File) iterator.next();
- // File f = new File(dir, victim);
- // if (f.exists()) {
- // f.delete();
- // }
- // resources.remove(victim);
- // }
- // }
- // }
-
- // private void maybeDeleteResource(String resName, List oldResources) {
- // if (resources.contains(resName)) {
- // oldResources.remove(resName);
- // List outputDirs = getOutputLocations(buildConfig);
- // for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
- // File dir = (File) iterator.next();
- // File source = new File(dir, resName);
- // if (source.exists() && (source.lastModified() >= lastSuccessfulBuildTime)) {
- // resources.remove(resName); // will ensure it is re-copied
- // }
- // }
- // }
- // }
-
- // private void deleteResourcesFromDirectory(File dir, List oldResources) {
- // File[] files = FileUtil.listFiles(dir, new FileFilter() {
- // public boolean accept(File f) {
- // boolean accept = !(f.isDirectory() || f.getName().endsWith(".class"));
- // return accept;
- // }
- // });
- //
- // // For each file, add it either as a real .class file or as a resource
- // for (int i = 0; i < files.length; i++) {
- // // ASSERT: files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath()
- // // or we are in trouble...
- // String filename = null;
- // try {
- // filename = files[i].getCanonicalPath().substring(dir.getCanonicalPath().length() + 1);
- // } catch (IOException e) {
- // // we are in trouble if this happens...
- // IMessage msg = new Message("call to getCanonicalPath() failed for file " + files[i] + " with: " + e.getMessage(),
- // new SourceLocation(files[i], 0), false);
- // buildManager.handler.handleMessage(msg);
- // filename = files[i].getAbsolutePath().substring(dir.getAbsolutePath().length() + 1);
- // }
- //
- // maybeDeleteResource(filename, oldResources);
- // }
- // }
-
- private void deleteClassFile(ClassFile cf) {
- classesFromName.remove(cf.fullyQualifiedTypeName);
- weaver.deleteClassFile(cf.fullyQualifiedTypeName);
- cf.deleteFromFileSystem(buildConfig);
- }
-
- private UnwovenClassFile createUnwovenClassFile(AjBuildConfig.BinarySourceFile bsf) {
- UnwovenClassFile ucf = null;
- try {
- File outputDir = buildConfig.getOutputDir();
- if (buildConfig.getCompilationResultDestinationManager() != null) {
- // createUnwovenClassFile is called only for classes that are on the inpath,
- // all inpath classes are put in the defaultOutputLocation, therefore,
- // this is the output dir
- outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
- }
- ucf = weaver.addClassFile(bsf.binSrc, bsf.fromInPathDirectory, outputDir);
- } catch (IOException ex) {
- IMessage msg = new Message("can't read class file " + bsf.binSrc.getPath(), new SourceLocation(bsf.binSrc, 0), false);
- buildManager.handler.handleMessage(msg);
- }
- return ucf;
- }
-
- public void noteResult(InterimCompilationResult result) {
- if (!maybeIncremental()) {
- return;
- }
-
- File sourceFile = new File(result.fileName());
- CompilationResult cr = result.result();
-
- references.put(sourceFile, new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences,cr.rootReferences));
-
- UnwovenClassFile[] unwovenClassFiles = result.unwovenClassFiles();
- for (UnwovenClassFile unwovenClassFile : unwovenClassFiles) {
- File lastTimeRound = classesFromName.get(unwovenClassFile.getClassName());
- recordClassFile(unwovenClassFile, lastTimeRound);
- String name = unwovenClassFile.getClassName();
- if (lastTimeRound == null) {
- deltaAddedClasses.add(name);
- }
- classesFromName.put(name, new File(unwovenClassFile.getFilename()));
- }
-
- // need to do this before types are deleted from the World...
- recordWhetherCompilationUnitDefinedAspect(sourceFile, cr);
- deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(sourceFile, unwovenClassFiles);
-
- recordFQNsResultingFromCompilationUnit(sourceFile, result);
- }
-
- public void noteNewResult(CompilationResult cr) {
- // if (!maybeIncremental()) {
- // return;
- // }
- //
- // // File sourceFile = new File(result.fileName());
- // // CompilationResult cr = result.result();
- // if (new String(cr.getFileName()).indexOf("C") != -1) {
- // cr.references.put(new String(cr.getFileName()),
- // new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
- // int stop = 1;
- // }
-
- // references.put(sourceFile, new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
- //
- // UnwovenClassFile[] unwovenClassFiles = cr.unwovenClassFiles();
- // for (int i = 0; i < unwovenClassFiles.length; i++) {
- // File lastTimeRound = (File) classesFromName.get(unwovenClassFiles[i].getClassName());
- // recordClassFile(unwovenClassFiles[i], lastTimeRound);
- // classesFromName.put(unwovenClassFiles[i].getClassName(), new File(unwovenClassFiles[i].getFilename()));
- // }
-
- // need to do this before types are deleted from the World...
- // recordWhetherCompilationUnitDefinedAspect(sourceFile, cr);
- // deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(sourceFile, unwovenClassFiles);
- //
- // recordFQNsResultingFromCompilationUnit(sourceFile, result);
- }
-
- /**
- * @param sourceFile
- * @param unwovenClassFiles
- */
- private void deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(File sourceFile,
- UnwovenClassFile[] unwovenClassFiles) {
- List<ClassFile> classFiles = this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(sourceFile);
- if (classFiles != null) {
-
- for (UnwovenClassFile unwovenClassFile : unwovenClassFiles) {
- // deleting also deletes types from the weaver... don't do this if they are
- // still present this time around...
- removeFromClassFilesIfPresent(unwovenClassFile.getClassName(), classFiles);
- }
- for (ClassFile cf : classFiles) {
- recordTypeChanged(cf.fullyQualifiedTypeName);
- resolvedTypeStructuresFromLastBuild.remove(cf.fullyQualifiedTypeName);
- // }
- // for (ClassFile cf : classFiles) {
- deleteClassFile(cf);
- }
- }
- }
-
- private void removeFromClassFilesIfPresent(String className, List<ClassFile> classFiles) {
- ClassFile victim = null;
- for (ClassFile cf : classFiles) {
- if (cf.fullyQualifiedTypeName.equals(className)) {
- victim = cf;
- break;
- }
- }
- if (victim != null) {
- classFiles.remove(victim);
- }
- }
-
- /**
- * Record the fully-qualified names of the types that were declared in the given source file.
- *
- * @param sourceFile, the compilation unit
- * @param icr, the CompilationResult from compiling it
- */
- private void recordFQNsResultingFromCompilationUnit(File sourceFile, InterimCompilationResult icr) {
- List<ClassFile> classFiles = new ArrayList<>();
- UnwovenClassFile[] types = icr.unwovenClassFiles();
- for (UnwovenClassFile type : types) {
- classFiles.add(new ClassFile(type.getClassName(), new File(type.getFilename())));
- }
- this.fullyQualifiedTypeNamesResultingFromCompilationUnit.put(sourceFile, classFiles);
- }
-
- /**
- * If this compilation unit defined an aspect, we need to know in case it is modified in a future increment.
- *
- * @param sourceFile
- * @param cr
- */
- private void recordWhetherCompilationUnitDefinedAspect(File sourceFile, CompilationResult cr) {
- this.sourceFilesDefiningAspects.remove(sourceFile);
-
- if (cr != null) {
- Map compiledTypes = cr.compiledTypes;
- if (compiledTypes != null) {
- for (char[] className : (Iterable<char[]>) compiledTypes.keySet()) {
- String typeName = new String(className).replace('/', '.');
- if (!typeName.contains(BcelWeaver.SYNTHETIC_CLASS_POSTFIX)) {
- ResolvedType rt = world.resolve(typeName);
- if (rt.isMissing()) {
- // This can happen in a case where another problem has occurred that prevented it being
- // correctly added to the world. Eg. pr148285. Duplicate types
- // throw new IllegalStateException("Type '" + rt.getSignature() + "' not found in world!");
- } else if (rt.isAspect()) {
- this.sourceFilesDefiningAspects.add(sourceFile);
- break;
- }
- }
- }
- }
- }
-
- }
-
- // private UnwovenClassFile removeFromPreviousIfPresent(UnwovenClassFile cf, InterimCompilationResult previous) {
- // if (previous == null)
- // return null;
- // UnwovenClassFile[] unwovenClassFiles = previous.unwovenClassFiles();
- // for (int i = 0; i < unwovenClassFiles.length; i++) {
- // UnwovenClassFile candidate = unwovenClassFiles[i];
- // if ((candidate != null) && candidate.getFilename().equals(cf.getFilename())) {
- // unwovenClassFiles[i] = null;
- // return candidate;
- // }
- // }
- // return null;
- // }
-
- private void recordClassFile(UnwovenClassFile thisTime, File lastTime) {
- if (simpleStrings == null) {
- // batch build
- // record resolved type for structural comparisons in future increments
- // this records a second reference to a structure already held in memory
- // by the world.
- ResolvedType rType = world.resolve(thisTime.getClassName());
- if (!rType.isMissing()) {
- try {
- ClassFileReader reader = new ClassFileReader(thisTime.getBytes(), null);
- boolean isAspect = false;
- if (rType instanceof ReferenceType && ((ReferenceType) rType).getDelegate() != null) {
- isAspect = ((ReferenceType) rType).isAspect();
- }
- this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(), new CompactTypeStructureRepresentation(
- reader, isAspect));
- } catch (ClassFormatException cfe) {
- throw new BCException("Unexpected problem processing class", cfe);
- }
- }
- return;
- }
-
- CompactTypeStructureRepresentation existingStructure = this.resolvedTypeStructuresFromLastBuild
- .get(thisTime.getClassName());
- ResolvedType newResolvedType = world.resolve(thisTime.getClassName());
- if (!newResolvedType.isMissing()) {
- try {
- ClassFileReader reader = new ClassFileReader(thisTime.getBytes(), null);
- boolean isAspect = false;
- if (newResolvedType instanceof ReferenceType && ((ReferenceType) newResolvedType).getDelegate() != null) {
- isAspect = ((ReferenceType) newResolvedType).isAspect();
- }
- this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(), new CompactTypeStructureRepresentation(
- reader, isAspect));
- } catch (ClassFormatException cfe) {
- try {
- String s = System.getProperty("aspectj.debug377096","false");
- if (s.equalsIgnoreCase("true")) {
- String location = System.getProperty("java.io.tmpdir","/tmp");
- String name = thisTime.getClassName();
- File f = File.createTempFile(location+File.separator+name, ".class");
- StringBuilder debug = new StringBuilder();
- debug.append("Debug377096: Dumping class called "+name+" to "+f.getName()+" size:"+thisTime.getBytes().length);
- DataOutputStream dos = new DataOutputStream(new FileOutputStream(f));
- dos.write(thisTime.getBytes());
- dos.close();
- throw new BCException(debug.toString(), cfe);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- throw new BCException("Unexpected problem processing class", cfe);
- }
- }
-
- if (lastTime == null) {
- recordTypeChanged(thisTime.getClassName());
- return;
- }
-
- if (newResolvedType.isMissing()) {
- return;
- }
- world.ensureAdvancedConfigurationProcessed();
- byte[] newBytes = thisTime.getBytes();
- try {
- ClassFileReader reader = new ClassFileReader(newBytes, lastTime.getAbsolutePath().toCharArray());
- // ignore local types since they're only visible inside a single method
- if (!(reader.isLocal() || reader.isAnonymous())) {
- if (hasStructuralChanges(reader, existingStructure)) {
- if (listenerDefined()) {
- // if (world.forDEBUG_structuralChangesCode) {
- // System.err.println("Detected a structural change in " + thisTime.getFilename());
- printStructuralChanges(thisTime.getFilename(),reader, existingStructure);
- }
- structuralChangesSinceLastFullBuild.put(thisTime.getFilename(), currentBuildTime);
- recordTypeChanged(new String(reader.getName()).replace('/', '.'));
- }
- }
- } catch (ClassFormatException e) {
- recordTypeChanged(thisTime.getClassName());
- }
- }
-
- /**
- * Compare the class structure of the new intermediate (unwoven) class with the existingResolvedType of the same class that we
- * have in the world, looking for any structural differences (and ignoring aj members resulting from weaving....)
- *
- * Some notes from Andy... lot of problems here, which I've eventually resolved by building the compactstructure based on a
- * classfilereader, rather than on a ResolvedType. There are accessors for inner types and funky fields that the compiler
- * creates to support the language - for non-static inner types it also mangles ctors to be prefixed with an instance of the
- * surrounding type.
- *
- * @param reader
- * @param existingType
- * @return
- */
- private boolean hasStructuralChanges(ClassFileReader reader, CompactTypeStructureRepresentation existingType) {
- if (existingType == null) {
- return true;
- }
-
- // modifiers
- if (!modifiersEqual(reader.getModifiers(), existingType.modifiers)) {
- return true;
- }
-
- // generic signature
- if (!CharOperation.equals(reader.getGenericSignature(), existingType.genericSignature)) {
- return true;
- }
-
- // superclass name
- if (!CharOperation.equals(reader.getSuperclassName(), existingType.superclassName)) {
- return true;
- }
-
- // have annotations changed on the type?
- IBinaryAnnotation[] newAnnos = reader.getAnnotations();
- if (newAnnos == null || newAnnos.length == 0) {
- if (existingType.annotations != null && existingType.annotations.length != 0) {
- return true;
- }
- } else {
- IBinaryAnnotation[] existingAnnos = existingType.annotations;
- if (existingAnnos == null || existingAnnos.length != newAnnos.length) {
- return true;
- }
- // Does not allow for an order switch
- // Does not cope with a change in values set on the annotation (hard to create a testcase where this is a problem tho)
- for (int i = 0; i < newAnnos.length; i++) {
- if (!CharOperation.equals(newAnnos[i].getTypeName(), existingAnnos[i].getTypeName())) {
- return true;
- }
- }
-
- }
-
- // interfaces
- char[][] existingIfs = existingType.interfaces;
- char[][] newIfsAsChars = reader.getInterfaceNames();
- if (newIfsAsChars == null) {
- newIfsAsChars = EMPTY_CHAR_ARRAY;
- } // damn I'm lazy...
- if (existingIfs == null) {
- existingIfs = EMPTY_CHAR_ARRAY;
- }
- if (existingIfs.length != newIfsAsChars.length) {
- return true;
- }
- new_interface_loop:
- for (char[] newIfsAsChar : newIfsAsChars) {
- for (char[] existingIf : existingIfs) {
- if (CharOperation.equals(existingIf, newIfsAsChar)) {
- continue new_interface_loop;
- }
- }
- return true;
- }
-
- // fields
- // CompactMemberStructureRepresentation[] existingFields = existingType.fields;
- IBinaryField[] newFields = reader.getFields();
- if (newFields == null) {
- newFields = CompactTypeStructureRepresentation.NoField;
- }
-
- // all redundant for now ... could be an optimization at some point...
- // remove any ajc$XXX fields from those we compare with
- // the existing fields - bug 129163
- // List nonGenFields = new ArrayList();
- // for (int i = 0; i < newFields.length; i++) {
- // IBinaryField field = newFields[i];
- // //if (!CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,field.getName())) { // this would skip ajc$ fields
- // //if ((field.getModifiers()&0x1000)==0) // 0x1000 => synthetic - this will skip synthetic fields (eg. this$0)
- // nonGenFields.add(field);
- // //}
- // }
- IBinaryField[] existingFs = existingType.binFields;
- if (newFields.length != existingFs.length) {
- return true;
- }
- new_field_loop:
- for (IBinaryField field : newFields) {
- char[] fieldName = field.getName();
- for (IBinaryField existingF : existingFs) {
- if (CharOperation.equals(existingF.getName(), fieldName)) {
- IBinaryField existing = existingF;
- if (!modifiersEqual(field.getModifiers(), existing.getModifiers())) {
- return true;
- }
- if (!CharOperation.equals(existing.getTypeName(), field.getTypeName())) {
- return true;
- }
-
- char[] existingGSig = existing.getGenericSignature();
- char[] fieldGSig = field.getGenericSignature();
- if ((existingGSig == null && fieldGSig != null) || (existingGSig != null && fieldGSig == null)) {
- return true;
- }
- if (existingGSig != null) {
- if (!CharOperation.equals(existingGSig, fieldGSig)) {
- return true;
- }
- }
-
- continue new_field_loop;
- }
- }
- return true;
- }
-
- // methods
- // CompactMemberStructureRepresentation[] existingMethods = existingType.methods;
- IBinaryMethod[] newMethods = reader.getMethods();
- if (newMethods == null) {
- newMethods = CompactTypeStructureRepresentation.NoMethod;
- }
-
- // all redundant for now ... could be an optimization at some point...
-
- // Ctors in a non-static inner type have an 'extra parameter' of the enclosing type.
- // If skippableDescriptorPrefix gets set here then it is set to the descriptor portion
- // for this 'extra parameter'. For an inner class of pkg.Foo the skippable descriptor
- // prefix will be '(Lpkg/Foo;' - so later when comparing <init> methods we know what to
- // compare.
- // IF THIS CODE NEEDS TO GET MORE COMPLICATED, I THINK ITS WORTH RIPPING IT ALL OUT AND
- // CREATING THE STRUCTURAL CHANGES OBJECT BASED ON CLASSREADER OUTPUT RATHER THAN
- // THE RESOLVEDTYPE - THEN THERE WOULD BE NO NEED TO TREAT SOME METHODS IN A PECULIAR
- // WAY.
- // char[] skippableDescriptorPrefix = null;
- // char[] enclosingTypeName = reader.getEnclosingTypeName();
- // boolean isStaticType = Modifier.isStatic(reader.getModifiers());
- // if (!isStaticType && enclosingTypeName!=null) {
- // StringBuffer sb = new StringBuffer();
- // sb.append("(L").append(new String(enclosingTypeName)).append(";");
- // skippableDescriptorPrefix = sb.toString().toCharArray();
- // }
- //
- //
- // // remove the aspectOf, hasAspect, clinit and ajc$XXX methods
- // // from those we compare with the existing methods - bug 129163
- // List nonGenMethods = new ArrayList();
- // for (int i = 0; i < newMethods.length; i++) {
- // IBinaryMethod method = newMethods[i];
- // // if ((method.getModifiers() & 0x1000)!=0) continue; // 0x1000 => synthetic - will cause us to skip access$0 - is this
- // always safe?
- // char[] methodName = method.getSelector();
- // // if (!CharOperation.equals(methodName,NameMangler.METHOD_ASPECTOF) &&
- // // !CharOperation.equals(methodName,NameMangler.METHOD_HASASPECT) &&
- // // !CharOperation.equals(methodName,NameMangler.STATIC_INITIALIZER) &&
- // // !CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,methodName) &&
- // // !CharOperation.prefixEquals(NameMangler.CLINIT,methodName)) {
- // nonGenMethods.add(method);
- // // }
- // }
- IBinaryMethod[] existingMs = existingType.binMethods;
- if (newMethods.length != existingMs.length) {
- return true;
- }
- new_method_loop:
- for (IBinaryMethod method : newMethods) {
- char[] methodName = method.getSelector();
- for (IBinaryMethod existingM : existingMs) {
- if (CharOperation.equals(existingM.getSelector(), methodName)) {
- // candidate match
- if (!CharOperation.equals(method.getMethodDescriptor(), existingM.getMethodDescriptor())) {
- // ok, the descriptors don't match, but is this a funky ctor on a non-static inner
- // type?
- // boolean mightBeOK =
- // skippableDescriptorPrefix!=null && // set for inner types
- // CharOperation.equals(methodName,NameMangler.INIT) && // ctor
- // CharOperation.prefixEquals(skippableDescriptorPrefix,method.getMethodDescriptor()); // checking for
- // prefix on the descriptor
- // if (mightBeOK) {
- // // OK, so the descriptor starts something like '(Lpkg/Foo;' - we now may need to look at the rest of the
- // // descriptor if it takes >1 parameter.
- // // eg. could be (Lpkg/C;Ljava/lang/String;) where the skippablePrefix is (Lpkg/C;
- // char [] md = method.getMethodDescriptor();
- // char[] remainder = CharOperation.subarray(md, skippableDescriptorPrefix.length, md.length);
- // if (CharOperation.equals(remainder,BRACKET_V)) continue new_method_loop; // no other parameters to worry
- // about
- // char[] comparableSig = CharOperation.subarray(existingMethods[j].signature, 1,
- // existingMethods[j].signature.length);
- // boolean match = CharOperation.equals(comparableSig, remainder);
- // if (match) continue new_method_loop;
- // }
- continue; // might be overloading
- } else {
- // matching sigs
- IBinaryMethod existing = existingM;
- if (!modifiersEqual(method.getModifiers(), existing.getModifiers())) {
- return true;
- }
-
- if (exceptionClausesDiffer(existing, method)) {
- return true;
- }
-
- char[] existingGSig = existing.getGenericSignature();
- char[] methodGSig = method.getGenericSignature();
- if ((existingGSig == null && methodGSig != null) || (existingGSig != null && methodGSig == null)) {
- return true;
- }
- if (existingGSig != null) {
- if (!CharOperation.equals(existingGSig, methodGSig)) {
- return true;
- }
- }
-
- continue new_method_loop;
- }
- }
- }
- return true; // (no match found)
- }
-
- // check for differences in inner types
- // TODO could make order insensitive
- IBinaryNestedType[] binaryNestedTypes = reader.getMemberTypes();
- IBinaryNestedType[] existingBinaryNestedTypes = existingType.getMemberTypes();
- if ((binaryNestedTypes == null && existingBinaryNestedTypes != null)
- || (binaryNestedTypes != null && existingBinaryNestedTypes == null)) {
- return true;
- }
- if (binaryNestedTypes != null) {
- int bnLength = binaryNestedTypes.length;
- if (existingBinaryNestedTypes.length != bnLength) {
- return true;
- }
- for (int m = 0; m < bnLength; m++) {
- IBinaryNestedType bnt = binaryNestedTypes[m];
- IBinaryNestedType existingBnt = existingBinaryNestedTypes[m];
- if (!CharOperation.equals(bnt.getName(), existingBnt.getName())) {
- return true;
- }
- }
- }
- return false;
- }
-
- private void logAnalysis(String filename, String info) {
- if (listenerDefined()) {
- getListener().recordDecision("StructuralAnalysis["+filename+"]: "+info);
- }
- }
-
- private boolean printStructuralChanges(String filename, ClassFileReader reader, CompactTypeStructureRepresentation existingType) {
- logAnalysis(filename,"appears to have structurally changed, printing changes:");
- if (existingType == null) {
- logAnalysis(filename,"have not seen this type before");
- return true;
- }
-
- // modifiers
- if (!modifiersEqual(reader.getModifiers(), existingType.modifiers)) {
- logAnalysis(filename,"modifiers changed. old=0x"+Integer.toHexString(existingType.getModifiers())+" new=0x"+Integer.toHexString(reader.getModifiers()));
- return true;
- }
-
- // generic signature
- if (!CharOperation.equals(reader.getGenericSignature(), existingType.genericSignature)) {
- logAnalysis(filename,"generic signature changed. old="+stringify(existingType.genericSignature)+" new="+stringify(reader.getGenericSignature()));
- return true;
- }
-
- // superclass name
- if (!CharOperation.equals(reader.getSuperclassName(), existingType.superclassName)) {
- logAnalysis(filename,"superclass name changed. old="+stringify(existingType.superclassName)+" new="+stringify(reader.getSuperclassName()));
- return true;
- }
-
- // have annotations changed on the type?
- IBinaryAnnotation[] newAnnos = reader.getAnnotations();
- if (newAnnos == null || newAnnos.length == 0) {
- if (existingType.annotations != null && existingType.annotations.length != 0) {
- logAnalysis(filename,"type used to have annotations and now does not: "+stringify(existingType.annotations));
- return true;
- }
- } else {
- IBinaryAnnotation[] existingAnnos = existingType.annotations;
- if (existingAnnos == null || existingAnnos.length != newAnnos.length) {
- logAnalysis(filename,"type now has annotations which it did not used to have: "+stringify(newAnnos));
- return true;
- }
- // Does not allow for an order switch
- // Does not cope with a change in values set on the annotation (hard to create a testcase where this is a problem tho)
- for (int i = 0; i < newAnnos.length; i++) {
- if (!CharOperation.equals(newAnnos[i].getTypeName(), existingAnnos[i].getTypeName())) {
- logAnalysis(filename,"type annotation change at position "+i+" old="+new String(existingAnnos[i].getTypeName())+" new="+new String(newAnnos[i].getTypeName()));
- return true;
- }
- }
-
- }
-
- // interfaces
- char[][] existingIfs = existingType.interfaces;
- char[][] newIfsAsChars = reader.getInterfaceNames();
- if (newIfsAsChars == null) {
- newIfsAsChars = EMPTY_CHAR_ARRAY;
- } // damn I'm lazy...
- if (existingIfs == null) {
- existingIfs = EMPTY_CHAR_ARRAY;
- }
- if (existingIfs.length != newIfsAsChars.length) {
- return true;
- }
- new_interface_loop:
- for (char[] newIfsAsChar : newIfsAsChars) {
- for (char[] existingIf : existingIfs) {
- if (CharOperation.equals(existingIf, newIfsAsChar)) {
- continue new_interface_loop;
- }
- }
- logAnalysis(filename, "set of interfaces changed. old=" + stringify(existingIfs) + " new=" + stringify(newIfsAsChars));
- return true;
- }
-
- // fields
- // CompactMemberStructureRepresentation[] existingFields = existingType.fields;
- IBinaryField[] newFields = reader.getFields();
- if (newFields == null) {
- newFields = CompactTypeStructureRepresentation.NoField;
- }
-
- // all redundant for now ... could be an optimization at some point...
- // remove any ajc$XXX fields from those we compare with
- // the existing fields - bug 129163
- // List nonGenFields = new ArrayList();
- // for (int i = 0; i < newFields.length; i++) {
- // IBinaryField field = newFields[i];
- // //if (!CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,field.getName())) { // this would skip ajc$ fields
- // //if ((field.getModifiers()&0x1000)==0) // 0x1000 => synthetic - this will skip synthetic fields (eg. this$0)
- // nonGenFields.add(field);
- // //}
- // }
- IBinaryField[] existingFs = existingType.binFields;
- if (newFields.length != existingFs.length) {
- logAnalysis(filename,"number of fields changed. old="+stringify(existingFs)+" new="+stringify(newFields));
- return true;
- }
- new_field_loop:
- for (IBinaryField field : newFields) {
- char[] fieldName = field.getName();
- for (IBinaryField existingF : existingFs) {
- if (CharOperation.equals(existingF.getName(), fieldName)) {
- IBinaryField existing = existingF;
- if (!modifiersEqual(field.getModifiers(), existing.getModifiers())) {
- logAnalysis(filename, "field modifiers changed '" + existing + "' old=0x" + Integer.toHexString(existing.getModifiers()) + " new=0x" + Integer.toHexString(field.getModifiers()));
- return true;
- }
- if (!CharOperation.equals(existing.getTypeName(), field.getTypeName())) {
- logAnalysis(filename, "field type changed '" + existing + "' old=" + new String(existing.getTypeName()) + " new=" + new String(field.getTypeName()));
- return true;
- }
-
- char[] existingGSig = existing.getGenericSignature();
- char[] fieldGSig = field.getGenericSignature();
- if ((existingGSig == null && fieldGSig != null) || (existingGSig != null && fieldGSig == null)) {
- logAnalysis(filename, "field generic sig changed '" + existing + "' old=" +
- (existingGSig == null ? "null" : new String(existingGSig)) + " new=" + (fieldGSig == null ? "null" : new String(fieldGSig)));
- return true;
- }
- if (existingGSig != null) {
- if (!CharOperation.equals(existingGSig, fieldGSig)) {
- logAnalysis(filename, "field generic sig changed '" + existing + "' old=" +
- (existingGSig == null ? "null" : new String(existingGSig)) + " new=" + (fieldGSig == null ? "null" : new String(fieldGSig)));
- return true;
- }
- }
-
- continue new_field_loop;
- }
- }
- logAnalysis(filename, "field changed. New field detected '" + field + "'");
- return true;
- }
-
- // methods
- // CompactMemberStructureRepresentation[] existingMethods = existingType.methods;
- IBinaryMethod[] newMethods = reader.getMethods();
- if (newMethods == null) {
- newMethods = CompactTypeStructureRepresentation.NoMethod;
- }
-
- // all redundant for now ... could be an optimization at some point...
-
- // Ctors in a non-static inner type have an 'extra parameter' of the enclosing type.
- // If skippableDescriptorPrefix gets set here then it is set to the descriptor portion
- // for this 'extra parameter'. For an inner class of pkg.Foo the skippable descriptor
- // prefix will be '(Lpkg/Foo;' - so later when comparing <init> methods we know what to
- // compare.
- // IF THIS CODE NEEDS TO GET MORE COMPLICATED, I THINK ITS WORTH RIPPING IT ALL OUT AND
- // CREATING THE STRUCTURAL CHANGES OBJECT BASED ON CLASSREADER OUTPUT RATHER THAN
- // THE RESOLVEDTYPE - THEN THERE WOULD BE NO NEED TO TREAT SOME METHODS IN A PECULIAR
- // WAY.
- // char[] skippableDescriptorPrefix = null;
- // char[] enclosingTypeName = reader.getEnclosingTypeName();
- // boolean isStaticType = Modifier.isStatic(reader.getModifiers());
- // if (!isStaticType && enclosingTypeName!=null) {
- // StringBuffer sb = new StringBuffer();
- // sb.append("(L").append(new String(enclosingTypeName)).append(";");
- // skippableDescriptorPrefix = sb.toString().toCharArray();
- // }
- //
- //
- // // remove the aspectOf, hasAspect, clinit and ajc$XXX methods
- // // from those we compare with the existing methods - bug 129163
- // List nonGenMethods = new ArrayList();
- // for (int i = 0; i < newMethods.length; i++) {
- // IBinaryMethod method = newMethods[i];
- // // if ((method.getModifiers() & 0x1000)!=0) continue; // 0x1000 => synthetic - will cause us to skip access$0 - is this
- // always safe?
- // char[] methodName = method.getSelector();
- // // if (!CharOperation.equals(methodName,NameMangler.METHOD_ASPECTOF) &&
- // // !CharOperation.equals(methodName,NameMangler.METHOD_HASASPECT) &&
- // // !CharOperation.equals(methodName,NameMangler.STATIC_INITIALIZER) &&
- // // !CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,methodName) &&
- // // !CharOperation.prefixEquals(NameMangler.CLINIT,methodName)) {
- // nonGenMethods.add(method);
- // // }
- // }
- IBinaryMethod[] existingMs = existingType.binMethods;
- if (newMethods.length != existingMs.length) {
- logAnalysis(filename,"number of methods changed. old="+stringify(existingMs)+" new="+stringify(newMethods));
- return true;
- }
- new_method_loop:
- for (IBinaryMethod method : newMethods) {
- char[] methodName = method.getSelector();
- for (IBinaryMethod existingM : existingMs) {
- if (CharOperation.equals(existingM.getSelector(), methodName)) {
- // candidate match
- if (!CharOperation.equals(method.getMethodDescriptor(), existingM.getMethodDescriptor())) {
- // ok, the descriptors don't match, but is this a funky ctor on a non-static inner
- // type?
- // boolean mightBeOK =
- // skippableDescriptorPrefix!=null && // set for inner types
- // CharOperation.equals(methodName,NameMangler.INIT) && // ctor
- // CharOperation.prefixEquals(skippableDescriptorPrefix,method.getMethodDescriptor()); // checking for
- // prefix on the descriptor
- // if (mightBeOK) {
- // // OK, so the descriptor starts something like '(Lpkg/Foo;' - we now may need to look at the rest of the
- // // descriptor if it takes >1 parameter.
- // // eg. could be (Lpkg/C;Ljava/lang/String;) where the skippablePrefix is (Lpkg/C;
- // char [] md = method.getMethodDescriptor();
- // char[] remainder = CharOperation.subarray(md, skippableDescriptorPrefix.length, md.length);
- // if (CharOperation.equals(remainder,BRACKET_V)) continue new_method_loop; // no other parameters to worry
- // about
- // char[] comparableSig = CharOperation.subarray(existingMethods[j].signature, 1,
- // existingMethods[j].signature.length);
- // boolean match = CharOperation.equals(comparableSig, remainder);
- // if (match) continue new_method_loop;
- // }
- continue; // might be overloading
- } else {
- // matching sigs
- IBinaryMethod existing = existingM;
- if (!modifiersEqual(method.getModifiers(), existing.getModifiers())) {
- logAnalysis(filename, "method modifiers changed '" + existing + "' old=0x" + Integer.toHexString(existing.getModifiers()) + " new=0x" + Integer.toHexString(method.getModifiers()));
- return true;
- }
-
- if (exceptionClausesDiffer(existing, method)) {
- logAnalysis(filename, "method exception clauses changed '" + existing + "' old=" + existing + " new=" + method);
- return true;
- }
-
- char[] existingGSig = existing.getGenericSignature();
- char[] methodGSig = method.getGenericSignature();
- if ((existingGSig == null && methodGSig != null) || (existingGSig != null && methodGSig == null)) {
- logAnalysis(filename, "method generic sig changed '" + existing + "' old=" +
- (existingGSig == null ? "null" : new String(existingGSig)) + " new=" + (methodGSig == null ? "null" : new String(methodGSig)));
- return true;
- }
- if (existingGSig != null) {
- if (!CharOperation.equals(existingGSig, methodGSig)) {
- logAnalysis(filename, "method generic sig changed '" + existing + "' old=" +
- (existingGSig == null ? "null" : new String(existingGSig)) + " new=" + (methodGSig == null ? "null" : new String(methodGSig)));
- return true;
- }
- }
-
- continue new_method_loop;
- }
- }
- // TODO missing a return true here? Meaning we have a field in the new that we can't find in the old!
- }
-
- logAnalysis(filename, "method changed. New method detected '" + stringify(method) + "' (might be a rename)");
- return true; // (no match found)
- }
-
- // check for differences in inner types
- // TODO could make order insensitive
- IBinaryNestedType[] binaryNestedTypes = reader.getMemberTypes();
- IBinaryNestedType[] existingBinaryNestedTypes = existingType.getMemberTypes();
- if ((binaryNestedTypes == null && existingBinaryNestedTypes != null)
- || (binaryNestedTypes != null && existingBinaryNestedTypes == null)) {
- logAnalysis(filename,"nested types changed");
- return true;
- }
- if (binaryNestedTypes != null) {
- int bnLength = binaryNestedTypes.length;
- if (existingBinaryNestedTypes.length != bnLength) {
- logAnalysis(filename,"nested types changed. old="+stringify(existingBinaryNestedTypes)+" new="+stringify(binaryNestedTypes));
- return true;
- }
- for (int m = 0; m < bnLength; m++) {
- IBinaryNestedType bnt = binaryNestedTypes[m];
- IBinaryNestedType existingBnt = existingBinaryNestedTypes[m];
- if (!CharOperation.equals(bnt.getName(), existingBnt.getName())) {
- logAnalysis(filename,"nested type changed name at position "+m+" old="+stringify(existingBinaryNestedTypes)+" new="+stringify(binaryNestedTypes));
- return true;
- }
- }
- }
- return false;
- }
-
- private String stringify(char[] chars) {
- if (chars == null) {
- return "null";
- }
- return new String(chars);
- }
-
- private String stringify(IBinaryNestedType[] binaryNestedTypes) {
- StringBuilder buf = new StringBuilder();
- for (IBinaryNestedType binaryNestedType: binaryNestedTypes) {
- buf.append(binaryNestedType).append(" ");
- }
- return buf.toString().trim();
- }
-
- private String stringify(IBinaryMethod[] methods) {
- StringBuilder buf = new StringBuilder();
- for (IBinaryMethod method: methods) {
- buf.append(stringify(method)).append(" ");
- }
- return "["+buf.toString().trim()+"]";
- }
-
- private String stringify(IBinaryMethod m) {
- StringBuilder buf = new StringBuilder();
- buf.append("0x").append(Integer.toHexString(m.getModifiers())).append(" ");
- buf.append(m.getSelector()).append(m.getMethodDescriptor());
- // IBinaryAnnotation[] annos = m.getAnnotations();
- // TODO include annotations, generic sig, etc
- return buf.toString().trim();
- }
-
- private String stringify(IBinaryField[] fields) {
- StringBuilder buf = new StringBuilder();
- for (IBinaryField field: fields) {
- buf.append(stringify(field)).append(" ");
- }
- return "["+buf.toString().trim()+"]";
- }
-
- private Object stringify(IBinaryField f) {
- StringBuilder buf = new StringBuilder();
- buf.append("0x").append(Integer.toHexString(f.getModifiers())).append(" ");
- buf.append(f.getTypeName()).append(f.getName());
- return buf.toString().trim();
- }
-
- private String stringify(char[][] arrayOfCharArrays) {
- StringBuilder buf = new StringBuilder();
- for (char[] charArray: arrayOfCharArrays) {
- buf.append(charArray).append(" ");
- }
- return buf.toString().trim();
- }
-
- private String stringify(IBinaryAnnotation[] annotations) {
- StringBuilder buf = new StringBuilder();
- for (IBinaryAnnotation anno: annotations) {
- buf.append(anno).append(" ");
- }
- return buf.toString().trim();
- }
-
- /**
- * For two methods, discover if there has been a change in the exception types specified.
- *
- * @return true if the exception types have changed
- */
- private boolean exceptionClausesDiffer(IBinaryMethod lastMethod, IBinaryMethod newMethod) {
- char[][] previousExceptionTypeNames = lastMethod.getExceptionTypeNames();
- char[][] newExceptionTypeNames = newMethod.getExceptionTypeNames();
- int pLength = previousExceptionTypeNames.length;
- int nLength = newExceptionTypeNames.length;
- if (pLength != nLength) {
- return true;
- }
- if (pLength == 0) {
- return false;
- }
- // TODO could be insensitive to an order change
- for (int i = 0; i < pLength; i++) {
- if (!CharOperation.equals(previousExceptionTypeNames[i], newExceptionTypeNames[i])) {
- return true;
- }
- }
- return false;
- }
-
- private boolean modifiersEqual(int eclipseModifiers, int resolvedTypeModifiers) {
- resolvedTypeModifiers = resolvedTypeModifiers & ExtraCompilerModifiers.AccJustFlag;
- eclipseModifiers = eclipseModifiers & ExtraCompilerModifiers.AccJustFlag;
- // if ((eclipseModifiers & CompilerModifiers.AccSuper) != 0) {
- // eclipseModifiers -= CompilerModifiers.AccSuper;
- // }
- return (eclipseModifiers == resolvedTypeModifiers);
- }
-
- // private static StringSet makeStringSet(List strings) {
- // StringSet ret = new StringSet(strings.size());
- // for (Iterator iter = strings.iterator(); iter.hasNext();) {
- // String element = (String) iter.next();
- // ret.add(element);
- // }
- // return ret;
- // }
-
- private String stringifySet(Set<?> l) {
- StringBuilder sb = new StringBuilder();
- sb.append("{");
- for (Iterator<?> iter = l.iterator(); iter.hasNext();) {
- Object el = iter.next();
- sb.append(el);
- if (iter.hasNext()) {
- sb.append(",");
- }
- }
- sb.append("}");
- return sb.toString();
- }
-
- protected void addAffectedSourceFiles(Set<File> addTo, Set<File> lastTimeSources) {
- if (qualifiedStrings.elementSize == 0 && simpleStrings.elementSize == 0) {
- return;
- }
- if (listenerDefined()) {
- getListener().recordDecision(
- "Examining whether any other files now need compilation based on just compiling: '"
- + stringifySet(lastTimeSources) + "'");
- }
- // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
- char[][][] qualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedStrings);
- // if a well known qualified name was found then we can skip over these
- if (qualifiedNames.length < qualifiedStrings.elementSize) {
- qualifiedNames = null;
- }
- char[][] simpleNames = ReferenceCollection.internSimpleNames(simpleStrings, true);
- // if a well known name was found then we can skip over these
- if (simpleNames.length < simpleStrings.elementSize) {
- simpleNames = null;
- }
-
- // System.err.println("simple: " + simpleStrings);
- // System.err.println("qualif: " + qualifiedStrings);
-
- for (Map.Entry<File, ReferenceCollection> entry : references.entrySet()) {
- ReferenceCollection refs = entry.getValue();
- if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
- File file = entry.getKey();
- if (file.exists()) {
- if (!lastTimeSources.contains(file)) { // ??? O(n**2)
- if (listenerDefined()) {
- getListener().recordDecision("Need to recompile '" + file.getName().toString() + "'");
- }
- addTo.add(file);
- }
- }
- }
- }
- // add in the things we compiled previously - I know that seems crap but otherwise we may pull woven
- // stuff off disk (since we no longer have UnwovenClassFile objects) in order to satisfy references
- // in the new files we are about to compile (see pr133532)
- if (addTo.size() > 0) {
- addTo.addAll(lastTimeSources);
- }
- // // XXX Promote addTo to a Set - then we don't need this rubbish? but does it need to be ordered?
- // if (addTo.size()>0) {
- // for (Iterator iter = lastTimeSources.iterator(); iter.hasNext();) {
- // Object element = (Object) iter.next();
- // if (!addTo.contains(element)) addTo.add(element);
- // }
- // }
-
- qualifiedStrings.clear();
- simpleStrings.clear();
- }
-
- /**
- * Record that a particular type has been touched during a compilation run. Information is used to ensure any types depending
- * upon this one are also recompiled.
- *
- * @param typename (possibly qualified) type name
- */
- protected void recordTypeChanged(String typename) {
- int lastDot = typename.lastIndexOf('.');
- String typeName;
- if (lastDot != -1) {
- String packageName = typename.substring(0, lastDot).replace('.', '/');
- qualifiedStrings.add(packageName);
- typeName = typename.substring(lastDot + 1);
- } else {
- qualifiedStrings.add("");
- typeName = typename;
- }
-
- int memberIndex = typeName.indexOf('$');
- if (memberIndex > 0) {
- typeName = typeName.substring(0, memberIndex);
- }
- simpleStrings.add(typeName);
- }
-
- /**
- * Record some additional dependencies between types. When any of the types specified in fullyQualifiedTypeNames changes, we
- * need to recompile the file named in the CompilationResult. This method patches that information into the existing data
- * structures.
- */
- public boolean recordDependencies(File file, String[] typeNameDependencies) {
- try {
- File sourceFile = new File(new String(file.getCanonicalPath()));
- ReferenceCollection existingCollection = references.get(sourceFile);
- if (existingCollection != null) {
- existingCollection.addDependencies(typeNameDependencies);
- return true;
- } else {
- ReferenceCollection rc = new ReferenceCollection(null, null, null);
- rc.addDependencies(typeNameDependencies);
- references.put(sourceFile, rc);
- return true;
- }
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- return false;
- }
-
- protected void addDependentsOf(File sourceFile) {
- List<ClassFile> cfs = this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(sourceFile);
- if (cfs != null) {
- for (ClassFile cf : cfs) {
- recordTypeChanged(cf.fullyQualifiedTypeName);
- }
- }
- }
-
- public void setStructureModel(AsmManager structureModel) {
- this.structureModel = structureModel;
- }
-
- public AsmManager getStructureModel() {
- return structureModel;
- }
-
- public void setWeaver(BcelWeaver bw) {
- weaver = bw;
- }
-
- public BcelWeaver getWeaver() {
- return weaver;
- }
-
- public void setWorld(BcelWorld bw) {
- world = bw;
- world.addTypeDelegateResolver(this);
- }
-
- public BcelWorld getBcelWorld() {
- return world;
- }
-
- //
- // public void setRelationshipMap(IRelationshipMap irm) {
- // relmap = irm;
- // }
- //
- // public IRelationshipMap getRelationshipMap() {
- // return relmap;
- // }
-
- public int getNumberOfStructuralChangesSinceLastFullBuild() {
- return structuralChangesSinceLastFullBuild.size();
- }
-
- /** Returns last time we did a full or incremental build. */
- public long getLastBuildTime() {
- return lastSuccessfulBuildTime;
- }
-
- /** Returns last time we did a full build */
- public long getLastFullBuildTime() {
- return lastSuccessfulFullBuildTime;
- }
-
- /**
- * @return Returns the buildConfig.
- */
- public AjBuildConfig getBuildConfig() {
- return this.buildConfig;
- }
-
- public void clearBinarySourceFiles() {
- this.binarySourceFiles = new HashMap<>();
- }
-
- public void recordBinarySource(String fromPathName, List<UnwovenClassFile> unwovenClassFiles) {
- this.binarySourceFiles.put(fromPathName, unwovenClassFiles);
- if (this.maybeIncremental()) {
- List<ClassFile> simpleClassFiles = new LinkedList<>();
- for (UnwovenClassFile ucf : unwovenClassFiles) {
- ClassFile cf = getClassFileFor(ucf);
- simpleClassFiles.add(cf);
- }
- this.inputClassFilesBySource.put(fromPathName, simpleClassFiles);
- }
- }
-
- /**
- * @param ucf
- * @return
- */
- private ClassFile getClassFileFor(UnwovenClassFile ucf) {
- return new ClassFile(ucf.getClassName(), new File(ucf.getFilename()));
- }
-
- public Map<String, List<UnwovenClassFile>> getBinarySourceMap() {
- return this.binarySourceFiles;
- }
-
- public Map<String, File> getClassNameToFileMap() {
- return this.classesFromName;
- }
-
- public boolean hasResource(String resourceName) {
- return this.resources.keySet().contains(resourceName);
- }
-
- public void recordResource(String resourceName, File resourceSourceLocation) {
- this.resources.put(resourceName, resourceSourceLocation);
- }
-
- /**
- * @return Returns the addedFiles.
- */
- public Set<File> getAddedFiles() {
- return this.addedFiles;
- }
-
- /**
- * @return Returns the deletedFiles.
- */
- public Set<File> getDeletedFiles() {
- return this.deletedFiles;
- }
-
- public void forceBatchBuildNextTimeAround() {
- this.batchBuildRequiredThisTime = true;
- }
-
- public boolean requiresFullBatchBuild() {
- return this.batchBuildRequiredThisTime;
- }
-
- private static class ClassFile {
- public String fullyQualifiedTypeName;
- public File locationOnDisk;
-
- public ClassFile(String fqn, File location) {
- this.fullyQualifiedTypeName = fqn;
- this.locationOnDisk = location;
- }
-
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("ClassFile(type=").append(fullyQualifiedTypeName).append(",location=").append(locationOnDisk).append(")");
- return s.toString();
- }
-
- public void deleteFromFileSystem(AjBuildConfig buildConfig) {
- String namePrefix = locationOnDisk.getName();
- namePrefix = namePrefix.substring(0, namePrefix.lastIndexOf('.'));
- final String targetPrefix = namePrefix + BcelWeaver.CLOSURE_CLASS_PREFIX;
- File dir = locationOnDisk.getParentFile();
- if (dir != null) {
- File[] weaverGenerated = dir.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.startsWith(targetPrefix);
- }
- });
- if (weaverGenerated != null) {
- for (File file : weaverGenerated) {
- file.delete();
- if (buildConfig != null && buildConfig.getCompilationResultDestinationManager() != null) {
- buildConfig.getCompilationResultDestinationManager().reportFileRemove(file.getPath(),
- CompilationResultDestinationManager.FILETYPE_CLASS);
- }
- }
- }
- }
- locationOnDisk.delete();
- if (buildConfig != null && buildConfig.getCompilationResultDestinationManager() != null) {
- buildConfig.getCompilationResultDestinationManager().reportFileRemove(locationOnDisk.getPath(),
- CompilationResultDestinationManager.FILETYPE_CLASS);
- }
- }
- }
-
- public void wipeAllKnowledge() {
- buildManager.state = null;
- // buildManager.setStructureModel(null);
- }
-
- public Map<String, char[]> getAspectNamesToFileNameMap() {
- return aspectsFromFileNames;
- }
-
- public void initializeAspectNamesToFileNameMap() {
- this.aspectsFromFileNames = new HashMap<>();
- }
-
- // Will allow us to record decisions made during incremental processing, hopefully aid in debugging
- public boolean listenerDefined() {
- return stateListener != null;
- }
-
- public IStateListener getListener() {
- return stateListener;
- }
-
- public IBinaryType checkPreviousBuild(String name) {
- return resolvedTypeStructuresFromLastBuild.get(name);
- }
-
- public AjBuildManager getAjBuildManager() {
- return buildManager;
- }
-
- public INameEnvironment getNameEnvironment() {
- return this.nameEnvironment;
- }
-
- public void setNameEnvironment(INameEnvironment nameEnvironment) {
- this.nameEnvironment = nameEnvironment;
- }
-
- public FileSystem getFileSystem() {
- return this.fileSystem;
- }
-
- public void setFileSystem(FileSystem fileSystem) {
- this.fileSystem = fileSystem;
- }
-
- /**
- * 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
- * an aspect and know to do more than just a tiny incremental build. <br>
- * TODO but this doesn't allow for a new aspect created on the aspectpath?
- *
- * @param aspectFile path to the file, eg. c:/temp/foo/Fred.class
- */
- public void recordAspectClassFile(String aspectFile) {
- aspectClassFiles.add(aspectFile);
- }
-
- public void write(CompressingDataOutputStream dos) throws IOException {
- // weaver
- weaver.write(dos);
- // world
- // model
- // local state
- }
-
- /**
- * See if we can create a delegate from a CompactTypeStructure - TODO better comment
- */
- @Override
- public ReferenceTypeDelegate getDelegate(ReferenceType referenceType) {
- File f = classesFromName.get(referenceType.getName());
- if (f == null) {
- return null; // not heard of it
- }
- try {
- ClassParser parser = new ClassParser(f.toString());
- return world.buildBcelDelegate(referenceType, parser.parse(), true, false);
- } catch (IOException e) {
- IMessage msg = new Message("Failed to recover " + referenceType, referenceType.getDelegate()!=null?referenceType.getSourceLocation():null, false);
- buildManager.handler.handleMessage(msg);
- }
- return null;
- }
- }
|