Beispiel #1
0
  private void run(String[] programArgs, String expectedOutput, String expectedError)
      throws IOException, ConfigurationException, InterruptedException {

    // Should be initialized at this point by call to Main.run()
    Configuration config = Configuration.getConfiguration();

    Path fullExecutable = config.getSystemImport().resolve(executable);

    List<String> programCommand = new ArrayList<String>();
    programCommand.add(fullExecutable.toAbsolutePath().toString());

    for (String arg : programArgs) programCommand.add(arg);

    Process program = new ProcessBuilder(programCommand).start();

    // regular output
    BufferedReader reader = new BufferedReader(new InputStreamReader(program.getInputStream()));
    BufferedReader errorReader =
        new BufferedReader(new InputStreamReader(program.getErrorStream()));
    StringBuilder builder = new StringBuilder();
    String line;
    do {
      line = reader.readLine();
      if (line != null) builder.append(line).append('\n');
    } while (line != null);
    String output = builder.toString();
    assertEquals(expectedOutput, output);

    // error output
    builder = new StringBuilder();
    do {
      line = errorReader.readLine();
      if (line != null) builder.append(line).append('\n');
    } while (line != null);

    String error = builder.toString();
    assertEquals(expectedError, error);

    program.waitFor(); // keeps program from being deleted while running
  }
Beispiel #2
0
 /**
  * Creates a new <code>TypeCollector</code> with the given tree of packages.
  *
  * @param p package tree
  * @param useSourceFiles if true, always use <tt>.shadow</tt> instead of <tt>.meta</tt> files
  * @throws ConfigurationException
  */
 public TypeCollector(Package p, boolean useSourceFiles) throws ConfigurationException {
   super(p);
   this.useSourceFiles = useSourceFiles;
   config = Configuration.getConfiguration();
 }
Beispiel #3
0
  /*
   * Adds a type or a whole package to the current list of imports.
   */
  public boolean addImport(String name) {
    String separator = File.separator; // Adds some platform independence.
    if (separator.equals("\\")) // Hack for Windows to deal with backslash escaping.
    separator = "\\\\";
    String path = name.replaceAll(":", separator);

    /* Which import paths are used depends on whether the requested package
     * is in the standard library or not. */
    List<Path> importPaths;
    if (path.startsWith("shadow")) {
      importPaths = new ArrayList<Path>();
      importPaths.add(config.getSystemImport());
    } else importPaths = config.getImports();

    boolean success = false;

    if (importPaths != null && importPaths.size() > 0) {
      for (Path importPath : importPaths) {
        // If an import path is relative, resolving it against the
        // current source file will make it absolute.
        // If it's absolute, no change will happen.
        importPath = currentFile.toPath().getParent().resolve(importPath);

        /* No @, must be a whole package import.
         * Add everything in the directory. */
        if (!path.contains("@")) {
          File fullPath = new File(importPath.toFile(), path);
          if (fullPath.isDirectory()) {
            File[] matchingShadow =
                fullPath.listFiles(
                    new FilenameFilter() {
                      @Override
                      public boolean accept(File dir, String name) {
                        return name.endsWith(".shadow");
                      }
                    });

            File[] matchingMeta =
                fullPath.listFiles(
                    new FilenameFilter() {
                      @Override
                      public boolean accept(File dir, String name) {
                        return name.endsWith(".meta");
                      }
                    });

            try {
              for (File file : matchingShadow)
                importList.add(stripExtension(file.getCanonicalPath()));

              for (File file : matchingMeta) {
                String canonicalPath = stripExtension(file.getCanonicalPath());
                if (!importList.contains(canonicalPath)) importList.add(canonicalPath);
              }

              success = true;
            } catch (IOException e) {
            }
          }
        }
        /* Single file import. */
        else {
          File shadowVersion;
          File metaVersion;
          String fixedPath;

          if (path.startsWith("default")) {
            fixedPath = path.replaceFirst("default@", "");
            shadowVersion = new File(currentFile.getParent(), fixedPath + ".shadow");
            metaVersion = new File(currentFile.getParent(), fixedPath + ".meta");
          } else {
            fixedPath = path.replaceAll("@", separator);
            shadowVersion = new File(importPath.toFile(), fixedPath + ".shadow");
            metaVersion = new File(importPath.toFile(), fixedPath + ".meta");
          }

          try {
            if (shadowVersion.exists()) {
              importList.add(stripExtension(shadowVersion.getCanonicalPath()));
              success = true;
            } else if (metaVersion.exists()) {
              importList.add(stripExtension(metaVersion.getCanonicalPath()));
              success = true;
            }
          } catch (IOException e) {
          }
        }

        if (success) return true;
      }
    } else addError(Error.INVALID_IMPORT, "No import paths specified, cannot import " + name);

    return false;
  }
Beispiel #4
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);
        }
      }
    }
  }