comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java @ 13455:69d2e4baa215

Truffle: new infrastructure related to instrumentation, and in particular debugging: support for managing Source objects; framework for generalized "instrumentation proxy nodes" (to be inserted into ASTs with no runtime cost when inactive), and "probes" (which can be attached to proxy nodes to receive event notification); a rudimentary interface and abstract implementation for a "debug manager" (mostly a placeholder at this point); and the beginning of a language-agnostic ExecutionContext interface.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 17 Dec 2013 20:22:45 -0800
parents
children f76593e3fedb
comparison
equal deleted inserted replaced
13306:dfb780080923 13455:69d2e4baa215
1 /*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.oracle.truffle.api.source;
26
27 import java.io.*;
28 import java.util.*;
29
30 import com.oracle.truffle.api.*;
31
32 /**
33 * A representation of source code information, suitable for hash table keys with equality defined
34 * in terms of content. There are three kinds of sources supported at present.
35 * <ul>
36 * <li><strong>File:</strong>Each file is represented as a canonical object, indexed by the
37 * absolute, canonical path name of the file. The textual contents of the file may be supplied when
38 * the object is created, or it may be read lazily. Only one lazy attempt will be made to read a
39 * file, and failure will result silently in null content.</li>
40 * <li><strong>Literal Source:</strong> A named text string, whose contents are supplied concretely
41 * (possibly via an {@link InputStream}), can also be used as a source. These are represented as
42 * value objects whose equality depends on both name and contents.</li>
43 * <li><strong>Fake Files:</strong> A named text string used for testing; its contents can be
44 * retrieved by name, unlike literal sources.</li>
45 * </ul>
46 */
47 public final class SourceManager {
48
49 // Only files and fake files are indexed.
50 private final Map<String, SourceImpl> sourceMap = new HashMap<>();
51
52 public SourceManager() {
53
54 }
55
56 /**
57 * Gets the canonical representation of a source file, whose contents will be read lazily and
58 * then cached.
59 *
60 * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read.
61 */
62 public Source get(String fileName, boolean reset) {
63 SourceImpl source = sourceMap.get(fileName);
64 if (source == null) {
65 String path = findPath(fileName);
66 if (path == null) {
67 throw new RuntimeException("Can't find file " + fileName);
68 }
69 source = sourceMap.get(path);
70 if (source == null) {
71 source = new FileSourceImpl(fileName, path);
72 sourceMap.put(path, source);
73 }
74 } else {
75 if (reset) {
76 source.reset();
77 }
78 }
79 return source;
80 }
81
82 /**
83 * Gets the canonical representation of a source file, whose contents will be read lazily and
84 * then cached.
85 */
86 public Source get(String fileName) {
87 return get(fileName, false);
88 }
89
90 /**
91 * Creates a source from literal text.
92 */
93 @SuppressWarnings("static-method")
94 public Source get(String name, String code) {
95 assert code != null;
96 return new LiteralSourceImpl(name, code);
97 }
98
99 /**
100 * Creates a source whose contents will be read immediately and cached.
101 */
102 @SuppressWarnings("static-method")
103 public Source get(String name, InputStream stream) throws IOException {
104 InputStreamReader reader = new InputStreamReader(stream);
105 return new LiteralSourceImpl(name, readCode(reader));
106 }
107
108 /**
109 * Creates a source from literal text, but which acts as a file and can be retrieved by name
110 * (unlike other literal sources); intended for testing.
111 */
112 public Source getFakeFile(String name, String code) {
113 final SourceImpl source = new LiteralSourceImpl(name, code);
114 sourceMap.put(name, source);
115 return source;
116 }
117
118 // If it names a real file, get the (canonical) normalized absolute path.
119 private static String findPath(String name) {
120 final File file = new File(name);
121 if (file.exists()) {
122 try {
123 return file.getCanonicalPath();
124 } catch (IOException e) {
125 }
126 }
127 return null;
128 }
129
130 private static String readCode(Reader reader) throws IOException {
131 final StringBuilder builder = new StringBuilder();
132 final char[] buffer = new char[1024];
133
134 while (true) {
135 final int n = reader.read(buffer);
136 if (n == -1) {
137 break;
138 }
139 builder.append(buffer, 0, n);
140 }
141
142 return builder.toString();
143 }
144
145 private abstract static class SourceImpl implements Source {
146
147 protected TextMap textMap = null;
148
149 protected abstract void reset();
150
151 public final InputStream getInputStream() {
152 return new ByteArrayInputStream(getCode().getBytes());
153 }
154
155 /**
156 * Gets the text (not including a possible terminating newline) in a (1-based) numbered
157 * line.
158 */
159 public final String getCode(int lineNumber) {
160 checkTextMap();
161 final int offset = textMap.lineStartOffset(lineNumber);
162 final int length = textMap.lineLength(lineNumber);
163 return getCode().substring(offset, offset + length);
164 }
165
166 /**
167 * The number of text lines in the source.
168 */
169 public final int getLineCount() {
170 return checkTextMap().lineCount();
171 }
172
173 /**
174 * The 1-based number of the line that includes a 0-based character offset.
175 */
176 public final int getLineNumber(int offset) {
177 return checkTextMap().offsetToLine(offset);
178 }
179
180 /**
181 * The 0-based character offset at the start of a (1-based) numbered line.
182 */
183 public final int getLineStartOffset(int lineNumber) {
184 return checkTextMap().lineStartOffset(lineNumber);
185 }
186
187 /**
188 * The number of characters (not counting a possible terminating newline) in a (1-based)
189 * numbered line.
190 */
191 public final int getLineLength(int lineNumber) {
192 return checkTextMap().lineLength(lineNumber);
193 }
194
195 private TextMap checkTextMap() {
196 if (textMap == null) {
197 final String code = getCode();
198 if (code == null) {
199 throw new RuntimeException("can't read file " + getName());
200 }
201 textMap = new TextMap(code);
202 }
203 return textMap;
204 }
205 }
206
207 public static class LiteralSourceImpl extends SourceImpl {
208
209 private final String name; // Name used originally to describe the source
210 private final String code;
211
212 public LiteralSourceImpl(String name, String code) {
213 this.name = name;
214 this.code = code;
215 }
216
217 @Override
218 public String getName() {
219 return name;
220 }
221
222 @Override
223 public String getCode() {
224 return code;
225 }
226
227 @Override
228 public String getPath() {
229 return name;
230 }
231
232 @Override
233 public Reader getReader() {
234 return new StringReader(code);
235 }
236
237 @Override
238 protected void reset() {
239 }
240
241 @Override
242 public int hashCode() {
243 final int prime = 31;
244 int result = 1;
245 result = prime * result + name.hashCode();
246 result = prime * result + (code == null ? 0 : code.hashCode());
247 return result;
248 }
249
250 @Override
251 public boolean equals(Object obj) {
252 if (this == obj) {
253 return true;
254 }
255 if (obj == null) {
256 return false;
257 }
258 if (!(obj instanceof LiteralSourceImpl)) {
259 return false;
260 }
261 LiteralSourceImpl other = (LiteralSourceImpl) obj;
262 return name.equals(other.name) && code.equals(other.code);
263 }
264
265 }
266
267 private static class FileSourceImpl extends SourceImpl {
268
269 private final String name; // Name used originally to describe the source
270 private String code = null;
271 private final String path; // Normalized path description of an actual file
272 private boolean readAttempted;
273
274 public FileSourceImpl(String name, String path) {
275 this.name = name;
276 this.path = path;
277 this.readAttempted = false;
278
279 }
280
281 @Override
282 public String getName() {
283 return name;
284 }
285
286 @Override
287 public String getCode() {
288 if (code == null && !readAttempted) {
289 readAttempted = true;
290 try {
291 code = readCode(getReader());
292 } catch (IOException e) {
293 }
294 }
295 return code;
296 }
297
298 @Override
299 public String getPath() {
300 return path;
301 }
302
303 @Override
304 public Reader getReader() {
305 if (code != null) {
306 return new StringReader(code);
307 }
308 try {
309 return new BufferedReader(new FileReader(path));
310 } catch (FileNotFoundException e) {
311 throw new RuntimeException("Can't find file " + path);
312 }
313 }
314
315 @Override
316 protected void reset() {
317 this.code = null;
318 this.readAttempted = false;
319 }
320
321 }
322
323 }