changeset 15630:2d63ce48d222

Truffle/Source Attribution: Replace direct creation of SourceSection objects with factory methods on Source; two of these greatly simplify source attribution by automatically computing either the row/column start location from a character offset or vice versa, depending on what?s made available from the parser. Minor API change on Visualizer.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 13 May 2014 18:28:33 -0700
parents 357e7202de5b
children 8de99b84c9cd
files 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/impl/DefaultSourceSection.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java
diffstat 6 files changed, 257 insertions(+), 186 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java	Mon May 12 21:29:29 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java	Tue May 13 18:28:33 2014 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -26,6 +26,8 @@
 
 import java.io.*;
 
+import com.oracle.truffle.api.source.*;
+
 /**
  * Represents a unit (typically a file) of guest language source code.
  */
@@ -97,4 +99,61 @@
      */
     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	Mon May 12 21:29:29 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java	Tue May 13 18:28:33 2014 -0700
@@ -25,46 +25,62 @@
 package com.oracle.truffle.api;
 
 /**
- * Description of contiguous text section within the source code of a guest language program.
+ * 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
+
     /**
-     * Returns the object representing the source program that contains this section.
+     * 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 source section (inclusive).
+     * 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 source section (inclusive).
+     * 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 source section.
-     * <p>
-     * The complete text of the source that contains this section can be retrieved via
-     * {@link Source#getCode()}.
+     * Returns the 0-based index of the first character in this section.
      *
      * @return the starting character index
      */
     int getCharIndex();
 
     /**
-     * Returns the length of this source section in characters.
-     * <p>
-     * The complete text of the source that contains this section can be retrieved via
-     * {@link Source#getCode()}.
+     * Returns the length of this section in characters.
      *
      * @return the number of characters in the section
      */
@@ -79,14 +95,14 @@
     int getCharEndIndex();
 
     /**
-     * Returns the identifier of this source section that is used for printing the section.
+     * Returns terse text describing this source section, typically used for printing the section.
      *
      * @return the identifier of the section
      */
     String getIdentifier();
 
     /**
-     * Returns text of the code represented by this source section.
+     * Returns text described by this section.
      *
      * @return the code as a String object
      */
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultSourceSection.java	Mon May 12 21:29:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2013, 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.impl;
-
-import com.oracle.truffle.api.*;
-
-/**
- * Represents a contiguous text section within the source code of a guest language program.
- */
-public 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;
-    }
-
-    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 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;
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Mon May 12 21:29:29 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Tue May 13 18:28:33 2014 -0700
@@ -61,7 +61,7 @@
     /**
      * Converts a value in the guest language to a display string.
      */
-    String displayValue(Object value);
+    String displayValue(ExecutionContext context, Object value);
 
     /**
      * Converts a slot identifier in the guest language to a display string.
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java	Mon May 12 21:29:29 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java	Tue May 13 18:28:33 2014 -0700
@@ -69,7 +69,7 @@
         return callTarget.toString();
     }
 
-    public String displayValue(Object value) {
+    public String displayValue(ExecutionContext context, Object value) {
         return value.toString();
     }
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java	Mon May 12 21:29:29 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java	Tue May 13 18:28:33 2014 -0700
@@ -199,6 +199,32 @@
             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();
@@ -211,7 +237,7 @@
         }
     }
 
-    public static class LiteralSourceImpl extends SourceImpl {
+    private static class LiteralSourceImpl extends SourceImpl {
 
         private final String name; // Name used originally to describe the source
         private final String code;
@@ -337,4 +363,142 @@
 
     }
 
+    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;
+        }
+
+    }
+
 }