OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 leg_apiimpl; | 5 library leg_apiimpl; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 | 9 |
10 import 'package:package_config/packages.dart'; | 10 import 'package:package_config/packages.dart'; |
11 import 'package:package_config/packages_file.dart' as pkgs; | 11 import 'package:package_config/packages_file.dart' as pkgs; |
12 import 'package:package_config/src/packages_impl.dart' show | 12 import 'package:package_config/src/packages_impl.dart' show |
13 MapPackages, | 13 MapPackages, |
14 NonFilePackagesDirectoryPackages; | 14 NonFilePackagesDirectoryPackages; |
15 import 'package:package_config/src/util.dart' show | 15 import 'package:package_config/src/util.dart' show |
16 checkValidPackageUri; | 16 checkValidPackageUri; |
17 import 'package:sdk_library_metadata/libraries.dart' as library_info; | |
18 | 17 |
19 import '../compiler_new.dart' as api; | 18 import '../compiler_new.dart' as api; |
20 import 'commandline_options.dart'; | 19 import 'commandline_options.dart'; |
21 import 'common.dart'; | 20 import 'common.dart'; |
22 import 'common/tasks.dart' show | 21 import 'common/tasks.dart' show |
23 GenericTask; | 22 GenericTask; |
24 import 'compiler.dart'; | 23 import 'compiler.dart'; |
25 import 'diagnostics/diagnostic_listener.dart' show | 24 import 'diagnostics/diagnostic_listener.dart' show |
26 DiagnosticOptions; | 25 DiagnosticOptions; |
27 import 'diagnostics/messages.dart' show | 26 import 'diagnostics/messages.dart' show |
28 Message; | 27 Message; |
29 import 'elements/elements.dart' as elements; | 28 import 'elements/elements.dart' as elements; |
30 import 'io/source_file.dart'; | 29 import 'io/source_file.dart'; |
30 import 'platform_configuration.dart' as platform_configuration; | |
31 import 'script.dart'; | 31 import 'script.dart'; |
32 | 32 |
33 const bool forceIncrementalSupport = | 33 const bool forceIncrementalSupport = |
34 const bool.fromEnvironment('DART2JS_EXPERIMENTAL_INCREMENTAL_SUPPORT'); | 34 const bool.fromEnvironment('DART2JS_EXPERIMENTAL_INCREMENTAL_SUPPORT'); |
35 | 35 |
36 /// Locations of the platform descriptor files relative to the library root. | |
37 const String _clientPlatform = "lib/dart_client.platform"; | |
38 const String _serverPlatform = "lib/dart_server.platform"; | |
39 const String _sharedPlatform = "lib/dart_shared.platform"; | |
40 const String _dart2dartPlatform = "lib/dart2dart.platform"; | |
41 | |
36 /// Implements the [Compiler] using a [api.CompilerInput] for supplying the | 42 /// Implements the [Compiler] using a [api.CompilerInput] for supplying the |
37 /// sources. | 43 /// sources. |
38 class CompilerImpl extends Compiler { | 44 class CompilerImpl extends Compiler { |
39 api.CompilerInput provider; | 45 api.CompilerInput provider; |
40 api.CompilerDiagnostics handler; | 46 api.CompilerDiagnostics handler; |
41 final Uri libraryRoot; | 47 final Uri platformConfigUri; |
42 final Uri packageConfig; | 48 final Uri packageConfig; |
43 final Uri packageRoot; | 49 final Uri packageRoot; |
44 final api.PackagesDiscoveryProvider packagesDiscoveryProvider; | 50 final api.PackagesDiscoveryProvider packagesDiscoveryProvider; |
45 Packages packages; | 51 Packages packages; |
46 List<String> options; | 52 List<String> options; |
47 Map<String, dynamic> environment; | 53 Map<String, dynamic> environment; |
48 bool mockableLibraryUsed = false; | 54 bool mockableLibraryUsed = false; |
49 final Set<library_info.Category> allowedLibraryCategories; | 55 |
56 /// A mapping of the dart: library-names to their location. | |
57 /// | |
58 /// Initialized in [setupSdk]. | |
59 Map<String, Uri> sdkLibraries; | |
50 | 60 |
51 GenericTask userHandlerTask; | 61 GenericTask userHandlerTask; |
52 GenericTask userProviderTask; | 62 GenericTask userProviderTask; |
53 GenericTask userPackagesDiscoveryTask; | 63 GenericTask userPackagesDiscoveryTask; |
54 | 64 |
65 Uri get libraryRoot => platformConfigUri.resolve("."); | |
66 | |
55 CompilerImpl(this.provider, | 67 CompilerImpl(this.provider, |
56 api.CompilerOutput outputProvider, | 68 api.CompilerOutput outputProvider, |
57 this.handler, | 69 this.handler, |
58 this.libraryRoot, | 70 Uri libraryRoot, |
59 this.packageRoot, | 71 this.packageRoot, |
60 List<String> options, | 72 List<String> options, |
61 this.environment, | 73 this.environment, |
62 [this.packageConfig, | 74 [this.packageConfig, |
63 this.packagesDiscoveryProvider]) | 75 this.packagesDiscoveryProvider]) |
64 : this.options = options, | 76 : this.options = options, |
65 this.allowedLibraryCategories = getAllowedLibraryCategories(options), | 77 this.platformConfigUri = resolvePlatformConfig(libraryRoot, options), |
66 super( | 78 super( |
67 outputProvider: outputProvider, | 79 outputProvider: outputProvider, |
68 enableTypeAssertions: hasOption(options, Flags.enableCheckedMode), | 80 enableTypeAssertions: hasOption(options, Flags.enableCheckedMode), |
69 enableUserAssertions: hasOption(options, Flags.enableCheckedMode), | 81 enableUserAssertions: hasOption(options, Flags.enableCheckedMode), |
70 trustTypeAnnotations: | 82 trustTypeAnnotations: |
71 hasOption(options, Flags.trustTypeAnnotations), | 83 hasOption(options, Flags.trustTypeAnnotations), |
72 trustPrimitives: | 84 trustPrimitives: |
73 hasOption(options, Flags.trustPrimitives), | 85 hasOption(options, Flags.trustPrimitives), |
74 enableMinification: hasOption(options, Flags.minify), | 86 enableMinification: hasOption(options, Flags.minify), |
75 useFrequencyNamer: | 87 useFrequencyNamer: |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
168 // CSV: Comma separated values. | 180 // CSV: Comma separated values. |
169 static List<String> extractCsvOption(List<String> options, String prefix) { | 181 static List<String> extractCsvOption(List<String> options, String prefix) { |
170 for (String option in options) { | 182 for (String option in options) { |
171 if (option.startsWith(prefix)) { | 183 if (option.startsWith(prefix)) { |
172 return option.substring(prefix.length).split(','); | 184 return option.substring(prefix.length).split(','); |
173 } | 185 } |
174 } | 186 } |
175 return const <String>[]; | 187 return const <String>[]; |
176 } | 188 } |
177 | 189 |
178 static Set<library_info.Category> getAllowedLibraryCategories( | 190 static Uri resolvePlatformConfig(Uri libraryRoot, |
179 List<String> options) { | 191 List<String> options) { |
180 Iterable<library_info.Category> categories = | 192 String platformConfigPath = |
181 extractCsvOption(options, '--categories=') | 193 extractStringOption(options, "--platform-config=", null); |
182 .map(library_info.parseCategory) | 194 if (platformConfigPath != null) { |
183 .where((x) => x != null); | 195 return libraryRoot.resolve(platformConfigPath); |
184 if (categories.isEmpty) { | 196 } else if (hasOption(options, '--output-type=dart')) { |
185 return new Set.from([library_info.Category.client]); | 197 return libraryRoot.resolve(_dart2dartPlatform); |
198 } else { | |
199 Iterable<String> categories = extractCsvOption(options, '--categories='); | |
200 if (categories.length == 0) { | |
201 return libraryRoot.resolve(_clientPlatform); | |
202 } | |
203 assert(categories.length <= 2); | |
204 if (categories.contains("Client")) { | |
205 if (categories.contains("Server")) { | |
206 return libraryRoot.resolve(_sharedPlatform); | |
207 } | |
208 return libraryRoot.resolve(_clientPlatform); | |
209 } | |
210 assert(categories.contains("Server")); | |
211 return libraryRoot.resolve(_serverPlatform); | |
186 } | 212 } |
187 return new Set.from(categories); | |
188 } | 213 } |
189 | 214 |
190 static bool hasOption(List<String> options, String option) { | 215 static bool hasOption(List<String> options, String option) { |
191 return options.indexOf(option) >= 0; | 216 return options.indexOf(option) >= 0; |
192 } | 217 } |
193 | 218 |
194 String lookupPatchPath(String dartLibraryName) { | |
195 library_info.LibraryInfo info = lookupLibraryInfo(dartLibraryName); | |
196 if (info == null) return null; | |
197 if (!info.isDart2jsLibrary) return null; | |
198 String path = info.dart2jsPatchPath; | |
199 if (path == null) return null; | |
200 return "lib/$path"; | |
201 } | |
202 | |
203 void log(message) { | 219 void log(message) { |
204 callUserHandler( | 220 callUserHandler( |
205 null, null, null, null, message, api.Diagnostic.VERBOSE_INFO); | 221 null, null, null, null, message, api.Diagnostic.VERBOSE_INFO); |
206 } | 222 } |
207 | 223 |
208 /// See [Compiler.translateResolvedUri]. | 224 /// See [Compiler.translateResolvedUri]. |
209 Uri translateResolvedUri(elements.LibraryElement importingLibrary, | 225 Uri translateResolvedUri(elements.LibraryElement importingLibrary, |
210 Uri resolvedUri, Spannable spannable) { | 226 Uri resolvedUri, Spannable spannable) { |
211 if (resolvedUri.scheme == 'dart') { | 227 if (resolvedUri.scheme == 'dart') { |
212 return translateDartUri(importingLibrary, resolvedUri, spannable); | 228 return translateDartUri(importingLibrary, resolvedUri, spannable); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 * See [LibraryLoader] for terminology on URIs. | 313 * See [LibraryLoader] for terminology on URIs. |
298 */ | 314 */ |
299 Uri translateUri(Spannable node, Uri readableUri) { | 315 Uri translateUri(Spannable node, Uri readableUri) { |
300 switch (readableUri.scheme) { | 316 switch (readableUri.scheme) { |
301 case 'package': return translatePackageUri(node, readableUri); | 317 case 'package': return translatePackageUri(node, readableUri); |
302 default: return readableUri; | 318 default: return readableUri; |
303 } | 319 } |
304 } | 320 } |
305 | 321 |
306 /// Translates "resolvedUri" with scheme "dart" to a [uri] resolved relative | 322 /// Translates "resolvedUri" with scheme "dart" to a [uri] resolved relative |
307 /// to [libraryRoot] according to the information in [library_info.libraries]. | 323 /// to [platformConfigUri] according to the information in the file at |
324 /// [platformConfigUri]. | |
308 /// | 325 /// |
309 /// Returns null and emits an error if the library could not be found or | 326 /// Returns null and emits an error if the library could not be found or |
310 /// imported into [importingLibrary]. | 327 /// imported into [importingLibrary]. |
311 /// | 328 /// |
312 /// If [importingLibrary] is a platform or patch library all dart2js libraries | 329 /// Internal libraries (whose name starts with '_') can be only resolved if |
313 /// can be resolved. Otherwise only libraries with categories in | 330 /// [importingLibrary] is a platform or patch library. |
314 /// [allowedLibraryCategories] can be resolved. | |
315 Uri translateDartUri(elements.LibraryElement importingLibrary, | 331 Uri translateDartUri(elements.LibraryElement importingLibrary, |
316 Uri resolvedUri, Spannable spannable) { | 332 Uri resolvedUri, Spannable spannable) { |
317 | 333 |
318 library_info.LibraryInfo libraryInfo = lookupLibraryInfo(resolvedUri.path); | 334 Uri location = lookupLibraryUri(resolvedUri.path); |
319 | 335 |
320 bool allowInternalLibraryAccess = false; | 336 if (location == null) { |
321 if (importingLibrary != null) { | 337 reporter.reportErrorMessage( |
322 if (importingLibrary.isPlatformLibrary || importingLibrary.isPatch) { | 338 spannable, |
323 allowInternalLibraryAccess = true; | 339 MessageKind.LIBRARY_NOT_FOUND, |
324 } else if (importingLibrary.canonicalUri.path.contains( | 340 {'resolvedUri': resolvedUri}); |
325 'sdk/tests/compiler/dart2js_native')) { | 341 return null; |
326 allowInternalLibraryAccess = true; | 342 } |
343 | |
344 if (resolvedUri.path.startsWith('_') ) { | |
345 bool allowInternalLibraryAccess = false; | |
floitsch
2015/10/30 20:43:07
bool allowInternalLibraryAccess = importingLibrary
sigurdm
2015/11/02 10:06:39
Done.
| |
346 if (importingLibrary != null) { | |
347 if (importingLibrary.isPlatformLibrary || importingLibrary.isPatch) { | |
348 allowInternalLibraryAccess = true; | |
349 } else if (importingLibrary.canonicalUri.path.contains( | |
350 'sdk/tests/compiler/dart2js_native')) { | |
351 allowInternalLibraryAccess = true; | |
352 } | |
353 } | |
354 | |
355 if (!allowInternalLibraryAccess) { | |
356 if (importingLibrary != null) { | |
357 reporter.reportErrorMessage( | |
358 spannable, | |
359 MessageKind.INTERNAL_LIBRARY_FROM, | |
360 {'resolvedUri': resolvedUri, | |
361 'importingUri': importingLibrary.canonicalUri}); | |
362 } else { | |
363 reporter.reportErrorMessage( | |
364 spannable, | |
365 MessageKind.INTERNAL_LIBRARY, | |
366 {'resolvedUri': resolvedUri}); | |
367 registerDisallowedLibraryUse(resolvedUri); | |
368 } | |
369 return null; | |
327 } | 370 } |
328 } | 371 } |
329 | 372 |
330 String computePath() { | 373 if (location.scheme == "unsupported") { |
331 if (libraryInfo == null) { | 374 reporter.reportErrorMessage( |
332 return null; | 375 spannable, |
333 } else if (!libraryInfo.isDart2jsLibrary) { | 376 MessageKind.LIBRARY_NOT_SUPPORTED, |
334 return null; | 377 {'resolvedUri': resolvedUri}); |
335 } else { | 378 registerDisallowedLibraryUse(resolvedUri); |
336 if (libraryInfo.isInternal && | |
337 !allowInternalLibraryAccess) { | |
338 if (importingLibrary != null) { | |
339 reporter.reportErrorMessage( | |
340 spannable, | |
341 MessageKind.INTERNAL_LIBRARY_FROM, | |
342 {'resolvedUri': resolvedUri, | |
343 'importingUri': importingLibrary.canonicalUri}); | |
344 } else { | |
345 reporter.reportErrorMessage( | |
346 spannable, | |
347 MessageKind.INTERNAL_LIBRARY, | |
348 {'resolvedUri': resolvedUri}); | |
349 registerDisallowedLibraryUse(resolvedUri); | |
350 } | |
351 return null; | |
352 } else if (!allowInternalLibraryAccess && | |
353 !allowedLibraryCategories.any(libraryInfo.categories.contains)) { | |
354 registerDisallowedLibraryUse(resolvedUri); | |
355 // TODO(sigurdm): Currently we allow the sdk libraries to import | |
356 // libraries from any category. We might want to revisit this. | |
357 return null; | |
358 } else { | |
359 return (libraryInfo.dart2jsPath != null) | |
360 ? libraryInfo.dart2jsPath | |
361 : libraryInfo.path; | |
362 } | |
363 } | |
364 } | |
365 | |
366 String path = computePath(); | |
367 | |
368 if (path == null) { | |
369 if (libraryInfo == null) { | |
370 reporter.reportErrorMessage( | |
371 spannable, | |
372 MessageKind.LIBRARY_NOT_FOUND, | |
373 {'resolvedUri': resolvedUri}); | |
374 } else { | |
375 reporter.reportErrorMessage( | |
376 spannable, | |
377 MessageKind.LIBRARY_NOT_SUPPORTED, | |
378 {'resolvedUri': resolvedUri}); | |
379 } | |
380 // TODO(johnniwinther): Support signaling the error through the returned | |
381 // value. | |
382 return null; | 379 return null; |
383 } | 380 } |
384 | 381 |
385 if (resolvedUri.path == 'html' || | 382 if (resolvedUri.path == 'html' || |
386 resolvedUri.path == 'io') { | 383 resolvedUri.path == 'io') { |
387 // TODO(ahe): Get rid of mockableLibraryUsed when test.dart | 384 // TODO(ahe): Get rid of mockableLibraryUsed when test.dart |
388 // supports this use case better. | 385 // supports this use case better. |
389 mockableLibraryUsed = true; | 386 mockableLibraryUsed = true; |
390 } | 387 } |
391 return libraryRoot.resolve("lib/$path"); | 388 return location; |
392 } | |
393 | |
394 Uri resolvePatchUri(String dartLibraryPath) { | |
395 String patchPath = lookupPatchPath(dartLibraryPath); | |
396 if (patchPath == null) return null; | |
397 return libraryRoot.resolve(patchPath); | |
398 } | 389 } |
399 | 390 |
400 Uri translatePackageUri(Spannable node, Uri uri) { | 391 Uri translatePackageUri(Spannable node, Uri uri) { |
401 try { | 392 try { |
402 checkValidPackageUri(uri); | 393 checkValidPackageUri(uri); |
403 } on ArgumentError catch (e) { | 394 } on ArgumentError catch (e) { |
404 reporter.reportErrorMessage( | 395 reporter.reportErrorMessage( |
405 node, | 396 node, |
406 MessageKind.INVALID_PACKAGE_URI, | 397 MessageKind.INVALID_PACKAGE_URI, |
407 {'uri': uri, 'exception': e.message}); | 398 {'uri': uri, 'exception': e.message}); |
408 return null; | 399 return null; |
409 } | 400 } |
410 return packages.resolve(uri, | 401 return packages.resolve(uri, |
411 notFound: (Uri notFound) { | 402 notFound: (Uri notFound) { |
412 reporter.reportErrorMessage( | 403 reporter.reportErrorMessage( |
413 node, | 404 node, |
414 MessageKind.LIBRARY_NOT_FOUND, | 405 MessageKind.LIBRARY_NOT_FOUND, |
415 {'resolvedUri': uri}); | 406 {'resolvedUri': uri}); |
416 return null; | 407 return null; |
417 }); | 408 }); |
418 } | 409 } |
419 | 410 |
420 Future<elements.LibraryElement> analyzeUri( | 411 Future<elements.LibraryElement> analyzeUri( |
421 Uri uri, | 412 Uri uri, |
422 {bool skipLibraryWithPartOfTag: true}) { | 413 {bool skipLibraryWithPartOfTag: true}) { |
414 List<Future> setupFunctions = new List<Future>(); | |
floitsch
2015/10/30 20:43:07
These are not functions.
I find even "setupFutures
sigurdm
2015/11/02 10:06:39
Done.
| |
415 if (sdkLibraries == null) { | |
416 setupFunctions.add(setupSdk()); | |
417 } | |
423 if (packages == null) { | 418 if (packages == null) { |
424 return setupPackages(uri).then((_) => super.analyzeUri(uri)); | 419 setupFunctions.add(setupPackages(uri)); |
425 } | 420 } |
426 return super.analyzeUri( | 421 return Future.wait(setupFunctions).then((_) => super.analyzeUri(uri)); |
427 uri, skipLibraryWithPartOfTag: skipLibraryWithPartOfTag); | |
428 } | 422 } |
429 | 423 |
430 Future setupPackages(Uri uri) { | 424 Future setupPackages(Uri uri) { |
431 if (packageRoot != null) { | 425 if (packageRoot != null) { |
432 // Use "non-file" packages because the file version requires a [Directory] | 426 // Use "non-file" packages because the file version requires a [Directory] |
433 // and we can't depend on 'dart:io' classes. | 427 // and we can't depend on 'dart:io' classes. |
434 packages = new NonFilePackagesDirectoryPackages(packageRoot); | 428 packages = new NonFilePackagesDirectoryPackages(packageRoot); |
435 } else if (packageConfig != null) { | 429 } else if (packageConfig != null) { |
436 return callUserProvider(packageConfig).then((packageConfigContents) { | 430 return callUserProvider(packageConfig).then((packageConfigContents) { |
437 if (packageConfigContents is String) { | 431 if (packageConfigContents is String) { |
(...skipping 20 matching lines...) Expand all Loading... | |
458 packages = Packages.noPackages; | 452 packages = Packages.noPackages; |
459 } else { | 453 } else { |
460 return callUserPackagesDiscovery(uri).then((p) { | 454 return callUserPackagesDiscovery(uri).then((p) { |
461 packages = p; | 455 packages = p; |
462 }); | 456 }); |
463 } | 457 } |
464 } | 458 } |
465 return new Future.value(); | 459 return new Future.value(); |
466 } | 460 } |
467 | 461 |
462 Future<Null> setupSdk() { | |
463 if (sdkLibraries == null) { | |
464 return platform_configuration.load(platformConfigUri, provider) | |
465 .then((Map<String, Uri> mapping) { | |
466 sdkLibraries = mapping; | |
467 }); | |
468 } else { | |
469 // The incremental compiler sets up the sdk before run. | |
470 // Therefore this will be called a second time. | |
471 return new Future.value(null); | |
472 } | |
473 } | |
474 | |
468 Future<bool> run(Uri uri) { | 475 Future<bool> run(Uri uri) { |
469 log('Allowed library categories: $allowedLibraryCategories'); | 476 log('Using platform configuration at ${platformConfigUri}'); |
470 | 477 |
471 return setupPackages(uri).then((_) { | 478 return Future.wait([setupSdk(), setupPackages(uri)]).then((_) { |
479 assert(sdkLibraries != null); | |
472 assert(packages != null); | 480 assert(packages != null); |
473 | 481 |
474 return super.run(uri).then((bool success) { | 482 return super.run(uri).then((bool success) { |
475 int cumulated = 0; | 483 int cumulated = 0; |
476 for (final task in tasks) { | 484 for (final task in tasks) { |
477 int elapsed = task.timing; | 485 int elapsed = task.timing; |
478 if (elapsed != 0) { | 486 if (elapsed != 0) { |
479 cumulated += elapsed; | 487 cumulated += elapsed; |
480 log('${task.name} took ${elapsed}msec'); | 488 log('${task.name} took ${elapsed}msec'); |
481 for (String subtask in task.subtasks) { | 489 for (String subtask in task.subtasks) { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
552 Future<Packages> callUserPackagesDiscovery(Uri uri) { | 560 Future<Packages> callUserPackagesDiscovery(Uri uri) { |
553 try { | 561 try { |
554 return userPackagesDiscoveryTask.measure( | 562 return userPackagesDiscoveryTask.measure( |
555 () => packagesDiscoveryProvider(uri)); | 563 () => packagesDiscoveryProvider(uri)); |
556 } catch (ex, s) { | 564 } catch (ex, s) { |
557 diagnoseCrashInUserCode('Uncaught exception in package discovery', ex, s); | 565 diagnoseCrashInUserCode('Uncaught exception in package discovery', ex, s); |
558 rethrow; | 566 rethrow; |
559 } | 567 } |
560 } | 568 } |
561 | 569 |
562 | |
563 fromEnvironment(String name) => environment[name]; | 570 fromEnvironment(String name) => environment[name]; |
564 | 571 |
565 library_info.LibraryInfo lookupLibraryInfo(String libraryName) { | 572 Uri lookupLibraryUri(String libraryName) { |
566 return library_info.libraries[libraryName]; | 573 assert(invariant(NO_LOCATION_SPANNABLE, |
574 sdkLibraries != null, message: "setupSdk() has not been run")); | |
575 return sdkLibraries[libraryName]; | |
576 } | |
577 | |
578 Uri resolvePatchUri(String libraryName) { | |
579 return backend.resolvePatchUri(libraryName, platformConfigUri); | |
567 } | 580 } |
568 } | 581 } |
OLD | NEW |