blob: 5f131987eb33309218426bc141f985c77694f02f [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.code_output;
import 'dart:async';
import 'source_information.dart';
class CodeOutputMarker {
final int offsetDelta;
final SourceFileLocation sourcePosition;
CodeOutputMarker(this.offsetDelta, this.sourcePosition);
}
abstract class CodeOutputListener {
void onText(String text);
void onDone(int length);
}
abstract class CodeOutput {
/// Write [text] to this output.
///
/// If the output is closed, a [StateError] is thrown.
void add(String text);
/// Adds the content of [buffer] to the output and adds its markers to
/// [markers].
///
/// If the output is closed, a [StateError] is thrown.
void addBuffer(CodeBuffer buffer);
/// Returns the number of characters currently write to this output.
int get length;
/// Returns `true` if this output has been closed.
bool get isClosed;
/// Closes the output. Further writes will cause a [StateError].
void close();
/// Applies [f] to every marker in this output.
void forEachSourceLocation(void f(int targetOffset,
SourceFileLocation sourceLocation));
}
abstract class AbstractCodeOutput extends CodeOutput {
List<CodeOutputMarker> markers = new List<CodeOutputMarker>();
int lastBufferOffset = 0;
int mappedRangeCounter = 0;
bool isClosed = false;
void _addInternal(String text);
@override
void add(String text) {
if (isClosed) {
throw new StateError("Code output is closed. Trying to write '$text'.");
}
if (mappedRangeCounter == 0) setSourceLocation(null);
_addInternal(text);
}
@override
void addBuffer(CodeBuffer other) {
if (other.markers.length > 0) {
CodeOutputMarker firstMarker = other.markers[0];
int offsetDelta =
length + firstMarker.offsetDelta - lastBufferOffset;
markers.add(new CodeOutputMarker(offsetDelta,
firstMarker.sourcePosition));
for (int i = 1; i < other.markers.length; ++i) {
markers.add(other.markers[i]);
}
lastBufferOffset = length + other.lastBufferOffset;
}
if (!other.isClosed) {
other.close();
}
_addInternal(other.getText());
}
void beginMappedRange() {
++mappedRangeCounter;
}
void endMappedRange() {
assert(mappedRangeCounter > 0);
--mappedRangeCounter;
}
void setSourceLocation(SourceFileLocation sourcePosition) {
if (sourcePosition == null) {
if (markers.length > 0 && markers.last.sourcePosition == null) return;
}
int offsetDelta = length - lastBufferOffset;
markers.add(new CodeOutputMarker(offsetDelta, sourcePosition));
lastBufferOffset = length;
}
void forEachSourceLocation(void f(int targetOffset, var sourcePosition)) {
int targetOffset = 0;
markers.forEach((marker) {
targetOffset += marker.offsetDelta;
f(targetOffset, marker.sourcePosition);
});
}
void close() {
if (isClosed) {
throw new StateError("Code output is already closed.");
}
isClosed = true;
}
}
/// [CodeOutput] using a [StringBuffer] as backend.
class CodeBuffer extends AbstractCodeOutput {
StringBuffer buffer = new StringBuffer();
@override
void _addInternal(String text) {
buffer.write(text);
}
@override
int get length => buffer.length;
String getText() {
return buffer.toString();
}
String toString() {
throw "Don't use CodeBuffer.toString() since it drops sourcemap data.";
}
}
/// [CodeOutput] using a [CompilationOutput] as backend.
class StreamCodeOutput extends AbstractCodeOutput {
int length = 0;
final EventSink<String> output;
final List<CodeOutputListener> _listeners;
StreamCodeOutput(this.output, [this._listeners]);
@override
void _addInternal(String text) {
output.add(text);
length += text.length;
if (_listeners != null) {
_listeners.forEach((listener) => listener.onText(text));
}
}
void close() {
output.close();
super.close();
if (_listeners != null) {
_listeners.forEach((listener) => listener.onDone(length));
}
}
}