# HG changeset patch # User Michael Van De Vanter # Date 1400952883 25200 # Node ID 09ac9ac9c4fc88d31f79e10371079a4559cf003f # Parent eb947cc7bff930c4fe7cadba0e6c56a2003dcfc9 Truffle: SourceManager renamed to SourceFactory - All methods are static, no longer accessed via ExecutionContext - Sources are indexed with weak references - File content caching is now optional; off by default diff -r eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Wed May 21 21:07:15 2014 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Sat May 24 10:34:43 2014 -0700 @@ -39,7 +39,6 @@ public abstract class ExecutionContext { private final ProbeManager probeManager = new ProbeManager(); - private final SourceManager sourceManager = new SourceManager(); private final List sourceListeners = new ArrayList<>(); private Visualizer visualizer = new DefaultVisualizer(); @@ -64,13 +63,6 @@ } /** - * Gets access to source management services. - */ - public final SourceManager getSourceManager() { - return sourceManager; - } - - /** * Registers a tool interested in being notified about the loading of {@link Source}s. */ public final void addSourceListener(SourceListener listener) { diff -r eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java Sat May 24 10:34:43 2014 -0700 @@ -0,0 +1,546 @@ +/* + * 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.util.*; + +import com.oracle.truffle.api.*; + +/** + * Provider for canonical representations of source code. Three kinds of sources are supported. + * + *

