// $Id: incrmnt.cpp,v 1.30 2004/03/25 13:32:27 ericb Exp $ // // This software is subject to the terms of the IBM Jikes Compiler // License Agreement available at the following URL: // http://ibm.com/developerworks/opensource/jikes. // Copyright (C) 1996, 2004 IBM Corporation and others. All Rights Reserved. // You must accept the terms of that agreement to use this software. // #include "platform.h" #include "control.h" #include "scanner.h" #include "parser.h" #include "semantic.h" #include "case.h" #include "set.h" #ifdef HAVE_JIKES_NAMESPACE namespace Jikes { // Open namespace Jikes block #endif void Control::RemoveTrashedTypes(SymbolSet& type_trash_set) { TypeSymbol* type; // // For each type T that is going to be trashed, and for each parent P of T // that is not itself being trashed, remove T from the set of dependents of // P. If T is a subtype of P it is also removed from the subtypes set. // for (type = (TypeSymbol*) type_trash_set.FirstElement(); type; type = (TypeSymbol*) type_trash_set.NextElement()) { TypeSymbol* parent; for (parent = (TypeSymbol*) type -> static_parents -> FirstElement(); parent; parent = (TypeSymbol*) type -> static_parents -> NextElement()) { if (! type_trash_set.IsElement(parent)) { parent -> dependents -> RemoveElement(type); parent -> subtypes -> RemoveElement(type); } } for (parent = (TypeSymbol*) type -> parents -> FirstElement(); parent; parent = (TypeSymbol*) type -> parents -> NextElement()) { if (! type_trash_set.IsElement(parent)) { parent -> dependents -> RemoveElement(type); parent -> subtypes -> RemoveElement(type); } } } // // We can now safely delete the type. // for (type = (TypeSymbol*) type_trash_set.FirstElement(); type; type = (TypeSymbol*) type_trash_set.NextElement()) { PackageSymbol* package = type -> ContainingPackage(); // // If a type that is about to be trashed was read in via a class file, // remove the class file. Note that invoking RemoveElement for a file // that it does not contain has no ill effect. // FileSymbol* file_symbol = type -> file_symbol; if (file_symbol && type -> Identity() == file_symbol -> Identity()) input_class_file_set.RemoveElement(file_symbol); // // If a type that is about to be trashed was contained in the // unnamed_package, remove it from the set "unnamed_package_types" // if (package == unnamed_package) unnamed_package_types.RemoveElement(type); // // Remove the type from its containing package. // package -> DeleteTypeSymbol(type); } } inline DirectoryEntry* Control::FindInputFile(FileSymbol* file_symbol) { int length = file_symbol -> Utf8NameLength() + FileSymbol::java_suffix_length; char* java_name = new char[length + 1]; // +1 for '\0' strcpy(java_name, file_symbol -> Utf8Name()); strcat(java_name, FileSymbol::java_suffix); DirectoryEntry* java_entry = file_symbol -> directory_symbol -> FindEntry(java_name, length); delete [] java_name; return java_entry; } // // For each file whose associated source (".java") has changed, add it to the // list to be recompiled... // void Control::FindMoreRecentInputFiles(SymbolSet& file_candidates) { FileSymbol* file_symbol; for (file_symbol = (FileSymbol*) file_candidates.FirstElement(); file_symbol; file_symbol = (FileSymbol*) file_candidates.NextElement()) { // // If the type is not zipped and it is not already contained in the // recompilation set, then check it... // if ((! file_symbol -> IsZip()) && (! recompilation_file_set.IsElement(file_symbol)) && (! expired_file_set.IsElement(file_symbol))) { // // If there is no java source file or its time stamp is not newer // than file_symbol then reset file_symbol to NULL. Otherwise, // reset file symbol to the newer file. // DirectoryEntry* java_entry = FindInputFile(file_symbol); if (! java_entry) { // A source file that was compiled in the previous pass no // longer exists. if (file_symbol -> IsJava()) expired_file_set.AddElement(file_symbol); } else if (java_entry -> Mtime() > file_symbol -> mtime) { // A newer file was found. file_symbol -> mtime = java_entry -> Mtime(); recompilation_file_set.AddElement(file_symbol); } } } } void Control::RereadDirectory(DirectorySymbol* directory_symbol) { directory_symbol -> ResetDirectory(); for (unsigned i = 0; i < directory_symbol -> subdirectories.Length(); i++) RereadDirectory(directory_symbol -> subdirectories[i]); } void Control::RereadDirectories() { for (unsigned i = (dot_classpath_index == 0 ? 0 : 1); i < classpath.Length(); i++) { PathSymbol* path_symbol = classpath[i]; if (! path_symbol -> IsZip()) RereadDirectory(path_symbol -> RootDirectory()); } } void Control::ComputeRecompilationSet(TypeDependenceChecker& dependence_checker) { SymbolSet type_trash_set; // // Find out if any source files has been touched since the last // compilation and add all such files to recompilation_file_set. // FindMoreRecentInputFiles(dependence_checker.file_set); // // Before messing with the files, compute a list of all the types that // have just been compiled. We need to do this here as we will be // "Resetting" and "reScanning" some files in the loop below, which in // effect removes the set of types to which they were associated in the // previous compilation. // int length_estimate = input_java_file_set.Size(); // problem size estimate Tuple input_types(length_estimate * 2); FileSymbol* file_symbol; for (file_symbol = (FileSymbol*) input_java_file_set.FirstElement(); file_symbol; file_symbol = (FileSymbol*) input_java_file_set.NextElement()) { for (unsigned i = 0; i < file_symbol -> types.Length(); i++) input_types.Next() = file_symbol -> types[i]; } // // Declare the closure set, and initialize it with the Union over the // closure of the types in the trash_bin. Essentially, we want to catch // all "compiled" types in the compilation that has a dependence on these // bad types. // SymbolSet dependents_closure(length_estimate); for (unsigned i = 0; i < type_trash_bin.Length(); i++) { TypeSymbol* type = type_trash_bin[i]; if (! dependents_closure.IsElement(type)) { if (type -> dependents_closure) dependents_closure.Union(*type -> dependents_closure); else dependents_closure.AddElement(type); } } // // Compute the set of types from the recompilation set that needs to be // recompiled and update the recompilation file set. // SymbolSet new_set(length_estimate), file_seen(length_estimate); new_set = recompilation_file_set; new_set.Union(expired_file_set); file_seen = new_set; // How much space do we need for a package declaration? estimate 64 tokens. StoragePool* ast_pool = new StoragePool(64); // // As long as there is a new_set of files to process,... // do { // // For each file in new_set, compute the reflexive transitive closure // of all types contained in that file. Next, reset and rescan the // file. If the scan was successful, iterate over the new list of // types to see if any of them had already been introduced in the // previous compilation via a class file. If so, add all such types to // the dependents closure. // for (FileSymbol* file_symbol = (FileSymbol*) new_set.FirstElement(); file_symbol; file_symbol = (FileSymbol*) new_set.NextElement()) { for (unsigned i = 0; i < file_symbol -> types.Length(); i++) { TypeSymbol* type = file_symbol -> types[i]; if (! dependents_closure.IsElement(type)) { if (type -> dependents_closure) dependents_closure.Union(*type -> dependents_closure); else dependents_closure.AddElement(type); } } if (! expired_file_set.IsElement(file_symbol)) { file_symbol -> Reset(); file_symbol -> SetJava(); scanner -> Scan(file_symbol); LexStream* lex_stream = file_symbol -> lex_stream; if (lex_stream) // did we have a successful scan! { AstPackageDeclaration* package_declaration = parser -> PackageHeaderParse(lex_stream, ast_pool); PackageSymbol* package = (package_declaration ? FindOrInsertPackage(lex_stream, package_declaration -> name) : unnamed_package); ast_pool -> Reset(); // // If the file contained more than one type, only the main // one would have been deleted. We now delete the others if // any... // for (unsigned k = 0; k < lex_stream -> NumTypes(); k++) { TokenIndex identifier_token = lex_stream -> Next(lex_stream -> Type(k)); NameSymbol* name_symbol = lex_stream -> NameSymbol(identifier_token); if (name_symbol) { TypeSymbol* type = package -> FindTypeSymbol(name_symbol); if (type && (! dependents_closure.IsElement(type))) { if (type -> dependents_closure) dependents_closure.Union(*type -> dependents_closure); else dependents_closure.AddElement(type); } } } } } } // // Iterate over the dependents_closure set. For each type T, add it to // the trash pile. If the file with which it is associated had not yet // been processed, mark it as having been "seen" and add it to the // new_set to be considered later. If the file had already been // processed but not yet added to the recompilation set, add it to the // recompilation set, read it in and if it contains types other than // the main one (that had previously been read in via class files) add // those new types to the trash pile. // new_set.SetEmpty(); TypeSymbol* type; for (type = (TypeSymbol*) dependents_closure.FirstElement(); type; type = (TypeSymbol*) dependents_closure.NextElement()) { type_trash_set.AddElement(type); FileSymbol* file_symbol = type -> file_symbol; if (file_symbol && (! file_seen.IsElement(file_symbol))) { file_seen.AddElement(file_symbol); new_set.AddElement(file_symbol); file_symbol -> mtime = 0; // to force a reread of the file. } } // // Check that the files in new_set exist, and if so, add them to the // recompilation_file_set. Note that if they exist, they will be added // because before a file is added to new_set its time stamp is reset // to 0. See loop above... // FindMoreRecentInputFiles(new_set); // // Empty out the dependents_closure set for the next round. // dependents_closure.SetEmpty(); } while (! new_set.IsEmpty()); delete ast_pool; // // Clean up the types that were compiled in the previous compilation pass. // for (unsigned j = 0; j < input_types.Length(); j++) input_types[j] -> RemoveCompilationReferences(); // // Reset the closure sets in all the types that were considered in the // dependence checker. // Tuple& type_list = dependence_checker.TypeList(); for (unsigned k = 0; k < type_list.Length(); k++) { TypeSymbol* type = type_list[k]; type -> index = TypeCycleChecker::OMEGA; type -> unit_index = TypeCycleChecker::OMEGA; type -> incremental_index = TypeCycleChecker::OMEGA; delete type -> dependents_closure; type -> dependents_closure = NULL; } // // Remove all dependence edges that are no longer valid. // RemoveTrashedTypes(type_trash_set); } // // Check whether or not there are files to be recompiled. // bool Control::IncrementalRecompilation() { // // Empty out the type lookup table so that it does not continue // to point to a type that is deleted here. // type_table.SetEmpty(); SymbolSet candidates(input_java_file_set.Size() + input_class_file_set.Size() + recompilation_file_set.Size()); if (! recompilation_file_set.IsEmpty()) candidates = recompilation_file_set; else { Ostream out; out.StandardOutput(); out << endl << "Incremental: Enter to continue or q + Enter to quit: " << flush; char ch; // See if the user types Q or presses enter/escape or sends an EOF while (1) { cin.get(ch); if (cin.eof() || (ch == U_q) || (ch == U_Q)) { return false; } if ((ch == U_ESCAPE) || (ch == U_LINE_FEED)) { break; } } candidates = input_java_file_set; candidates.Union(input_class_file_set); } if (!candidates.IsEmpty()) { TypeDependenceChecker dependence_checker(this, candidates, type_trash_bin); dependence_checker.PartialOrder(); // // Compute the initial set of files that need to be recompiled. Place // them in recompilation_file_set. // RereadDirectories(); ComputeRecompilationSet(dependence_checker); } // // Starting with the initial recompilation_file_set, complete the // computation of the set of files that need to be recompiled. (Add all // new files to recompilation_file_set). Also, complete the computation of // type_trash_set, the set of files that should be removed from the // database as they will be recompiled. // fprintf(stderr, "%s", (recompilation_file_set.IsEmpty() && expired_file_set.IsEmpty() ? "\nnothing changed...\n" : "\nok...\n")); fflush(stderr); return true; } #ifdef HAVE_JIKES_NAMESPACE } // Close namespace Jikes block #endif