public void taintPackagesDependingOnChangedClasspathPackages ( ) throws IOException { // 1. Collect fully qualified names of all interesting classpath dependencies Set < String > fqDependencies = new HashSet <> ( ) ; for ( Package pkg : prev . packages ( ) . values ( ) ) { // Check if this package was compiled. If it's presence is recorded // because it was on the class path and we needed to save it's // public api, it's not a candidate for tainting. if ( pkg . sources ( ) . isEmpty ( ) ) continue ; pkg . typeClasspathDependencies ( ) . values ( ) . forEach ( fqDependencies :: addAll ) ; } // 2. Extract the public APIs from the on disk .class files // (Reason for doing step 1 in a separate phase is to avoid extracting // public APIs of the same class twice.) PubApiExtractor pubApiExtractor = new PubApiExtractor ( options ) ; Map < String , PubApi > onDiskPubApi = new HashMap <> ( ) ; for ( String cpDep : fqDependencies ) { onDiskPubApi . put ( cpDep , pubApiExtractor . getPubApi ( cpDep ) ) ; } pubApiExtractor . close ( ) ; // 3. Compare them with the public APIs as of last compilation (loaded from javac_state) nextPkg : for ( Package pkg : prev . packages ( ) . values ( ) ) { // Check if this package was compiled. If it's presence is recorded // because it was on the class path and we needed to save it's // public api, it's not a candidate for tainting. if ( pkg . sources ( ) . isEmpty ( ) ) continue ; Set < String > cpDepsOfThisPkg = new HashSet <> ( ) ; for ( Set < String > cpDeps : pkg . typeClasspathDependencies ( ) . values ( ) ) cpDepsOfThisPkg . addAll ( cpDeps ) ; for ( String fqDep : cpDepsOfThisPkg ) { String depPkg = ":" + fqDep . substring ( 0 , fqDep . lastIndexOf ( ' ' ) ) ; PubApi prevPkgApi = prev . packages ( ) . get ( depPkg ) . getPubApi ( ) ; // This PubApi directly lists the members of the class, // i.e. [ MEMBER1, MEMBER2, ... ] PubApi prevDepApi = prevPkgApi . types . get ( fqDep ) . pubApi ; // In order to dive *into* the class, we need to add // .types.get(fqDep).pubApi below. PubApi currentDepApi = onDiskPubApi . get ( fqDep ) . types . get ( fqDep ) . pubApi ; if ( ! currentDepApi . isBackwardCompatibleWith ( prevDepApi ) ) { List < String > apiDiff = currentDepApi . diff ( prevDepApi ) ; taintPackage ( pkg . name ( ) , "depends on classpath " + "package which has an updated package api: " + String . join ( "\n" , apiDiff ) ) ; //Log.debug("========================================"); //Log.debug("------ PREV API ------------------------"); //prevDepApi.asListOfStrings().forEach(Log::debug); //Log.debug("------ CURRENT API ---------------------"); //currentDepApi.asListOfStrings().forEach(Log::debug); //Log.debug("========================================"); continue nextPkg ; } } } }
Compare the javac_state recorded public apis of packages on the classpath with the actual public apis on the classpath .