| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 protocol; | 5 library protocol; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 | 9 |
| 10 import 'package:analysis_server/src/computer/element.dart' show | |
| 11 elementFromEngine; | |
| 12 import 'package:analysis_server/src/search/search_result.dart' show | |
| 13 searchResultFromMatch; | |
| 14 import 'package:analysis_server/src/services/correction/fix.dart' show Fix; | |
| 15 import 'package:analysis_server/src/services/json.dart'; | 10 import 'package:analysis_server/src/services/json.dart'; |
| 16 import 'package:analysis_server/src/services/search/search_engine.dart' as | |
| 17 engine; | |
| 18 import 'package:analyzer/src/generated/ast.dart' as engine; | |
| 19 import 'package:analyzer/src/generated/element.dart' as engine; | |
| 20 import 'package:analyzer/src/generated/engine.dart' as engine; | |
| 21 import 'package:analyzer/src/generated/error.dart' as engine; | |
| 22 import 'package:analyzer/src/generated/source.dart' as engine; | |
| 23 | 11 |
| 24 part 'generated_protocol.dart'; | 12 part 'generated_protocol.dart'; |
| 25 | 13 |
| 26 | 14 |
| 27 final Map<String, RefactoringKind> REQUEST_ID_REFACTORING_KINDS = | 15 final Map<String, RefactoringKind> REQUEST_ID_REFACTORING_KINDS = |
| 28 new HashMap<String, RefactoringKind>(); | 16 new HashMap<String, RefactoringKind>(); |
| 29 | 17 |
| 30 /** | 18 /** |
| 31 * Translate the input [map], applying [keyCallback] to all its keys, and | 19 * Translate the input [map], applying [keyCallback] to all its keys, and |
| 32 * [valueCallback] to all its values. | 20 * [valueCallback] to all its values. |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 72 SourceEdit edit) { | 60 SourceEdit edit) { |
| 73 SourceFileEdit fileEdit = change.getFileEdit(file); | 61 SourceFileEdit fileEdit = change.getFileEdit(file); |
| 74 if (fileEdit == null) { | 62 if (fileEdit == null) { |
| 75 fileEdit = new SourceFileEdit(file, fileStamp); | 63 fileEdit = new SourceFileEdit(file, fileStamp); |
| 76 change.addFileEdit(fileEdit); | 64 change.addFileEdit(fileEdit); |
| 77 } | 65 } |
| 78 fileEdit.add(edit); | 66 fileEdit.add(edit); |
| 79 } | 67 } |
| 80 | 68 |
| 81 | 69 |
| 82 void _addElementEditToSourceChange(SourceChange change, engine.Element element, | |
| 83 SourceEdit edit) { | |
| 84 engine.AnalysisContext context = element.context; | |
| 85 engine.Source source = element.source; | |
| 86 _addSourceEditToSourceChange(change, context, source, edit); | |
| 87 } | |
| 88 | |
| 89 | |
| 90 void _addSourceEditToSourceChange(SourceChange change, | |
| 91 engine.AnalysisContext context, engine.Source source, SourceEdit edit) { | |
| 92 String file = source.fullName; | |
| 93 int fileStamp = context.getModificationStamp(source); | |
| 94 change.addEdit(file, fileStamp, edit); | |
| 95 } | |
| 96 | |
| 97 /** | |
| 98 * Create an AnalysisError based on error information from the analyzer | |
| 99 * engine. Access via AnalysisError.fromEngine(). | |
| 100 */ | |
| 101 AnalysisError _analysisErrorFromEngine(engine.LineInfo lineInfo, | |
| 102 engine.AnalysisError error) { | |
| 103 engine.ErrorCode errorCode = error.errorCode; | |
| 104 // prepare location | |
| 105 Location location; | |
| 106 { | |
| 107 String file = error.source.fullName; | |
| 108 int offset = error.offset; | |
| 109 int length = error.length; | |
| 110 int startLine = -1; | |
| 111 int startColumn = -1; | |
| 112 if (lineInfo != null) { | |
| 113 engine.LineInfo_Location lineLocation = lineInfo.getLocation(offset); | |
| 114 if (lineLocation != null) { | |
| 115 startLine = lineLocation.lineNumber; | |
| 116 startColumn = lineLocation.columnNumber; | |
| 117 } | |
| 118 } | |
| 119 location = new Location(file, offset, length, startLine, startColumn); | |
| 120 } | |
| 121 // done | |
| 122 var severity = new AnalysisErrorSeverity(errorCode.errorSeverity.name); | |
| 123 var type = new AnalysisErrorType(errorCode.type.name); | |
| 124 String message = error.message; | |
| 125 String correction = error.correction; | |
| 126 return new AnalysisError( | |
| 127 severity, | |
| 128 type, | |
| 129 location, | |
| 130 message, | |
| 131 correction: correction); | |
| 132 } | |
| 133 | |
| 134 /** | |
| 135 * Returns a list of AnalysisErrors correponding to the given list of Engine | |
| 136 * errors. Access via AnalysisError.listFromEngine(). | |
| 137 */ | |
| 138 List<AnalysisError> _analysisErrorListFromEngine(engine.LineInfo lineInfo, | |
| 139 List<engine.AnalysisError> errors) { | |
| 140 return errors.map((engine.AnalysisError error) { | |
| 141 return new AnalysisError.fromEngine(lineInfo, error); | |
| 142 }).toList(); | |
| 143 } | |
| 144 | |
| 145 /** | 70 /** |
| 146 * Get the result of applying the edit to the given [code]. Access via | 71 * Get the result of applying the edit to the given [code]. Access via |
| 147 * SourceEdit.apply(). | 72 * SourceEdit.apply(). |
| 148 */ | 73 */ |
| 149 String _applyEdit(String code, SourceEdit edit) { | 74 String _applyEdit(String code, SourceEdit edit) { |
| 150 if (edit.length < 0) { | 75 if (edit.length < 0) { |
| 151 throw new RangeError('length is negative'); | 76 throw new RangeError('length is negative'); |
| 152 } | 77 } |
| 153 return code.substring(0, edit.offset) + | 78 return code.substring(0, edit.offset) + |
| 154 edit.replacement + | 79 edit.replacement + |
| 155 code.substring(edit.end); | 80 code.substring(edit.end); |
| 156 } | 81 } |
| 157 | 82 |
| 158 /** | 83 /** |
| 159 * Get the result of applying a set of [edits] to the given [code]. Edits | 84 * Get the result of applying a set of [edits] to the given [code]. Edits |
| 160 * are applied in the order they appear in [edits]. Access via | 85 * are applied in the order they appear in [edits]. Access via |
| 161 * SourceEdit.applySequence(). | 86 * SourceEdit.applySequence(). |
| 162 */ | 87 */ |
| 163 String _applySequence(String code, Iterable<SourceEdit> edits) { | 88 String _applySequence(String code, Iterable<SourceEdit> edits) { |
| 164 edits.forEach((SourceEdit edit) { | 89 edits.forEach((SourceEdit edit) { |
| 165 code = edit.apply(code); | 90 code = edit.apply(code); |
| 166 }); | 91 }); |
| 167 return code; | 92 return code; |
| 168 } | 93 } |
| 169 | 94 |
| 170 /** | 95 /** |
| 171 * Map an element kind from the analyzer engine to a [CompletionSuggestionKind]. | |
| 172 */ | |
| 173 CompletionSuggestionKind | |
| 174 _completionSuggestionKindFromElementKind(engine.ElementKind kind) { | |
| 175 // ElementKind.ANGULAR_FORMATTER, | |
| 176 // ElementKind.ANGULAR_COMPONENT, | |
| 177 // ElementKind.ANGULAR_CONTROLLER, | |
| 178 // ElementKind.ANGULAR_DIRECTIVE, | |
| 179 // ElementKind.ANGULAR_PROPERTY, | |
| 180 // ElementKind.ANGULAR_SCOPE_PROPERTY, | |
| 181 // ElementKind.ANGULAR_SELECTOR, | |
| 182 // ElementKind.ANGULAR_VIEW, | |
| 183 if (kind == engine.ElementKind.CLASS) return CompletionSuggestionKind.CLASS; | |
| 184 // ElementKind.COMPILATION_UNIT, | |
| 185 if (kind == | |
| 186 engine.ElementKind.CONSTRUCTOR) return CompletionSuggestionKind.CONSTRUCTO
R; | |
| 187 // ElementKind.DYNAMIC, | |
| 188 // ElementKind.EMBEDDED_HTML_SCRIPT, | |
| 189 // ElementKind.ERROR, | |
| 190 // ElementKind.EXPORT, | |
| 191 // ElementKind.EXTERNAL_HTML_SCRIPT, | |
| 192 if (kind == engine.ElementKind.FIELD) return CompletionSuggestionKind.FIELD; | |
| 193 if (kind == | |
| 194 engine.ElementKind.FUNCTION) return CompletionSuggestionKind.FUNCTION; | |
| 195 if (kind == | |
| 196 engine.ElementKind.FUNCTION_TYPE_ALIAS) return | |
| 197 CompletionSuggestionKind.FUNCTION_TYPE_ALIAS; | |
| 198 if (kind == engine.ElementKind.GETTER) return CompletionSuggestionKind.GETTER; | |
| 199 // ElementKind.HTML, | |
| 200 if (kind == engine.ElementKind.IMPORT) return CompletionSuggestionKind.IMPORT; | |
| 201 // ElementKind.LABEL, | |
| 202 // ElementKind.LIBRARY, | |
| 203 if (kind == | |
| 204 engine.ElementKind.LOCAL_VARIABLE) return | |
| 205 CompletionSuggestionKind.LOCAL_VARIABLE; | |
| 206 if (kind == engine.ElementKind.METHOD) return CompletionSuggestionKind.METHOD; | |
| 207 // ElementKind.NAME, | |
| 208 if (kind == | |
| 209 engine.ElementKind.PARAMETER) return CompletionSuggestionKind.PARAMETER; | |
| 210 // ElementKind.POLYMER_ATTRIBUTE, | |
| 211 // ElementKind.POLYMER_TAG_DART, | |
| 212 // ElementKind.POLYMER_TAG_HTML, | |
| 213 // ElementKind.PREFIX, | |
| 214 if (kind == engine.ElementKind.SETTER) return CompletionSuggestionKind.SETTER; | |
| 215 if (kind == | |
| 216 engine.ElementKind.TOP_LEVEL_VARIABLE) return | |
| 217 CompletionSuggestionKind.TOP_LEVEL_VARIABLE; | |
| 218 // ElementKind.TYPE_PARAMETER, | |
| 219 // ElementKind.UNIVERSE | |
| 220 throw new ArgumentError('Unknown CompletionSuggestionKind for: $kind'); | |
| 221 } | |
| 222 | |
| 223 /** | |
| 224 * Create an ElementKind based on a value from the analyzer engine. Access | |
| 225 * this function via new ElementKind.fromEngine(). | |
| 226 */ | |
| 227 ElementKind _elementKindFromEngine(engine.ElementKind kind) { | |
| 228 if (kind == engine.ElementKind.CLASS) { | |
| 229 return ElementKind.CLASS; | |
| 230 } | |
| 231 if (kind == engine.ElementKind.COMPILATION_UNIT) { | |
| 232 return ElementKind.COMPILATION_UNIT; | |
| 233 } | |
| 234 if (kind == engine.ElementKind.CONSTRUCTOR) { | |
| 235 return ElementKind.CONSTRUCTOR; | |
| 236 } | |
| 237 if (kind == engine.ElementKind.FIELD) { | |
| 238 return ElementKind.FIELD; | |
| 239 } | |
| 240 if (kind == engine.ElementKind.FUNCTION) { | |
| 241 return ElementKind.FUNCTION; | |
| 242 } | |
| 243 if (kind == engine.ElementKind.FUNCTION_TYPE_ALIAS) { | |
| 244 return ElementKind.FUNCTION_TYPE_ALIAS; | |
| 245 } | |
| 246 if (kind == engine.ElementKind.GETTER) { | |
| 247 return ElementKind.GETTER; | |
| 248 } | |
| 249 if (kind == engine.ElementKind.LABEL) { | |
| 250 return ElementKind.LABEL; | |
| 251 } | |
| 252 if (kind == engine.ElementKind.LIBRARY) { | |
| 253 return ElementKind.LIBRARY; | |
| 254 } | |
| 255 if (kind == engine.ElementKind.LOCAL_VARIABLE) { | |
| 256 return ElementKind.LOCAL_VARIABLE; | |
| 257 } | |
| 258 if (kind == engine.ElementKind.METHOD) { | |
| 259 return ElementKind.METHOD; | |
| 260 } | |
| 261 if (kind == engine.ElementKind.PARAMETER) { | |
| 262 return ElementKind.PARAMETER; | |
| 263 } | |
| 264 if (kind == engine.ElementKind.PREFIX) { | |
| 265 return ElementKind.PREFIX; | |
| 266 } | |
| 267 if (kind == engine.ElementKind.SETTER) { | |
| 268 return ElementKind.SETTER; | |
| 269 } | |
| 270 if (kind == engine.ElementKind.TOP_LEVEL_VARIABLE) { | |
| 271 return ElementKind.TOP_LEVEL_VARIABLE; | |
| 272 } | |
| 273 if (kind == engine.ElementKind.TYPE_PARAMETER) { | |
| 274 return ElementKind.TYPE_PARAMETER; | |
| 275 } | |
| 276 return ElementKind.UNKNOWN; | |
| 277 } | |
| 278 | |
| 279 /** | |
| 280 * Returns the [FileEdit] for the given [file], maybe `null`. | 96 * Returns the [FileEdit] for the given [file], maybe `null`. |
| 281 */ | 97 */ |
| 282 SourceFileEdit _getChangeFileEdit(SourceChange change, String file) { | 98 SourceFileEdit _getChangeFileEdit(SourceChange change, String file) { |
| 283 for (SourceFileEdit fileEdit in change.edits) { | 99 for (SourceFileEdit fileEdit in change.edits) { |
| 284 if (fileEdit.file == file) { | 100 if (fileEdit.file == file) { |
| 285 return fileEdit; | 101 return fileEdit; |
| 286 } | 102 } |
| 287 } | 103 } |
| 288 return null; | 104 return null; |
| 289 } | 105 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 304 } | 120 } |
| 305 for (int i = 0; i < listA.length; i++) { | 121 for (int i = 0; i < listA.length; i++) { |
| 306 if (!itemEqual(listA[i], listB[i])) { | 122 if (!itemEqual(listA[i], listB[i])) { |
| 307 return false; | 123 return false; |
| 308 } | 124 } |
| 309 } | 125 } |
| 310 return true; | 126 return true; |
| 311 } | 127 } |
| 312 | 128 |
| 313 /** | 129 /** |
| 314 * Creates a new [Location]. | |
| 315 */ | |
| 316 Location _locationForArgs(engine.AnalysisContext context, engine.Source source, | |
| 317 engine.SourceRange range) { | |
| 318 int startLine = 0; | |
| 319 int startColumn = 0; | |
| 320 { | |
| 321 engine.LineInfo lineInfo = context.getLineInfo(source); | |
| 322 if (lineInfo != null) { | |
| 323 engine.LineInfo_Location offsetLocation = | |
| 324 lineInfo.getLocation(range.offset); | |
| 325 startLine = offsetLocation.lineNumber; | |
| 326 startColumn = offsetLocation.columnNumber; | |
| 327 } | |
| 328 } | |
| 329 return new Location( | |
| 330 source.fullName, | |
| 331 range.offset, | |
| 332 range.length, | |
| 333 startLine, | |
| 334 startColumn); | |
| 335 } | |
| 336 | |
| 337 /** | |
| 338 * Creates a new [Location] for the given [engine.Element]. | |
| 339 */ | |
| 340 Location _locationFromElement(engine.Element element) { | |
| 341 engine.AnalysisContext context = element.context; | |
| 342 engine.Source source = element.source; | |
| 343 if (context == null || source == null) { | |
| 344 return null; | |
| 345 } | |
| 346 String name = element.displayName; | |
| 347 int offset = element.nameOffset; | |
| 348 int length = name != null ? name.length : 0; | |
| 349 if (element is engine.CompilationUnitElement) { | |
| 350 offset = 0; | |
| 351 length = 0; | |
| 352 } | |
| 353 engine.SourceRange range = new engine.SourceRange(offset, length); | |
| 354 return _locationForArgs(context, source, range); | |
| 355 } | |
| 356 | |
| 357 /** | |
| 358 * Creates a new [Location] for the given [engine.SearchMatch]. | |
| 359 */ | |
| 360 Location _locationFromMatch(engine.SearchMatch match) { | |
| 361 engine.Element enclosingElement = match.element; | |
| 362 return _locationForArgs( | |
| 363 enclosingElement.context, | |
| 364 enclosingElement.source, | |
| 365 match.sourceRange); | |
| 366 } | |
| 367 | |
| 368 /** | |
| 369 * Creates a new [Location] for the given [engine.AstNode]. | |
| 370 */ | |
| 371 Location _locationFromNode(engine.AstNode node) { | |
| 372 engine.CompilationUnit unit = | |
| 373 node.getAncestor((node) => node is engine.CompilationUnit); | |
| 374 engine.CompilationUnitElement unitElement = unit.element; | |
| 375 engine.AnalysisContext context = unitElement.context; | |
| 376 engine.Source source = unitElement.source; | |
| 377 engine.SourceRange range = new engine.SourceRange(node.offset, node.length); | |
| 378 return _locationForArgs(context, source, range); | |
| 379 } | |
| 380 | |
| 381 /** | |
| 382 * Creates a new [Location] for the given [engine.CompilationUnit]. | |
| 383 */ | |
| 384 Location _locationFromUnit(engine.CompilationUnit unit, | |
| 385 engine.SourceRange range) { | |
| 386 engine.CompilationUnitElement unitElement = unit.element; | |
| 387 engine.AnalysisContext context = unitElement.context; | |
| 388 engine.Source source = unitElement.source; | |
| 389 return _locationForArgs(context, source, range); | |
| 390 } | |
| 391 | |
| 392 /** | |
| 393 * Compare the maps [mapA] and [mapB], using [valueEqual] to compare map | 130 * Compare the maps [mapA] and [mapB], using [valueEqual] to compare map |
| 394 * values. | 131 * values. |
| 395 */ | 132 */ |
| 396 bool _mapEqual(Map mapA, Map mapB, bool valueEqual(a, b)) { | 133 bool _mapEqual(Map mapA, Map mapB, bool valueEqual(a, b)) { |
| 397 if (mapA.length != mapB.length) { | 134 if (mapA.length != mapB.length) { |
| 398 return false; | 135 return false; |
| 399 } | 136 } |
| 400 for (var key in mapA.keys) { | 137 for (var key in mapA.keys) { |
| 401 if (!mapB.containsKey(key)) { | 138 if (!mapB.containsKey(key)) { |
| 402 return false; | 139 return false; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 424 return b; | 161 return b; |
| 425 } | 162 } |
| 426 } else if (a == RefactoringProblemSeverity.ERROR) { | 163 } else if (a == RefactoringProblemSeverity.ERROR) { |
| 427 if (b == RefactoringProblemSeverity.FATAL) { | 164 if (b == RefactoringProblemSeverity.FATAL) { |
| 428 return b; | 165 return b; |
| 429 } | 166 } |
| 430 } | 167 } |
| 431 return a; | 168 return a; |
| 432 } | 169 } |
| 433 | 170 |
| 434 /** | |
| 435 * Create an OverriddenMember based on an element from the analyzer engine. | |
| 436 */ | |
| 437 OverriddenMember _overriddenMemberFromEngine(engine.Element member) { | |
| 438 Element element = elementFromEngine(member); | |
| 439 String className = member.enclosingElement.displayName; | |
| 440 return new OverriddenMember(element, className); | |
| 441 } | |
| 442 | |
| 443 | 171 |
| 444 /** | 172 /** |
| 445 * Create a [RefactoringFeedback] corresponding the given [kind]. | 173 * Create a [RefactoringFeedback] corresponding the given [kind]. |
| 446 */ | 174 */ |
| 447 RefactoringFeedback _refactoringFeedbackFromJson(JsonDecoder jsonDecoder, | 175 RefactoringFeedback _refactoringFeedbackFromJson(JsonDecoder jsonDecoder, |
| 448 String jsonPath, Object json, Map feedbackJson) { | 176 String jsonPath, Object json, Map feedbackJson) { |
| 449 RefactoringKind kind = jsonDecoder.refactoringKind; | 177 RefactoringKind kind = jsonDecoder.refactoringKind; |
| 450 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) { | 178 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) { |
| 451 return new ExtractLocalVariableFeedback.fromJson( | 179 return new ExtractLocalVariableFeedback.fromJson( |
| 452 jsonDecoder, | 180 jsonDecoder, |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 return new MoveFileOptions.fromJson(jsonDecoder, jsonPath, json); | 221 return new MoveFileOptions.fromJson(jsonDecoder, jsonPath, json); |
| 494 } | 222 } |
| 495 if (kind == RefactoringKind.RENAME) { | 223 if (kind == RefactoringKind.RENAME) { |
| 496 return new RenameOptions.fromJson(jsonDecoder, jsonPath, json); | 224 return new RenameOptions.fromJson(jsonDecoder, jsonPath, json); |
| 497 } | 225 } |
| 498 return null; | 226 return null; |
| 499 } | 227 } |
| 500 | 228 |
| 501 | 229 |
| 502 /** | 230 /** |
| 503 * Create a SearchResultKind based on a value from the search engine. | |
| 504 */ | |
| 505 SearchResultKind _searchResultKindFromEngine(engine.MatchKind kind) { | |
| 506 if (kind == engine.MatchKind.DECLARATION) { | |
| 507 return SearchResultKind.DECLARATION; | |
| 508 } | |
| 509 if (kind == engine.MatchKind.READ) { | |
| 510 return SearchResultKind.READ; | |
| 511 } | |
| 512 if (kind == engine.MatchKind.READ_WRITE) { | |
| 513 return SearchResultKind.READ_WRITE; | |
| 514 } | |
| 515 if (kind == engine.MatchKind.WRITE) { | |
| 516 return SearchResultKind.WRITE; | |
| 517 } | |
| 518 if (kind == engine.MatchKind.INVOCATION) { | |
| 519 return SearchResultKind.INVOCATION; | |
| 520 } | |
| 521 if (kind == engine.MatchKind.REFERENCE) { | |
| 522 return SearchResultKind.REFERENCE; | |
| 523 } | |
| 524 return SearchResultKind.UNKNOWN; | |
| 525 } | |
| 526 | |
| 527 | |
| 528 /** | |
| 529 * Type of callbacks used to decode parts of JSON objects. [jsonPath] is a | 231 * Type of callbacks used to decode parts of JSON objects. [jsonPath] is a |
| 530 * string describing the part of the JSON object being decoded, and [value] is | 232 * string describing the part of the JSON object being decoded, and [value] is |
| 531 * the part to decode. | 233 * the part to decode. |
| 532 */ | 234 */ |
| 533 typedef Object JsonDecoderCallback(String jsonPath, Object value); | 235 typedef Object JsonDecoderCallback(String jsonPath, Object value); |
| 534 | 236 |
| 535 /** | 237 /** |
| 536 * Base class for decoding JSON objects. The derived class must implement | 238 * Base class for decoding JSON objects. The derived class must implement |
| 537 * error reporting logic. | 239 * error reporting logic. |
| 538 */ | 240 */ |
| (...skipping 582 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1121 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 823 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
| 1122 hash = hash ^ (hash >> 11); | 824 hash = hash ^ (hash >> 11); |
| 1123 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 825 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
| 1124 } | 826 } |
| 1125 | 827 |
| 1126 static int hash2(a, b) => finish(combine(combine(0, a), b)); | 828 static int hash2(a, b) => finish(combine(combine(0, a), b)); |
| 1127 | 829 |
| 1128 static int hash4(a, b, c, d) => | 830 static int hash4(a, b, c, d) => |
| 1129 finish(combine(combine(combine(combine(0, a), b), c), d)); | 831 finish(combine(combine(combine(combine(0, a), b), c), d)); |
| 1130 } | 832 } |
| OLD | NEW |