+ * 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 fake 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 RuntimeException if the file can not be read + */ + public static Source fromFile(String fileName, boolean reset) throws RuntimeException { + + // TODO (mlvdv) throw IOException + + SourceImpl source = lookup(fileName); + if (source == null) { + final File file = new File(fileName); + String path = null; + if (file.exists()) { + try { + path = file.getCanonicalPath(); + } catch (IOException e) { + throw new RuntimeException("Can't find file " + fileName); + } + } + 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 RuntimeException if the file can not be read + */ + public static Source fromFile(String fileName) throws RuntimeException { + return fromFile(fileName, false); + } + + /** + * Creates a non-canonical source from literal text. + * + * @param code textual source code + * @param description a note about the origin, possibly useful for 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 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 fakeFileName string to use for indexing/lookup + * @return a newly created, source representation, canonical with respect to its name + */ + public static Source asFakeFile(String code, String fakeFileName) { + final SourceImpl source = new LiteralSourceImpl(fakeFileName, code); + store(fakeFileName, 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 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; + } + + @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; + } + + @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 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 eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java Wed May 21 21:07:15 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,504 +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.util.*; - -import com.oracle.truffle.api.*; - -/** - * A representation of source code information, suitable for hash table keys with equality defined - * in terms of content. There are three kinds of sources supported at present. - *

    - *
  • File: Each file is represented as a canonical object, indexed by the - * absolute, canonical path name of the file. The textual contents of the file may be supplied when - * the object is created, or it may be read lazily. Only one lazy attempt will be made to read a - * file, and failure will result silently in null content.
  • - *
  • Literal Source: A named text string, whose contents are supplied concretely - * (possibly via an {@link InputStream}), can also be used as a source. These are represented as - * value objects whose equality depends on both name and contents.
  • - *
  • Fake Files: A named text string used for testing; its contents can be - * retrieved by name, unlike literal sources.
  • - *
- *

- * Cache: - *

    - *
  1. Access to source file contents via {@link Source#getInputStream()} or - * {@link Source#getReader()} does not by itself result in the file's contents being cached - * in the {@link Source} object.
  2. - *
  3. Access to source file contents via {@link Source#getCode()} or any other {@link Source} - * methods related to file's contents will result in the contents being cached in the - * {@link Source} object.
  4. - *
  5. Once source file contents have been cached, access to source file contents via - * {@link Source#getInputStream()} or {@link Source#getReader()} will be provided from the cache.
  6. - *
  7. Any access to source file contents via the cache will result in a timestamp check and - * possible cache reload.
  8. - *
- */ -public final class SourceManager { - - // Only files and fake files are indexed. - private final Map pathToSource = new HashMap<>(); - - public SourceManager() { - - } - - /** - * Gets the canonical representation of a source file, whose contents will be read lazily and - * then cached. - * - * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read - */ - public Source get(String fileName, boolean reset) { - - SourceImpl source = pathToSource.get(fileName); - if (source == null) { - final File file = new File(fileName); - String path = null; - if (file.exists()) { - try { - path = file.getCanonicalPath(); - } catch (IOException e) { - throw new RuntimeException("Can't find file " + fileName); - } - } - source = pathToSource.get(path); - if (source == null) { - source = new FileSourceImpl(file, fileName, path); - pathToSource.put(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. - */ - public Source get(String fileName) { - return get(fileName, false); - } - - /** - * Creates a source from literal text. - */ - @SuppressWarnings("static-method") - public Source get(String name, String code) { - assert code != null; - return new LiteralSourceImpl(name, code); - } - - /** - * Creates a source whose contents will be read immediately and cached. - */ - @SuppressWarnings("static-method") - public Source get(String name, InputStream stream) throws IOException { - InputStreamReader reader = new InputStreamReader(stream); - return new LiteralSourceImpl(name, readCode(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. - */ - public Source getFakeFile(String name, String code) { - final SourceImpl source = new LiteralSourceImpl(name, code); - pathToSource.put(name, source); - return source; - } - - private static String readCode(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 { - - 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; - } - - @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 (code == null || timeStamp != file.lastModified()) { - try { - code = readCode(getReader()); - timeStamp = file.lastModified(); - } catch (IOException e) { - } - } - return code; - } - - @Override - public String getPath() { - return path; - } - - @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 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 eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java Wed May 21 21:07:15 2014 -0700 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java Sat May 24 10:34:43 2014 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -54,20 +54,21 @@ static class TestCase { protected final Description name; - protected final Source source; + protected final Path path; + protected final String sourceName; protected final String testInput; protected final String expectedOutput; protected String actualOutput; - protected TestCase(Class testClass, String name, Source source, String testInput, String expectedOutput) { - this.name = Description.createTestDescription(testClass, name); - this.source = source; + protected TestCase(Class testClass, String baseName, String sourceName, Path path, String testInput, String expectedOutput) { + this.name = Description.createTestDescription(testClass, baseName); + this.sourceName = sourceName; + this.path = path; this.testInput = testInput; this.expectedOutput = expectedOutput; } } - private final SourceManager sourceManager = new SourceManager(); private final List testCases; public SLTestRunner(Class runningClass) throws InitializationError { @@ -89,7 +90,7 @@ return testCases; } - protected List createTests(final Class c) throws IOException, InitializationError { + protected static List createTests(final Class c) throws IOException, InitializationError { SLTestSuite suite = c.getAnnotation(SLTestSuite.class); if (suite == null) { throw new InitializationError(String.format("@%s annotation required on class '%s' to run with '%s'.", SLTestSuite.class.getSimpleName(), c.getName(), SLTestRunner.class.getSimpleName())); @@ -130,7 +131,7 @@ expectedOutput = readAllLines(outputFile); } - foundCases.add(new TestCase(c, baseName, sourceManager.get(sourceName, readAllLines(sourceFile)), testInput, expectedOutput)); + foundCases.add(new TestCase(c, baseName, sourceName, sourceFile, testInput, expectedOutput)); } return FileVisitResult.CONTINUE; } @@ -154,8 +155,9 @@ ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream printer = new PrintStream(out); try { - SLContext context = new SLContext(sourceManager, new BufferedReader(new StringReader(repeat(testCase.testInput, REPEATS))), printer); - SLMain.run(context, testCase.source, null, REPEATS); + SLContext context = new SLContext(new BufferedReader(new StringReader(repeat(testCase.testInput, REPEATS))), printer); + final Source source = SourceFactory.fromText(readAllLines(testCase.path), testCase.sourceName); + SLMain.run(context, source, null, REPEATS); String actualOutput = new String(out.toByteArray()); Assert.assertEquals(repeat(testCase.expectedOutput, REPEATS), actualOutput); diff -r eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Wed May 21 21:07:15 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Sat May 24 10:34:43 2014 -0700 @@ -27,6 +27,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.builtins.*; @@ -117,13 +118,14 @@ * The main entry point. Use the mx command "mx sl" to run it with the correct class path setup. */ public static void main(String[] args) throws IOException { - SourceManager sourceManager = new SourceManager(); + + SLContext context = new SLContext(new BufferedReader(new InputStreamReader(System.in)), System.out); Source source; if (args.length == 0) { - source = sourceManager.get("stdin", System.in); + source = SourceFactory.fromReader(new InputStreamReader(System.in), "stdin"); } else { - source = sourceManager.get(args[0]); + source = SourceFactory.fromFile(args[0]); } int repeats = 1; @@ -131,7 +133,6 @@ repeats = Integer.parseInt(args[1]); } - SLContext context = new SLContext(sourceManager, new BufferedReader(new InputStreamReader(System.in)), System.out); run(context, source, System.out, repeats); } @@ -144,8 +145,16 @@ logOutput.println("== running on " + Truffle.getRuntime().getName()); } + final SourceCallback sourceCallback = context.getSourceCallback(); + /* Parse the SL source file. */ + if (sourceCallback != null) { + sourceCallback.startLoading(source); + } Parser.parseSL(context, source); + if (sourceCallback != null) { + sourceCallback.endLoading(source); + } /* Lookup our main entry point, which is per definition always named "main". */ SLFunction main = context.getFunctionRegistry().lookup("main"); if (main.getCallTarget() == null) { diff -r eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java Wed May 21 21:07:15 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java Sat May 24 10:34:43 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.parser.*; import com.oracle.truffle.sl.runtime.*; @@ -44,7 +45,7 @@ @SlowPath private static void doDefineFunction(SLContext context, String code) { - Source source = context.getSourceManager().get("[defineFunction]", code); + Source source = SourceFactory.fromText(code, "[defineFunction]"); /* The same parsing code as for parsing the initial source. */ Parser.parseSL(context, source); } diff -r eb947cc7bff9 -r 09ac9ac9c4fc graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Wed May 21 21:07:15 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Sat May 24 10:34:43 2014 -0700 @@ -24,10 +24,11 @@ import java.io.*; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.nodes.*; import com.oracle.truffle.sl.nodes.local.*; @@ -43,26 +44,27 @@ * However, if two separate scripts run in one Java VM at the same time, they have a different * context. Therefore, the context is not a singleton. */ -public final class SLContext { - private final SourceManager sourceManager; +public final class SLContext extends ExecutionContext { private final BufferedReader input; private final PrintStream output; private final SLFunctionRegistry functionRegistry; + private SourceCallback sourceCallback = null; - public SLContext(SourceManager sourceManager, BufferedReader input, PrintStream output) { - this.sourceManager = sourceManager; + public SLContext(BufferedReader input, PrintStream output) { this.input = input; this.output = output; this.functionRegistry = new SLFunctionRegistry(); - installBuiltins(); } - /** - * Returns the source manger that controls all SL source code that is executed. - */ - public SourceManager getSourceManager() { - return sourceManager; + @Override + public String getLanguageShortName() { + return "Simple"; + } + + @Override + public void setSourceCallback(SourceCallback sourceCallback) { + this.sourceCallback = sourceCallback; } /** @@ -88,6 +90,10 @@ return functionRegistry; } + public SourceCallback getSourceCallback() { + return sourceCallback; + } + /** * Adds all builtin functions to the {@link SLFunctionRegistry}. This method lists all * {@link SLBuiltinNode builtin implementation classes}.