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); |
+ } |
+} |
+ |
+ |
+ |