Package wyc.builder

Source Code of wyc.builder.WhileyBuilder

  // Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package wyc.builder;

import java.io.*;
import java.util.*;

import wyfs.lang.Content;
import wyfs.lang.Path;
import wyfs.util.Trie;
import wyil.*;
import wyil.lang.*;
import wyil.util.*;
import wybs.lang.*;
import wybs.util.*;
import wyc.lang.*;
import wycc.lang.NameID;
import wycc.lang.Pipeline;
import wycc.lang.Transform;
import wycc.util.Logger;
import wycc.util.Pair;
import wycc.util.ResolveError;

/**
* Responsible for managing the process of turning source files into binary code
* for execution. Each source file is passed through a pipeline of stages that
* modify it in a variety of ways. The main stages are:
* <ol>
* <li>
* <p>
* <b>Lexing and Parsing</b>, where the source file is converted into an
* Abstract Syntax Tree (AST) representation.
* </p>
* </li>
* <li>
* <p>
* <b>Name Resolution</b>, where the fully qualified names of all external
* symbols are determined.
* </p>
* </li>
* <li>
* <p>
* <b>Type Propagation</b>, where the types of all expressions are determined by
* propagation from e.g. declared parameter types.
* </p>
* </li>
* <li>
* <p>
* <b>WYIL Generation</b>, where the the AST is converted into the Whiley
* Intermediate Language (WYIL). A number of passes are then made over this
* before it is ready for code generation.
* </p>
* </li>
* <li>
* <p>
* <b>Code Generation</b>. Here, the executable code is finally generated. This
* could be Java bytecode, or something else (e.g. JavaScript).
* </p>
* </li>
* </ol>
* Every stage of the compiler can be configured by setting various options.
* Stages can also be bypassed (typically for testing) and new ones can be
* added.
*
* @author David J. Pearce
*
*/
public final class WhileyBuilder implements Builder {

  /**
   * The master project for identifying all resources available to the
   * builder. This includes all modules declared in the project being compiled
   * and/or defined in external resources (e.g. jar files).
   */
  private final Build.Project project;

  /**
   * The list of stages which must be applied to a Wyil file.
   */
  private final List<Transform<WyilFile>> stages;

  private Logger logger;

  /**
   * A map of the source files currently being compiled.
   */
  private final HashMap<Path.ID, Path.Entry<WhileyFile>> srcFiles = new HashMap<Path.ID, Path.Entry<WhileyFile>>();

  /**
   * The import cache caches specific import queries to their result sets.
   * This is extremely important to avoid recomputing these result sets every
   * time. For example, the statement <code>import whiley.lang.*</code>
   * corresponds to the triple <code>("whiley.lang",*,null)</code>.
   */
  private final HashMap<Trie,ArrayList<Path.ID>> importCache = new HashMap();

  public WhileyBuilder(Build.Project namespace, Pipeline<WyilFile> pipeline) {
    this.stages = pipeline.instantiate(this);
    this.logger = Logger.NULL;
    this.project = namespace;
  }

  public Build.Project project() {
    return project;
  }

  public void setLogger(Logger logger) {
    this.logger = logger;
  }

  public Set<Path.Entry<?>> build(Collection<Pair<Path.Entry<?>, Path.Root>> delta)
      throws IOException {
    Runtime runtime = Runtime.getRuntime();
    long startTime = System.currentTimeMillis();
    long startMemory = runtime.freeMemory();
    long tmpTime = startTime;
    long tmpMemory = startMemory;

    // ========================================================================
    // Parse and register source files
    // ========================================================================

    srcFiles.clear();
    int count=0;
    for (Pair<Path.Entry<?>,Path.Root> p : delta) {
      Path.Entry<?> src = p.first();
      if (src.contentType() == WhileyFile.ContentType) {
        Path.Entry<WhileyFile> sf = (Path.Entry<WhileyFile>) src;
        WhileyFile wf = sf.read();
        count++;
        srcFiles.put(wf.module, sf);
      }
    }

    logger.logTimedMessage("Parsed " + count + " source file(s).",
        System.currentTimeMillis() - tmpTime, tmpMemory - runtime.freeMemory());

    // ========================================================================
    // Flow Type source files
    // ========================================================================

    runtime = Runtime.getRuntime();
    tmpTime = System.currentTimeMillis();
    tmpMemory = runtime.freeMemory();

    ArrayList<WhileyFile> files = new ArrayList<WhileyFile>();
    for(Pair<Path.Entry<?>,Path.Root> p : delta) {
      Path.Entry<?> f = p.first();
      if (f.contentType() == WhileyFile.ContentType) {
        Path.Entry<WhileyFile> sf = (Path.Entry<WhileyFile>) f;
        WhileyFile wf = sf.read();
        //new FlowTyping(resolver).propagate(wf);
        files.add(wf);
      }
    }

    FlowTypeChecker flowChecker = new FlowTypeChecker(this);
    flowChecker.propagate(files);

    logger.logTimedMessage("Typed " + count + " source file(s).",
        System.currentTimeMillis() - tmpTime, tmpMemory - runtime.freeMemory());

    // ========================================================================
    // Code Generation
    // ========================================================================

    runtime = Runtime.getRuntime();
    tmpTime = System.currentTimeMillis();
    tmpMemory = runtime.freeMemory();

    //CodeGenerator generator = new CodeGenerator();
    CodeGenerator generator = new CodeGenerator(this,flowChecker);
    HashSet<Path.Entry<?>> generatedFiles = new HashSet<Path.Entry<?>>();
    for (Pair<Path.Entry<?>, Path.Root> p : delta) {
      Path.Entry<?> src = p.first();
      Path.Root dst = p.second();
      if (src.contentType() == WhileyFile.ContentType) {
        Path.Entry<WhileyFile> source = (Path.Entry<WhileyFile>) src;
        Path.Entry<WyilFile> target = dst.create(src.id(),
            WyilFile.ContentType);
        generatedFiles.add(target);
        WhileyFile wf = source.read();
        WyilFile wyil = generator.generate(wf);
        target.write(wyil);
      }
    }

    logger.logTimedMessage("Generated code for " + count + " source file(s).",
          System.currentTimeMillis() - tmpTime, tmpMemory - runtime.freeMemory());

    // ========================================================================
    // Pipeline Stages
    // ========================================================================

    for (Transform stage : stages) {
      for (Pair<Path.Entry<?>, Path.Root> p : delta) {
        Path.Entry<?> src = p.first();
        Path.Root dst = p.second();
        Path.Entry<WyilFile> wf = dst.get(src.id(),
            WyilFile.ContentType);
        process(wf.read(), stage);
      }
    }

    // ========================================================================
    // Done
    // ========================================================================

    long endTime = System.currentTimeMillis();
    logger.logTimedMessage("Whiley => Wyil: compiled " + delta.size() + " file(s)",
        endTime - startTime, startMemory - runtime.freeMemory());

    return generatedFiles;
  }

