Ejemplo n.º 1
0
  /*
   * 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);
        }
      }
    }
  }