OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 /// Source information system that maps spans of Dart AST nodes to spans of |
| 6 /// JavaScript nodes. |
| 7 |
| 8 library dart2js.source_information.start_end; |
| 9 |
| 10 import '../dart2jslib.dart' show |
| 11 MessageKind, |
| 12 SourceSpan; |
| 13 import '../elements/elements.dart' show |
| 14 AstElement, |
| 15 LocalElement; |
| 16 import '../js/js.dart' as js; |
| 17 import '../js/js_source_mapping.dart'; |
| 18 import '../scanner/scannerlib.dart' show Token; |
| 19 import '../tree/tree.dart' show Node, Send; |
| 20 |
| 21 import 'source_file.dart'; |
| 22 import 'source_information.dart'; |
| 23 |
| 24 /// Source information that contains start source position and optionally an |
| 25 /// end source position. |
| 26 class StartEndSourceInformation extends SourceInformation { |
| 27 @override |
| 28 final SourceLocation startPosition; |
| 29 |
| 30 @override |
| 31 final SourceLocation endPosition; |
| 32 |
| 33 StartEndSourceInformation(this.startPosition, [this.endPosition]); |
| 34 |
| 35 @override |
| 36 List<SourceLocation> get sourceLocations { |
| 37 if (endPosition == null) { |
| 38 return <SourceLocation>[startPosition]; |
| 39 } else { |
| 40 return <SourceLocation>[startPosition, endPosition]; |
| 41 } |
| 42 } |
| 43 |
| 44 @override |
| 45 SourceSpan get sourceSpan { |
| 46 Uri uri = startPosition.sourceUri; |
| 47 int begin = startPosition.offset; |
| 48 int end = endPosition == null ? begin : endPosition.offset; |
| 49 return new SourceSpan(uri, begin, end); |
| 50 } |
| 51 |
| 52 int get hashCode { |
| 53 return 0x7FFFFFFF & |
| 54 (startPosition.hashCode * 17 + endPosition.hashCode * 19); |
| 55 } |
| 56 |
| 57 bool operator ==(other) { |
| 58 if (identical(this, other)) return true; |
| 59 if (other is! StartEndSourceInformation) return false; |
| 60 return startPosition == other.startPosition && |
| 61 endPosition == other.endPosition; |
| 62 } |
| 63 |
| 64 // TODO(johnniwinther): Inline this in |
| 65 // [StartEndSourceInformationBuilder.buildDeclaration]. |
| 66 static StartEndSourceInformation _computeSourceInformation( |
| 67 AstElement element) { |
| 68 |
| 69 AstElement implementation = element.implementation; |
| 70 SourceFile sourceFile = implementation.compilationUnit.script.file; |
| 71 String name = computeElementNameForSourceMaps(element); |
| 72 Node node = implementation.node; |
| 73 Token beginToken; |
| 74 Token endToken; |
| 75 if (node == null) { |
| 76 // Synthesized node. Use the enclosing element for the location. |
| 77 beginToken = endToken = element.position; |
| 78 } else { |
| 79 beginToken = node.getBeginToken(); |
| 80 endToken = node.getEndToken(); |
| 81 } |
| 82 // TODO(johnniwinther): find the right sourceFile here and remove offset |
| 83 // checks below. |
| 84 SourceLocation sourcePosition, endSourcePosition; |
| 85 if (beginToken.charOffset < sourceFile.length) { |
| 86 sourcePosition = |
| 87 new OffsetSourceLocation(sourceFile, beginToken.charOffset, name); |
| 88 } |
| 89 if (endToken.charOffset < sourceFile.length) { |
| 90 endSourcePosition = |
| 91 new OffsetSourceLocation(sourceFile, endToken.charOffset, name); |
| 92 } |
| 93 return new StartEndSourceInformation(sourcePosition, endSourcePosition); |
| 94 } |
| 95 |
| 96 /// Create a textual representation of the source information using [uriText] |
| 97 /// as the Uri representation. |
| 98 String _computeText(String uriText) { |
| 99 StringBuffer sb = new StringBuffer(); |
| 100 sb.write('$uriText:'); |
| 101 // Use 1-based line/startPosition info to match usual dart tool output. |
| 102 sb.write('[${startPosition.line + 1},${startPosition.column + 1}]'); |
| 103 if (endPosition != null) { |
| 104 sb.write('-[${endPosition.line + 1},${endPosition.column + 1}]'); |
| 105 } |
| 106 return sb.toString(); |
| 107 } |
| 108 |
| 109 String get shortText { |
| 110 return _computeText(startPosition.sourceUri.pathSegments.last); |
| 111 } |
| 112 |
| 113 String toString() { |
| 114 return _computeText('${startPosition.sourceUri}'); |
| 115 } |
| 116 } |
| 117 |
| 118 class StartEndSourceInformationStrategy |
| 119 implements JavaScriptSourceInformationStrategy { |
| 120 const StartEndSourceInformationStrategy(); |
| 121 |
| 122 @override |
| 123 SourceInformationBuilder createBuilderForContext(AstElement element) { |
| 124 return new StartEndSourceInformationBuilder(element); |
| 125 } |
| 126 |
| 127 @override |
| 128 SourceInformationProcessor createProcessor(SourceMapper sourceMapper) { |
| 129 return new StartEndSourceInformationProcessor(sourceMapper); |
| 130 } |
| 131 } |
| 132 |
| 133 class StartEndSourceInformationProcessor extends SourceInformationProcessor { |
| 134 final SourceMapper sourceMapper; |
| 135 |
| 136 /// Used to track whether a terminating source location marker has been |
| 137 /// registered for the top-most node with source information. |
| 138 bool hasRegisteredRoot = false; |
| 139 |
| 140 StartEndSourceInformationProcessor(this.sourceMapper); |
| 141 |
| 142 @override |
| 143 void onPositions(js.Node node, |
| 144 int startPosition, |
| 145 int endPosition, |
| 146 int closingPosition) { |
| 147 if (node.sourceInformation != null) { |
| 148 StartEndSourceInformation sourceInformation = node.sourceInformation; |
| 149 sourceMapper.register( |
| 150 node, startPosition, sourceInformation.startPosition); |
| 151 if (sourceInformation.endPosition != null) { |
| 152 sourceMapper.register(node, endPosition, sourceInformation.endPosition); |
| 153 } |
| 154 if (!hasRegisteredRoot) { |
| 155 sourceMapper.register(node, endPosition, null); |
| 156 hasRegisteredRoot = true; |
| 157 } |
| 158 } |
| 159 } |
| 160 } |
| 161 |
| 162 /// [SourceInformationBuilder] that generates [PositionSourceInformation]. |
| 163 class StartEndSourceInformationBuilder extends SourceInformationBuilder { |
| 164 final SourceFile sourceFile; |
| 165 final String name; |
| 166 |
| 167 StartEndSourceInformationBuilder(AstElement element) |
| 168 : sourceFile = element.compilationUnit.script.file, |
| 169 name = computeElementNameForSourceMaps(element); |
| 170 |
| 171 SourceInformation buildDeclaration(AstElement element) { |
| 172 return StartEndSourceInformation._computeSourceInformation(element); |
| 173 } |
| 174 |
| 175 SourceLocation sourceFileLocationForToken(Token token) { |
| 176 SourceLocation location = |
| 177 new OffsetSourceLocation(sourceFile, token.charOffset, name); |
| 178 checkValidSourceFileLocation(location, sourceFile, token.charOffset); |
| 179 return location; |
| 180 } |
| 181 |
| 182 void checkValidSourceFileLocation( |
| 183 SourceLocation location, SourceFile sourceFile, int offset) { |
| 184 if (!location.isValid) { |
| 185 throw MessageKind.INVALID_SOURCE_FILE_LOCATION.message( |
| 186 {'offset': offset, |
| 187 'fileName': sourceFile.filename, |
| 188 'length': sourceFile.length}); |
| 189 } |
| 190 } |
| 191 |
| 192 @override |
| 193 SourceInformation buildLoop(Node node) { |
| 194 return new StartEndSourceInformation( |
| 195 sourceFileLocationForToken(node.getBeginToken()), |
| 196 sourceFileLocationForToken(node.getEndToken())); |
| 197 } |
| 198 |
| 199 @override |
| 200 SourceInformation buildGeneric(Node node) { |
| 201 return new StartEndSourceInformation( |
| 202 sourceFileLocationForToken(node.getBeginToken())); |
| 203 } |
| 204 |
| 205 @override |
| 206 SourceInformation buildReturn(Node node) { |
| 207 return buildGeneric(node); |
| 208 } |
| 209 |
| 210 @override |
| 211 SourceInformation buildGet(Node node) => buildGeneric(node); |
| 212 |
| 213 @override |
| 214 SourceInformation buildAssignment(Node node) => buildGeneric(node); |
| 215 |
| 216 @override |
| 217 SourceInformation buildCall(Node receiver, Node call) { |
| 218 return buildGeneric(receiver); |
| 219 } |
| 220 |
| 221 @override |
| 222 SourceInformation buildIf(Node node) => buildGeneric(node); |
| 223 |
| 224 @override |
| 225 SourceInformationBuilder forContext( |
| 226 AstElement element, {SourceInformation sourceInformation}) { |
| 227 return new StartEndSourceInformationBuilder(element); |
| 228 } |
| 229 } |
| 230 |
| 231 |
| 232 |
OLD | NEW |