OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.code_output; | 5 library dart2js.code_output; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import '../source_file.dart'; | |
10 | |
11 import 'source_map_builder.dart'; | 9 import 'source_map_builder.dart'; |
12 | 10 |
13 class CodeOutputMarker { | 11 class CodeOutputMarker { |
14 final int offsetDelta; | 12 final int offsetDelta; |
15 final SourceFileLocation sourcePosition; | 13 final SourceFileLocation sourcePosition; |
16 | 14 |
17 CodeOutputMarker(this.offsetDelta, this.sourcePosition); | 15 CodeOutputMarker(this.offsetDelta, this.sourcePosition); |
18 } | 16 } |
19 | 17 |
18 abstract class CodeOutputListener { | |
19 void onText(String text); | |
20 void onDone(int length); | |
21 } | |
22 | |
20 abstract class CodeOutput { | 23 abstract class CodeOutput { |
24 /// Write [text] to this output. | |
25 /// | |
26 /// If the output is closed, a [StateError] is thrown. | |
27 void add(String text); | |
28 | |
29 /// Adds the content of [buffer] to the output and adds its markers to | |
30 /// [markers]. | |
31 /// | |
32 /// If the output is closed, a [StateError] is thrown. | |
33 void addBuffer(CodeBuffer buffer); | |
34 | |
35 /// Returns the number of characters currently write to this output. | |
36 int get length; | |
37 | |
38 /// Returns `true` if this output has been closed. | |
39 bool get isClosed; | |
40 | |
41 /// Closes the output. Further writes will cause a [StateError]. | |
42 void close(); | |
43 | |
44 /// Applies [f] to every marker in this output. | |
45 void forEachSourceLocation(void f(int targetOffset, | |
46 SourceFileLocation sourceLocation)); | |
47 } | |
48 | |
49 abstract class AbstractCodeOutput extends CodeOutput { | |
21 List<CodeOutputMarker> markers = new List<CodeOutputMarker>(); | 50 List<CodeOutputMarker> markers = new List<CodeOutputMarker>(); |
22 | |
23 int lastBufferOffset = 0; | 51 int lastBufferOffset = 0; |
24 int mappedRangeCounter = 0; | 52 int mappedRangeCounter = 0; |
53 bool isClosed = false; | |
25 | 54 |
26 int get length; | 55 void _addInternal(String text); |
27 | 56 |
28 void _writeInternal(String text); | 57 @override |
29 | 58 void add(String text) { |
30 /// Converts [object] to a string and adds it to the buffer. If [object] is a | 59 if (isClosed) { |
31 /// [CodeBuffer], adds its markers to [markers]. | 60 throw new StateError("Code output is closed. Trying to write '$text'."); |
32 void write(var object) { | |
33 if (object is CodeBuffer) { | |
34 addBuffer(object); | |
35 return; | |
36 } | 61 } |
37 if (mappedRangeCounter == 0) setSourceLocation(null); | 62 if (mappedRangeCounter == 0) setSourceLocation(null); |
38 _writeInternal(object); | 63 _addInternal(text); |
39 } | 64 } |
40 | 65 |
66 @override | |
41 void addBuffer(CodeBuffer other) { | 67 void addBuffer(CodeBuffer other) { |
42 if (other.markers.length > 0) { | 68 if (other.markers.length > 0) { |
43 CodeOutputMarker firstMarker = other.markers[0]; | 69 CodeOutputMarker firstMarker = other.markers[0]; |
44 int offsetDelta = | 70 int offsetDelta = |
45 length + firstMarker.offsetDelta - lastBufferOffset; | 71 length + firstMarker.offsetDelta - lastBufferOffset; |
46 markers.add(new CodeOutputMarker(offsetDelta, | 72 markers.add(new CodeOutputMarker(offsetDelta, |
47 firstMarker.sourcePosition)); | 73 firstMarker.sourcePosition)); |
48 for (int i = 1; i < other.markers.length; ++i) { | 74 for (int i = 1; i < other.markers.length; ++i) { |
49 markers.add(other.markers[i]); | 75 markers.add(other.markers[i]); |
50 } | 76 } |
51 lastBufferOffset = length + other.lastBufferOffset; | 77 lastBufferOffset = length + other.lastBufferOffset; |
52 } | 78 } |
53 _writeInternal(other.getText()); | 79 if (!other.isClosed) { |
80 other.close(); | |
81 } | |
82 _addInternal(other.getText()); | |
54 } | 83 } |
55 | 84 |
56 void beginMappedRange() { | 85 void beginMappedRange() { |
57 ++mappedRangeCounter; | 86 ++mappedRangeCounter; |
58 } | 87 } |
59 | 88 |
60 void endMappedRange() { | 89 void endMappedRange() { |
61 assert(mappedRangeCounter > 0); | 90 assert(mappedRangeCounter > 0); |
62 --mappedRangeCounter; | 91 --mappedRangeCounter; |
63 } | 92 } |
64 | 93 |
65 void setSourceLocation(SourceFileLocation sourcePosition) { | 94 void setSourceLocation(SourceFileLocation sourcePosition) { |
66 if (sourcePosition == null) { | 95 if (sourcePosition == null) { |
67 if (markers.length > 0 && markers.last.sourcePosition == null) return; | 96 if (markers.length > 0 && markers.last.sourcePosition == null) return; |
68 } | 97 } |
69 int offsetDelta = length - lastBufferOffset; | 98 int offsetDelta = length - lastBufferOffset; |
70 markers.add(new CodeOutputMarker(offsetDelta, sourcePosition)); | 99 markers.add(new CodeOutputMarker(offsetDelta, sourcePosition)); |
71 lastBufferOffset = length; | 100 lastBufferOffset = length; |
72 } | 101 } |
73 | 102 |
74 void forEachSourceLocation(void f(int targetOffset, var sourcePosition)) { | 103 void forEachSourceLocation(void f(int targetOffset, var sourcePosition)) { |
75 int targetOffset = 0; | 104 int targetOffset = 0; |
76 markers.forEach((marker) { | 105 markers.forEach((marker) { |
77 targetOffset += marker.offsetDelta; | 106 targetOffset += marker.offsetDelta; |
78 f(targetOffset, marker.sourcePosition); | 107 f(targetOffset, marker.sourcePosition); |
79 }); | 108 }); |
80 } | 109 } |
110 | |
111 void close() { | |
112 if (isClosed) { | |
113 throw new StateError("Code output is already closed."); | |
114 } | |
115 isClosed = true; | |
116 } | |
81 } | 117 } |
82 | 118 |
83 /// [CodeOutput] using a [StringBuffer] as backend. | 119 /// [CodeOutput] using a [StringBuffer] as backend. |
84 class CodeBuffer extends CodeOutput implements StringBuffer { | 120 class CodeBuffer extends AbstractCodeOutput { |
85 StringBuffer buffer = new StringBuffer(); | 121 StringBuffer buffer = new StringBuffer(); |
86 | 122 |
87 @override | 123 @override |
88 void _writeInternal(String text) { | 124 void _addInternal(String text) { |
89 buffer.write(text); | 125 buffer.write(text); |
90 } | 126 } |
91 | 127 |
92 @override | 128 @override |
93 int get length => buffer.length; | 129 int get length => buffer.length; |
94 | 130 |
95 @override | |
96 bool get isEmpty => buffer.isEmpty; | |
97 | |
98 @override | |
99 bool get isNotEmpty => buffer.isNotEmpty; | |
100 | |
101 @override | |
102 void writeAll(Iterable<Object> objects, [String separator = ""]) { | |
103 Iterator iterator = objects.iterator; | |
104 if (!iterator.moveNext()) return; | |
105 if (separator.isEmpty) { | |
106 do { | |
107 write(iterator.current); | |
108 } while (iterator.moveNext()); | |
109 } else { | |
110 write(iterator.current); | |
111 while (iterator.moveNext()) { | |
112 write(separator); | |
113 write(iterator.current); | |
114 } | |
115 } | |
116 } | |
117 | |
118 @override | |
119 void writeln([var object = ""]) { | |
120 write(object); | |
121 write("\n"); | |
122 } | |
123 | |
124 @override | |
125 void writeCharCode(int charCode) { | |
126 buffer.writeCharCode(charCode); | |
127 } | |
128 | |
129 @override | |
130 void clear() { | |
131 buffer = new StringBuffer(); | |
132 markers.clear(); | |
133 lastBufferOffset = 0; | |
134 } | |
135 | |
136 String toString() { | |
137 throw "Don't use CodeBuffer.toString() since it drops sourcemap data."; | |
138 } | |
139 | |
140 String getText() { | 131 String getText() { |
141 return buffer.toString(); | 132 return buffer.toString(); |
142 } | 133 } |
134 | |
135 String toString() => getText(); | |
floitsch
2015/01/09 16:35:19
I thought we wanted to avoid this, so that one doe
Johnni Winther
2015/01/13 08:53:05
We do. Reverted.
| |
143 } | 136 } |
144 | 137 |
145 /// [CodeOutput] using a [CompilationOutput] as backend. | 138 /// [CodeOutput] using a [CompilationOutput] as backend. |
146 class StreamCodeOutput extends CodeOutput { | 139 class StreamCodeOutput extends AbstractCodeOutput { |
147 int length = 0; | 140 int length = 0; |
148 final EventSink<String> output; | 141 final EventSink<String> output; |
142 final List<CodeOutputListener> _listeners; | |
149 | 143 |
150 StreamCodeOutput(this.output); | 144 StreamCodeOutput(this.output, [this._listeners]); |
151 | 145 |
152 @override | 146 @override |
153 void _writeInternal(String text) { | 147 void _addInternal(String text) { |
154 output.add(text); | 148 output.add(text); |
155 length += text.length; | 149 length += text.length; |
150 if (_listeners != null) { | |
151 _listeners.forEach((listener) => listener.onText(text)); | |
152 } | |
156 } | 153 } |
157 | 154 |
158 void close() { | 155 void close() { |
159 output.close(); | 156 output.close(); |
157 super.close(); | |
158 if (_listeners != null) { | |
159 _listeners.forEach((listener) => listener.onDone(length)); | |
160 } | |
160 } | 161 } |
161 } | 162 } |
162 | |
163 /// [StreamCodeSink] that collects line information. | |
164 class LineColumnCodeOutput extends StreamCodeOutput | |
165 implements LineColumnProvider { | |
166 int lastLineStart = 0; | |
167 List<int> lineStarts = <int>[0]; | |
168 | |
169 LineColumnCodeOutput(EventSink<String> output) : super(output); | |
170 | |
171 @override | |
172 void _writeInternal(String text) { | |
173 int offset = lastLineStart; | |
174 int index = 0; | |
175 while (index < text.length) { | |
176 // Unix uses '\n' and Windows uses '\r\n', so this algorithm works for | |
177 // both platforms. | |
178 index = text.indexOf('\n', index) + 1; | |
179 if (index <= 0) break; | |
180 lastLineStart = offset + index; | |
181 lineStarts.add(lastLineStart); | |
182 } | |
183 super._writeInternal(text); | |
184 } | |
185 | |
186 @override | |
187 int getLine(int offset) { | |
188 List<int> starts = lineStarts; | |
189 if (offset < 0|| starts.last <= offset) { | |
190 throw 'bad position #$offset in buffer with length ${length}.'; | |
191 } | |
192 int first = 0; | |
193 int count = starts.length; | |
194 while (count > 1) { | |
195 int step = count ~/ 2; | |
196 int middle = first + step; | |
197 int lineStart = starts[middle]; | |
198 if (offset < lineStart) { | |
199 count = step; | |
200 } else { | |
201 first = middle; | |
202 count -= step; | |
203 } | |
204 } | |
205 return first; | |
206 } | |
207 | |
208 @override | |
209 int getColumn(int line, int offset) { | |
210 return offset - lineStarts[line]; | |
211 } | |
212 | |
213 @override | |
214 void close() { | |
215 lineStarts.add(length); | |
216 super.close(); | |
217 } | |
218 } | |
OLD | NEW |