changeset 16068:74e142bd2b12

Truffle/Source: major API revision - All source-related classes now in com.oracle.truffle.api.source - SourceFactory replaced with factory methods on Source - Revision, renaming, and documentation to methods on Source and SourceSection - NullSourceSection is now a utility class
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Fri, 06 Jun 2014 22:13:00 -0700
parents 915ebb306fcc
children 4291873b259b
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/NullSourceSection.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/NullSourceSection.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java
diffstat 9 files changed, 1081 insertions(+), 1132 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/NullSourceSection.java	Fri Jun 06 17:51:47 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api;
-
-/**
- * Marker for a special flavor of {@link SourceSection} that has no content and can be ignored.
- */
-public interface NullSourceSection extends SourceSection {
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java	Fri Jun 06 17:51:47 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api;
-
-import java.io.*;
-import java.net.*;
-
-import com.oracle.truffle.api.source.*;
-
-/**
- * Represents a unit of guest language source code.
- */
-public interface Source {
-
-    /**
-     * Returns the name of this resource holding a guest language program. An example would be the
-     * name of a guest language source code file.
-     *
-     * @return the name of the guest language program
-     */
-    String getName();
-
-    /**
-     * Returns a short version of the name of the resource holding a guest language program (as
-     * described in @getName). For example, this could be just the name of the file, rather than a
-     * full path.
-     *
-     * @return the short name of the guest language program
-     */
-    String getShortName();
-
-    /**
-     * The normalized, canonical name if the source is a file.
-     */
-    String getPath();
-
-    /**
-     * The URL if the source is retrieved via URL.
-     */
-    URL getURL();
-
-    /**
-     * Access to the source contents.
-     */
-    Reader getReader();
-
-    /**
-     * Access to the source contents.
-     */
-    InputStream getInputStream();
-
-    /**
-     * Return the complete text of the code.
-     */
-    String getCode();
-
-    /**
-     * Given a 1-based line number, return the text in the line, not including a possible
-     * terminating newline.
-     */
-    String getCode(int lineNumber);
-
-    /**
-     * The number of text lines in the source, including empty lines; characters at the end of the
-     * source without a terminating newline count as a line.
-     */
-    int getLineCount();
-
-    /**
-     * Given a 0-based character offset, return the 1-based number of the line that includes the
-     * position.
-     */
-    int getLineNumber(int offset);
-
-    /**
-     * Given a 1-based line number, return the 0-based offset of the first character in the line.
-     */
-    int getLineStartOffset(int lineNumber);
-
-    /**
-     * The number of characters (not counting a possible terminating newline) in a (1-based)
-     * numbered line.
-     */
-    int getLineLength(int lineNumber);
-
-    /**
-     * Creates a representation of a contiguous region of text in the source. Computes the
-     * {@code (startLine, startColumn)} values by building a {@linkplain TextMap map} of lines in
-     * the source.
-     * <p>
-     * Checks the position arguments for consistency with the source.
-     * <p>
-     * The resulting representation defines hash/equality around equivalent location, presuming that
-     * {@link Source} representations are cannonical.
-     *
-     *
-     * @param identifier terse description of the region
-     * @param charIndex 0-based position of the first character in the section
-     * @param length the number of characters in the section
-     * @return newly created object representing the specified region
-     * @throws IllegalArgumentException if either of the arguments are outside the text of the
-     *             source
-     * @throws IllegalStateException if the source is one of the "null" instances
-     */
-    SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException, IllegalStateException;
-
-    /**
-     * Creates a representation of a contiguous region of text in the source. Computes the
-     * {@code charIndex} value by building a {@linkplain TextMap map} of lines in the source.
-     * <p>
-     * Checks the position arguments for consistency with the source.
-     * <p>
-     * The resulting representation defines hash/equality around equivalent location, presuming that
-     * {@link Source} representations are cannonical.
-     *
-     * @param identifier terse description of the region
-     * @param startLine 1-based line number of the first character in the section
-     * @param startColumn 1-based column number of the first character in the section
-     * @param length the number of characters in the section
-     * @return newly created object representing the specified region
-     * @throws IllegalArgumentException if arguments are outside the text of the source
-     * @throws IllegalStateException if the source is one of the "null" instances
-     */
-    SourceSection createSection(String identifier, int startLine, int startColumn, int length);
-
-    /**
-     * Creates a representation of a contiguous region of text in the source.
-     * <p>
-     * This method performs no checks on the validity of the arguments.
-     * <p>
-     * The resulting representation defines hash/equality around equivalent location, presuming that
-     * {@link Source} representations are cannonical.
-     *
-     * @param identifier terse description of the region
-     * @param startLine 1-based line number of the first character in the section
-     * @param startColumn 1-based column number of the first character in the section
-     * @param charIndex the 0-based index of the first character of the section
-     * @param length the number of characters in the section
-     * @return newly created object representing the specified region
-     */
-    SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java	Fri Jun 06 17:51:47 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api;
-
-/**
- * Description of contiguous section of text within a {@link Source} of program code.
- *
- * The starting location of the section can be described using two different coordinates:
- * <ul>
- * <li>{@code (startLine, startColumn)}: rows and columns are 1-based, so the first character in a
- * source file is at position {@code (1,1)}. {@code Tab} characters are counted as occupying one
- * column.</li>
- * <li><b>character index</b>: 0-based offset of the character from the beginning of the source, so
- * the first character in a file is at index {@code 0}.</li>
- * </ul>
- * The {@code Newline} that terminates each line counts as a single character for the purpose of a
- * character index and when counting the length of text. The {@code (line,column)} coordinates of a
- * {@code Newline} should never appear in a text section.
- * <p>
- * If the final character of source is not a {@code Newline}, the final characters of the text are
- * still considered to be a line ("unterminated").
- * <p>
- *
- * @see Source#createSection(String, int, int, int, int)
- * @see Source#createSection(String, int, int, int)
- * @see Source#createSection(String, int, int)
- */
-public interface SourceSection {
-
-    // TODO support alternate text representations/encodings
-
-    /**
-     * Representation of the source program that contains this section.
-     *
-     * @return the source object
-     */
-    Source getSource();
-
-    /**
-     * Returns 1-based line number of the first character in this section (inclusive).
-     *
-     * @return the starting line number
-     */
-    int getStartLine();
-
-    /**
-     * Returns the 1-based column number of the first character in this section (inclusive).
-     *
-     * @return the starting column number
-     */
-    int getStartColumn();
-
-    /**
-     * Returns the 0-based index of the first character in this section.
-     *
-     * @return the starting character index
-     */
-    int getCharIndex();
-
-    /**
-     * Returns the length of this section in characters.
-     *
-     * @return the number of characters in the section
-     */
-    int getCharLength();
-
-    /**
-     * Returns the index of the text position immediately following the last character in the
-     * section.
-     *
-     * @return the end position of the section
-     */
-    int getCharEndIndex();
-
-    /**
-     * Returns terse text describing this source section, typically used for printing the section.
-     *
-     * @return the identifier of the section
-     */
-    String getIdentifier();
-
-    /**
-     * Returns text described by this section.
-     *
-     * @return the code as a String object
-     */
-    String getCode();
-
-    /**
-     * Returns a short description of the source section, using just the file name, rather than its
-     * full path.
-     *
-     * @return a short description of the source section
-     */
-    String getShortDescription();
-
-    /**
-     * Singleton instance with no content.
-     */
-    SourceSection NULL = new NullSourceSection() {
-
-        @Override
-        public Source getSource() {
-            return null;
-        }
-
-        @Override
-        public int getStartLine() {
-            return 0;
-        }
-
-        @Override
-        public int getStartColumn() {
-            return 0;
-        }
-
-        @Override
-        public int getCharIndex() {
-            return 0;
-        }
-
-        @Override
-        public int getCharLength() {
-            return 0;
-        }
-
-        @Override
-        public int getCharEndIndex() {
-            return 0;
-        }
-
-        @Override
-        public String getIdentifier() {
-            return null;
-        }
-
-        @Override
-        public String getCode() {
-            return null;
-        }
-
-        @Override
-        public String getShortDescription() {
-            return "short";
-        }
-
-    };
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java	Fri Jun 06 22:13:00 2014 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.source;
+
+/**
+ * A specification for a location in guest language source, expressed as a line number in a specific
+ * instance of {@link Source}, suitable for hash table keys with equality defined in terms of
+ * content.
+ */
+public interface LineLocation extends Comparable<Object> {
+
+    public Source getSource();
+
+    /**
+     * Gets the 1-based number of a line in the source
+     */
+    public int getLineNumber();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/NullSourceSection.java	Fri Jun 06 22:13:00 2014 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.source;
+
+/**
+ * A special subtype of {@link SourceSection} that represents unavailable source, e.g. for language
+ * <em>builtins</em>.
+ */
+public class NullSourceSection implements SourceSection {
+
+    private final String kind;
+    private final String name;
+    private final String asCode;
+
+    /**
+     * Placeholder for source that is unavailable, e.g. for language <em>builtins</em>.
+     *
+     * @param kind the general category, e.g. "JS builtin"
+     * @param name specific name for this section
+     */
+    public NullSourceSection(String kind, String name) {
+        this(kind, name, kind);
+    }
+
+    /**
+     * Placeholder for source that is unavailable, e.g. for language <em>builtins</em>.
+     *
+     * @param kind the general category, e.g. "JS builtin"
+     * @param name specific name for this section
+     * @param asCode string to return when {@link #getCode()} is called
+     */
+    public NullSourceSection(String kind, String name, String asCode) {
+        this.kind = kind;
+        this.name = name;
+        this.asCode = asCode;
+    }
+
+    public final Source getSource() {
+        return null;
+    }
+
+    public final int getStartLine() {
+        throw new UnsupportedOperationException(this.toString());
+    }
+
+    public final LineLocation getLineLocation() {
+        throw new UnsupportedOperationException(this.toString());
+    }
+
+    public final int getStartColumn() {
+        throw new UnsupportedOperationException(this.toString());
+    }
+
+    public final int getCharIndex() {
+        throw new UnsupportedOperationException(this.toString());
+    }
+
+    public final int getCharLength() {
+        throw new UnsupportedOperationException(this.toString());
+    }
+
+    public final int getCharEndIndex() {
+        throw new UnsupportedOperationException(this.toString());
+    }
+
+    public final String getIdentifier() {
+        return name;
+    }
+
+    public final String getCode() {
+        return asCode;
+    }
+
+    public final String getShortDescription() {
+        return kind + ": " + name;
+    }
+
+    @Override
+    public String toString() {
+        return getShortDescription();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Fri Jun 06 22:13:00 2014 -0700
@@ -0,0 +1,823 @@
+/*
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.source;
+
+import java.io.*;
+import java.lang.ref.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Representation of a guest language source code unit and its contents. Sources originate in
+ * several ways:
+ * <ul>
+ * <li><strong>Literal:</strong> A named text string. These are not indexed and should be considered
+ * value objects; equality is defined based on contents. <br>
+ * See {@link Source#fromText(String, String)}</li>
+ * <p>
+ * <li><strong>File:</strong> Each file is represented as a canonical object, indexed by the
+ * absolute, canonical path name of the file. File contents are <em>read lazily</em> and contents
+ * optionally <em>cached</em>. <br>
+ * See {@link Source#fromFileName(String)}<br>
+ * See {@link Source#fromFileName(String, boolean)}</li>
+ * <p>
+ * <li><strong>URL:</strong> Each URL source is represented as a canonical object, indexed by the
+ * URL. Contents are <em>read eagerly</em> and <em>cached</em>. <br>
+ * See {@link Source#fromURL(URL, String)}</li>
+ * <p>
+ * <li><strong>Reader:</strong> Contents are <em>read eagerly</em> and treated as a <em>Literal</em>
+ * . <br>
+ * See {@link Source#fromReader(Reader, String)}</li>
+ * <p>
+ * <li><strong>Pseudo File:</strong> A literal text string that can be retrieved by name as if it
+ * were a file, unlike literal sources; useful for testing. <br>
+ * See {@link Source#asPseudoFile(String, String)}</li>
+ * </ul>
+ * <p>
+ * <strong>File cache:</strong>
+ * <ol>
+ * <li>File content caching is optional, <em>off</em> by default.</li>
+ * <li>The first access to source file contents will result in the contents being read, and (if
+ * enabled) cached.</li>
+ * <li>If file contents have been cached, access to contents via {@link Source#getInputStream()} or
+ * {@link Source#getReader()} will be provided from the cache.</li>
+ * <li>Any access to file contents via the cache will result in a timestamp check and possible cache
+ * reload.</li>
+ * </ol>
+ */
+public abstract class Source {
+
+    // TODO (mlvdv) consider canonicalizing and reusing SourceSection instances
+    // TOOD (mlvdv) connect SourceSections into a spatial tree for fast geometric lookup
+
+    // Files and pseudo files are indexed.
+    private static final Map<String, WeakReference<Source>> filePathToSource = new HashMap<>();
+
+    private static boolean fileCacheEnabled = true;
+
+    /**
+     * Gets the canonical representation of a source file, whose contents will be read lazily and
+     * then cached.
+     *
+     * @param fileName name
+     * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read
+     * @return canonical representation of the file's contents.
+     * @throws IOException if the file can not be read
+     */
+    public static Source fromFileName(String fileName, boolean reset) throws IOException {
+
+        final WeakReference<Source> nameRef = filePathToSource.get(fileName);
+        Source source = nameRef == null ? null : nameRef.get();
+        if (source == null) {
+            final File file = new File(fileName);
+            if (!file.canRead()) {
+                throw new IOException("Can't read file " + fileName);
+            }
+            final String path = file.getCanonicalPath();
+            final WeakReference<Source> pathRef = filePathToSource.get(path);
+            source = pathRef == null ? null : pathRef.get();
+            if (source == null) {
+                source = new FileSource(file, fileName, path);
+                filePathToSource.put(path, new WeakReference<>(source));
+            }
+        }
+        if (reset) {
+            source.reset();
+        }
+        return source;
+    }
+
+    /**
+     * Gets the canonical representation of a source file, whose contents will be read lazily and
+     * then cached.
+     *
+     * @param fileName name
+     * @return canonical representation of the file's contents.
+     * @throws IOException if the file can not be read
+     */
+    public static Source fromFileName(String fileName) throws IOException {
+        return fromFileName(fileName, false);
+    }
+
+    /**
+     * Creates a non-canonical source from literal text.
+     *
+     * @param code textual source code
+     * @param description a note about the origin, for error messages and debugging
+     * @return a newly created, non-indexed source representation
+     */
+    public static Source fromText(String code, String description) {
+        assert code != null;
+        return new LiteralSource(description, code);
+    }
+
+    /**
+     * Creates a source whose contents will be read immediately from a URL and cached.
+     *
+     * @param url
+     * @param name identifies the origin, possibly useful for debugging
+     * @return a newly created, non-indexed source representation
+     * @throws IOException if reading fails
+     */
+    public static Source fromURL(URL url, String name) throws IOException {
+        return URLSource.get(url, name);
+    }
+
+    /**
+     * Creates a source whose contents will be read immediately and cached.
+     *
+     * @param reader
+     * @param description a note about the origin, possibly useful for debugging
+     * @return a newly created, non-indexed source representation
+     * @throws IOException if reading fails
+     */
+    public static Source fromReader(Reader reader, String description) throws IOException {
+        return new LiteralSource(description, read(reader));
+    }
+
+    /**
+     * Creates a source from literal text, but which acts as a file and can be retrieved by name
+     * (unlike other literal sources); intended for testing.
+     *
+     * @param code textual source code
+     * @param pseudoFileName string to use for indexing/lookup
+     * @return a newly created, source representation, canonical with respect to its name
+     */
+    public static Source asPseudoFile(String code, String pseudoFileName) {
+        final Source source = new LiteralSource(pseudoFileName, code);
+        filePathToSource.put(pseudoFileName, new WeakReference<>(source));
+        return source;
+    }
+
+    /**
+     * Enables/disables caching of file contents, <em>disabled</em> by default. Caching of sources
+     * created from literal text or readers is always enabled.
+     */
+    public static void setFileCaching(boolean enabled) {
+        fileCacheEnabled = enabled;
+    }
+
+    private static String read(Reader reader) throws IOException {
+        final StringBuilder builder = new StringBuilder();
+        final char[] buffer = new char[1024];
+
+        while (true) {
+            final int n = reader.read(buffer);
+            if (n == -1) {
+                break;
+            }
+            builder.append(buffer, 0, n);
+        }
+
+        return builder.toString();
+    }
+
+    protected Source() {
+    }
+
+    protected TextMap textMap = null;
+
+    protected abstract void reset();
+
+    /**
+     * Returns the name of this resource holding a guest language program. An example would be the
+     * name of a guest language source code file.
+     *
+     * @return the name of the guest language program
+     */
+    public abstract String getName();
+
+    /**
+     * Returns a short version of the name of the resource holding a guest language program (as
+     * described in @getName). For example, this could be just the name of the file, rather than a
+     * full path.
+     *
+     * @return the short name of the guest language program
+     */
+    public abstract String getShortName();
+
+    /**
+     * The normalized, canonical name if the source is a file.
+     */
+    public abstract String getPath();
+
+    /**
+     * The URL if the source is retrieved via URL.
+     */
+    public abstract URL getURL();
+
+    /**
+     * Access to the source contents.
+     */
+    public abstract Reader getReader();
+
+    /**
+     * Access to the source contents.
+     */
+    public final InputStream getInputStream() {
+        return new ByteArrayInputStream(getCode().getBytes());
+    }
+
+    /**
+     * Return the complete text of the code.
+     */
+    public abstract String getCode();
+
+    /**
+     * Gets the text (not including a possible terminating newline) in a (1-based) numbered line.
+     */
+    public final String getCode(int lineNumber) {
+        checkTextMap();
+        final int offset = textMap.lineStartOffset(lineNumber);
+        final int length = textMap.lineLength(lineNumber);
+        return getCode().substring(offset, offset + length);
+    }
+
+    /**
+     * The number of text lines in the source, including empty lines; characters at the end of the
+     * source without a terminating newline count as a line.
+     */
+    public final int getLineCount() {
+        return checkTextMap().lineCount();
+    }
+
+    /**
+     * Given a 0-based character offset, return the 1-based number of the line that includes the
+     * position.
+     */
+    public final int getLineNumber(int offset) {
+        return checkTextMap().offsetToLine(offset);
+    }
+
+    /**
+     * Given a 1-based line number, return the 0-based offset of the first character in the line.
+     */
+    public final int getLineStartOffset(int lineNumber) {
+        return checkTextMap().lineStartOffset(lineNumber);
+    }
+
+    /**
+     * The number of characters (not counting a possible terminating newline) in a (1-based)
+     * numbered line.
+     */
+    public final int getLineLength(int lineNumber) {
+        return checkTextMap().lineLength(lineNumber);
+    }
+
+    /**
+     * Creates a representation of a contiguous region of text in the source.
+     * <p>
+     * This method performs no checks on the validity of the arguments.
+     * <p>
+     * The resulting representation defines hash/equality around equivalent location, presuming that
+     * {@link Source} representations are canonical.
+     *
+     * @param identifier terse description of the region
+     * @param startLine 1-based line number of the first character in the section
+     * @param startColumn 1-based column number of the first character in the section
+     * @param charIndex the 0-based index of the first character of the section
+     * @param length the number of characters in the section
+     * @return newly created object representing the specified region
+     */
+    public final SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length) {
+        return new DefaultSourceSection(this, identifier, startLine, startColumn, charIndex, length);
+    }
+
+    /**
+     * Creates a representation of a contiguous region of text in the source. Computes the
+     * {@code charIndex} value by building a {@linkplain TextMap map} of lines in the source.
+     * <p>
+     * Checks the position arguments for consistency with the source.
+     * <p>
+     * The resulting representation defines hash/equality around equivalent location, presuming that
+     * {@link Source} representations are canonical.
+     *
+     * @param identifier terse description of the region
+     * @param startLine 1-based line number of the first character in the section
+     * @param startColumn 1-based column number of the first character in the section
+     * @param length the number of characters in the section
+     * @return newly created object representing the specified region
+     * @throws IllegalArgumentException if arguments are outside the text of the source
+     * @throws IllegalStateException if the source is one of the "null" instances
+     */
+    public final SourceSection createSection(String identifier, int startLine, int startColumn, int length) {
+        checkTextMap();
+        final int lineStartOffset = textMap.lineStartOffset(startLine);
+        if (startColumn > textMap.lineLength(startLine)) {
+            throw new IllegalArgumentException("column out of range");
+        }
+        final int startOffset = lineStartOffset + startColumn - 1;
+        return new DefaultSourceSection(this, identifier, startLine, startColumn, startOffset, length);
+    }
+
+    /**
+     * Creates a representation of a contiguous region of text in the source. Computes the
+     * {@code (startLine, startColumn)} values by building a {@linkplain TextMap map} of lines in
+     * the source.
+     * <p>
+     * Checks the position arguments for consistency with the source.
+     * <p>
+     * The resulting representation defines hash/equality around equivalent location, presuming that
+     * {@link Source} representations are canonical.
+     *
+     *
+     * @param identifier terse description of the region
+     * @param charIndex 0-based position of the first character in the section
+     * @param length the number of characters in the section
+     * @return newly created object representing the specified region
+     * @throws IllegalArgumentException if either of the arguments are outside the text of the
+     *             source
+     * @throws IllegalStateException if the source is one of the "null" instances
+     */
+    public final SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException {
+        final int codeLength = getCode().length();
+        if (!(charIndex >= 0 && length >= 0 && charIndex + length <= codeLength)) {
+            throw new IllegalArgumentException("text positions out of range");
+        }
+        checkTextMap();
+        final int startLine = getLineNumber(charIndex);
+        final int startColumn = charIndex - getLineStartOffset(startLine) + 1;
+
+        return new DefaultSourceSection(this, identifier, startLine, startColumn, charIndex, length);
+    }
+
+    /**
+     * Creates a representation of a line of text in the source identified only by line number, from
+     * which the character information will be computed.
+     *
+     * @param identifier terse description of the line
+     * @param lineNumber 1-based line number of the first character in the section
+     * @return newly created object representing the specified line
+     * @throws IllegalArgumentException if the line does not exist the source
+     * @throws IllegalStateException if the source is one of the "null" instances
+     */
+    public final SourceSection createSection(String identifier, int lineNumber) {
+        checkTextMap();
+        final int charIndex = textMap.lineStartOffset(lineNumber);
+        final int length = textMap.lineLength(lineNumber);
+        return createSection(identifier, charIndex, length);
+    }
+
+    /**
+     * Creates a representation of a line number in this source, suitable for use as a hash table
+     * key with equality defined to mean equivalent location.
+     *
+     * @param lineNumber a 1-based line number in this source
+     * @return a representation of a line in this source
+     */
+    public final LineLocation createLineLocation(int lineNumber) {
+        return new LineLocationImpl(this, lineNumber);
+    }
+
+    private TextMap checkTextMap() {
+        if (textMap == null) {
+            final String code = getCode();
+            if (code == null) {
+                throw new RuntimeException("can't read file " + getName());
+            }
+            textMap = new TextMap(code);
+        }
+        return textMap;
+    }
+
+    private static final class LiteralSource extends Source {
+
+        private final String name; // Name used originally to describe the source
+        private final String code;
+
+        public LiteralSource(String name, String code) {
+            this.name = name;
+            this.code = code;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String getShortName() {
+            return name;
+        }
+
+        @Override
+        public String getCode() {
+            return code;
+        }
+
+        @Override
+        public String getPath() {
+            return name;
+        }
+
+        @Override
+        public URL getURL() {
+            return null;
+        }
+
+        @Override
+        public Reader getReader() {
+            return new StringReader(code);
+        }
+
+        @Override
+        protected void reset() {
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + name.hashCode();
+            result = prime * result + (code == null ? 0 : code.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (!(obj instanceof LiteralSource)) {
+                return false;
+            }
+            LiteralSource other = (LiteralSource) obj;
+            return name.equals(other.name) && code.equals(other.code);
+        }
+
+    }
+
+    private static final class FileSource extends Source {
+
+        private final File file;
+        private final String name; // Name used originally to describe the source
+        private final String path;  // Normalized path description of an actual file
+
+        private String code = null;  // A cache of the file's contents
+        private long timeStamp;      // timestamp of the cache in the file system
+
+        public FileSource(File file, String name, String path) {
+            this.file = file;
+            this.name = name;
+            this.path = path;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String getShortName() {
+            return file.getName();
+        }
+
+        @Override
+        public String getCode() {
+            if (fileCacheEnabled) {
+                if (code == null || timeStamp != file.lastModified()) {
+                    try {
+                        code = read(getReader());
+                        timeStamp = file.lastModified();
+                    } catch (IOException e) {
+                    }
+                }
+                return code;
+            }
+            try {
+                return read(new FileReader(file));
+            } catch (IOException e) {
+            }
+            return null;
+        }
+
+        @Override
+        public String getPath() {
+            return path;
+        }
+
+        @Override
+        public URL getURL() {
+            return null;
+        }
+
+        @Override
+        public Reader getReader() {
+            if (code != null && timeStamp == file.lastModified()) {
+                return new StringReader(code);
+            }
+            try {
+                return new FileReader(file);
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException("Can't find file " + path);
+            }
+        }
+
+        @Override
+        protected void reset() {
+            this.code = null;
+        }
+
+    }
+
+    private static final class URLSource extends Source {
+
+        private static final Map<URL, WeakReference<URLSource>> urlToSource = new HashMap<>();
+
+        public static URLSource get(URL url, String name) throws IOException {
+            WeakReference<URLSource> sourceRef = urlToSource.get(url);
+            URLSource source = sourceRef == null ? null : sourceRef.get();
+            if (source == null) {
+                source = new URLSource(url, name);
+                urlToSource.put(url, new WeakReference<>(source));
+            }
+            return source;
+        }
+
+        private final URL url;
+        private final String name;
+        private String code = null;  // A cache of the source contents
+
+        public URLSource(URL url, String name) throws IOException {
+            this.url = url;
+            this.name = name;
+            code = read(new InputStreamReader(url.openStream()));
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String getShortName() {
+            return name;
+        }
+
+        @Override
+        public String getPath() {
+            return url.getPath();
+        }
+
+        @Override
+        public URL getURL() {
+            return url;
+        }
+
+        @Override
+        public Reader getReader() {
+            return new StringReader(code);
+        }
+
+        @Override
+        public String getCode() {
+            return code;
+        }
+
+        @Override
+        protected void reset() {
+        }
+
+    }
+
+    private static final class DefaultSourceSection implements SourceSection {
+
+        private final Source source;
+        private final String identifier;
+        private final int startLine;
+        private final int startColumn;
+        private final int charIndex;
+        private final int charLength;
+
+        /**
+         * Creates a new object representing a contiguous text section within the source code of a
+         * guest language program's text.
+         * <p>
+         * The starting location of the section is specified using two different coordinate:
+         * <ul>
+         * <li><b>(row, column)</b>: rows and columns are 1-based, so the first character in a
+         * source file is at position {@code (1,1)}.</li>
+         * <li><b>character index</b>: 0-based offset of the character from the beginning of the
+         * source, so the first character in a file is at index {@code 0}.</li>
+         * </ul>
+         * The <b>newline</b> that terminates each line counts as a single character for the purpose
+         * of a character index. The (row,column) coordinates of a newline character should never
+         * appear in a text section.
+         * <p>
+         *
+         * @param source object representing the complete source program that contains this section
+         * @param identifier an identifier used when printing the section
+         * @param startLine the 1-based number of the start line of the section
+         * @param startColumn the 1-based number of the start column of the section
+         * @param charIndex the 0-based index of the first character of the section
+         * @param charLength the length of the section in number of characters
+         */
+        public DefaultSourceSection(Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) {
+            this.source = source;
+            this.identifier = identifier;
+            this.startLine = startLine;
+            this.startColumn = startColumn;
+            this.charIndex = charIndex;
+            this.charLength = charLength;
+        }
+
+        @Override
+        public final Source getSource() {
+            return source;
+        }
+
+        @Override
+        public final int getStartLine() {
+            return startLine;
+        }
+
+        @Override
+        public final LineLocation getLineLocation() {
+            return source.createLineLocation(startLine);
+        }
+
+        @Override
+        public final int getStartColumn() {
+            return startColumn;
+        }
+
+        @Override
+        public final int getCharIndex() {
+            return charIndex;
+        }
+
+        @Override
+        public final int getCharLength() {
+            return charLength;
+        }
+
+        @Override
+        public final int getCharEndIndex() {
+            return charIndex + charLength;
+        }
+
+        @Override
+        public final String getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        public final String getCode() {
+            return getSource().getCode().substring(charIndex, charIndex + charLength);
+        }
+
+        @Override
+        public final String getShortDescription() {
+            return String.format("%s:%d", source.getShortName(), startLine);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s:%d", source.getName(), startLine);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + charIndex;
+            result = prime * result + charLength;
+            result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
+            result = prime * result + ((source == null) ? 0 : source.hashCode());
+            result = prime * result + startColumn;
+            result = prime * result + startLine;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (!(obj instanceof DefaultSourceSection)) {
+                return false;
+            }
+            DefaultSourceSection other = (DefaultSourceSection) obj;
+            if (charIndex != other.charIndex) {
+                return false;
+            }
+            if (charLength != other.charLength) {
+                return false;
+            }
+            if (identifier == null) {
+                if (other.identifier != null) {
+                    return false;
+                }
+            } else if (!identifier.equals(other.identifier)) {
+                return false;
+            }
+            if (source == null) {
+                if (other.source != null) {
+                    return false;
+                }
+            } else if (!source.equals(other.source)) {
+                return false;
+            }
+            if (startColumn != other.startColumn) {
+                return false;
+            }
+            if (startLine != other.startLine) {
+                return false;
+            }
+            return true;
+        }
+
+    }
+
+    private static final class LineLocationImpl implements LineLocation {
+        private final Source source;
+        private final int line;
+
+        public LineLocationImpl(Source source, int line) {
+            assert source != null;
+            this.source = source;
+            this.line = line;
+        }
+
+        @Override
+        public Source getSource() {
+            return source;
+        }
+
+        @Override
+        public int getLineNumber() {
+            return line;
+        }
+
+        @Override
+        public String toString() {
+            return "SourceLine [" + source.getName() + ", " + line + "]";
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + line;
+            result = prime * result + source.hashCode();
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (!(obj instanceof LineLocationImpl)) {
+                return false;
+            }
+            LineLocationImpl other = (LineLocationImpl) obj;
+            if (line != other.line) {
+                return false;
+            }
+            return source.equals(other.source);
+        }
+
+        @Override
+        public int compareTo(Object o) {
+            final LineLocationImpl other = (LineLocationImpl) o;
+            final int nameOrder = source.getName().compareTo(other.source.getName());
+            if (nameOrder != 0) {
+                return nameOrder;
+            }
+            return Integer.compare(line, other.line);
+        }
+
+    }
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java	Fri Jun 06 17:51:47 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,665 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.source;
-
-import java.io.*;
-import java.lang.ref.*;
-import java.net.*;
-import java.util.*;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Provider for canonical representations of source code. Three kinds of sources are supported.
- * <ul>
- * <li><strong>File:</strong> Each file is represented as a canonical object, indexed by the
- * absolute, canonical path name of the file. File contents are <em>read lazily</em> and contents
- * optionally <em>cached</em>.</li>
- * <li><strong>Literal Source:</strong> A named text string, whose contents are supplied concretely
- * (possibly via an {@link Reader}), can also be used as a source. These are not indexed and should
- * be considered value objects; equality is defined based on contents.</li>
- * <li><strong>Pseudo Files:</strong> A named text string used for testing; its contents can be
- * retrieved by name, unlike literal sources.</li>
- * </ul>
- * <p>
- * <strong>File cache:</strong>
- * <ol>
- * <li>File content caching is optional, <em>off</em> by default.</li>
- * <li>The first access to source file contents will result in the contents being read, and (if
- * enabled) cached.</li>
- * <li>If file contents have been cached, access to contents via {@link Source#getInputStream()} or
- * {@link Source#getReader()} will be provided from the cache.</li>
- * <li>Any access to file contents via the cache will result in a timestamp check and possible cache
- * reload.</li>
- * </ol>
- */
-public final class SourceFactory {
-
-    // Only files and pseudo files are indexed.
-    private static final Map<String, WeakReference<SourceImpl>> pathToSource = new HashMap<>();
-
-    private static boolean fileCacheEnabled = true;
-
-    private SourceFactory() {
-    }
-
-    /**
-     * Gets the canonical representation of a source file, whose contents will be read lazily and
-     * then cached.
-     *
-     * @param fileName name
-     * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read
-     * @return canonical representation of the file's contents.
-     * @throws IOException if the file can not be read
-     */
-    public static Source fromFile(String fileName, boolean reset) throws IOException {
-
-        SourceImpl source = lookup(fileName);
-        if (source == null) {
-            final File file = new File(fileName);
-            if (!file.canRead()) {
-                throw new IOException("Can't read file " + fileName);
-            }
-            String path = file.getCanonicalPath();
-            source = lookup(path);
-            if (source == null) {
-                source = new FileSourceImpl(file, fileName, path);
-                store(path, source);
-            }
-        }
-        if (reset) {
-            source.reset();
-        }
-        return source;
-    }
-
-    /**
-     * Gets the canonical representation of a source file, whose contents will be read lazily and
-     * then cached.
-     *
-     * @param fileName name
-     * @return canonical representation of the file's contents.
-     * @throws IOException if the file can not be read
-     */
-    public static Source fromFile(String fileName) throws IOException {
-        return fromFile(fileName, false);
-    }
-
-    /**
-     * Creates a non-canonical source from literal text.
-     *
-     * @param code textual source code
-     * @param description a note about the origin, for error messages and debugging
-     * @return a newly created, non-indexed source representation
-     */
-    public static Source fromText(String code, String description) {
-        assert code != null;
-        return new LiteralSourceImpl(description, code);
-    }
-
-    /**
-     * Creates a source whose contents will be read immediately from a URL and cached.
-     *
-     * @param url
-     * @param name identifies the origin, possibly useful for debugging
-     * @return a newly created, non-indexed source representation
-     * @throws IOException if reading fails
-     */
-    public static Source fromURL(URL url, String name) throws IOException {
-        return URLSourceImpl.get(url, name);
-    }
-
-    /**
-     * Creates a source whose contents will be read immediately and cached.
-     *
-     * @param reader
-     * @param description a note about the origin, possibly useful for debugging
-     * @return a newly created, non-indexed source representation
-     * @throws IOException if reading fails
-     */
-    public static Source fromReader(Reader reader, String description) throws IOException {
-        return new LiteralSourceImpl(description, read(reader));
-    }
-
-    /**
-     * Creates a source from literal text, but which acts as a file and can be retrieved by name
-     * (unlike other literal sources); intended for testing.
-     *
-     * @param code textual source code
-     * @param pseudoFileName string to use for indexing/lookup
-     * @return a newly created, source representation, canonical with respect to its name
-     */
-    public static Source asPseudoFile(String code, String pseudoFileName) {
-        final SourceImpl source = new LiteralSourceImpl(pseudoFileName, code);
-        store(pseudoFileName, source);
-        return source;
-    }
-
-    /**
-     * Creates a non-canonical representation of a source that is unavailable; attempts to access
-     * any information about the source beyond the name/description will result in a
-     * {@link IllegalStateException}
-     *
-     * @param description a note about the origin, for error messages and debugging
-     * @return a newly created, non-indexed representation of an unavailable source
-     */
-    public static Source asNull(String description) {
-        return new NullSourceImpl(description);
-    }
-
-    /**
-     * Enables/disables caching of file contents, <em>disabled</em> by default. Caching of sources
-     * created from literal text or readers is always enabled.
-     */
-    public static void setFileCaching(boolean enabled) {
-        fileCacheEnabled = enabled;
-    }
-
-    private static SourceImpl lookup(String key) {
-        WeakReference<SourceImpl> sourceRef = pathToSource.get(key);
-        return sourceRef == null ? null : sourceRef.get();
-    }
-
-    private static void store(String key, SourceImpl source) {
-        pathToSource.put(key, new WeakReference<>(source));
-    }
-
-    private static String read(Reader reader) throws IOException {
-        final StringBuilder builder = new StringBuilder();
-        final char[] buffer = new char[1024];
-
-        while (true) {
-            final int n = reader.read(buffer);
-            if (n == -1) {
-                break;
-            }
-            builder.append(buffer, 0, n);
-        }
-
-        return builder.toString();
-    }
-
-    private abstract static class SourceImpl implements Source {
-        // TODO (mlvdv) consider canonicalizing and reusing SourceSection instances
-        // TOOD (mlvdv) connect SourceSections into a spatial tree for fast geometric lookup
-
-        protected TextMap textMap = null;
-
-        protected abstract void reset();
-
-        public final InputStream getInputStream() {
-            return new ByteArrayInputStream(getCode().getBytes());
-        }
-
-        /**
-         * Gets the text (not including a possible terminating newline) in a (1-based) numbered
-         * line.
-         */
-        public final String getCode(int lineNumber) {
-            checkTextMap();
-            final int offset = textMap.lineStartOffset(lineNumber);
-            final int length = textMap.lineLength(lineNumber);
-            return getCode().substring(offset, offset + length);
-        }
-
-        /**
-         * The number of text lines in the source.
-         */
-        public final int getLineCount() {
-            return checkTextMap().lineCount();
-        }
-
-        /**
-         * The 1-based number of the line that includes a 0-based character offset.
-         */
-        public final int getLineNumber(int offset) {
-            return checkTextMap().offsetToLine(offset);
-        }
-
-        /**
-         * The 0-based character offset at the start of a (1-based) numbered line.
-         */
-        public final int getLineStartOffset(int lineNumber) {
-            return checkTextMap().lineStartOffset(lineNumber);
-        }
-
-        /**
-         * The number of characters (not counting a possible terminating newline) in a (1-based)
-         * numbered line.
-         */
-        public final int getLineLength(int lineNumber) {
-            return checkTextMap().lineLength(lineNumber);
-        }
-
-        public final SourceSection createSection(String identifier, int startOffset, int sectionLength) throws IllegalArgumentException {
-            final int codeLength = getCode().length();
-            if (!(startOffset >= 0 && sectionLength >= 0 && startOffset + sectionLength <= codeLength)) {
-                throw new IllegalArgumentException("text positions out of range");
-            }
-            checkTextMap();
-            final int startLine = getLineNumber(startOffset);
-            final int startColumn = startOffset - getLineStartOffset(startLine) + 1;
-
-            return new SourceSectionImpl(this, identifier, startLine, startColumn, startOffset, sectionLength);
-        }
-
-        public SourceSection createSection(String identifier, int startLine, int startColumn, int sectionLength) {
-            checkTextMap();
-            final int lineStartOffset = textMap.lineStartOffset(startLine);
-            if (startColumn > textMap.lineLength(startLine)) {
-                throw new IllegalArgumentException("column out of range");
-            }
-            final int startOffset = lineStartOffset + startColumn - 1;
-            return new SourceSectionImpl(this, identifier, startLine, startColumn, startOffset, sectionLength);
-        }
-
-        public SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length) {
-            return new SourceSectionImpl(this, identifier, startLine, startColumn, charIndex, length);
-        }
-
-        private TextMap checkTextMap() {
-            if (textMap == null) {
-                final String code = getCode();
-                if (code == null) {
-                    throw new RuntimeException("can't read file " + getName());
-                }
-                textMap = new TextMap(code);
-            }
-            return textMap;
-        }
-    }
-
-    private static class LiteralSourceImpl extends SourceImpl {
-
-        private final String name; // Name used originally to describe the source
-        private final String code;
-
-        public LiteralSourceImpl(String name, String code) {
-            this.name = name;
-            this.code = code;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public String getShortName() {
-            return name;
-        }
-
-        @Override
-        public String getCode() {
-            return code;
-        }
-
-        @Override
-        public String getPath() {
-            return name;
-        }
-
-        public URL getURL() {
-            return null;
-        }
-
-        @Override
-        public Reader getReader() {
-            return new StringReader(code);
-        }
-
-        @Override
-        protected void reset() {
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + name.hashCode();
-            result = prime * result + (code == null ? 0 : code.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (!(obj instanceof LiteralSourceImpl)) {
-                return false;
-            }
-            LiteralSourceImpl other = (LiteralSourceImpl) obj;
-            return name.equals(other.name) && code.equals(other.code);
-        }
-
-    }
-
-    private static class FileSourceImpl extends SourceImpl {
-
-        private final File file;
-        private final String name; // Name used originally to describe the source
-        private final String path;  // Normalized path description of an actual file
-
-        private String code = null;  // A cache of the file's contents
-        private long timeStamp;      // timestamp of the cache in the file system
-
-        public FileSourceImpl(File file, String name, String path) {
-            this.file = file;
-            this.name = name;
-            this.path = path;
-        }
-
-        @Override
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public String getShortName() {
-            return file.getName();
-        }
-
-        @Override
-        public String getCode() {
-            if (fileCacheEnabled) {
-                if (code == null || timeStamp != file.lastModified()) {
-                    try {
-                        code = read(getReader());
-                        timeStamp = file.lastModified();
-                    } catch (IOException e) {
-                    }
-                }
-                return code;
-            }
-            try {
-                return read(new FileReader(file));
-            } catch (IOException e) {
-            }
-            return null;
-        }
-
-        @Override
-        public String getPath() {
-            return path;
-        }
-
-        public URL getURL() {
-            return null;
-        }
-
-        @Override
-        public Reader getReader() {
-            if (code != null && timeStamp == file.lastModified()) {
-                return new StringReader(code);
-            }
-            try {
-                return new FileReader(file);
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException("Can't find file " + path);
-            }
-        }
-
-        @Override
-        protected void reset() {
-            this.code = null;
-        }
-
-    }
-
-    private static class URLSourceImpl extends SourceImpl {
-
-        private static final Map<URL, WeakReference<URLSourceImpl>> urlToSource = new HashMap<>();
-
-        public static URLSourceImpl get(URL url, String name) throws IOException {
-            WeakReference<URLSourceImpl> sourceRef = urlToSource.get(url);
-            URLSourceImpl source = sourceRef == null ? null : sourceRef.get();
-            if (source == null) {
-                source = new URLSourceImpl(url, name);
-                urlToSource.put(url, new WeakReference<>(source));
-            }
-            return source;
-        }
-
-        private final URL url;
-        private final String name;
-        private String code = null;  // A cache of the source contents
-
-        public URLSourceImpl(URL url, String name) throws IOException {
-            this.url = url;
-            this.name = name;
-            code = read(new InputStreamReader(url.openStream()));
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public String getShortName() {
-            return name;
-        }
-
-        public String getPath() {
-            return url.getPath();
-        }
-
-        public URL getURL() {
-            return url;
-        }
-
-        public Reader getReader() {
-            return new StringReader(code);
-        }
-
-        public String getCode() {
-            return code;
-        }
-
-        @Override
-        protected void reset() {
-        }
-
-    }
-
-    private static final class NullSourceImpl extends SourceImpl {
-
-        private final String description;
-
-        public NullSourceImpl(String description) {
-            this.description = description;
-        }
-
-        public String getName() {
-            return description;
-        }
-
-        public String getShortName() {
-            return description;
-        }
-
-        public String getPath() {
-            return null;
-        }
-
-        public URL getURL() {
-            return null;
-        }
-
-        public Reader getReader() {
-            return null;
-        }
-
-        public String getCode() {
-            throw new IllegalStateException("null source: " + description);
-        }
-
-        @Override
-        protected void reset() {
-        }
-
-    }
-
-    private static class SourceSectionImpl implements SourceSection {
-
-        private final Source source;
-        private final String identifier;
-        private final int startLine;
-        private final int startColumn;
-        private final int charIndex;
-        private final int charLength;
-
-        /**
-         * Creates a new object representing a contiguous text section within the source code of a
-         * guest language program's text.
-         * <p>
-         * The starting location of the section is specified using two different coordinate:
-         * <ul>
-         * <li><b>(row, column)</b>: rows and columns are 1-based, so the first character in a
-         * source file is at position {@code (1,1)}.</li>
-         * <li><b>character index</b>: 0-based offset of the character from the beginning of the
-         * source, so the first character in a file is at index {@code 0}.</li>
-         * </ul>
-         * The <b>newline</b> that terminates each line counts as a single character for the purpose
-         * of a character index. The (row,column) coordinates of a newline character should never
-         * appear in a text section.
-         * <p>
-         *
-         * @param source object representing the complete source program that contains this section
-         * @param identifier an identifier used when printing the section
-         * @param startLine the 1-based number of the start line of the section
-         * @param startColumn the 1-based number of the start column of the section
-         * @param charIndex the 0-based index of the first character of the section
-         * @param charLength the length of the section in number of characters
-         */
-        public SourceSectionImpl(Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) {
-            this.source = source;
-            this.identifier = identifier;
-            this.startLine = startLine;
-            this.startColumn = startColumn;
-            this.charIndex = charIndex;
-            this.charLength = charLength;
-        }
-
-        public final Source getSource() {
-            return source;
-        }
-
-        public final int getStartLine() {
-            return startLine;
-        }
-
-        public final int getStartColumn() {
-            return startColumn;
-        }
-
-        public final int getCharIndex() {
-            return charIndex;
-        }
-
-        public final int getCharLength() {
-            return charLength;
-        }
-
-        public final int getCharEndIndex() {
-            return charIndex + charLength;
-        }
-
-        public final String getIdentifier() {
-            return identifier;
-        }
-
-        public final String getCode() {
-            return getSource().getCode().substring(charIndex, charIndex + charLength);
-        }
-
-        public final String getShortDescription() {
-            return String.format("%s:%d", source.getShortName(), startLine);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("%s:%d", source.getName(), startLine);
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + charIndex;
-            result = prime * result + charLength;
-            result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
-            result = prime * result + ((source == null) ? 0 : source.hashCode());
-            result = prime * result + startColumn;
-            result = prime * result + startLine;
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (!(obj instanceof SourceSectionImpl)) {
-                return false;
-            }
-            SourceSectionImpl other = (SourceSectionImpl) obj;
-            if (charIndex != other.charIndex) {
-                return false;
-            }
-            if (charLength != other.charLength) {
-                return false;
-            }
-            if (identifier == null) {
-                if (other.identifier != null) {
-                    return false;
-                }
-            } else if (!identifier.equals(other.identifier)) {
-                return false;
-            }
-            if (source == null) {
-                if (other.source != null) {
-                    return false;
-                }
-            } else if (!source.equals(other.source)) {
-                return false;
-            }
-            if (startColumn != other.startColumn) {
-                return false;
-            }
-            if (startLine != other.startLine) {
-                return false;
-            }
-            return true;
-        }
-
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java	Fri Jun 06 17:51:47 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.source;
-
-import com.oracle.truffle.api.*;
-
-/**
- * A specification for a location in guest language source, expressed as a line number in a specific
- * instance of {@link Source}, suitable for hash table keys with equality defined in terms of
- * content.
- */
-public class SourceLineLocation implements Comparable<Object> {
-
-    private final Source source;
-    private final int line;
-
-    public SourceLineLocation(Source source, int line) {
-        assert source != null;
-        this.source = source;
-        this.line = line;
-    }
-
-    public SourceLineLocation(SourceSection sourceSection) {
-        this(sourceSection.getSource(), sourceSection.getStartLine());
-    }
-
-    public Source getSource() {
-        return source;
-    }
-
-    public int getLine() {
-        return line;
-    }
-
-    @Override
-    public String toString() {
-        return "SourceLine [" + source.getName() + ", " + line + "]";
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + line;
-        result = prime * result + source.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof SourceLineLocation)) {
-            return false;
-        }
-        SourceLineLocation other = (SourceLineLocation) obj;
-        if (line != other.line) {
-            return false;
-        }
-        return source.equals(other.source);
-    }
-
-    @Override
-    public int compareTo(Object o) {
-        final SourceLineLocation other = (SourceLineLocation) o;
-        final int nameOrder = source.getName().compareTo(other.source.getName());
-        if (nameOrder != 0) {
-            return nameOrder;
-        }
-        return Integer.compare(line, other.line);
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java	Fri Jun 06 22:13:00 2014 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.source;
+
+/**
+ * Description of contiguous section of text within a {@link Source} of program code; supports
+ * multiple modes of access to the text and its location. A special {@linkplain NullSourceSection
+ * null subtype} should be used for code that is not available from source, e.g language builtins.
+ *
+ * @see Source#createSection(String, int, int, int, int)
+ * @see Source#createSection(String, int, int, int)
+ * @see Source#createSection(String, int, int)
+ * @see Source#createSection(String, int)
+ * @see NullSourceSection
+ */
+public interface SourceSection {
+
+    // TODO support alternate text representations/encodings
+
+    /**
+     * Representation of the source program that contains this section.
+     *
+     * @return the source object
+     */
+    Source getSource();
+
+    /**
+     * Returns 1-based line number of the first character in this section (inclusive).
+     *
+     * @return the starting line number
+     */
+    int getStartLine();
+
+    /**
+     * Gets a representation of the first line of the section, suitable for a hash key.
+     */
+    LineLocation getLineLocation();
+
+    /**
+     * Returns the 1-based column number of the first character in this section (inclusive).
+     *
+     * @return the starting column number
+     */
+    int getStartColumn();
+
+    /**
+     * Returns the 0-based index of the first character in this section.
+     *
+     * @return the starting character index
+     */
+    int getCharIndex();
+
+    /**
+     * Returns the length of this section in characters.
+     *
+     * @return the number of characters in the section
+     */
+    int getCharLength();
+
+    /**
+     * Returns the index of the text position immediately following the last character in the
+     * section.
+     *
+     * @return the end position of the section
+     */
+    int getCharEndIndex();
+
+    /**
+     * Returns terse text describing this source section, typically used for printing the section.
+     *
+     * @return the identifier of the section
+     */
+    String getIdentifier();
+
+    /**
+     * Returns text described by this section.
+     *
+     * @return the code as a String object
+     */
+    String getCode();
+
+    /**
+     * Returns a short description of the source section, using just the file name, rather than its
+     * full path.
+     *
+     * @return a short description of the source section
+     */
+    String getShortDescription();
+
+}