| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, 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 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 6 import 'package:analyzer/src/summary/format.dart'; |
| 7 import 'package:analyzer/src/summary/idl.dart'; |
| 8 import 'package:analyzer/src/summary/name_filter.dart'; |
| 9 |
| 10 /** |
| 11 * Create a [LinkedLibraryBuilder] corresponding to the given |
| 12 * [definingUnit], which should be the defining compilation unit for a library. |
| 13 * Compilation units referenced by the defining compilation unit via `part` |
| 14 * declarations will be retrieved using [getPart]. Public namespaces for |
| 15 * libraries referenced by the defining compilation unit via `import` |
| 16 * declarations (and files reachable from them via `part` and `export` |
| 17 * declarations) will be retrieved using [getImport]. |
| 18 */ |
| 19 LinkedLibraryBuilder prelink(UnlinkedUnit definingUnit, GetPartCallback getPart, |
| 20 GetImportCallback getImport, GetDeclaredVariable getDeclaredVariable) { |
| 21 return new _Prelinker(definingUnit, getPart, getImport, getDeclaredVariable) |
| 22 .prelink(); |
| 23 } |
| 24 |
| 25 /** |
| 26 * Return the raw string value of the variable with the given [name], |
| 27 * or `null` of the variable is not defined. |
| 28 */ |
| 29 typedef String GetDeclaredVariable(String name); |
| 30 |
| 31 /** |
| 32 * Type of the callback used by the prelinker to obtain public namespace |
| 33 * information about libraries imported by the library to be prelinked (and |
| 34 * the transitive closure of parts and exports reachable from those libraries). |
| 35 * [relativeUri] should be interpreted relative to the defining compilation |
| 36 * unit of the library being prelinked. |
| 37 * |
| 38 * If no file exists at the given uri, `null` should be returned. |
| 39 */ |
| 40 typedef UnlinkedPublicNamespace GetImportCallback(String relativeUri); |
| 41 |
| 42 /** |
| 43 * Type of the callback used by the prelinker to obtain unlinked summaries of |
| 44 * part files of the library to be prelinked. [relativeUri] should be |
| 45 * interpreted relative to the defining compilation unit of the library being |
| 46 * prelinked. |
| 47 * |
| 48 * If no file exists at the given uri, `null` should be returned. |
| 49 */ |
| 50 typedef UnlinkedUnit GetPartCallback(String relativeUri); |
| 51 |
| 52 /** |
| 53 * A [_Meaning] representing a class. |
| 54 */ |
| 55 class _ClassMeaning extends _Meaning { |
| 56 final Map<String, _Meaning> namespace; |
| 57 |
| 58 _ClassMeaning(int unit, int dependency, int numTypeParameters, this.namespace) |
| 59 : super(unit, ReferenceKind.classOrEnum, dependency, numTypeParameters); |
| 60 } |
| 61 |
| 62 /** |
| 63 * A [_Meaning] stores all the information necessary to find the declaration |
| 64 * referred to by a name in a namespace. |
| 65 */ |
| 66 class _Meaning { |
| 67 /** |
| 68 * Which unit in the dependent library contains the declared entity. |
| 69 */ |
| 70 final int unit; |
| 71 |
| 72 /** |
| 73 * The kind of entity being referred to. |
| 74 */ |
| 75 final ReferenceKind kind; |
| 76 |
| 77 /** |
| 78 * Which of the dependencies of the library being prelinked contains the |
| 79 * declared entity. |
| 80 */ |
| 81 final int dependency; |
| 82 |
| 83 /** |
| 84 * If the entity being referred to is generic, the number of type parameters |
| 85 * it accepts. Otherwise zero. |
| 86 */ |
| 87 final int numTypeParameters; |
| 88 |
| 89 _Meaning(this.unit, this.kind, this.dependency, this.numTypeParameters); |
| 90 |
| 91 /** |
| 92 * Encode this [_Meaning] as a [LinkedExportName], using the given [name]. |
| 93 */ |
| 94 LinkedExportName encodeExportName(String name) { |
| 95 return new LinkedExportNameBuilder( |
| 96 name: name, dependency: dependency, unit: unit, kind: kind); |
| 97 } |
| 98 |
| 99 /** |
| 100 * Encode this [_Meaning] as a [LinkedReference]. |
| 101 */ |
| 102 LinkedReferenceBuilder encodeReference() { |
| 103 return new LinkedReferenceBuilder( |
| 104 unit: unit, |
| 105 kind: kind, |
| 106 dependency: dependency, |
| 107 numTypeParameters: numTypeParameters); |
| 108 } |
| 109 } |
| 110 |
| 111 /** |
| 112 * A [_Meaning] representing a prefix introduced by an import directive. |
| 113 */ |
| 114 class _PrefixMeaning extends _Meaning { |
| 115 final Map<String, _Meaning> namespace = <String, _Meaning>{}; |
| 116 |
| 117 _PrefixMeaning() : super(0, ReferenceKind.prefix, 0, 0); |
| 118 } |
| 119 |
| 120 /** |
| 121 * Helper class containing temporary data structures needed to prelink a single |
| 122 * library. |
| 123 * |
| 124 * Note: throughout this class, a `null` value for a relative URI represents |
| 125 * the defining compilation unit of the library being prelinked. |
| 126 */ |
| 127 class _Prelinker { |
| 128 final UnlinkedUnit definingUnit; |
| 129 final GetPartCallback getPart; |
| 130 final GetImportCallback getImport; |
| 131 final GetDeclaredVariable getDeclaredVariable; |
| 132 |
| 133 /** |
| 134 * Cache of values returned by [getImport]. |
| 135 */ |
| 136 final Map<String, UnlinkedPublicNamespace> importCache = |
| 137 <String, UnlinkedPublicNamespace>{}; |
| 138 |
| 139 /** |
| 140 * Cache of values returned by [getPart]. |
| 141 */ |
| 142 final Map<String, UnlinkedUnit> partCache = <String, UnlinkedUnit>{}; |
| 143 |
| 144 /** |
| 145 * Names defined inside the library being prelinked. |
| 146 */ |
| 147 final Map<String, _Meaning> privateNamespace = <String, _Meaning>{ |
| 148 'dynamic': new _Meaning(0, ReferenceKind.classOrEnum, 0, 0), |
| 149 'void': new _Meaning(0, ReferenceKind.classOrEnum, 0, 0) |
| 150 }; |
| 151 |
| 152 /** |
| 153 * List of dependencies of the library being prelinked. This will be output |
| 154 * to [LinkedLibrary.dependencies]. |
| 155 */ |
| 156 final List<LinkedDependencyBuilder> dependencies = <LinkedDependencyBuilder>[ |
| 157 new LinkedDependencyBuilder() |
| 158 ]; |
| 159 |
| 160 /** |
| 161 * Map from the relative URI of a dependent library to the index of the |
| 162 * corresponding entry in [dependencies]. |
| 163 */ |
| 164 final Map<String, int> uriToDependency = <String, int>{null: 0}; |
| 165 |
| 166 /** |
| 167 * List of public namespaces corresponding to each entry in [dependencies]. |
| 168 */ |
| 169 final List<Map<String, _Meaning>> dependencyToPublicNamespace = |
| 170 <Map<String, _Meaning>>[null]; |
| 171 |
| 172 _Prelinker(this.definingUnit, this.getPart, this.getImport, |
| 173 this.getDeclaredVariable) { |
| 174 partCache[null] = definingUnit; |
| 175 importCache[null] = definingUnit.publicNamespace; |
| 176 } |
| 177 |
| 178 /** |
| 179 * Compute the public namespace for the library whose URI is reachable from |
| 180 * [definingUnit] via [relativeUri], by aggregating together public namespace |
| 181 * information from all of its parts. |
| 182 */ |
| 183 Map<String, _Meaning> aggregatePublicNamespace(String relativeUri) { |
| 184 if (uriToDependency.containsKey(relativeUri)) { |
| 185 return dependencyToPublicNamespace[uriToDependency[relativeUri]]; |
| 186 } |
| 187 assert(dependencies.length == dependencyToPublicNamespace.length); |
| 188 int dependency = dependencies.length; |
| 189 uriToDependency[relativeUri] = dependency; |
| 190 List<String> unitUris = getUnitUris(relativeUri); |
| 191 LinkedDependencyBuilder linkedDependency = new LinkedDependencyBuilder( |
| 192 uri: relativeUri, parts: unitUris.sublist(1)); |
| 193 dependencies.add(linkedDependency); |
| 194 |
| 195 Map<String, _Meaning> aggregated = <String, _Meaning>{}; |
| 196 |
| 197 for (int unitNum = 0; unitNum < unitUris.length; unitNum++) { |
| 198 String unitUri = unitUris[unitNum]; |
| 199 UnlinkedPublicNamespace importedNamespace = getImportCached(unitUri); |
| 200 if (importedNamespace == null) { |
| 201 continue; |
| 202 } |
| 203 for (UnlinkedPublicName name in importedNamespace.names) { |
| 204 aggregated.putIfAbsent(name.name, () { |
| 205 if (name.kind == ReferenceKind.classOrEnum) { |
| 206 Map<String, _Meaning> namespace = <String, _Meaning>{}; |
| 207 name.members.forEach((executable) { |
| 208 namespace[executable.name] = new _Meaning( |
| 209 unitNum, executable.kind, 0, executable.numTypeParameters); |
| 210 }); |
| 211 return new _ClassMeaning( |
| 212 unitNum, dependency, name.numTypeParameters, namespace); |
| 213 } |
| 214 return new _Meaning( |
| 215 unitNum, name.kind, dependency, name.numTypeParameters); |
| 216 }); |
| 217 } |
| 218 } |
| 219 |
| 220 dependencyToPublicNamespace.add(aggregated); |
| 221 return aggregated; |
| 222 } |
| 223 |
| 224 /** |
| 225 * Compute the export namespace for the library whose URI is reachable from |
| 226 * [definingUnit] via [relativeUri], by aggregating together public namespace |
| 227 * information from the library and the transitive closure of its exports. |
| 228 * |
| 229 * If [relativeUri] is `null` (meaning the export namespace of [definingUnit] |
| 230 * should be computed), then names defined in [definingUnit] are ignored. |
| 231 */ |
| 232 Map<String, _Meaning> computeExportNamespace(String relativeUri) { |
| 233 Map<String, _Meaning> exportNamespace = relativeUri == null |
| 234 ? <String, _Meaning>{} |
| 235 : aggregatePublicNamespace(relativeUri); |
| 236 void chaseExports( |
| 237 NameFilter filter, String relativeUri, Set<String> seenUris) { |
| 238 if (seenUris.add(relativeUri)) { |
| 239 UnlinkedPublicNamespace exportedNamespace = |
| 240 getImportCached(relativeUri); |
| 241 if (exportedNamespace != null) { |
| 242 for (UnlinkedExportPublic export in exportedNamespace.exports) { |
| 243 String relativeExportUri = |
| 244 _selectUri(export.uri, export.configurations); |
| 245 String exportUri = resolveUri(relativeUri, relativeExportUri); |
| 246 NameFilter newFilter = filter.merge( |
| 247 new NameFilter.forUnlinkedCombinators(export.combinators)); |
| 248 aggregatePublicNamespace(exportUri) |
| 249 .forEach((String name, _Meaning meaning) { |
| 250 if (newFilter.accepts(name) && |
| 251 !exportNamespace.containsKey(name)) { |
| 252 exportNamespace[name] = meaning; |
| 253 } |
| 254 }); |
| 255 chaseExports(newFilter, exportUri, seenUris); |
| 256 } |
| 257 } |
| 258 seenUris.remove(relativeUri); |
| 259 } |
| 260 } |
| 261 |
| 262 chaseExports(NameFilter.identity, relativeUri, new Set<String>()); |
| 263 return exportNamespace; |
| 264 } |
| 265 |
| 266 /** |
| 267 * Extract all the names defined in [unit] (which is the [unitNum]th unit in |
| 268 * the library being prelinked) and store them in [privateNamespace]. |
| 269 * Excludes names introduced by `import` statements. |
| 270 */ |
| 271 void extractPrivateNames(UnlinkedUnit unit, int unitNum) { |
| 272 for (UnlinkedClass cls in unit.classes) { |
| 273 privateNamespace.putIfAbsent(cls.name, () { |
| 274 Map<String, _Meaning> namespace = <String, _Meaning>{}; |
| 275 cls.fields.forEach((field) { |
| 276 if (field.isStatic && field.isConst) { |
| 277 namespace[field.name] = |
| 278 new _Meaning(unitNum, ReferenceKind.propertyAccessor, 0, 0); |
| 279 } |
| 280 }); |
| 281 cls.executables.forEach((executable) { |
| 282 ReferenceKind kind = null; |
| 283 if (executable.kind == UnlinkedExecutableKind.constructor) { |
| 284 kind = ReferenceKind.constructor; |
| 285 } else if (executable.kind == |
| 286 UnlinkedExecutableKind.functionOrMethod && |
| 287 executable.isStatic) { |
| 288 kind = ReferenceKind.method; |
| 289 } else if (executable.kind == UnlinkedExecutableKind.getter && |
| 290 executable.isStatic) { |
| 291 kind = ReferenceKind.propertyAccessor; |
| 292 } |
| 293 if (kind != null && executable.name.isNotEmpty) { |
| 294 namespace[executable.name] = new _Meaning( |
| 295 unitNum, kind, 0, executable.typeParameters.length); |
| 296 } |
| 297 }); |
| 298 return new _ClassMeaning( |
| 299 unitNum, 0, cls.typeParameters.length, namespace); |
| 300 }); |
| 301 } |
| 302 for (UnlinkedEnum enm in unit.enums) { |
| 303 privateNamespace.putIfAbsent(enm.name, () { |
| 304 Map<String, _Meaning> namespace = <String, _Meaning>{}; |
| 305 enm.values.forEach((UnlinkedEnumValue value) { |
| 306 namespace[value.name] = |
| 307 new _Meaning(unitNum, ReferenceKind.propertyAccessor, 0, 0); |
| 308 }); |
| 309 namespace['values'] = |
| 310 new _Meaning(unitNum, ReferenceKind.propertyAccessor, 0, 0); |
| 311 return new _ClassMeaning(unitNum, 0, 0, namespace); |
| 312 }); |
| 313 } |
| 314 for (UnlinkedExecutable executable in unit.executables) { |
| 315 privateNamespace.putIfAbsent( |
| 316 executable.name, |
| 317 () => new _Meaning( |
| 318 unitNum, |
| 319 executable.kind == UnlinkedExecutableKind.functionOrMethod |
| 320 ? ReferenceKind.topLevelFunction |
| 321 : ReferenceKind.topLevelPropertyAccessor, |
| 322 0, |
| 323 executable.typeParameters.length)); |
| 324 } |
| 325 for (UnlinkedTypedef typedef in unit.typedefs) { |
| 326 privateNamespace.putIfAbsent( |
| 327 typedef.name, |
| 328 () => new _Meaning(unitNum, ReferenceKind.typedef, 0, |
| 329 typedef.typeParameters.length)); |
| 330 } |
| 331 for (UnlinkedVariable variable in unit.variables) { |
| 332 privateNamespace.putIfAbsent( |
| 333 variable.name, |
| 334 () => new _Meaning( |
| 335 unitNum, ReferenceKind.topLevelPropertyAccessor, 0, 0)); |
| 336 if (!(variable.isConst || variable.isFinal)) { |
| 337 privateNamespace.putIfAbsent( |
| 338 variable.name + '=', |
| 339 () => new _Meaning( |
| 340 unitNum, ReferenceKind.topLevelPropertyAccessor, 0, 0)); |
| 341 } |
| 342 } |
| 343 } |
| 344 |
| 345 /** |
| 346 * Filter the export namespace for the library whose URI is reachable from |
| 347 * [definingUnit] via [relativeUri], retaining only those names accepted by |
| 348 * [combinators], and store the resulting names in [result]. Names that |
| 349 * already exist in [result] are not overwritten. |
| 350 */ |
| 351 void filterExportNamespace(String relativeUri, |
| 352 List<UnlinkedCombinator> combinators, Map<String, _Meaning> result) { |
| 353 Map<String, _Meaning> exportNamespace = computeExportNamespace(relativeUri); |
| 354 NameFilter filter = new NameFilter.forUnlinkedCombinators(combinators); |
| 355 exportNamespace.forEach((String name, _Meaning meaning) { |
| 356 if (filter.accepts(name) && !result.containsKey(name)) { |
| 357 result[name] = meaning; |
| 358 } |
| 359 }); |
| 360 } |
| 361 |
| 362 /** |
| 363 * Wrapper around [getImport] that caches the return value in [importCache]. |
| 364 */ |
| 365 UnlinkedPublicNamespace getImportCached(String relativeUri) { |
| 366 return importCache.putIfAbsent(relativeUri, () => getImport(relativeUri)); |
| 367 } |
| 368 |
| 369 /** |
| 370 * Wrapper around [getPart] that caches the return value in [partCache] and |
| 371 * updates [importCache] appropriately. |
| 372 */ |
| 373 UnlinkedUnit getPartCached(String relativeUri) { |
| 374 return partCache.putIfAbsent(relativeUri, () { |
| 375 UnlinkedUnit unit = getPart(relativeUri); |
| 376 importCache[relativeUri] = unit?.publicNamespace; |
| 377 return unit; |
| 378 }); |
| 379 } |
| 380 |
| 381 /** |
| 382 * Compute the set of relative URIs of all the compilation units in the |
| 383 * library whose URI is reachable from [definingUnit] via [relativeUri]. |
| 384 */ |
| 385 List<String> getUnitUris(String relativeUri) { |
| 386 List<String> result = <String>[relativeUri]; |
| 387 UnlinkedPublicNamespace publicNamespace = getImportCached(relativeUri); |
| 388 if (publicNamespace != null) { |
| 389 result.addAll(publicNamespace.parts |
| 390 .map((String uri) => resolveUri(relativeUri, uri))); |
| 391 } |
| 392 return result; |
| 393 } |
| 394 |
| 395 /** |
| 396 * Process a single `import` declaration in the library being prelinked. The |
| 397 * return value is the index of the imported library in [dependencies]. |
| 398 */ |
| 399 int handleImport(UnlinkedImport import) { |
| 400 String uri = import.isImplicit |
| 401 ? 'dart:core' |
| 402 : _selectUri(import.uri, import.configurations); |
| 403 Map<String, _Meaning> targetNamespace = null; |
| 404 if (import.prefixReference != 0) { |
| 405 // The name introduced by an import declaration can't have a prefix of |
| 406 // its own. |
| 407 assert( |
| 408 definingUnit.references[import.prefixReference].prefixReference == 0); |
| 409 String prefix = definingUnit.references[import.prefixReference].name; |
| 410 _Meaning prefixMeaning = privateNamespace[prefix]; |
| 411 if (prefixMeaning is _PrefixMeaning) { |
| 412 targetNamespace = prefixMeaning.namespace; |
| 413 } |
| 414 } else { |
| 415 targetNamespace = privateNamespace; |
| 416 } |
| 417 filterExportNamespace(uri, import.combinators, targetNamespace); |
| 418 return uriToDependency[uri]; |
| 419 } |
| 420 |
| 421 /** |
| 422 * Produce a [LinkedUnit] for the given [unit], by resolving every one of |
| 423 * its references. |
| 424 */ |
| 425 LinkedUnitBuilder linkUnit(UnlinkedUnit unit) { |
| 426 if (unit == null) { |
| 427 return new LinkedUnitBuilder(); |
| 428 } |
| 429 Map<int, Map<String, _Meaning>> prefixNamespaces = |
| 430 <int, Map<String, _Meaning>>{}; |
| 431 List<LinkedReferenceBuilder> references = <LinkedReferenceBuilder>[]; |
| 432 for (int i = 0; i < unit.references.length; i++) { |
| 433 UnlinkedReference reference = unit.references[i]; |
| 434 Map<String, _Meaning> namespace; |
| 435 if (reference.prefixReference == 0) { |
| 436 namespace = privateNamespace; |
| 437 } else { |
| 438 // Prefix references must always point backward. |
| 439 assert(reference.prefixReference < i); |
| 440 namespace = prefixNamespaces[reference.prefixReference]; |
| 441 // Expressions like 'a.b.c.d' cannot be prelinked. |
| 442 namespace ??= const <String, _Meaning>{}; |
| 443 } |
| 444 _Meaning meaning = namespace[reference.name]; |
| 445 if (meaning != null) { |
| 446 if (meaning is _PrefixMeaning) { |
| 447 prefixNamespaces[i] = meaning.namespace; |
| 448 } else if (meaning is _ClassMeaning) { |
| 449 prefixNamespaces[i] = meaning.namespace; |
| 450 } |
| 451 references.add(meaning.encodeReference()); |
| 452 } else { |
| 453 references |
| 454 .add(new LinkedReferenceBuilder(kind: ReferenceKind.unresolved)); |
| 455 } |
| 456 } |
| 457 return new LinkedUnitBuilder(references: references); |
| 458 } |
| 459 |
| 460 /** |
| 461 * Form the [LinkedLibrary] for the [definingUnit] that was passed to the |
| 462 * constructor. |
| 463 */ |
| 464 LinkedLibraryBuilder prelink() { |
| 465 // Gather up the unlinked summaries for all the compilation units in the |
| 466 // library. |
| 467 List<UnlinkedUnit> units = getUnitUris(null).map(getPartCached).toList(); |
| 468 |
| 469 // Create the private namespace for the library by gathering all the names |
| 470 // defined in its compilation units. |
| 471 for (int unitNum = 0; unitNum < units.length; unitNum++) { |
| 472 UnlinkedUnit unit = units[unitNum]; |
| 473 if (unit != null) { |
| 474 extractPrivateNames(unit, unitNum); |
| 475 } |
| 476 } |
| 477 |
| 478 // Fill in exported names. This must be done before filling in prefixes |
| 479 // defined in import declarations, because prefixes shouldn't shadow |
| 480 // exports. |
| 481 List<LinkedExportNameBuilder> exportNames = <LinkedExportNameBuilder>[]; |
| 482 computeExportNamespace(null).forEach((String name, _Meaning meaning) { |
| 483 if (!privateNamespace.containsKey(name)) { |
| 484 exportNames.add(meaning.encodeExportName(name)); |
| 485 } |
| 486 }); |
| 487 |
| 488 // Fill in prefixes defined in import declarations. |
| 489 for (UnlinkedImport import in units[0].imports) { |
| 490 if (import.prefixReference != 0) { |
| 491 privateNamespace.putIfAbsent( |
| 492 units[0].references[import.prefixReference].name, |
| 493 () => new _PrefixMeaning()); |
| 494 } |
| 495 } |
| 496 |
| 497 // Fill in imported and exported names. |
| 498 List<int> importDependencies = |
| 499 definingUnit.imports.map(handleImport).toList(); |
| 500 List<int> exportDependencies = |
| 501 definingUnit.publicNamespace.exports.map((UnlinkedExportPublic exp) { |
| 502 String uri = _selectUri(exp.uri, exp.configurations); |
| 503 return uriToDependency[uri]; |
| 504 }).toList(); |
| 505 |
| 506 // Link each compilation unit. |
| 507 List<LinkedUnitBuilder> linkedUnits = units.map(linkUnit).toList(); |
| 508 |
| 509 return new LinkedLibraryBuilder( |
| 510 units: linkedUnits, |
| 511 dependencies: dependencies, |
| 512 importDependencies: importDependencies, |
| 513 exportDependencies: exportDependencies, |
| 514 exportNames: exportNames, |
| 515 numPrelinkedDependencies: dependencies.length); |
| 516 } |
| 517 |
| 518 /** |
| 519 * Resolve [relativeUri] relative to [sourceUri]. Works correctly if |
| 520 * [sourceUri] is also relative. |
| 521 */ |
| 522 String resolveUri(String sourceUri, String relativeUri) { |
| 523 if (sourceUri == null) { |
| 524 return relativeUri; |
| 525 } else { |
| 526 return resolveRelativeUri(Uri.parse(sourceUri), Uri.parse(relativeUri)) |
| 527 .toString(); |
| 528 } |
| 529 } |
| 530 |
| 531 /** |
| 532 * Return the URI of the first configuration from the given [configurations] |
| 533 * which condition is satisfied, or the [defaultUri]. |
| 534 */ |
| 535 String _selectUri( |
| 536 String defaultUri, List<UnlinkedConfiguration> configurations) { |
| 537 for (UnlinkedConfiguration configuration in configurations) { |
| 538 if (getDeclaredVariable(configuration.name) == configuration.value) { |
| 539 return configuration.uri; |
| 540 } |
| 541 } |
| 542 return defaultUri; |
| 543 } |
| 544 } |
| OLD | NEW |