| Index: pkg/compiler/lib/src/io/start_end_information.dart
 | 
| diff --git a/pkg/compiler/lib/src/io/start_end_information.dart b/pkg/compiler/lib/src/io/start_end_information.dart
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..b76a2176571ae840528ef4ac5dfced0f776c209d
 | 
| --- /dev/null
 | 
| +++ b/pkg/compiler/lib/src/io/start_end_information.dart
 | 
| @@ -0,0 +1,232 @@
 | 
| +// Copyright (c) 2015, 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.
 | 
| +
 | 
| +/// Source information system that maps spans of Dart AST nodes to spans of
 | 
| +/// JavaScript nodes.
 | 
| +
 | 
| +library dart2js.source_information.start_end;
 | 
| +
 | 
| +import '../dart2jslib.dart' show
 | 
| +    MessageKind,
 | 
| +    SourceSpan;
 | 
| +import '../elements/elements.dart' show
 | 
| +    AstElement,
 | 
| +    LocalElement;
 | 
| +import '../js/js.dart' as js;
 | 
| +import '../js/js_source_mapping.dart';
 | 
| +import '../scanner/scannerlib.dart' show Token;
 | 
| +import '../tree/tree.dart' show Node, Send;
 | 
| +
 | 
| +import 'source_file.dart';
 | 
| +import 'source_information.dart';
 | 
| +
 | 
| +/// Source information that contains start source position and optionally an
 | 
| +/// end source position.
 | 
