changeset 21987:b2d1c8ff592a

Less classes in the source API package. Merging interfaces and their only implementation into final classes. Hiding NullSourceSection behind factory method. Using JDK's standard CharsetDecoder instead of proprietary BytesDecoder.
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Wed, 01 Jul 2015 10:23:36 +0200
parents 67ea94a23074
children 8a09e8c7725a
files truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/BytesSourceSectionTest.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/BytesDecoder.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/NullSourceSection.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertFalseBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java
diffstat 21 files changed, 271 insertions(+), 491 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java	Wed Jul 01 10:23:36 2015 +0200
@@ -48,7 +48,7 @@
     @Theory
     public void testSourceSections(int value0, int value1, int value2) {
         TestRootNode<SourceSection0> root = createRoot(SourceSection0Factory.getInstance());
-        SourceSection section = new NullSourceSection("a", "b");
+        SourceSection section = SourceSection.createUnavailable("a", "b");
         root.getNode().assignSourceSection(section);
         expectSourceSection(root.getNode(), section);
         assertThat((int) executeWith(root, value0), is(value0));
@@ -97,7 +97,8 @@
 
     @Test
     public void testCreateCast() {
-        SourceSection section = new NullSourceSection("a", "b");
+        SourceSection section = SourceSection.createUnavailable("a", "b");
+        assertNull(section.getSource());
         TestRootNode<SourceSection1> root = createRootPrefix(SourceSection1Factory.getInstance(), true, section);
         expectSourceSection(root.getNode(), section);
         assertThat((int) executeWith(root, 1), is(1));
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/BytesSourceSectionTest.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/BytesSourceSectionTest.java	Wed Jul 01 10:23:36 2015 +0200
@@ -35,7 +35,7 @@
     @Test
     public void testSectionsFromLineNumberASCII() {
         final byte[] bytes = "foo\nbar\nbaz\n".getBytes(StandardCharsets.US_ASCII);
-        final Source source = Source.fromBytes(bytes, "description", new BytesDecoder.UTF8BytesDecoder());
+        final Source source = Source.fromBytes(bytes, "description", StandardCharsets.US_ASCII);
         assertEquals("foo", source.createSection("identifier", 1).getCode());
         assertEquals("bar", source.createSection("identifier", 2).getCode());
         assertEquals("baz", source.createSection("identifier", 3).getCode());
@@ -44,7 +44,7 @@
     @Test
     public void testSectionsFromOffsetsASCII() {
         final byte[] bytes = "foo\nbar\nbaz\n".getBytes(StandardCharsets.US_ASCII);
-        final Source source = Source.fromBytes(bytes, "description", new BytesDecoder.UTF8BytesDecoder());
+        final Source source = Source.fromBytes(bytes, "description", StandardCharsets.US_ASCII);
         assertEquals("foo", source.createSection("identifier", 0, 3).getCode());
         assertEquals("bar", source.createSection("identifier", 4, 3).getCode());
         assertEquals("baz", source.createSection("identifier", 8, 3).getCode());
@@ -53,7 +53,7 @@
     @Test
     public void testOffset() {
         final byte[] bytes = "xxxfoo\nbar\nbaz\nxxx".getBytes(StandardCharsets.US_ASCII);
-        final Source source = Source.fromBytes(bytes, 3, bytes.length - 6, "description", new BytesDecoder.UTF8BytesDecoder());
+        final Source source = Source.fromBytes(bytes, 3, bytes.length - 6, "description", StandardCharsets.US_ASCII);
         assertEquals("foo", source.createSection("identifier", 0, 3).getCode());
         assertEquals("bar", source.createSection("identifier", 4, 3).getCode());
         assertEquals("baz", source.createSection("identifier", 8, 3).getCode());
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Wed Jul 01 10:23:36 2015 +0200
@@ -187,9 +187,8 @@
     protected static String sourceInfo(Node node) {
         final SourceSection src = node.getSourceSection();
         if (src != null) {
-            if (src instanceof NullSourceSection) {
-                final NullSourceSection nullSection = (NullSourceSection) src;
-                return nullSection.getShortDescription();
+            if (src.getSource() == null) {
+                return src.getShortDescription();
             } else {
                 return src.getSource().getName() + ":" + src.getStartLine();
             }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Jul 01 10:23:36 2015 +0200
@@ -728,7 +728,7 @@
 
     private static String displaySourceAttribution(Node node) {
         final SourceSection section = node.getSourceSection();
-        if (section instanceof NullSourceSection) {
+        if (section != null && section.getSource() == null) {
             return "source: " + section.getShortDescription();
         }
         if (section != null) {
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/BytesDecoder.java	Tue Jun 30 17:29:17 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +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.source;
-
-import java.nio.charset.*;
-import java.util.*;
-
-/**
- * For a language where strings do not map into Java strings, provides utilities to find line
- * endings and to decode raw bytes into an approximate representation for tools to display.
- * <p>
- * See {@link Source#fromBytes}.
- */
-public interface BytesDecoder {
-
-    String decode(byte[] bytes, int byteIndex, int length);
-
-    void decodeLines(byte[] bytes, int byteIndex, int length, LineMarker lineMarker);
-
-    public interface LineMarker {
-
-        void markLine(int index);
-
-    }
-
-    public static class UTF8BytesDecoder implements BytesDecoder {
-
-        @Override
-        public String decode(byte[] bytes, int byteIndex, int length) {
-            return new String(Arrays.copyOfRange(bytes, byteIndex, byteIndex + length), StandardCharsets.UTF_8);
-        }
-
-        @Override
-        public void decodeLines(byte[] bytes, int byteIndex, int length, LineMarker lineMarker) {
-            for (int n = byteIndex; n < byteIndex + length; n++) {
-                if (bytes[n] == '\n') {
-                    lineMarker.markLine(n + 1);
-                }
-            }
-        }
-
-    }
-
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java	Wed Jul 01 10:23:36 2015 +0200
@@ -24,37 +24,76 @@
  */
 package com.oracle.truffle.api.source;
 
-import java.util.*;
-
 /**
  * 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 {
+public final class LineLocation implements Comparable<LineLocation> {
+    private final Source source;
+    private final int line;
 
-    Source getSource();
+    LineLocation(Source source, int line) {
+        assert source != null;
+        this.source = source;
+        this.line = line;
+    }
+
+    public Source getSource() {
+        return source;
+    }
 
     /**
      * Gets the 1-based number of a line in the source.
+     * 
+     * @return value from 1 to infinity
      */
-    int getLineNumber();
+    public int getLineNumber() {
+        return line;
+    }
 
-    String getShortDescription();
+    public String getShortDescription() {
+        return source.getShortName() + ":" + line;
+    }
 
-    /**
-     * Default comparator by (1) textual path name, (2) line number.
-     */
-    Comparator<LineLocation> COMPARATOR = new Comparator<LineLocation>() {
+    @Override
+    public String toString() {
+        return "Line[" + getShortDescription() + "]";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + line;
+        result = prime * result + source.getHashKey().hashCode();
+        return result;
+    }
 
-        public int compare(LineLocation l1, LineLocation l2) {
-            final int sourceResult = l1.getSource().getPath().compareTo(l2.getSource().getPath());
-            if (sourceResult != 0) {
-                return sourceResult;
-            }
-            return Integer.compare(l1.getLineNumber(), l2.getLineNumber());
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof LineLocation)) {
+            return false;
         }
+        LineLocation other = (LineLocation) obj;
+        if (line != other.line) {
+            return false;
+        }
+        return source.getHashKey().equals(other.source.getHashKey());
+    }
 
-    };
-
+    @Override
+    public int compareTo(LineLocation o) {
+        final int sourceResult = this.getSource().getPath().compareTo(o.getSource().getPath());
+        if (sourceResult != 0) {
+            return sourceResult;
+        }
+        return Integer.compare(this.getLineNumber(), o.getLineNumber());
+    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/NullSourceSection.java	Tue Jun 30 17:29:17 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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 int getEndLine() {
-        throw new UnsupportedOperationException(this.toString());
-    }
-
-    public int getEndColumn() {
-        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();
-    }
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Wed Jul 01 10:23:36 2015 +0200
@@ -31,6 +31,11 @@
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.instrument.*;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
 
 /**
  * Representation of a guest language source code unit and its contents. Sources originate in
@@ -374,11 +379,11 @@
      *
      * @param bytes the raw bytes of the source
      * @param description a note about the origin, possibly useful for debugging
-     * @param decoder how to decode the bytes into Java strings
+     * @param charset how to decode the bytes into Java strings
      * @return a newly created, non-indexed source representation
      */
-    public static Source fromBytes(byte[] bytes, String description, BytesDecoder decoder) {
-        return fromBytes(bytes, 0, bytes.length, description, decoder);
+    public static Source fromBytes(byte[] bytes, String description, Charset charset) {
+        return fromBytes(bytes, 0, bytes.length, description, charset);
     }
 
     /**
@@ -391,13 +396,13 @@
      * @param byteIndex where the string starts in the byte array
      * @param length the length of the string in the byte array
      * @param description a note about the origin, possibly useful for debugging
-     * @param decoder how to decode the bytes into Java strings
+     * @param charset how to decode the bytes into Java strings
      * @return a newly created, non-indexed source representation
      */
-    public static Source fromBytes(byte[] bytes, int byteIndex, int length, String description, BytesDecoder decoder) {
+    public static Source fromBytes(byte[] bytes, int byteIndex, int length, String description, Charset charset) {
         CompilerAsserts.neverPartOfCompilation();
 
-        final BytesSource source = new BytesSource(description, bytes, byteIndex, length, decoder);
+        final BytesSource source = new BytesSource(description, bytes, byteIndex, length, charset);
         notifyNewSource(source).tagAs(Tags.FROM_BYTES);
         return source;
     }
@@ -645,7 +650,7 @@
      * @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);
+        return new SourceSection(null, this, identifier, startLine, startColumn, charIndex, length);
     }
 
     /**
@@ -671,7 +676,7 @@
             throw new IllegalArgumentException("column out of range");
         }
         final int startOffset = lineStartOffset + startColumn - 1;
-        return new DefaultSourceSection(this, identifier, startLine, startColumn, startOffset, length);
+        return new SourceSection(null, this, identifier, startLine, startColumn, startOffset, length);
     }
 
     /**
@@ -697,7 +702,7 @@
         checkRange(charIndex, length);
         final int startLine = getLineNumber(charIndex);
         final int startColumn = charIndex - getLineStartOffset(startLine) + 1;
-        return new DefaultSourceSection(this, identifier, startLine, startColumn, charIndex, length);
+        return new SourceSection(null, this, identifier, startLine, startColumn, charIndex, length);
     }
 
     void checkRange(int charIndex, int length) {
@@ -730,7 +735,7 @@
      * @return a representation of a line in this source
      */
     public final LineLocation createLineLocation(int lineNumber) {
-        return new LineLocationImpl(this, lineNumber);
+        return new LineLocation(this, lineNumber);
     }
 
     /**
@@ -1106,14 +1111,14 @@
         private final byte[] bytes;
         private final int byteIndex;
         private final int length;
-        private final BytesDecoder decoder;
+        private final CharsetDecoder decoder;
 
-        public BytesSource(String name, byte[] bytes, int byteIndex, int length, BytesDecoder decoder) {
+        public BytesSource(String name, byte[] bytes, int byteIndex, int length, Charset decoder) {
             this.name = name;
             this.bytes = bytes;
             this.byteIndex = byteIndex;
             this.length = length;
-            this.decoder = decoder;
+            this.decoder = decoder.newDecoder();
         }
 
         @Override
@@ -1147,12 +1152,26 @@
 
         @Override
         public String getCode() {
-            return decoder.decode(bytes, byteIndex, length);
+            ByteBuffer bb = ByteBuffer.wrap(bytes, byteIndex, length);
+            CharBuffer chb;
+            try {
+                chb = decoder.decode(bb);
+            } catch (CharacterCodingException ex) {
+                return "";
+            }
+            return chb.toString();
         }
 
         @Override
         public String getCode(int byteOffset, int codeLength) {
-            return decoder.decode(bytes, byteIndex + byteOffset, codeLength);
+            ByteBuffer bb = ByteBuffer.wrap(bytes, byteIndex + byteOffset, codeLength);
+            CharBuffer chb;
+            try {
+                chb = decoder.decode(bb);
+            } catch (CharacterCodingException ex) {
+                return "";
+            }
+            return chb.toString();
         }
 
         @Override
@@ -1164,226 +1183,8 @@
 
         @Override
         TextMap createTextMap() {
-            return TextMap.fromBytes(bytes, byteIndex, length, decoder);
-        }
-    }
-
-    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 Source getSource() {
-            return source;
-        }
-
-        @Override
-        public int getStartLine() {
-            return startLine;
-        }
-
-        @Override
-        public LineLocation getLineLocation() {
-            return source.createLineLocation(startLine);
-        }
-
-        @Override
-        public int getStartColumn() {
-            return startColumn;
-        }
-
-        public int getEndLine() {
-            return source.getLineNumber(charIndex + charLength - 1);
-        }
-
-        public int getEndColumn() {
-            return source.getColumnNumber(charIndex + charLength - 1);
-        }
-
-        @Override
-        public int getCharIndex() {
-            return charIndex;
-        }
-
-        @Override
-        public int getCharLength() {
-            return charLength;
-        }
-
-        @Override
-        public int getCharEndIndex() {
-            return charIndex + charLength;
-        }
-
-        @Override
-        public String getIdentifier() {
-            return identifier;
-        }
-
-        @Override
-        public String getCode() {
-            return getSource().getCode(charIndex, charLength);
-        }
-
-        @Override
-        public String getShortDescription() {
-            return String.format("%s:%d", source.getShortName(), startLine);
-        }
-
-        @Override
-        public String toString() {
-            return getCode();
+            return TextMap.fromString(getCode());
         }
-
-        @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 getShortDescription() {
-            return source.getShortName() + ":" + line;
-        }
-
-        @Override
-        public String toString() {
-            return "Line[" + getShortDescription() + "]";
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + line;
-            result = prime * result + source.getHashKey().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.getHashKey().equals(other.source.getHashKey());
-        }
-
     }
 
     /**
@@ -1462,29 +1263,6 @@
             return new TextMap(nlOffsets, textLength, finalNL);
         }
 
-        public static TextMap fromBytes(byte[] bytes, int byteIndex, int length, BytesDecoder bytesDecoder) {
-            final ArrayList<Integer> lines = new ArrayList<>();
-            lines.add(0);
-
-            bytesDecoder.decodeLines(bytes, byteIndex, length, new BytesDecoder.LineMarker() {
-
-                public void markLine(int index) {
-                    lines.add(index);
-                }
-            });
-
-            lines.add(Integer.MAX_VALUE);
-
-            final int[] nlOffsets = new int[lines.size()];
-            for (int line = 0; line < lines.size(); line++) {
-                nlOffsets[line] = lines.get(line);
-            }
-
-            final boolean finalNL = length > 0 && (length == nlOffsets[nlOffsets.length - 2]);
-
-            return new TextMap(nlOffsets, length, finalNL);
-        }
-
         /**
          * Converts 0-based character offset to 1-based number of the line containing the character.
          *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java	Wed Jul 01 10:23:36 2015 +0200
@@ -26,72 +26,129 @@
 
 /**
  * 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.
+ * multiple modes of access to the text and its location. A special
+ * {@link #createUnavailable(java.lang.String, java.lang.String) null value} 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
+ * @see #createUnavailable
  */
-public interface SourceSection {
+public final class 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;
+    private final String kind;
 
-    // TODO support alternate text representations/encodings
+    /**
+     * 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
+     */
+    SourceSection(String kind, Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) {
+        this.kind = kind;
+        this.source = source;
+        this.identifier = identifier;
+        this.startLine = startLine;
+        this.startColumn = startColumn;
+        this.charIndex = charIndex;
+        this.charLength = charLength;
+    }
 
     /**
      * Representation of the source program that contains this section.
      *
      * @return the source object
      */
-    Source getSource();
+    public Source getSource() {
+        return source;
+    }
 
     /**
      * Returns 1-based line number of the first character in this section (inclusive).
      *
      * @return the starting line number
      */
-    int getStartLine();
+    public int getStartLine() {
+        return startLine;
+    }
 
     /**
      * Gets a representation of the first line of the section, suitable for a hash key.
+     * 
+     * @return first line of the section
      */
-    LineLocation getLineLocation();
+    public LineLocation getLineLocation() {
+        return source.createLineLocation(startLine);
+    }
 
     /**
      * Returns the 1-based column number of the first character in this section (inclusive).
      *
      * @return the starting column number
      */
-    int getStartColumn();
+    public int getStartColumn() {
+        return startColumn;
+    }
 
     /**
      * Returns 1-based line number of the last character in this section (inclusive).
      *
      * @return the starting line number
      */
-    int getEndLine();
+    public int getEndLine() {
+        return source.getLineNumber(charIndex + charLength - 1);
+    }
 
     /**
      * Returns the 1-based column number of the last character in this section (inclusive).
      *
      * @return the starting column number
      */
-    int getEndColumn();
+    public int getEndColumn() {
+        return source.getColumnNumber(charIndex + charLength - 1);
+    }
 
     /**
      * Returns the 0-based index of the first character in this section.
      *
      * @return the starting character index
      */
-    int getCharIndex();
+    public int getCharIndex() {
+        return charIndex;
+    }
 
     /**
      * Returns the length of this section in characters.
      *
      * @return the number of characters in the section
      */
-    int getCharLength();
+    public int getCharLength() {
+        return charLength;
+    }
 
     /**
      * Returns the index of the text position immediately following the last character in the
@@ -99,21 +156,27 @@
      *
      * @return the end position of the section
      */
-    int getCharEndIndex();
+    public int getCharEndIndex() {
+        return charIndex + charLength;
+    }
 
     /**
      * Returns terse text describing this source section, typically used for printing the section.
      *
      * @return the identifier of the section
      */
-    String getIdentifier();
+    public String getIdentifier() {
+        return identifier;
+    }
 
     /**
      * Returns text described by this section.
      *
      * @return the code as a String object
      */
-    String getCode();
+    public String getCode() {
+        return getSource().getCode(charIndex, charLength);
+    }
 
     /**
      * Returns a short description of the source section, using just the file name, rather than its
@@ -121,6 +184,84 @@
      *
      * @return a short description of the source section
      */
-    String getShortDescription();
+    public String getShortDescription() {
+        if (source == null) {
+            return kind + ": " + identifier;
+        }
+        return String.format("%s:%d", source.getShortName(), startLine);
+    }
+
+    @Override
+    public String toString() {
+        return getCode();
+    }
+
+    @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 SourceSection)) {
+            return false;
+        }
+        SourceSection other = (SourceSection) 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;
+    }
+
+    /**
+     * Placeholder for source that is unavailable, e.g. for language <em>builtins</em>. The
+     * <code>SourceSection</code> created by this method returns <code>null</code> when queried for
+     * a {@link #getSource()} - regular source sections created via one of
+     * {@link Source#createSection(java.lang.String, int) Source.createSection} methods have a non-
+     * <code>null</code> source.
+     *
+     * @param kind the general category, e.g. "JS builtin"
+     * @param name specific name for this section
+     * @return source section which is mostly <em>empty</em>
+     */
+    public static SourceSection createUnavailable(String kind, String name) {
+        return new SourceSection(kind, null, name, -1, -1, -1, -1);
+    }
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Wed Jul 01 10:23:36 2015 +0200
@@ -321,7 +321,7 @@
         result.append("Type error");
         if (ex.getNode() != null && ex.getNode().getSourceSection() != null) {
             SourceSection ss = ex.getNode().getSourceSection();
-            if (ss != null && !(ss instanceof NullSourceSection)) {
+            if (ss != null && ss.getSource() != null) {
                 result.append(" at ").append(ss.getSource().getShortName()).append(" line ").append(ss.getStartLine()).append(" col ").append(ss.getStartColumn());
             }
         }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertFalseBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertFalseBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -37,7 +37,7 @@
 public abstract class SLAssertFalseBuiltin extends SLBuiltinNode {
 
     public SLAssertFalseBuiltin() {
-        super(new NullSourceSection("SL builtin", "assertFalse"));
+        super(SourceSection.createUnavailable("SL builtin", "assertFalse"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -37,7 +37,7 @@
 public abstract class SLAssertTrueBuiltin extends SLBuiltinNode {
 
     public SLAssertTrueBuiltin() {
-        super(new NullSourceSection("SL builtin", "assertTrue"));
+        super(SourceSection.createUnavailable("SL builtin", "assertTrue"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -37,7 +37,7 @@
 public abstract class SLDefineFunctionBuiltin extends SLBuiltinNode {
 
     public SLDefineFunctionBuiltin() {
-        super(new NullSourceSection("SL builtin", "defineFunction"));
+        super(SourceSection.createUnavailable("SL builtin", "defineFunction"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -37,7 +37,7 @@
 public abstract class SLHelloEqualsWorldBuiltin extends SLBuiltinNode {
 
     public SLHelloEqualsWorldBuiltin() {
-        super(new NullSourceSection("SL builtin", "helloEqualsWorld"));
+        super(SourceSection.createUnavailable("SL builtin", "helloEqualsWorld"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -33,7 +33,7 @@
 public abstract class SLNanoTimeBuiltin extends SLBuiltinNode {
 
     public SLNanoTimeBuiltin() {
-        super(new NullSourceSection("SL builtin", "nanoTime"));
+        super(SourceSection.createUnavailable("SL builtin", "nanoTime"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -33,7 +33,7 @@
 public abstract class SLNewObjectBuiltin extends SLBuiltinNode {
 
     public SLNewObjectBuiltin() {
-        super(new NullSourceSection("SL builtin", "new"));
+        super(SourceSection.createUnavailable("SL builtin", "new"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -43,7 +43,7 @@
 public abstract class SLPrintlnBuiltin extends SLBuiltinNode {
 
     public SLPrintlnBuiltin() {
-        super(new NullSourceSection("SL builtin", "println"));
+        super(SourceSection.createUnavailable("SL builtin", "println"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -38,7 +38,7 @@
 public abstract class SLReadlnBuiltin extends SLBuiltinNode {
 
     public SLReadlnBuiltin() {
-        super(new NullSourceSection("SL builtin", "readln"));
+        super(SourceSection.createUnavailable("SL builtin", "readln"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java	Wed Jul 01 10:23:36 2015 +0200
@@ -39,7 +39,7 @@
 public abstract class SLStackTraceBuiltin extends SLBuiltinNode {
 
     public SLStackTraceBuiltin() {
-        super(new NullSourceSection("SL builtin", "stacktrace"));
+        super(SourceSection.createUnavailable("SL builtin", "stacktrace"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Wed Jul 01 10:23:36 2015 +0200
@@ -275,7 +275,7 @@
     private static final class LineLocationEntryComparator implements Comparator<Entry<LineLocation, CoverageRecord>> {
 
         public int compare(Entry<LineLocation, CoverageRecord> e1, Entry<LineLocation, CoverageRecord> e2) {
-            return LineLocation.COMPARATOR.compare(e1.getKey(), e2.getKey());
+            return e1.getKey().compareTo(e2.getKey());
         }
     }
 
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Tue Jun 30 17:29:17 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Wed Jul 01 10:23:36 2015 +0200
@@ -109,7 +109,7 @@
         @Override
         public void newProbeInserted(Probe probe) {
             final SourceSection sourceSection = probe.getProbedSourceSection();
-            if (sourceSection != null && !(sourceSection instanceof NullSourceSection)) {
+            if (sourceSection != null && sourceSection.getSource() != null) {
                 final LineLocation lineLocation = sourceSection.getLineLocation();
                 if (TRACE) {
                     trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());