/* * Does actual collection of types based on a list of files. */ private void collectTypes( List<File> files, boolean hasMain, Set<String> mustRecompile, Map<String, TreeSet<String>> dependencies) throws ParseException, ShadowException, TypeCheckException, IOException, ConfigurationException { // Create and fill the initial set of files to be checked. TreeSet<String> uncheckedFiles = new TreeSet<String>(); String main = null; // May or may not be null, based on hasMain. if (files.isEmpty()) { throw new ConfigurationException("No files provided for typechecking"); } else if (hasMain) { // Assume the main file is the first and only file. main = stripExtension(files.get(0).getCanonicalPath()); uncheckedFiles.add(main); } else { for (File file : files) { String path = stripExtension(file.getCanonicalPath()); uncheckedFiles.add(path); } } FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".shadow"); } }; /* Add standard imports. */ File standard = new File(config.getSystemImport().toFile(), "shadow" + File.separator + "standard"); if (!standard.exists()) throw new ConfigurationException( "Invalid path to shadow:standard: " + standard.getCanonicalPath()); TreeSet<String> standardDependencies = new TreeSet<String>(); File[] imports = standard.listFiles(filter); for (File file : imports) { String name = stripExtension(file.getCanonicalPath()); uncheckedFiles.add(name); standardDependencies.add(name); } /* Add io imports (necessary for console programs). */ File io = new File(config.getSystemImport().toFile(), "shadow" + File.separator + "io"); if (!io.exists()) throw new ConfigurationException("Invalid path to shadow:io: " + io.getCanonicalPath()); imports = standard.listFiles(filter); for (File file : imports) { String name = stripExtension(file.getCanonicalPath()); uncheckedFiles.add(name); standardDependencies.add(name); } /* As long as there are unchecked files, remove one and process it. */ while (!uncheckedFiles.isEmpty()) { String canonical = uncheckedFiles.first(); uncheckedFiles.remove(canonical); File canonicalFile = new File(canonical + ".shadow"); // Depending on the circumstances, the compiler may choose to either // compile/recompile source files, or rely on existing binaries/IR. if (canonicalFile.exists()) { File meta = new File(canonical + ".meta"); File llvm = new File(canonical + ".ll"); // If source compilation was not requested and the binaries exist // that are newer than the source, use those binaries. if (!useSourceFiles && !mustRecompile.contains(canonical) && meta.exists() && meta.lastModified() >= canonicalFile.lastModified() && llvm.exists() && llvm.lastModified() >= meta.lastModified()) canonicalFile = meta; else mustRecompile.add(canonical); } else if (!useSourceFiles) canonicalFile = new File(canonical + ".meta"); ShadowParser parser = new ShadowFileParser(canonicalFile); currentFile = canonicalFile; Node node = parser.CompilationUnit(); // Make another collector to walk the current file. TypeCollector collector = new TypeCollector(new Package(), useSourceFiles); // Keeping a current files gives us a file whose directory we can check against. collector.currentFile = currentFile; ASTWalker walker = new ASTWalker(collector); walker.walk(node); if (canonical.equals(main)) mainType = node.getType(); fileTable.put(canonical, node); /* Copy types from other collector into our package tree. */ for (Type type : collector.packageTree) { try { packageTree.addQualifiedPackage(type.getPackage().toString()).addType(type); if (mainType != null && type.getPackage() == packageTree && mainType.getPackage() != packageTree) { // Imported class has default package but the main type doesn't. // The only classes without a package that will be imported will be // in the same directory as the main type. // Implication: classes in the same directory have different packages. String message = "Type " + type + " belongs to the default package, but types defined in the same directory belong to other packages"; addWarning(Error.MISMATCHED_PACKAGE, message); } } catch (PackageException e) { addError(Error.INVALID_PACKAGE, e.getMessage()); } } // Copy errors for the other collector into our error list. if (collector.errorList.size() > 0) errorList.addAll(collector.errorList); // Copy warnings. if (collector.warningList.size() > 0) warningList.addAll(collector.warningList); /* Track the dependencies for this file (if dependencies are being used). * If any of its dependencies need to be recompiled, this file will need * to be recompiled. */ TreeSet<String> dependencySet = null; if (dependencies != null) { dependencySet = new TreeSet<String>(standardDependencies); dependencies.put(canonical, dependencySet); } for (String _import : collector.importList) { if (!fileTable.containsKey(_import)) uncheckedFiles.add(_import); if (dependencySet != null) dependencySet.add(_import); } /* Add files in the directory after imports. */ File[] directoryFiles = canonicalFile.getParentFile().listFiles(filter); for (File file : directoryFiles) { String name = stripExtension(file.getCanonicalPath()); if (!fileTable.containsKey(name)) uncheckedFiles.add(name); if (dependencySet != null) dependencySet.add(name); } /* Copy file table from other collector into our table. */ Map<Type, Node> otherNodeTable = collector.typeTable; for (Type type : otherNodeTable.keySet()) { if (!typeTable.containsKey(type)) { Node otherNode = otherNodeTable.get(type); typeTable.put(type, otherNode); } } } }