| +class StartEndSourceInformation extends SourceInformation {
 | 
| +  @override
 | 
| +  final SourceLocation startPosition;
 | 
| +
 | 
| +  @override
 | 
| +  final SourceLocation endPosition;
 | 
| +
 | 
| +  StartEndSourceInformation(this.startPosition, [this.endPosition]);
 | 
| +
 | 
| +  @override
 | 
| +  List<SourceLocation> get sourceLocations {
 | 
| +    if (endPosition == null) {
 | 
| +      return <SourceLocation>[startPosition];
 | 
| +    } else {
 | 
| +      return <SourceLocation>[startPosition, endPosition];
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceSpan get sourceSpan {
 | 
| +    Uri uri = startPosition.sourceUri;
 | 
| +    int begin = startPosition.offset;
 | 
| +    int end = endPosition == null ? begin : endPosition.offset;
 | 
| +    return new SourceSpan(uri, begin, end);
 | 
| +  }
 | 
| +
 | 
| +  int get hashCode {
 | 
| +    return 0x7FFFFFFF &
 | 
| +           (startPosition.hashCode * 17 + endPosition.hashCode * 19);
 | 
| +  }
 | 
| +
 | 
| +  bool operator ==(other) {
 | 
| +    if (identical(this, other)) return true;
 | 
| +    if (other is! StartEndSourceInformation) return false;
 | 
| +    return startPosition == other.startPosition &&
 | 
| +           endPosition == other.endPosition;
 | 
| +  }
 | 
| +
 | 
| +  // TODO(johnniwinther): Inline this in
 | 
| +  // [StartEndSourceInformationBuilder.buildDeclaration].
 | 
| +  static StartEndSourceInformation _computeSourceInformation(
 | 
| +      AstElement element) {
 | 
| +
 | 
| +    AstElement implementation = element.implementation;
 | 
| +    SourceFile sourceFile = implementation.compilationUnit.script.file;
 | 
| +    String name = computeElementNameForSourceMaps(element);
 | 
| +    Node node = implementation.node;
 | 
| +    Token beginToken;
 | 
| +    Token endToken;
 | 
| +    if (node == null) {
 | 
| +      // Synthesized node. Use the enclosing element for the location.
 | 
| +      beginToken = endToken = element.position;
 | 
| +    } else {
 | 
| +      beginToken = node.getBeginToken();
 | 
| +      endToken = node.getEndToken();
 | 
| +    }
 | 
| +    // TODO(johnniwinther): find the right sourceFile here and remove offset
 | 
| +    // checks below.
 | 
| +    SourceLocation sourcePosition, endSourcePosition;
 | 
| +    if (beginToken.charOffset < sourceFile.length) {
 | 
| +      sourcePosition =
 | 
| +          new OffsetSourceLocation(sourceFile, beginToken.charOffset, name);
 | 
| +    }
 | 
| +    if (endToken.charOffset < sourceFile.length) {
 | 
| +      endSourcePosition =
 | 
| +          new OffsetSourceLocation(sourceFile, endToken.charOffset, name);
 | 
| +    }
 | 
| +    return new StartEndSourceInformation(sourcePosition, endSourcePosition);
 | 
| +  }
 | 
| +
 | 
| +  /// Create a textual representation of the source information using [uriText]
 | 
| +  /// as the Uri representation.
 | 
| +  String _computeText(String uriText) {
 | 
| +    StringBuffer sb = new StringBuffer();
 | 
| +    sb.write('$uriText:');
 | 
| +    // Use 1-based line/startPosition info to match usual dart tool output.
 | 
| +    sb.write('[${startPosition.line + 1},${startPosition.column + 1}]');
 | 
| +    if (endPosition != null) {
 | 
| +      sb.write('-[${endPosition.line + 1},${endPosition.column + 1}]');
 | 
| +    }
 | 
| +    return sb.toString();
 | 
| +  }
 | 
| +
 | 
| +  String get shortText {
 | 
| +    return _computeText(startPosition.sourceUri.pathSegments.last);
 | 
| +  }
 | 
| +
 | 
| +  String toString() {
 | 
| +    return _computeText('${startPosition.sourceUri}');
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +class StartEndSourceInformationStrategy
 | 
| +    implements JavaScriptSourceInformationStrategy {
 | 
| +  const StartEndSourceInformationStrategy();
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformationBuilder createBuilderForContext(AstElement element) {
 | 
| +    return new StartEndSourceInformationBuilder(element);
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformationProcessor createProcessor(SourceMapper sourceMapper) {
 | 
| +    return new StartEndSourceInformationProcessor(sourceMapper);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +class StartEndSourceInformationProcessor extends SourceInformationProcessor {
 | 
| +  final SourceMapper sourceMapper;
 | 
| +
 | 
| +  /// Used to track whether a terminating source location marker has been
 | 
| +  /// registered for the top-most node with source information.
 | 
| +  bool hasRegisteredRoot = false;
 | 
| +
 | 
| +  StartEndSourceInformationProcessor(this.sourceMapper);
 | 
| +
 | 
| +  @override
 | 
| +  void onPositions(js.Node node,
 | 
| +                   int startPosition,
 | 
| +                   int endPosition,
 | 
| +                   int closingPosition) {
 | 
| +    if (node.sourceInformation != null) {
 | 
| +      StartEndSourceInformation sourceInformation = node.sourceInformation;
 | 
| +      sourceMapper.register(
 | 
| +          node, startPosition, sourceInformation.startPosition);
 | 
| +      if (sourceInformation.endPosition != null) {
 | 
| +        sourceMapper.register(node, endPosition, sourceInformation.endPosition);
 | 
| +      }
 | 
| +      if (!hasRegisteredRoot) {
 | 
| +        sourceMapper.register(node, endPosition, null);
 | 
| +        hasRegisteredRoot = true;
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +/// [SourceInformationBuilder] that generates [PositionSourceInformation].
 | 
| +class StartEndSourceInformationBuilder extends SourceInformationBuilder {
 | 
| +  final SourceFile sourceFile;
 | 
| +  final String name;
 | 
| +
 | 
| +  StartEndSourceInformationBuilder(AstElement element)
 | 
| +      : sourceFile = element.compilationUnit.script.file,
 | 
| +        name = computeElementNameForSourceMaps(element);
 | 
| +
 | 
| +  SourceInformation buildDeclaration(AstElement element) {
 | 
| +    return StartEndSourceInformation._computeSourceInformation(element);
 | 
| +  }
 | 
| +
 | 
| +  SourceLocation sourceFileLocationForToken(Token token) {
 | 
| +    SourceLocation location =
 | 
| +        new OffsetSourceLocation(sourceFile, token.charOffset, name);
 | 
| +    checkValidSourceFileLocation(location, sourceFile, token.charOffset);
 | 
| +    return location;
 | 
| +  }
 | 
| +
 | 
| +  void checkValidSourceFileLocation(
 | 
| +      SourceLocation location, SourceFile sourceFile, int offset) {
 | 
| +    if (!location.isValid) {
 | 
| +      throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message(
 | 
| +          {'offset': offset,
 | 
| +           'fileName': sourceFile.filename,
 | 
| +           'length': sourceFile.length});
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildLoop(Node node) {
 | 
| +    return new StartEndSourceInformation(
 | 
| +        sourceFileLocationForToken(node.getBeginToken()),
 | 
| +        sourceFileLocationForToken(node.getEndToken()));
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildGeneric(Node node) {
 | 
| +    return new StartEndSourceInformation(
 | 
| +        sourceFileLocationForToken(node.getBeginToken()));
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildReturn(Node node) {
 | 
| +    return buildGeneric(node);
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildGet(Node node) => buildGeneric(node);
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildAssignment(Node node) => buildGeneric(node);
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildCall(Node receiver, Node call) {
 | 
| +    return buildGeneric(receiver);
 | 
| +  }
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformation buildIf(Node node) => buildGeneric(node);
 | 
| +
 | 
| +  @override
 | 
| +  SourceInformationBuilder forContext(
 | 
| +      AstElement element, {SourceInformation sourceInformation}) {
 | 
| +    return new StartEndSourceInformationBuilder(element);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +
 | 
| 
 |