  // ======================================================================
  // Public Accessors
  // ======================================================================

  public boolean exists(Path.ID id) {
    try {
      return project.exists(id, WhileyFile.ContentType)
          || project.exists(id, WyilFile.ContentType);
    } catch(Exception e) {
      return false;
    }
  }

  /**
   * Determine whether a given name exists or not.
   *
   * @param nid --- Name ID to check
   * @return
   */
  public boolean isName(NameID nid) throws IOException {
    Path.ID mid = nid.module();
    Path.Entry<WhileyFile> wf = srcFiles.get(mid);
    if(wf != null) {
      // FIXME: check for the right kind of name
      return wf.read().hasName(nid.name());
    } else {
      Path.Entry<WyilFile> m = project.get(mid,WyilFile.ContentType);
      if(m != null) {
        return m.read().hasName(nid.name());
      } else {
        return false;
      }
    }
  }

  /**
   * This method takes a given import declaration, and expands it to find all
   * matching modules.
   *
   * @param imp
   * @return
   */
  public List<Path.ID> imports(Trie key) throws ResolveError {
    try {
      ArrayList<Path.ID> matches = importCache.get(key);
      if (matches != null) {
        // cache hit
        return matches;
      } else {
        // cache miss
        matches = new ArrayList<Path.ID>();

        for(Path.Entry<WhileyFile> sf : srcFiles.values()) {
          if(key.matches(sf.id())) {
            matches.add(sf.id());
          }
        }
        if(key.isConcrete()) {
          // A concrete key is one which does not contain a wildcard.
          // Therefore, it corresponds to exactly one possible item.
          // It is helpful, from a performance perspective, to use
          // NameSpace.exists() in such case, as this conveys the fact
          // that we're only interested in a single item.
          if(project.exists(key,WyilFile.ContentType)) {
            matches.add(key);
          }
        } else {
          Content.Filter<?> binFilter = Content.filter(key,
              WyilFile.ContentType);
          for (Path.ID mid : project.match(binFilter)) {
            matches.add(mid);
          }
        }
        importCache.put(key, matches);
      }
      return matches;
    } catch(Exception e) {
      throw new ResolveError(e.getMessage(),e);
    }
  }

  /**
   * Get the source file associated with a given module identifier. If the
   * source file does not exist, null is returned.
   *
   * @param mid
   * @return
   * @throws IOException
   */
  public WhileyFile getSourceFile(Path.ID mid) throws IOException {
    Path.Entry<WhileyFile> e = srcFiles.get(mid);
    if(e != null) {
      return e.read();
    } else {
      return null;
    }
  }

  /**
   * Get the (compiled) module associated with a given module identifier. If
   * the module does not exist, a resolve error is thrown.
   *
   * @param mid
   * @return
   * @throws IOException
   */
  public WyilFile getModule(Path.ID mid) throws IOException {
    return project.get(mid, WyilFile.ContentType).read();
  }

  // ======================================================================
  // Private Implementation
  // ======================================================================

  private void process(WyilFile module, Transform stage) throws IOException {
    Runtime runtime = Runtime.getRuntime();
    long start = System.currentTimeMillis();
    long memory = runtime.freeMemory();
    String name = name(stage.getClass().getSimpleName());

    try {
      stage.apply(module);
      logger.logTimedMessage("[" + module.filename() + "] applied "
          + name, System.currentTimeMillis() - start, memory - runtime.freeMemory());
      System.gc();
    } catch (RuntimeException ex) {
      logger.logTimedMessage("[" + module.filename() + "] failed on "
          + name + " (" + ex.getMessage() + ")",
          System.currentTimeMillis() - start, memory - runtime.freeMemory());
      throw ex;
    } catch (IOException ex) {
      logger.logTimedMessage("[" + module.filename() + "] failed on "
          + name + " (" + ex.getMessage() + ")",
          System.currentTimeMillis() - start, memory - runtime.freeMemory());
      throw ex;
    }
  }

  private static String name(String camelCase) {
    boolean firstTime = true;
    String r = "";
    for(int i=0;i!=camelCase.length();++i) {
      char c = camelCase.charAt(i);
      if(!firstTime && Character.isUpperCase(c)) {
        r += " ";
      }
      firstTime=false;
      r += Character.toLowerCase(c);;
    }
    return r;
  }
}
TOP

Related Classes of wyc.builder.WhileyBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.