# HG changeset patch # User Michael Van De Vanter # Date 1402117980 25200 # Node ID 74e142bd2b12ca97ca230ec5c64522fe2b73e8d9 # Parent 915ebb306fccb79bf67655ccef0bab465631af14 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 diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/NullSourceSection.java --- 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 { - -} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java --- 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. - *

- * Checks the position arguments for consistency with the source. - *

- * 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. - *

- * Checks the position arguments for consistency with the source. - *

- * 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. - *

- * This method performs no checks on the validity of the arguments. - *

- * 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); - -} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java --- 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: - *

- * 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. - *

- * 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"). - *

- * - * @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"; - } - - }; - -} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java --- /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 { + + public Source getSource(); + + /** + * Gets the 1-based number of a line in the source + */ + public int getLineNumber(); + +} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/NullSourceSection.java --- /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 + * builtins. + */ +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 builtins. + * + * @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 builtins. + * + * @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(); + } + +} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java --- /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: + *
    + *
  • Literal: A named text string. These are not indexed and should be considered + * value objects; equality is defined based on contents.
    + * See {@link Source#fromText(String, String)}
  • + *

    + *

  • File: Each file is represented as a canonical object, indexed by the + * absolute, canonical path name of the file. File contents are read lazily and contents + * optionally cached.
    + * See {@link Source#fromFileName(String)}
    + * See {@link Source#fromFileName(String, boolean)}
  • + *

    + *

  • URL: Each URL source is represented as a canonical object, indexed by the + * URL. Contents are read eagerly and cached.
    + * See {@link Source#fromURL(URL, String)}
  • + *

    + *

  • Reader: Contents are read eagerly and treated as a Literal + * .
    + * See {@link Source#fromReader(Reader, String)}
  • + *

    + *

  • Pseudo File: A literal text string that can be retrieved by name as if it + * were a file, unlike literal sources; useful for testing.
    + * See {@link Source#asPseudoFile(String, String)}
  • + *
+ *

+ * File cache: + *

    + *
  1. File content caching is optional, off by default.
  2. + *
  3. The first access to source file contents will result in the contents being read, and (if + * enabled) cached.
  4. + *
  5. If file contents have been cached, access to contents via {@link Source#getInputStream()} or + * {@link Source#getReader()} will be provided from the cache.
  6. + *
  7. Any access to file contents via the cache will result in a timestamp check and possible cache + * reload.
  8. + *
+ */ +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> 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 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 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, disabled 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. + *

+ * This method performs no checks on the validity of the arguments. + *

+ * 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. + *

+ * Checks the position arguments for consistency with the source. + *

+ * 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. + *

+ * Checks the position arguments for consistency with the source. + *

+ * 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> urlToSource = new HashMap<>(); + + public static URLSource get(URL url, String name) throws IOException { + WeakReference 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. + *

+ * The starting location of the section is specified using two different coordinate: + *

    + *
  • (row, column): rows and columns are 1-based, so the first character in a + * source file is at position {@code (1,1)}.
  • + *
  • character index: 0-based offset of the character from the beginning of the + * source, so the first character in a file is at index {@code 0}.
  • + *
+ * The newline 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. + *

+ * + * @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); + } + + } +} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java --- 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. - *

    - *
  • File: Each file is represented as a canonical object, indexed by the - * absolute, canonical path name of the file. File contents are read lazily and contents - * optionally cached.
  • - *
  • Literal Source: 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.
  • - *
  • Pseudo Files: A named text string used for testing; its contents can be - * retrieved by name, unlike literal sources.
  • - *
- *

- * File cache: - *

    - *
  1. File content caching is optional, off by default.
  2. - *
  3. The first access to source file contents will result in the contents being read, and (if - * enabled) cached.
  4. - *
  5. If file contents have been cached, access to contents via {@link Source#getInputStream()} or - * {@link Source#getReader()} will be provided from the cache.
  6. - *
  7. Any access to file contents via the cache will result in a timestamp check and possible cache - * reload.
  8. - *
- */ -public final class SourceFactory { - - // Only files and pseudo files are indexed. - private static final Map> 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, disabled 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 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> urlToSource = new HashMap<>(); - - public static URLSourceImpl get(URL url, String name) throws IOException { - WeakReference 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. - *

- * The starting location of the section is specified using two different coordinate: - *

    - *
  • (row, column): rows and columns are 1-based, so the first character in a - * source file is at position {@code (1,1)}.
  • - *
  • character index: 0-based offset of the character from the beginning of the - * source, so the first character in a file is at index {@code 0}.
  • - *
- * The newline 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. - *

- * - * @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; - } - - } - -} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java --- 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 { - - 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); - } - -} diff -r 915ebb306fcc -r 74e142bd2b12 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java --- /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(); + +}