diff --git a/pkg/analysis_server/test/verify_sorted_test.dart b/pkg/analysis_server/test/verify_sorted_test.dart index 4b40969a15f0..06c1f18b6c94 100644 --- a/pkg/analysis_server/test/verify_sorted_test.dart +++ b/pkg/analysis_server/test/verify_sorted_test.dart @@ -31,10 +31,6 @@ void main() { group('linter', () { buildTestsForLinter(); }); - - group('nnbd_migration', () { - buildTestsForNnbdMigration(); - }); } void buildTests({ @@ -67,7 +63,6 @@ void buildTestsForAnalysisServer() { // remove these exclusions. 'lib/protocol/protocol_constants.dart', 'lib/protocol/protocol_generated.dart', - 'lib/src/edit/nnbd_migration/resources/resources.g.dart', 'test/integration/support/integration_test_methods.dart', 'test/integration/support/protocol_matchers.dart', // The following are not generated, but can't be sorted because they contain @@ -124,12 +119,6 @@ void buildTestsForLinter() { ]); } -void buildTestsForNnbdMigration() { - buildTests( - packagePath: 'nnbd_migration', - excludedPaths: ['lib/src/front_end/resources/resources.g.dart']); -} - void buildTestsIn(AnalysisSession session, String testDirPath, List excludedPath, Folder directory) { var pathContext = session.resourceProvider.pathContext; diff --git a/pkg/nnbd_migration/.gitignore b/pkg/nnbd_migration/.gitignore deleted file mode 100644 index 7138b41f8afe..000000000000 --- a/pkg/nnbd_migration/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -lib/src/front_end/resources/*.js.deps -lib/src/front_end/resources/*.js.map -lib/src/front_end/resources/migration.js diff --git a/pkg/nnbd_migration/CHANGELOG.md b/pkg/nnbd_migration/CHANGELOG.md deleted file mode 100644 index 66a6ebd68486..000000000000 --- a/pkg/nnbd_migration/CHANGELOG.md +++ /dev/null @@ -1,6 +0,0 @@ -## 0.1.1 -* Add README text indicating that this package should not be used; instead, - migrations should be launched using `dart migrate`. - -## 0.1.0 -* Separated from analysis_server project. diff --git a/pkg/nnbd_migration/LICENSE b/pkg/nnbd_migration/LICENSE deleted file mode 100644 index a0d5f5431886..000000000000 --- a/pkg/nnbd_migration/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2019, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkg/nnbd_migration/OWNERS b/pkg/nnbd_migration/OWNERS deleted file mode 100644 index 80d5ff4f329a..000000000000 --- a/pkg/nnbd_migration/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -file:/tools/OWNERS_ANALYZER -file:/tools/OWNERS_FOUNDATION diff --git a/pkg/nnbd_migration/README.md b/pkg/nnbd_migration/README.md deleted file mode 100644 index 5b005f8fab4e..000000000000 --- a/pkg/nnbd_migration/README.md +++ /dev/null @@ -1,169 +0,0 @@ -# Null safety migration tooling - -**Note**: - - * This migration tool is now available through the SDK, using the `dart - migrate` command. Support for running it via `pub activate` is deprecated. - * The null safety migration tooling is in an early state and may have bugs and - other issues. - * As null safety is still in preview, we recommend only doing trial - migrations. The final migration of apps and packages should not be done - until the feature is more complete. - * For best results, use SDK version 2.9.0-10.0.dev or higher. - -## How migration works - -The migration uses a _new interactive algorithm_ designed specifically for [Dart -null safety](https://dart.dev/null-safety). - -Typical code migration tools are designed to be run once, handle most cases, and -let the developer do manual cleanup on the result. This does **not work well** -for null safety and attempting this workflow will result in a lot more manual -work. Similarly, after your migration has been applied, the migration **cannot -be rerun** without first reverting it. - -### Why does the interactive approach save so much time? - -Remember that Dart already has nullable types. Every type in old Dart code is -nullable! What old Dart lacks is _non-null_ types. - -And like most migrations, our tool tries to preserve your code's current -behavior. In the case of null safety, we may mark a lot of your types as -nullable -- because they really were nullable before. - -Nulls are traced through your program as far as they can go, and types are -marked nullable in this process. If the tool makes a single mistake or choice -you disagree with, it can lead to many excess nullable types. - -### Interactive feedback to the tool - -Unintentional null is the top cause of crashes in Dart programs. By marking your -intention with comments like `/*?*/` and `/*!*/`, we can stop these -unintentional nulls from spreading through your program in your migrated code. -Adding a small number of these hints will have a huge impact on migration -quality. - -The high level workflow of the tool is therefore driven through an interactive -web UI. After starting the tool with `dart migrate`, open the presented URL in a -browser. Scan through the changes, use the "nullability trace" feature to find -the best place to add a nullability hint (adding a hint in the best place can -prevent dozens of types from being made nullable). Rerun the migration and -repeat, committing the hints as you go. When the output is correct and -acceptable, apply the migration. - -For example, - -```dart -List ints = const [0, null]; -int zero = ints[0]; -int one = zero + 1; -List zeroOne = [zero, one]; -``` - -The default migration will be backwards compatible, but not ideal. - -```dart -List ints = const [0, null]; -int? zero = ints[0]; -int one = zero! + 1; -List zeroOne = [zero, one]; -``` - -`zero` should not be marked nullable, but it is. We then have cascading quality -issues, such as null-checking a value that shouldn't have been marked null, and -marking other variables as null due to deep null tracing. We can fix this all by -adding a single `/*!*/` hint. - -```dart -List ints = const [0, null]; -int/*!*/ zero = ints[0]!; // Just add /*!*/ here, the migration tool does the rest! -int one = zero + 1; -List zeroOne = [zero, one]; -``` - -If you add one hint before migrating, you have done the equivalent of making -five manual edits after migrating. To find the best place to put your hints, use -the preview tool's nullability trace feature. This lets you trace back up to the -root cause of any type's inferred nullability. Add hints as close to the -original source of null as possible to have the biggest impact to the migration. - -**Note**: The migration tool **cannot be rerun on a migrated codebase.** At -that point in time, every nullable and non-nullable type is indistinguishable -from an **intentionally** nullable or non-nullable type. The opportunity to -change large numbers of types for you at once without also accidentally changing -your intent has been lost. A long migration effort (such as one on a large -project) can be done incrementally, by committing these hints over time. - - - -## Migrating a package - -1. Select a package to work on, and open a command terminal in the top-level of - the package directory. -2. Run `pub get` in order to make available all dependencies of the package. -3. It is best to migrate a package to null safety _after_ the package's - dependencies have migrated to null safety. Run - `pub outdated --mode=null-safety` to learn the migration status of the - package's dependencies. See the - [pub outdated documentation](https://dart.dev/tools/pub/cmd/pub-outdated) - for more information. -4. It is best to migrate a package starting from a clean code repository state - (`git status`, for example), in case you must revert the migration. Ensure - there are no pending changes in the package's code repository. -5. Run the migration tool from the top-level of the package directory: - - ``` - dart migrate - ``` - -The migration tool will display a URL for the web interface. Open that URL in a -browser to view, analyze, and improve the proposed null-safe migration. - -## Using the tool - -1. Run the tool (see above). -2. Once analysis and migration is complete, open the indicated URL in a browser. -3. Start with an important or interesting file in your package on the left side - by clicking on it. -4. Look at the proposed edits in the upper right, and click on them in turn. -5. If you see an edit that looks wrong: - 1. Use the "trace view" in the bottom right to find the root cause - 2. Either click on an "add hint" button to correct it at the root, or open - your editor and make the change manually. - * Some changes are as simple as adding a `/*!*/` hint on a type. The - tool has buttons to do this for you. - * Others may require larger refactors. These changes can be made in - your editor. - * Changes may even be committed to source code management before finally - _applying_ the migration. In this way, a migration of a large package - can be carried out over multiple sessions, or between multiple - engineers. Committing hints and other adjustments along the way helps - to separate the concerns of describing user intent vs committing to the - migration result. - 3. Periodically rerun the migration and repeat. -6. Once you are satisfied with the proposed migration: - 1. Save your work using git or other means. Applying the migration will - overwrite the existing files on disk. - * Note: In addition to making edits to the Dart source code in - the package, applying the migration edits the package's `pubspec.yaml` - file, in order to change the Dart SDK version constraints, under the - `environment` field, and the "Package Config" file, located in the - package's `.dart_tool` directory, named `package_config.json`. - 2. Apply the migration by clicking the `Apply Migration` button in the - interface. - 3. Tip: leaving the web UI open may help you if you later have test failures - or analysis errors. -7. Rerun `pub get`, then analyze and test your package. - 1. If there are new static analysis issues, or if a test fails, you may - still use the preview to help you figure out what went wrong. - 2. If large changes are required, revert the migration, and go back to step - one. The tool does not provide any revert capability; this must be done - via source code management (for example, `git checkout`). -8. Commit and/or publish your migrated null-safe code. - -## Providing feedback - -Please file issues at https://github.com/dart-lang/sdk/issues, and reference the -`area-migration` label (you may not be able to apply the label yourself). diff --git a/pkg/nnbd_migration/TRIAGE.md b/pkg/nnbd_migration/TRIAGE.md deleted file mode 100644 index 1abd578ac67e..000000000000 --- a/pkg/nnbd_migration/TRIAGE.md +++ /dev/null @@ -1,72 +0,0 @@ -# Triage Priorities for Dart Migration tool - -This document describes the relative priorities for bugs filed under the -`area-migration` tag in GitHub as in -[this search](https://github.com/dart-lang/sdk/issues?q=is%3Aopen+is%3Aissue+label%3Aarea-migration). -While there are always exceptions to any rule, in general try to align our -priorities with these definitions. - -To triage bugs, search for `area-migration` -[bugs that are not currently triaged](https://github.com/dart-lang/sdk/issues?q=is%3Aopen+is%3Aissue+label%3Aarea-migration+-label%3AP0+-label%3AP1+-label%3AP2+-label%3AP3+-label%3AP4) -and for each bug, mark priority based on how closely it matches with the below -constraints. - -## Migration tool triage priorities - -Descriptions here use [terms and definitions](#terms-and-definitions) from the -end of this document. If your bug doesn't precisely match one of these, -consider how impactful it is compared to examples given here and pick a priority -reflecting that. - -### P0 - -* Crashes that can't be worked around by `--ignore-exceptions`, typical impact or widespread. -* Crashes that can be worked around by `--ignore-exceptions`, widespread. - -### P1 - -* Crashes that can be worked around by `--ignore-exceptions`, typical impact. -* An enhancement required for critical milestones for key users, or that has - significant evidence gathered indicating a positive impact if implemented. -* A problem that is significantly impairing a key user's ability to migrate - their code. - -### P2 - -* Crashes, edge case. -* An enhancement with typical impact that doesn't fit the criteria above but - would still be significantly beneficial to users (i.e. more than just a "would - be nice" feature). -* A problem with typical impact that doesn't fit the criteria above, but impairs - users' ability to migrate their code. - -### P3 - -* Crashes, theoretical. -* An enhancement that doesn't fit the criteria above. -* A problem that doesn't fit the criteria above, but impairs users' ability to - migrate their code. - -## Terms and definitions - -### Terms describing impact - -* "widespread" - Impact endemic throughout the ecosystem, or at least far - enough that this is impacting multiple key users. -* "typical impact" - Known to impact a key user, or likely to impact a - significant percentage of all users. Issues are assumed to have typical - impact unless we have evidence otherwise. -* "edge cases" - Impacting only small parts of the ecosystem. For example, one - package, or one key user with a workaround. Note this is an edge case from - the perspective of the ecosystem vs. language definition. Note that since the - migration tool is still in an early adoption phase, if a bug has only been - reported by one user, that's not sufficient evidence that it's an edge case. - To be considered an edge case, we need to have a concrete reason to suspect - that the bug is caused by an unusual pattern in the user's source code, or - unusual user behavior. -* "theoretical" - Something that we think is unlikely to happen in the wild - and there's no evidence for it happening in the wild. - -### Other terms - -* "key users" - Flutter, Pub, Fuchsia, Dart, Google3, 1P diff --git a/pkg/nnbd_migration/analysis_options.yaml b/pkg/nnbd_migration/analysis_options.yaml deleted file mode 100644 index 0032b11ad34d..000000000000 --- a/pkg/nnbd_migration/analysis_options.yaml +++ /dev/null @@ -1,31 +0,0 @@ -include: package:lints/recommended.yaml - -analyzer: - errors: - # There are currently 175 violations. - always_declare_return_types: ignore - # There are currently 60 violations. - annotate_overrides: ignore - # A number of class names mix UpperCamelCase with underscores. - camel_case_types: ignore - # This package freely and frequently imports implementation from analyzer. - implementation_imports: ignore - # Our test methods are named with snake_case. - non_constant_identifier_names: ignore - # There are currently 200 violations. - omit_local_variable_types: ignore - # There are currently 20 violations. - unawaited_futures: ignore - todo: ignore - - language: - strict-casts: true - -linter: - rules: - - enable_null_safety - - library_annotations - - unawaited_futures - - unnecessary_library_directive - - unnecessary_parenthesis - - unreachable_from_main diff --git a/pkg/nnbd_migration/bin/migrate.dart b/pkg/nnbd_migration/bin/migrate.dart deleted file mode 100644 index aac79667144f..000000000000 --- a/pkg/nnbd_migration/bin/migrate.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:io'; - -import 'package:args/src/arg_results.dart'; -import 'package:nnbd_migration/migration_cli.dart'; - -void main(List args) async { - var cli = MigrationCli(binaryName: 'nnbd_migration'); - late ArgResults argResults; - try { - try { - argResults = MigrationCli.createParser().parse(args); - } on FormatException catch (e) { - cli.handleArgParsingException(e); - } - await cli.decodeCommandLineArgs(argResults)?.run(); - } on MigrationExit catch (migrationExit) { - exitCode = migrationExit.exitCode; - } -} diff --git a/pkg/nnbd_migration/lib/fix_reason_target.dart b/pkg/nnbd_migration/lib/fix_reason_target.dart deleted file mode 100644 index 1588b0530e6b..000000000000 --- a/pkg/nnbd_migration/lib/fix_reason_target.dart +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2020, 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. - -/// Data structure representing a part of a type. When a fix has multiple -/// reasons (due to a complex type having different nullabilities at different -/// locations), this data structure allows us to tell which part of the type -/// is associated with each reason. -abstract class FixReasonTarget { - /// Fix reason target representing the full type, rather than a part of it. - static const FixReasonTarget root = _FixReasonTarget_Root(); - - const FixReasonTarget._(); - - /// Gets a fix reason target representing the type's return type. - FixReasonTarget get returnType => _FixReasonTarget_ReturnType(this); - - /// Returns a description of the fix reason target that's suitable for - /// appending to a description of a nullability trace. - /// - /// For example, `root.returnType.suffix` returns ` for return type`. This - /// can be attached to a string like `nullability reason` to form - /// `nullability reason for return type`. - String get suffix => _describe('for'); - - /// Gets a fix reason target representing the type's yielded type. This - /// represents the type argument of `Future` or `FutureOr`, or in the case - /// where `await` is applied to a non-future type, the type itself. - FixReasonTarget get yieldedType => _FixReasonTarget_YieldedType(this); - - /// Gets a fix reason target representing one of the type's named parameters. - FixReasonTarget namedParameter(String name) => - _FixReasonTarget_NamedParameter(this, name); - - /// Gets a fix reason target representing one of the type's positional - /// parameters. - FixReasonTarget positionalParameter(int i) => - _FixReasonTarget_PositionalParameter(this, i); - - /// Gets a fix reason target representing one of the type's type arguments. - FixReasonTarget typeArgument(int i) => _FixReasonTarget_TypeArgument(this, i); - - String _describe(String preposition); -} - -/// Fix reason target representing a named parameter of a function type. -class _FixReasonTarget_NamedParameter extends _FixReasonTarget_Part { - final String name; - - _FixReasonTarget_NamedParameter(super.inner, this.name); - - @override - int get hashCode => Object.hash(2, inner, name); - - @override - bool operator ==(Object other) => - other is _FixReasonTarget_NamedParameter && - inner == other.inner && - name == other.name; - - @override - String _describe(String preposition) => - ' $preposition parameter $name${inner._describe('of')}'; -} - -/// Fix reason target representing a type that forms part of a larger type (e.g. -/// the `int` part of `List`). -abstract class _FixReasonTarget_Part extends FixReasonTarget { - final FixReasonTarget inner; - - _FixReasonTarget_Part(this.inner) : super._(); -} - -/// Fix reason target representing a positional parameter of a function type. -class _FixReasonTarget_PositionalParameter extends _FixReasonTarget_Part { - final int index; - - _FixReasonTarget_PositionalParameter(super.inner, this.index); - - @override - int get hashCode => Object.hash(1, inner, index); - - @override - bool operator ==(Object other) => - other is _FixReasonTarget_PositionalParameter && - inner == other.inner && - index == other.index; - - @override - String _describe(String preposition) => - ' $preposition parameter $index${inner._describe('of')}'; -} - -/// Fix reason target representing the return type of a function type. -class _FixReasonTarget_ReturnType extends _FixReasonTarget_Part { - _FixReasonTarget_ReturnType(super.inner); - - @override - int get hashCode => Object.hash(3, inner); - - @override - bool operator ==(Object other) => - other is _FixReasonTarget_ReturnType && inner == other.inner; - - @override - String _describe(String preposition) => - ' $preposition return type${inner._describe('of')}'; -} - -/// Fix reason target representing the root of the type in question. -class _FixReasonTarget_Root extends FixReasonTarget { - const _FixReasonTarget_Root() : super._(); - - @override - int get hashCode => 0; - - @override - bool operator ==(Object other) => other is _FixReasonTarget_Root; - - @override - String _describe(String preposition) => ''; -} - -/// Fix reason target representing a type argument of an interface type. -class _FixReasonTarget_TypeArgument extends _FixReasonTarget_Part { - final int index; - - _FixReasonTarget_TypeArgument(super.inner, this.index); - - @override - int get hashCode => Object.hash(5, inner, index); - - @override - bool operator ==(Object other) => - other is _FixReasonTarget_TypeArgument && - inner == other.inner && - index == other.index; - - @override - String _describe(String preposition) => - ' $preposition type argument $index${inner._describe('of')}'; -} - -/// Fix reason target representing the type argument of `Future` or `FutureOr`, -/// or in the case where `await` is applied to a non-future type, the type -/// itself. -/// -/// This allows the migration tool to describe a type correspondence that exists -/// in a subtype check involving `FutureOr`, for example if the user tries to -/// assign `List` to `FutureOr>`, then the migration tool -/// determines that it needs to change `*` into `?`. To make this determination -/// it has to form a correspondence between the type argument of the source type -/// and the type argument of the type argument of the destination type. To -/// explain to the user which part of the two types is involved in the -/// correspondence, we need an ambiguous way of referring to either "type -/// argument of type argument of" or simply "type argument". The solution is to -/// describe the fix reason target as "type argument of yielded type". -class _FixReasonTarget_YieldedType extends _FixReasonTarget_Part { - _FixReasonTarget_YieldedType(super.inner); - - @override - int get hashCode => Object.hash(4, inner); - - @override - bool operator ==(Object other) => - other is _FixReasonTarget_YieldedType && inner == other.inner; - - @override - String _describe(String preposition) => - ' $preposition yielded type${inner._describe('from')}'; -} diff --git a/pkg/nnbd_migration/lib/instrumentation.dart b/pkg/nnbd_migration/lib/instrumentation.dart deleted file mode 100644 index a8a8f27bc78e..000000000000 --- a/pkg/nnbd_migration/lib/instrumentation.dart +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; - -/// Data structure used by the nullability migration engine to refer to a -/// specific location in source code. -class CodeReference { - final String path; - - final int line; - - final int column; - - final int offset; - - /// Name of the enclosing function, or `null` if not known. - String? function; - - CodeReference(this.path, this.offset, this.line, this.column, this.function); - - /// Creates a [CodeReference] pointing to the given [node]. - factory CodeReference.fromAstNode(AstNode node) { - var compilationUnit = node.thisOrAncestorOfType()!; - var source = compilationUnit.declaredElement!.source; - var location = compilationUnit.lineInfo.getLocation(node.offset); - return CodeReference(source.fullName, node.offset, location.lineNumber, - location.columnNumber, _computeEnclosingName(node)); - } - - factory CodeReference.fromElement(Element element) { - var unitElement = element.thisOrAncestorOfType(); - if (unitElement == null) { - var enclosingElement = element.enclosingElement; - if (enclosingElement is LibraryElement) { - unitElement = enclosingElement.definingCompilationUnit; - } else { - throw StateError('Unexpected element: $element'); - } - } - - var path = unitElement.source.fullName; - var offset = element.nameOffset; - - var location = unitElement.lineInfo.getLocation(offset); - return CodeReference(path, offset, location.lineNumber, - location.columnNumber, _computeElementFullName(element)); - } - - /// Gets a short description of this code reference (using the last component - /// of the path rather than the full path) - String get shortName => '$shortPath:$line:$column'; - - /// Gets the last component of the path part of this code reference. - String get shortPath { - var pathAsUri = Uri.file(path); - return pathAsUri.pathSegments.last; - } - - @override - String toString() { - var pathAsUri = Uri.file(path); - return '${function ?? 'unknown'} ($pathAsUri:$line:$column)'; - } - - static String? _computeElementFullName(Element? element) { - List parts = []; - while (element != null) { - var elementName = _computeElementName(element); - if (elementName != null) { - parts.add(elementName); - } - element = element.enclosingElement; - } - if (parts.isEmpty) return null; - return parts.reversed.join('.'); - } - - static String? _computeElementName(Element element) { - if (element is CompilationUnitElement || element is LibraryElement) { - return null; - } - return element.name; - } - - static String? _computeEnclosingName(AstNode? node) { - List parts = []; - while (node != null) { - var nodeName = _computeNodeDeclarationName(node); - if (nodeName != null) { - parts.add(nodeName); - } else if (parts.isEmpty && node is VariableDeclarationList) { - parts.add(node.variables.first.declaredElement!.name); - } - node = node.parent; - } - if (parts.isEmpty) return null; - return parts.reversed.join('.'); - } - - static String? _computeNodeDeclarationName(AstNode node) { - if (node is ExtensionDeclaration) { - return node.declaredElement?.name ?? ''; - } else if (node is Declaration) { - var name = node.declaredElement?.name; - return name == '' ? '' : name; - } else { - return null; - } - } -} - -/// Information exposed to the migration client about the set of nullability -/// nodes decorating a type in the program being migrated. -abstract class DecoratedTypeInfo { - /// Information about the graph node associated with the decision of whether - /// or not to make this type into a nullable type. - NullabilityNodeInfo? get node; - - /// If [type] is a function type, information about the set of nullability - /// nodes decorating the type's return type. - DecoratedTypeInfo? get returnType; - - /// The original (pre-migration) type that is being migrated. - DartType? get type; - - /// If [type] is a function type, looks up information about the set of - /// nullability nodes decorating one of the type's named parameter types. - DecoratedTypeInfo? namedParameter(String name); - - /// If [type] is a function type, looks up information about the set of - /// nullability nodes decorating one of the type's positional parameter types. - /// (This could be an optional or a required positional parameter). - DecoratedTypeInfo? positionalParameter(int i); - - /// If [type] is an interface type, looks up information about the set of - /// nullability nodes decorating one of the type's type arguments. - DecoratedTypeInfo? typeArgument(int i); -} - -/// Information about a propagation step that occurred during downstream -/// propagation. -abstract class DownstreamPropagationStepInfo implements PropagationStepInfo { - DownstreamPropagationStepInfo? get principalCause; - - /// The node whose nullability was changed. - /// - /// Any propagation step that took effect should have a non-null value here. - /// Propagation steps that are pending but have not taken effect yet, or that - /// never had an effect (e.g. because an edge was not triggered) will have a - /// `null` value for this field. - NullabilityNodeInfo? get targetNode; -} - -/// Information exposed to the migration client about an edge in the nullability -/// graph. -/// -/// A graph edge represents a dependency relationship between two types being -/// migrated, suggesting that if one type (the source) is made nullable, it may -/// be desirable to make the other type (the destination) nullable as well. -abstract class EdgeInfo implements FixReasonInfo { - /// User-friendly description of the edge, or `null` if not known. - String get description; - - /// Information about the graph node that this edge "points to". - NullabilityNodeInfo get destinationNode; - - /// The set of "guard nodes" for this edge. Guard nodes are graph nodes whose - /// nullability determines whether it is important to satisfy a graph edge. - /// If at least one of an edge's guards is non-nullable, then it is not - /// important to satisfy the graph edge. (Typically this is because the code - /// that led to the graph edge being created is only reachable if the guards - /// are all nullable). - Iterable get guards; - - /// A boolean indicating whether the graph edge is a "hard" edge. Hard edges - /// are associated with unconditional control flow, and thus allow information - /// about non-nullability to be propagated "upstream" through the nullability - /// graph. - bool get isHard; - - /// A boolean indicating whether the graph edge is "satisfied". At its heart, - /// the nullability propagation algorithm is an effort to satisfy graph edges - /// in a way that corresponds to the user's intent. A graph edge is - /// considered satisfied if any of the following is true: - /// - Its [sourceNode] is non-nullable. - /// - One of its [guards] is non-nullable. - /// - Its [destinationNode] is nullable. - bool get isSatisfied; - - /// Indicates whether all the upstream nodes of this edge are nullable (and - /// thus downstream nullability propagation should try to make the destination - /// node nullable, if possible). - bool get isTriggered; - - /// A boolean indicating whether the graph edge is a "union" edge. Union - /// edges are edges for which the nullability propagation algorithm tries to - /// ensure that both the [sourceNode] and the [destinationNode] have the - /// same nullability. Typically these are associated with situations where - /// Dart language semantics require two types to be the same type (e.g. a type - /// formal bound on a generic function type in a base class, and the - /// corresponding type formal bound on a generic function type in an - /// overriding class). - /// - /// The [isHard] property is always true for union edges. - bool get isUnion; - - /// Indicates whether the downstream node of this edge is non-nullable and the - /// edge is hard (and thus upstream nullability propagation should try to make - /// the source node non-nullable, if possible). - bool get isUpstreamTriggered; - - /// Information about the graph node that this edge "points away from". - NullabilityNodeInfo? get sourceNode; -} - -/// Information exposed to the migration client about the location in source -/// code that led an edge to be introduced into the nullability graph. -abstract class EdgeOriginInfo { - /// If the proximate cause of the edge being introduced into the graph - /// corresponds to the type of an element in an already migrated-library, the - /// corresponding element; otherwise `null`. - /// - /// Note that either [node] or [element] will always be non-null. - Element? get element; - - /// The kind of origin represented by this info. - EdgeOriginKind? get kind; - - /// If the proximate cause of the edge being introduced into the graph - /// corresponds to an AST node in a source file that is being migrated, the - /// corresponding AST node; otherwise `null`. - /// - /// Note that either [node] or [element] will always be non-null. - AstNode? get node; - - /// If [node] is non-null, the source file that it appears in. Otherwise - /// `null`. - Source? get source; -} - -/// An enumeration of the various kinds of edge origins created by the migration -/// engine. -enum EdgeOriginKind { - alreadyMigratedType, - alwaysNullableType, - angularAnnotation, - angularConstructorArgument, - argumentErrorCheckNotNull, - assignmentFromAngularInjectorGet, - builtValueNullableAnnotation, - callTearOff, - compoundAssignment, - // See [DummyOrigin]. - dummy, - dynamicAssignment, - enumValue, - expressionChecks, - externalDynamic, - fieldFormalParameter, - fieldNotInitialized, - forEachVariable, - getterSetterCorrespondence, - greatestLowerBound, - ifNull, - implicitMixinSuperCall, - implicitNullInitializer, - implicitNullReturn, - implicitThis, - inferredTypeParameterInstantiation, - instanceCreation, - instantiateToBounds, - isCheckComponentType, - isCheckMainType, - iteratorMethodReturn, - listLengthConstructor, - literal, - namedParameterNotSupplied, - nonNullableBoolType, - nonNullableObjectSuperclass, - nonNullableUsage, - nonNullAssertion, - nullabilityComment, - nullAwareAccess, - optionalFormalParameter, - parameterInheritance, - publicMethodArgument, - quiverCheckNotNull, - returnTypeInheritance, - stackTraceTypeOrigin, - thisOrSuper, - throw_, - typedefReference, - typeParameterInstantiation, - uninitializedRead, -} - -/// Interface used by the migration engine to expose information to its client -/// about a reason for a modification to the source file. -abstract class FixReasonInfo {} - -/// Abstract interface for assigning ids numbers to nodes, and performing -/// lookups afterwards. -abstract class NodeMapper extends NodeToIdMapper { - /// Gets the node corresponding to the given [id]. - NullabilityNodeInfo? nodeForId(int? id); -} - -/// Abstract interface for assigning ids numbers to nodes. -abstract class NodeToIdMapper { - /// Gets the id corresponding to the given [node]. - int idForNode(NullabilityNodeInfo node); -} - -/// Interface used by the migration engine to expose information to its client -/// about the decisions made during migration, and how those decisions relate to -/// the input source code. -abstract class NullabilityMigrationInstrumentation { - /// Called whenever changes are decided upon for a given [source] file. - /// - /// The format of the changes is a map from source file offset to a list of - /// changes to be applied at that offset. - void changes(Source source, Map> changes); - - /// Called whenever an explicit [typeAnnotation] is found in the source code, - /// to report the nullability [node] that was associated with this type. If - /// the migration engine determines that the [node] should be nullable, a `?` - /// will be inserted after the type annotation. - void explicitTypeNullability( - Source? source, TypeAnnotation typeAnnotation, NullabilityNodeInfo? node); - - /// Called whenever reference is made to an [element] outside of the code - /// being migrated, to report the nullability nodes associated with the type - /// of the element. - void externalDecoratedType(Element element, DecoratedTypeInfo decoratedType); - - /// Called whenever reference is made to an [typeParameter] outside of the - /// code being migrated, to report the nullability nodes associated with the - /// bound of the type parameter. - void externalDecoratedTypeParameterBound( - TypeParameterElement typeParameter, DecoratedTypeInfo decoratedType); - - /// Called when the migration process is finished. - void finished(); - - /// Called whenever the migration engine creates a graph edge between - /// nullability nodes, to report information about the edge that was created, - /// and why it was created. - void graphEdge(EdgeInfo edge, EdgeOriginInfo originInfo); - - /// Called when the migration engine starts up, to report information about - /// the immutable migration nodes [never] and [always] that are used as the - /// starting point for nullability propagation. - void immutableNodes(NullabilityNodeInfo never, NullabilityNodeInfo always); - - /// Called whenever the migration engine encounters an implicit return type - /// associated with an AST node, to report the nullability nodes associated - /// with the implicit return type of the AST node. - /// - /// [node] is the AST node having an implicit return type; it may be an - /// executable declaration, function-typed formal parameter declaration, - /// function type alias declaration, GenericFunctionType, or a function - /// expression. - void implicitReturnType( - Source? source, AstNode node, DecoratedTypeInfo? decoratedReturnType); - - /// Called whenever the migration engine encounters an implicit type - /// associated with an AST node, to report the nullability nodes associated - /// with the implicit type of the AST node. - /// - /// [node] is the AST node having an implicit type; it may be a formal - /// parameter, a declared identifier, or a variable in a variable declaration - /// list. - void implicitType( - Source? source, AstNode? node, DecoratedTypeInfo decoratedType); - - /// Called whenever the migration engine encounters an AST node with implicit - /// type arguments, to report the nullability nodes associated with the - /// implicit type arguments of the AST node. - /// - /// [node] is the AST node having implicit type arguments; it may be a - /// constructor redirection, function expression invocation, method - /// invocation, instance creation expression, list/map/set literal, or type - /// annotation. - void implicitTypeArguments( - Source? source, AstNode node, Iterable types); - - /// Clear any data from the propagation step in preparation for that step - /// being re-run. - void prepareForUpdate(); -} - -/// Information exposed to the migration client about a single node in the -/// nullability graph. -abstract class NullabilityNodeInfo implements FixReasonInfo { - /// List of compound nodes wrapping this node. - final List outerCompoundNodes = []; - - /// Source code location corresponding to this nullability node, or `null` if - /// not known. - CodeReference? get codeReference; - - /// Some nodes get nullability from downstream, so the downstream edges are - /// available to query as well. - Iterable get downstreamEdges; - - /// The hint actions users can perform on this node, indexed by the type of - /// hint. - /// - /// Each edit is represented as a [Map>] as is typical - /// of [AtomicEdit]s since they do not have an offset. See extensions - /// [AtomicEditMap] and [AtomicEditList] for usage. - Map>> get hintActions; - - /// After migration is complete, this getter can be used to query whether - /// the type associated with this node was determined to be "exact nullable." - bool get isExactNullable; - - /// Indicates whether the node is immutable. The only immutable nodes in the - /// nullability graph are the nodes `never` and `always` that are used as the - /// starting points for nullability propagation. - bool get isImmutable; - - /// After migration is complete, this getter can be used to query whether - /// the type associated with this node was determined to be nullable. - bool get isNullable; - - /// The edges that caused this node to have the nullability that it has. - Iterable get upstreamEdges; - - /// If [isNullable] is false, the propagation step that caused this node to - /// become non-nullable (if any). - UpstreamPropagationStepInfo? get whyNotNullable; - - /// If [isNullable] is true, the propagation step that caused this node to - /// become nullable. - DownstreamPropagationStepInfo? get whyNullable; -} - -abstract class PropagationStepInfo { - CodeReference? get codeReference; - - /// The nullability edge associated with this propagation step, if any. - /// Otherwise `null`. - EdgeInfo? get edge; -} - -/// Reason information for a simple fix that isn't associated with any edges or -/// nodes. -abstract class SimpleFixReasonInfo implements FixReasonInfo { - /// Code location of the fix. - CodeReference get codeReference; - - /// Description of the fix. - String get description; -} - -/// A simple implementation of [NodeMapper] that assigns ids to nodes as they -/// are requested, backed by a map. -/// -/// Be careful not to leak references to nodes by holding on to this beyond the -/// lifetime of the nodes it maps. -class SimpleNodeMapper extends NodeMapper { - final _nodeToId = {}; - final _idToNode = {}; - - @override - int idForNode(NullabilityNodeInfo node) { - final id = _nodeToId.putIfAbsent(node, () => _nodeToId.length); - _idToNode.putIfAbsent(id, () => node); - return id; - } - - @override - NullabilityNodeInfo? nodeForId(int? id) => _idToNode[id!]; -} - -/// Information exposed to the migration client about a node in the nullability -/// graph resulting from a type substitution. -abstract class SubstitutionNodeInfo extends NullabilityNodeInfo { - /// Nullability node representing the inner type of the substitution. - /// - /// For example, if this NullabilityNode arose from substituting `int*` for - /// `T` in the type `T*`, [innerNode] is the nullability corresponding to the - /// `*` in `int*`. - NullabilityNodeInfo get innerNode; - - /// Nullability node representing the outer type of the substitution. - /// - /// For example, if this NullabilityNode arose from substituting `int*` for - /// `T` in the type `T*`, [innerNode] is the nullability corresponding to the - /// `*` in `T*`. - NullabilityNodeInfo get outerNode; -} - -/// Information about a propagation step that occurred during upstream -/// propagation. -abstract class UpstreamPropagationStepInfo implements PropagationStepInfo { - bool get isStartingPoint; - - /// The node whose nullability was changed. - /// - /// Any propagation step that took effect should have a non-null value here. - /// Propagation steps that are pending but have not taken effect yet, or that - /// never had an effect (e.g. because an edge was not triggered) will have a - /// `null` value for this field. - NullabilityNodeInfo get node; - - UpstreamPropagationStepInfo? get principalCause; -} diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart deleted file mode 100644 index 650c27f719ce..000000000000 --- a/pkg/nnbd_migration/lib/migration_cli.dart +++ /dev/null @@ -1,1149 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:async'; -import 'dart:io' hide File; - -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/diagnostic/diagnostic.dart'; -import 'package:analyzer/error/error.dart'; -import 'package:analyzer/file_system/file_system.dart' - show File, ResourceProvider; -import 'package:analyzer/file_system/physical_file_system.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart'; -import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; -import 'package:analyzer/src/error/codes.dart'; -import 'package:analyzer/src/util/sdk.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart' - hide AnalysisError; -import 'package:args/args.dart'; -import 'package:cli_util/cli_logging.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/exceptions.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart'; -import 'package:nnbd_migration/src/front_end/migration_state.dart'; -import 'package:nnbd_migration/src/front_end/non_nullable_fix.dart'; -import 'package:nnbd_migration/src/messages.dart'; -import 'package:nnbd_migration/src/utilities/progress_bar.dart'; -import 'package:nnbd_migration/src/utilities/source_edit_diff_formatter.dart'; -import 'package:path/path.dart' show Context; - -String _pluralize(int count, String single, {String? multiple}) { - return count == 1 ? single : (multiple ?? '${single}s'); -} - -String _removePeriod(String value) { - return value.endsWith('.') ? value.substring(0, value.length - 1) : value; -} - -/// The result of a round of static analysis; primarily a list of -/// [AnalysisError]s. -class AnalysisResult { - final List errors; - final Map lineInfo; - final Context pathContext; - final String rootDirectory; - final bool allSourcesAlreadyMigrated; - - AnalysisResult(this.errors, this.lineInfo, this.pathContext, - this.rootDirectory, this.allSourcesAlreadyMigrated) { - errors.sort((AnalysisError one, AnalysisError two) { - if (one.source != two.source) { - return one.source.fullName.compareTo(two.source.fullName); - } - return one.offset - two.offset; - }); - } - - bool get hasErrors => errors.isNotEmpty; - - /// Whether the errors include any which may be the result of not yet having - /// run "pub get". - bool get hasImportErrors => errors.any( - (error) => error.errorCode == CompileTimeErrorCode.URI_DOES_NOT_EXIST); - - /// Converts the list of errors into JSON, for displaying in the web preview. - List> toJson() { - var result = >[]; - // severity • Message ... at foo/bar.dart:6:1 • (error_code) - for (var error in errors) { - var lineInfoForThisFile = lineInfo[error.source.fullName]!; - var location = lineInfoForThisFile.getLocation(error.offset); - var path = - pathContext.relative(error.source.fullName, from: rootDirectory); - result.add({ - 'severity': error.severity.name, - 'message': _removePeriod(error.message), - 'location': '$path:${location.lineNumber}:${location.columnNumber}', - 'code': error.errorCode.name.toLowerCase(), - }); - } - return result; - } -} - -/// Data structure recording command-line options for the migration tool that -/// have been passed in by the client. -class CommandLineOptions { - static const applyChangesFlag = 'apply-changes'; - static const helpFlag = 'help'; - static const ignoreErrorsFlag = 'ignore-errors'; - static const ignoreExceptionsFlag = 'ignore-exceptions'; - static const previewHostnameOption = 'preview-hostname'; - static const previewPortOption = 'preview-port'; - static const sdkPathOption = 'sdk-path'; - static const skipImportCheckFlag = 'skip-import-check'; - static const summaryOption = 'summary'; - static const verboseFlag = 'verbose'; - static const webPreviewFlag = 'web-preview'; - - final bool applyChanges; - - final String directory; - - final bool? ignoreErrors; - - final bool? ignoreExceptions; - - final String? previewHostname; - - final int? previewPort; - - final String sdkPath; - - final bool? skipImportCheck; - - final String? summary; - - final bool? webPreview; - - CommandLineOptions( - {required this.applyChanges, - required this.directory, - required this.ignoreErrors, - required this.ignoreExceptions, - required this.previewHostname, - required this.previewPort, - required this.sdkPath, - required this.skipImportCheck, - required this.summary, - required this.webPreview}); -} - -/// Command-line API for the migration tool, with additional parameters exposed -/// for testing. -/// -/// Recommended usage: create an instance of this object and call -/// [decodeCommandLineArgs]. If it returns non-null, call -/// [MigrationCliRunner.run] on the result. If either method throws a -/// [MigrationExit], exit with the error code contained therein. -class MigrationCli { - /// A list of all the command-line options supported by the tool. - /// - /// This may be used by clients that wish to run migration but provide their - /// own command-line interface. - static final List options = [ - MigrationCliOption( - CommandLineOptions.verboseFlag, - (parser, hide) => parser.addFlag( - CommandLineOptions.verboseFlag, - abbr: 'v', - defaultsTo: false, - help: 'Show additional command output.', - negatable: false, - )), - MigrationCliOption( - CommandLineOptions.applyChangesFlag, - (parser, hide) => parser.addFlag(CommandLineOptions.applyChangesFlag, - defaultsTo: false, - negatable: false, - help: - 'Apply the proposed null safety changes to the files on disk.')), - MigrationCliOption( - CommandLineOptions.ignoreErrorsFlag, - (parser, hide) => parser.addFlag( - CommandLineOptions.ignoreErrorsFlag, - defaultsTo: false, - negatable: false, - help: - 'Attempt to perform null safety analysis even if the project has ' - 'analysis errors.', - )), - MigrationCliOption( - CommandLineOptions.skipImportCheckFlag, - (parser, hide) => parser.addFlag( - CommandLineOptions.skipImportCheckFlag, - defaultsTo: false, - negatable: false, - help: 'Go ahead with migration even if some imported files have ' - 'not yet been migrated.', - )), - MigrationCliOption.separator('Web interface options:'), - MigrationCliOption( - CommandLineOptions.webPreviewFlag, - (parser, hide) => parser.addFlag( - CommandLineOptions.webPreviewFlag, - defaultsTo: true, - negatable: true, - help: - 'Show an interactive preview of the proposed null safety changes ' - 'in a browser window. Use --no-web-preview to print proposed changes ' - 'to the console.', - )), - MigrationCliOption( - CommandLineOptions.previewHostnameOption, - (parser, hide) => parser.addOption( - CommandLineOptions.previewHostnameOption, - defaultsTo: 'localhost', - valueHelp: 'host', - help: 'Run the preview server on the specified hostname. If not ' - 'specified, "localhost" is used. Use "any" to specify IPv6.any or ' - 'IPv4.any.', - )), - MigrationCliOption( - CommandLineOptions.previewPortOption, - (parser, hide) => parser.addOption( - CommandLineOptions.previewPortOption, - valueHelp: 'port', - help: - 'Run the preview server on the specified port. If not specified, ' - 'dynamically allocate a port.', - )), - MigrationCliOption.separator('Additional options:'), - MigrationCliOption( - CommandLineOptions.summaryOption, - (parser, hide) => parser.addOption( - CommandLineOptions.summaryOption, - help: 'Output a machine-readable summary of migration changes.', - valueHelp: 'path', - )), - // hidden options - MigrationCliOption( - CommandLineOptions.ignoreExceptionsFlag, - (parser, hide) => parser.addFlag( - CommandLineOptions.ignoreExceptionsFlag, - defaultsTo: false, - negatable: false, - help: - 'Attempt to perform null safety analysis even if exceptions occur.', - hide: hide, - )), - MigrationCliOption( - CommandLineOptions.sdkPathOption, - (parser, hide) => parser.addOption( - CommandLineOptions.sdkPathOption, - valueHelp: 'sdk-path', - help: 'The path to the Dart SDK.', - hide: hide, - )), - ]; - - static const String migrationGuideLink = - 'See https://dart.dev/go/null-safety-migration for a migration guide.'; - - /// The name of the executable, for reporting in help messages. - final String binaryName; - - /// The SDK path that should be used if none is provided by the user. Used in - /// testing to install a mock SDK. - final String? defaultSdkPathOverride; - - /// Factory to create an appropriate Logger instance to give feedback to the - /// user. Used in testing to allow user feedback messages to be tested. - final Logger Function(bool isVerbose) loggerFactory; - - /// Resource provider that should be used to access the filesystem. Used in - /// testing to redirect to an in-memory filesystem. - final ResourceProvider resourceProvider; - - /// Logger instance we use to give feedback to the user. - final Logger logger; - - /// The environment variables, tracked to help users debug if SDK_PATH was - /// specified and that resulted in any [ExperimentStatusException]s. - final Map _environmentVariables; - - MigrationCli({ - required this.binaryName, - @visibleForTesting this.loggerFactory = _defaultLoggerFactory, - @visibleForTesting this.defaultSdkPathOverride, - @visibleForTesting ResourceProvider? resourceProvider, - @visibleForTesting Map? environmentVariables, - }) : logger = loggerFactory(false), - resourceProvider = - resourceProvider ?? PhysicalResourceProvider.INSTANCE, - _environmentVariables = environmentVariables ?? Platform.environment; - - Context get pathContext => resourceProvider.pathContext; - - /// Parses and validates command-line arguments, and creates a - /// [MigrationCliRunner] that is prepared to perform migration. - /// - /// If the user asked for help, it is printed using the logger configured in - /// the constructor, and `null` is returned. - /// - /// If the user supplied a bad option, a message is printed using the logger - /// configured in the constructor, and [MigrationExit] is thrown. - MigrationCliRunner? decodeCommandLineArgs(ArgResults argResults, - {bool? isVerbose}) { - try { - isVerbose ??= argResults[CommandLineOptions.verboseFlag] as bool; - if (argResults[CommandLineOptions.helpFlag] as bool) { - _showUsage(isVerbose); - return null; - } - var rest = argResults.rest; - String migratePath; - if (rest.isEmpty) { - migratePath = pathContext.current; - } else if (rest.length > 1) { - throw _BadArgException('No more than one path may be specified.'); - } else { - migratePath = pathContext - .normalize(pathContext.join(pathContext.current, rest[0])); - } - var migrateResource = resourceProvider.getResource(migratePath); - if (migrateResource is File) { - if (migrateResource.exists) { - throw _BadArgException('$migratePath is a file.'); - } else { - throw _BadArgException('$migratePath does not exist.'); - } - } - var applyChanges = - argResults[CommandLineOptions.applyChangesFlag] as bool; - var previewPortRaw = - argResults[CommandLineOptions.previewPortOption] as String?; - int? previewPort; - try { - previewPort = previewPortRaw == null ? null : int.parse(previewPortRaw); - } on FormatException catch (_) { - throw _BadArgException( - 'Invalid value for --${CommandLineOptions.previewPortOption}'); - } - bool webPreview; - if (argResults.wasParsed(CommandLineOptions.webPreviewFlag)) { - webPreview = argResults[CommandLineOptions.webPreviewFlag] as bool; - } else { - // If the `webPreviewFlag` wasn't explicitly passed, then the value of - // this option is based on the value of the [applyChanges] option. - webPreview = !applyChanges; - } - if (applyChanges && webPreview) { - throw _BadArgException('--apply-changes requires --no-web-preview'); - } - var options = CommandLineOptions( - applyChanges: applyChanges, - directory: migratePath, - ignoreErrors: - argResults[CommandLineOptions.ignoreErrorsFlag] as bool?, - ignoreExceptions: - argResults[CommandLineOptions.ignoreExceptionsFlag] as bool?, - previewHostname: - argResults[CommandLineOptions.previewHostnameOption] as String?, - previewPort: previewPort, - sdkPath: argResults[CommandLineOptions.sdkPathOption] as String? ?? - defaultSdkPathOverride ?? - getSdkPath(), - skipImportCheck: - argResults[CommandLineOptions.skipImportCheckFlag] as bool?, - summary: argResults[CommandLineOptions.summaryOption] as String?, - webPreview: webPreview); - return MigrationCliRunner(this, options, - logger: isVerbose ? loggerFactory(true) : null); - } on Object catch (exception) { - handleArgParsingException(exception); - } - } - - Never handleArgParsingException(Object exception) { - String message; - if (exception is FormatException) { - message = exception.message; - } else if (exception is _BadArgException) { - message = exception.message; - } else { - message = - 'Exception occurred while parsing command-line options: $exception'; - } - logger.stderr(message); - _showUsage(false); - throw MigrationExit(1); - } - - void _showUsage(bool isVerbose) { - logger.stderr('Usage: $binaryName [options...] []'); - - logger.stderr(''); - logger.stderr(createParser(hide: !isVerbose).usage); - if (!isVerbose) { - logger.stderr(''); - logger - .stderr('Run "$binaryName -h -v" for verbose help output, including ' - 'less commonly used options.'); - } - } - - static ArgParser createParser({bool hide = true}) { - var parser = ArgParser(); - parser.addFlag(CommandLineOptions.helpFlag, - abbr: 'h', - help: - 'Display this help message. Add --verbose to show hidden options.', - defaultsTo: false, - negatable: false); - defineOptions(parser, hide); - return parser; - } - - static void defineOptions(ArgParser parser, bool hide) { - for (var option in options) { - option.addToParser(parser, hide); - } - } - - static Logger _defaultLoggerFactory(bool isVerbose) { - var ansi = Ansi(Ansi.terminalSupportsAnsi); - if (isVerbose) { - return Logger.verbose(ansi: ansi); - } else { - return Logger.standard(ansi: ansi); - } - } -} - -/// Data structure representing a single command-line option to the migration -/// tool, or a separator in the list of command-line options. -class MigrationCliOption { - /// The name of the option, without the leading `--`. - final String name; - - /// Callback function that can be used to add the option or separator to the - /// given [parser]. If [hide] is `true`, and the option is rarely used, it - /// is added as a hidden option. - final void Function(ArgParser parser, bool hide) addToParser; - - /// If `true`, this is a separator between command line options; if `false`, - /// it's an option. - final bool isSeparator; - - MigrationCliOption(this.name, this.addToParser) : isSeparator = false; - - MigrationCliOption.separator(this.name) - : addToParser = ((parser, hide) => parser.addSeparator(name)), - isSeparator = true; -} - -/// Internals of the command-line API for the migration tool, with additional -/// methods exposed for testing. -/// -/// This class may be used directly by clients that with to run migration but -/// provide their own command-line interface. -class MigrationCliRunner implements DartFixListenerClient { - final MigrationCli cli; - - /// Logger instance we use to give feedback to the user. - final Logger logger; - - /// The result of parsing command-line options. - final CommandLineOptions options; - - final Map lineInfo = {}; - - DartFixListener? _dartFixListener; - - _FixCodeProcessor? _fixCodeProcessor; - - AnalysisContextCollectionImpl? _contextCollection; - - bool _hasExceptions = false; - - bool _hasAnalysisErrors = false; - - /// Subscription of interrupt signals (control-C). - StreamSubscription? _sigIntSubscription; - - /// Completes when an interrupt signal (control-C) is received. - late Completer sigIntSignalled; - - MigrationCliRunner(this.cli, this.options, {Logger? logger}) - : logger = logger ?? cli.logger; - - @visibleForTesting - DriverBasedAnalysisContext get analysisContext { - // Handle the case of more than one analysis context being found (typically, - // the current directory and one or more sub-directories). - if (hasMultipleAnalysisContext) { - return contextCollection!.contextFor(options.directory); - } else { - return contextCollection!.contexts.single; - } - } - - Ansi get ansi => logger.ansi; - - AnalysisContextCollectionImpl? get contextCollection { - _contextCollection ??= AnalysisContextCollectionImpl( - includedPaths: [options.directory], - resourceProvider: resourceProvider, - sdkPath: pathContext.normalize(options.sdkPath)); - return _contextCollection; - } - - @visibleForTesting - bool get hasMultipleAnalysisContext { - return contextCollection!.contexts.length > 1; - } - - @visibleForTesting - bool get isPreviewServerRunning => - _fixCodeProcessor?.isPreviewServerRunning ?? false; - - Context get pathContext => resourceProvider.pathContext; - - ResourceProvider get resourceProvider => cli.resourceProvider; - - /// Called after changes have been applied on disk. Maybe overridden by a - /// derived class. - void applyHook() {} - - /// Computes the internet address that should be passed to `HttpServer.bind` - /// when starting the preview server. May be overridden in derived classes. - Object? computeBindAddress() { - var hostname = options.previewHostname; - if (hostname == 'localhost') { - return InternetAddress.loopbackIPv4; - } else if (hostname == 'any') { - return InternetAddress.anyIPv6; - } else { - return hostname; - } - } - - /// Computes the set of file paths that should be analyzed by the migration - /// engine. May be overridden by a derived class. - /// - /// All files to be migrated must be included in the returned set. It is - /// permissible for the set to contain additional files that could help the - /// migration tool build up a more complete nullability graph (for example - /// generated files, or usages of the code-to-be-migrated by one of its - /// clients). - /// - /// By default returns the set of all `.dart` files contained in the context. - Set computePathsToProcess(DriverBasedAnalysisContext context) => - context.contextRoot - .analyzedFiles() - .where((s) => - s.endsWith('.dart') && - // Any file may have been deleted since its initial analysis. - resourceProvider.getFile(s).exists) - .toSet(); - - NonNullableFix createNonNullableFix(DartFixListener listener, - ResourceProvider resourceProvider, Object? bindAddress, - {List included = const [], - int? preferredPort, - String? summaryPath, - required String sdkPath}) { - return NonNullableFix(listener, resourceProvider, bindAddress, logger, - (String? path) => shouldBeMigrated(path!), - included: included, - preferredPort: preferredPort, - summaryPath: summaryPath, - sdkPath: sdkPath); - } - - /// Subscribes to the interrupt signal (control-C). - @visibleForTesting - void listenForSignalInterrupt() { - var stream = ProcessSignal.sigint.watch(); - sigIntSignalled = Completer(); - _sigIntSubscription = stream.listen((_) { - if (!sigIntSignalled.isCompleted) { - sigIntSignalled.complete(); - } - }); - } - - @override - void onException(String detail) { - if (_hasExceptions) { - if (!options.ignoreExceptions!) { - // Our intention is to exit immediately when an exception occurred. We - // tried, but failed (probably due to permissive mode logic in the - // migration tool itself catching the MigrationExit exception). The - // stack has now been unwound further, so throw again. - throw MigrationExit(1); - } - // We're not exiting immediately when an exception occurs. We've already - // reported that an exception happened. So do nothing further. - return; - } - _hasExceptions = true; - if (options.ignoreExceptions!) { - logger.stdout(''' -Exception(s) occurred during migration. Attempting to perform -migration anyway due to the use of --${CommandLineOptions.ignoreExceptionsFlag}. - -To see exception details, re-run without --${CommandLineOptions.ignoreExceptionsFlag}. -'''); - } else { - if (_hasAnalysisErrors) { - logger.stderr(''' -Aborting migration due to an exception. This may be due to a bug in -the migration tool, or it may be due to errors in the source code -being migrated. If possible, try to fix errors in the source code and -re-try migrating. If that doesn't work, consider filing a bug report -at: -'''); - } else { - logger.stderr(''' -Aborting migration due to an exception. This most likely is due to a -bug in the migration tool. Please consider filing a bug report at: -'''); - } - logger.stderr('https://github.com/dart-lang/sdk/issues/new'); - var sdkVersion = Platform.version.split(' ')[0]; - logger.stderr(''' -Please include the SDK version ($sdkVersion) in your bug report. - -To attempt to perform migration anyway, you may re-run with ---${CommandLineOptions.ignoreExceptionsFlag}. - -Exception details: -'''); - logger.stderr(detail); - throw MigrationExit(1); - } - } - - @override - void onFatalError(String detail) { - logger.stderr(detail); - throw MigrationExit(1); - } - - @override - void onMessage(String detail) { - logger.stdout(detail); - } - - /// Runs the full migration process. - /// - /// If something goes wrong, a message is printed using the logger configured - /// in the constructor, and [MigrationExit] is thrown. - Future run() async { - logger.stdout('Migrating ${options.directory}'); - logger.stdout(''); - - logger.stdout(MigrationCli.migrationGuideLink); - logger.stdout(''); - - if (hasMultipleAnalysisContext) { - logger.stdout('Note: more than one project found; migrating the ' - 'top-level project.'); - logger.stdout(''); - } - - NonNullableFix nonNullableFix; - - logger.stdout(ansi.emphasized('Analyzing project...')); - _fixCodeProcessor = _FixCodeProcessor(analysisContext, this); - _dartFixListener = DartFixListener( - DriverProviderImpl(resourceProvider, analysisContext), this); - nonNullableFix = createNonNullableFix( - _dartFixListener!, resourceProvider, computeBindAddress(), - included: [options.directory], - preferredPort: options.previewPort, - summaryPath: options.summary, - sdkPath: options.sdkPath); - nonNullableFix.rerunFunction = _rerunFunction; - _fixCodeProcessor!.registerCodeTask(nonNullableFix); - - try { - var analysisResult = await _fixCodeProcessor!.runFirstPhase(); - - if (analysisResult.hasErrors) { - _logErrors(analysisResult); - if (!options.ignoreErrors!) { - throw MigrationExit(1); - } - } else if (analysisResult.allSourcesAlreadyMigrated) { - _logAlreadyMigrated(); - throw MigrationExit(0); - } else { - logger.stdout('No analysis issues found.'); - } - } on ExperimentStatusException catch (e) { - logger.stdout(e.toString()); - final sdkPathVar = cli._environmentVariables['SDK_PATH']; - if (sdkPathVar != null) { - logger.stdout('$sdkPathEnvironmentVariableSet: $sdkPathVar'); - } - throw MigrationExit(1); - } - - logger.stdout(''); - logger.stdout(ansi.emphasized('Generating migration suggestions...')); - var previewUrls = (await _fixCodeProcessor!.runLaterPhases()).previewUrls; - - if (options.applyChanges) { - logger.stdout(ansi.emphasized('Applying changes:')); - - var allEdits = _dartFixListener!.sourceChange.edits; - _applyMigrationSuggestions(allEdits); - - logger.stdout(''); - logger.stdout( - 'Applied ${allEdits.length} ${_pluralize(allEdits.length, 'edit')}.'); - - // Note: do not open the web preview if apply-changes is specified, as we - // currently cannot tell the web preview to disable the "apply migration" - // button. - return; - } - - if (options.webPreview!) { - assert(previewUrls!.length == 1, - 'Got unexpected extra preview URLs from server'); - - var url = previewUrls!.single; - // TODO(#41809): Open a browser automatically. - logger.stdout(''' -View the migration suggestions by visiting: - - ${ansi.emphasized(url)} - -Use this interactive web view to review, improve, or apply the results. -When finished with the preview, hit ctrl-c to terminate this process. - -If you make edits outside of the web view (in your IDE), use the 'Rerun from -sources' action. - -'''); - - listenForSignalInterrupt(); - await Future.any([ - sigIntSignalled.future, - nonNullableFix.serverIsShutdown.future, - ]); - // Either the interrupt signal was caught, or the server was shutdown. - // Either way, cancel the interrupt signal subscription, and shutdown the - // server. - _sigIntSubscription?.cancel(); - nonNullableFix.shutdownServer(); - } else { - logger.stdout(ansi.emphasized('Diff of changes:')); - - _displayChangeDiff(_dartFixListener!); - - logger.stdout(''); - logger.stdout('To apply these changes, re-run the tool with ' - '--${CommandLineOptions.applyChangesFlag}.'); - } - } - - /// Determines whether a migrated version of the file at [path] should be - /// output by the migration too. May be overridden by a derived class. - /// - /// This method should return `false` for files that are being considered by - /// the migration tool for information only (for example generated files, or - /// usages of the code-to-be-migrated by one of its clients). - /// - /// By default returns `true` if the file is contained within the context - /// root. This means that if a client overrides [computePathsToProcess] to - /// return additional paths that aren't inside the user's project, but doesn't - /// override this method, then those additional paths will be analyzed but not - /// migrated. - bool shouldBeMigrated(String path) { - return analysisContext.contextRoot.isAnalyzed(path); - } - - /// Perform the indicated source edits to the given source, returning the - /// resulting transformed text. - String _applyEdits(SourceFileEdit sourceFileEdit, String source) { - List edits = _sortEdits(sourceFileEdit); - return SourceEdit.applySequence(source, edits); - } - - void _applyMigrationSuggestions(List edits) { - // Apply the changes to disk. - for (SourceFileEdit sourceFileEdit in edits) { - String relPath = - pathContext.relative(sourceFileEdit.file, from: options.directory); - int count = sourceFileEdit.edits.length; - logger.stdout(' $relPath ($count ${_pluralize(count, 'change')})'); - - String? source; - var file = resourceProvider.getFile(sourceFileEdit.file); - try { - source = file.readAsStringSync(); - } catch (_) {} - - if (source == null) { - logger.stdout(' Unable to retrieve source for file.'); - } else { - source = _applyEdits(sourceFileEdit, source); - - try { - file.writeAsStringSync(source); - } catch (e) { - logger.stdout(' Unable to write source for file: $e'); - } - } - } - applyHook(); - } - - void _displayChangeDiff(DartFixListener migrationResults) { - Map> fileSuggestions = {}; - for (DartFixSuggestion suggestion in migrationResults.suggestions) { - String file = suggestion.location.file; - fileSuggestions.putIfAbsent(file, () => []); - fileSuggestions[file]!.add(suggestion); - } - - // present a diff-like view - var diffStyle = DiffStyle(logger.ansi); - for (SourceFileEdit sourceFileEdit in migrationResults.sourceChange.edits) { - String file = sourceFileEdit.file; - String relPath = pathContext.relative(file, from: options.directory); - var edits = sourceFileEdit.edits; - int count = edits.length; - - logger.stdout(''); - logger.stdout('${ansi.emphasized(relPath)} ' - '($count ${_pluralize(count, 'change')}):'); - - String? source; - try { - source = resourceProvider.getFile(file).readAsStringSync(); - } catch (_) {} - - if (source == null) { - logger.stdout(' (unable to retrieve source for file)'); - } else { - for (var line - in diffStyle.formatDiff(source, _sourceEditsToAtomicEdits(edits))) { - logger.stdout(' $line'); - } - } - } - } - - void _logAlreadyMigrated() { - logger.stdout(migratedAlready); - } - - void _logErrors(AnalysisResult analysisResult) { - logger.stdout(''); - - var issueCount = analysisResult.errors.length; - logger.stdout( - '$issueCount analysis ${_pluralize(issueCount, 'issue')} found:'); - - _IssueRenderer renderer = - _IssueRenderer(logger, options.directory, pathContext, lineInfo); - for (AnalysisError error in analysisResult.errors) { - renderer.render(error); - } - logger.stdout(''); - _hasAnalysisErrors = true; - - if (options.ignoreErrors!) { - logger.stdout('Note: analysis errors will result in erroneous migration ' - 'suggestions.'); - logger.stdout('Continuing with migration suggestions due to the use of ' - '--${CommandLineOptions.ignoreErrorsFlag}.'); - } else { - // Fail with how to continue. - logger.stdout("The migration tool didn't start, due to analysis errors."); - logger.stdout(''); - if (analysisResult.hasImportErrors) { - logger.stdout(''' -The following steps might fix your problem: -1. Run `dart pub get`. -2. Try running `dart migrate` again. -'''); - } else if (analysisResult.allSourcesAlreadyMigrated) { - logger.stdout(''' -The following steps might fix your problem: -1. Set the lower SDK constraint (in pubspec.yaml) to a version before 2.12. -2. Run `dart pub get`. -3. Try running `dart migrate` again. -'''); - } else { - const ignoreErrors = CommandLineOptions.ignoreErrorsFlag; - logger.stdout(''' -We recommend fixing the analysis issues before running `dart migrate`. -Alternatively, you can run `dart migrate --$ignoreErrors`, but you might -get erroneous migration suggestions. -'''); - } - logger.stdout( - 'More information: https://dart.dev/go/null-safety-migration'); - } - } - - Future _rerunFunction() async { - logger.stdout(ansi.emphasized('Re-analyzing project...')); - - _dartFixListener!.reset(); - await _fixCodeProcessor!.prepareToRerun(); - var analysisResult = await _fixCodeProcessor!.runFirstPhase(); - if (analysisResult.hasErrors && !options.ignoreErrors!) { - _logErrors(analysisResult); - return MigrationState( - _fixCodeProcessor!._task.migration, - _fixCodeProcessor!._task.includedRoot, - _dartFixListener, - _fixCodeProcessor!._task.instrumentationListener, - {}, - _fixCodeProcessor!._task.shouldBeMigratedFunction, - analysisResult); - } else if (analysisResult.allSourcesAlreadyMigrated) { - _logAlreadyMigrated(); - return MigrationState( - _fixCodeProcessor!._task.migration, - _fixCodeProcessor!._task.includedRoot, - _dartFixListener, - _fixCodeProcessor!._task.instrumentationListener, - {}, - _fixCodeProcessor!._task.shouldBeMigratedFunction, - analysisResult); - } else { - logger.stdout(ansi.emphasized('Re-generating migration suggestions...')); - return await _fixCodeProcessor!.runLaterPhases(); - } - } - - List _sortEdits(SourceFileEdit sourceFileEdit) { - // Sort edits in reverse offset order. - List edits = sourceFileEdit.edits.toList(); - edits.sort((a, b) { - return b.offset - a.offset; - }); - return edits; - } - - static Map> _sourceEditsToAtomicEdits( - List edits) { - return { - for (var edit in edits) - edit.offset: [AtomicEdit.replace(edit.length, edit.replacement)] - }; - } -} - -/// Exception thrown by [MigrationCli] if the client should exit. -class MigrationExit { - /// The exit code that the client should set. - final int exitCode; - - MigrationExit(this.exitCode); -} - -/// An abstraction over the static methods on [Process]. -/// -/// Used in tests to run mock processes. -abstract class ProcessManager { - const factory ProcessManager.system() = SystemProcessManager; - - /// Run a process synchronously, as in [Process.runSync]. - ProcessResult runSync(String executable, List arguments, - {String? workingDirectory}); -} - -/// A [ProcessManager] that directs all method calls to static methods of -/// [Process], in order to run real processes. -class SystemProcessManager implements ProcessManager { - const SystemProcessManager(); - - ProcessResult runSync(String executable, List arguments, - {String? workingDirectory}) => - Process.runSync(executable, arguments, - workingDirectory: workingDirectory ?? Directory.current.path); -} - -class _BadArgException implements Exception { - final String message; - - _BadArgException(this.message); -} - -class _FixCodeProcessor extends Object { - static const numPhases = 3; - - final DriverBasedAnalysisContext context; - - /// The task used to migrate to NNBD. - late final NonNullableFix _task; - - Set pathsToProcess; - - late ProgressBar _progressBar; - - final MigrationCliRunner _migrationCli; - - _FixCodeProcessor(this.context, this._migrationCli) - : pathsToProcess = _migrationCli.computePathsToProcess(context); - - bool get isPreviewServerRunning => _task.isPreviewServerRunning; - - Future prepareToRerun() async { - var driver = context.driver; - pathsToProcess = _migrationCli.computePathsToProcess(context); - pathsToProcess.forEach(driver.changeFile); - await driver.applyPendingFileChanges(); - } - - /// Call the supplied [process] function to process each compilation unit. - Future processResources( - Future Function(ResolvedUnitResult result) process) async { - var driver = context.currentSession; - var pathsProcessed = {}; - for (var path in pathsToProcess) { - if (pathsProcessed.contains(path)) continue; - var result = await driver.getResolvedLibrary(path); - // Parts will either be found in a library, below, or if the library - // isn't [isIncluded], will be picked up in the final loop. - if (result is ResolvedLibraryResult) { - for (var unit in result.units) { - if (!pathsProcessed.contains(unit.path)) { - await process(unit); - pathsProcessed.add(unit.path); - } - } - } - } - - for (var path in pathsToProcess.difference(pathsProcessed)) { - var result = await driver.getResolvedUnit(path); - if (result is ResolvedUnitResult) { - await process(result); - } - } - } - - void registerCodeTask(NonNullableFix task) { - _task = task; - } - - Future runFirstPhase() async { - var analysisErrors = []; - - // All tasks should be registered; [numPhases] should be finalized. - _progressBar = ProgressBar(_migrationCli.logger, pathsToProcess.length); - - // Process each source file. - bool allSourcesAlreadyMigrated = true; - await processResources((ResolvedUnitResult result) async { - if (!result.unit.featureSet.isEnabled(Feature.non_nullable)) { - allSourcesAlreadyMigrated = false; - } - _progressBar.tick(); - List errors = result.errors - .where((error) => error.severity == Severity.error) - .toList(); - if (errors.isNotEmpty) { - analysisErrors.addAll(errors); - _migrationCli.lineInfo[result.path] = result.lineInfo; - } - if (_migrationCli.options.ignoreErrors! || analysisErrors.isEmpty) { - await _task.prepareUnit(result); - } - }); - - var unmigratedDependencies = _task.migration!.unmigratedDependencies; - if (unmigratedDependencies.isNotEmpty) { - if (_migrationCli.options.skipImportCheck!) { - _migrationCli.logger.stdout(unmigratedDependenciesWarning); - } else { - throw ExperimentStatusException.unmigratedDependencies( - unmigratedDependencies); - } - } - - return AnalysisResult( - analysisErrors, - _migrationCli.lineInfo, - _migrationCli.pathContext, - _migrationCli.options.directory, - allSourcesAlreadyMigrated); - } - - Future runLaterPhases() async { - _progressBar = ProgressBar( - _migrationCli.logger, pathsToProcess.length * (numPhases - 1)); - - await processResources((ResolvedUnitResult result) async { - _progressBar.tick(); - await _task.processUnit(result); - }); - await processResources((ResolvedUnitResult result) async { - _progressBar.tick(); - if (_migrationCli.shouldBeMigrated(result.path)) { - await _task.finalizeUnit(result); - } - }); - _progressBar.complete(); - _migrationCli.logger.stdout(_migrationCli.ansi - .emphasized('Compiling instrumentation information...')); - // Update the tasks paths-to-process, in case of new or deleted files. - _task.pathsToProcess = pathsToProcess; - var state = await _task.finish(); - _task.processPackage(context.contextRoot.root, state.neededPackages); - if (_migrationCli.options.webPreview!) { - await _task.startPreviewServer(state, _migrationCli.applyHook); - } - state.previewUrls = _task.previewUrls; - - return state; - } -} - -/// Given a Logger and an analysis issue, render the issue to the logger. -class _IssueRenderer { - final Logger logger; - final String rootDirectory; - final Context pathContext; - final Map lineInfo; - - _IssueRenderer( - this.logger, this.rootDirectory, this.pathContext, this.lineInfo); - - void render(AnalysisError issue) { - // severity • Message ... at foo/bar.dart:6:1 • (error_code) - var lineInfoForThisFile = lineInfo[issue.source.fullName]!; - var location = lineInfoForThisFile.getLocation(issue.offset); - - final Ansi ansi = logger.ansi; - - logger.stdout( - ' ${ansi.error(issue.severity.name)} • ' - '${ansi.emphasized(_removePeriod(issue.message))} ' - 'at ${pathContext.relative(issue.source.fullName, from: rootDirectory)}' - ':${location.lineNumber}:${location.columnNumber} ' - '• (${issue.errorCode.name.toLowerCase()})', - ); - } -} - -extension on Severity { - /// Returns the simple name of the Severity, as a String. - String get name { - switch (this) { - case Severity.error: - return 'error'; - case Severity.warning: - return 'warning'; - case Severity.info: - return 'info'; - } - } -} diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart deleted file mode 100644 index a948a9fee022..000000000000 --- a/pkg/nnbd_migration/lib/nnbd_migration.dart +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert'; - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/nullability_migration_impl.dart'; -import 'package:pub_semver/pub_semver.dart'; - -export 'package:nnbd_migration/src/utilities/hint_utils.dart' show HintComment; - -/// Description of fixes that might be performed by nullability migration. -class NullabilityFixDescription { - /// A `.then((value) => ...)` suffix was added to an expression. - static const addThen = NullabilityFixDescription._( - appliedMessage: 'Added `.then` to adjust type of Future expression', - kind: NullabilityFixKind.addThen); - - /// An import was added to the library. - static const addImport = NullabilityFixDescription._( - appliedMessage: 'Added import for use in migrated code', - kind: NullabilityFixKind.addImport); - - /// A variable declaration needs to be marked as "late". - static const addLate = NullabilityFixDescription._( - appliedMessage: 'Added a late keyword', kind: NullabilityFixKind.addLate); - - /// A variable declaration needs to be marked as "late" due to the presence of - /// a `/*late*/` hint. - static const addLateDueToHint = NullabilityFixDescription._( - appliedMessage: 'Added a late keyword, due to a hint', - kind: NullabilityFixKind.addLateDueToHint); - - /// A variable declaration needs to be marked as "late" due to being certainly - /// assigned in test setup. - static const addLateDueToTestSetup = NullabilityFixDescription._( - appliedMessage: 'Added a late keyword, due to assignment in `setUp`', - kind: NullabilityFixKind.addLateDueToTestSetup); - - /// A variable declaration needs to be marked as "late" and "final" due to the - /// presence of a `/*late final*/` hint. - static const addLateFinalDueToHint = NullabilityFixDescription._( - appliedMessage: 'Added late and final keywords, due to a hint', - kind: NullabilityFixKind.addLateFinalDueToHint); - - /// An expression's value needs to be null-checked. - static const checkExpression = NullabilityFixDescription._( - appliedMessage: 'Added a non-null assertion to nullable expression', - kind: NullabilityFixKind.checkExpression, - ); - - /// An expression's value will be null-checked due to a hint. - static const checkExpressionDueToHint = NullabilityFixDescription._( - appliedMessage: 'Accepted a null check hint', - kind: NullabilityFixKind.checkExpressionDueToHint, - ); - - /// A compound assignment's combiner operator returns a type that isn't - /// assignable to the LHS of the assignment. - static const compoundAssignmentHasBadCombinedType = - NullabilityFixDescription._( - appliedMessage: 'Compound assignment has bad combined type', - kind: NullabilityFixKind.compoundAssignmentHasBadCombinedType, - ); - - /// A compound assignment's LHS has a nullable type. - static const compoundAssignmentHasNullableSource = - NullabilityFixDescription._( - appliedMessage: 'Compound assignment has nullable source', - kind: NullabilityFixKind.compoundAssignmentHasNullableSource, - ); - - /// Informative message: a condition of an if-test or conditional expression - /// will always evaluate to `false` in strong checking mode. - static const conditionFalseInStrongMode = NullabilityFixDescription._( - appliedMessage: 'Condition will always be false in strong checking mode', - kind: NullabilityFixKind.conditionFalseInStrongMode); - - /// Informative message: a condition of an if-test or conditional expression - /// will always evaluate to `true` in strong checking mode. - static const conditionTrueInStrongMode = NullabilityFixDescription._( - appliedMessage: 'Condition will always be true in strong checking mode', - kind: NullabilityFixKind.conditionTrueInStrongMode); - - /// An if-test or conditional expression needs to have its condition - /// discarded. - static const discardCondition = NullabilityFixDescription._( - appliedMessage: 'Discarded a condition which is always true', - kind: NullabilityFixKind.removeDeadCode, - ); - - /// An if-test or conditional expression needs to have its condition and - /// "else" branch discarded. - static const discardElse = NullabilityFixDescription._( - appliedMessage: 'Discarded an unreachable conditional else branch', - kind: NullabilityFixKind.removeDeadCode, - ); - - /// An if-test or conditional expression needs to have its condition and - /// "then" branch discarded. - static const discardThen = NullabilityFixDescription._( - appliedMessage: - 'Discarded a condition which is always false, and the "then" branch ' - 'that follows', - kind: NullabilityFixKind.removeDeadCode, - ); - - /// An if-test needs to be discarded completely. - static const discardIf = NullabilityFixDescription._( - appliedMessage: 'Discarded an if-test with no effect', - kind: NullabilityFixKind.removeDeadCode, - ); - - static const downcastExpression = NullabilityFixDescription._( - appliedMessage: 'Added a downcast to an expression', - kind: NullabilityFixKind.downcastExpression, - ); - - /// Informative message: there is no valid migration for `null` in a - /// non-nullable context. - static const noValidMigrationForNull = NullabilityFixDescription._( - appliedMessage: 'No valid migration for expression with type `Null` in ' - 'a non-nullable context', - kind: NullabilityFixKind.noValidMigrationForNull); - - /// Informative message: a null-aware access won't be necessary in strong - /// checking mode. - static const nullAwarenessUnnecessaryInStrongMode = - NullabilityFixDescription._( - appliedMessage: - 'Null-aware access will be unnecessary in strong checking mode', - kind: NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode); - - /// Informative message: a null-aware assignment won't be necessary in strong - /// checking mode. - static const nullAwareAssignmentUnnecessaryInStrongMode = - NullabilityFixDescription._( - appliedMessage: - 'Null-aware assignment will be unnecessary in strong checking mode', - kind: NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode); - - static const otherCastExpression = NullabilityFixDescription._( - appliedMessage: 'Added a cast to an expression (non-downcast)', - kind: NullabilityFixKind.otherCastExpression, - ); - - /// An unnecessary downcast has been discarded. - static const removeLanguageVersionComment = NullabilityFixDescription._( - appliedMessage: 'Removed language version comment so that null safety ' - 'features will be allowed in this file', - kind: NullabilityFixKind.removeLanguageVersionComment, - ); - - /// An unnecessary downcast has been discarded. - static const removeAs = NullabilityFixDescription._( - appliedMessage: 'Discarded a downcast that is now unnecessary', - kind: NullabilityFixKind.removeAs, - ); - - /// A null-aware operator needs to be changed into its non-null-aware - /// equivalent. - static const removeNullAwareness = NullabilityFixDescription._( - appliedMessage: - 'Changed a null-aware access into an ordinary access, because the target cannot be null', - kind: NullabilityFixKind.removeDeadCode); - - /// A null-aware assignment was removed because its LHS is non-nullable. - static const removeNullAwareAssignment = NullabilityFixDescription._( - appliedMessage: - 'Removed a null-aware assignment, because the target cannot be null', - kind: NullabilityFixKind.removeDeadCode); - - /// A built_value `@nullable` annotation has been discarded. - static const removeNullableAnnotation = NullabilityFixDescription._( - appliedMessage: 'Discarded a use of the built_value annotation @nullable', - kind: NullabilityFixKind.removeNullableAnnotation, - ); - - /// A message used to indicate a fix has been applied. - final String appliedMessage; - - /// The kind of fix described. - final NullabilityFixKind kind; - - /// A formal parameter needs to have a required keyword added. - factory NullabilityFixDescription.addRequired( - String? className, String? functionName, String paramName) { - var paramContainerName = - className == null ? functionName! : "'$className.$functionName'"; - return NullabilityFixDescription._( - appliedMessage: "Add 'required' keyword to parameter '$paramName' in " - '$paramContainerName', - kind: NullabilityFixKind.addRequired, - ); - } - - /// An explicit type needs to be added. - factory NullabilityFixDescription.addType(String typeText) => - NullabilityFixDescription._( - appliedMessage: "Add the explicit type '$typeText'", - kind: NullabilityFixKind.replaceVar, - ); - - /// A method call was changed from calling one method to another. - factory NullabilityFixDescription.changeMethodName( - String oldName, String newName) => - NullabilityFixDescription._( - appliedMessage: "Changed method '$oldName' to '$newName'", - kind: NullabilityFixKind.changeMethodName); - - /// An explicit type mentioned in the source program needs to be made - /// nullable. - factory NullabilityFixDescription.makeTypeNullable(String type) => - NullabilityFixDescription._( - appliedMessage: "Changed type '$type' to be nullable", - kind: NullabilityFixKind.makeTypeNullable, - ); - - /// An explicit type mentioned in the source program will be made - /// nullable due to a nullability hint. - factory NullabilityFixDescription.makeTypeNullableDueToHint(String type) => - NullabilityFixDescription._( - appliedMessage: - "Changed type '$type' to be nullable, due to a nullability hint", - kind: NullabilityFixKind.makeTypeNullableDueToHint, - ); - - /// A 'var' declaration needs to be replaced with an explicit type. - factory NullabilityFixDescription.replaceVar(String typeText) => - NullabilityFixDescription._( - appliedMessage: "Replace 'var' with '$typeText'", - kind: NullabilityFixKind.replaceVar, - ); - - /// An explicit type mentioned in the source program does not need to be made - /// nullable. - factory NullabilityFixDescription.typeNotMadeNullable(String type) => - NullabilityFixDescription._( - appliedMessage: "Type '$type' was not made nullable", - kind: NullabilityFixKind.typeNotMadeNullable, - ); - - /// An explicit type mentioned in the source program does not need to be made - /// nullable. - factory NullabilityFixDescription.typeNotMadeNullableDueToHint(String type) => - NullabilityFixDescription._( - appliedMessage: "Type '$type' was not made nullable due to a hint", - kind: NullabilityFixKind.typeNotMadeNullableDueToHint, - ); - - const NullabilityFixDescription._( - {required this.appliedMessage, required this.kind}); - - @override - int get hashCode => Object.hash(appliedMessage, kind); - - @override - bool operator ==(Object other) => - other is NullabilityFixDescription && - appliedMessage == other.appliedMessage && - kind == other.kind; - - @override - String toString() => - 'NullabilityFixDescription(${json.encode(appliedMessage)}, $kind)'; -} - -/// An enumeration of the various kinds of nullability fixes. -enum NullabilityFixKind { - addThen, - addImport, - addLate, - addLateDueToHint, - addLateDueToTestSetup, - addLateFinalDueToHint, - addRequired, - addType, - changeMethodName, - checkExpression, - checkExpressionDueToHint, - compoundAssignmentHasNullableSource, - compoundAssignmentHasBadCombinedType, - conditionFalseInStrongMode, - conditionTrueInStrongMode, - downcastExpression, - makeTypeNullable, - makeTypeNullableDueToHint, - noValidMigrationForNull, - nullAwarenessUnnecessaryInStrongMode, - nullAwareAssignmentUnnecessaryInStrongMode, - otherCastExpression, - removeAs, - removeDeadCode, - removeLanguageVersionComment, - removeNullableAnnotation, - replaceVar, - typeNotMadeNullable, - typeNotMadeNullableDueToHint, -} - -/// Provisional API for DartFix to perform nullability migration. -/// -/// Usage: pass each input source file to [prepareInput]. Then pass each input -/// source file to [processInput]. Then pass each input source file to -/// [finalizeInput]. Then call [finish] to obtain the modifications that need -/// to be made to each source file. -abstract class NullabilityMigration { - /// Prepares to perform nullability migration. - /// - /// If [permissive] is `true`, exception handling logic will try to proceed - /// as far as possible even though the migration algorithm is not yet - /// complete. TODO(paulberry): remove this mode once the migration algorithm - /// is fully implemented. - /// - /// Optional parameter [removeViaComments] indicates whether code that the - /// migration tool wishes to remove should instead be commenting it out. - /// - /// Optional parameter [warnOnWeakCode] indicates whether weak-only code - /// should be warned about or removed (in the way specified by - /// [removeViaComments]). - factory NullabilityMigration(NullabilityMigrationListener? listener, - {bool? permissive, - NullabilityMigrationInstrumentation? instrumentation, - bool? removeViaComments, - bool warnOnWeakCode}) = NullabilityMigrationImpl; - - /// Check if this migration is being run permissively. - bool? get isPermissive; - - /// Use this getter after any calls to [prepareInput] to obtain a list of URIs - /// of unmigrated dependencies. Ideally, this list should be empty before the - /// user tries to migrate their package. - List get unmigratedDependencies; - - void finalizeInput(ResolvedUnitResult result); - - /// Finishes the migration. Returns a map indicating packages that have been - /// newly imported by the migration; the caller should ensure that these - /// packages are properly imported by the package's pubspec. - /// - /// Keys of the returned map are package names; values indicate the minimum - /// required version of each package. - Map finish(); - - void prepareInput(ResolvedUnitResult result); - - void processInput(ResolvedUnitResult result); - - /// Update the migration after an edge has been added or removed. - void update(); -} - -/// [NullabilityMigrationListener] is used by [NullabilityMigration] -/// to communicate source changes or "fixes" to the client. -abstract class NullabilityMigrationListener { - /// [addEdit] is called once for each source edit, in the order in which they - /// appear in the source file. - void addEdit(Source source, SourceEdit edit); - - void addSuggestion(String descriptions, Location location); - - /// [reportException] is called once for each exception that occurs in - /// "permissive mode", reporting the location of the exception and the - /// exception details. - void reportException( - Source? source, AstNode? node, Object exception, StackTrace stackTrace); -} diff --git a/pkg/nnbd_migration/lib/nullability_state.dart b/pkg/nnbd_migration/lib/nullability_state.dart deleted file mode 100644 index 0adfd38c6641..000000000000 --- a/pkg/nnbd_migration/lib/nullability_state.dart +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2019, 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. - -/// State representation used to determine whether a nullability node expresses -/// non-null intent. -class NonNullIntent { - /// State of a nullability node for which no non-null intent has been seen. - static const none = NonNullIntent._('none', false); - - /// State of a nullability node for which indirect evidence of non-null intent - /// has been seen (e.g. an assertion or a use of a value in a non-null - /// context). - static const indirect = NonNullIntent._('indirect', true); - - /// State of a nullability node for which direct evidence of non-null intent - /// has been seen (e.g. an explicit "/*!*/" on a type, or a non-nullable type - /// coming from a migrated library). - static const direct = NonNullIntent._('direct', true, isDirect: true); - - final String name; - - /// Indicates whether this state represents a determination that non-null - /// intent is present. - final bool isPresent; - - /// Indicates whether this state represents a direct determination of non-null - /// intent (see [direct]). - final bool isDirect; - - const NonNullIntent._(this.name, this.isPresent, {this.isDirect = false}); - - /// Returns a [NonNullIntent] object representing the result of adding - /// indirect non-null intent to `this`. - NonNullIntent addIndirect() => isPresent ? this : indirect; - - @override - String toString() => name; -} - -/// State of a nullability node. -class Nullability { - /// State of a nullability node that has been determined to be non-nullable - /// by propagating upstream. - static const nonNullable = Nullability._('non-nullable', false); - - /// State of a nullability node that has been determined to be nullable by - /// propagating downstream. - static const ordinaryNullable = Nullability._('ordinary nullable', true); - - /// State of a nullability node that has been determined to be nullable by - /// propagating upstream from a contravariant use of a generic. - static const exactNullable = - Nullability._('exact nullable', true, isExactNullable: true); - - /// Name of the state (for use in debugging). - final String name; - - /// Indicates whether the given state should be considered nullable. - /// - /// After propagation, any nodes that remain in the undetermined state are - /// considered to be non-nullable, so this field is returns `false` for nodes - /// in that state. - final bool isNullable; - - /// Indicates whether a node in this state is "exact nullable", meaning that - /// is needs to be nullable because it represents a type argument that will - /// later be used in a contravariant way that requires it to be nullable. - final bool isExactNullable; - - const Nullability._(this.name, this.isNullable, - {this.isExactNullable = false}); - - @override - String toString() => name; -} diff --git a/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart b/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart deleted file mode 100644 index 6a357ab51419..000000000000 --- a/pkg/nnbd_migration/lib/src/already_migrated_code_decorator.dart +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/generated/element_type_provider.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edge_origin.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; - -/// This class transforms ordinary [DartType]s into their corresponding -/// [DecoratedType]s, assuming the [DartType]s come from code that has already -/// been migrated to NNBD. -class AlreadyMigratedCodeDecorator { - final NullabilityGraph _graph; - - final TypeProvider _typeProvider; - - AlreadyMigratedCodeDecorator(this._graph, this._typeProvider); - - /// Transforms [type], which should have come from code that has already been - /// migrated to NNBD, into the corresponding [DecoratedType]. - /// - /// TODO(paulberry): do we still need element or can we use target now? - DecoratedType decorate( - DartType type, Element element, NullabilityNodeTarget target) { - if (type is VoidType || type is DynamicType) { - var node = NullabilityNode.forAlreadyMigrated(target); - _graph.makeNullableUnion( - node, AlwaysNullableTypeOrigin.forElement(element, type is VoidType)); - return DecoratedType(type, node); - } - NullabilityNode node; - var nullabilitySuffix = type.nullabilitySuffix; - if (nullabilitySuffix == NullabilitySuffix.question) { - node = NullabilityNode.forAlreadyMigrated(target); - _graph.makeNullableUnion( - node, AlreadyMigratedTypeOrigin.forElement(element, true)); - } else { - node = NullabilityNode.forAlreadyMigrated(target); - _graph.makeNonNullableUnion( - node, AlreadyMigratedTypeOrigin.forElement(element, false)); - } - if (type is FunctionType) { - for (var element in type.typeFormals) { - DecoratedTypeParameterBounds.current!.put( - element, - decorate( - element.bound ?? - (_typeProvider.objectType as TypeImpl) - .withNullability(NullabilitySuffix.question), - element, - target.typeFormalBound(element.name))); - } - var positionalParameters = []; - var namedParameters = {}; - int index = 0; - for (var parameter in type.parameters) { - if (parameter.isPositional) { - positionalParameters.add(decorate( - parameter.type, element, target.positionalParameter(index++))); - } else { - var name = parameter.name; - namedParameters[name] = - decorate(parameter.type, element, target.namedParameter(name)); - } - } - return DecoratedType(type, node, - returnType: decorate(type.returnType, element, target.returnType()), - namedParameters: namedParameters, - positionalParameters: positionalParameters); - } else if (type is InterfaceType) { - var typeParameters = type.element.typeParameters; - if (typeParameters.isNotEmpty) { - assert(type.typeArguments.length == typeParameters.length); - int index = 0; - return DecoratedType(type, node, typeArguments: [ - for (var t in type.typeArguments) - decorate(t, element, target.typeArgument(index++)) - ]); - } - return DecoratedType(type, node); - } else if (type is TypeParameterType) { - return DecoratedType(type, node); - } else if (type.isBottom) { - return DecoratedType(type, node); - } else { - // TODO(paulberry) - throw UnimplementedError( - 'Unable to decorate already-migrated type $type'); - } - } - - /// Get all the decorated immediate supertypes of the non-migrated class - /// [class_]. - Iterable getImmediateSupertypes(InterfaceElement class_) { - var allSupertypes = []; - InterfaceType? supertype; - if (class_ is ClassElement) { - supertype = class_.supertype; - } - if (supertype != null) { - allSupertypes.add(supertype); - } - if (class_ is MixinElement) { - allSupertypes.addAll(class_.superclassConstraints); - } - allSupertypes.addAll(class_.preMigrationInterfaces); - allSupertypes.addAll(class_.mixins); - var type = class_.thisType; - if (type.isDartAsyncFuture) { - // Add FutureOr as a supertype of Future. - allSupertypes.add(_typeProvider.futureOrType(type.typeArguments.single)); - } - return [ - for (var t in allSupertypes) - decorate(t, class_, NullabilityNodeTarget.element(class_)) - ]; - } -} - -extension on InterfaceElement { - List get preMigrationInterfaces { - var previousElementTypeProvider = ElementTypeProvider.current; - try { - ElementTypeProvider.current = const ElementTypeProvider(); - return interfaces; - } finally { - ElementTypeProvider.current = previousElementTypeProvider; - } - } -} diff --git a/pkg/nnbd_migration/lib/src/conditional_discard.dart b/pkg/nnbd_migration/lib/src/conditional_discard.dart deleted file mode 100644 index c94b5ea29801..000000000000 --- a/pkg/nnbd_migration/lib/src/conditional_discard.dart +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; - -/// Container for information gathered during nullability migration about a -/// conditional check that might need to be discarded. -/// -/// This information will be associated with an Expression in the input program -/// whose boolean value influences control flow (e.g. the condition of an `if` -/// statement). -/// -/// TODO(paulberry): simplify this once PotentialModification is no longer -/// needed. -class ConditionalDiscard { - /// Nullability node that will be `nullable` if the code path that results - /// from the condition evaluating to `true` will be reachable after - /// nullability migration, and therefore should be kept. - /// - /// `null` if the code path should be kept regardless of the outcome of - /// migration. - final NullabilityNode? trueGuard; - - /// Nullability node that will be `nullable` if the code path that results - /// from the condition evaluating to `false` will be reachable after - /// nullability migration, and therefore should be kept. - /// - /// `null` if the code path should be kept regardless of the outcome of - /// migration. - final NullabilityNode? falseGuard; - - /// Indicates whether the condition is pure (free from side effects). - /// - /// For example, a condition like `x == null` is pure (assuming `x` is a local - /// variable or static variable), because evaluating it has no user-visible - /// effect other than returning a boolean value. - /// - /// If [pureCondition] is `false`, and either [trueGuard] or [falseGuard] is - /// `false`, that it is safe to delete the condition expression as well as the - /// dead code branch (e.g. it means that `if (x == null) f(); else g();` could - /// be changed to simply `g();`). - final bool pureCondition; - - ConditionalDiscard(this.trueGuard, this.falseGuard, this.pureCondition); - - /// Indicates whether the code path that results from the condition evaluating - /// to `false` is reachable after migration. - bool get keepFalse => falseGuard == null || falseGuard!.isNullable; - - /// Indicates whether the code path that results from the condition evaluating - /// to `true` is reachable after migration. - bool get keepTrue => trueGuard == null || trueGuard!.isNullable; - - FixReasonInfo? get reason => !keepTrue ? trueGuard : falseGuard; -} diff --git a/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart b/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart deleted file mode 100644 index 0e448dc51b84..000000000000 --- a/pkg/nnbd_migration/lib/src/decorated_class_hierarchy.dart +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/variables.dart'; - -/// Responsible for building and maintaining information about nullability -/// decorations related to the class hierarchy. -/// -/// For instance, if one class is a subclass of the other, we record the -/// nullabilities of all the types involved in the subclass relationship. -class DecoratedClassHierarchy { - final Variables? _variables; - - final NullabilityGraph _graph; - - /// Cache for speeding up the computation of - /// [_getGenericSupertypeDecorations]. - final Map> - _genericSupertypeDecorations = {}; - - DecoratedClassHierarchy(this._variables, this._graph); - - /// Retrieves a [DecoratedType] describing how [type] implements [superclass]. - /// - /// If the [type] is a [TypeParameterType], it will be resolved against its - /// bound. - DecoratedType asInstanceOf(DecoratedType type, InterfaceElement? superclass) { - if (superclass != null && - superclass.thisType.isDartAsyncFutureOr && - !type.type!.isDartAsyncFuture && - !type.type!.isDartAsyncFutureOr) { - return DecoratedType( - superclass.instantiate( - typeArguments: [type.type!], - nullabilitySuffix: NullabilitySuffix.none), - type.node, - typeArguments: [type]); - } - type = _getInterfaceType(type); - var typeType = type.type as InterfaceType; - var class_ = typeType.element; - if (class_ == superclass) return type; - var result = getDecoratedSupertype(class_, superclass!); - if (result.typeArguments.isNotEmpty && type.typeArguments.isNotEmpty) { - // TODO(paulberry): test - result = result.substitute(type.asSubstitution); - } - return result.withNode(type.node); - } - - /// Retrieves a [DecoratedType] describing how [class_] implements - /// [superclass]. - /// - /// If [class_] does not implement [superclass], raises an exception. - /// - /// Note that the returned [DecoratedType] will have a node of `never`, - /// because the relationship between a class and its superclass is not - /// nullable. - DecoratedType getDecoratedSupertype( - InterfaceElement class_, InterfaceElement superclass) { - assert(!(class_.library.isDartCore && class_.name == 'Null')); - if (superclass.typeParameters.isEmpty) { - return DecoratedType( - superclass.instantiate( - typeArguments: const [], - nullabilitySuffix: NullabilitySuffix.none, - ), - _graph.never, - ); - } - return _getGenericSupertypeDecorations(class_)[superclass] ?? - (throw StateError('Unrelated types: $class_ and $superclass')); - } - - /// Computes a map whose keys are all the superclasses of [class_], and whose - /// values indicate how [class_] implements each superclass. - Map _getGenericSupertypeDecorations( - InterfaceElement class_) { - var decorations = _genericSupertypeDecorations[class_]; - if (decorations == null) { - // Call ourselves recursively to compute how each of [class_]'s direct - // superclasses relates to all of its transitive superclasses. - decorations = {}; - var decoratedDirectSupertypes = - _variables!.decoratedDirectSupertypes(class_); - for (var entry in decoratedDirectSupertypes.entries) { - var superclass = entry.key; - var decoratedSupertype = entry.value!; - var supertype = decoratedSupertype.type as InterfaceType; - // Compute a type substitution to determine how [class_] relates to - // this specific [superclass]. - Map substitution = {}; - for (int i = 0; i < supertype.typeArguments.length; i++) { - substitution[supertype.element.typeParameters[i]] = - decoratedSupertype.typeArguments[i]!; - } - // Apply that substitution to the relation between [superclass] and - // each of its transitive superclasses, to determine the relation - // between [class_] and the transitive superclass. - var recursiveSupertypeDecorations = - _getGenericSupertypeDecorations(superclass); - for (var entry in recursiveSupertypeDecorations.entries) { - decorations[entry.key] ??= entry.value.substitute(substitution); - } - // Also record the relation between [class_] and its direct - // superclass. - decorations[superclass] ??= decoratedSupertype; - } - _genericSupertypeDecorations[class_] = decorations; - } - return decorations; - } - - DecoratedType _getInterfaceType(DecoratedType type) { - var typeType = type.type; - if (typeType is InterfaceType) { - return type; - } - - if (typeType is TypeParameterType) { - final innerType = _getInterfaceType( - _variables!.decoratedTypeParameterBound(typeType.element)!); - return type.substitute({typeType.element: innerType}); - } - - throw ArgumentError('$type is an unexpected type'); - } -} diff --git a/pkg/nnbd_migration/lib/src/decorated_type.dart b/pkg/nnbd_migration/lib/src/decorated_type.dart deleted file mode 100644 index 35fe682ec392..000000000000 --- a/pkg/nnbd_migration/lib/src/decorated_type.dart +++ /dev/null @@ -1,695 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/type_algebra.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; - -/// Representation of a type in the code to be migrated. In addition to -/// tracking the (unmigrated) [DartType], we track the [ConstraintVariable]s -/// indicating whether the type, and the types that compose it, are nullable. -class DecoratedType implements DecoratedTypeInfo, SubstitutedType { - @override - final DartType? type; - - @override - final NullabilityNode node; - - @override - final DecoratedType? returnType; - - /// If `this` is a function type, the [DecoratedType] of each of its - /// positional parameters (including both required and optional positional - /// parameters). - final List? positionalParameters; - - /// If `this` is a function type, the [DecoratedType] of each of its named - /// parameters. - final Map? namedParameters; - - /// If `this` is a parameterized type, the [DecoratedType] of each of its - /// type parameters. - /// - /// TODO(paulberry): how should we handle generic typedefs? - final List typeArguments; - - DecoratedType(this.type, this.node, - {this.returnType, - this.positionalParameters = const [], - this.namedParameters = const {}, - this.typeArguments = const []}) { - assert(() { - var type = this.type; - if (type is InterfaceType) { - assert(returnType == null); - assert(positionalParameters!.isEmpty); - assert(namedParameters!.isEmpty); - assert(typeArguments.length == type.typeArguments.length); - for (int i = 0; i < typeArguments.length; i++) { - assert(typeArguments[i]!.type == type.typeArguments[i], - '${typeArguments[i]!.type} != ${type.typeArguments[i]}'); - } - } else if (type is FunctionType) { - assert(returnType!.type == type.returnType); - int positionalParameterCount = 0; - int namedParameterCount = 0; - for (var parameter in type.parameters) { - if (parameter.isNamed) { - assert(namedParameters![parameter.name]!.type == parameter.type); - namedParameterCount++; - } else { - assert(positionalParameters![positionalParameterCount].type == - parameter.type); - positionalParameterCount++; - } - } - assert(positionalParameters!.length == positionalParameterCount); - assert(namedParameters!.length == namedParameterCount); - assert(typeArguments.isEmpty); - } else if (node is TypeParameterType) { - assert(returnType == null); - assert(positionalParameters!.isEmpty); - assert(namedParameters!.isEmpty); - assert(typeArguments.isEmpty); - } else { - assert(returnType == null); - assert(positionalParameters!.isEmpty); - assert(namedParameters!.isEmpty); - assert(typeArguments.isEmpty); - } - return true; - }()); - } - - /// Creates a decorated type corresponding to [type], with fresh nullability - /// nodes everywhere that don't correspond to any source location. These - /// nodes can later be unioned with other nodes. - factory DecoratedType.forImplicitFunction( - TypeProvider typeProvider, - FunctionType type, - NullabilityNode node, - NullabilityGraph graph, - NullabilityNodeTarget target, - {DecoratedType? returnType}) { - var positionalParameters = []; - var namedParameters = {}; - int index = 0; - for (var parameter in type.parameters) { - if (parameter.isPositional) { - positionalParameters.add(DecoratedType.forImplicitType(typeProvider, - parameter.type, graph, target.positionalParameter(index++))); - } else { - var name = parameter.name; - namedParameters[name] = DecoratedType.forImplicitType( - typeProvider, parameter.type, graph, target.namedParameter(name)); - } - } - for (var element in type.typeFormals) { - if (DecoratedTypeParameterBounds.current!.get(element) == null) { - DecoratedTypeParameterBounds.current!.put( - element, - DecoratedType.forImplicitType( - typeProvider, - element.bound ?? typeProvider.objectType, - graph, - target.typeFormalBound(element.name))); - } - } - return DecoratedType(type, node, - returnType: returnType ?? - DecoratedType.forImplicitType( - typeProvider, type.returnType, graph, target.returnType()), - namedParameters: namedParameters, - positionalParameters: positionalParameters); - } - - /// Creates a DecoratedType corresponding to [type], with fresh nullability - /// nodes everywhere that don't correspond to any source location. These - /// nodes can later be unioned with other nodes. - factory DecoratedType.forImplicitType(TypeProvider typeProvider, - DartType? type, NullabilityGraph graph, NullabilityNodeTarget target, - {List? typeArguments}) { - var nullabilityNode = NullabilityNode.forInferredType(target); - if (type is InterfaceType) { - assert(() { - if (typeArguments != null) { - assert(typeArguments.length == type.typeArguments.length); - for (var i = 0; i < typeArguments.length; ++i) { - assert(typeArguments[i]!.type == type.typeArguments[i]); - } - } - return true; - }()); - - int index = 0; - typeArguments ??= type.typeArguments - .map((t) => DecoratedType.forImplicitType( - typeProvider, t, graph, target.typeArgument(index++))) - .toList(); - return DecoratedType(type, nullabilityNode, typeArguments: typeArguments); - } else if (type is FunctionType) { - if (typeArguments != null) { - throw 'Not supported: implicit function type with explicit type ' - 'arguments'; - } - return DecoratedType.forImplicitFunction( - typeProvider, type, nullabilityNode, graph, target); - } else { - assert(typeArguments == null); - return DecoratedType(type, nullabilityNode); - } - } - - /// If `this` represents an interface type, returns the substitution necessary - /// to produce this type using the class's type as a starting point. - /// Otherwise throws an exception. - /// - /// For instance, if `this` represents `List`, returns the substitution - /// `{T: int?1}`, where `T` is the [TypeParameterElement] for `List`'s type - /// parameter. - Map get asSubstitution { - var type = this.type; - if (type is InterfaceType) { - return { - for (int i = 0; i < typeArguments.length; i++) - type.element.typeParameters[i]: typeArguments[i]! - }; - } else { - throw StateError( - 'Tried to convert a non-interface type to a substitution'); - } - } - - /// If this type is a function type, returns its generic formal parameters. - /// Otherwise returns `null`. - List? get typeFormals { - var type = this.type; - if (type is FunctionType) { - return type.typeFormals; - } else { - return null; - } - } - - @override - // TODO(srawlins): Override `hashCode` in this class. It should be based on - // [node] and [type], but there is more logic, as seen below in the use of - // [RenamedDecoratedFunctionTypes.match]. - // ignore: hash_and_equals - bool operator ==(Object other) { - if (other is DecoratedType) { - if (!identical(node, other.node)) return false; - var thisType = type; - var otherType = other.type; - if (thisType is FunctionType && otherType is FunctionType) { - if (thisType.normalParameterTypes.length != - otherType.normalParameterTypes.length) { - return false; - } - if (thisType.typeFormals.length != otherType.typeFormals.length) { - return false; - } - var renamed = RenamedDecoratedFunctionTypes.match( - this, other, (bound1, bound2) => bound1 == bound2); - if (renamed == null) return false; - if (renamed.returnType1 != renamed.returnType2) return false; - if (!_compareLists( - renamed.positionalParameters1, renamed.positionalParameters2)) { - return false; - } - if (!_compareMaps(renamed.namedParameters1, renamed.namedParameters2)) { - return false; - } - return true; - } else if (thisType is InterfaceType && otherType is InterfaceType) { - if (thisType.element != otherType.element) return false; - if (!_compareLists(typeArguments, other.typeArguments)) { - return false; - } - return true; - } else { - return thisType == otherType; - } - } - return false; - } - - /// Converts one function type into another by substituting the given - /// [argumentTypes] for the function's generic parameters. - DecoratedType instantiate(List argumentTypes) { - var type = this.type as FunctionType; - var typeFormals = type.typeFormals; - assert(argumentTypes.length == typeFormals.length); - List undecoratedArgumentTypes = []; - Map substitution = {}; - for (int i = 0; i < argumentTypes.length; i++) { - var argumentType = argumentTypes[i]; - undecoratedArgumentTypes.add(argumentType.type!); - substitution[typeFormals[i]] = argumentType; - } - return _substituteFunctionAfterFormals( - type.instantiate(undecoratedArgumentTypes), substitution); - } - - @override - DecoratedTypeInfo? namedParameter(String name) => namedParameters![name]; - - @override - DecoratedTypeInfo? positionalParameter(int i) => positionalParameters![i]; - - /// Updates the [roles] map with information about the nullability nodes - /// pointed to by this decorated type. - /// - /// Each entry stored in [roles] maps the role of the node to the node itself. - /// Roles look like pathnames, where each path component is an integer to - /// represent a type argument (or a positional parameter type, in the case of - /// a function type), an name to represent a named parameter type, or `@r` to - /// represent a return type. - void recordRoles(Map roles, - {String rolePrefix = ''}) { - roles[rolePrefix] = node; - returnType?.recordRoles(roles, rolePrefix: '$rolePrefix/@r'); - for (int i = 0; i < positionalParameters!.length; i++) { - positionalParameters![i].recordRoles(roles, rolePrefix: '$rolePrefix/$i'); - } - for (var entry in namedParameters!.entries) { - entry.value.recordRoles(roles, rolePrefix: '$rolePrefix/${entry.key}'); - } - for (int i = 0; i < typeArguments.length; i++) { - typeArguments[i]!.recordRoles(roles, rolePrefix: '$rolePrefix/$i'); - } - } - - /// Apply the given [substitution] to this type. - /// - /// [undecoratedResult] is the result of the substitution, as determined by - /// the normal type system. If not supplied, it is inferred. - DecoratedType substitute( - Map substitution, - [DartType? undecoratedResult]) { - if (substitution.isEmpty) return this; - if (undecoratedResult == null) { - var type = this.type!; - undecoratedResult = Substitution.fromPairs( - substitution.keys.toList(), - substitution.values.map((d) => d.type!).toList(), - ).substituteType(type); - if (undecoratedResult is FunctionType && type is FunctionType) { - for (int i = 0; i < undecoratedResult.typeFormals.length; i++) { - DecoratedTypeParameterBounds.current!.put( - undecoratedResult.typeFormals[i], - DecoratedTypeParameterBounds.current!.get(type.typeFormals[i])); - } - } - } - return _substitute(substitution, undecoratedResult); - } - - @override - String toString() { - var trailing = node.debugSuffix; - var type = this.type; - if (type is TypeParameterType || type is VoidType) { - return '$type$trailing'; - } else if (type is InterfaceType) { - var name = type.element.name; - var args = ''; - if (type.typeArguments.isNotEmpty) { - args = '<${typeArguments.join(', ')}>'; - } - return '$name$args$trailing'; - } else if (type is FunctionType) { - String formals = ''; - if (type.typeFormals.isNotEmpty) { - formals = '<${type.typeFormals.join(', ')}>'; - } - List paramStrings = []; - for (int i = 0; i < positionalParameters!.length; i++) { - var prefix = ''; - if (i == type.normalParameterTypes.length) { - prefix = '['; - } - paramStrings.add('$prefix${positionalParameters![i]}'); - } - if (type.normalParameterTypes.length < positionalParameters!.length) { - paramStrings.last += ']'; - } - if (namedParameters!.isNotEmpty) { - var prefix = '{'; - for (var entry in namedParameters!.entries) { - paramStrings.add('$prefix${entry.key}: ${entry.value}'); - prefix = ''; - } - paramStrings.last += '}'; - } - var args = paramStrings.join(', '); - return '$returnType Function$formals($args)$trailing'; - } else if (type is DynamicTypeImpl) { - return 'dynamic'; - } else if (type!.isBottom) { - return 'Never$trailing'; - } else { - throw '$type'; // TODO(paulberry) - } - } - - @override - DecoratedTypeInfo? typeArgument(int i) => typeArguments[i]; - - /// Creates a shallow copy of `this`, replacing the nullability node. - DecoratedType withNode(NullabilityNode node) => DecoratedType(type, node, - returnType: returnType, - positionalParameters: positionalParameters, - namedParameters: namedParameters, - typeArguments: typeArguments); - - /// Creates a shallow copy of `this`, replacing the nullability node and the - /// type. - DecoratedType withNodeAndType(NullabilityNode node, DartType? type) => - DecoratedType(type, node, - returnType: returnType, - positionalParameters: positionalParameters, - namedParameters: namedParameters, - typeArguments: typeArguments); - - @override - DecoratedType _performSubstitution( - DecoratedType other, DartType undecoratedResult) => - withNodeAndType( - NullabilityNode.forSubstitution(node, other.node), undecoratedResult); - - /// Internal implementation of [_substitute], used as a recursion target. - DecoratedType _substitute( - Map substitution, - DartType? undecoratedResult) { - var type = this.type; - if (type is FunctionType && undecoratedResult is FunctionType) { - var typeFormals = type.typeFormals; - assert(typeFormals.length == undecoratedResult.typeFormals.length); - if (typeFormals.isNotEmpty) { - // The analyzer sometimes allocates fresh type variables when performing - // substitutions, so we need to reflect that in our decorations by - // substituting to use the type variables the analyzer used. - substitution = - Map.from(substitution); - for (int i = 0; i < typeFormals.length; i++) { - // Check if it's a fresh type variable. - if (undecoratedResult.typeFormals[i].enclosingElement == null) { - substitution[typeFormals[i]] = - _TypeVariableReplacement(undecoratedResult.typeFormals[i]); - } - } - for (int i = 0; i < typeFormals.length; i++) { - var typeFormal = typeFormals[i]; - var oldDecoratedBound = - DecoratedTypeParameterBounds.current!.get(typeFormal); - var undecoratedResult2 = undecoratedResult.typeFormals[i].bound; - if (undecoratedResult2 == null) { - if (oldDecoratedBound == null) { - assert( - false, 'Could not find old decorated bound for type formal'); - // Recover the best we can by assuming a bound of `dynamic`. - oldDecoratedBound = DecoratedType( - DynamicTypeImpl.instance, - NullabilityNode.forInferredType( - NullabilityNodeTarget.text('Type parameter bound'))); - } - undecoratedResult2 = oldDecoratedBound.type; - } - var newDecoratedBound = - oldDecoratedBound!._substitute(substitution, undecoratedResult2); - if (identical(typeFormal, undecoratedResult.typeFormals[i])) { - assert(oldDecoratedBound == newDecoratedBound); - } else { - DecoratedTypeParameterBounds.current! - .put(undecoratedResult.typeFormals[i], newDecoratedBound); - } - } - } - return _substituteFunctionAfterFormals(undecoratedResult, substitution); - } else if (type is InterfaceType && undecoratedResult is InterfaceType) { - List newTypeArguments = []; - for (int i = 0; i < typeArguments.length; i++) { - newTypeArguments.add(typeArguments[i]! - .substitute(substitution, undecoratedResult.typeArguments[i])); - } - return DecoratedType(undecoratedResult, node, - typeArguments: newTypeArguments); - } else if (type is TypeParameterType) { - var inner = substitution[type.element]; - if (inner == null) { - return this; - } else { - return inner._performSubstitution(this, undecoratedResult!); - } - } else if (type is VoidType || type is DynamicType) { - return this; - } - throw '$type.substitute($type | $substitution)'; // TODO(paulberry) - } - - /// Performs the logic that is common to substitution and function type - /// instantiation. Namely, a decorated type is formed whose undecorated type - /// is [undecoratedResult], and whose return type, positional parameters, and - /// named parameters are formed by performing the given [substitution]. - DecoratedType _substituteFunctionAfterFormals(FunctionType undecoratedResult, - Map substitution) { - var newPositionalParameters = []; - var numRequiredParameters = undecoratedResult.normalParameterTypes.length; - for (int i = 0; i < positionalParameters!.length; i++) { - var undecoratedParameterType = i < numRequiredParameters - ? undecoratedResult.normalParameterTypes[i] - : undecoratedResult.optionalParameterTypes[i - numRequiredParameters]; - newPositionalParameters.add(positionalParameters![i] - ._substitute(substitution, undecoratedParameterType)); - } - var newNamedParameters = {}; - for (var entry in namedParameters!.entries) { - var name = entry.key; - var undecoratedParameterType = - undecoratedResult.namedParameterTypes[name]; - newNamedParameters[name] = - entry.value._substitute(substitution, undecoratedParameterType); - } - return DecoratedType(undecoratedResult, node, - returnType: - returnType!._substitute(substitution, undecoratedResult.returnType), - positionalParameters: newPositionalParameters, - namedParameters: newNamedParameters); - } - - static bool _compareLists( - List? list1, List? list2) { - if (identical(list1, list2)) return true; - if (list1!.length != list2!.length) return false; - for (int i = 0; i < list1.length; i++) { - if (list1[i] != list2[i]) return false; - } - return true; - } - - static bool _compareMaps( - Map? map1, Map? map2) { - if (identical(map1, map2)) return true; - if (map1!.length != map2!.length) return false; - for (var entry in map1.entries) { - if (entry.value != map2[entry.key]) return false; - } - return true; - } -} - -/// Data structure mapping type parameters to their decorated bounds. -/// -/// Since we need to be able to access this mapping globally throughout the -/// migration engine, from places where we can't easily inject it, the current -/// mapping is stored in a static variable. -class DecoratedTypeParameterBounds { - /// The [DecoratedTypeParameterBounds] currently in use, or `null` if we are - /// not currently in a stage of migration where we need access to the - /// decorated types of type parameter bounds. - /// - /// If `null`, then attempts to look up the decorated types of type parameter - /// bounds will fail. - static DecoratedTypeParameterBounds? current; - - final _orphanBounds = Expando(); - - final _parentedBounds = {}; - - DecoratedType? get(TypeParameterElement element) { - if (element.enclosingElement == null) { - return _orphanBounds[element]; - } else { - return _parentedBounds[element]; - } - } - - void put(TypeParameterElement element, DecoratedType? bounds) { - if (element.enclosingElement == null) { - _orphanBounds[element] = bounds; - } else { - _parentedBounds[element] = bounds; - } - } -} - -/// Helper class that renames the type parameters in two decorated function -/// types so that they match. -class RenamedDecoratedFunctionTypes { - final DecoratedType? returnType1; - - final DecoratedType? returnType2; - - final List? positionalParameters1; - - final List? positionalParameters2; - - final Map? namedParameters1; - - final Map? namedParameters2; - - RenamedDecoratedFunctionTypes._( - this.returnType1, - this.returnType2, - this.positionalParameters1, - this.positionalParameters2, - this.namedParameters1, - this.namedParameters2); - - /// Attempt to find a renaming of the type parameters of [type1] and [type2] - /// (both of which should be function types) such that the generic type - /// parameters match. - /// - /// The callback [boundsMatcher] is used to determine whether type parameter - /// bounds match. - /// - /// If such a renaming can be found, it is returned. If not, `null` is - /// returned. - static RenamedDecoratedFunctionTypes? match( - DecoratedType type1, - DecoratedType type2, - bool Function(DecoratedType, DecoratedType) boundsMatcher) { - if (!_isNeeded(type1.typeFormals, type2.typeFormals)) { - return RenamedDecoratedFunctionTypes._( - type1.returnType, - type2.returnType, - type1.positionalParameters, - type2.positionalParameters, - type1.namedParameters, - type2.namedParameters); - } - // Create a fresh set of type variables and substitute so we can - // compare safely. - var substitution1 = {}; - var substitution2 = {}; - var newParameters = []; - for (int i = 0; i < type1.typeFormals!.length; i++) { - var newParameter = - TypeParameterElementImpl.synthetic(type1.typeFormals![i].name); - newParameters.add(newParameter); - var newType = _TypeVariableReplacement(newParameter); - substitution1[type1.typeFormals![i]] = newType; - substitution2[type2.typeFormals![i]] = newType; - } - for (int i = 0; i < type1.typeFormals!.length; i++) { - var bound1 = DecoratedTypeParameterBounds.current! - .get((type1.type as FunctionType).typeFormals[i])! - .substitute(substitution1); - var bound2 = DecoratedTypeParameterBounds.current! - .get((type2.type as FunctionType).typeFormals[i])! - .substitute(substitution2); - if (!boundsMatcher(bound1, bound2)) return null; - DecoratedTypeParameterBounds.current!.put(newParameters[i], bound1); - } - var returnType1 = type1.returnType!.substitute(substitution1); - var returnType2 = type2.returnType!.substitute(substitution2); - var positionalParameters1 = - _substituteList(type1.positionalParameters!, substitution1); - var positionalParameters2 = - _substituteList(type2.positionalParameters!, substitution2); - var namedParameters1 = - _substituteMap(type1.namedParameters!, substitution1); - var namedParameters2 = - _substituteMap(type2.namedParameters!, substitution2); - return RenamedDecoratedFunctionTypes._( - returnType1, - returnType2, - positionalParameters1, - positionalParameters2, - namedParameters1, - namedParameters2); - } - - static bool _isNeeded(List? formals1, - List? formals2) { - if (identical(formals1, formals2)) return false; - if (formals1!.length != formals2!.length) return true; - for (int i = 0; i < formals1.length; i++) { - if (!identical(formals1[i], formals2[i])) return true; - } - return false; - } - - static List _substituteList(List list, - Map substitution) { - return list.map((t) => t!.substitute(substitution)).toList(); - } - - static Map _substituteMap( - Map map, - Map substitution) { - var result = {}; - for (var entry in map.entries) { - result[entry.key] = entry.value!.substitute(substitution); - } - return result; - } -} - -/// Abstract base class for anything that can appear in the "value" position of -/// a decorated type substitution map (in other words, anything that a type -/// variable can be replaced with when performing a substitution). A -/// substituted type is either a [DecoratedType] (in which case the substitution -/// will be fully general, and the resulting decorated type will have a -/// nullability that's based on both the original type and the substituted -/// type), or it can be a [_TypeVariableReplacement], which is used when one -/// type variable is being replaced with another, but there is no change in -/// nullability. -abstract class SubstitutedType { - /// The undecorated type to be substituted - DartType? get type; - - /// Called by substitute methods to perform the actual substitution. [other] - /// is the decorated type to be substituted, and [undecoratedResult] is the - /// expected undecorated result of the substitution. - DecoratedType _performSubstitution( - DecoratedType other, DartType undecoratedResult); -} - -/// Data structure used as a value in a substitution map if the only -/// substitution that needs to be performed is to replace one type variable with -/// another. -class _TypeVariableReplacement implements SubstitutedType { - @override - final DartType type; - - _TypeVariableReplacement(TypeParameterElement newTypeVariable) - : type = TypeParameterTypeImpl( - element: newTypeVariable, - nullabilitySuffix: NullabilitySuffix.star, - ); - - @override - DecoratedType _performSubstitution( - DecoratedType other, DartType undecoratedResult) => - other.withNodeAndType(other.node, undecoratedResult); -} diff --git a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart b/pkg/nnbd_migration/lib/src/decorated_type_operations.dart deleted file mode 100644 index 356d86ceb6ca..000000000000 --- a/pkg/nnbd_migration/lib/src/decorated_type_operations.dart +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:_fe_analyzer_shared/src/field_promotability.dart'; -import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; -import 'package:_fe_analyzer_shared/src/type_inference/type_operations.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edge_origin.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/variables.dart'; - -/// [Operations] that works with [DecoratedType]s. -class DecoratedTypeOperations - implements Operations { - final TypeSystem _typeSystem; - final TypeProvider _typeProvider; - final Variables? _variableRepository; - final NullabilityGraph _graph; - - @override - late final DecoratedType boolType = - DecoratedType(_typeProvider.boolType, _graph.never); - - DecoratedTypeOperations(this._typeSystem, this._typeProvider, - this._variableRepository, this._graph); - - @override - bool areStructurallyEqual(DecoratedType type1, DecoratedType type2) { - // TODO(scheglov): implement areStructurallyEqual - throw UnimplementedError('TODO(scheglov)'); - } - - @override - TypeClassification classifyType(DecoratedType type) { - if (type.type!.isDartCoreNull) { - return TypeClassification.nullOrEquivalent; - } else { - return TypeClassification.potentiallyNullable; - } - } - - @override - DecoratedType factor(DecoratedType from, DecoratedType what) { - // TODO(scheglov): https://github.com/dart-lang/sdk/issues/41672 - return from; - } - - @override - bool forcePromotion( - DecoratedType to, - DecoratedType from, - List? promotedTypes, - List? newPromotedTypes) { - // Do not force promotion if it appears that the element's type was just - // demoted. - if (promotedTypes != null && - (newPromotedTypes == null || - newPromotedTypes.length < promotedTypes.length)) { - return false; - } - if (!isSubtypeOf(to, from)) { - return false; - } - var fromSources = from.node.upstreamEdges; - // Do not force promotion if [to] already points to [from]. - if (fromSources.length == 1 && fromSources.single.sourceNode == to.node) { - return false; - } - return true; - } - - @override - DecoratedType glb(DecoratedType type1, DecoratedType type2) { - // TODO: implement glb - throw UnimplementedError(); - } - - @override - bool isAssignableTo(DecoratedType fromType, DecoratedType toType) { - // TODO: implement isAssignableTo - throw UnimplementedError(); - } - - @override - bool isDynamic(DecoratedType type) { - return type is DynamicType; - } - - @override - bool isError(DecoratedType type) { - return type is InvalidType; - } - - @override - bool isNever(DecoratedType type) { - return false; - } - - @override - bool isPropertyPromotable(Object property) { - // TODO(paulberry): research whether we would get higher quality migrations - // if we returned `true` instead. - return false; - } - - @override - bool isSameType(DecoratedType type1, DecoratedType type2) { - return type1 == type2; - } - - @override - bool isSubtypeOf(DecoratedType leftType, DecoratedType rightType) { - if (!_typeSystem.isSubtypeOf(leftType.type!, rightType.type!)) { - // Pre-migrated types don't meet the subtype requirement. Not a subtype. - return false; - } else if (rightType.node == _graph.never && - leftType.node != _graph.never) { - // The "never" node will never be nullable, so not a subtype. - return false; - } else { - // We don't know whether a subtype relation will hold once the graph is - // solved. Assume it will. - return true; - } - } - - @override - bool isTypeParameterType(DecoratedType type) => - type.type is TypeParameterType; - - @override - DecoratedType lub(DecoratedType type1, DecoratedType type2) { - // TODO: implement lub - throw UnimplementedError(); - } - - @override - DecoratedType makeNullable(DecoratedType type) { - // TODO: implement makeNullable - throw UnimplementedError(); - } - - @override - DecoratedType? matchIterableType(DecoratedType type) { - // TODO: implement matchIterableType - throw UnimplementedError(); - } - - @override - DecoratedType? matchListType(DecoratedType type) { - // TODO: implement matchListType - throw UnimplementedError(); - } - - @override - MapPatternTypeArguments? matchMapType(DecoratedType type) { - // TODO: implement matchMapType - throw UnimplementedError(); - } - - @override - DecoratedType? matchStreamType(DecoratedType type) { - // TODO: implement matchStreamType - throw UnimplementedError(); - } - - @override - DecoratedType normalize(DecoratedType type) { - // TODO(scheglov): implement normalize - throw UnimplementedError('TODO(scheglov)'); - } - - @override - DecoratedType promoteToNonNull(DecoratedType type) { - return type.withNode(_graph.never); - } - - @override - // This function walks [chain1] and [chain2], creating an intersection similar - // to that of [VariableModel.joinPromotedTypes]. - List? refinePromotedTypes(List? chain1, - List? chain2, List? promotedTypes) { - if (chain1 == null || chain2 == null) return promotedTypes; - - // This method can only handle very simple joins. - if (chain1.length != chain2.length) return promotedTypes; - - // The promotion chains were intersected without any promotions being - // dropped. There is nothing to do here. - if (promotedTypes != null && promotedTypes.length == chain1.length) { - return promotedTypes; - } - - var result = []; - int index = 0; - while (index < chain1.length) { - if (!isSameType(chain1[index], chain2[index])) { - break; - } - result.add(chain1[index]); - index++; - } - - if (index != chain1.length - 1) { - // This method can only handle the situation in which the promotion chains - // are identical up to the last node. If we are not in such situation, - // return the previous result. - return promotedTypes; - } - - DecoratedType firstType = chain1[index]; - DecoratedType secondType = chain2[index]; - - var node = NullabilityNode.forGLB(); - var origin = - // TODO(srawlins): How to get the source or astNode from within here... - GreatestLowerBoundOrigin(null /* source */, null /* astNode */); - _graph.connect(firstType.node, node, origin, guards: [secondType.node]); - _graph.connect(node, firstType.node, origin); - _graph.connect(node, secondType.node, origin); - - return result..add(firstType.withNode(node)); - } - - @override - DecoratedType? tryPromoteToType(DecoratedType to, DecoratedType from) { - // TODO(paulberry): implement appropriate logic for type variable promotion. - if (isSubtypeOf(to, from)) { - return to; - } - - // Allow promotion from non-null types to other types, preserving non-null. - var keepNonNull = promoteToNonNull(to); - if (isSubtypeOf(keepNonNull, from)) { - return keepNonNull; - } else { - return null; - } - } - - @override - DecoratedType variableType(PromotableElement variable) { - return _variableRepository!.decoratedElementType(variable); - } - - @override - PropertyNonPromotabilityReason? whyPropertyIsNotPromotable(Object property) => - null; -} diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart deleted file mode 100644 index fcd3625543f6..000000000000 --- a/pkg/nnbd_migration/lib/src/edge_builder.dart +++ /dev/null @@ -1,4148 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; -import 'package:_fe_analyzer_shared/src/type_inference/assigned_variables.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/ast/ast.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; -import 'package:analyzer/src/dart/element/member.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl; -import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart'; -import 'package:analyzer/src/error/best_practices_verifier.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/conditional_discard.dart'; -import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edge_origin.dart'; -import 'package:nnbd_migration/src/expression_checks.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:nnbd_migration/src/utilities/built_value_transformer.dart'; -import 'package:nnbd_migration/src/utilities/completeness_tracker.dart'; -import 'package:nnbd_migration/src/utilities/hint_utils.dart'; -import 'package:nnbd_migration/src/utilities/permissive_mode.dart'; -import 'package:nnbd_migration/src/utilities/resolution_utils.dart'; -import 'package:nnbd_migration/src/utilities/scoped_set.dart'; -import 'package:nnbd_migration/src/utilities/where_not_null_transformer.dart'; -import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart'; -import 'package:nnbd_migration/src/variables.dart'; - -import 'decorated_type_operations.dart'; - -/// A potentially reversible decision is that downcasts and sidecasts should -/// assume non-nullability. This could be changed such that we assume the -/// widest type, or the narrowest type. For now we assume non-nullability, but -/// have a flag to isolate that work. -const _assumeNonNullabilityInCasts = true; - -/// Test class mixing in _AssignmentChecker, to allow [checkAssignment] to be -/// more easily unit tested. -@visibleForTesting -class AssignmentCheckerForTesting extends Object with _AssignmentChecker { - @override - final TypeSystem _typeSystem; - - @override - final TypeProvider typeProvider; - - final NullabilityGraph _graph; - - /// Tests should fill in this map with the bounds of any type parameters being - /// tested. - final Map bounds = {}; - - @override - final DecoratedClassHierarchy _decoratedClassHierarchy; - - AssignmentCheckerForTesting(this._typeSystem, this.typeProvider, this._graph, - this._decoratedClassHierarchy); - - void checkAssignment(EdgeOrigin origin, - {required DecoratedType source, - required DecoratedType destination, - required bool hard}) { - super._checkAssignment(origin, FixReasonTarget.root, - source: source, destination: destination, hard: hard); - } - - @override - void _connect(NullabilityNode? source, NullabilityNode? destination, - EdgeOrigin origin, FixReasonTarget edgeTarget, - {bool hard = false, bool checkable = true}) { - _graph.connect(source, destination!, origin, - hard: hard, checkable: checkable); - } - - @override - DecoratedType? _getCallMethodType(DecoratedType type) => null; - - @override - DecoratedType _getTypeParameterTypeBound(DecoratedType type) { - return bounds[(type.type as TypeParameterType).element] ?? - (throw StateError('Unknown bound for $type')); - } -} - -/// Visitor that builds nullability graph edges by examining code to be -/// migrated. -/// -/// The return type of each `visit...` method is a [DecoratedType] indicating -/// the static type of the visited expression, along with the constraint -/// variables that will determine its nullability. For `visit...` methods that -/// don't visit expressions, `null` will be returned. -class EdgeBuilder extends GeneralizingAstVisitor - with - _AssignmentChecker, - PermissiveModeVisitor, - CompletenessTracker, - ResolutionUtils { - final TypeSystem _typeSystem; - - final InheritanceManager3 _inheritanceManager; - - /// The repository of constraint variables and decorated types (from a - /// previous pass over the source code). - final Variables _variables; - - final NullabilityMigrationListener? listener; - - final NullabilityMigrationInstrumentation? instrumentation; - - final NullabilityGraph _graph; - - TypeProvider typeProvider; - - @override - final Source? source; - - @override - final DecoratedClassHierarchy? _decoratedClassHierarchy; - - /// If we are visiting a function body or initializer, instance of flow - /// analysis. Otherwise `null`. - FlowAnalysis? _flowAnalysis; - - /// If we are visiting a function body or initializer, assigned variable - /// information used in flow analysis. Otherwise `null`. - AssignedVariables? _assignedVariables; - - /// The outermost function or method being visited, or `null` if the visitor - /// is not inside any function or method. - ExecutableElement? _currentExecutable; - - /// The [DecoratedType] of the innermost function or method being visited, or - /// `null` if the visitor is not inside any function or method. - /// - /// This is needed to construct the appropriate nullability constraints for - /// return statements. - DecoratedType? _currentFunctionType; - - /// If the innermost enclosing executable is a constructor with field formal - /// parameters, a map from each field's getter to the corresponding field - /// formal parameter element. Otherwise, an empty map. - Map - _currentFieldFormals = const {}; - - FunctionExpression? _currentFunctionExpression; - - /// The [InterfaceElement] or [ExtensionElement] of the current interface - /// or extension being visited, or null. - Element? _currentInterfaceOrExtension; - - /// If an extension declaration is being visited, the decorated type of the - /// type appearing in the `on` clause (this is the type of `this` inside the - /// extension declaration). Null if an extension declaration is not being - /// visited. - DecoratedType? _currentExtendedType; - - /// The [DecoratedType] of the innermost list or set literal being visited, or - /// `null` if the visitor is not inside any list or set. - /// - /// This is needed to construct the appropriate nullability constraints for - /// ui as code elements. - DecoratedType? _currentLiteralElementType; - - /// The key [DecoratedType] of the innermost map literal being visited, or - /// `null` if the visitor is not inside any map. - /// - /// This is needed to construct the appropriate nullability constraints for - /// ui as code elements. - DecoratedType? _currentMapKeyType; - - /// The value [DecoratedType] of the innermost map literal being visited, or - /// `null` if the visitor is not inside any map. - /// - /// This is needed to construct the appropriate nullability constraints for - /// ui as code elements. - DecoratedType? _currentMapValueType; - - /// Information about the most recently visited binary expression whose - /// boolean value could possibly affect nullability analysis. - _ConditionInfo? _conditionInfo; - - /// The set of nullability nodes that would have to be `nullable` for the code - /// currently being visited to be reachable. - /// - /// Guard variables are attached to the left hand side of any generated - /// constraints, so that constraints do not take effect if they come from - /// code that can be proven unreachable by the migration tool. - final _guards = []; - - /// The scope of locals (parameters, variables) that are post-dominated by the - /// current node as we walk the AST. We use a [_ScopedLocalSet] so that outer - /// scopes may track their post-dominators separately from inner scopes. - /// - /// Note that this is not guaranteed to be complete. It is used to make hard - /// edges on a best-effort basis. - var _postDominatedLocals = ScopedSet(); - - /// Map whose keys are expressions of the form `a?.b` on the LHS of - /// assignments, and whose values are the nullability nodes corresponding to - /// the expression preceding `?.`. These are needed in order to properly - /// analyze expressions like `a?.b += c`, since the type of the compound - /// assignment is nullable if the type of the expression preceding `?.` is - /// nullable. - final Map _conditionalNodes = {}; - - /// If we are visiting a cascade expression, the decorated type of the target - /// of the cascade. Otherwise `null`. - DecoratedType? _currentCascadeTargetType; - - /// While visiting a class declaration, set of class fields that lack - /// initializers at their declaration sites. - Set? _fieldsNotInitializedAtDeclaration; - - /// While visiting a constructor, set of class fields that lack initializers - /// at their declaration sites *and* for which we haven't yet found an - /// initializer in the constructor declaration. - Set? _fieldsNotInitializedByConstructor; - - /// Current nesting depth of [visitNamedType] - int _typeNameNesting = 0; - - final Set _lateHintedLocals = {}; - - final Map _nullCheckHints = {}; - - /// Helper that assists us in transforming Iterable methods to their "OrNull" - /// equivalents. - final WhereOrNullTransformer _whereOrNullTransformer; - - /// Helper that assists us in transforming calls to `Iterable.where` to - /// `Iterable.whereNotNull`. - final WhereNotNullTransformer _whereNotNullTransformer; - - /// Deferred processing that should be performed once we have finished - /// evaluating the decorated type of a method invocation. - final Map - _deferredMethodInvocationProcessing = {}; - - /// If we are visiting a local function or closure, the set of local variables - /// assigned to so far inside it. Otherwise `null`. - Set? _elementsWrittenToInLocalFunction; - - final LibraryElement _library; - - EdgeBuilder(this.typeProvider, this._typeSystem, this._variables, this._graph, - this.source, this.listener, this._decoratedClassHierarchy, this._library, - {this.instrumentation}) - : _inheritanceManager = InheritanceManager3(), - _whereOrNullTransformer = - WhereOrNullTransformer(typeProvider, _typeSystem), - _whereNotNullTransformer = - WhereNotNullTransformer(typeProvider, _typeSystem); - - /// The synthetic element we use as a stand-in for `this` when analyzing - /// extension methods. - Element get _extensionThis => DynamicElementImpl.instance; - - /// Gets the decorated type of [element] from [_variables], performing any - /// necessary substitutions. - /// - /// [node] is used as the AST node for the edge origin if any graph edges need - /// to be created. [targetType], if provided, indicates the type of the - /// target (receiver) for a method, getter, or setter invocation. - /// [targetExpression], if provided, is the expression for the target - /// (receiver) for a method, getter, or setter invocation. - DecoratedType getOrComputeElementType(AstNode node, Element element, - {DecoratedType? targetType, - Expression? targetExpression, - bool isNullAware = false}) { - Map? substitution; - Element? baseElement = element.declaration; - if (targetType != null) { - var enclosingElement = baseElement!.enclosingElement; - if (enclosingElement is InterfaceElement) { - if (targetType.type.explicitBound is InterfaceType && - enclosingElement.typeParameters.isNotEmpty) { - substitution = _decoratedClassHierarchy! - .asInstanceOf(targetType, enclosingElement) - .asSubstitution; - } - } else { - assert(enclosingElement is ExtensionElement); - final extensionElement = enclosingElement as ExtensionElement; - // The semantics of calling an extension method or extension property - // are essentially the same as calling a static function where the - // receiver is passed as an invisible `this` parameter whose type is the - // extension's "on" type. If the extension declaration has type - // parameters, they behave like inferred type parameters of the static - // function. - // - // So what we need to do is (1) create a set of DecoratedTypes to - // represent the inferred types of the type parameters, (2) ensure that - // the receiver type is assignable to "on" type (with those decorated - // types substituted into it), and (3) substitute those decorated types - // into the declared type of the extension method or extension property, - // so that the caller will match up parameter types and the return type - // appropriately. - // - // Taking each step in turn: - // (1) create a set of decorated types to represent the inferred types - // of the type parameters. Note that we must make sure each of these - // types satisfies its associated bound. - var typeParameters = extensionElement.typeParameters; - if (typeParameters.isNotEmpty) { - var preMigrationSubstitution = (element as Member).substitution.map; - substitution = {}; - var target = NullabilityNodeTarget.text('extension'); - for (int i = 0; i < typeParameters.length; i++) { - var typeParameter = typeParameters[i]; - var decoratedTypeArgument = DecoratedType.forImplicitType( - typeProvider, - preMigrationSubstitution[typeParameter], - _graph, - target.typeArgument(i)); - substitution[typeParameter] = decoratedTypeArgument; - var edgeOrigin = - InferredTypeParameterInstantiationOrigin(source, node); - _checkAssignment(edgeOrigin, FixReasonTarget.root, - source: decoratedTypeArgument, - destination: - _variables.decoratedTypeParameterBound(typeParameter)!, - hard: true); - } - } - // (2) ensure that the receiver type is assignable to "on" type (with - // those decorated types substituted into it) - var onType = _variables.decoratedElementType(extensionElement); - if (substitution != null) { - onType = onType.substitute(substitution); - } - _checkAssignment(InferredTypeParameterInstantiationOrigin(source, node), - FixReasonTarget.root, - source: targetType, - destination: onType, - hard: !isNullAware && - (targetExpression == null || - _isReferenceInScope(targetExpression))); - // (3) substitute those decorated types into the declared type of the - // extension method or extension property, so that the caller will match - // up parameter types and the return type appropriately. - // - // There's nothing more we need to do here. The substitution below - // will do the job. - } - } - DecoratedType decoratedBaseType; - if (baseElement is PropertyAccessorElement && - baseElement.isSynthetic && - !baseElement.variable.isSynthetic) { - var variable = baseElement.variable; - var decoratedElementType = _variables.decoratedElementType(variable); - if (baseElement.isGetter) { - var target = NullabilityNodeTarget.text('getter function'); - decoratedBaseType = DecoratedType( - baseElement.type, NullabilityNode.forInferredType(target), - returnType: decoratedElementType); - } else { - assert(baseElement.isSetter); - var target = NullabilityNodeTarget.text('setter function'); - decoratedBaseType = DecoratedType( - baseElement.type, NullabilityNode.forInferredType(target), - positionalParameters: [decoratedElementType], - returnType: DecoratedType(VoidTypeImpl.instance, - NullabilityNode.forInferredType(target.returnType()))); - } - } else { - decoratedBaseType = _variables.decoratedElementType(baseElement!); - } - if (substitution != null) { - return decoratedBaseType.substitute(substitution); - } else { - return decoratedBaseType; - } - } - - @override - // TODO(srawlins): Theoretically, edges should be connected between arguments - // and parameters, as in an instance creation. It is quite rare though, to - // declare a class and use it as an annotation in the same package. - DecoratedType? visitAnnotation(Annotation node) { - var previousFlowAnalysis = _flowAnalysis; - var previousAssignedVariables = _assignedVariables; - if (_flowAnalysis == null) { - _assignedVariables = AssignedVariables(); - // Note: we are using flow analysis to help us track true nullabilities; - // it's not necessary to replicate old bugs. So we pass `true` for - // `respectImplicitlyTypedVarInitializers`. - _flowAnalysis = FlowAnalysis( - DecoratedTypeOperations( - _typeSystem, typeProvider, _variables, _graph), - _assignedVariables!, - respectImplicitlyTypedVarInitializers: true, - fieldPromotionEnabled: false); - } - try { - _dispatch(node.constructorName); - _dispatchList(node.arguments?.arguments); - } finally { - _flowAnalysis = previousFlowAnalysis; - _assignedVariables = previousAssignedVariables; - } - annotationVisited(node); - return null; - } - - @override - DecoratedType visitAsExpression(AsExpression node) { - if (BestPracticesVerifier.isUnnecessaryCast( - node, _typeSystem as TypeSystemImpl)) { - _variables.recordUnnecessaryCast(source, node); - } - _dispatch(node.type); - final typeNode = _variables.decoratedTypeAnnotation(source, node.type); - _handleAssignment(node.expression, destinationType: typeNode); - _flowAnalysis!.asExpression_end(node.expression, typeNode); - return typeNode; - } - - @override - DecoratedType? visitAssertInitializer(AssertInitializer node) { - _flowAnalysis!.assert_begin(); - _checkExpressionNotNull(node.condition); - if (identical(_conditionInfo?.condition, node.condition)) { - var intentNode = _conditionInfo!.trueDemonstratesNonNullIntent; - if (intentNode != null && _conditionInfo!.postDominatingIntent!) { - _graph.makeNonNullable(_conditionInfo!.trueDemonstratesNonNullIntent, - NonNullAssertionOrigin(source, node)); - } - } - _flowAnalysis!.assert_afterCondition(node.condition); - _dispatch(node.message); - _flowAnalysis!.assert_end(); - return null; - } - - @override - DecoratedType? visitAssertStatement(AssertStatement node) { - _flowAnalysis!.assert_begin(); - _checkExpressionNotNull(node.condition); - if (identical(_conditionInfo?.condition, node.condition)) { - var intentNode = _conditionInfo!.trueDemonstratesNonNullIntent; - if (intentNode != null && _conditionInfo!.postDominatingIntent!) { - _graph.makeNonNullable(_conditionInfo!.trueDemonstratesNonNullIntent, - NonNullAssertionOrigin(source, node)); - } - } - _flowAnalysis!.assert_afterCondition(node.condition); - _dispatch(node.message); - _flowAnalysis!.assert_end(); - return null; - } - - @override - DecoratedType? visitAssignmentExpression(AssignmentExpression node) { - bool isQuestionAssign = false; - bool isCompound = false; - if (node.operator.type == TokenType.QUESTION_QUESTION_EQ) { - isQuestionAssign = true; - } else if (node.operator.type != TokenType.EQ) { - isCompound = true; - } - - var sourceIsSetupCall = false; - if (node.leftHandSide is SimpleIdentifier && - _isCurrentFunctionExpressionFoundInTestSetUpCall()) { - var assignee = - getWriteOrReadElement(node.leftHandSide as SimpleIdentifier)!; - var enclosingElementOfCurrentFunction = - _currentFunctionExpression!.declaredElement!.enclosingElement; - if (enclosingElementOfCurrentFunction == assignee.enclosingElement) { - // [node]'s enclosing function is a function expression passed directly - // to a call to the test package's `setUp` function, and [node] is an - // assignment to a variable declared in the same scope as the call to - // `setUp`. - sourceIsSetupCall = true; - } - } - - var isInjectorGetAssignment = _isInjectorGetCall(node.rightHandSide); - - var expressionType = _handleAssignment(node.rightHandSide, - assignmentExpression: node, - compoundOperatorInfo: isCompound ? node : null, - questionAssignNode: isQuestionAssign ? node : null, - sourceIsSetupCall: sourceIsSetupCall, - isInjectorGetAssignment: isInjectorGetAssignment); - var conditionalNode = _conditionalNodes[node.leftHandSide]; - if (conditionalNode != null) { - expressionType = expressionType!.withNode( - NullabilityNode.forLUB(conditionalNode, expressionType.node)); - _variables.recordDecoratedExpressionType(node, expressionType); - } - - return expressionType; - } - - @override - DecoratedType visitAwaitExpression(AwaitExpression node) { - var expressionType = _dispatch(node.expression)!; - var type = expressionType.type!; - if (type.isDartCoreNull) { - // Nothing to do; awaiting `null` produces `null`. - } else if (_typeSystem.isSubtypeOf(type, typeProvider.futureDynamicType)) { - expressionType = _decoratedClassHierarchy! - .asInstanceOf(expressionType, typeProvider.futureElement) - .typeArguments[0]!; - } else if (type.isDartAsyncFutureOr) { - expressionType = expressionType.typeArguments[0]!; - } - return expressionType; - } - - @override - DecoratedType visitBinaryExpression(BinaryExpression node) { - var operatorType = node.operator.type; - var leftOperand = node.leftOperand; - var rightOperand = node.rightOperand; - if (operatorType == TokenType.EQ_EQ || operatorType == TokenType.BANG_EQ) { - var leftType = _dispatch(leftOperand)!; - _graph.connectDummy(leftType.node, DummyOrigin(source, node)); - var equalityInfo = - _flowAnalysis!.equalityOperand_end(leftOperand, leftType); - var rightType = _dispatch(rightOperand)!; - _graph.connectDummy(rightType.node, DummyOrigin(source, node)); - bool notEqual = operatorType == TokenType.BANG_EQ; - _flowAnalysis!.equalityOperation_end(node, equalityInfo, - _flowAnalysis!.equalityOperand_end(rightOperand, rightType), - notEqual: notEqual); - - void buildNullConditionInfo(NullLiteral nullLiteral, - Expression otherOperand, DecoratedType otherType) { - assert(nullLiteral != otherOperand); - // TODO(paulberry): only set falseChecksNonNull in unconditional - // control flow - // TODO(paulberry): figure out what the rules for isPure should be. - bool isPure = otherOperand is SimpleIdentifier; - var otherNode = otherType.node; - var otherTypeType = otherType.type; - if (otherTypeType is TypeParameterType) { - var boundNullability = - DecoratedTypeParameterBounds.current!.get(otherTypeType.element); - if (boundNullability != null) { - otherNode = - NullabilityNode.forLUB(otherNode, boundNullability.node); - } - } - var conditionInfo = _ConditionInfo(node, - isPure: isPure, - postDominatingIntent: _isReferenceInScope(otherOperand), - trueGuard: otherNode, - falseDemonstratesNonNullIntent: otherNode); - _conditionInfo = notEqual ? conditionInfo.not(node) : conditionInfo; - } - - if (rightOperand is NullLiteral) { - buildNullConditionInfo(rightOperand, leftOperand, leftType); - _graph.makeNullable(leftType.node, NullAwareAccessOrigin(source, node)); - } else if (leftOperand is NullLiteral) { - buildNullConditionInfo(leftOperand, rightOperand, rightType); - _graph.makeNullable( - rightType.node, NullAwareAccessOrigin(source, node)); - } - - return _makeNonNullableBoolType(node); - } else if (operatorType == TokenType.AMPERSAND_AMPERSAND || - operatorType == TokenType.BAR_BAR) { - bool isAnd = operatorType == TokenType.AMPERSAND_AMPERSAND; - _flowAnalysis!.logicalBinaryOp_begin(); - _checkExpressionNotNull(leftOperand); - _flowAnalysis! - .logicalBinaryOp_rightBegin(node.leftOperand, node, isAnd: isAnd); - _postDominatedLocals.doScoped( - action: () => _checkExpressionNotNull(rightOperand)); - _flowAnalysis!.logicalBinaryOp_end(node, rightOperand, isAnd: isAnd); - return _makeNonNullableBoolType(node); - } else if (operatorType == TokenType.QUESTION_QUESTION) { - DecoratedType expressionType; - var leftType = _dispatch(leftOperand)!; - _flowAnalysis!.ifNullExpression_rightBegin(node.leftOperand, leftType); - try { - _guards.add(leftType.node); - DecoratedType? rightType; - _postDominatedLocals.doScoped(action: () { - rightType = _dispatch(rightOperand); - }); - var ifNullNode = NullabilityNode.forIfNotNull(node); - expressionType = _decorateUpperOrLowerBound( - node, node.staticType, leftType, rightType!, true, - node: ifNullNode); - _connect(rightType!.node, expressionType.node, - IfNullOrigin(source, node), null); - } finally { - _flowAnalysis!.ifNullExpression_end(); - _guards.removeLast(); - } - _variables.recordDecoratedExpressionType(node, expressionType); - return expressionType; - } else if (operatorType.isUserDefinableOperator) { - var targetType = _checkExpressionNotNull(leftOperand); - var callee = node.staticElement; - if (callee == null) { - _dispatch(rightOperand); - return _makeNullableDynamicType(node); - } else { - var calleeType = getOrComputeElementType(node, callee, - targetType: targetType, targetExpression: leftOperand); - assert(calleeType.positionalParameters!.isNotEmpty); // TODO(paulberry) - _handleAssignment(rightOperand, - destinationType: calleeType.positionalParameters![0]); - return _fixNumericTypes(calleeType.returnType!, node.staticType); - } - } else { - // TODO(paulberry) - _dispatch(leftOperand); - _dispatch(rightOperand); - _unimplemented( - node, 'Binary expression with operator ${node.operator.lexeme}'); - } - } - - @override - DecoratedType visitBooleanLiteral(BooleanLiteral node) { - _flowAnalysis!.booleanLiteral(node, node.value); - return _makeNonNullLiteralType(node); - } - - @override - DecoratedType? visitBreakStatement(BreakStatement node) { - _flowAnalysis!.handleBreak(FlowAnalysisHelper.getLabelTarget( - node, node.label?.staticElement as LabelElement?, - isBreak: true)); - // Later statements no longer post-dominate the declarations because we - // exited (or, in parent scopes, conditionally exited). - // TODO(mfairhurst): don't clear post-dominators beyond the current loop. - _postDominatedLocals.clearEachScope(); - - return null; - } - - @override - DecoratedType? visitCascadeExpression(CascadeExpression node) { - var oldCascadeTargetType = _currentCascadeTargetType; - try { - _currentCascadeTargetType = _checkExpressionNotNull(node.target); - _dispatchList(node.cascadeSections); - return _currentCascadeTargetType; - } finally { - _currentCascadeTargetType = oldCascadeTargetType; - } - } - - @override - DecoratedType? visitCatchClause(CatchClause node) { - _flowAnalysis!.tryCatchStatement_catchBegin( - node.exceptionParameter?.declaredElement, - node.stackTraceParameter?.declaredElement); - _dispatch(node.exceptionType); - // The catch clause may not execute, so create a new scope for - // post-dominators. - _postDominatedLocals.doScoped(action: () => _dispatch(node.body)); - _flowAnalysis!.tryCatchStatement_catchEnd(); - return null; - } - - @override - DecoratedType? visitClassDeclaration(ClassDeclaration node) { - visitClassOrMixinOrExtensionDeclaration(node); - _dispatch(node.extendsClause); - _dispatch(node.implementsClause); - _dispatch(node.withClause); - _dispatch(node.typeParameters); - return null; - } - - DecoratedType? visitClassOrMixinOrExtensionDeclaration( - CompilationUnitMember node) { - assert(node is ClassDeclaration || - node is ExtensionDeclaration || - node is MixinDeclaration); - try { - _currentInterfaceOrExtension = node.declaredElement; - - List members; - if (node is ClassDeclaration) { - members = node.members; - } else if (node is ExtensionDeclaration) { - members = node.members; - } else { - members = (node as MixinDeclaration).members; - } - - _fieldsNotInitializedAtDeclaration = { - for (var member in members) - if (member is FieldDeclaration && - _variables.getLateHint(source, member.fields) == null) - for (var field in member.fields.variables) - if (!field.declaredElement!.isStatic && field.initializer == null) - field.declaredElement as FieldElement? - }; - if (_currentInterfaceOrExtension is ClassElement && - (_currentInterfaceOrExtension as ClassElement) - .unnamedConstructor - ?.isSynthetic == - true) { - _handleUninitializedFields(node, _fieldsNotInitializedAtDeclaration!); - } - _dispatchList(node.metadata); - _dispatchList(members); - _fieldsNotInitializedAtDeclaration = null; - } finally { - _currentInterfaceOrExtension = null; - } - return null; - } - - @override - DecoratedType? visitClassTypeAlias(ClassTypeAlias node) { - _dispatch(node.superclass); - _dispatch(node.implementsClause); - _dispatch(node.withClause); - var classElement = node.declaredElement!; - var supertype = classElement.supertype!; - var superElement = supertype.element; - for (var constructorElement in classElement.constructors) { - assert(constructorElement.isSynthetic); - var superConstructorElement = - superElement.getNamedConstructor(constructorElement.name)!; - var constructorDecoratedType = _variables - .decoratedElementType(constructorElement) - .substitute(_decoratedClassHierarchy! - .getDecoratedSupertype(classElement, superElement) - .asSubstitution); - var superConstructorDecoratedType = - _variables.decoratedElementType(superConstructorElement); - var origin = ImplicitMixinSuperCallOrigin(source, node); - _linkDecoratedTypeParameters( - constructorDecoratedType, superConstructorDecoratedType, origin, - isUnion: true); - } - return null; - } - - @override - DecoratedType? visitComment(Comment node) { - // Ignore comments. - return null; - } - - @override - DecoratedType visitConditionalExpression(ConditionalExpression node) { - _flowAnalysis!.conditional_conditionBegin(); - _checkExpressionNotNull(node.condition); - NullabilityNode? trueGuard; - NullabilityNode? falseGuard; - if (identical(_conditionInfo?.condition, node.condition)) { - trueGuard = _conditionInfo!.trueGuard; - falseGuard = _conditionInfo!.falseGuard; - _variables.recordConditionalDiscard(source, node, - ConditionalDiscard(trueGuard, falseGuard, _conditionInfo!.isPure)); - } - - late DecoratedType thenType; - late DecoratedType elseType; - - // Post-dominators diverge as we branch in the conditional. - // Note: we don't have to create a scope for each branch because they can't - // define variables. - _postDominatedLocals.doScoped(action: () { - _flowAnalysis!.conditional_thenBegin(node.condition, node); - if (trueGuard != null) { - _guards.add(trueGuard); - } - try { - thenType = _dispatch(node.thenExpression)!; - if (trueGuard != null) { - thenType = thenType - .withNode(_nullabilityNodeForGLB(node, thenType.node, trueGuard)); - } - } finally { - if (trueGuard != null) { - _guards.removeLast(); - } - } - _flowAnalysis!.conditional_elseBegin(node.thenExpression, thenType); - if (falseGuard != null) { - _guards.add(falseGuard); - } - try { - elseType = _dispatch(node.elseExpression)!; - if (falseGuard != null) { - elseType = elseType.withNode( - _nullabilityNodeForGLB(node, elseType.node, falseGuard)); - } - } finally { - if (falseGuard != null) { - _guards.removeLast(); - } - } - }); - - var overallType = _decorateUpperOrLowerBound( - node, node.staticType, thenType, elseType, true); - _flowAnalysis! - .conditional_end(node, overallType, node.elseExpression, elseType); - _variables.recordDecoratedExpressionType(node, overallType); - return overallType; - } - - @override - DecoratedType? visitConstructorDeclaration(ConstructorDeclaration node) { - _fieldsNotInitializedByConstructor = - _fieldsNotInitializedAtDeclaration!.toSet(); - _dispatch(node.redirectedConstructor?.type.typeArguments); - _handleExecutableDeclaration( - node, - node.declaredElement!, - node.metadata, - null, - node.parameters, - node.initializers, - node.body, - node.redirectedConstructor); - _fieldsNotInitializedByConstructor = null; - return null; - } - - @override - DecoratedType? visitConstructorFieldInitializer( - ConstructorFieldInitializer node) { - _fieldsNotInitializedByConstructor!.remove(node.fieldName.staticElement); - _handleAssignment(node.expression, - destinationType: - getOrComputeElementType(node, node.fieldName.staticElement!)); - return null; - } - - @override - DecoratedType? visitContinueStatement(ContinueStatement node) { - _flowAnalysis!.handleContinue(FlowAnalysisHelper.getLabelTarget( - node, node.label?.staticElement as LabelElement?, - isBreak: false)); - // Later statements no longer post-dominate the declarations because we - // exited (or, in parent scopes, conditionally exited). - // TODO(mfairhurst): don't clear post-dominators beyond the current loop. - _postDominatedLocals.clearEachScope(); - - return null; - } - - @override - DecoratedType? visitDefaultFormalParameter(DefaultFormalParameter node) { - _dispatch(node.parameter); - var defaultValue = node.defaultValue; - var declaredElement = node.declaredElement; - if (defaultValue == null) { - if (declaredElement!.hasRequired) { - // Nothing to do; the implicit default value of `null` will never be - // reached. - } else if (_variables.getRequiredHint(source, node) != null) { - // Nothing to do; assume the implicit default value of `null` will never - // be reached. - } else { - var enclosingElement = declaredElement.enclosingElement; - if (enclosingElement is ConstructorElement && - enclosingElement.isFactory && - enclosingElement.redirectedConstructor != null) { - // Redirecting factory constructors inherit their parameters' default - // values from the constructors they redirect to, so the lack of a - // default value doesn't mean the parameter has to be nullable. - } else { - _graph.makeNullable( - getOrComputeElementType(node, declaredElement).node, - OptionalFormalParameterOrigin(source, node)); - } - } - } else { - _handleAssignment(defaultValue, - destinationType: getOrComputeElementType(node, declaredElement!), - fromDefaultValue: true); - } - return null; - } - - @override - DecoratedType? visitDoStatement(DoStatement node) { - _flowAnalysis!.doStatement_bodyBegin(node); - _dispatch(node.body); - _flowAnalysis!.doStatement_conditionBegin(); - _checkExpressionNotNull(node.condition); - _flowAnalysis!.doStatement_end(node.condition); - return null; - } - - @override - DecoratedType visitDoubleLiteral(DoubleLiteral node) { - return _makeNonNullLiteralType(node); - } - - @override - DecoratedType? visitExpressionFunctionBody(ExpressionFunctionBody node) { - if (_currentFunctionType == null) { - _unimplemented( - node, - 'ExpressionFunctionBody with no current function ' - '(parent is ${node.parent.runtimeType})'); - } - _handleAssignment(node.expression, - destinationType: _currentFunctionType!.returnType, - wrapFuture: node.isAsynchronous); - return null; - } - - @override - DecoratedType visitExpressionStatement(ExpressionStatement node) { - var decoratedType = _dispatch(node.expression)!; - if (node.expression is! CascadeExpression) { - // Don't add a dummy edge for cascade expression, since - // it forces the target of cascade to be nullable, which - // is almost always wrong. - _graph.connectDummy(decoratedType.node, DummyOrigin(source, node)); - } - return decoratedType; - } - - DecoratedType? visitExtensionDeclaration(ExtensionDeclaration node) { - _dispatch(node.typeParameters); - _dispatch(node.extendedType); - _currentExtendedType = - _variables.decoratedTypeAnnotation(source, node.extendedType); - visitClassOrMixinOrExtensionDeclaration(node); - _currentExtendedType = null; - return null; - } - - @override - DecoratedType? visitExtensionOverride(ExtensionOverride node) { - return _dispatch(node.argumentList.arguments.single); - } - - @override - DecoratedType? visitFieldFormalParameter(FieldFormalParameter node) { - _dispatchList(node.metadata); - _dispatch(node.parameters); - var parameterElement = node.declaredElement as FieldFormalParameterElement; - var parameterType = _variables.decoratedElementType(parameterElement); - var field = parameterElement.field; - if (field != null) { - _fieldsNotInitializedByConstructor!.remove(field); - var fieldType = _variables.decoratedElementType(field); - var origin = FieldFormalParameterOrigin(source, node); - if (node.type == null) { - _linkDecoratedTypes(parameterType, fieldType, origin, isUnion: false); - _checkAssignment(origin, FixReasonTarget.root, - source: fieldType, destination: parameterType, hard: false); - } else { - _dispatch(node.type); - _checkAssignment(origin, FixReasonTarget.root, - source: parameterType, destination: fieldType, hard: true); - } - } - - return null; - } - - @override - DecoratedType? visitForElement(ForElement node) { - _handleForLoopParts(node, node.forLoopParts, node.body, - (body) => _handleCollectionElement(body as CollectionElement)); - return null; - } - - @override - DecoratedType? visitForStatement(ForStatement node) { - _handleForLoopParts( - node, node.forLoopParts, node.body, (body) => _dispatch(body)); - return null; - } - - @override - DecoratedType? visitFunctionDeclaration(FunctionDeclaration node) { - _dispatchList(node.metadata); - _dispatch(node.returnType); - if (_flowAnalysis != null) { - // This is a local function. - var previousPostDominatedLocals = _postDominatedLocals; - var previousElementsWrittenToInLocalFunction = - _elementsWrittenToInLocalFunction; - try { - _elementsWrittenToInLocalFunction = {}; - _postDominatedLocals = ScopedSet(); - _flowAnalysis!.functionExpression_begin(node); - _dispatch(node.functionExpression); - _flowAnalysis!.functionExpression_end(); - } finally { - for (var element in _elementsWrittenToInLocalFunction!) { - previousElementsWrittenToInLocalFunction?.add(element); - previousPostDominatedLocals.removeFromAllScopes(element); - } - _elementsWrittenToInLocalFunction = - previousElementsWrittenToInLocalFunction; - _postDominatedLocals = previousPostDominatedLocals; - } - } else { - _createFlowAnalysis(node, node.functionExpression.parameters); - // Initialize a new postDominator scope that contains only the parameters. - try { - _dispatch(node.functionExpression); - _flowAnalysis!.finish(); - } finally { - _flowAnalysis = null; - _assignedVariables = null; - } - var declaredElement = node.declaredElement; - if (declaredElement is PropertyAccessorElement) { - if (declaredElement.isGetter) { - var setter = declaredElement.correspondingSetter; - if (setter != null) { - _handleGetterSetterCorrespondence( - node, null, declaredElement, setter.declaration); - } - } else { - assert(declaredElement.isSetter); - var getter = declaredElement.correspondingGetter; - if (getter != null) { - _handleGetterSetterCorrespondence( - node, null, getter.declaration, declaredElement); - } - } - } else if (declaredElement != null && declaredElement.isPublic) { - _makeArgumentsNullable(declaredElement, node); - } - } - return null; - } - - @override - DecoratedType? visitFunctionExpression(FunctionExpression node) { - // TODO(mfairhurst): enable edge builder "_insideFunction" hard edge tests. - _dispatch(node.parameters); - _dispatch(node.typeParameters); - if (node.parent is! FunctionDeclaration) { - _flowAnalysis!.functionExpression_begin(node); - } - _addParametersToFlowAnalysis(node.parameters); - var previousFunction = _currentFunctionExpression; - var previousFunctionType = _currentFunctionType; - var previousFieldFormals = _currentFieldFormals; - _currentFunctionExpression = node; - _currentFunctionType = - _variables.decoratedElementType(node.declaredElement!); - _currentFieldFormals = const {}; - var previousPostDominatedLocals = _postDominatedLocals; - var previousElementsWrittenToInLocalFunction = - _elementsWrittenToInLocalFunction; - var previousExecutable = _currentExecutable; - _currentExecutable ??= node.declaredElement; - try { - if (node.parent is! FunctionDeclaration) { - _elementsWrittenToInLocalFunction = {}; - } - _postDominatedLocals = ScopedSet(); - _postDominatedLocals.doScoped( - elements: node.declaredElement!.parameters, - action: () => _dispatch(node.body)); - _variables.recordDecoratedExpressionType(node, _currentFunctionType); - return _currentFunctionType; - } finally { - if (node.parent is! FunctionDeclaration) { - _flowAnalysis!.functionExpression_end(); - for (var element in _elementsWrittenToInLocalFunction!) { - previousElementsWrittenToInLocalFunction?.add(element); - previousPostDominatedLocals.removeFromAllScopes(element); - } - _elementsWrittenToInLocalFunction = - previousElementsWrittenToInLocalFunction; - } - _currentFunctionType = previousFunctionType; - _currentFieldFormals = previousFieldFormals; - _currentFunctionExpression = previousFunction; - _postDominatedLocals = previousPostDominatedLocals; - _currentExecutable = previousExecutable; - } - } - - @override - DecoratedType? visitFunctionExpressionInvocation( - FunctionExpressionInvocation node) { - final argumentList = node.argumentList; - final typeArguments = node.typeArguments; - _dispatch(typeArguments); - DecoratedType calleeType = _checkExpressionNotNull(node.function); - DecoratedType? result; - if (calleeType.type is FunctionType) { - result = _handleInvocationArguments(node, argumentList.arguments, - typeArguments, node.typeArgumentTypes, calleeType, null, - invokeType: node.staticInvokeType); - } else { - // Invocation of type `dynamic` or `Function`. - _dispatch(argumentList); - result = _makeNullableDynamicType(node); - } - return result; - } - - @override - DecoratedType? visitIfElement(IfElement node) { - _flowAnalysis!.ifStatement_conditionBegin(); - _checkExpressionNotNull(node.expression); - _flowAnalysis!.ifStatement_thenBegin(node.expression, node); - NullabilityNode? trueGuard; - NullabilityNode? falseGuard; - if (identical(_conditionInfo?.condition, node.expression)) { - trueGuard = _conditionInfo!.trueGuard; - falseGuard = _conditionInfo!.falseGuard; - _variables.recordConditionalDiscard(source, node, - ConditionalDiscard(trueGuard, falseGuard, _conditionInfo!.isPure)); - } - if (trueGuard != null) { - _guards.add(trueGuard); - } - try { - _postDominatedLocals.doScoped( - action: () => _handleCollectionElement(node.thenElement)); - } finally { - if (trueGuard != null) { - _guards.removeLast(); - } - } - if (node.elseElement != null) { - _flowAnalysis!.ifStatement_elseBegin(); - if (falseGuard != null) { - _guards.add(falseGuard); - } - try { - _postDominatedLocals.doScoped( - action: () => _handleCollectionElement(node.elseElement)); - } finally { - if (falseGuard != null) { - _guards.removeLast(); - } - } - } - _flowAnalysis!.ifStatement_end(node.elseElement != null); - return null; - } - - @override - DecoratedType? visitIfStatement(IfStatement node) { - _flowAnalysis!.ifStatement_conditionBegin(); - _checkExpressionNotNull(node.expression); - NullabilityNode? trueGuard; - NullabilityNode? falseGuard; - if (identical(_conditionInfo?.condition, node.expression)) { - trueGuard = _conditionInfo!.trueGuard; - falseGuard = _conditionInfo!.falseGuard; - _variables.recordConditionalDiscard(source, node, - ConditionalDiscard(trueGuard, falseGuard, _conditionInfo!.isPure)); - } - if (trueGuard != null) { - _guards.add(trueGuard); - } - try { - _flowAnalysis!.ifStatement_thenBegin(node.expression, node); - // We branched, so create a new scope for post-dominators. - _postDominatedLocals.doScoped( - action: () => _dispatch(node.thenStatement)); - } finally { - if (trueGuard != null) { - _guards.removeLast(); - } - } - if (falseGuard != null) { - _guards.add(falseGuard); - } - var elseStatement = node.elseStatement; - try { - if (elseStatement != null) { - _flowAnalysis!.ifStatement_elseBegin(); - // We branched, so create a new scope for post-dominators. - _postDominatedLocals.doScoped( - action: () => _dispatch(node.elseStatement)); - } - } finally { - _flowAnalysis!.ifStatement_end(elseStatement != null); - if (falseGuard != null) { - _guards.removeLast(); - } - } - return null; - } - - @override - DecoratedType? visitImplicitCallReference(ImplicitCallReference node) { - return _handlePropertyAccessGeneralized( - node: node, - target: node.expression, - propertyName: 'call', - isNullAware: false, - isCascaded: false, - inSetterContext: false, - callee: node.staticElement); - } - - @override - DecoratedType? visitIndexExpression(IndexExpression node) { - DecoratedType? targetType; - var target = node.target; - if (node.isCascaded) { - targetType = _currentCascadeTargetType; - } else if (target != null) { - targetType = _checkExpressionNotNull(target); - } - var callee = getWriteOrReadElement(node); - DecoratedType? result; - if (callee == null) { - // Dynamic dispatch. The return type is `dynamic`. - // TODO(paulberry): would it be better to assume a return type of `Never` - // so that we don't unnecessarily propagate nullabilities everywhere? - result = _makeNullableDynamicType(node); - } else { - var calleeType = getOrComputeElementType(node, callee, - targetType: targetType, targetExpression: target); - // TODO(paulberry): substitute if necessary - _handleAssignment(node.index, - destinationType: calleeType.positionalParameters![0]); - if (node.inSetterContext()) { - result = calleeType.positionalParameters![1]; - } else { - result = calleeType.returnType; - } - } - return result; - } - - @override - DecoratedType visitInstanceCreationExpression( - InstanceCreationExpression node) { - var callee = node.constructorName.staticElement!; - var typeParameters = callee.enclosingElement.typeParameters; - Iterable typeArgumentTypes; - List decoratedTypeArguments; - var typeArguments = node.constructorName.type.typeArguments; - late List parameterEdgeOrigins; - var target = - NullabilityNodeTarget.text('constructed type').withCodeRef(node); - if (typeArguments != null) { - _dispatch(typeArguments); - typeArgumentTypes = typeArguments.arguments.map((t) => t.type); - decoratedTypeArguments = typeArguments.arguments - .map((t) => _variables.decoratedTypeAnnotation(source, t)) - .toList(); - parameterEdgeOrigins = typeArguments.arguments - .map((typeAnn) => TypeParameterInstantiationOrigin(source, typeAnn)) - .toList(); - } else { - var staticType = node.staticType; - if (staticType is InterfaceType) { - typeArgumentTypes = staticType.typeArguments; - int index = 0; - decoratedTypeArguments = typeArgumentTypes.map((t) { - return DecoratedType.forImplicitType( - typeProvider, t, _graph, target.typeArgument(index++)); - }).toList(); - instrumentation?.implicitTypeArguments( - source, node, decoratedTypeArguments); - parameterEdgeOrigins = List.filled(typeArgumentTypes.length, - InferredTypeParameterInstantiationOrigin(source, node)); - } else { - // Note: this could happen if the code being migrated has errors. - typeArgumentTypes = const []; - decoratedTypeArguments = const []; - } - } - - if (node.staticType!.isDartCoreList && - callee.name == '' && - node.argumentList.arguments.length == 1) { - _graph.connect(_graph.always, decoratedTypeArguments[0].node, - ListLengthConstructorOrigin(source, node)); - } - - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullable( - nullabilityNode, InstanceCreationOrigin(source, node)); - var createdType = DecoratedType(node.staticType, nullabilityNode, - typeArguments: decoratedTypeArguments); - var calleeType = - getOrComputeElementType(node, callee, targetType: createdType); - for (var i = 0; i < decoratedTypeArguments.length; ++i) { - _checkAssignment(parameterEdgeOrigins.elementAt(i), - FixReasonTarget.root.typeArgument(i), - source: decoratedTypeArguments[i], - destination: - _variables.decoratedTypeParameterBound(typeParameters[i])!, - hard: true); - } - _handleInvocationArguments(node, node.argumentList.arguments, typeArguments, - typeArgumentTypes, calleeType, typeParameters); - return createdType; - } - - @override - DecoratedType visitIntegerLiteral(IntegerLiteral node) { - return _makeNonNullLiteralType(node); - } - - @override - DecoratedType visitIsExpression(IsExpression node) { - var expression = node.expression; - var expressionNode = _dispatch(expression)!.node; - var type = node.type; - _dispatch(type); - var decoratedType = _variables.decoratedTypeAnnotation(source, type); - // The main type of the is check historically could not be nullable. - // Making it nullable could change runtime behavior. - _graph.makeNonNullable( - decoratedType.node, IsCheckMainTypeOrigin(source, type)); - _conditionInfo = _ConditionInfo(node, - isPure: expression is SimpleIdentifier, - postDominatingIntent: _isReferenceInScope(expression), - trueDemonstratesNonNullIntent: expressionNode); - if (node.notOperator != null) { - _conditionInfo = _conditionInfo!.not(node); - } - if (!_assumeNonNullabilityInCasts) { - // TODO(mfairhurst): wire this to handleDowncast if we do not assume - // nullability. - assert(false); - } - _flowAnalysis!.isExpression_end( - node, expression, node.notOperator != null, decoratedType); - return _makeNonNullableBoolType(node); - } - - @override - DecoratedType? visitLabel(Label node) { - // Labels are identifiers but they don't have types so we don't need to - // visit them directly. - return null; - } - - @override - DecoratedType? visitLibraryDirective(LibraryDirective node) { - // skip directives, but not their metadata - _dispatchList(node.metadata); - return null; - } - - @override - DecoratedType visitListLiteral(ListLiteral node) { - final previousLiteralType = _currentLiteralElementType; - try { - var listType = node.staticType as InterfaceType?; - if (node.typeArguments == null) { - var target = - NullabilityNodeTarget.text('list element type').withCodeRef(node); - var elementType = DecoratedType.forImplicitType( - typeProvider, listType!.typeArguments[0], _graph, target); - instrumentation?.implicitTypeArguments(source, node, [elementType]); - _currentLiteralElementType = elementType; - } else { - _dispatch(node.typeArguments); - _currentLiteralElementType = _variables.decoratedTypeAnnotation( - source, node.typeArguments!.arguments[0]); - } - node.elements.forEach(_handleCollectionElement); - return _makeNonNullLiteralType(node, - typeArguments: [_currentLiteralElementType]); - } finally { - _currentLiteralElementType = previousLiteralType; - } - } - - @override - DecoratedType? visitMapLiteralEntry(MapLiteralEntry node) { - assert(_currentMapKeyType != null); - assert(_currentMapValueType != null); - _handleAssignment(node.key, destinationType: _currentMapKeyType); - _handleAssignment(node.value, destinationType: _currentMapValueType); - return null; - } - - @override - DecoratedType? visitMethodDeclaration(MethodDeclaration node) { - if (BuiltValueTransformer.findNullableAnnotation(node) != null) { - _graph.makeNullable( - _variables - .decoratedElementType(node.declaredElement!.declaration) - .returnType! - .node, - BuiltValueNullableOrigin(source, node)); - } - _handleExecutableDeclaration(node, node.declaredElement!, node.metadata, - node.returnType, node.parameters, null, node.body, null); - _dispatch(node.typeParameters); - return null; - } - - @override - DecoratedType? visitMethodInvocation(MethodInvocation node) { - DecoratedType? targetType; - var target = node.target; - bool isNullAware = node.isNullAware; - var callee = node.methodName.staticElement; - bool calleeIsStatic = callee is ExecutableElement && callee.isStatic; - _dispatch(node.typeArguments); - - if (node.isCascaded) { - targetType = _currentCascadeTargetType; - } else if (target != null) { - if (_isPrefix(target)) { - // Nothing to do. - } else if (calleeIsStatic) { - _dispatch(target); - } else if (isNullAware) { - targetType = _handleNullAwareTarget(target, node); - } else { - targetType = _handleTarget(target, node.methodName.name, callee); - } - } else if (target == null && callee!.enclosingElement is ClassElement) { - targetType = _thisOrSuper(node); - _checkThisNotNull(targetType, node); - } - DecoratedType? expressionType; - DecoratedType? calleeType; - if (targetType != null && - targetType.type is FunctionType && - node.methodName.name == 'call') { - // If `X` has a function type, then in the expression `X.call()`, the - // function being called is `X` itself, so the callee type is simply the - // type of `X`. - calleeType = targetType; - } else if (callee != null) { - calleeType = getOrComputeElementType(node, callee, - targetType: targetType, - targetExpression: target, - isNullAware: isNullAware); - if (callee is PropertyAccessorElement) { - calleeType = calleeType.returnType; - } - } - if (calleeType == null) { - // Dynamic dispatch. The return type is `dynamic`. - // TODO(paulberry): would it be better to assume a return type of `Never` - // so that we don't unnecessarily propagate nullabilities everywhere? - _dispatch(node.argumentList); - expressionType = _makeNullableDynamicType(node); - } else { - expressionType = _handleInvocationArguments( - node, - node.argumentList.arguments, - node.typeArguments, - node.typeArgumentTypes, - calleeType, - null, - invokeType: node.staticInvokeType); - // Do any deferred processing for this method invocation. - var deferredProcessing = _deferredMethodInvocationProcessing.remove(node); - if (deferredProcessing != null) { - expressionType = deferredProcessing(expressionType); - } - if (isNullAware) { - expressionType = expressionType!.withNode( - NullabilityNode.forLUB(targetType!.node, expressionType.node)); - } - _variables.recordDecoratedExpressionType(node, expressionType); - } - _handleCustomCheckNotNull(node); - _handleQuiverCheckNotNull(node); - return expressionType; - } - - @override - DecoratedType? visitMixinDeclaration(MixinDeclaration node) { - visitClassOrMixinOrExtensionDeclaration(node); - _dispatch(node.implementsClause); - _dispatch(node.onClause); - _dispatch(node.typeParameters); - return null; - } - - @override - DecoratedType? visitNamedType(NamedType node) { - try { - _typeNameNesting++; - var typeArguments = node.typeArguments?.arguments; - var element = node.element; - if (element is TypeAliasElement && - element.aliasedElement is GenericFunctionTypeElement) { - var aliasedElement = element.aliasedElement!; - final typedefType = _variables.decoratedElementType(aliasedElement); - final typeNameType = _variables.decoratedTypeAnnotation(source, node); - - Map substitutions; - if (node.typeArguments == null) { - // TODO(mfairhurst): substitute instantiations to bounds - substitutions = {}; - } else { - substitutions = - Map.fromIterables( - element.typeParameters, - node.typeArguments!.arguments.map( - (t) => _variables.decoratedTypeAnnotation(source, t))); - } - - final decoratedType = typedefType.substitute(substitutions); - final origin = TypedefReferenceOrigin(source, node); - _linkDecoratedTypeParameters(decoratedType, typeNameType, origin, - isUnion: true); - _linkDecoratedTypes( - decoratedType.returnType!, typeNameType.returnType, origin, - isUnion: true); - } else if (element is TypeParameterizedElement) { - if (typeArguments == null) { - var instantiatedType = - _variables.decoratedTypeAnnotation(source, node); - var origin = InstantiateToBoundsOrigin(source, node); - for (int i = 0; i < instantiatedType.typeArguments.length; i++) { - _linkDecoratedTypes( - instantiatedType.typeArguments[i]!, - _variables - .decoratedTypeParameterBound(element.typeParameters[i]), - origin, - isUnion: false); - } - } else { - for (int i = 0; i < typeArguments.length; i++) { - DecoratedType? bound; - bound = _variables - .decoratedTypeParameterBound(element.typeParameters[i]); - assert(bound != null); - var argumentType = - _variables.decoratedTypeAnnotation(source, typeArguments[i]); - _checkAssignment( - TypeParameterInstantiationOrigin(source, typeArguments[i]), - FixReasonTarget.root, - source: argumentType, - destination: bound!, - hard: true); - } - } - } - node.visitChildren(this); - // If the type name is followed by a `/*!*/` comment, it is considered to - // apply to the type and not to the "as" expression. In order to prevent - // a future call to _handleNullCheck from interpreting it as applying to - // the "as" expression, we need to store the `/*!*/` comment in - // _nullCheckHints. - var token = node.endToken; - _nullCheckHints[token] = getPostfixHint(token); - namedTypeVisited(node); // Note this has been visited to TypeNameTracker. - return null; - } finally { - _typeNameNesting--; - } - } - - @override - DecoratedType? visitNamespaceDirective(NamespaceDirective node) { - // skip directives, but not their metadata - _dispatchList(node.metadata); - return null; - } - - @override - DecoratedType? visitNode(AstNode node) { - for (var child in node.childEntities) { - if (child is AstNode) { - _dispatch(child); - } - } - return null; - } - - @override - DecoratedType visitNullLiteral(NullLiteral node) { - var target = NullabilityNodeTarget.text('null literal').withCodeRef(node); - var decoratedType = DecoratedType.forImplicitType( - typeProvider, node.staticType, _graph, target); - _flowAnalysis!.nullLiteral(node, decoratedType); - _graph.makeNullable(decoratedType.node, LiteralOrigin(source, node)); - return decoratedType; - } - - @override - DecoratedType? visitParenthesizedExpression(ParenthesizedExpression node) { - var result = _dispatch(node.expression); - _flowAnalysis!.parenthesizedExpression(node, node.expression); - return result; - } - - @override - DecoratedType? visitPartOfDirective(PartOfDirective node) { - // skip directives, but not their metadata - _dispatchList(node.metadata); - return null; - } - - @override - DecoratedType visitPostfixExpression(PostfixExpression node) { - if (node.operator.type.isIncrementOperator) { - var operand = node.operand; - var targetType = _checkExpressionNotNull(operand); - var callee = node.staticElement; - DecoratedType writeType; - if (callee == null) { - // Dynamic dispatch. The return type is `dynamic`. - // TODO(paulberry): would it be better to assume a return type of `Never` - // so that we don't unnecessarily propagate nullabilities everywhere? - writeType = _makeNullableDynamicType(node); - } else { - var calleeType = getOrComputeElementType(node, callee, - targetType: targetType, targetExpression: operand); - writeType = _fixNumericTypes(calleeType.returnType!, node.staticType); - } - if (operand is SimpleIdentifier) { - var element = getWriteOrReadElement(operand); - if (element is PromotableElement) { - _flowAnalysis!.write(node, element, writeType, null); - } - } - return targetType; - } - _unimplemented( - node, 'Postfix expression with operator ${node.operator.lexeme}'); - } - - @override - DecoratedType? visitPrefixedIdentifier(PrefixedIdentifier node) { - if (node.prefix.staticElement is LibraryImportElement) { - // TODO(paulberry) - _unimplemented(node, 'PrefixedIdentifier with a prefix'); - } else { - return _handlePropertyAccess( - node, node.prefix, node.identifier, false, false); - } - } - - @override - DecoratedType? visitPrefixExpression(PrefixExpression node) { - var operand = node.operand; - var targetType = _checkExpressionNotNull(operand); - var operatorType = node.operator.type; - if (operatorType == TokenType.BANG) { - _flowAnalysis!.logicalNot_end(node, operand); - return _makeNonNullableBoolType(node); - } else { - var callee = node.staticElement; - var isIncrementOrDecrement = operatorType.isIncrementOperator; - DecoratedType? staticType; - if (callee == null) { - // Dynamic dispatch. The return type is `dynamic`. - // TODO(paulberry): would it be better to assume a return type of `Never` - // so that we don't unnecessarily propagate nullabilities everywhere? - staticType = _makeNullableDynamicType(node); - } else { - var calleeType = getOrComputeElementType(node, callee, - targetType: targetType, targetExpression: operand); - if (isIncrementOrDecrement) { - staticType = - _fixNumericTypes(calleeType.returnType!, node.staticType); - } else { - staticType = _handleInvocationArguments( - node, [], null, null, calleeType, null); - } - } - if (isIncrementOrDecrement) { - if (operand is SimpleIdentifier) { - var element = getWriteOrReadElement(operand); - if (element is PromotableElement) { - _flowAnalysis!.write(node, element, staticType!, null); - } - } - } - return staticType; - } - } - - @override - DecoratedType? visitPropertyAccess(PropertyAccess node) { - return _handlePropertyAccess(node, node.target, node.propertyName, - node.isNullAware, node.isCascaded); - } - - @override - DecoratedType? visitRedirectingConstructorInvocation( - RedirectingConstructorInvocation node) { - var callee = node.staticElement!; - var calleeType = _variables.decoratedElementType(callee); - _handleInvocationArguments( - node, node.argumentList.arguments, null, null, calleeType, null); - return null; - } - - @override - DecoratedType visitRethrowExpression(RethrowExpression node) { - _flowAnalysis!.handleExit(); - var target = - NullabilityNodeTarget.text('rethrow expression').withCodeRef(node); - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullable(nullabilityNode, ThrowOrigin(source, node)); - return DecoratedType(node.staticType, nullabilityNode); - } - - @override - DecoratedType? visitReturnStatement(ReturnStatement node) { - DecoratedType? returnType = _currentFunctionType!.returnType; - Expression? returnValue = node.expression; - var functionBody = node.thisOrAncestorOfType()!; - if (functionBody.isGenerator) { - // Do not connect the return value to the return type. - return _dispatch(returnValue); - } - final isAsync = functionBody.isAsynchronous; - if (returnValue == null) { - var target = - NullabilityNodeTarget.text('implicit null return').withCodeRef(node); - var implicitNullType = DecoratedType.forImplicitType( - typeProvider, typeProvider.nullType, _graph, target); - var origin = ImplicitNullReturnOrigin(source, node); - _graph.makeNullable(implicitNullType.node, origin); - _checkAssignment(origin, FixReasonTarget.root, - source: - isAsync ? _futureOf(implicitNullType, node) : implicitNullType, - destination: returnType!, - hard: false); - } else { - _handleAssignment(returnValue, - destinationType: returnType, wrapFuture: isAsync); - } - - _flowAnalysis!.handleExit(); - // Later statements no longer post-dominate the declarations because we - // exited (or, in parent scopes, conditionally exited). - // TODO(mfairhurst): don't clear post-dominators beyond the current function. - _postDominatedLocals.clearEachScope(); - - return null; - } - - @override - DecoratedType visitSetOrMapLiteral(SetOrMapLiteral node) { - var setOrMapType = node.staticType as InterfaceType?; - var typeArguments = node.typeArguments?.arguments; - - if (node.isSet) { - final previousLiteralType = _currentLiteralElementType; - try { - if (typeArguments == null) { - assert(setOrMapType!.typeArguments.length == 1); - var target = - NullabilityNodeTarget.text('set element type').withCodeRef(node); - var elementType = DecoratedType.forImplicitType( - typeProvider, setOrMapType!.typeArguments[0], _graph, target); - instrumentation?.implicitTypeArguments(source, node, [elementType]); - _currentLiteralElementType = elementType; - } else { - assert(typeArguments.length == 1); - _dispatch(node.typeArguments); - _currentLiteralElementType = - _variables.decoratedTypeAnnotation(source, typeArguments[0]); - } - node.elements.forEach(_handleCollectionElement); - return _makeNonNullLiteralType(node, - typeArguments: [_currentLiteralElementType]); - } finally { - _currentLiteralElementType = previousLiteralType; - } - } else { - assert(node.isMap); - - final previousKeyType = _currentMapKeyType; - final previousValueType = _currentMapValueType; - try { - if (typeArguments == null) { - assert(setOrMapType!.typeArguments.length == 2); - var targetKey = - NullabilityNodeTarget.text('map key type').withCodeRef(node); - var keyType = DecoratedType.forImplicitType( - typeProvider, setOrMapType!.typeArguments[0], _graph, targetKey); - _currentMapKeyType = keyType; - var targetValue = - NullabilityNodeTarget.text('map value type').withCodeRef(node); - var valueType = DecoratedType.forImplicitType( - typeProvider, setOrMapType.typeArguments[1], _graph, targetValue); - _currentMapValueType = valueType; - instrumentation - ?.implicitTypeArguments(source, node, [keyType, valueType]); - } else { - assert(typeArguments.length == 2); - _dispatch(node.typeArguments); - _currentMapKeyType = - _variables.decoratedTypeAnnotation(source, typeArguments[0]); - _currentMapValueType = - _variables.decoratedTypeAnnotation(source, typeArguments[1]); - } - - node.elements.forEach(_handleCollectionElement); - return _makeNonNullLiteralType(node, - typeArguments: [_currentMapKeyType, _currentMapValueType]); - } finally { - _currentMapKeyType = previousKeyType; - _currentMapValueType = previousValueType; - } - } - } - - @override - DecoratedType? visitSimpleIdentifier(SimpleIdentifier node) { - DecoratedType? targetType; - DecoratedType? result; - var staticElement = _favorFieldFormalElements(getWriteOrReadElement(node)); - if (staticElement is PromotableElement) { - if (!node.inDeclarationContext()) { - var promotedType = _flowAnalysis!.variableRead(node, staticElement); - if (promotedType != null) return promotedType; - } - var type = getOrComputeElementType(node, staticElement); - if (!node.inDeclarationContext() && - node.inGetterContext() && - !_lateHintedLocals.contains(staticElement) && - !_flowAnalysis!.isAssigned(staticElement)) { - _graph.makeNullable(type.node, UninitializedReadOrigin(source, node)); - } - result = type; - } else if (staticElement is FunctionElement || - staticElement is MethodElement || - staticElement is ConstructorElement) { - if (staticElement!.enclosingElement is ClassElement) { - targetType = _thisOrSuper(node); - } - result = - getOrComputeElementType(node, staticElement, targetType: targetType); - } else if (staticElement is PropertyAccessorElement) { - if (staticElement.enclosingElement is ClassElement) { - targetType = _thisOrSuper(node); - } - var elementType = - getOrComputeElementType(node, staticElement, targetType: targetType); - result = staticElement.isGetter - ? elementType.returnType - : elementType.positionalParameters![0]; - } else if (staticElement is TypeDefiningElement) { - result = _makeNonNullLiteralType(node); - } else if (staticElement is ExtensionElement) { - result = _makeNonNullLiteralType(node); - } else if (staticElement == null) { - assert(node.toString() == 'void', "${node.toString()} != 'void'"); - result = _makeNullableVoidType(node); - } else if (staticElement.enclosingElement is ClassElement && - staticElement.enclosingElement is EnumElement) { - result = getOrComputeElementType(node, staticElement); - } else { - // TODO(paulberry) - _unimplemented(node, - 'Simple identifier with a static element of type ${staticElement.runtimeType}'); - } - if (targetType != null) { - _checkThisNotNull(targetType, node); - } - return result; - } - - @override - DecoratedType? visitSpreadElement(SpreadElement node) { - final spreadType = node.expression.staticType!; - DecoratedType? spreadTypeDecorated; - var target = - NullabilityNodeTarget.text('spread element type').withCodeRef(node); - if (_typeSystem.isSubtypeOf(spreadType, typeProvider.mapObjectObjectType)) { - assert(_currentMapKeyType != null && _currentMapValueType != null); - final expectedType = typeProvider.mapType( - _currentMapKeyType!.type!, _currentMapValueType!.type!); - final expectedDecoratedType = DecoratedType.forImplicitType( - typeProvider, expectedType, _graph, target, - typeArguments: [_currentMapKeyType, _currentMapValueType]); - - spreadTypeDecorated = _handleAssignment(node.expression, - destinationType: expectedDecoratedType); - } else if (_typeSystem.isSubtypeOf( - spreadType, typeProvider.iterableDynamicType)) { - assert(_currentLiteralElementType != null); - final expectedType = - typeProvider.iterableType(_currentLiteralElementType!.type!); - final expectedDecoratedType = DecoratedType.forImplicitType( - typeProvider, expectedType, _graph, target, - typeArguments: [_currentLiteralElementType]); - - spreadTypeDecorated = _handleAssignment(node.expression, - destinationType: expectedDecoratedType); - } else { - // Downcast. We can't assume nullability here, so do nothing. - } - - if (!node.isNullAware) { - _checkExpressionNotNull(node.expression, sourceType: spreadTypeDecorated); - } - - return null; - } - - @override - DecoratedType visitStringLiteral(StringLiteral node) { - node.visitChildren(this); - return _makeNonNullLiteralType(node); - } - - @override - DecoratedType? visitSuperConstructorInvocation( - SuperConstructorInvocation node) { - var callee = node.staticElement!; - var target = NullabilityNodeTarget.text('super constructor invocation') - .withCodeRef(node); - var nullabilityNode = NullabilityNode.forInferredType(target); - var class_ = node.thisOrAncestorOfType()!; - var decoratedSupertype = _decoratedClassHierarchy!.getDecoratedSupertype( - class_.declaredElement!, callee.enclosingElement); - var typeArguments = decoratedSupertype.typeArguments; - Iterable typeArgumentTypes; - typeArgumentTypes = typeArguments.map((t) => t!.type); - var createdType = DecoratedType(callee.returnType, nullabilityNode, - typeArguments: typeArguments); - var calleeType = - getOrComputeElementType(node, callee, targetType: createdType); - var constructorTypeParameters = callee.enclosingElement.typeParameters; - - _handleInvocationArguments( - node, - node.argumentList.arguments, - null /*typeArguments*/, - typeArgumentTypes, - calleeType, - constructorTypeParameters); - return null; - } - - @override - DecoratedType? visitSuperExpression(SuperExpression node) { - return _thisOrSuper(node); - } - - @override - DecoratedType? visitSwitchStatement(SwitchStatement node) { - var scrutineeType = _dispatch(node.expression)!; - _flowAnalysis! - .switchStatement_expressionEnd(node, node.expression, scrutineeType); - var hasDefault = false; - for (var member in node.members) { - _postDominatedLocals.doScoped(action: () { - var hasLabel = member.labels.isNotEmpty; - _flowAnalysis!.switchStatement_beginAlternatives(); - _flowAnalysis!.switchStatement_beginAlternative(); - _flowAnalysis!.constantPattern_end(node.expression, scrutineeType, - patternsEnabled: false); - _flowAnalysis!.switchStatement_endAlternative(null, {}); - _flowAnalysis! - .switchStatement_endAlternatives(node, hasLabels: hasLabel); - if (member is SwitchCase) { - _dispatch(member.expression); - } else { - hasDefault = true; - } - _dispatchList(member.statements); - _flowAnalysis!.switchStatement_afterCase(); - }); - } - _flowAnalysis!.switchStatement_end(hasDefault); - return null; - } - - @override - DecoratedType visitSymbolLiteral(SymbolLiteral node) { - return _makeNonNullLiteralType(node); - } - - @override - DecoratedType? visitThisExpression(ThisExpression node) { - return _thisOrSuper(node); - } - - @override - DecoratedType visitThrowExpression(ThrowExpression node) { - _dispatch(node.expression); - // TODO(paulberry): do we need to check the expression type? I think not. - _flowAnalysis!.handleExit(); - var target = - NullabilityNodeTarget.text('throw expression').withCodeRef(node); - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullable(nullabilityNode, ThrowOrigin(source, node)); - return DecoratedType(node.staticType, nullabilityNode); - } - - @override - DecoratedType? visitTryStatement(TryStatement node) { - var finallyBlock = node.finallyBlock; - if (finallyBlock != null) { - _flowAnalysis!.tryFinallyStatement_bodyBegin(); - } - var catchClauses = node.catchClauses; - if (catchClauses.isNotEmpty) { - _flowAnalysis!.tryCatchStatement_bodyBegin(); - } - var body = node.body; - _dispatch(body); - if (catchClauses.isNotEmpty) { - _flowAnalysis!.tryCatchStatement_bodyEnd(body); - _dispatchList(catchClauses); - _flowAnalysis!.tryCatchStatement_end(); - } - if (finallyBlock != null) { - _flowAnalysis!.tryFinallyStatement_finallyBegin( - catchClauses.isNotEmpty ? node : body); - _dispatch(finallyBlock); - _flowAnalysis!.tryFinallyStatement_end(); - } - return null; - } - - @override - DecoratedType? visitVariableDeclarationList(VariableDeclarationList node) { - var parent = node.parent; - bool isTopLevel = - parent is FieldDeclaration || parent is TopLevelVariableDeclaration; - _dispatchList(node.metadata); - _dispatch(node.type); - for (var variable in node.variables) { - _dispatchList(variable.metadata); - var initializer = variable.initializer; - var declaredElement = variable.declaredElement!; - if (isTopLevel) { - assert(_flowAnalysis == null); - _createFlowAnalysis(variable, null); - } else { - assert(_flowAnalysis != null); - if (declaredElement is PromotableElement && - _variables.getLateHint(source, node) != null) { - _lateHintedLocals.add(declaredElement); - } - } - var type = _variables.decoratedElementType(declaredElement); - var enclosingElement = declaredElement.enclosingElement; - if (!declaredElement.isStatic && enclosingElement is ClassElement) { - var overriddenElements = _inheritanceManager.getOverridden2( - enclosingElement, - Name(enclosingElement.library.source.uri, declaredElement.name)); - for (var overriddenElement - in overriddenElements ?? []) { - _handleFieldOverriddenDeclaration( - variable, type, enclosingElement, overriddenElement); - } - if (!declaredElement.isFinal) { - var overriddenElements = _inheritanceManager.getOverridden2( - enclosingElement, - Name(enclosingElement.library.source.uri, - '${declaredElement.name}=')); - for (var overriddenElement - in overriddenElements ?? []) { - _handleFieldOverriddenDeclaration( - variable, type, enclosingElement, overriddenElement); - } - } - } - try { - if (declaredElement is PromotableElement) { - _flowAnalysis!.declare( - declaredElement, _variables.decoratedElementType(declaredElement), - initialized: initializer != null); - } - if (initializer == null) { - // For top level variables and static fields, we have to generate an - // implicit assignment of `null`. For instance fields, this is done - // when processing constructors. For local variables, this is done - // when processing variable reads (only if flow analysis indicates - // the variable isn't definitely assigned). - if (isTopLevel && - _variables.getLateHint(source, node) == null && - !(declaredElement is FieldElement && !declaredElement.isStatic)) { - _graph.makeNullable( - type.node, ImplicitNullInitializerOrigin(source, node)); - } - } else { - _handleAssignment(initializer, destinationType: type); - } - if (isTopLevel) { - _flowAnalysis!.finish(); - } - } finally { - if (isTopLevel) { - _flowAnalysis = null; - _assignedVariables = null; - } - } - } - - // Track post-dominators, except we cannot make hard edges to multi - // declarations. Consider: - // - // int? x = null, y = 0; - // y.toDouble(); - // - // We cannot make a hard edge from y to never in this case. - if (node.variables.length == 1) { - _postDominatedLocals.add(node.variables.single.declaredElement!); - } - - return null; - } - - @override - DecoratedType? visitWhileStatement(WhileStatement node) { - // Note: we do not create guards. A null check here is *very* unlikely to be - // unnecessary after analysis. - _flowAnalysis!.whileStatement_conditionBegin(node); - _checkExpressionNotNull(node.condition); - _flowAnalysis!.whileStatement_bodyBegin(node, node.condition); - _postDominatedLocals.doScoped(action: () => _dispatch(node.body)); - _flowAnalysis!.whileStatement_end(); - return null; - } - - void _addParametersToFlowAnalysis(FormalParameterList? parameters) { - if (parameters != null) { - for (var parameter in parameters.parameters) { - var declaredElement = parameter.declaredElement!; - // TODO(paulberry): `skipDuplicateCheck` is currently needed to work - // around a failure in api_test.dart; fix this. - _flowAnalysis!.declare( - declaredElement, _variables.decoratedElementType(declaredElement), - initialized: true, skipDuplicateCheck: true); - } - } - } - - /// Visits [expression] and generates the appropriate edge to assert that its - /// value is non-null. - /// - /// Returns the decorated type of [expression]. - DecoratedType _checkExpressionNotNull(Expression expression, - {DecoratedType? sourceType}) { - if (_isPrefix(expression)) { - throw ArgumentError('cannot check non-nullability of a prefix'); - } - sourceType ??= _dispatch(expression); - if (sourceType == null) { - throw StateError('No type computed for ${expression.runtimeType} ' - '(${expression.toSource()}) offset=${expression.offset}'); - } - var origin = _makeEdgeOrigin(sourceType, expression); - var hard = _shouldUseHardEdge(expression); - var edge = _graph.makeNonNullable(sourceType.node, origin, - hard: hard, guards: _guards); - if (origin is ExpressionChecksOrigin) { - origin.checks.edges[FixReasonTarget.root] = edge; - } - return sourceType; - } - - /// Generates the appropriate edge to assert that the value of `this` is - /// non-null. - void _checkThisNotNull(DecoratedType? thisType, AstNode node) { - // `this` can only be `null` in extensions, so if we're not in an extension, - // there's nothing to do. - if (_currentExtendedType == null) return; - var origin = ImplicitThisOrigin(source, node); - var hard = _postDominatedLocals.isInScope(_extensionThis); - _graph.makeNonNullable(thisType!.node, origin, hard: hard, guards: _guards); - } - - /// Computes the map to be stored in [_currentFieldFormals] while visiting the - /// constructor having the given [constructorElement]. - Map - _computeFieldFormalMap(ConstructorElement constructorElement) { - var result = {}; - for (var parameter in constructorElement.parameters) { - if (parameter is FieldFormalParameterElement) { - var getter = parameter.field?.getter; - if (getter != null) { - result[getter] = parameter; - } - } - } - return result; - } - - @override - void _connect(NullabilityNode? source, NullabilityNode? destination, - EdgeOrigin origin, FixReasonTarget? edgeTarget, - {bool hard = false, bool checkable = true}) { - var edge = _graph.connect(source, destination!, origin, - hard: hard, checkable: checkable, guards: _guards); - if (origin is ExpressionChecksOrigin) { - origin.checks.edges[edgeTarget] = edge; - } - } - - void _createFlowAnalysis(Declaration node, FormalParameterList? parameters) { - assert(_flowAnalysis == null); - assert(_assignedVariables == null); - _assignedVariables = - FlowAnalysisHelper.computeAssignedVariables(node, parameters); - // Note: we are using flow analysis to help us track true nullabilities; - // it's not necessary to replicate old bugs. So we pass `true` for - // `respectImplicitlyTypedVarInitializers`. - _flowAnalysis = FlowAnalysis( - DecoratedTypeOperations(_typeSystem, typeProvider, _variables, _graph), - _assignedVariables!, - respectImplicitlyTypedVarInitializers: true, - fieldPromotionEnabled: false); - if (parameters != null) { - for (var parameter in parameters.parameters) { - var declaredElement = parameter.declaredElement!; - _flowAnalysis!.declare( - declaredElement, _variables.decoratedElementType(declaredElement), - initialized: true); - } - } - } - - /// Creates a type that can be used to check that an expression's value is - /// non-nullable. - DecoratedType _createNonNullableType(Expression expression) { - var target = - NullabilityNodeTarget.text('expression type').withCodeRef(expression); - // Note: it's not necessary for the type to precisely match the type of the - // expression, since all we are going to do is cause a single graph edge to - // be built; it is sufficient to pass in any decorated type whose node is - // non-nullable. So we use `Object`. - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullableUnion( - nullabilityNode, NonNullableUsageOrigin(source, expression)); - return DecoratedType(typeProvider.objectType, nullabilityNode); - } - - DecoratedType _decorateUpperOrLowerBound(AstNode astNode, DartType? type, - DecoratedType left, DecoratedType right, bool isLUB, - {NullabilityNode? node}) { - var leftType = left.type; - var rightType = right.type; - if (leftType is TypeParameterType && leftType != type) { - // We are "unwrapping" a type parameter type to its bound. - final typeParam = leftType.element; - return _decorateUpperOrLowerBound( - astNode, - type, - left.substitute( - {typeParam: _variables.decoratedTypeParameterBound(typeParam)!}), - right, - isLUB, - node: node); - } - if (rightType is TypeParameterType && rightType != type) { - // We are "unwrapping" a type parameter type to its bound. - final typeParam = rightType.element; - return _decorateUpperOrLowerBound( - astNode, - type, - left, - right.substitute( - {typeParam: _variables.decoratedTypeParameterBound(typeParam)!}), - isLUB, - node: node); - } - - node ??= isLUB - ? NullabilityNode.forLUB(left.node, right.node) - : _nullabilityNodeForGLB(astNode, left.node, right.node); - - if (type is DynamicType || type is VoidType) { - return DecoratedType(type, node); - } else if (leftType!.isBottom) { - return right.withNode(node); - } else if (rightType!.isBottom) { - return left.withNode(node); - } else if (type is InterfaceType) { - if (type.typeArguments.isEmpty) { - return DecoratedType(type, node); - } else { - if (leftType.isDartCoreNull) { - assert(isLUB, "shouldn't be possible to get C from GLB(null, S)"); - return DecoratedType(type, node, typeArguments: right.typeArguments); - } else if (rightType.isDartCoreNull) { - assert(isLUB, "shouldn't be possible to get C from GLB(S, null)"); - return DecoratedType(type, node, typeArguments: left.typeArguments); - } else if (leftType is InterfaceType && rightType is InterfaceType) { - List leftTypeArguments; - List rightTypeArguments; - if (isLUB) { - leftTypeArguments = _decoratedClassHierarchy! - .asInstanceOf(left, type.element) - .typeArguments; - rightTypeArguments = _decoratedClassHierarchy! - .asInstanceOf(right, type.element) - .typeArguments; - } else { - if (leftType.element != type.element || - rightType.element != type.element) { - _unimplemented(astNode, 'GLB with substitution'); - } - leftTypeArguments = left.typeArguments; - rightTypeArguments = right.typeArguments; - } - List newTypeArguments = []; - for (int i = 0; i < type.typeArguments.length; i++) { - newTypeArguments.add(_decorateUpperOrLowerBound( - astNode, - type.typeArguments[i], - leftTypeArguments[i]!, - rightTypeArguments[i]!, - isLUB)); - } - return DecoratedType(type, node, typeArguments: newTypeArguments); - } else { - _unimplemented( - astNode, - 'LUB/GLB with unexpected types: ${leftType.runtimeType}/' - '${rightType.runtimeType}'); - } - } - } else if (type is FunctionType) { - var leftType = left.type!; - var rightType = right.type; - if (leftType.isDartCoreNull) { - assert( - isLUB, "shouldn't be possible to get a function from GLB(null, S)"); - return DecoratedType(type, node, - returnType: right.returnType, - positionalParameters: right.positionalParameters, - namedParameters: right.namedParameters); - } else if (rightType!.isDartCoreNull) { - assert( - isLUB, "shouldn't be possible to get a function from GLB(S, null)"); - return DecoratedType(type, node, - returnType: left.returnType, - positionalParameters: left.positionalParameters, - namedParameters: left.namedParameters); - } - if (leftType is FunctionType && rightType is FunctionType) { - var returnType = _decorateUpperOrLowerBound(astNode, type.returnType, - left.returnType!, right.returnType!, isLUB); - List positionalParameters = []; - Map namedParameters = {}; - int positionalParameterCount = 0; - for (var parameter in type.parameters) { - DecoratedType? leftParameterType; - DecoratedType? rightParameterType; - if (parameter.isNamed) { - leftParameterType = left.namedParameters![parameter.name]; - rightParameterType = right.namedParameters![parameter.name]; - } else { - leftParameterType = - left.positionalParameters![positionalParameterCount]; - rightParameterType = - right.positionalParameters![positionalParameterCount]; - positionalParameterCount++; - } - var decoratedParameterType = _decorateUpperOrLowerBound(astNode, - parameter.type, leftParameterType!, rightParameterType!, !isLUB); - if (parameter.isNamed) { - namedParameters[parameter.name] = decoratedParameterType; - } else { - positionalParameters.add(decoratedParameterType); - } - } - return DecoratedType(type, node, - returnType: returnType, - positionalParameters: positionalParameters, - namedParameters: namedParameters); - } else { - _unimplemented( - astNode, - 'LUB/GLB with unexpected types: ${leftType.runtimeType}/' - '${rightType.runtimeType}'); - } - } else if (type is TypeParameterType) { - var leftType = left.type!; - var rightType = right.type; - if (leftType.isDartCoreNull || rightType!.isDartCoreNull) { - assert(isLUB, "shouldn't be possible to get T from GLB(null, S)"); - return DecoratedType(type, node); - } - - assert(leftType.element == type.element && - rightType.element == type.element); - return DecoratedType(type, node); - } - _unimplemented(astNode, '_decorateUpperOrLowerBound'); - } - - DecoratedType? _dispatch(AstNode? node, {bool skipNullCheckHint = false}) { - try { - var type = node?.accept(this); - if (!skipNullCheckHint && node is Expression) { - type = _handleNullCheckHint(node, type); - } - return type; - } catch (exception, stackTrace) { - if (listener != null) { - listener!.reportException(source, node, exception, stackTrace); - return null; - } else { - rethrow; - } - } - } - - void _dispatchList(List? nodeList) { - if (nodeList == null) return; - for (var node in nodeList) { - _dispatch(node); - } - } - - /// If the innermost enclosing executable is a constructor with field formal - /// parameters, and [staticElement] refers to the getter associated with one - /// of those fields, returns the corresponding field formal parameter element. - /// Otherwise returns [staticElement] unchanged. - /// - /// This allows us to treat null checks on the field as though they were null - /// checks on the field formal parameter, which is not strictly correct, but - /// tends to produce migrations that are more in line with user intent. - Element? _favorFieldFormalElements(Element? staticElement) { - if (staticElement is PropertyAccessorElement) { - var fieldFormal = _currentFieldFormals[staticElement]; - if (fieldFormal != null) { - return fieldFormal; - } - } - return staticElement; - } - - DecoratedType _fixNumericTypes( - DecoratedType decoratedType, DartType? undecoratedType) { - if (decoratedType.type!.isDartCoreNum && undecoratedType!.isDartCoreInt) { - // In a few cases the type computed by normal method lookup is `num`, - // but special rules kick in to cause the type to be `int` instead. If - // that is the case, we need to fix up the decorated type. - return DecoratedType(undecoratedType, decoratedType.node); - } else { - return decoratedType; - } - } - - DecoratedType _futureOf(DecoratedType type, AstNode node) => - DecoratedType.forImplicitType( - typeProvider, - typeProvider.futureType(type.type!), - _graph, - NullabilityNodeTarget.text('implicit future').withCodeRef(node), - typeArguments: [type]); - - @override - DecoratedType? _getCallMethodType(DecoratedType type) { - var typeType = type.type; - if (typeType is InterfaceType) { - var callMethod = typeType.lookUpMethod2('call', _library); - if (callMethod != null) { - return _variables - .decoratedElementType(callMethod.declaration) - .substitute(type.asSubstitution); - } - } - return null; - } - - @override - DecoratedType? _getTypeParameterTypeBound(DecoratedType type) { - // TODO(paulberry): once we've wired up flow analysis, return promoted - // bounds if applicable. - return _variables - .decoratedTypeParameterBound((type.type as TypeParameterType).element); - } - - /// Creates the necessary constraint(s) for an assignment of the given - /// [expression] to a destination whose type is [destinationType]. - /// - /// Optionally, the caller may supply an [assignmentExpression] instead of - /// [destinationType]. In this case, then the type comes from visiting the - /// LHS of the assignment expression. If the LHS of the assignment expression - /// refers to a local variable, we mark it as assigned in flow analysis at the - /// proper time. - /// - /// Set [wrapFuture] to true to handle assigning Future to R. - DecoratedType? _handleAssignment(Expression expression, - {DecoratedType? destinationType, - AssignmentExpression? assignmentExpression, - AssignmentExpression? compoundOperatorInfo, - AssignmentExpression? questionAssignNode, - bool fromDefaultValue = false, - bool wrapFuture = false, - bool sourceIsSetupCall = false, - bool isInjectorGetAssignment = false}) { - assert( - (assignmentExpression == null) != (destinationType == null), - 'Either assignmentExpression or destinationType should be supplied, ' - 'but not both'); - PromotableElement? destinationLocalVariable; - if (destinationType == null) { - var destinationExpression = assignmentExpression!.leftHandSide; - if (destinationExpression is SimpleIdentifier) { - var element = getWriteOrReadElement(destinationExpression); - if (element is PromotableElement) { - destinationLocalVariable = element; - } - } - if (destinationLocalVariable != null) { - _dispatch(destinationExpression); - destinationType = getOrComputeElementType( - destinationExpression, destinationLocalVariable); - } else { - destinationType = _dispatch(destinationExpression); - } - } - if (sourceIsSetupCall && - isInjectorGetAssignment && - destinationType != null) { - _graph.makeNonNullable( - destinationType.node, - AssignmentFromAngularInjectorGetOrigin( - source, assignmentExpression!.leftHandSide as SimpleIdentifier, - isSetupAssignment: sourceIsSetupCall)); - } - - if (questionAssignNode != null) { - _guards.add(destinationType!.node); - _flowAnalysis!.ifNullExpression_rightBegin( - questionAssignNode.leftHandSide, destinationType); - } - DecoratedType? sourceType; - try { - sourceType = _dispatch(expression); - if (wrapFuture) { - sourceType = _wrapFuture(sourceType!, expression); - } - if (sourceType == null) { - throw StateError('No type computed for ${expression.runtimeType} ' - '(${expression.toSource()}) offset=${expression.offset}'); - } - EdgeOrigin edgeOrigin = _makeEdgeOrigin(sourceType, expression, - isSetupAssignment: sourceIsSetupCall); - if (compoundOperatorInfo != null) { - var compoundOperatorMethod = compoundOperatorInfo.staticElement; - if (compoundOperatorMethod != null) { - _checkAssignment( - CompoundAssignmentOrigin(source, compoundOperatorInfo), - FixReasonTarget.root, - source: destinationType!, - destination: _createNonNullableType(compoundOperatorInfo), - hard: _shouldUseHardEdge(assignmentExpression!.leftHandSide)); - DecoratedType compoundOperatorType = getOrComputeElementType( - compoundOperatorInfo, compoundOperatorMethod, - targetType: destinationType, - targetExpression: compoundOperatorInfo.leftHandSide); - assert(compoundOperatorType.positionalParameters!.isNotEmpty); - _checkAssignment(edgeOrigin, FixReasonTarget.root, - source: sourceType, - destination: compoundOperatorType.positionalParameters![0], - hard: _shouldUseHardEdge(expression), - sourceIsFunctionLiteral: expression is FunctionExpression); - sourceType = _fixNumericTypes(compoundOperatorType.returnType!, - compoundOperatorInfo.staticType); - _checkAssignment( - CompoundAssignmentOrigin(source, compoundOperatorInfo), - FixReasonTarget.root, - source: sourceType, - destination: destinationType, - hard: false); - } else { - sourceType = _makeNullableDynamicType(compoundOperatorInfo); - } - } else { - if (_tryTransformOrElse(expression, sourceType) || - _tryTransformWhere( - expression, edgeOrigin, sourceType, destinationType!)) { - // Nothing further to do. - } else { - var hard = _shouldUseHardEdge(expression, - isConditionallyExecuted: questionAssignNode != null); - _checkAssignment(edgeOrigin, FixReasonTarget.root, - source: sourceType, - destination: destinationType, - hard: hard, - sourceIsFunctionLiteral: expression is FunctionExpression); - } - } - if (destinationLocalVariable != null) { - _flowAnalysis!.write(assignmentExpression!, destinationLocalVariable, - sourceType, compoundOperatorInfo == null ? expression : null); - } - if (questionAssignNode != null) { - _flowAnalysis!.ifNullExpression_end(); - // a ??= b is only nullable if both a and b are nullable. - sourceType = destinationType!.withNode(_nullabilityNodeForGLB( - questionAssignNode, sourceType.node, destinationType.node)); - _variables.recordDecoratedExpressionType( - questionAssignNode, sourceType); - } - } finally { - if (questionAssignNode != null) { - _guards.removeLast(); - } - } - if (assignmentExpression != null) { - var element = _referencedElement(assignmentExpression.leftHandSide); - if (element != null) { - _postDominatedLocals.removeFromAllScopes(element); - _elementsWrittenToInLocalFunction?.add(element); - } - } - return sourceType; - } - - DecoratedType? _handleCollectionElement(CollectionElement? element) { - if (element is Expression) { - assert(_currentLiteralElementType != null); - return _handleAssignment(element, - destinationType: _currentLiteralElementType); - } else { - return _dispatch(element); - } - } - - void _handleConstructorRedirection( - FormalParameterList parameters, ConstructorName redirectedConstructor) { - var callee = redirectedConstructor.staticElement!.declaration; - var redirectedClass = callee.enclosingElement; - var calleeType = _variables.decoratedElementType(callee); - var typeArguments = redirectedConstructor.type.typeArguments; - var typeArgumentTypes = - typeArguments?.arguments.map((t) => t.type).toList(); - _handleInvocationArguments( - redirectedConstructor, - parameters.parameters, - typeArguments, - typeArgumentTypes, - calleeType, - redirectedClass.typeParameters); - } - - void _handleCustomCheckNotNull(MethodInvocation node) { - var callee = node.methodName.staticElement; - if (node.argumentList.arguments.isNotEmpty && - callee is ExecutableElement && - callee.isStatic) { - var enclosingElement = callee.enclosingElement; - if (enclosingElement is ClassElement) { - if (callee.name == 'checkNotNull' && - enclosingElement.name == 'ArgumentError' && - callee.library.isDartCore || - callee.name == 'checkNotNull' && - enclosingElement.name == 'BuiltValueNullFieldError' && - callee.library.source.uri.toString() == - 'package:built_value/built_value.dart') { - var argument = node.argumentList.arguments.first; - if (argument is SimpleIdentifier && _isReferenceInScope(argument)) { - var argumentType = _variables.decoratedElementType( - _favorFieldFormalElements(getWriteOrReadElement(argument))!); - _graph.makeNonNullable(argumentType.node, - ArgumentErrorCheckNotNullOrigin(source, argument)); - } - } - } - } - } - - void _handleExecutableDeclaration( - Declaration node, - ExecutableElement declaredElement, - NodeList metadata, - TypeAnnotation? returnType, - FormalParameterList? parameters, - NodeList? initializers, - FunctionBody body, - ConstructorName? redirectedConstructor) { - assert(_currentFunctionType == null); - assert(_currentFieldFormals.isEmpty); - assert(_currentExecutable == null); - _dispatchList(metadata); - _dispatch(returnType); - _createFlowAnalysis(node, parameters); - _dispatch(parameters); - - // Be over conservative with public methods' arguments: - // Unless we have reasons for non-nullability, assume they are nullable. - // Soft edge to `always` node does exactly this. - bool isOverride = false; - final thisClass = declaredElement.enclosingElement; - if (thisClass is InterfaceElement) { - final name = Name(thisClass.library.source.uri, declaredElement.name); - isOverride = _inheritanceManager.getOverridden2(thisClass, name) != null; - } - if (!isOverride && - declaredElement.isPublic && - declaredElement is! PropertyAccessorElement && - // operator == treats `null` specially. - !(declaredElement.isOperator && declaredElement.name == '==')) { - _makeArgumentsNullable(declaredElement, node); - } - _currentFunctionType = _variables.decoratedElementType(declaredElement); - _currentFieldFormals = declaredElement is ConstructorElement - ? _computeFieldFormalMap(declaredElement) - : const {}; - _currentExecutable = declaredElement; - _addParametersToFlowAnalysis(parameters); - // Push a scope of post-dominated declarations on the stack. - _postDominatedLocals.pushScope(elements: declaredElement.parameters); - if (declaredElement.enclosingElement is ExtensionElement) { - _postDominatedLocals.add(_extensionThis); - } - try { - _dispatchList(initializers); - if (declaredElement is ConstructorElement && - !declaredElement.isFactory && - declaredElement.redirectedConstructor == null) { - _handleUninitializedFields(node, _fieldsNotInitializedByConstructor!); - } - _dispatch(body); - if (redirectedConstructor != null) { - _handleConstructorRedirection(parameters!, redirectedConstructor); - } - if (declaredElement is! ConstructorElement) { - var enclosingElement = declaredElement.enclosingElement; - if (enclosingElement is ClassElement) { - var overriddenElements = _inheritanceManager.getOverridden2( - enclosingElement, - Name(enclosingElement.library.source.uri, declaredElement.name)); - for (var overriddenElement - in overriddenElements ?? []) { - _handleExecutableOverriddenDeclaration(node, returnType, parameters, - enclosingElement, overriddenElement); - } - if (declaredElement is PropertyAccessorElement) { - if (declaredElement.isGetter) { - var setters = [declaredElement.correspondingSetter]; - if (setters[0] == null && !declaredElement.isStatic) { - // No corresponding setter in this class; look for inherited - // setters. - var getterName = declaredElement.name; - var setterName = '$getterName='; - var inheritedMembers = _inheritanceManager.getOverridden2( - enclosingElement, - Name(enclosingElement.library.source.uri, setterName)); - if (inheritedMembers != null) { - setters = [ - for (var setter in inheritedMembers) - if (setter is PropertyAccessorElement) setter - ]; - } - } - for (var setter in setters) { - if (setter != null) { - _handleGetterSetterCorrespondence( - node, - declaredElement.isStatic ? null : enclosingElement, - declaredElement, - setter.declaration); - } - } - } else { - assert(declaredElement.isSetter); - assert(declaredElement.name.endsWith('=')); - var getters = [declaredElement.correspondingGetter]; - if (getters[0] == null && !declaredElement.isStatic) { - // No corresponding getter in this class; look for inherited - // getters. - var setterName = declaredElement.name; - var getterName = setterName.substring(0, setterName.length - 1); - var inheritedMembers = _inheritanceManager.getOverridden2( - enclosingElement, - Name(enclosingElement.library.source.uri, getterName)); - if (inheritedMembers != null) { - getters = [ - for (var getter in inheritedMembers) - if (getter is PropertyAccessorElement) getter - ]; - } - } - for (var getter in getters) { - if (getter != null) { - _handleGetterSetterCorrespondence( - node, - declaredElement.isStatic ? null : enclosingElement, - getter.declaration, - declaredElement); - } - } - } - } - } - } - _flowAnalysis!.finish(); - } finally { - _flowAnalysis = null; - _assignedVariables = null; - _currentFunctionType = null; - _currentFieldFormals = const {}; - _postDominatedLocals.popScope(); - _currentExecutable = null; - } - } - - void _handleExecutableOverriddenDeclaration( - Declaration node, - TypeAnnotation? returnType, - FormalParameterList? parameters, - ClassElement classElement, - Element overriddenElement) { - overriddenElement = overriddenElement.declaration!; - var overriddenClass = - overriddenElement.enclosingElement as InterfaceElement; - var decoratedSupertype = _decoratedClassHierarchy! - .getDecoratedSupertype(classElement, overriddenClass); - var substitution = decoratedSupertype.asSubstitution; - if (overriddenElement is PropertyAccessorElement && - overriddenElement.isSynthetic) { - assert(node is MethodDeclaration); - var method = node as MethodDeclaration; - var decoratedOverriddenField = - _variables.decoratedElementType(overriddenElement.variable); - var overriddenFieldType = - decoratedOverriddenField.substitute(substitution); - if (method.isGetter) { - _checkAssignment( - ReturnTypeInheritanceOrigin(source, node), FixReasonTarget.root, - source: _currentFunctionType!.returnType!, - destination: overriddenFieldType, - hard: true); - } else { - assert(method.isSetter); - DecoratedType currentParameterType = - _currentFunctionType!.positionalParameters!.single; - DecoratedType overriddenParameterType = overriddenFieldType; - _checkAssignment( - ParameterInheritanceOrigin(source, node), FixReasonTarget.root, - source: overriddenParameterType, - destination: currentParameterType, - hard: true); - } - } else { - var decoratedOverriddenFunctionType = - _variables.decoratedElementType(overriddenElement); - var overriddenFunctionType = - decoratedOverriddenFunctionType.substitute(substitution); - if (returnType == null) { - _linkDecoratedTypes( - _currentFunctionType!.returnType!, - overriddenFunctionType.returnType, - ReturnTypeInheritanceOrigin(source, node), - isUnion: false); - } else { - _checkAssignment( - ReturnTypeInheritanceOrigin(source, node), FixReasonTarget.root, - source: _currentFunctionType!.returnType!, - destination: overriddenFunctionType.returnType!, - hard: true); - } - if (parameters != null) { - int positionalParameterCount = 0; - for (var parameter in parameters.parameters) { - NormalFormalParameter normalParameter; - if (parameter is NormalFormalParameter) { - normalParameter = parameter; - } else { - normalParameter = (parameter as DefaultFormalParameter).parameter; - } - DecoratedType? currentParameterType; - DecoratedType? overriddenParameterType; - if (parameter.isNamed) { - var name = normalParameter.name!.lexeme; - currentParameterType = _currentFunctionType!.namedParameters![name]; - overriddenParameterType = - overriddenFunctionType.namedParameters![name]; - } else { - if (positionalParameterCount < - _currentFunctionType!.positionalParameters!.length) { - currentParameterType = _currentFunctionType! - .positionalParameters![positionalParameterCount]; - } - if (positionalParameterCount < - overriddenFunctionType.positionalParameters!.length) { - overriddenParameterType = overriddenFunctionType - .positionalParameters![positionalParameterCount]; - } - positionalParameterCount++; - } - if (overriddenParameterType != null) { - var origin = ParameterInheritanceOrigin(source, node); - if (_isUntypedParameter(normalParameter)) { - _linkDecoratedTypes( - overriddenParameterType, currentParameterType, origin, - isUnion: false); - } else { - _checkAssignment(origin, FixReasonTarget.root, - source: overriddenParameterType, - destination: currentParameterType!, - hard: false, - checkable: false); - } - } - } - } - } - } - - void _handleFieldOverriddenDeclaration( - VariableDeclaration node, - DecoratedType type, - ClassElement classElement, - Element overriddenElement) { - overriddenElement = overriddenElement.declaration!; - var overriddenClass = - overriddenElement.enclosingElement as InterfaceElement; - var decoratedSupertype = _decoratedClassHierarchy! - .getDecoratedSupertype(classElement, overriddenClass); - var substitution = decoratedSupertype.asSubstitution; - if (overriddenElement is PropertyAccessorElement) { - DecoratedType? unsubstitutedOverriddenType; - if (overriddenElement.isSynthetic) { - unsubstitutedOverriddenType = - _variables.decoratedElementType(overriddenElement.variable); - } else { - if (overriddenElement.isGetter) { - unsubstitutedOverriddenType = - _variables.decoratedElementType(overriddenElement).returnType; - } else { - unsubstitutedOverriddenType = _variables - .decoratedElementType(overriddenElement) - .positionalParameters![0]; - } - } - var overriddenType = - unsubstitutedOverriddenType!.substitute(substitution); - if (overriddenElement.isGetter) { - _checkAssignment( - ReturnTypeInheritanceOrigin(source, node), FixReasonTarget.root, - source: type, destination: overriddenType, hard: true); - } else { - assert(overriddenElement.isSetter); - _checkAssignment( - ParameterInheritanceOrigin(source, node), FixReasonTarget.root, - source: overriddenType, destination: type, hard: true); - } - } else { - assert(false, 'Field overrides non-property-accessor'); - } - } - - void _handleForLoopParts(AstNode node, ForLoopParts parts, AstNode body, - DecoratedType? Function(AstNode) bodyHandler) { - if (parts is ForParts) { - if (parts is ForPartsWithDeclarations) { - _dispatch(parts.variables); - } else if (parts is ForPartsWithExpression) { - var initializationType = _dispatch(parts.initialization); - if (initializationType != null) { - _graph.connectDummy( - initializationType.node, DummyOrigin(source, parts)); - } - } - _flowAnalysis!.for_conditionBegin(node); - if (parts.condition != null) { - _checkExpressionNotNull(parts.condition!); - } - _flowAnalysis! - .for_bodyBegin(node is Statement ? node : null, parts.condition); - } else if (parts is ForEachParts) { - Element? lhsElement; - DecoratedType? lhsType; - if (parts is ForEachPartsWithDeclaration) { - var variableElement = parts.loopVariable.declaredElement!; - _flowAnalysis!.declare( - variableElement, _variables.decoratedElementType(variableElement), - initialized: true); - lhsElement = variableElement; - _dispatch(parts.loopVariable.type); - lhsType = _variables.decoratedElementType(lhsElement); - } else if (parts is ForEachPartsWithIdentifier) { - lhsElement = parts.identifier.staticElement; - lhsType = _dispatch(parts.identifier); - } else { - throw StateError( - 'Unexpected ForEachParts subtype: ${parts.runtimeType}'); - } - var iterableType = _checkExpressionNotNull(parts.iterable); - DecoratedType? elementType; - if (lhsType != null) { - var iterableTypeType = iterableType.type!; - if (_typeSystem.isSubtypeOf( - iterableTypeType, typeProvider.iterableDynamicType)) { - elementType = _decoratedClassHierarchy! - .asInstanceOf(iterableType, typeProvider.iterableElement) - .typeArguments[0]; - _checkAssignment( - ForEachVariableOrigin(source, parts), FixReasonTarget.root, - source: elementType!, destination: lhsType, hard: false); - } - } - _flowAnalysis!.forEach_bodyBegin(node); - if (lhsElement is PromotableElement) { - _flowAnalysis!.write(node, lhsElement, - elementType ?? _makeNullableDynamicType(node), null); - } - } - - // The condition may fail/iterable may be empty, so the body gets a new - // post-dominator scope. - _postDominatedLocals.doScoped(action: () { - bodyHandler(body); - - if (parts is ForParts) { - _flowAnalysis!.for_updaterBegin(); - for (var updater in parts.updaters) { - var updaterType = _dispatch(updater)!; - _graph.connectDummy(updaterType.node, DummyOrigin(source, updater)); - } - _flowAnalysis!.for_end(); - } else { - _flowAnalysis!.forEach_end(); - } - }); - } - - void _handleGetterSetterCorrespondence(Declaration node, ClassElement? class_, - PropertyAccessorElement getter, PropertyAccessorElement setter) { - DecoratedType? getType; - if (getter.isSynthetic) { - var field = getter.variable; - if (field.isSynthetic) return; - getType = _variables.decoratedElementType(field); - } else { - getType = _variables.decoratedElementType(getter).returnType; - } - DecoratedType? setType; - if (setter.isSynthetic) { - var field = setter.variable; - if (field.isSynthetic) return; - setType = _variables.decoratedElementType(field); - } else { - setType = - _variables.decoratedElementType(setter).positionalParameters!.single; - } - Map getterSubstitution = const {}; - Map setterSubstitution = const {}; - if (class_ != null) { - var getterClass = getter.enclosingElement as InterfaceElement; - if (!identical(class_, getterClass)) { - getterSubstitution = _decoratedClassHierarchy! - .getDecoratedSupertype(class_, getterClass) - .asSubstitution; - } - var setterClass = setter.enclosingElement as InterfaceElement; - if (!identical(class_, setterClass)) { - setterSubstitution = _decoratedClassHierarchy! - .getDecoratedSupertype(class_, setterClass) - .asSubstitution; - } - } - _checkAssignment( - GetterSetterCorrespondenceOrigin(source, node), FixReasonTarget.root, - source: getType!.substitute(getterSubstitution), - destination: setType.substitute(setterSubstitution), - hard: true); - } - - /// Instantiate [type] with [argumentTypes], assigning [argumentTypes] to - /// [bounds]. - DecoratedType _handleInstantiation(DecoratedType type, - List argumentTypes, List edgeOrigins) { - for (var i = 0; i < argumentTypes.length; ++i) { - _checkAssignment( - edgeOrigins.elementAt(i), FixReasonTarget.root.typeArgument(i), - source: argumentTypes[i], - destination: DecoratedTypeParameterBounds.current! - .get((type.type as FunctionType).typeFormals[i])!, - hard: true); - } - - return type.instantiate(argumentTypes); - } - - /// Creates the necessary constraint(s) for an [ArgumentList] when invoking an - /// executable element whose type is [calleeType]. - /// - /// Only pass [typeArguments] or [typeArgumentTypes] depending on the use - /// case; only one will be used. - /// - /// Returns the decorated return type of the invocation, after any necessary - /// substitutions. - DecoratedType? _handleInvocationArguments( - AstNode node, - Iterable arguments, - TypeArgumentList? typeArguments, - Iterable? typeArgumentTypes, - DecoratedType calleeType, - List? constructorTypeParameters, - {DartType? invokeType}) { - var typeFormals = constructorTypeParameters ?? calleeType.typeFormals!; - var target = NullabilityNodeTarget.text('invocation').withCodeRef(node); - if (typeFormals.isNotEmpty) { - if (typeArguments != null) { - var argumentTypes = typeArguments.arguments - .map((t) => _variables.decoratedTypeAnnotation(source, t)) - .toList(); - var origins = typeArguments.arguments - .map((typeAnnotation) => - TypeParameterInstantiationOrigin(source, typeAnnotation)) - .toList(); - if (constructorTypeParameters != null) { - calleeType = calleeType.substitute( - Map.fromIterables( - constructorTypeParameters, argumentTypes)); - } else { - calleeType = _handleInstantiation(calleeType, argumentTypes, origins); - } - } else { - if (invokeType is FunctionType) { - var argumentTypes = typeArgumentTypes! - .map((argType) => DecoratedType.forImplicitType( - typeProvider, argType, _graph, target)) - .toList(); - instrumentation?.implicitTypeArguments(source, node, argumentTypes); - calleeType = _handleInstantiation( - calleeType, - argumentTypes, - List.filled(argumentTypes.length, - InferredTypeParameterInstantiationOrigin(source, node))); - } else if (constructorTypeParameters != null) { - // No need to instantiate; caller has already substituted in the - // correct type arguments. - } else { - assert( - false, - 'invoke type should be a non-null function type, or ' - 'dynamic/Function, which have no type arguments. ' - '(got $invokeType)'); - } - } - } - int i = 0; - var suppliedNamedParameters = {}; - for (var argument in arguments) { - String? name; - Expression expression; - if (argument is NamedExpression) { - name = argument.name.label.name; - expression = argument.expression; - } else if (argument is FormalParameter) { - if (argument.isNamed) { - name = argument.name!.lexeme; - } - // TODO(scheglov) This is a hack. - expression = (argument as FormalParameterImpl).identifierForMigration!; - } else { - expression = argument as Expression; - } - DecoratedType? parameterType; - if (name != null) { - parameterType = calleeType.namedParameters![name]; - if (parameterType == null) { - // TODO(paulberry) - _unimplemented(expression, 'Missing type for named parameter'); - } - suppliedNamedParameters.add(name); - } else { - if (calleeType.positionalParameters!.length <= i) { - // TODO(paulberry) - _unimplemented(node, 'Missing positional parameter at $i'); - } - parameterType = calleeType.positionalParameters![i++]; - } - _handleAssignment(expression, destinationType: parameterType); - } - // Any parameters not supplied must be optional. - for (var entry in calleeType.namedParameters!.entries) { - if (suppliedNamedParameters.contains(entry.key)) continue; - entry.value.node.recordNamedParameterNotSupplied( - _guards, _graph, NamedParameterNotSuppliedOrigin(source, node)); - } - return calleeType.returnType; - } - - DecoratedType? _handleNullAwareTarget(Expression? target, Expression node) { - var targetType = _dispatch(target); - if (target is SimpleIdentifier) { - var targetElement = target.staticElement; - if (targetElement is ParameterElement && - targetElement.enclosingElement == _currentExecutable && - !_currentExecutable!.name.startsWith('_')) { - _graph.makeNullable(_variables.decoratedElementType(targetElement).node, - NullAwareAccessOrigin(source, node)); - } - } - return targetType; - } - - DecoratedType? _handleNullCheckHint( - Expression expression, DecoratedType? type) { - // Sometimes we think we're looking at an expression but we're really not - // because we're inside a type name. If this happens, ignore trailing - // `/*!*/`s because they're not expression null check hints, they're type - // non-nullability hints (which are handled by NodeBuilder). - if (_typeNameNesting > 0) return type; - var token = expression.endToken; - if (_nullCheckHints.containsKey(token)) { - // Already visited this location. - return type; - } - var hint = _nullCheckHints[token] = getPostfixHint(token); - if (hint != null && hint.kind == HintCommentKind.bang) { - _variables.recordNullCheckHint(source, expression, hint); - return type!.withNode(_graph.never); - } else { - return type; - } - } - - DecoratedType? _handlePropertyAccess(Expression node, Expression? target, - SimpleIdentifier propertyName, bool isNullAware, bool isCascaded) { - if (!isCascaded && _isPrefix(target)) { - return _dispatch(propertyName, skipNullCheckHint: true); - } - var callee = getWriteOrReadElement(propertyName); - return _handlePropertyAccessGeneralized( - node: node, - target: target, - propertyName: propertyName.name, - isNullAware: isNullAware, - isCascaded: isCascaded, - inSetterContext: propertyName.inSetterContext(), - callee: callee); - } - - DecoratedType? _handlePropertyAccessGeneralized( - {required Expression node, - required Expression? target, - required String propertyName, - required bool isNullAware, - required bool isCascaded, - required bool inSetterContext, - required Element? callee}) { - DecoratedType? targetType; - bool calleeIsStatic = callee is ExecutableElement && callee.isStatic; - if (isCascaded) { - targetType = _currentCascadeTargetType; - } else if (calleeIsStatic) { - _dispatch(target); - } else if (isNullAware) { - targetType = _handleNullAwareTarget(target, node); - } else { - targetType = _handleTarget(target, propertyName, callee); - } - DecoratedType? calleeType; - if (targetType != null && - targetType.type is FunctionType && - propertyName == 'call') { - // If `X` has a function type, then in the expression `X.call`, the - // function being torn off is `X` itself, so the callee type is simply the - // non-nullable counterpart to the type of `X`. - var nullabilityNodeTarget = - NullabilityNodeTarget.text('expression').withCodeRef(node); - var nullabilityNode = - NullabilityNode.forInferredType(nullabilityNodeTarget); - _graph.makeNonNullableUnion( - nullabilityNode, CallTearOffOrigin(source, node)); - calleeType = targetType.withNode(nullabilityNode); - } else if (callee != null) { - calleeType = getOrComputeElementType(node, callee, - targetType: targetType, targetExpression: target); - } - if (calleeType == null) { - // Dynamic dispatch. - return _makeNullableDynamicType(node); - } - if (inSetterContext) { - if (isNullAware) { - _conditionalNodes[node] = targetType!.node; - } - return calleeType.positionalParameters![0]; - } else { - var expressionType = callee is PropertyAccessorElement - ? calleeType.returnType - : calleeType; - if (isNullAware) { - expressionType = expressionType!.withNode( - NullabilityNode.forLUB(targetType!.node, expressionType.node)); - _variables.recordDecoratedExpressionType(node, expressionType); - } - return expressionType; - } - } - - /// Check whether [node] is a call to the quiver package's [`checkNotNull`], - /// and if so, potentially mark the first argument as non-nullable. - /// - /// [`checkNotNull`]: https://pub.dev/documentation/quiver/latest/quiver.check/checkNotNull.html - void _handleQuiverCheckNotNull(MethodInvocation node) { - var callee = node.methodName.staticElement; - var calleeUri = callee?.library?.source.uri; - var isQuiverCheckNull = callee?.name == 'checkNotNull' && - calleeUri != null && - calleeUri.isScheme('package') && - calleeUri.path.startsWith('quiver/'); - - if (isQuiverCheckNull && node.argumentList.arguments.isNotEmpty) { - var argument = node.argumentList.arguments.first; - if (argument is SimpleIdentifier && _isReferenceInScope(argument)) { - var argumentType = - getOrComputeElementType(argument, argument.staticElement!); - _graph.makeNonNullable( - argumentType.node, QuiverCheckNotNullOrigin(source, argument)); - } - } - } - - DecoratedType? _handleTarget( - Expression? target, String name, Element? callee) { - if (isDeclaredOnObject(name)) { - return _dispatch(target); - } else if ((callee is MethodElement || callee is PropertyAccessorElement) && - callee!.enclosingElement is ExtensionElement) { - // Extension methods can be called on a `null` target, when the `on` type - // of the extension is nullable. Note: we don't need to check whether the - // target type is assignable to the extended type; that is done in - // [getOrComputeElementType]. - return _dispatch(target); - } else { - return _checkExpressionNotNull(target!); - } - } - - void _handleUninitializedFields(AstNode node, Set fields) { - for (var field in fields) { - _graph.makeNullable(_variables.decoratedElementType(field!).node, - FieldNotInitializedOrigin(source, node)); - } - } - - /// Returns whether [_currentFunctionExpression] is an argument to the test - /// package's `setUp` function. - bool _isCurrentFunctionExpressionFoundInTestSetUpCall() { - var parent = _currentFunctionExpression?.parent; - if (parent is ArgumentList) { - var grandParent = parent.parent; - if (grandParent is MethodInvocation) { - var enclosingInvocation = grandParent.methodName; - if (enclosingInvocation.name == 'setUp') { - var uri = enclosingInvocation.staticElement!.library?.source.uri; - if (uri != null && - uri.isScheme('package') && - uri.path.startsWith('test_core/')) { - return true; - } - } - } - } - return false; - } - - bool _isPrefix(Expression? e) => - e is SimpleIdentifier && e.staticElement is PrefixElement; - - bool _isReferenceInScope(Expression expression) { - var element = _referencedElement(expression); - return element != null && _postDominatedLocals.isInScope(element); - } - - bool _isUntypedParameter(NormalFormalParameter parameter) { - if (parameter is SimpleFormalParameter) { - return parameter.type == null; - } else if (parameter is FieldFormalParameter) { - return parameter.type == null; - } else { - return false; - } - } - - void _linkDecoratedTypeParameters( - DecoratedType x, DecoratedType? y, EdgeOrigin origin, - {bool isUnion = true}) { - for (int i = 0; - i < x.positionalParameters!.length && - i < y!.positionalParameters!.length; - i++) { - _linkDecoratedTypes( - x.positionalParameters![i], y.positionalParameters![i], origin, - isUnion: isUnion); - } - for (var entry in x.namedParameters!.entries) { - var superParameterType = y!.namedParameters![entry.key]; - if (superParameterType != null) { - _linkDecoratedTypes(entry.value, y.namedParameters![entry.key], origin, - isUnion: isUnion); - } - } - } - - void _linkDecoratedTypes(DecoratedType x, DecoratedType? y, EdgeOrigin origin, - {bool isUnion = true}) { - if (isUnion) { - _graph.union(x.node, y!.node, origin); - } else { - _graph.connect(x.node, y!.node, origin, hard: true); - } - _linkDecoratedTypeParameters(x, y, origin, isUnion: isUnion); - for (int i = 0; - i < x.typeArguments.length && i < y.typeArguments.length; - i++) { - _linkDecoratedTypes(x.typeArguments[i]!, y.typeArguments[i], origin, - isUnion: isUnion); - } - if (x.returnType != null && y.returnType != null) { - _linkDecoratedTypes(x.returnType!, y.returnType, origin, - isUnion: isUnion); - } - } - - void _makeArgumentsNullable( - ExecutableElement declaredElement, Declaration node) { - for (final p in declaredElement.parameters) { - if (p is! FieldFormalParameterElement && - p is! SuperFormalParameterElement && - p is! ConstVariableElement) { - final decoratedType = _variables.decoratedElementType(p); - if (decoratedType.type is TypeParameterType) continue; - _graph.makeNullable( - decoratedType.node, PublicMethodArgumentOrigin(source, node)); - } - } - } - - EdgeOrigin _makeEdgeOrigin(DecoratedType sourceType, Expression expression, - {bool isSetupAssignment = false}) { - if (sourceType.type is DynamicType) { - return DynamicAssignmentOrigin(source, expression, - isSetupAssignment: isSetupAssignment); - } else { - ExpressionChecksOrigin expressionChecksOrigin = ExpressionChecksOrigin( - source, expression, ExpressionChecks(), - isSetupAssignment: isSetupAssignment); - _variables.recordExpressionChecks( - source, expression, expressionChecksOrigin); - return expressionChecksOrigin; - } - } - - DecoratedType _makeNonNullableBoolType(Expression expression) { - assert(expression.staticType!.isDartCoreBool); - var target = - NullabilityNodeTarget.text('expression').withCodeRef(expression); - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullableUnion( - nullabilityNode, NonNullableBoolTypeOrigin(source, expression)); - return DecoratedType(typeProvider.boolType, nullabilityNode); - } - - DecoratedType _makeNonNullLiteralType(Expression expression, - {List typeArguments = const []}) { - var target = - NullabilityNodeTarget.text('expression').withCodeRef(expression); - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullableUnion( - nullabilityNode, LiteralOrigin(source, expression)); - return DecoratedType(expression.staticType, nullabilityNode, - typeArguments: typeArguments); - } - - DecoratedType _makeNullableDynamicType(AstNode astNode) { - var target = - NullabilityNodeTarget.text('dynamic type').withCodeRef(astNode); - var decoratedType = DecoratedType.forImplicitType( - typeProvider, typeProvider.dynamicType, _graph, target); - _graph.makeNullable( - decoratedType.node, AlwaysNullableTypeOrigin(source, astNode, false)); - return decoratedType; - } - - DecoratedType _makeNullableVoidType(SimpleIdentifier astNode) { - var target = NullabilityNodeTarget.text('void type').withCodeRef(astNode); - var decoratedType = DecoratedType.forImplicitType( - typeProvider, typeProvider.voidType, _graph, target); - _graph.makeNullable( - decoratedType.node, AlwaysNullableTypeOrigin(source, astNode, true)); - return decoratedType; - } - - NullabilityNode _nullabilityNodeForGLB( - AstNode astNode, NullabilityNode leftNode, NullabilityNode rightNode) { - var node = NullabilityNode.forGLB(); - var origin = GreatestLowerBoundOrigin(source, astNode); - _graph.connect(leftNode, node, origin, guards: [rightNode]); - _graph.connect(node, leftNode, origin); - _graph.connect(node, rightNode, origin); - return node; - } - - /// Returns the element referenced directly by [expression], if any; otherwise - /// returns `null`. - Element? _referencedElement(Expression expression) { - expression = expression.unParenthesized; - if (expression is SimpleIdentifier) { - return _favorFieldFormalElements(expression.staticElement); - } else if (expression is ThisExpression || expression is SuperExpression) { - return _extensionThis; - } else { - return null; - } - } - - /// Determines whether uses of [expression] should cause hard edges to be - /// created in the nullability graph. - /// - /// If [isConditionallyExecuted] is `true`, that indicates that [expression] - /// appears in a context where it might not get executed (e.g. on the RHS of - /// a `??=`). - bool _shouldUseHardEdge(Expression expression, - {bool isConditionallyExecuted = false}) { - expression = expression.unParenthesized; - if (expression is ListLiteral || expression is SetOrMapLiteral) { - // List, set, and map literals have either explicit or implicit type - // arguments. If supplying a nullable type for one of these type - // arguments would lead to an error (e.g. `f([])` where `f` requires - // a `List`), then we should use a hard edge, to ensure that the - // migrated type argument will be non-nullable. - return true; - } else if (expression is AsExpression) { - // "as" expressions have an explicit type. If making this type nullable - // would lead to an error (e.g. `f(x as int?)` where `f` requires an - // `int`),then we should use a hard edge, to ensure that the migrated type - // will be non-nullable. - return true; - } - // For other expressions, we should use a hard edge only if (a) the - // expression is unconditionally executed, and (b) the expression is a - // reference to a local variable or parameter and it post-dominates the - // declaration of that local variable or parameter. - return !isConditionallyExecuted && _isReferenceInScope(expression); - } - - DecoratedType? _thisOrSuper(Expression node) { - if (_currentInterfaceOrExtension == null) { - return null; - } - - NullabilityNode makeNonNullableNode(NullabilityNodeTarget target) { - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullableUnion(nullabilityNode, - ThisOrSuperOrigin(source, node, node is ThisExpression)); - return nullabilityNode; - } - - var token = node.beginToken.lexeme; - var target = - NullabilityNodeTarget.text('$token expression').withCodeRef(node); - if (_currentInterfaceOrExtension is InterfaceElement) { - final type = (_currentInterfaceOrExtension as InterfaceElement).thisType; - - // Instantiate the type, and any type arguments, with non-nullable types, - // because the type of `this` is always `ClassName` - // with no `?`s. (Even if some of the type parameters are allowed to be - // instantiated with nullable types at runtime, a reference to `this` - // can't be migrated in such a way that forces them to be nullable.) - var index = 0; - return DecoratedType(type, makeNonNullableNode(target), - typeArguments: type.typeArguments - .map((t) => DecoratedType( - t, makeNonNullableNode(target.typeArgument(index++)))) - .toList()); - } else { - assert(_currentInterfaceOrExtension is ExtensionElement); - assert(_currentExtendedType != null); - return _currentExtendedType; - } - } - - /// If [node] is an `orElse` argument to an iterable method that should be - /// transformed, performs the necessary assignment checks on the expression - /// type and returns `true` (this indicates to the caller that no further - /// assignment checks need to be performed); otherwise returns `false`. - bool _tryTransformOrElse(Expression? expression, DecoratedType sourceType) { - var transformationInfo = - _whereOrNullTransformer.tryTransformOrElseArgument(expression); - if (transformationInfo != null) { - // Don't build any edges for this argument; if necessary we'll transform - // it rather than make things nullable. But do save the nullability of - // the return value of the `orElse` method, so that we can later connect - // it to the nullability of the value returned from the method - // invocation. - var extraNullability = sourceType.returnType!.node; - _deferredMethodInvocationProcessing[transformationInfo.methodInvocation] = - (methodInvocationType) { - var newNode = NullabilityNode.forInferredType( - NullabilityNodeTarget.text( - 'return value from ${transformationInfo.originalName}')); - var origin = IteratorMethodReturnOrigin( - source, transformationInfo.methodInvocation); - _graph.connect(methodInvocationType!.node, newNode, origin); - _graph.connect(extraNullability, newNode, origin); - return methodInvocationType.withNode(newNode); - }; - return true; - } else { - return false; - } - } - - /// If [node] is a call to `Iterable.where` that should be transformed, - /// performs the necessary assignment checks on the expression type and - /// returns `true` (this indicates to the caller that no further assignment - /// checks need to be performed); otherwise returns `false`. - bool _tryTransformWhere(Expression? expression, EdgeOrigin edgeOrigin, - DecoratedType sourceType, DecoratedType destinationType) { - var transformationInfo = - _whereNotNullTransformer.tryTransformMethodInvocation(expression); - if (transformationInfo != null) { - _checkAssignment(edgeOrigin, FixReasonTarget.root, - source: _whereNotNullTransformer.transformDecoratedInvocationType( - sourceType, _graph), - destination: destinationType, - hard: false); - return true; - } else { - return false; - } - } - - Never _unimplemented(AstNode? node, String message) { - StringBuffer buffer = StringBuffer(); - buffer.write(message); - if (node != null) { - CompilationUnit unit = node.root as CompilationUnit; - buffer.write(' in "'); - buffer.write(node.toSource()); - buffer.write('" on line '); - buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber); - buffer.write(' of "'); - buffer.write(unit.declaredElement!.source.fullName); - buffer.write('"'); - } - throw UnimplementedError(buffer.toString()); - } - - /// Produce Future for some T, however, we would like to merely - /// upcast T to that type if possible, skipping the flatten when not - /// necessary. - DecoratedType _wrapFuture(DecoratedType type, AstNode? node) { - var dartType = type.type!; - if (dartType.isDartCoreNull || dartType.isBottom) { - return _futureOf(type, node!); - } - - if (dartType is InterfaceType && - dartType.element == typeProvider.futureOrElement) { - var typeArguments = type.typeArguments; - if (typeArguments.length == 1) { - // Wrapping FutureOr?2 should produce Future, where either 1 - // or 2 being nullable causes 3 to become nullable. - var typeArgument = typeArguments[0]!; - return _futureOf( - typeArgument - .withNode(NullabilityNode.forLUB(typeArgument.node, type.node)), - node!); - } - } - - if (_typeSystem.isSubtypeOf(dartType, typeProvider.futureDynamicType)) { - return _decoratedClassHierarchy! - .asInstanceOf(type, typeProvider.futureElement); - } - - return _futureOf(type, node!); - } - - /// If the [node] is the finishing identifier of an assignment, return its - /// "writeElement", otherwise return its "staticElement", which might be - /// thought as the "readElement". - static Element? getWriteOrReadElement(AstNode node) { - var writeElement = _getWriteElement(node); - if (writeElement != null) { - return writeElement; - } - - if (node is IndexExpression) { - return node.staticElement; - } else if (node is SimpleIdentifier) { - return node.staticElement; - } else { - return null; - } - } - - /// If the [node] is the target of a [CompoundAssignmentExpression], - /// return the corresponding "writeElement", which is the local variable, - /// the setter referenced with a [SimpleIdentifier] or a [PropertyAccess], - /// or the `[]=` operator. - static Element? _getWriteElement(AstNode node) { - var parent = node.parent; - if (parent is AssignmentExpression && parent.leftHandSide == node) { - return parent.writeElement; - } else if (parent is PostfixExpression) { - return parent.writeElement; - } else if (parent is PrefixExpression) { - return parent.writeElement; - } - - if (parent is PrefixedIdentifier && parent.identifier == node) { - return _getWriteElement(parent); - } else if (parent is PropertyAccess && parent.propertyName == node) { - return _getWriteElement(parent); - } else { - return null; - } - } - - /// Whether `expression` is a call to Angular's [Injector.get], with exactly - /// one argument. - static bool _isInjectorGetCall(Expression expression) { - if (expression is! MethodInvocation) { - return false; - } - - if (expression.methodName.name != 'get') { - return false; - } - if (expression.argumentList.arguments.length != 1) { - // If a second argument is passed, which may indicate that a non-null - // value isn't necessarily expected, don't count this call. - return false; - } - - var target = expression.target; - if (target is! Identifier) { - return false; - } - - var receiver = target.staticType?.element; - if (receiver?.name != 'Injector') { - return false; - } - - var uri = receiver?.library?.source.uri; - return uri != null && - uri.isScheme('package') && - uri.path.startsWith('angular/'); - } -} - -/// Implementation of [_checkAssignment] for [EdgeBuilder]. -/// -/// This has been moved to its own mixin to allow it to be more easily unit -/// tested. -mixin _AssignmentChecker { - TypeProvider get typeProvider; - - DecoratedClassHierarchy? get _decoratedClassHierarchy; - - TypeSystem get _typeSystem; - - /// Creates the necessary constraint(s) for an assignment from [source] to - /// [destination]. [origin] should be used as the origin for any edges - /// created. [hard] indicates whether a hard edge should be created. - /// [sourceIsFunctionLiteral] indicates whether the source of the assignment - /// is a function literal expression. - void _checkAssignment(EdgeOrigin origin, FixReasonTarget edgeTarget, - {required DecoratedType source, - required DecoratedType destination, - required bool hard, - bool checkable = true, - bool sourceIsFunctionLiteral = false}) { - var sourceType = source.type!; - var destinationType = destination.type!; - if (!_typeSystem.isSubtypeOf(sourceType, destinationType)) { - // Not a proper upcast assignment. - if (_typeSystem.isSubtypeOf(destinationType, sourceType)) { - // But rather a downcast. - _checkDowncast(origin, - source: source, destination: destination, hard: hard); - return; - } - if (destinationType is FunctionType) { - var callMethodType = _getCallMethodType(source); - if (callMethodType != null) { - // Handle implicit `.call` coercion - _checkAssignment(origin, edgeTarget, - source: callMethodType, - destination: destination, - hard: false, - checkable: false, - sourceIsFunctionLiteral: sourceIsFunctionLiteral); - return; - } - } - // A side cast. This may be an explicit side cast, or illegal code. There - // is no nullability we can infer here. - assert( - _assumeNonNullabilityInCasts, - 'side cast not supported without assuming non-nullability:' - ' $sourceType to $destinationType'); - _connect(source.node, destination.node, origin, edgeTarget, hard: hard); - return; - } - _connect(source.node, destination.node, origin, edgeTarget, - hard: hard, checkable: checkable); - _checkAssignment_recursion(origin, edgeTarget, - source: source, - destination: destination, - sourceIsFunctionLiteral: sourceIsFunctionLiteral, - hard: hard); - } - - /// Does the recursive part of [_checkAssignment], visiting all of the types - /// constituting [source] and [destination], and creating the appropriate - /// edges between them. [sourceIsFunctionLiteral] indicates whether the - /// source of the assignment is a function literal expression. - void _checkAssignment_recursion(EdgeOrigin origin, FixReasonTarget edgeTarget, - {required DecoratedType source, - required DecoratedType destination, - bool sourceIsFunctionLiteral = false, - bool hard = false}) { - var sourceType = source.type!; - var destinationType = destination.type!; - assert(_typeSystem.isSubtypeOf(sourceType, destinationType)); - if (destinationType.isDartAsyncFutureOr) { - var s1 = destination.typeArguments[0]; - if (sourceType.isDartAsyncFutureOr) { - // This is a special case not in the subtyping spec. The subtyping spec - // covers this case by expanding the LHS first, which is fine but - // leads to redundant edges (which might be confusing for users) - // if T0 is FutureOr then: - // - T0 <: T1 iff Future <: T1 and S0 <: T1 - // Since T1 is FutureOr, this is equivalent to: - // - T0 <: T1 iff (Future <: Future or Future <: S1) and - // (S0 <: Future or S0 <: S1) - // Which is equivalent to: - // - T0 <: T1 iff (S0 <: S1 or Future <: S1) and - // (S0 <: Future or S0 <: S1) - // Which is equivalent to (distributing the "and"): - // - T0 <: T1 iff (S0 <: S1 and (S0 <: Future or S0 <: S1)) or - // (Future <: S1 and (S0 <: Future or S0 <: S1)) - // Which is equivalent to (distributing the "and"s): - // - T0 <: T1 iff (S0 <: S1 and S0 <: Future) or - // (S0 <: S1 and S0 <: S1) or - // (Future <: S1 and S0 <: Future) or - // (Future <: S1 and S0 <: S1) - // If S0 <: S1, the relation is satisfied. Otherwise the only term that - // matters is (Future <: S1 and S0 <: Future), so this is - // equivalent to: - // - T0 <: T1 iff S0 <: S1 or (Future <: S1 and S0 <: Future) - // Let's consider whether there are any cases where the RHS of this "or" - // can be satisfied but not the LHS. That is, assume that - // Future <: S1 and S0 <: Future hold, but not S0 <: S1. S1 - // must not be a top type (otherwise S0 <: S1 would hold), so the only - // way Future <: S1 can hold is if S1 is Future or FutureOr - // for some A. In either case, Future simplifies to Future, so - // we know that S0 <: Future. Also, in either case, Future <: S1. - // Combining these, we have that S0 <: S1, contradicting our assumption. - // So the RHS of the "or" is redundant, and we can simplify to: - // - S0 <: S1. - var s0 = source.typeArguments[0]!; - _checkAssignment(origin, edgeTarget.yieldedType, - source: s0, destination: s1!, hard: false); - return; - } - // (From the subtyping spec): - // if T1 is FutureOr then T0 <: T1 iff any of the following hold: - // - either T0 <: Future - if (_typeSystem.isSubtypeOf( - sourceType, typeProvider.futureType(s1!.type!))) { - // E.g. FutureOr = (... as Future) - // This is handled by the InterfaceType logic below, since we treat - // FutureOr as a supertype of Future. - } - // - or T0 <: S1 - else if (_typeSystem.isSubtypeOf(sourceType, s1.type!)) { - // E.g. FutureOr = (... as int) - _checkAssignment_recursion(origin, edgeTarget.yieldedType, - source: source, destination: s1); - return; - } - // - or T0 is X0 and X0 has bound S0 and S0 <: T1 - // - or T0 is X0 & S0 and S0 <: T1 - else if (sourceType is TypeParameterType) { - throw UnimplementedError('TODO(paulberry)'); - } else { - // Not a subtype. This should never happen, since we handle the - // implicit downcast case above. - assert(false, 'not a subtype'); - } - } - if (sourceType.isBottom || sourceType.isDartCoreNull) { - // No further edges need to be created, since all types are trivially - // supertypes of bottom (and of Null, in the pre-migration world). - } else if (sourceType is TypeParameterType) { - // Handle this before handling dynamic/object/void, to correctly infer - // nullabilities in `Object o = T`. - if (destinationType is TypeParameterType) { - // No further edges need to be created, since type parameter types - // aren't made up of other types. - } else { - // Effectively this is an assignment from the type parameter's bound to - // the destination type. - _checkAssignment(origin, edgeTarget, - source: _getTypeParameterTypeBound(source)!, - destination: destination, - hard: false); - return; - } - } else if (destinationType is DynamicType || - destinationType is VoidType || - destinationType.isDartCoreObject) { - // No further edges need to be created, since all types are trivially - // subtypes of dynamic, Object, and void, since all are treated as - // equivalent to dynamic for subtyping purposes. - } else if (sourceType is InterfaceType && - destinationType is InterfaceType) { - var rewrittenSource = _decoratedClassHierarchy! - .asInstanceOf(source, destinationType.element); - assert(rewrittenSource.typeArguments.length == - destination.typeArguments.length); - for (int i = 0; i < rewrittenSource.typeArguments.length; i++) { - _checkAssignment(origin, edgeTarget.typeArgument(i), - source: rewrittenSource.typeArguments[i]!, - destination: destination.typeArguments[i]!, - hard: hard, - checkable: false); - } - } else if (sourceType is FunctionType && destinationType is FunctionType) { - // If the source is a function literal, we want a hard edge, so that if a - // function returning non-null is required, we will insure that the - // function literal has a non-nullable return type (e.g. by inserting null - // checks into the function literal). - _checkAssignment(origin, edgeTarget.returnType, - source: source.returnType!, - destination: destination.returnType!, - hard: sourceIsFunctionLiteral, - checkable: false); - if (source.typeArguments.isNotEmpty || - destination.typeArguments.isNotEmpty) { - throw UnimplementedError('TODO(paulberry)'); - } - for (int i = 0; - i < source.positionalParameters!.length && - i < destination.positionalParameters!.length; - i++) { - // Note: source and destination are swapped due to contravariance. - _checkAssignment(origin, edgeTarget.positionalParameter(i), - source: destination.positionalParameters![i], - destination: source.positionalParameters![i], - hard: false, - checkable: false); - } - for (var entry in destination.namedParameters!.entries) { - // Note: source and destination are swapped due to contravariance. - _checkAssignment(origin, edgeTarget.namedParameter(entry.key), - source: entry.value, - destination: source.namedParameters![entry.key]!, - hard: false, - checkable: false); - } - } else if (destinationType is DynamicType || sourceType is DynamicType) { - // ok; nothing further to do. - } else if (destinationType is InterfaceType && sourceType is FunctionType) { - // Either this is an upcast to Function or Object, or it is erroneous - // code. In either case we don't need to create any additional edges. - } else { - throw '$destination <= $source'; // TODO(paulberry) - } - } - - void _checkDowncast(EdgeOrigin origin, - {required DecoratedType source, - required DecoratedType destination, - required bool hard}) { - var destinationType = destination.type!; - final sourceType = source.type!; - assert(_typeSystem.isSubtypeOf(destinationType, sourceType)); - // Nullability should narrow to maintain subtype relationship. - _connect(source.node, destination.node, origin, FixReasonTarget.root, - hard: hard); - - if (sourceType is DynamicType || - sourceType.isDartCoreObject || - sourceType is VoidType) { - if (destinationType is InterfaceType) { - for (final param in destinationType.element.typeParameters) { - assert(param.bound == null, - 'downcast to type parameters with bounds not supported'); - } - } - if (destinationType is FunctionType) { - // Nothing else to do. - return; - } - } else if (destinationType.isDartCoreNull) { - // There's not really much we can infer from trying to assign a type to - // Null. We could say that the source of the assignment must be nullable, - // but that's not really useful because the nullability won't propagate - // anywhere. Besides, the code is probably erroneous (e.g. the user is - // trying to store a value into a `List`). So do nothing. - return; - } else if (destinationType is TypeParameterType) { - if (sourceType is! TypeParameterType) { - // Assume an assignment to the type parameter's bound. - _checkAssignment(origin, FixReasonTarget.root, - source: source, - destination: _getTypeParameterTypeBound(destination)!, - hard: false); - } else if (destinationType == sourceType) { - // Nothing to do. - return; - } - } else if (sourceType.isDartAsyncFutureOr) { - if (destination.type!.isDartAsyncFuture) { - // FutureOr is nullable, so the Future should be nullable too. - _connect(source.typeArguments[0]!.node, destination.node, origin, - FixReasonTarget.root.yieldedType, - hard: hard); - _checkDowncast(origin, - source: source.typeArguments[0]!, - destination: destination.typeArguments[0]!, - hard: false); - } else if (destination.type!.isDartAsyncFutureOr) { - _checkDowncast(origin, - source: source.typeArguments[0]!, - destination: destination.typeArguments[0]!, - hard: false); - } else { - _checkDowncast(origin, - source: source.typeArguments[0]!, - destination: destination, - hard: false); - } - } else if (destinationType is InterfaceType) { - if (sourceType is InterfaceType) { - final target = _decoratedClassHierarchy! - .asInstanceOf(destination, sourceType.element); - for (var i = 0; i < source.typeArguments.length; ++i) { - _checkDowncast(origin, - source: source.typeArguments[i]!, - destination: target.typeArguments[i]!, - hard: false); - } - } else { - assert(false, - 'downcasting from ${sourceType.runtimeType} to interface type'); - } - } else if (destinationType is FunctionType) { - if (sourceType.isDartCoreFunction) { - // Nothing else to do. - return; - } - } else { - assert( - false, - 'downcasting from ${sourceType.runtimeType} to ' - '${destinationType.runtimeType} not supported. ($sourceType $destinationType)'); - } - } - - void _connect(NullabilityNode? source, NullabilityNode? destination, - EdgeOrigin origin, FixReasonTarget edgeTarget, - {bool hard = false, bool checkable = true}); - - /// If [type] represents a class containing a `call` method, returns the - /// decorated type of the `call` method, with appropriate substitutions. - /// Otherwise returns `null`. - DecoratedType? _getCallMethodType(DecoratedType type); - - /// Given a [type] representing a type parameter, retrieves the type's bound. - DecoratedType? _getTypeParameterTypeBound(DecoratedType type); -} - -/// Information about a binary expression whose boolean value could possibly -/// affect nullability analysis. -class _ConditionInfo { - /// The [expression] of interest. - final Expression condition; - - /// Indicates whether [condition] is pure (free from side effects). - /// - /// For example, a condition like `x == null` is pure (assuming `x` is a local - /// variable or static variable), because evaluating it has no user-visible - /// effect other than returning a boolean value. - final bool isPure; - - /// Indicates whether the intents postdominate the intent node declarations. - final bool? postDominatingIntent; - - /// If not `null`, the [NullabilityNode] that would need to be nullable in - /// order for [condition] to evaluate to `true`. - final NullabilityNode? trueGuard; - - /// If not `null`, the [NullabilityNode] that would need to be nullable in - /// order for [condition] to evaluate to `false`. - final NullabilityNode? falseGuard; - - /// If not `null`, the [NullabilityNode] that should be asserted to have - /// non-null intent if [condition] is asserted to be `true`. - final NullabilityNode? trueDemonstratesNonNullIntent; - - /// If not `null`, the [NullabilityNode] that should be asserted to have - /// non-null intent if [condition] is asserted to be `false`. - final NullabilityNode? falseDemonstratesNonNullIntent; - - _ConditionInfo(this.condition, - {required this.isPure, - this.postDominatingIntent, - this.trueGuard, - this.falseGuard, - this.trueDemonstratesNonNullIntent, - this.falseDemonstratesNonNullIntent}); - - /// Returns a new [_ConditionInfo] describing the boolean "not" of `this`. - _ConditionInfo not(Expression condition) => _ConditionInfo(condition, - isPure: isPure, - postDominatingIntent: postDominatingIntent, - trueGuard: falseGuard, - falseGuard: trueGuard, - trueDemonstratesNonNullIntent: falseDemonstratesNonNullIntent, - falseDemonstratesNonNullIntent: trueDemonstratesNonNullIntent); -} - -extension on DartType? { - DartType? get explicitBound { - final self = this; - if (self is TypeParameterType && - self.nullabilitySuffix == NullabilitySuffix.star) { - return self.element.bound.explicitBound; - } - return self; - } -} diff --git a/pkg/nnbd_migration/lib/src/edge_origin.dart b/pkg/nnbd_migration/lib/src/edge_origin.dart deleted file mode 100644 index 426801d7d9ea..000000000000 --- a/pkg/nnbd_migration/lib/src/edge_origin.dart +++ /dev/null @@ -1,780 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:nnbd_migration/instrumentation.dart'; - -/// Edge origin resulting from a type in already-migrated code. -/// -/// For example, in the Map class in dart:core: -/// V? operator [](Object key); -/// -/// this class is used for the edge connecting `always` to the return type of -/// `operator []`, due to the fact that dart:core has already been migrated and -/// the type is explicitly nullable. -/// -/// Note that since a single element can have a complex type, it is likely that -/// multiple edges will be created with an [AlreadyMigratedTypeOrigin] pointing -/// to the same type. To distinguish which edge corresponds to which part of -/// the element's type, use the callbacks -/// [NullabilityMigrationInstrumentation.externalDecoratedType] and -/// [NullabilityMigrationInstrumentation.externalDecoratedTypeParameterBound]. -class AlreadyMigratedTypeOrigin extends EdgeOrigin { - /// Indicates whether the already-migrated type is nullable or not. - final bool isNullable; - - AlreadyMigratedTypeOrigin.forElement(Element super.element, this.isNullable) - : super.forElement(); - - @override - String get description => '${isNullable ? 'nullable' : 'non-nullable'}' - ' type in already-migrated code'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.alreadyMigratedType; -} - -/// Edge origin resulting from the use of a type that is always nullable. -/// -/// For example, in the following code snippet: -/// void f(dynamic x) {} -/// -/// this class is used for the edge connecting `always` to the type of f's `x` -/// parameter, due to the fact that the `dynamic` type is always considered -/// nullable. -class AlwaysNullableTypeOrigin extends EdgeOrigin { - /// Indicates whether the always-nullable type is the `void` type (if `false`, - /// it is the `dynamic` type). - final bool isVoid; - - AlwaysNullableTypeOrigin(super.source, AstNode super.node, this.isVoid); - - AlwaysNullableTypeOrigin.forElement(Element super.element, this.isVoid) - : super.forElement(); - - @override - String get description => - '${isVoid ? 'void' : 'dynamic'} type is nullable by definition'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.alwaysNullableType; -} - -/// Edge origin resulting from the presence of an Angular annotation such as -/// `@Optional()`, `@Attribute(...)`, `@ViewChild(...)`, or `@ContentChild(...)`. -class AngularAnnotationOrigin extends EdgeOrigin { - AngularAnnotationOrigin(super.source, AstNode super.node); - - @override - String get description => - 'annotated with an Angular annotation indicating nullability'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.angularAnnotation; -} - -/// Edge origin resulting from `@Component(...)` or `@Injectable()` class -/// constructor arguments having neither `@Optional()` nor `@Attribute()` -/// annotation. -class AngularConstructorArgumentOrigin extends EdgeOrigin { - AngularConstructorArgumentOrigin(super.source, AstNode super.node); - - @override - String get description => - 'Component or @Injectable() class constructor arguments without' - ' @Optional/@Attribute annotations must be non-nullable'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.angularConstructorArgument; -} - -/// Edge origin resulting from the presence of a call to -/// `ArgumentError.checkNotNull`. -/// -/// For example, in the following code snippet: -/// void f(int i) { -/// ArgumentError.checkNotNull(i); -/// } -/// -/// this class is used for the edge connecting the type of f's `i` parameter to -/// `never`, due to the `checkNotNull` call proclaiming that `i` is not `null`. -class ArgumentErrorCheckNotNullOrigin extends EdgeOrigin { - ArgumentErrorCheckNotNullOrigin(super.source, SimpleIdentifier super.node); - - @override - String get description => 'value checked to be non-null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.argumentErrorCheckNotNull; -} - -/// Edge origin resulting from an assignment from a call to Angular's -/// `Injector.get` in a test `setUp` callback. -/// -/// For example, in the following code snippet: -/// -/// Foo foo; -/// setUp(() { -/// foo = injector.get(Foo); -/// }); -/// -/// this class is used for the edge connecting the type of the `foo` variable to -/// `never`, due to the assignment from `injector.get`. While `Injector.get` has -/// a return type of `dynamic`, it's use in a test `setUp` callback is more than -/// likely intended to yield a non-null value. -class AssignmentFromAngularInjectorGetOrigin extends EdgeOrigin { - AssignmentFromAngularInjectorGetOrigin( - super.source, SimpleIdentifier super.node, - {super.isSetupAssignment = false}); - - @override - String get description => 'value retrieved from Injector.get in test setUp'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.assignmentFromAngularInjectorGet; -} - -/// Edge origin resulting from a use of built_value's `@nullable` annotation. -class BuiltValueNullableOrigin extends EdgeOrigin { - BuiltValueNullableOrigin(super.source, AstNode super.node); - - @override - String get description => 'method is marked with the `@nullable` annotation'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.builtValueNullableAnnotation; -} - -/// An edge origin used for edges that originated because of a tear-off of -/// `call` on a function type. -class CallTearOffOrigin extends EdgeOrigin { - CallTearOffOrigin(super.source, AstNode super.node); - - @override - String get description => 'tear-off of .call'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.callTearOff; -} - -/// Edge origin resulting from the use of a value on the LHS of a compound -/// assignment. -class CompoundAssignmentOrigin extends EdgeOrigin { - CompoundAssignmentOrigin(super.source, AssignmentExpression super.node); - - @override - String get description => 'compound assignment'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.compoundAssignment; - - @override - AssignmentExpression? get node => super.node as AssignmentExpression?; -} - -/// Edge origin resulting from the use of an element which does not affect the -/// nullability graph in other ways. -class DummyOrigin extends EdgeOrigin { - DummyOrigin(super.source, AstNode super.node); - - @override - String get description => 'dummy'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.dummy; -} - -/// An edge origin used for edges that originated because of an assignment -/// involving a value with a dynamic type. -class DynamicAssignmentOrigin extends EdgeOrigin { - DynamicAssignmentOrigin(super.source, super.node, - {super.isSetupAssignment = false}); - - @override - String get description => 'assignment of dynamic value'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.dynamicAssignment; -} - -/// Common interface for classes providing information about how an edge came -/// to be; that is, what was found in the source code that led the migration -/// tool to create the edge. -abstract class EdgeOrigin extends EdgeOriginInfo { - @override - final Source? source; - - @override - final AstNode? node; - - @override - final Element? element; - - /// Whether the origin of the edge is due to the assignment of a variable - /// from within function literal argument to the `setUp` function of the test - /// package. - final bool isSetupAssignment; - - EdgeOrigin(this.source, this.node, {this.isSetupAssignment = false}) - : element = null; - - EdgeOrigin.forElement(this.element, {this.isSetupAssignment = false}) - : source = null, - node = null; - - /// Retrieves the location in the source code that caused this edge to be - /// created, or `null` if unknown. - CodeReference? get codeReference { - if (node != null) { - return CodeReference.fromAstNode(node!); - } - return null; - } - - /// User-friendly description of the edge. - String get description; -} - -/// An edge origin used for edges that originated because of a reference to an -/// enum value, which cannot be null. -class EnumValueOrigin extends EdgeOrigin { - EnumValueOrigin(super.source, AstNode super.node); - - @override - String get description => 'non-nullable enum value'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.enumValue; -} - -/// Edge origin resulting from an explicit or implicit `dynamic` type annotation -/// appearing in an external declaration. -class ExternalDynamicOrigin extends EdgeOrigin { - ExternalDynamicOrigin(super.source, AstNode super.node); - - @override - String get description => 'dynamic type in external declaration'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.externalDynamic; -} - -/// Edge origin resulting from the relationship between a field formal parameter -/// and the corresponding field. -class FieldFormalParameterOrigin extends EdgeOrigin { - FieldFormalParameterOrigin(super.source, FieldFormalParameter super.node); - - @override - String get description => 'field formal parameter'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.fieldFormalParameter; -} - -/// An edge origin used for edges that originated because a field was not -/// initialized. -/// -/// The AST node associated with the edge is the AST node for the constructor -/// that failed to initialize the field (or the class, if the constructor is -/// synthetic). -class FieldNotInitializedOrigin extends EdgeOrigin { - FieldNotInitializedOrigin(super.source, AstNode super.node); - - @override - String get description => 'field not initialized'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.fieldNotInitialized; -} - -/// Edge origin resulting from the use of an iterable type in a for-each loop. -/// -/// For example, in the following code snippet: -/// void f(Iterable l) { -/// for (int i in l) {} -/// } -/// -/// this class is used for the edge connecting the type of `l`'s `int` type -/// parameter to the type of `i`. -class ForEachVariableOrigin extends EdgeOrigin { - ForEachVariableOrigin(super.source, ForEachParts super.node); - - @override - String get description => 'variable in "for each" loop'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.forEachVariable; -} - -/// Edge origin resulting from the relationship between a getter and a setter. -class GetterSetterCorrespondenceOrigin extends EdgeOrigin { - GetterSetterCorrespondenceOrigin(super.source, AstNode super.node); - - @override - String get description => 'getter/setter correspondence'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.getterSetterCorrespondence; -} - -/// Edge origin resulting from the use of greatest lower bound. -/// -/// For example, in the following code snippet: -/// void Function(int) f(void Function(int) x, void Function(int) y) -/// => x ?? y; -/// -/// the `int` in the return type is nullable if both the `int`s in the types of -/// `x` and `y` are nullable, due to the fact that the `int` in the return type -/// is the greatest lower bound of the two other `int`s. -class GreatestLowerBoundOrigin extends EdgeOrigin { - GreatestLowerBoundOrigin(super.source, super.node); - - @override - String get description => 'greatest lower bound'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.greatestLowerBound; -} - -/// Edge origin resulting from the presence of a `??` operator. -class IfNullOrigin extends EdgeOrigin { - IfNullOrigin(super.source, AstNode super.node); - - @override - String get description => 'if-null operator'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.ifNull; -} - -/// Edge origin resulting from the implicit call from a mixin application -/// constructor to the corresponding super constructor. -/// -/// For example, in the following code snippet: -/// class C { -/// C(int i); -/// } -/// mixin M {} -/// class D = C with M; -/// -/// this class is used for the edge connecting the types of the `i` parameters -/// between the implicit constructor for `D` and the explicit constructor for -/// `C`. -class ImplicitMixinSuperCallOrigin extends EdgeOrigin { - ImplicitMixinSuperCallOrigin(super.source, ClassTypeAlias super.node); - - @override - String get description => 'implicit super call in mixin constructor'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.implicitMixinSuperCall; -} - -/// Edge origin resulting from the implicit assignment of `null` to a top level -/// variable or field that lacks an initializer. -class ImplicitNullInitializerOrigin extends EdgeOrigin { - ImplicitNullInitializerOrigin(super.source, AstNode super.node); - - @override - String get description => 'uninitialized variable'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.implicitNullInitializer; -} - -/// Edge origin resulting from a `return;` statement which implicitly returns -/// `null`. -class ImplicitNullReturnOrigin extends EdgeOrigin { - ImplicitNullReturnOrigin(super.source, ReturnStatement super.node); - - @override - String get description => 'implicit return of null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.implicitNullReturn; - - @override - ReturnStatement? get node => super.node as ReturnStatement?; -} - -/// Edge origin used for edges that arise from an implicit use of `this`, e.g. -/// during a method call from an extension. -class ImplicitThisOrigin extends EdgeOrigin { - ImplicitThisOrigin(super.source, AstNode super.node); - - @override - String get description => 'implicit use of `this`'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.implicitThis; -} - -/// Edge origin resulting from the inference of a type parameter, which -/// can affects the nullability of that type parameter's bound. -class InferredTypeParameterInstantiationOrigin extends EdgeOrigin { - InferredTypeParameterInstantiationOrigin(super.source, AstNode super.node); - - @override - String get description => 'inferred type parameter'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.inferredTypeParameterInstantiation; -} - -/// An edge origin used for edges that originated because of an instance -/// creation expression. -class InstanceCreationOrigin extends EdgeOrigin { - InstanceCreationOrigin(super.source, AstNode super.node); - - @override - String get description => 'instance creation'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.instanceCreation; -} - -/// Edge origin resulting from a class that is instantiated to bounds. -/// -/// For example, in the following code snippet: -/// class C {} -/// C x; -/// -/// this class is used for the edge connecting the type of x's type parameter -/// with the type bound in the declaration of C. -class InstantiateToBoundsOrigin extends EdgeOrigin { - InstantiateToBoundsOrigin(super.source, NamedType super.node); - - @override - String get description => 'type instantiated to bounds'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.instantiateToBounds; -} - -/// Edge origin resulting from the use of a type as the main type in an 'is' -/// check. -/// -/// Before the migration, there was no way to say `is int?`, and therefore, -/// `is int` should migrate to non-null int. -class IsCheckMainTypeOrigin extends EdgeOrigin { - IsCheckMainTypeOrigin(super.source, TypeAnnotation super.node); - - @override - String get description => '"is" check does not accept null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.isCheckMainType; -} - -/// An edge origin used for the return type of an iterator method that might be -/// changed into an extension method from package:collection. -class IteratorMethodReturnOrigin extends EdgeOrigin { - IteratorMethodReturnOrigin(super.source, AstNode super.node); - - @override - String get description => - 'Call to iterator method with orElse that returns null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.iteratorMethodReturn; -} - -/// An edge origin used for the type argument of a list constructor that -/// specified an initial length, because that type argument must be nullable. -class ListLengthConstructorOrigin extends EdgeOrigin { - ListLengthConstructorOrigin(super.source, AstNode super.node); - - @override - String get description => 'construction of list via a length'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.listLengthConstructor; -} - -/// An edge origin used for edges that originated because a literal expression -/// has a known nullability. -class LiteralOrigin extends EdgeOrigin { - LiteralOrigin(super.source, AstNode super.node); - - @override - String get description => 'literal expression'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.literal; -} - -/// Edge origin resulting from a call site that does not supply a named -/// parameter. -/// -/// For example, in the following code snippet: -/// void f({int i}) {} -/// main() { -/// f(); -/// } -/// -/// this class is used for the edge connecting `always` to the type of f's `i` -/// parameter, due to the fact that the call to `f` implicitly passes a null -/// value for `i`. -class NamedParameterNotSuppliedOrigin extends EdgeOrigin { - NamedParameterNotSuppliedOrigin(super.source, AstNode super.node); - - @override - String get description => 'named parameter not supplied'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.namedParameterNotSupplied; -} - -/// Edge origin for the nullability of an expression that whose type is fixed by -/// the language definition to be non-nullable `bool`. -class NonNullableBoolTypeOrigin extends EdgeOrigin { - NonNullableBoolTypeOrigin(super.source, AstNode super.node); - - @override - String get description => 'non-null boolean expression'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.nonNullableBoolType; -} - -/// Edge origin resulting from the class/superclass relationship for a class -/// whose superclass is implicitly `Object`. -class NonNullableObjectSuperclass extends EdgeOrigin { - NonNullableObjectSuperclass(super.source, AstNode super.node); - - @override - String get description => 'implicit supertype of Object'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.nonNullableObjectSuperclass; -} - -/// Edge origin resulting from the usage of a value in a circumstance that -/// requires it to be non-nullable -class NonNullableUsageOrigin extends EdgeOrigin { - NonNullableUsageOrigin(super.source, AstNode super.node); - - @override - String get description => 'value cannot be null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.nonNullableUsage; -} - -/// Edge origin resulting from the presence of a non-null assertion. -/// -/// For example, in the following code snippet: -/// void f(int i) { -/// assert(i != null); -/// } -/// -/// this class is used for the edge connecting the type of f's `i` parameter to -/// `never`, due to the assert statement proclaiming that `i` is not `null`. -class NonNullAssertionOrigin extends EdgeOrigin { - NonNullAssertionOrigin(super.source, Assertion super.node); - - @override - String get description => 'value asserted to be non-null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.nonNullAssertion; -} - -/// Edge origin resulting from the presence of an explicit nullability hint -/// comment. -/// -/// For example, in the following code snippet: -/// void f(int/*?*/ i) {} -/// -/// this class is used for the edge connecting `always` to the type of f's `i` -/// parameter, due to the presence of the `/*?*/` comment. -class NullabilityCommentOrigin extends EdgeOrigin { - /// Indicates whether the nullability comment makes the type nullable or - /// non-nullable. - final bool isNullable; - - NullabilityCommentOrigin(super.source, AstNode super.node, this.isNullable) - : assert(node is TypeAnnotation || - node is FunctionTypedFormalParameter || - (node is FieldFormalParameter && node.parameters != null)); - - @override - String get description => - 'explicitly hinted to be ${isNullable ? 'nullable' : 'non-nullable'}'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.nullabilityComment; -} - -/// An edge origin arising from a null aware access. -class NullAwareAccessOrigin extends EdgeOrigin { - NullAwareAccessOrigin(super.source, AstNode super.node); - - @override - String get description => 'null aware access'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.nullAwareAccess; -} - -/// Edge origin resulting from the presence of an optional formal parameter. -/// -/// For example, in the following code snippet: -/// void f({int i}) {} -/// -/// this class is used for the edge connecting `always` to the type of f's `i` -/// parameter, due to the fact that `i` is optional and has no initializer. -class OptionalFormalParameterOrigin extends EdgeOrigin { - OptionalFormalParameterOrigin( - super.source, DefaultFormalParameter super.node); - - @override - String get description => 'optional formal parameter must be nullable'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.optionalFormalParameter; -} - -/// Edge origin resulting from an inheritance relationship between two method -/// parameters. -class ParameterInheritanceOrigin extends EdgeOrigin { - ParameterInheritanceOrigin(super.source, AstNode super.node); - - @override - String get description => 'function parameter override'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.parameterInheritance; -} - -/// Edge origin arising from arguments of public methods. -class PublicMethodArgumentOrigin extends EdgeOrigin { - PublicMethodArgumentOrigin(super.source, AstNode super.node); - - @override - String get description => 'public method argument'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.publicMethodArgument; -} - -/// Edge origin resulting from the presence of a call to quiver's -/// `checkNotNull`. -/// -/// For example, in the following code snippet: -/// import 'package:quiver/check.dart'; -/// void f(int i) { -/// checkNotNull(i); -/// } -/// -/// this class is used for the edge connecting the type of f's `i` parameter to -/// `never`, due to the `checkNotNull` call proclaiming that `i` is not `null`. -class QuiverCheckNotNullOrigin extends EdgeOrigin { - QuiverCheckNotNullOrigin(super.source, SimpleIdentifier super.node); - - @override - String get description => 'value checked to be non-null'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.quiverCheckNotNull; -} - -/// Edge origin resulting from an inheritance relationship between two method -/// return types. -class ReturnTypeInheritanceOrigin extends EdgeOrigin { - ReturnTypeInheritanceOrigin(super.source, AstNode super.node); - - @override - String get description => 'function return type override'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.returnTypeInheritance; -} - -/// Edge origin resulting from the use of a stacktrace parameter in a catch -/// directive. The type of such parameters is fixed by the language as -/// non-nullable `StackTrace`. -class StackTraceTypeOrigin extends EdgeOrigin { - StackTraceTypeOrigin(super.source, super.node); - - @override - String get description => 'stack trace variable is nullable'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.stackTraceTypeOrigin; -} - -/// Edge origin resulting from the use of `this` or `super`. -class ThisOrSuperOrigin extends EdgeOrigin { - /// Indicates whether the expression in question is `this`. If `false`, the - /// expression in question is `super`. - final bool isThis; - - ThisOrSuperOrigin(super.source, AstNode super.node, this.isThis); - - @override - String get description => - 'type of "${isThis ? 'this' : 'super'}" is non-nullable'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.thisOrSuper; -} - -/// An edge origin used for edges that originated from the type of a `throw` or -/// `rethrow`. -class ThrowOrigin extends EdgeOrigin { - ThrowOrigin(super.source, AstNode super.node); - - @override - String get description => - 'type of thrown expression is presumed non-nullable'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.throw_; -} - -/// Edge origin resulting from a usage of a typedef. -/// -/// Since typedefs require multiple phases to resolve, they are represented by -/// a set of inferred nodes. In the secondary phases of graph build, those get -/// unioned with references to the nodes referring to source code. The origin of -/// those union edges will be [TypedefReferenceOrigin]. -class TypedefReferenceOrigin extends EdgeOrigin { - TypedefReferenceOrigin(super.source, NamedType super.node); - - @override - String get description => 'reference to typedef'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.typedefReference; -} - -/// Edge origin resulting from the instantiation of a type parameter, which -/// affects the nullability of that type parameter's bound. -class TypeParameterInstantiationOrigin extends EdgeOrigin { - TypeParameterInstantiationOrigin(super.source, TypeAnnotation super.node); - - @override - String get description => 'type parameter instantiation'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.typeParameterInstantiation; - - @override - TypeAnnotation? get node => super.node as TypeAnnotation?; -} - -/// Edge origin resulting from the read of a variable that has not been -/// definitely assigned a value. -class UninitializedReadOrigin extends EdgeOrigin { - UninitializedReadOrigin(super.source, AstNode super.node); - - @override - String get description => 'local variable might not be initialized'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.uninitializedRead; -} diff --git a/pkg/nnbd_migration/lib/src/edit_plan.dart b/pkg/nnbd_migration/lib/src/edit_plan.dart deleted file mode 100644 index 96df4f7c6861..000000000000 --- a/pkg/nnbd_migration/lib/src/edit_plan.dart +++ /dev/null @@ -1,1764 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert'; -import 'dart:math'; - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/precedence.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; - -Map>? _removeCode( - int offset, int end, _RemovalStyle removalStyle, AtomicEditInfo? info) { - if (offset < end) { - // TODO(paulberry): handle preexisting comments? - switch (removalStyle) { - case _RemovalStyle.commentSpace: - return { - offset: [AtomicEdit.insert('/* ', info: info)], - end: [AtomicEdit.insert('*/ ', info: info)] - }; - case _RemovalStyle.delete: - return { - offset: [AtomicEdit.delete(end - offset, info: info)] - }; - case _RemovalStyle.spaceComment: - return { - offset: [AtomicEdit.insert(' /*', info: info)], - end: [AtomicEdit.insert(' */', info: info)] - }; - case _RemovalStyle.spaceInsideComment: - return { - offset: [AtomicEdit.insert('/* ', info: info)], - end: [AtomicEdit.insert(' */', info: info)] - }; - } - } else { - return null; - } -} - -/// A single atomic change to a source file, decoupled from the location at -/// which the change is made. The [EditPlan] class performs its duties by -/// creating and manipulating [AtomicEdit] objects. -/// -/// A list of [AtomicEdit]s may be converted to a [SourceEdit] using the -/// extension [AtomicEditList], and a map of offsets to lists of [AtomicEdit]s -/// may be converted to a list of [SourceEdit] using the extension -/// [AtomicEditMap]. -/// -/// May be subclassed to allow additional information to be recorded about the -/// edit. -class AtomicEdit { - /// Additional information about this edit, or `null` if no additional - /// information is available. - final AtomicEditInfo? info; - - /// The number of characters that should be deleted by this edit, or `0` if no - /// characters should be deleted. - final int length; - - /// The characters that should be inserted by this edit, or the empty string - /// if no characters should be inserted. - final String replacement; - - /// If `true`, this edit shouldn't actually be made to the source file; it - /// exists merely to provide additional information to be shown in the preview - /// tool. - final bool isInformative; - - /// Initialize an edit to delete [length] characters. - /// - /// Optional argument [info] contains information about why the change was - /// made. - const AtomicEdit.delete(this.length, {this.info, this.isInformative = false}) - : assert(length > 0), - replacement = ''; - - /// Initialize an edit to insert the [replacement] characters. - /// - /// Optional argument [info] contains information about why the change was - /// made. - const AtomicEdit.insert(this.replacement, - {this.info, this.isInformative = false}) - : assert(replacement.length > 0), - length = 0; - - /// Initialize an edit to replace [length] characters with the [replacement] - /// characters. - /// - /// Optional argument [info] contains information about why the change was - /// made. - const AtomicEdit.replace(this.length, this.replacement, {this.info}) - : assert(length > 0 || replacement.length > 0), - isInformative = false; - - /// Return `true` if this edit is a deletion (no characters added). - bool get isDeletion => replacement.isEmpty; - - /// Return `true` if this edit is an insertion (no characters removed). - bool get isInsertion => length == 0; - - /// Return `true` if this edit is a replacement. - bool get isReplacement => length > 0 && replacement.isNotEmpty; - - @override - String toString() { - if (isInsertion) { - return 'InsertText(${json.encode(replacement)})'; - } else if (isDeletion) { - return 'DeleteText($length)'; - } else { - return 'ReplaceText($length, ${json.encode(replacement)})'; - } - } -} - -/// Information stored along with an atomic edit indicating how it arose. -class AtomicEditInfo { - /// A description of the change that was made. - final NullabilityFixDescription description; - - /// The reasons for the edit. - final Map fixReasons; - - /// If the edit is being made due to a hint, the hint in question; otherwise - /// `null`. - final HintComment? hintComment; - - AtomicEditInfo(this.description, this.fixReasons, {this.hintComment}); -} - -/// An [EditPlan] is a builder capable of accumulating a set of edits to be -/// applied to a given [AstNode]. -/// -/// Examples of edits include replacing it with a different node, prefixing or -/// suffixing it with additional text, or deleting some of the text it contains. -/// When the text being produced represents an expression, [EditPlan] also keeps -/// track of the precedence of the expression and whether it ends in a -/// cascade--this allows automatic insertion of parentheses when necessary, as -/// well as removal of parentheses when they become unnecessary. -/// -/// Typical usage will be to produce one or more [EditPlan] objects representing -/// changes to be made to the source code, compose them together, and then call -/// [EditPlan.finalize] to convert into a representation of the concrete edits -/// that need to be made to the source file. -abstract class EditPlan { - EditPlan._(); - - /// Returns the "parent" of the node edited by this [EditPlan]. For edit - /// plans that replace one AST node with another, this is the parent of the - /// AST node being replaced. For edit plans that insert or delete AST nodes, - /// this is the parent of the AST nodes that will be inserted or deleted. - AstNode? get parentNode; -} - -/// Factory class for creating [EditPlan]s. -class EditPlanner { - /// Indicates whether code removed by the EditPlanner should be removed by - /// commenting it out. A value of `false` means to actually delete the code - /// that is removed. - final bool? removeViaComments; - - /// The line info for the source file being edited. This is used when - /// removing statements that fill one or more lines, so that we can remove - /// the indentation as well as the statement, and avoid leaving behind ugly - /// whitespace. - final LineInfo? lineInfo; - - /// The text of the source file being edited. This is used when removing - /// code, so that we can figure out if it is safe to remove adjoining - /// whitespace. - final String? sourceText; - - EditPlanner(this.lineInfo, this.sourceText, {this.removeViaComments = false}); - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// converting the [hint] (which precedes the node) into text that is not - /// commented out. - NodeProducingEditPlan acceptPrefixHint( - NodeProducingEditPlan innerPlan, HintComment hint, - {AtomicEditInfo? info}) { - var affixPlan = innerPlan is _CommentAffixPlan - ? innerPlan - : _CommentAffixPlan(innerPlan); - var changes = hint.changesToAccept(sourceText, info: info); - assert(affixPlan.offset! >= _endForChanges(changes)!); - affixPlan.offset = _offsetForChanges(changes); - affixPlan._prefixChanges = changes + affixPlan._prefixChanges; - return affixPlan; - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// converting the [hint] (which follows the node) into text that is not - /// commented out. - NodeProducingEditPlan acceptSuffixHint( - NodeProducingEditPlan innerPlan, HintComment hint, - {AtomicEditInfo? info}) { - var affixPlan = innerPlan is _CommentAffixPlan - ? innerPlan - : _CommentAffixPlan(innerPlan); - var changes = hint.changesToAccept(sourceText); - assert(affixPlan.end! <= _offsetForChanges(changes)!); - affixPlan.end = _endForChanges(changes); - affixPlan._postfixChanges += hint.changesToAccept(sourceText, info: info); - return affixPlan; - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// appending the given [operand], with an intervening binary [operator]. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan addBinaryPostfix( - NodeProducingEditPlan innerPlan, TokenType operator, String operand, - {AtomicEditInfo? info}) { - assert(innerPlan.sourceNode is Expression); - var precedence = Precedence.forTokenType(operator); - var isAssociative = precedence != Precedence.relational && - precedence != Precedence.equality && - precedence != Precedence.assignment; - return surround(innerPlan, - suffix: [AtomicEdit.insert(' ${operator.lexeme} $operand', info: info)], - outerPrecedence: precedence, - innerPrecedence: precedence, - associative: isAssociative); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// prepending the given [operand], with an intervening binary [operator]. - /// - /// Optional argument [info] contains information about why the change was - /// made. - /// - /// If the expression represented by [operand] is known not to end in a - /// cascade expression, caller may optionally set [allowCascade] to `true` to - /// prevent a rare corner case where parentheses would be added unnecessarily. - /// Note that it is always safe to leave [allowCascade] at its default value - /// of `false`. - NodeProducingEditPlan addBinaryPrefix( - String operand, TokenType operator, NodeProducingEditPlan innerPlan, - {AtomicEditInfo? info, bool allowCascade = false}) { - assert(innerPlan.sourceNode is Expression); - var precedence = Precedence.forTokenType(operator); - var isAssociative = precedence == Precedence.assignment; - return surround(innerPlan, - prefix: [AtomicEdit.insert('$operand ${operator.lexeme} ', info: info)], - outerPrecedence: precedence, - innerPrecedence: precedence, - associative: isAssociative, - allowCascade: allowCascade); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// appending the given [comment]]. - /// - /// Optional argument [info] contains information about why the change was - /// made. - /// - /// Optional argument [isInformative] indicates whether the comment is simply - /// informative, or should actually be applied to the final output (the - /// default). - NodeProducingEditPlan addCommentPostfix( - NodeProducingEditPlan innerPlan, String comment, - {AtomicEditInfo? info, bool isInformative = false}) { - var end = innerPlan.end!; - return surround(innerPlan, suffix: [ - AtomicEdit.insert(' ', isInformative: isInformative), - AtomicEdit.insert(comment, info: info, isInformative: isInformative), - if (!_isJustBefore(end, const [')', ']', '}', ';']) && - !_isJustBeforeWhitespace(end)) - AtomicEdit.insert(' ', isInformative: isInformative) - ]); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// appending the given text with postfix precedence. This could be used, for - /// example, to append a property access or method call. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan addPostfix(NodeProducingEditPlan innerPlan, String text, - {AtomicEditInfo? info}) { - assert(innerPlan.sourceNode is Expression); - return surround(innerPlan, - suffix: [AtomicEdit.insert(text, info: info)], - outerPrecedence: Precedence.postfix, - innerPrecedence: Precedence.postfix, - associative: true); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// appending the given postfix [operator]. This could be used, for example, - /// to add a null check. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan addUnaryPostfix( - NodeProducingEditPlan innerPlan, TokenType operator, - {AtomicEditInfo? info}) => - addPostfix(innerPlan, operator.lexeme, info: info); - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// prepending the given prefix [operator]. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan addUnaryPrefix( - TokenType operator, NodeProducingEditPlan innerPlan, - {AtomicEditInfo? info}) { - assert(innerPlan.sourceNode is Expression); - return surround(innerPlan, - prefix: [AtomicEdit.insert(operator.lexeme, info: info)], - outerPrecedence: Precedence.prefix, - innerPrecedence: Precedence.prefix, - associative: true); - } - - /// Creates a [_PassThroughBuilder] object based around [node]. - /// - /// Exposed so that we can substitute a mock class in unit tests. - @visibleForTesting - PassThroughBuilder createPassThroughBuilder(AstNode? node) => - _PassThroughBuilderImpl(node); - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// dropping the given nullability [hint]. - NodeProducingEditPlan dropNullabilityHint( - NodeProducingEditPlan innerPlan, HintComment hint, - {AtomicEditInfo? info}) { - var affixPlan = innerPlan is _CommentAffixPlan - ? innerPlan - : _CommentAffixPlan(innerPlan); - var changes = hint.changesToRemove(sourceText, info: info); - assert(affixPlan.end! <= _offsetForChanges(changes)!); - affixPlan.end = _endForChanges(changes); - affixPlan._postfixChanges += changes; - return affixPlan; - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// appending an informative ` `, to illustrate that the type is non-nullable. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan explainNonNullable(NodeProducingEditPlan innerPlan, - {AtomicEditInfo? info}) { - assert(innerPlan.sourceNode is TypeAnnotation); - return surround(innerPlan, - suffix: [AtomicEdit.insert(' ', info: info, isInformative: true)]); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// removing from the source code any code that is in [sourceNode] but not in - /// [innerPlan.sourceNode]. This is intended to be used to drop unnecessary - /// syntax (for example, to drop an unnecessary cast). - /// - /// If no changes are required to the AST node that is being extracted, the - /// caller may create innerPlan using [EditPlan.passThrough]. - /// - /// Optional parameters [infoBefore] and [infoAfter] contain information about - /// why the change was made. The reason there are two of these parameters is - /// because in general, two chunks of source code will be removed: the code - /// coming before [innerPlan.sourceNode] and the code coming after it. - /// - /// [innerPlan] will be finalized as a side effect (either immediately or when - /// the newly created plan is finalized), so it should not be re-used by the - /// caller. - NodeProducingEditPlan extract( - AstNode? sourceNode, NodeProducingEditPlan innerPlan, - {AtomicEditInfo? infoBefore, - AtomicEditInfo? infoAfter, - bool alwaysDelete = false}) { - var parent = innerPlan.sourceNode!.parent; - if (!identical(parent, sourceNode) && parent is ParenthesizedExpression) { - innerPlan = _ProvisionalParenEditPlan(parent, innerPlan); - } - return _ExtractEditPlan( - sourceNode, innerPlan, this, infoBefore, infoAfter, alwaysDelete); - } - - /// Converts [plan] to a representation of the concrete edits that need - /// to be made to the source file. These edits may be converted into - /// [SourceEdit]s using the extensions [AtomicEditList] and [AtomicEditMap]. - /// - /// Finalizing an [EditPlan] is a destructive operation; it should not be used - /// again after it is finalized. - Map>? finalize(EditPlan plan) { - // Convert to a plan for the top level CompilationUnit. - var parent = plan.parentNode; - if (parent != null) { - var unit = parent.thisOrAncestorOfType(); - plan = passThrough(unit, innerPlans: [plan]); - } - // The plan for a compilation unit should always be a NodeProducingEditPlan. - // So we can just ask it for its changes. - return (plan as NodeProducingEditPlan)._getChanges(false); - } - - /// Creates a new edit plan that adds an informative message to the given - /// [token]. - /// - /// The created edit plan should be inserted into the list of inner plans for - /// a pass-through plan targeted at the [containingNode]. See [passThrough]. - EditPlan informativeMessageForToken(AstNode containingNode, Token token, - {AtomicEditInfo? info}) { - return _TokenChangePlan(containingNode, { - token.offset: [ - AtomicEdit.delete(token.lexeme.length, info: info, isInformative: true) - ] - }); - } - - /// Creates a new edit plan that inserts the text indicated by [edits] at the - /// given [offset]. - /// - /// The created edit will have the given [parentNode]. In general this should - /// be the innermost AST node containing the given [offset]. - EditPlan insertText(AstNode parentNode, int offset, List edits) { - assert(!edits.any((edit) => !edit.isInsertion), - 'All edits should be insertions'); - return _TokenChangePlan(parentNode, {offset: edits}); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// appending a `?`, to make a type nullable. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan makeNullable(NodeProducingEditPlan innerPlan, - {AtomicEditInfo? info}) { - var sourceNode = innerPlan.sourceNode; - assert(sourceNode is TypeAnnotation || - sourceNode is FunctionTypedFormalParameter || - (sourceNode is FieldFormalParameter && sourceNode.parameters != null)); - return surround(innerPlan, suffix: [AtomicEdit.insert('?', info: info)]); - } - - /// Creates a new edit plan that makes no changes to [node], but may make - /// changes to some of its descendants (specified via [innerPlans]). - /// - /// Note that the [innerPlans] must be specified in document order. - /// - /// All plans in [innerPlans] will be finalized as a side effect (either - /// immediately or when the newly created plan is finalized), so they should - /// not be re-used by the caller. - NodeProducingEditPlan passThrough(AstNode? node, - {Iterable innerPlans = const []}) { - // It's possible that some of the inner plans are nested more deeply within - // [node] than others. We want to group these inner plans together into - // pass through plans at each level in the AST until we bubble up to [node]. - // To do so, we form a stack of [_PassThroughBuilder] objects to handle each - // level of AST depth, where the first entry in the stack corresponds to - // [node], and each subsequent entry will correspond to a child of the - // previous. - var builderStack = [createPassThroughBuilder(node)]; - var ancestryPath = []; - for (var plan in innerPlans) { - // Compute the ancestryPath (the path from `plan.parentNode` up to - // `node`). Note that whereas builderStack walks stepwise down the AST, - // ancestryStack will walk stepwise up the AST, with the last entry of - // ancestryStack corresponding to the first entry of builderStack. We - // re-use the same list for each loop iteration to reduce GC load. - ancestryPath.clear(); - for (var parent = plan.parentNode; - !identical(parent, node); - parent = parent!.parent) { - ancestryPath.add(parent); - } - ancestryPath.add(node); - // Find the deepest entry in builderStack that's on the ancestryPath. - var builderIndex = _findMatchingBuilder(builderStack, ancestryPath); - // We're finished with all builders beyond that entry. - while (builderStack.length > builderIndex + 1) { - var passThrough = builderStack.removeLast().finish(this); - builderStack.last.add(passThrough); - } - // And we may need to add new builders to make our way down to - // `plan.parentNode`. - while (builderStack.length < ancestryPath.length) { - // Since builderStack and ancestryPath walk in different directions - // through the AST, when building entry builderIndex, we need to count - // backwards from the end of ancestryPath to figure out which node to - // associate the builder with. - builderStack.add(createPassThroughBuilder( - ancestryPath[ancestryPath.length - builderStack.length - 1])); - } - // Now the deepest entry in the builderStack corresponds to - // `plan.parentNode`, so we can add the plan to it. - builderStack.last.add(plan); - } - // We're now finished with all builders. - while (true) { - var passThrough = builderStack.removeLast().finish(this); - if (builderStack.isEmpty) return passThrough; - builderStack.last.add(passThrough); - } - } - - /// Creates a new edit plan that removes [sourceNode] from the AST. - /// - /// [sourceNode] must be one element of a variable length sequence maintained - /// by [sourceNode]'s parent (for example, a statement in a block, an element - /// in a list, a declaration in a class, etc.). If it is not, an exception is - /// thrown. - /// - /// Optional argument [info] contains information about why the change was - /// made. - EditPlan removeNode(AstNode sourceNode, {AtomicEditInfo? info}) { - var result = tryRemoveNode(sourceNode, info: info); - if (result == null) { - var parent = sourceNode.parent; - throw StateError( - 'Cannot remove node whose parent is of type ${parent.runtimeType}'); - } - return result; - } - - /// Creates a new edit plan that removes a sequence of adjacent nodes from - /// the AST, starting with [firstSourceNode] and ending with [lastSourceNode]. - /// - /// [firstSourceNode] and [lastSourceNode] must be elements of a variable - /// length sequence maintained by their (common) parent (for example, - /// statements in a block, elements in a list, declarations in a class, etc.) - /// [lastSourceNode] must come after [firstSourceNode]. - /// - /// If [firstSourceNode] and [lastSourceNode] are the same node, then the - /// behavior is identical to [removeNode] (i.e. just the one node is removed). - /// - /// Optional argument [info] contains information about why the change was - /// made. - EditPlan removeNodes(AstNode firstSourceNode, AstNode lastSourceNode, - {AtomicEditInfo? info}) { - var parent = firstSourceNode.parent; - assert(identical(lastSourceNode.parent, parent)); - var sequenceNodes = _computeSequenceNodes(parent); - if (sequenceNodes == null) { - throw StateError( - 'Cannot remove node whose parent is of type ${parent.runtimeType}'); - } - var firstIndex = sequenceNodes.indexOf(firstSourceNode); - assert(firstIndex != -1); - var lastIndex = sequenceNodes.indexOf(lastSourceNode, firstIndex); - assert(lastIndex >= firstIndex); - return _RemoveEditPlan(parent, firstIndex, lastIndex, info); - } - - /// Creates a new edit plan that removes null awareness from [sourceNode]. - /// - /// The created edit plan should be inserted into the list of inner plans for - /// a pass-through plan targeted at the source node. See [passThrough]. - /// - /// Optional argument [info] contains information about why the change was - /// made. - /// - /// Optional argument [isInformative] indicates whether the comment is simply - /// informative, or should actually be applied to the final output (the - /// default). - EditPlan removeNullAwareness(Expression sourceNode, - {AtomicEditInfo? info, bool isInformative = false}) { - Token? operator; - if (sourceNode is MethodInvocation) { - operator = sourceNode.operator; - } else if (sourceNode is PropertyAccess) { - operator = sourceNode.operator; - } else { - throw StateError( - 'Tried to remove null awareness from an unexpected node type: ' - '${sourceNode.runtimeType}'); - } - assert(operator!.type == TokenType.QUESTION_PERIOD); - return _TokenChangePlan(sourceNode, { - operator!.offset: [ - AtomicEdit.delete(1, info: info, isInformative: isInformative) - ] - }); - } - - /// Creates a new edit plan that replaces the contents of [sourceNode] with - /// the given [replacement] text. - /// - /// If the edit plan is going to be used in a context where an expression is - /// expected, additional arguments should be provided to control the behavior - /// of parentheses insertion and deletion: [precedence] indicates the - /// precedence of the resulting expression. [endsInCascade] indicates whether - /// the resulting plan will end in a cascade. - /// - /// Optional argument [info] contains information about why the change was - /// made. - NodeProducingEditPlan replace( - AstNode sourceNode, List replacement, - {Precedence precedence = Precedence.primary, - bool endsInCascade = false, - AtomicEditInfo? info}) { - assert(!replacement.any((edit) => !edit.isInsertion), - 'All edits should be insertions'); - return _SimpleEditPlan(sourceNode, precedence, endsInCascade, { - sourceNode.offset: [ - AtomicEdit.delete(sourceNode.length, info: info), - ...replacement - ] - }); - } - - /// Creates a new edit plan that replaces [token] with the given [replacement] - /// text. - /// - /// [parentNode] should be the innermost AST node containing [token]. - EditPlan replaceToken(AstNode parentNode, Token token, String replacement, - {AtomicEditInfo? info}) { - return _TokenChangePlan(parentNode, { - token.offset: [AtomicEdit.replace(token.length, replacement, info: info)] - }); - } - - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// surrounding it with [prefix] and [suffix] text. This could be used, for - /// example, to add a cast. - /// - /// Note that it's tricky to get precedence correct. When possible, use one - /// of the other methods in this class, such as [addBinaryPostfix], - /// [addBinaryPrefix], [addUnaryPostfix], or [addUnaryPrefix]. - /// - /// If the edit plan is going to be used in a context where an expression is - /// expected, additional arguments should be provided to control the behavior - /// of parentheses insertion and deletion: [outerPrecedence] indicates the - /// precedence of the resulting expression. [innerPrecedence] indicates the - /// precedence that is required for [innerPlan]. [associative] indicates - /// whether it is allowed for [innerPlan]'s precedence to match - /// [innerPrecedence]. [allowCascade] indicates whether [innerPlan] can end - /// in a cascade section without requiring parentheses. [endsInCascade] - /// indicates whether the resulting plan will end in a cascade. - /// - /// So, for example, if it is desired to append the suffix ` + foo` to an - /// expression, specify `Precedence.additive` for [outerPrecedence] and - /// [innerPrecedence], and `true` for [associative] (since addition associates - /// to the left). - /// - /// Note that [endsInCascade] is ignored if there is no [suffix] (since in - /// this situation, whether the final plan ends in a cascade section will be - /// determined by [innerPlan]). - NodeProducingEditPlan surround(NodeProducingEditPlan innerPlan, - {List? prefix, - List? suffix, - Precedence outerPrecedence = Precedence.primary, - Precedence innerPrecedence = Precedence.none, - bool associative = false, - bool allowCascade = false, - bool endsInCascade = false}) { - var parensNeeded = innerPlan._parensNeeded( - threshold: innerPrecedence, - associative: associative, - allowCascade: allowCascade); - var innerChanges = - innerPlan._getChanges(parensNeeded) ?? >{}; - if (prefix != null) { - (innerChanges[innerPlan.offset] ??= []).insertAll(0, prefix); - } - if (suffix != null) { - (innerChanges[innerPlan.end] ??= []).addAll(suffix); - } - return _SimpleEditPlan( - innerPlan.sourceNode, - outerPrecedence, - suffix == null - ? innerPlan.endsInCascade && !parensNeeded - : endsInCascade, - innerChanges); - } - - /// Tries to create a new edit plan that removes [node] from the AST. - /// - /// [node] must be one element of a variable length sequence maintained by - /// [node]'s parent (for example, a statement in a block, an element in a - /// list, a declaration in a class, etc.). If it is not, `null` is returned. - /// - /// Optional argument [info] contains information about why the change was - /// made. - EditPlan? tryRemoveNode(AstNode sourceNode, - {List? sequenceNodes, AtomicEditInfo? info}) { - var parent = sourceNode.parent; - sequenceNodes ??= _computeSequenceNodes(parent); - if (sequenceNodes == null) { - return null; - } - var index = sequenceNodes.indexOf(sourceNode); - assert(index != -1); - return _RemoveEditPlan(parent, index, index, info); - } - - /// Walks backward through the source text, starting at [offset] and stopping - /// before passing any non-whitespace character. - /// - /// Does not walk further than [limit] (which should be less than or equal to - /// [offset]). - int _backAcrossWhitespace(int offset, int limit) { - assert(limit <= offset); - return limit + sourceText!.substring(limit, offset).trimRight().length; - } - - /// Walks backward through the source text, starting at [offset] and stopping - /// when the beginning of the line is reached. - /// - /// If [offset] is at the beginning of the line, it is returned unchanged. - int _backToLineStart(int offset) { - var lineNumber = lineInfo!.getLocation(offset).lineNumber; - // lineNumber is one-based, but lineInfo.lineStarts expects a zero-based - // index, so we need `lineInfo.lineStarts[lineNumber - 1]`. - return lineInfo!.lineStarts[lineNumber - 1]; - } - - int? _endForChanges(Map> changes) { - int? result; - for (var entry in changes.entries) { - var end = entry.key; - for (var edit in entry.value) { - end = end! + edit.length; - } - if (result == null || end! > result) result = end; - } - return result; - } - - /// Finds the deepest entry in [builderStack] that matches an entry in - /// [ancestryStack], taking advantage of the fact that [builderStack] walks - /// stepwise down the AST, and [ancestryStack] walks stepwise up the AST, with - /// the last entry of [ancestryStack] corresponding to the first entry of - /// [builderStack]. - int _findMatchingBuilder( - List builderStack, List ancestryStack) { - var builderIndex = builderStack.length - 1; - while (builderIndex > 0) { - var ancestryIndex = ancestryStack.length - builderIndex - 1; - if (ancestryIndex >= 0 && - identical( - builderStack[builderIndex].node, ancestryStack[ancestryIndex])) { - break; - } - --builderIndex; - } - return builderIndex; - } - - /// Walks forward through the source text, starting at [offset] and stopping - /// before passing any non-whitespace character. - /// - /// Does not walk further than [limit] (which should be greater than or equal - /// to [offset]). - int _forwardAcrossWhitespace(int offset, int limit) { - return limit - sourceText!.substring(offset, limit).trimLeft().length; - } - - /// Walks forward through the source text, starting at [offset] and stopping - /// at the beginning of the next line (or at the end of the document, if this - /// line is the last line). - int _forwardToLineEnd(int offset) { - int lineNumber = lineInfo!.getLocation(offset).lineNumber; - // lineNumber is one-based, so if it is equal to - // `lineInfo.lineStarts.length`, then we are on the last line. - if (lineNumber >= lineInfo!.lineStarts.length) { - return sourceText!.length; - } - // lineInfo.lineStarts expects a zero-based index, so - // `lineInfo.lineStarts[lineNumber]` gives us the beginning of the next - // line. - return lineInfo!.lineStarts[lineNumber]; - } - - /// Determines whether the given source [offset] comes just after one of the - /// characters in [characters]. - bool _isJustAfter(int offset, List characters) => - offset > 0 && characters.contains(sourceText![offset - 1]); - - /// Determines whether the given source [end] comes just before one of the - /// characters in [characters]. - bool _isJustBefore(int end, List characters) => - end < sourceText!.length && characters.contains(sourceText![end]); - - /// Determines whether the given source [end] comes just before whitespace. - /// For the purpose of this check, the end of the file is considered - /// whitespace. - bool _isJustBeforeWhitespace(int end) => - end >= sourceText!.length || _isWhitespaceRange(end, end + 1); - - /// Determines if the characters between [offset] and [end] in the source text - /// are all whitespace characters. - bool _isWhitespaceRange(int offset, int end) { - return sourceText!.substring(offset, end).trimRight().isEmpty; - } - - int? _offsetForChanges(Map> changes) { - int? result; - for (var key in changes.keys) { - if (result == null || key! < result) result = key; - } - return result; - } - - /// If the given [node] maintains a variable-length sequence of child nodes, - /// returns a list containing those child nodes, otherwise returns `null`. - /// - /// The returned list may or may not be the exact list used by the node to - /// maintain its child nodes. For example, [CompilationUnit] maintains its - /// directives and declarations in separate lists, so the returned list is - /// a new list containing both directives and declarations. - static List? _computeSequenceNodes(AstNode? node) { - if (node is Block) { - return node.statements; - } else if (node is ListLiteral) { - return node.elements; - } else if (node is SetOrMapLiteral) { - return node.elements; - } else if (node is ArgumentList) { - return node.arguments; - } else if (node is FormalParameter) { - return node.metadata; - } else if (node is FormalParameterList) { - return node.parameters; - } else if (node is VariableDeclarationList) { - return node.variables; - } else if (node is TypeArgumentList) { - return node.arguments; - } else if (node is TypeParameterList) { - return node.typeParameters; - } else if (node is EnumDeclaration) { - return node.constants; - } else if (node is ClassDeclaration) { - return node.members; - } else if (node is CompilationUnit) { - return [...node.directives, ...node.declarations]; - } else if (node is MethodDeclaration) { - return node.metadata; - } else { - return null; - } - } -} - -/// Specialization of [EditPlan] for the situation where the text being produced -/// represents a single expression (i.e. an expression, statement, class -/// declaration, etc.) -abstract class NodeProducingEditPlan extends EditPlan { - /// The AST node to which the edit plan applies. - final AstNode? sourceNode; - - NodeProducingEditPlan._(this.sourceNode) : super._(); - - /// Offset just past the end of the source text affected by this plan. - int? get end => sourceNode!.end; - - /// If the result of executing this [EditPlan] will be an expression, - /// indicates whether the expression will end in an unparenthesized cascade. - @visibleForTesting - bool get endsInCascade; - - /// Offset of the start of the source text affected by this plan. - int? get offset => sourceNode!.offset; - - @override - AstNode? get parentNode => sourceNode!.parent; - - /// Determines whether the text produced by this [EditPlan] would need - /// parentheses if it were to be used as a replacement for its [sourceNode]. - /// - /// If this [EditPlan] would produce an expression that ends in a cascade, it - /// will be necessary to search the [sourceNode]'s ancestors to see if any of - /// them represents a cascade section (and hence, parentheses are required). - /// If a non-null value is provided for [cascadeSearchLimit], it is the most - /// distant ancestor that will be searched. - @visibleForTesting - bool? parensNeededFromContext(AstNode? cascadeSearchLimit) { - if (sourceNode is! Expression) return false; - var parent = sourceNode!.parent; - return parent == null - ? false - : parent - .accept(_ParensNeededFromContextVisitor(this, cascadeSearchLimit)); - } - - /// Modifies [changes] to insert parentheses enclosing the [sourceNode]. This - /// works even if [changes] already includes modifications at the beginning or - /// end of [sourceNode]--the parentheses are inserted outside of any - /// preexisting changes. - Map>? _createAddParenChanges( - Map>? changes) { - changes ??= {}; - (changes[offset] ??= []).insert(0, const AtomicEdit.insert('(')); - (changes[end] ??= []).add(const AtomicEdit.insert(')')); - return changes; - } - - /// Computes the necessary set of [changes] for this [EditPlan], either - /// including or not including parentheses depending on the value of [parens]. - /// - /// An [EditPlan] for which [_getChanges] has been called is considered to be - /// finalized. - Map>? _getChanges(bool parens); - - /// Determines if the text that would be produced by [EditPlan] needs to be - /// surrounded by parens, based on the context in which it will be used. - bool _parensNeeded( - {required Precedence threshold, - bool associative = false, - bool allowCascade = false}); -} - -/// Data structure that accumulates together a set of [EditPlans] sharing a -/// common parent node, and groups them together into an [EditPlan] with a -/// parent node one level up the AST. -@visibleForTesting -abstract class PassThroughBuilder { - /// The AST node that is the parent of all the [EditPlan]s being accumulated. - AstNode? get node; - - /// Accumulate another edit plan. - void add(EditPlan innerPlan); - - /// Called when no more edit plans need to be added. Returns the final - /// [EditPlan]. - NodeProducingEditPlan finish(EditPlanner planner); -} - -/// [EditPlan] that wraps an inner plan with optional prefix and suffix changes. -class _CommentAffixPlan extends _NestedEditPlan { - Map>? _prefixChanges; - - Map>? _postfixChanges; - - @override - int? offset; - - @override - int? end; - - _CommentAffixPlan(NodeProducingEditPlan innerPlan) - : offset = innerPlan.offset, - end = innerPlan.end, - super(innerPlan.sourceNode, innerPlan); - - @override - Map>? _getChanges(bool parens) => - _prefixChanges + innerPlan._getChanges(parens) + _postfixChanges; -} - -/// Visitor that determines whether a given [AstNode] ends in a cascade. -class _EndsInCascadeVisitor extends UnifyingAstVisitor { - bool endsInCascade = false; - - final int end; - - _EndsInCascadeVisitor(this.end); - - @override - void visitCascadeExpression(CascadeExpression node) { - if (node.end != end) return; - endsInCascade = true; - } - - @override - void visitNode(AstNode node) { - if (node.end != end) return; - node.visitChildren(this); - } -} - -/// [EditPlan] representing an "extraction" of an inner AST node, e.g. replacing -/// `a + b * c` with `b + c`. -/// -/// Defers computation of whether parentheses are needed to the inner plan. -class _ExtractEditPlan extends _NestedEditPlan { - final EditPlanner _planner; - - final AtomicEditInfo? _infoBefore; - - final AtomicEditInfo? _infoAfter; - - /// Whether text-to-be-removed should be removed (as opposed to commented out) - /// even when [EditPlan.removeViaComments] is true. - final bool _alwaysDelete; - - _ExtractEditPlan(super.sourceNode, super.innerPlan, this._planner, - this._infoBefore, this._infoAfter, this._alwaysDelete); - - @override - Map>? _getChanges(bool parens) { - // Get the inner changes. If they already have provisional parens and we - // need them, use them. - var useInnerParens = parens && innerPlan is _ProvisionalParenEditPlan; - var changes = innerPlan._getChanges(useInnerParens); - // TODO(paulberry): don't remove comments - _RemovalStyle leadingChangeRemovalStyle; - _RemovalStyle trailingChangeRemovalStyle; - if (_alwaysDelete || !_planner.removeViaComments!) { - leadingChangeRemovalStyle = _RemovalStyle.delete; - trailingChangeRemovalStyle = _RemovalStyle.delete; - } else { - leadingChangeRemovalStyle = _RemovalStyle.commentSpace; - trailingChangeRemovalStyle = _RemovalStyle.spaceComment; - } - // Extract the inner expression. - changes = _removeCode(offset!, innerPlan.offset!, leadingChangeRemovalStyle, - _infoBefore) + - changes + - _removeCode( - innerPlan.end!, end!, trailingChangeRemovalStyle, _infoAfter); - // Apply parens if needed. - if (parens && !useInnerParens) { - changes = _createAddParenChanges(changes); - } - return changes; - } -} - -/// [EditPlan] representing additional edits performed on the result of a -/// previous [innerPlan]. -/// -/// By default, defers computation of whether parentheses are needed to the -/// inner plan. -abstract class _NestedEditPlan extends NodeProducingEditPlan { - final NodeProducingEditPlan innerPlan; - - _NestedEditPlan(super.sourceNode, this.innerPlan) : super._(); - - @override - bool get endsInCascade => innerPlan.endsInCascade; - - @override - bool _parensNeeded( - {required Precedence threshold, - bool associative = false, - bool allowCascade = false}) => - innerPlan._parensNeeded( - threshold: threshold, - associative: associative, - allowCascade: allowCascade); -} - -/// Visitor that determines whether an [_editPlan] needs to be parenthesized -/// based on the context surrounding its source node. To use this class, visit -/// the source node's parent. -class _ParensNeededFromContextVisitor extends GeneralizingAstVisitor { - final NodeProducingEditPlan _editPlan; - - /// If [_editPlan] would produce an expression that ends in a cascade, it - /// will be necessary to search the [_target]'s ancestors to see if any of - /// them represents a cascade section (and hence, parentheses are required). - /// If a non-null value is provided for [_cascadeSearchLimit], it is the most - /// distant ancestor that will be searched. - final AstNode? _cascadeSearchLimit; - - _ParensNeededFromContextVisitor(this._editPlan, this._cascadeSearchLimit) { - assert(_target is Expression); - } - - AstNode? get _target => _editPlan.sourceNode; - - @override - bool visitAsExpression(AsExpression node) { - if (identical(_target, node.expression)) { - return _editPlan._parensNeeded(threshold: Precedence.relational); - } else { - return false; - } - } - - @override - bool visitAssignmentExpression(AssignmentExpression node) { - if (identical(_target, node.rightHandSide)) { - return _editPlan._parensNeeded( - threshold: Precedence.none, - allowCascade: !_isRightmostDescendantOfCascadeSection(node)); - } else { - return false; - } - } - - @override - bool visitAwaitExpression(AwaitExpression node) { - assert(identical(_target, node.expression)); - return _editPlan._parensNeeded( - threshold: Precedence.prefix, associative: true); - } - - @override - bool visitBinaryExpression(BinaryExpression node) { - var precedence = node.precedence; - return _editPlan._parensNeeded( - threshold: precedence, - associative: identical(_target, node.leftOperand) && - precedence != Precedence.relational && - precedence != Precedence.equality); - } - - @override - bool visitCascadeExpression(CascadeExpression node) { - if (identical(_target, node.target)) { - return _editPlan._parensNeeded( - threshold: Precedence.cascade, associative: true, allowCascade: true); - } else { - return false; - } - } - - @override - bool visitConditionalExpression(ConditionalExpression node) { - if (identical(_target, node.condition)) { - return _editPlan._parensNeeded(threshold: Precedence.conditional); - } else { - return _editPlan._parensNeeded(threshold: Precedence.none); - } - } - - @override - bool visitExtensionOverride(ExtensionOverride node) { - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } - - @override - bool visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { - assert(identical(_target, node.function)); - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } - - @override - bool visitIndexExpression(IndexExpression node) { - if (identical(_target, node.target)) { - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } else { - return false; - } - } - - @override - bool visitIsExpression(IsExpression node) { - if (identical(_target, node.expression)) { - return _editPlan._parensNeeded(threshold: Precedence.relational); - } else { - return false; - } - } - - @override - bool visitMethodInvocation(MethodInvocation node) { - // Note: it's tempting to assert identical(_target, node.target) here, - // because in a method invocation like `x.m(...)`, the only AST node that's - // a child of the method invocation and semantically represents an - // expression is the target (`x` in this example). Unfortunately, that - // doesn't work, because even though `m` isn't semantically an expression, - // it's represented in the analyzer AST as an identifier and Identifier - // implements Expression. So we have to handle both `x` and `m`. - // - // Fortunately we don't have to do any extra work to handle `m`, because it - // will always be an identifier, hence it will always be high precedence and - // it will never require parentheses. So we just do the correct logic for - // the target, without asserting. - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } - - @override - bool visitNode(AstNode node) { - return false; - } - - @override - bool visitParenthesizedExpression(ParenthesizedExpression node) { - assert(identical(_target, node.expression)); - return false; - } - - @override - bool visitPostfixExpression(PostfixExpression node) { - assert(identical(_target, node.operand)); - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } - - @override - bool visitPrefixedIdentifier(PrefixedIdentifier node) { - if (identical(_target, node.prefix)) { - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } else { - assert(identical(_target, node.identifier)); - return _editPlan._parensNeeded( - threshold: Precedence.primary, associative: true); - } - } - - @override - bool visitPrefixExpression(PrefixExpression node) { - assert(identical(_target, node.operand)); - return _editPlan._parensNeeded( - threshold: Precedence.prefix, associative: true); - } - - @override - bool visitPropertyAccess(PropertyAccess node) { - if (identical(_target, node.target)) { - return _editPlan._parensNeeded( - threshold: Precedence.postfix, associative: true); - } else { - assert(identical(_target, node.propertyName)); - return _editPlan._parensNeeded( - threshold: Precedence.primary, associative: true); - } - } - - @override - bool visitThrowExpression(ThrowExpression node) { - assert(identical(_target, node.expression)); - return _editPlan._parensNeeded( - threshold: Precedence.assignment, - associative: true, - allowCascade: !_isRightmostDescendantOfCascadeSection(node)); - } - - /// Searches the ancestors of [node] to determine if it is the rightmost - /// descendant of a cascade section. (If this is the case, parentheses may be - /// required). The search is limited by [_cascadeSearchLimit]. - bool _isRightmostDescendantOfCascadeSection(AstNode node) { - while (true) { - var parent = node.parent; - if (parent == null) { - // No more ancestors, so we can stop. - return false; - } - if (parent is CascadeExpression && !identical(parent.target, node)) { - // Node is a cascade section. - return true; - } - if (parent.end != node.end) { - // Node is not the rightmost descendant of parent, so we can stop. - return false; - } - if (identical(node, _cascadeSearchLimit)) { - // We reached the cascade search limit so we don't have to look any - // further. - return false; - } - node = parent; - } - } -} - -class _PassThroughBuilderImpl implements PassThroughBuilder { - @override - final AstNode? node; - - /// The [EditPlan]s accumulated so far. - final List innerPlans = []; - - /// The [EditPlanner] currently being used to create this - /// [_PassThroughEditPlan]. - late EditPlanner planner; - - /// Determination of whether the resulting [EditPlan] will end in a cascade, - /// or `null` if it is not yet known. - bool? endsInCascade; - - /// The set of changes aggregated together so far. - Map>? changes; - - /// If [node] is a sequence, the list of its child nodes. Otherwise `null`. - List? sequenceNodes; - - /// If [node] is a sequence that uses separators (e.g. a list literal, which - /// uses comma separators), a list of its separators. Otherwise `null`. - List? separators; - - /// If [separators] is non-null, and nodes are being removed from the - /// sequence, this boolean indicates whether each node should be removed along - /// with the separator that *precedes* it. - /// - /// `false` indicates that each node should be removed along with the - /// separator that *follows* it. - bool removeLeadingSeparators = false; - - _PassThroughBuilderImpl(this.node); - - @override - void add(EditPlan innerPlan) { - assert(identical(innerPlan.parentNode, node)); - innerPlans.add(innerPlan); - } - - @override - NodeProducingEditPlan finish(EditPlanner planner) { - this.planner = planner; - var node = this.node; - if (node is ParenthesizedExpression) { - assert(innerPlans.length <= 1); - var innerPlan = innerPlans.isEmpty - ? planner.passThrough(node.expression) - : innerPlans[0]; - if (innerPlan is NodeProducingEditPlan) { - return _ProvisionalParenEditPlan(node, innerPlan); - } - } - - // Make a provisional determination of whether the result will end in a - // cascade. - // TODO(paulberry): can we make some of these computations lazy? - endsInCascade = node is CascadeExpression ? true : null; - sequenceNodes = EditPlanner._computeSequenceNodes(node); - separators = - sequenceNodes == null ? null : _computeSeparators(node, sequenceNodes); - _processPlans(); - Precedence precedence; - if (node is FunctionExpression && node.body is ExpressionFunctionBody) { - // To avoid ambiguities when adding `as Type` after a function expression, - // assume assignment precedence. - // TODO(paulberry): this is a hack - see - // https://github.com/dart-lang/sdk/issues/40536 - precedence = Precedence.assignment; - } else if (node is Expression) { - precedence = node.precedence; - } else { - precedence = Precedence.primary; - } - return _PassThroughEditPlan._( - node, precedence, endsInCascade ?? node!.endsInCascade, changes); - } - - /// Starting at index [planIndex] of [innerPlans] (whose value is [plan]), - /// scans forward to see if there is a range of inner plans that remove a - /// contiguous range of AST nodes. - /// - /// Returns the index into [innerPlans] of the last such contiguous plan, or - /// [planIndex] if a contiguous range of removals wasn't found. - int _findConsecutiveRemovals(int planIndex, _RemoveEditPlan plan) { - assert(identical(innerPlans[planIndex], plan)); - var lastRemovePlanIndex = planIndex; - var lastRemoveEditPlan = plan; - while (lastRemovePlanIndex + 1 < innerPlans.length) { - var nextPlan = innerPlans[lastRemovePlanIndex + 1]; - if (nextPlan is _RemoveEditPlan) { - if (nextPlan.firstChildIndex == lastRemoveEditPlan.lastChildIndex + 1) { - // Removals are consecutive. Slurp up. - lastRemovePlanIndex++; - lastRemoveEditPlan = nextPlan; - continue; - } - } - break; - } - return lastRemovePlanIndex; - } - - /// Processes an inner plan of type [NodeProducingEditPlan]. - void _handleNodeProducingEditPlan(NodeProducingEditPlan innerPlan) { - var parensNeeded = innerPlan.parensNeededFromContext(node)!; - assert(_checkParenLogic(innerPlan, parensNeeded)); - if (!parensNeeded && innerPlan is _ProvisionalParenEditPlan) { - var innerInnerPlan = innerPlan.innerPlan; - if (innerInnerPlan is _PassThroughEditPlan) { - // Input source code had redundant parens, so keep them. - parensNeeded = true; - } - } - changes += innerPlan._getChanges(parensNeeded); - // Note: we use innerPlan.sourceNode.end here instead of innerPlan.end, - // because what we care about is the input grammar, so we don't want to be - // fooled by any whitespace or comments included in the innerPlan. - if (endsInCascade == null && innerPlan.sourceNode!.end == node!.end) { - endsInCascade = !parensNeeded && innerPlan.endsInCascade; - } - } - - /// Processes one or more inner plans of type [_RemoveEditPlan], and returns - /// an updated [planIndex] pointing to the next inner plan to be processed. - /// - /// [firstPlan] should be the plan located at index [planIndex]. - int _handleRemoveEditPlans(_RemoveEditPlan firstPlan, int planIndex) { - assert(identical(innerPlans[planIndex], firstPlan)); - assert(identical(firstPlan.parentNode, node)); - var firstPlanIndex = planIndex; - var lastPlanIndex = _findConsecutiveRemovals(firstPlanIndex, firstPlan); - var lastPlan = innerPlans[lastPlanIndex] as _RemoveEditPlan; - int lastRemovalEnd; - int nextRemovalOffset; - removeLeadingSeparators = separators != null && - firstPlan.firstChildIndex != 0 && - lastPlan.lastChildIndex >= separators!.length; - if (planner.removeViaComments!) { - nextRemovalOffset = _removalOffset(firstPlan); - lastRemovalEnd = _removalEnd(lastPlan); - } else { - var firstRemovalOffset = _removalOffset(firstPlan); - var firstLineStart = planner._backToLineStart(firstRemovalOffset); - var startsOnLineBoundary = - planner._isWhitespaceRange(firstLineStart, firstRemovalOffset); - lastRemovalEnd = _removalEnd(lastPlan); - var lastLineEnd = planner._forwardToLineEnd(lastRemovalEnd); - var endsOnLineBoundary = - planner._isWhitespaceRange(lastRemovalEnd, lastLineEnd); - if (!endsOnLineBoundary) { - // E.g. removing B and C, and possibly A, from `A; B; C; D;`. Want to - // remove the whitespace after `C;`. - lastRemovalEnd = - planner._forwardAcrossWhitespace(lastRemovalEnd, lastLineEnd); - } else if (!startsOnLineBoundary) { - // E.g. removing B and C from `A; B; C;`. Want to remove the whitespace - // before `B`. - firstRemovalOffset = - planner._backAcrossWhitespace(firstRemovalOffset, firstLineStart); - } else { - // Removing whole lines. - firstRemovalOffset = firstLineStart; - lastRemovalEnd = lastLineEnd; - } - if (firstPlanIndex == 0 && lastPlanIndex == sequenceNodes!.length - 1) { - // We're removing everything. Try to remove additional whitespace so - // that we're left with just `()`, `{}`, or `[]`. - var candidateFirstRemovalOffset = planner._backAcrossWhitespace( - firstRemovalOffset, min(firstRemovalOffset, node!.offset)); - if (planner - ._isJustAfter(candidateFirstRemovalOffset, const ['(', '[', '{'])) { - var candidateLastRemovalEnd = - planner._forwardAcrossWhitespace(lastRemovalEnd, node!.end); - if (planner - ._isJustBefore(candidateLastRemovalEnd, const [')', ']', '}'])) { - firstRemovalOffset = candidateFirstRemovalOffset; - lastRemovalEnd = candidateLastRemovalEnd; - } - } - } - nextRemovalOffset = firstRemovalOffset; - } - - for (; planIndex <= lastPlanIndex; planIndex++) { - var innerPlan = innerPlans[planIndex] as _RemoveEditPlan; - var offset = nextRemovalOffset; - int end; - if (planIndex == lastPlanIndex) { - end = lastRemovalEnd; - } else { - var nextInnerPlan = innerPlans[planIndex + 1] as _RemoveEditPlan; - assert(identical(nextInnerPlan.parentNode, node)); - nextRemovalOffset = _removalOffset(nextInnerPlan); - if (planner.removeViaComments!) { - end = _removalEnd(innerPlans[planIndex] as _RemoveEditPlan); - } else { - var lineStart = planner._backToLineStart(nextRemovalOffset); - if (planner._isWhitespaceRange(lineStart, nextRemovalOffset)) { - // The next node to remove starts at the beginning of a line - // (possibly with whitespace before it). Consider the removal of - // the whitespace to be part of removing the next node. - nextRemovalOffset = lineStart; - } - end = nextRemovalOffset; - } - } - changes += _removeCode( - offset, - end, - planner.removeViaComments! - ? _RemovalStyle.spaceInsideComment - : _RemovalStyle.delete, - innerPlan.info); - } - - return planIndex; - } - - /// Walks through the plans in [innerPlans], adjusting them as necessary and - /// collecting their changes in [changes]. - void _processPlans() { - int planIndex = 0; - while (planIndex < innerPlans.length) { - var innerPlan = innerPlans[planIndex]; - if (innerPlan is NodeProducingEditPlan) { - _handleNodeProducingEditPlan(innerPlan); - planIndex++; - } else if (innerPlan is _RemoveEditPlan) { - planIndex = _handleRemoveEditPlans(innerPlan, planIndex); - } else if (innerPlan is _TokenChangePlan) { - changes += innerPlan.changes; - planIndex++; - } else { - throw UnimplementedError('Unrecognized inner plan type'); - } - } - } - - /// Computes the end for the text that should be removed by the given - /// [innerPlan]. - int _removalEnd(_RemoveEditPlan innerPlan) { - if (separators != null && - !removeLeadingSeparators && - innerPlan.lastChildIndex < separators!.length) { - return separators![innerPlan.lastChildIndex].end; - } else { - return sequenceNodes![innerPlan.lastChildIndex].end; - } - } - - /// Computes the offset for the text that should be removed by the given - /// [innerPlan]. - int _removalOffset(_RemoveEditPlan innerPlan) { - if (separators != null && removeLeadingSeparators) { - return separators![innerPlan.firstChildIndex - 1].offset; - } else { - return sequenceNodes![innerPlan.firstChildIndex].offset; - } - } - - static bool _checkParenLogic(EditPlan innerPlan, bool parensNeeded) { - if (innerPlan is _SimpleEditPlan && innerPlan._innerChanges == null) { - if (innerPlan.sourceNode is FunctionExpression) { - // Skip parentheses check for function expressions; it produces false - // failures when examining an expression like `x ?? (y) => z`, due to - // https://github.com/dart-lang/sdk/issues/40536. - // TODO(paulberry): fix this. - } else { - assert( - !parensNeeded, - "Code prior to fixes didn't need parens here, " - "shouldn't need parens now."); - } - } - return true; - } - - /// Compute the set of tokens used by the given [parent] node to separate its - /// [childNodes]. - static List? _computeSeparators( - AstNode? parent, List? childNodes) { - if (parent is Block || - parent is ClassDeclaration || - parent is CompilationUnit || - parent is FormalParameter || - parent is MethodDeclaration) { - // These parent types don't use separators. - return null; - } else { - var result = []; - for (var child in childNodes!) { - var separator = child.endToken.next; - if (separator != null && separator.type == TokenType.COMMA) { - result.add(separator); - } - } - assert(result.length == childNodes.length || - result.length == childNodes.length - 1); - return result; - } - } -} - -/// [EditPlan] representing an AstNode that is not to be changed, but may have -/// some changes applied to some of its descendants. -class _PassThroughEditPlan extends _SimpleEditPlan { - _PassThroughEditPlan._( - super.node, super.precedence, super.endsInCascade, super.innerChanges); -} - -/// [EditPlan] applying to a [ParenthesizedExpression]. Unlike the normal -/// behavior of adding parentheses when needed, [_ProvisionalParenEditPlan] -/// preserves existing parens if they are needed, and removes them if they are -/// not. -/// -/// Defers computation of whether parentheses are needed to the inner plan. -class _ProvisionalParenEditPlan extends _NestedEditPlan { - /// Creates a new edit plan that consists of executing [innerPlan], and then - /// possibly removing surrounding parentheses from the source code. - /// - /// Caller should not re-use [innerPlan] after this call--it (and the data - /// structures it points to) may be incorporated into this edit plan and later - /// modified. - _ProvisionalParenEditPlan( - ParenthesizedExpression super.node, super.innerPlan); - - @override - Map>? _getChanges(bool parens) { - var changes = innerPlan._getChanges(false); - if (!parens) { - changes ??= {}; - (changes[offset] ??= []).insert(0, const AtomicEdit.delete(1)); - (changes[end! - 1] ??= []).add(const AtomicEdit.delete(1)); - } - return changes; - } -} - -/// Enum used by [_ExtractEditPlan._removeCode] to describe how code should be -/// removed. -enum _RemovalStyle { - /// Code should be removed by commenting it out. Inserted comment delimiters - /// should be a comment delimiter followed by a space (i.e. `/* ` and `*/ `). - commentSpace, - - /// Code should be removed by deleting it. - delete, - - /// Code should be removed by commenting it out. Inserted comment delimiters - /// should be a space followed by a comment delimiter (i.e. ` /*` and ` */`). - spaceComment, - - /// Code should be removed by commenting it out. Inserted comment delimiters - /// should have a space inside the comment. - spaceInsideComment, -} - -/// [EditPlan] representing one or more AstNodes that are to be removed from -/// their (common) parent, which must be an AST node that stores a list of -/// sub-nodes. -/// -/// If more than one node is to be removed by this [EditPlan], they must be -/// contiguous. -class _RemoveEditPlan extends EditPlan { - @override - final AstNode? parentNode; - - /// Index of the node to be removed within the parent. - final int firstChildIndex; - - /// Index of the node to be removed within the parent. - final int lastChildIndex; - - final AtomicEditInfo? info; - - _RemoveEditPlan( - this.parentNode, this.firstChildIndex, this.lastChildIndex, this.info) - : super._(); -} - -/// Implementation of [EditPlan] underlying simple cases where no computation -/// needs to be deferred. -class _SimpleEditPlan extends NodeProducingEditPlan { - final Precedence _precedence; - - @override - final bool endsInCascade; - - final Map>? _innerChanges; - - bool _finalized = false; - - _SimpleEditPlan( - super.node, this._precedence, this.endsInCascade, this._innerChanges) - : super._(); - - @override - Map>? _getChanges(bool parens) { - assert(!_finalized); - _finalized = true; - return parens ? _createAddParenChanges(_innerChanges) : _innerChanges; - } - - @override - bool _parensNeeded( - {required Precedence threshold, - bool associative = false, - bool allowCascade = false}) { - if (endsInCascade && !allowCascade) return true; - if (_precedence < threshold) return true; - if (_precedence == threshold && !associative) return true; - return false; - } -} - -/// [EditPlan] representing a change (or changes) to be made to a token in the -/// [parentNode]. -/// -/// This is used, for example, to change the `?.` token of a [MethodInvocation] -/// or [PropertyAccess] to `.`. -class _TokenChangePlan extends EditPlan { - @override - final AstNode parentNode; - - /// The changes to be made. - final Map> changes; - - _TokenChangePlan(this.parentNode, this.changes) : super._(); -} - -/// Extension containing useful operations on a list of [AtomicEdit]s. -extension AtomicEditList on List { - /// Converts a list of [AtomicEdits] to a single [SourceEdit] by concatenating - /// them. - /// - /// If [includeInformative] is `true`, informative edits are included; - /// otherwise they are ignored. - SourceEdit toSourceEdit(int offset, {bool includeInformative = false}) { - var totalLength = 0; - var replacement = ''; - for (var edit in this) { - if (!edit.isInformative || includeInformative) { - totalLength += edit.length; - replacement += edit.replacement; - } - } - return SourceEdit(offset, totalLength, replacement); - } -} - -/// Extension containing useful operations on a map from offsets to lists of -/// [AtomicEdit]s. This data structure is used by [EditPlan]s to accumulate -/// source file changes. -extension AtomicEditMap on Map>? { - /// Destructively combines two change representations. If one or the other - /// input is null, the other input is returned unchanged for efficiency. - Map>? operator +( - Map>? newChanges) { - if (newChanges == null) return this; - if (this == null) { - return newChanges; - } else { - for (var entry in newChanges.entries) { - var currentValue = this![entry.key]; - if (currentValue == null) { - this![entry.key] = entry.value; - } else { - currentValue.addAll(entry.value); - } - } - return this; - } - } - - /// Applies the changes to source file text. - /// - /// If [includeInformative] is `true`, informative edits are included; - /// otherwise they are ignored. - String applyTo(String code, {bool includeInformative = false}) { - return SourceEdit.applySequence( - code, toSourceEdits(includeInformative: includeInformative)); - } - - /// Converts the changes to a list of [SourceEdit]s. The list is reverse - /// sorted by offset so that they can be applied in order. - /// - /// If [includeInformative] is `true`, informative edits are included; - /// otherwise they are ignored. - List toSourceEdits({bool includeInformative = false}) { - return [ - for (var offset in this!.keys.toList()..sort((a, b) => b!.compareTo(a!))) - this![offset]! - .toSourceEdit(offset!, includeInformative: includeInformative) - ]; - } -} - -/// Extension allowing an AstNode to be queried to see if it ends in a cascade -/// expression. -extension EndsInCascadeExtension on AstNode { - @visibleForTesting - bool get endsInCascade { - var visitor = _EndsInCascadeVisitor(end); - accept(visitor); - return visitor.endsInCascade; - } -} diff --git a/pkg/nnbd_migration/lib/src/exceptions.dart b/pkg/nnbd_migration/lib/src/exceptions.dart deleted file mode 100644 index e6493cc7f255..000000000000 --- a/pkg/nnbd_migration/lib/src/exceptions.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:nnbd_migration/src/messages.dart'; - -/// A [StateError] specific to the ways that the NNBD experiment can be -/// misconfigured which may prevent the tool from working. -class ExperimentStatusException extends StateError { - /// The SDK was analyzed without NNBD semantics. - ExperimentStatusException.sdkExperimentDisabled() : super(nnbdExperimentOff); - - /// The SDK does not contain the NNBD sources, it is the pre-unfork copy. - ExperimentStatusException.sdkPreforkSources() : super(sdkNnbdOff); - - /// The user's code imports unmigrated dependencies. - ExperimentStatusException.unmigratedDependencies(List uris) - : super(unmigratedDependenciesError(uris)); - - /// Throw an [ExperimentStatusException] if the [result] seems to have - /// incorrectly configured experiment flags/nnbd sources. - static void sanityCheck(ResolvedUnitResult result) { - final equalsParamType = result.typeProvider.objectType - .getMethod('==')! - .parameters[0] - .type - .getDisplayString(withNullability: true); - if (equalsParamType == 'Object*') { - throw ExperimentStatusException.sdkExperimentDisabled(); - } - - if (equalsParamType != 'Object') { - throw ExperimentStatusException.sdkPreforkSources(); - } - } -} diff --git a/pkg/nnbd_migration/lib/src/expression_checks.dart b/pkg/nnbd_migration/lib/src/expression_checks.dart deleted file mode 100644 index 428bd779c87b..000000000000 --- a/pkg/nnbd_migration/lib/src/expression_checks.dart +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/edge_origin.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; - -/// Container for information gathered during nullability migration about the -/// set of runtime checks that might need to be performed on the value of an -/// expression. -/// -/// TODO(paulberry): we don't currently have any way of distinguishing checks -/// based on the nullability of the type itself (which can be checked by adding -/// a trailing `!`) from checks based on type parameters (which will have to be -/// checked using an `as` expression). -class ExpressionChecks { - /// All nullability edges that are related to this potential check. - final Map edges = {}; - - ExpressionChecks(); -} - -/// [EdgeOrigin] object associated with [ExpressionChecks]. This is a separate -/// object so that it can safely store a pointer to an AST node. (We don't want -/// to store pointers to AST nodes in [ExpressionChecks] objects because they -/// are persisted for the duration of the migration calculation). -class ExpressionChecksOrigin extends EdgeOrigin { - final ExpressionChecks checks; - - ExpressionChecksOrigin(super.source, Expression? super.node, this.checks, - {super.isSetupAssignment = false}); - - @override - String get description => 'data flow'; - - @override - EdgeOriginKind get kind => EdgeOriginKind.expressionChecks; -} diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart deleted file mode 100644 index d44fc54d0ee3..000000000000 --- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart +++ /dev/null @@ -1,1518 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/src/dart/ast/ast.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/fix_builder.dart'; -import 'package:nnbd_migration/src/utilities/hint_utils.dart'; - -/// Base class representing a change that might need to be made to an -/// expression. -abstract class ExpressionChange { - /// The type of the expression after the change is applied. - final DartType resultType; - - ExpressionChange(this.resultType); - - /// Description of the change. - NullabilityFixDescription get description; - - /// Creates a [NodeProducingEditPlan] that applies the change to [innerPlan]. - NodeProducingEditPlan applyExpression(FixAggregator aggregator, - NodeProducingEditPlan innerPlan, AtomicEditInfo? info); - - /// Creates a string that applies the change to the [inner] text string. - String applyText(FixAggregator aggregator, String inner); - - /// Describes the change, for use in debugging. - String describe(); -} - -/// Visitor that combines together the changes produced by [FixBuilder] into a -/// concrete set of source code edits using the infrastructure of [EditPlan]. -class FixAggregator extends UnifyingAstVisitor { - /// Map from the [AstNode]s that need to have changes made, to the changes - /// that need to be applied to them. - final Map _changes; - - /// The set of [EditPlan]s being accumulated. - List _plans = []; - - final EditPlanner planner; - - /// Map from library to the prefix we should use when inserting code that - /// refers to it. - final Map _importPrefixes = {}; - - final bool _warnOnWeakCode; - - FixAggregator._(this.planner, this._changes, this._warnOnWeakCode, - CompilationUnitElement compilationUnitElement) { - for (var importElement in compilationUnitElement.library.libraryImports) { - // TODO(paulberry): the `??=` should ensure that if there are two imports, - // one prefixed and one not, we prefer the prefix. Test this. - _importPrefixes[importElement.importedLibrary] ??= - importElement.prefix?.element.name; - } - } - - /// Creates the necessary Dart code to refer to the given element, using an - /// import prefix if necessary. - /// - /// TODO(paulberry): if the element is not currently imported, we should - /// update or add an import statement as necessary. - String? elementToCode(Element element) { - var name = element.name; - var library = element.library; - var prefix = _importPrefixes[library]; - if (prefix != null) { - return '$prefix.$name'; - } else { - return name; - } - } - - /// Gathers all the changes to nodes descended from [node] into a single - /// [EditPlan]. - NodeProducingEditPlan innerPlanForNode(AstNode node) { - var innerPlans = innerPlansForNode(node); - return planner.passThrough(node, innerPlans: innerPlans); - } - - /// Gathers all the changes to nodes descended from [node] into a list of - /// [EditPlan]s, one for each change. - List innerPlansForNode(AstNode node) { - var previousPlans = _plans; - try { - _plans = []; - node.visitChildren(this); - return _plans; - } finally { - _plans = previousPlans; - } - } - - /// Gathers all the changes to [node] and its descendants into a single - /// [EditPlan]. - EditPlan planForNode(AstNode? node) { - var change = _changes[node]; - if (change != null) { - return change._apply(node!, this); - } else { - return planner.passThrough(node, innerPlans: innerPlansForNode(node!)); - } - } - - /// Creates a string representation of the given type parameter element, - /// suitable for inserting into the user's source code. - String typeFormalToCode(TypeParameterElement formal) { - var bound = formal.bound; - if (bound == null || - bound is DynamicType || - (bound.isDartCoreObject && - bound.nullabilitySuffix != NullabilitySuffix.none)) { - return formal.name; - } - return '${formal.name} extends ${typeToCode(bound)}'; - } - - String typeToCode(DartType type) { - // TODO(paulberry): is it possible to share code with DartType.toString() - // somehow? - String suffix = - type.nullabilitySuffix == NullabilitySuffix.question ? '?' : ''; - if (type is InterfaceType) { - var name = elementToCode(type.element); - var typeArguments = type.typeArguments; - if (typeArguments.isEmpty) { - return '$name$suffix'; - } else { - var args = [for (var arg in typeArguments) typeToCode(arg)].join(', '); - return '$name<$args>$suffix'; - } - } else if (type is FunctionType) { - var buffer = StringBuffer(); - buffer.write(typeToCode(type.returnType)); - buffer.write(' Function'); - var typeFormals = type.typeFormals; - if (typeFormals.isNotEmpty) { - var formals = [for (var formal in typeFormals) typeFormalToCode(formal)] - .join(', '); - buffer.write('<$formals>'); - } - buffer.write('('); - String optionalOrNamedCloser = ''; - bool commaNeeded = false; - for (var parameter in type.parameters) { - if (commaNeeded) { - buffer.write(', '); - } else { - commaNeeded = true; - } - if (optionalOrNamedCloser.isEmpty && !parameter.isRequiredPositional) { - if (parameter.isPositional) { - buffer.write('['); - optionalOrNamedCloser = ']'; - } else { - buffer.write('{'); - optionalOrNamedCloser = '}'; - } - } - buffer.write(typeToCode(parameter.type)); - if (parameter.isNamed) { - buffer.write(' ${parameter.name}'); - } - } - buffer.write(optionalOrNamedCloser); - buffer.write(')'); - buffer.write(suffix); - return buffer.toString(); - } else { - return type.toString(); - } - } - - @override - void visitNode(AstNode node) { - var change = _changes[node]; - if (change != null) { - var innerPlan = change._apply(node, this); - _plans.add(innerPlan); - } else { - node.visitChildren(this); - } - } - - /// Runs the [FixAggregator] on a [unit] and returns the resulting edits. - static Map>? run(CompilationUnit unit, - String? sourceText, Map changes, - {bool? removeViaComments = false, bool warnOnWeakCode = false}) { - var planner = EditPlanner(unit.lineInfo, sourceText, - removeViaComments: removeViaComments); - var aggregator = FixAggregator._( - planner, changes, warnOnWeakCode, unit.declaredElement!); - unit.accept(aggregator); - if (aggregator._plans.isEmpty) return {}; - EditPlan plan; - if (aggregator._plans.length == 1) { - plan = aggregator._plans[0]; - } else { - plan = planner.passThrough(unit, innerPlans: aggregator._plans); - } - return planner.finalize(plan); - } -} - -/// [ExpressionChange] describing the addition of an `as` cast to an expression. -class IntroduceAsChange extends ExpressionChange { - /// The type being cast to. - final DartType type; - - /// Indicates whether this is a downcast. - final bool isDowncast; - - IntroduceAsChange(this.type, {required this.isDowncast}) : super(type); - - @override - NullabilityFixDescription get description => isDowncast - ? NullabilityFixDescription.downcastExpression - : NullabilityFixDescription.otherCastExpression; - - @override - NodeProducingEditPlan applyExpression(FixAggregator aggregator, - NodeProducingEditPlan innerPlan, AtomicEditInfo? info) => - aggregator.planner.addBinaryPostfix( - innerPlan, TokenType.AS, aggregator.typeToCode(type), - info: info); - - @override - String applyText(FixAggregator aggregator, String inner) => - '$inner as ${aggregator.typeToCode(type)}'; - - @override - String describe() => 'IntroduceAsChange($type)'; -} - -/// [ExpressionChange] describing the addition of an `as` cast to an expression -/// having a Future type. -class IntroduceThenChange extends ExpressionChange { - /// The change that should be made to the value the future completes with. - final ExpressionChange innerChange; - - IntroduceThenChange(super.resultType, this.innerChange); - - @override - NullabilityFixDescription get description => - NullabilityFixDescription.addThen; - - @override - NodeProducingEditPlan applyExpression(FixAggregator aggregator, - NodeProducingEditPlan innerPlan, AtomicEditInfo? info) => - aggregator.planner.addPostfix(innerPlan, - '.then((value) => ${innerChange.applyText(aggregator, 'value')})', - info: info); - - @override - String applyText(FixAggregator aggregator, String inner) => - '$inner.then((value) => ${innerChange.applyText(aggregator, 'value')})'; - - @override - String describe() => 'IntroduceThenChange($innerChange)'; -} - -/// Reasons that a variable declaration is to be made late. -enum LateAdditionReason { - /// It was inferred that the associated variable declaration is to be made - /// late through the late-inferring algorithm. - inference, - - /// It was inferred that the associated variable declaration is to be made - /// late, because it is a test variable which is assigned during setup. - testVariableInference, -} - -/// Base class representing a kind of change that [FixAggregator] might make to -/// a particular AST node. -abstract class NodeChange { - NodeChange._(); - - /// Indicates whether this node exists solely to provide informative - /// information. - bool get isInformative => false; - - Iterable get _toStringParts => const []; - - @override - String toString() => '$runtimeType(${_toStringParts.join(', ')})'; - - /// Applies this change to the given [node], producing an [EditPlan]. The - /// [aggregator] may be used to gather up any edits to the node's descendants - /// into their own [EditPlan]s. - /// - /// Note: the reason the caller can't just gather up the edits and pass them - /// in is that some changes don't preserve all of the structure of the nodes - /// below them (e.g. dropping an unnecessary cast), so those changes need to - /// be able to call the appropriate [aggregator] methods just on the nodes - /// they need. - /// - /// May return `null` if no changes need to be made. - EditPlan _apply(N node, FixAggregator aggregator); - - /// Creates the appropriate specialized kind of [NodeChange] appropriate for - /// the given [node]. - static NodeChange? create(AstNode node) => - node.accept(_NodeChangeVisitor._instance); -} - -/// Implementation of [NodeChange] specialized for operating on [Annotation] -/// nodes. -class NodeChangeForAnnotation extends NodeChange { - /// Indicates whether the node should be changed into a `required` keyword. - bool changeToRequiredKeyword = false; - - /// If [changeToRequiredKeyword] is `true`, the information that should be - /// contained in the edit. - AtomicEditInfo? changeToRequiredKeywordInfo; - - NodeChangeForAnnotation() : super._(); - - @override - Iterable get _toStringParts => - [if (changeToRequiredKeyword) 'changeToRequiredKeyword']; - - @override - EditPlan _apply(Annotation node, FixAggregator aggregator) { - if (!changeToRequiredKeyword) { - return aggregator.innerPlanForNode(node); - } - var name = node.name; - if (name is PrefixedIdentifier) { - name = name.identifier; - } - if (aggregator.planner.sourceText!.substring(name.offset, name.end) == - 'required') { - // The text `required` already exists in the annotation; we can just - // extract it. - return aggregator.planner.extract( - node, aggregator.planForNode(name) as NodeProducingEditPlan, - infoBefore: changeToRequiredKeywordInfo, alwaysDelete: true); - } else { - return aggregator.planner.replace(node, - [AtomicEdit.insert('required', info: changeToRequiredKeywordInfo)]); - } - } -} - -/// Implementation of [NodeChange] specialized for operating on [ArgumentList] -/// nodes. -class NodeChangeForArgumentList extends NodeChange { - /// Map whose keys are the arguments that should be dropped from this argument - /// list, if any. Values are info about why the arguments are being dropped. - final Map _argumentsToDrop = {}; - - NodeChangeForArgumentList() : super._(); - - /// Queries the map whose keys are the arguments that should be dropped from - /// this argument list, if any. Values are info about why the arguments are - /// being dropped. - @visibleForTesting - Map get argumentsToDrop => _argumentsToDrop; - - @override - Iterable get _toStringParts => - [if (_argumentsToDrop.isNotEmpty) 'argumentsToDrop: $_argumentsToDrop']; - - /// Updates `this` so that the given [argument] will be dropped, using [info] - /// to annotate the reason why it is being dropped. - void dropArgument(Expression argument, AtomicEditInfo? info) { - assert(!_argumentsToDrop.containsKey(argument)); - _argumentsToDrop[argument] = info; - } - - @override - EditPlan _apply(ArgumentList node, FixAggregator aggregator) { - assert(_argumentsToDrop.keys.every((e) => identical(e.parent, node))); - List innerPlans = []; - for (var argument in node.arguments) { - if (_argumentsToDrop.containsKey(argument)) { - innerPlans.add(aggregator.planner - .removeNode(argument, info: _argumentsToDrop[argument])); - } else { - innerPlans.add(aggregator.planForNode(argument)); - } - } - return aggregator.planner.passThrough(node, innerPlans: innerPlans); - } -} - -/// Implementation of [NodeChange] specialized for operating on [AsExpression] -/// nodes. -class NodeChangeForAsExpression extends NodeChangeForExpression { - /// Indicates whether the cast should be removed. - bool removeAs = false; - - @override - Iterable get _toStringParts => - [...super._toStringParts, if (removeAs) 'removeAs']; - - @override - EditPlan _apply(AsExpression node, FixAggregator aggregator) { - if (removeAs) { - return aggregator.planner.extract(node, - aggregator.planForNode(node.expression) as NodeProducingEditPlan, - infoAfter: - AtomicEditInfo(NullabilityFixDescription.removeAs, const {})); - } else { - return super._apply(node, aggregator); - } - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [AssignmentExpression] nodes. -class NodeChangeForAssignment - extends NodeChangeForExpression - with NodeChangeForAssignmentLike { - /// Indicates whether the user should be warned that the assignment is a - /// null-aware assignment that will have no effect when strong checking is - /// enabled. - bool isWeakNullAware = false; - - @override - Iterable get _toStringParts => - [...super._toStringParts, if (isWeakNullAware) 'isWeakNullAware']; - - @override - NodeProducingEditPlan _apply( - AssignmentExpression node, FixAggregator aggregator) { - var lhsPlan = aggregator.planForNode(node.leftHandSide); - if (isWeakNullAware && !aggregator._warnOnWeakCode) { - // Just keep the LHS - return aggregator.planner.extract(node, lhsPlan as NodeProducingEditPlan, - infoAfter: AtomicEditInfo( - NullabilityFixDescription.removeNullAwareAssignment, const {})); - } - var operatorPlan = _makeOperatorPlan(aggregator, node, node.operator); - var rhsPlan = aggregator.planForNode(node.rightHandSide); - var innerPlans = [ - lhsPlan, - if (operatorPlan != null) operatorPlan, - rhsPlan - ]; - return _applyExpression(aggregator, - aggregator.planner.passThrough(node, innerPlans: innerPlans)); - } - - EditPlan? _makeOperatorPlan( - FixAggregator aggregator, AssignmentExpression node, Token operator) { - var operatorPlan = super._makeOperatorPlan(aggregator, node, operator); - if (operatorPlan != null) return operatorPlan; - if (isWeakNullAware) { - assert(aggregator._warnOnWeakCode); - return aggregator.planner.informativeMessageForToken(node, operator, - info: AtomicEditInfo( - NullabilityFixDescription - .nullAwareAssignmentUnnecessaryInStrongMode, - const {})); - } else { - return null; - } - } -} - -/// Common behaviors for expressions that can represent an assignment (possibly -/// through desugaring). -mixin NodeChangeForAssignmentLike - on NodeChangeForExpression { - /// Indicates whether the user should be warned that the assignment has a - /// bad combined type (the return type of the combiner isn't assignable to the - /// write type of the target). - bool hasBadCombinedType = false; - - /// Indicates whether the user should be warned that the assignment has a - /// nullable source type. - bool hasNullableSource = false; - - @override - Iterable get _toStringParts => [ - ...super._toStringParts, - if (hasBadCombinedType) 'hasBadCombinedType', - if (hasNullableSource) 'hasNullableSource' - ]; - - EditPlan? _makeOperatorPlan( - FixAggregator aggregator, N node, Token operator) { - if (hasNullableSource) { - return aggregator.planner.informativeMessageForToken(node, operator, - info: AtomicEditInfo( - NullabilityFixDescription.compoundAssignmentHasNullableSource, - const {})); - } else if (hasBadCombinedType) { - return aggregator.planner.informativeMessageForToken(node, operator, - info: AtomicEditInfo( - NullabilityFixDescription.compoundAssignmentHasBadCombinedType, - const {})); - } else { - return null; - } - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [CompilationUnit] nodes. -class NodeChangeForCompilationUnit extends NodeChange { - /// A map of the imports that should be added, or the empty map if no imports - /// should be added. - /// - /// Each import is expressed as a map entry whose key is the URI to import and - /// whose value is the set of symbols to show. - final Map> _addImports = {}; - - bool removeLanguageVersionComment = false; - - NodeChangeForCompilationUnit() : super._(); - - /// Queries a map of the imports that should be added, or the empty map if no - /// imports should be added. - /// - /// Each import is expressed as a map entry whose key is the URI to import and - /// whose value is the set of symbols to show. - @visibleForTesting - Map> get addImports => _addImports; - - @override - Iterable get _toStringParts => [ - if (_addImports.isNotEmpty) 'addImports: $_addImports', - if (removeLanguageVersionComment) 'removeLanguageVersionComment' - ]; - - /// Updates `this` so that an import of [uri] will be added, showing [name]. - void addImport(String uri, String name) { - (_addImports[uri] ??= {}).add(name); - } - - @override - EditPlan _apply(CompilationUnit node, FixAggregator aggregator) { - List innerPlans = []; - if (removeLanguageVersionComment) { - final comment = (node as CompilationUnitImpl).languageVersionToken!; - innerPlans.add(aggregator.planner.replaceToken(node, comment, '', - info: AtomicEditInfo( - NullabilityFixDescription.removeLanguageVersionComment, - const {}))); - } - _processDirectives(node, aggregator, innerPlans); - for (var declaration in node.declarations) { - innerPlans.add(aggregator.planForNode(declaration)); - } - return aggregator.planner.passThrough(node, innerPlans: innerPlans); - } - - /// Adds the necessary inner plans to [innerPlans] for the directives part of - /// [node]. This solely involves adding imports. - void _processDirectives(CompilationUnit node, FixAggregator aggregator, - List innerPlans) { - List>> importsToAdd = - _addImports.entries.toList(); - importsToAdd.sort((x, y) => x.key.compareTo(y.key)); - - void insertImport(int offset, MapEntry> importToAdd, - {String prefix = '', String suffix = '\n'}) { - var shownNames = importToAdd.value.toList(); - shownNames.sort(); - innerPlans.add(aggregator.planner.insertText(node, offset, [ - if (prefix.isNotEmpty) AtomicEdit.insert(prefix), - AtomicEdit.insert( - "import '${importToAdd.key}' show ${shownNames.join(', ')};", - info: AtomicEditInfo(NullabilityFixDescription.addImport, {})), - if (suffix.isNotEmpty) AtomicEdit.insert(suffix) - ])); - } - - if (node.directives.every((d) => d is LibraryDirective)) { - while (importsToAdd.isNotEmpty) { - insertImport( - node.declarations.beginToken!.offset, importsToAdd.removeAt(0), - suffix: importsToAdd.isEmpty ? '\n\n' : '\n'); - } - } else { - for (var directive in node.directives) { - while (importsToAdd.isNotEmpty && - _shouldImportGoBefore(importsToAdd.first.key, directive)) { - insertImport(directive.offset, importsToAdd.removeAt(0)); - } - innerPlans.add(aggregator.planForNode(directive)); - } - while (importsToAdd.isNotEmpty) { - insertImport(node.directives.last.end, importsToAdd.removeAt(0), - prefix: '\n', suffix: ''); - } - } - } - - /// Determines whether a new import of [newImportUri] should be sorted before - /// an existing [directive]. - bool _shouldImportGoBefore(String newImportUri, Directive directive) { - if (directive is ImportDirective) { - return newImportUri.compareTo(directive.uri.stringValue!) < 0; - } else if (directive is LibraryDirective) { - // Library directives must come before imports. - return false; - } else { - // Everything else tends to come after imports. - return true; - } - } -} - -/// Common infrastructure used by [NodeChange] objects that operate on AST nodes -/// with conditional behavior (if statements, if elements, and conditional -/// expressions). -mixin NodeChangeForConditional on NodeChange { - /// If not `null`, indicates that the condition expression is known to - /// evaluate to either `true` or `false`, so the other branch of the - /// conditional is dead code and should be eliminated. - bool? conditionValue; - - /// If [conditionValue] is not `null`, the reason that should be included in - /// the [AtomicEditInfo] for the edit that removes the dead code. - FixReasonInfo? conditionReason; - - @override - Iterable get _toStringParts => - [...super._toStringParts, if (conditionValue != null) 'conditionValue']; - - /// If dead code removal is warranted for [node], returns an [EditPlan] that - /// removes the dead code (and performs appropriate updates within any - /// descendant AST nodes that remain). Otherwise returns `null`. - EditPlan? _applyConditional(N node, FixAggregator aggregator, - AstNode conditionNode, AstNode thenNode, AstNode? elseNode) { - if (conditionValue == null) return null; - if (aggregator._warnOnWeakCode) { - var conditionPlan = aggregator.innerPlanForNode(conditionNode); - var info = AtomicEditInfo( - conditionValue! - ? NullabilityFixDescription.conditionTrueInStrongMode - : NullabilityFixDescription.conditionFalseInStrongMode, - {FixReasonTarget.root: conditionReason}); - var commentedConditionPlan = aggregator.planner.addCommentPostfix( - conditionPlan, '/* == $conditionValue */', - info: info, isInformative: true); - return aggregator.planner.passThrough(node, innerPlans: [ - commentedConditionPlan, - aggregator.planForNode(thenNode), - if (elseNode != null) aggregator.planForNode(elseNode) - ]); - } - AstNode? nodeToKeep; - NullabilityFixDescription descriptionBefore, descriptionAfter; - if (conditionValue!) { - nodeToKeep = thenNode; - descriptionBefore = NullabilityFixDescription.discardCondition; - if (elseNode == null) { - descriptionAfter = descriptionBefore; - } else { - descriptionAfter = NullabilityFixDescription.discardElse; - } - } else { - nodeToKeep = elseNode; - descriptionBefore = - descriptionAfter = NullabilityFixDescription.discardThen; - } - if (nodeToKeep == null || - nodeToKeep is Block && nodeToKeep.statements.isEmpty) { - // The conditional node collapses to a no-op, so try to remove it - // entirely. - var info = AtomicEditInfo(NullabilityFixDescription.discardIf, - {FixReasonTarget.root: conditionReason}); - var removeNode = aggregator.planner.tryRemoveNode(node, info: info); - if (removeNode != null) { - return removeNode; - } else { - // We can't remove the node because it's not inside a sequence, so we - // have to create a suitable replacement. - if (node is IfStatement) { - return aggregator.planner - .replace(node, [AtomicEdit.insert('{}', info: info)], info: info); - } else if (node is IfElement) { - return aggregator.planner.replace( - node, [AtomicEdit.insert('...{}', info: info)], - info: info); - } else { - // We should never get here; the only types of conditional nodes that - // can wind up collapsing to a no-op are if statements and if - // elements. - throw StateError( - 'Unexpected node type collapses to no-op: ${node.runtimeType}'); - } - } - } - var infoBefore = AtomicEditInfo( - descriptionBefore, {FixReasonTarget.root: conditionReason}); - var infoAfter = AtomicEditInfo( - descriptionAfter, {FixReasonTarget.root: conditionReason}); - if (nodeToKeep is Block && nodeToKeep.statements.length == 1) { - var singleStatement = nodeToKeep.statements[0]; - if (singleStatement is VariableDeclarationStatement) { - // It's not safe to eliminate the {} because it increases the scope of - // the variable declarations - } else { - nodeToKeep = singleStatement; - } - } - return aggregator.planner.extract( - node, aggregator.planForNode(nodeToKeep) as NodeProducingEditPlan, - infoBefore: infoBefore, infoAfter: infoAfter); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [ConditionalExpression] nodes. -class NodeChangeForConditionalExpression - extends NodeChangeForExpression - with NodeChangeForConditional { - @override - EditPlan _apply(ConditionalExpression node, FixAggregator aggregator) { - return _applyConditional(node, aggregator, node.condition, - node.thenExpression, node.elseExpression) ?? - super._apply(node, aggregator); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [DefaultFormalParameter] nodes. -class NodeChangeForDefaultFormalParameter - extends NodeChange { - /// Indicates whether a `required` keyword should be added to this node. - bool addRequiredKeyword = false; - - /// If [addRequiredKeyword] is `true`, the information that should be - /// contained in the edit. - AtomicEditInfo? addRequiredKeywordInfo; - - /// If [addRequiredKeyword] is `true`, and there is a `/*required*/` hint, - /// the hint comment that should be converted into a simple `required` string. - HintComment? requiredHint; - - /// If non-null, indicates a `@required` annotation which should be removed - /// from this node. - Annotation? annotationToRemove; - - /// If [annotationToRemove] is non-null, the information that should be - /// contained in the edit. - AtomicEditInfo? removeAnnotationInfo; - - NodeChangeForDefaultFormalParameter() : super._(); - - @override - Iterable get _toStringParts => - [if (addRequiredKeyword) 'addRequiredKeyword']; - - @override - EditPlan _apply(DefaultFormalParameter node, FixAggregator aggregator) { - if (!addRequiredKeyword) return aggregator.innerPlanForNode(node); - - if (requiredHint != null) { - var innerPlan = aggregator.innerPlanForNode(node); - return aggregator.planner.acceptPrefixHint(innerPlan, requiredHint!, - info: addRequiredKeywordInfo); - } - - var offset = node.firstTokenAfterCommentAndMetadata!.offset; - return aggregator.planner.passThrough(node, innerPlans: [ - aggregator.planner.insertText(node, offset, [ - AtomicEdit.insert('required ', info: addRequiredKeywordInfo), - ]), - if (annotationToRemove != null) - aggregator.planner - .removeNode(annotationToRemove!, info: removeAnnotationInfo), - ...aggregator.innerPlansForNode(node), - ]); - } -} - -/// Implementation of [NodeChange] specialized for operating on [Expression] -/// nodes. -class NodeChangeForExpression extends NodeChange { - /// The list of [ExpressionChange] objects that should be applied to the - /// expression, in the order they should be applied. - final List expressionChanges = []; - - /// The list of [AtomicEditInfo] objects corresponding to each change in - /// [expressionChanges]. - final List expressionChangeInfos = []; - - NodeChangeForExpression() : super._(); - - @override - Iterable get _toStringParts => [ - for (var expressionChange in expressionChanges) - expressionChange.describe() - ]; - - void addExpressionChange(ExpressionChange change, AtomicEditInfo? info) { - expressionChanges.add(change); - expressionChangeInfos.add(info); - } - - @override - EditPlan _apply(N node, FixAggregator aggregator) { - var innerPlan = aggregator.innerPlanForNode(node); - return _applyExpression(aggregator, innerPlan); - } - - /// If the expression needs to be wrapped in another expression (e.g. a null - /// check), wraps the given [innerPlan] to produce appropriate result. - /// Otherwise returns [innerPlan] unchanged. - NodeProducingEditPlan _applyExpression( - FixAggregator aggregator, NodeProducingEditPlan innerPlan) { - var plan = innerPlan; - for (int i = 0; i < expressionChanges.length; i++) { - plan = expressionChanges[i] - .applyExpression(aggregator, plan, expressionChangeInfos[i]); - } - return plan; - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [FieldFormalParameter] nodes. -class NodeChangeForFieldFormalParameter - extends NodeChangeForType { - /// If not `null`, an explicit type annotation that should be added to the - /// parameter. - DartType? addExplicitType; - - NodeChangeForFieldFormalParameter() : super._(); - - @override - Iterable get _toStringParts => - [if (addExplicitType != null) 'addExplicitType']; - - @override - EditPlan _apply(FieldFormalParameter node, FixAggregator aggregator) { - if (addExplicitType != null) { - var typeText = aggregator.typeToCode(addExplicitType!); - // Even a field formal parameter can use `var`, `final`. - if (node.keyword?.keyword == Keyword.VAR) { - // TODO(srawlins): Test instrumentation info. - var info = - AtomicEditInfo(NullabilityFixDescription.replaceVar(typeText), {}); - return aggregator.planner.passThrough(node, innerPlans: [ - aggregator.planner - .replaceToken(node, node.keyword!, typeText, info: info), - ...aggregator.innerPlansForNode(node), - ]); - } else { - // TODO(srawlins): Test instrumentation info. - var info = - AtomicEditInfo(NullabilityFixDescription.addType(typeText), {}); - var offset = node.thisKeyword.offset; - return aggregator.planner.passThrough(node, innerPlans: [ - aggregator.planner.insertText(node, offset, [ - AtomicEdit.insert(typeText, info: info), - AtomicEdit.insert(' ') - ]), - ...aggregator.innerPlansForNode(node), - ]); - } - } else { - return super._apply(node, aggregator); - } - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [FunctionTypedFormalParameter] nodes. -class NodeChangeForFunctionTypedFormalParameter - extends NodeChangeForType { - NodeChangeForFunctionTypedFormalParameter() : super._(); -} - -/// Implementation of [NodeChange] specialized for operating on [IfElement] -/// nodes. -class NodeChangeForIfElement extends NodeChange - with NodeChangeForConditional { - NodeChangeForIfElement() : super._(); - - @override - EditPlan _apply(IfElement node, FixAggregator aggregator) { - return _applyConditional(node, aggregator, node.expression, - node.thenElement, node.elseElement) ?? - aggregator.innerPlanForNode(node); - } -} - -/// Implementation of [NodeChange] specialized for operating on [IfStatement] -/// nodes. -class NodeChangeForIfStatement extends NodeChange - with NodeChangeForConditional { - NodeChangeForIfStatement() : super._(); - - @override - EditPlan _apply(IfStatement node, FixAggregator aggregator) { - return _applyConditional(node, aggregator, node.expression, - node.thenStatement, node.elseStatement) ?? - aggregator.innerPlanForNode(node); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [MethodDeclaration] nodes. -class NodeChangeForMethodDeclaration extends NodeChange { - /// If non-null, indicates a `@nullable` annotation which should be removed - /// from this node. - Annotation? annotationToRemove; - - /// If [annotationToRemove] is non-null, the information that should be - /// contained in the edit. - AtomicEditInfo? removeAnnotationInfo; - - NodeChangeForMethodDeclaration() : super._(); - - @override - Iterable get _toStringParts => - [if (annotationToRemove != null) 'annotationToRemove']; - - @override - EditPlan _apply(MethodDeclaration node, FixAggregator aggregator) { - return aggregator.planner.passThrough(node, innerPlans: [ - if (annotationToRemove != null) - aggregator.planner - .removeNode(annotationToRemove!, info: removeAnnotationInfo), - ...aggregator.innerPlansForNode(node), - ]); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [MethodInvocation] nodes. -class NodeChangeForMethodInvocation - extends NodeChangeForExpression - with NodeChangeForNullAware { - @override - NodeProducingEditPlan _apply( - MethodInvocation node, FixAggregator aggregator) { - var target = node.target; - var targetPlan = target == null ? null : aggregator.planForNode(target); - var nullAwarePlan = _applyNullAware(node, aggregator); - var methodNamePlan = aggregator.planForNode(node.methodName); - var typeArguments = node.typeArguments; - var typeArgumentsPlan = - typeArguments == null ? null : aggregator.planForNode(typeArguments); - var argumentListPlan = aggregator.planForNode(node.argumentList); - var innerPlans = [ - if (targetPlan != null) targetPlan, - if (nullAwarePlan != null) nullAwarePlan, - methodNamePlan, - if (typeArgumentsPlan != null) typeArgumentsPlan, - argumentListPlan - ]; - return _applyExpression(aggregator, - aggregator.planner.passThrough(node, innerPlans: innerPlans)); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [SimpleIdentifier] nodes that represent a method name. -class NodeChangeForMethodName extends NodeChange { - /// The name the method name should be changed to, or `null` if no change - /// should be made. - String? _replacement; - - /// Info object associated with the replacement. - AtomicEditInfo? _replacementInfo; - - NodeChangeForMethodName() : super._(); - - /// Queries the name the method name should be changed to, or `null` if no - /// change should be made. - @visibleForTesting - String? get replacement => _replacement; - - /// Queries the info object associated with the replacement. - @visibleForTesting - AtomicEditInfo? get replacementInfo => _replacementInfo; - - @override - Iterable get _toStringParts => - [if (replacement != null) 'replacement: $replacement']; - - /// Updates `this` so that the method name will be changed to [replacement], - /// using [info] to annotate the reason for the change. - void replaceWith(String replacement, AtomicEditInfo? info) { - assert(_replacement == null); - _replacement = replacement; - _replacementInfo = info; - } - - @override - EditPlan _apply(SimpleIdentifier node, FixAggregator aggregator) { - if (replacement != null) { - return aggregator.planner.replace( - node, [AtomicEdit.insert(replacement!, info: replacementInfo)], - info: replacementInfo); - } else { - return aggregator.innerPlanForNode(node); - } - } -} - -/// Common infrastructure used by [NodeChange] objects that operate on AST nodes -/// with that can be null-aware (method invocations and property accesses). -mixin NodeChangeForNullAware on NodeChange { - /// Indicates how null-awareness should be handled. - NullAwarenessRemovalType nullAwarenessRemoval = NullAwarenessRemovalType.none; - - @override - Iterable get _toStringParts => [ - ...super._toStringParts, - if (nullAwarenessRemoval == NullAwarenessRemovalType.strong) - 'removeNullAwareness (strong)' - else if (nullAwarenessRemoval == NullAwarenessRemovalType.weak) - 'removeNullAwareness (weak)' - ]; - - /// Returns an [EditPlan] that removes null awareness, if appropriate. - /// Otherwise returns `null`. - EditPlan? _applyNullAware(N node, FixAggregator aggregator) { - if (nullAwarenessRemoval == NullAwarenessRemovalType.none) return null; - var weak = aggregator._warnOnWeakCode && - nullAwarenessRemoval == NullAwarenessRemovalType.weak; - var description = weak - ? NullabilityFixDescription.nullAwarenessUnnecessaryInStrongMode - : NullabilityFixDescription.removeNullAwareness; - return aggregator.planner.removeNullAwareness(node, - info: AtomicEditInfo(description, const {}), isInformative: weak); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [PostfixExpression] nodes. -class NodeChangeForPostfixExpression - extends NodeChangeForExpression - with NodeChangeForAssignmentLike { - @override - NodeProducingEditPlan _apply( - PostfixExpression node, FixAggregator aggregator) { - var operandPlan = aggregator.planForNode(node.operand); - var operatorPlan = _makeOperatorPlan(aggregator, node, node.operator); - var innerPlans = [ - operandPlan, - if (operatorPlan != null) operatorPlan - ]; - return _applyExpression(aggregator, - aggregator.planner.passThrough(node, innerPlans: innerPlans)); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [PrefixExpression] nodes. -class NodeChangeForPrefixExpression - extends NodeChangeForExpression - with NodeChangeForAssignmentLike { - @override - NodeProducingEditPlan _apply( - PrefixExpression node, FixAggregator aggregator) { - var operatorPlan = _makeOperatorPlan(aggregator, node, node.operator); - var operandPlan = aggregator.planForNode(node.operand); - var innerPlans = [ - if (operatorPlan != null) operatorPlan, - operandPlan - ]; - return _applyExpression(aggregator, - aggregator.planner.passThrough(node, innerPlans: innerPlans)); - } -} - -/// Implementation of [NodeChange] specialized for operating on [PropertyAccess] -/// nodes. -class NodeChangeForPropertyAccess - extends NodeChangeForExpression - with NodeChangeForNullAware { - @override - NodeProducingEditPlan _apply(PropertyAccess node, FixAggregator aggregator) { - var targetPlan = - node.target == null ? null : aggregator.planForNode(node.target); - var nullAwarePlan = _applyNullAware(node, aggregator); - var propertyNamePlan = aggregator.planForNode(node.propertyName); - var innerPlans = [ - if (targetPlan != null) targetPlan, - if (nullAwarePlan != null) nullAwarePlan, - propertyNamePlan - ]; - return _applyExpression(aggregator, - aggregator.planner.passThrough(node, innerPlans: innerPlans)); - } -} - -/// Implementation of [NodeChange] specialized for operating on [ShowCombinator] -/// nodes. -class NodeChangeForShowCombinator extends NodeChange { - /// A set of the names that should be added, or the empty set if no names - /// should be added. - final Set _addNames = {}; - - NodeChangeForShowCombinator() : super._(); - - /// Queries the set of names that should be added, or the empty set if no - /// names should be added. - @visibleForTesting - Iterable get addNames => _addNames; - - @override - Iterable get _toStringParts => [ - if (_addNames.isNotEmpty) 'addNames: $_addNames', - ]; - - /// Updates `this` so that [name] will be added. - void addName(String name) { - _addNames.add(name); - } - - @override - EditPlan _apply(ShowCombinator node, FixAggregator aggregator) { - List innerPlans = []; - List namesToAdd = _addNames.toList(); - namesToAdd.sort(); - - void insertName(int offset, String nameToAdd, - {String prefix = '', String suffix = ', '}) { - innerPlans.add(aggregator.planner.insertText(node, offset, [ - if (prefix.isNotEmpty) AtomicEdit.insert(prefix), - AtomicEdit.insert(nameToAdd), - if (suffix.isNotEmpty) AtomicEdit.insert(suffix) - ])); - } - - for (var shownName in node.shownNames) { - while (namesToAdd.isNotEmpty && - namesToAdd.first.compareTo(shownName.name) < 0) { - insertName(shownName.offset, namesToAdd.removeAt(0)); - } - innerPlans.add(aggregator.planForNode(shownName)); - } - while (namesToAdd.isNotEmpty) { - insertName(node.shownNames.last.end, namesToAdd.removeAt(0), - prefix: ', ', suffix: ''); - } - return aggregator.planner.passThrough(node, innerPlans: innerPlans); - } -} - -/// Implementation of [NodeChange] specialized for operating on -/// [SimpleFormalParameter] nodes. -class NodeChangeForSimpleFormalParameter - extends NodeChange { - /// If not `null`, an explicit type annotation that should be added to the - /// parameter. - DartType? addExplicitType; - - NodeChangeForSimpleFormalParameter() : super._(); - - @override - Iterable get _toStringParts => - [if (addExplicitType != null) 'addExplicitType']; - - @override - EditPlan _apply(SimpleFormalParameter node, FixAggregator aggregator) { - var innerPlan = aggregator.innerPlanForNode(node); - if (addExplicitType == null) return innerPlan; - var typeText = aggregator.typeToCode(addExplicitType!); - if (node.keyword?.keyword == Keyword.VAR) { - // TODO(srawlins): Test instrumentation info. - var info = - AtomicEditInfo(NullabilityFixDescription.replaceVar(typeText), {}); - return aggregator.planner.passThrough(node, innerPlans: [ - aggregator.planner - .replaceToken(node, node.keyword!, typeText, info: info), - ...aggregator.innerPlansForNode(node), - ]); - } else { - // TODO(srawlins): Test instrumentation info. - var info = - AtomicEditInfo(NullabilityFixDescription.addType(typeText), {}); - // Skip past the offset of any metadata, a potential `final` keyword, and - // a potential `covariant` keyword. - var offset = node.type?.offset ?? node.name!.offset; - return aggregator.planner.passThrough(node, innerPlans: [ - aggregator.planner.insertText(node, offset, - [AtomicEdit.insert(typeText, info: info), AtomicEdit.insert(' ')]), - ...aggregator.innerPlansForNode(node), - ]); - } - } -} - -/// Implementation of [NodeChange] specialized for operating on nodes which -/// represent a type, and can be made and hinted nullable and non-nullable. -abstract class NodeChangeForType extends NodeChange { - bool _makeNullable = false; - - HintComment? _nullabilityHint; - - /// The decorated type of the type annotation, or `null` if there is no - /// decorated type info of interest. If [makeNullable] is `true`, the node - /// from this type will be attached to the edit that adds the `?`. If - /// [_makeNullable] is `false`, the node from this type will be attached to - /// the information about why the node wasn't made nullable. - DecoratedType? _decoratedType; - - NodeChangeForType._() : super._(); - - @override - bool get isInformative => !_makeNullable; - - /// Indicates whether the type should be made nullable by adding a `?`. - bool get makeNullable => _makeNullable; - - /// If we are making the type nullable due to a hint, the comment that caused - /// it. - HintComment? get nullabilityHint => _nullabilityHint; - - @override - Iterable get _toStringParts => [ - if (_makeNullable) 'makeNullable', - if (_nullabilityHint != null) 'nullabilityHint' - ]; - - void recordNullability(DecoratedType decoratedType, bool makeNullable, - {HintComment? nullabilityHint}) { - _decoratedType = decoratedType; - _makeNullable = makeNullable; - _nullabilityHint = nullabilityHint; - } - - @override - EditPlan _apply(N node, FixAggregator aggregator) { - var innerPlan = aggregator.innerPlanForNode(node); - if (_decoratedType == null) return innerPlan; - var typeName = - _decoratedType!.type!.getDisplayString(withNullability: false); - var fixReasons = {FixReasonTarget.root: _decoratedType!.node}; - if (_makeNullable) { - var hint = _nullabilityHint; - if (hint != null) { - return aggregator.planner.acceptSuffixHint(innerPlan, hint, - info: AtomicEditInfo( - NullabilityFixDescription.makeTypeNullableDueToHint(typeName), - fixReasons, - hintComment: hint)); - } else { - return aggregator.planner.makeNullable(innerPlan, - info: AtomicEditInfo( - NullabilityFixDescription.makeTypeNullable(typeName), - fixReasons)); - } - } else { - var hint = _nullabilityHint; - if (hint != null) { - return aggregator.planner.dropNullabilityHint(innerPlan, hint, - info: AtomicEditInfo( - NullabilityFixDescription.typeNotMadeNullableDueToHint( - typeName), - fixReasons, - hintComment: hint)); - } else { - return aggregator.planner.explainNonNullable(innerPlan, - info: AtomicEditInfo( - NullabilityFixDescription.typeNotMadeNullable(typeName), - fixReasons)); - } - } - } -} - -/// Implementation of [NodeChange] specialized for operating on [TypeAnnotation] -/// nodes. -class NodeChangeForTypeAnnotation extends NodeChangeForType { - NodeChangeForTypeAnnotation() : super._(); -} - -/// Implementation of [NodeChange] specialized for operating on -/// [VariableDeclarationList] nodes. -class NodeChangeForVariableDeclarationList - extends NodeChange { - /// If an explicit type should be added to this variable declaration, the type - /// that should be added. Otherwise `null`. - DartType? addExplicitType; - - /// Indicates whether a "late" annotation should be added to this variable - /// declaration, caused by inference. - LateAdditionReason? lateAdditionReason; - - /// If a "late" annotation should be added to this variable declaration, and - /// the cause is a "late" hint, the hint that caused it. Otherwise `null`. - HintComment? lateHint; - - NodeChangeForVariableDeclarationList() : super._(); - - @override - Iterable get _toStringParts => [ - if (addExplicitType != null) 'addExplicitType', - if (lateAdditionReason != null) 'lateAdditionReason', - if (lateHint != null) 'lateHint' - ]; - - @override - EditPlan _apply(VariableDeclarationList node, FixAggregator aggregator) { - List innerPlans = []; - if (lateAdditionReason != null) { - var description = lateAdditionReason == LateAdditionReason.inference - ? NullabilityFixDescription.addLate - : NullabilityFixDescription.addLateDueToTestSetup; - var info = AtomicEditInfo(description, {}); - innerPlans.add(aggregator.planner.insertText( - node, - node.firstTokenAfterCommentAndMetadata.offset, - [AtomicEdit.insert('late', info: info), AtomicEdit.insert(' ')])); - } - if (addExplicitType != null) { - var typeText = aggregator.typeToCode(addExplicitType!); - if (node.keyword?.keyword == Keyword.VAR) { - var info = - AtomicEditInfo(NullabilityFixDescription.replaceVar(typeText), {}); - innerPlans.add(aggregator.planner - .replaceToken(node, node.keyword!, typeText, info: info)); - } else { - var info = - AtomicEditInfo(NullabilityFixDescription.addType(typeText), {}); - innerPlans.add(aggregator.planner.insertText( - node, - node.variables.first.offset, - [AtomicEdit.insert(typeText, info: info), AtomicEdit.insert(' ')])); - } - } - innerPlans.addAll(aggregator.innerPlansForNode(node)); - var plan = aggregator.planner.passThrough(node, innerPlans: innerPlans); - if (lateHint != null) { - var description = lateHint!.kind == HintCommentKind.late_ - ? NullabilityFixDescription.addLateDueToHint - : NullabilityFixDescription.addLateFinalDueToHint; - plan = aggregator.planner.acceptPrefixHint(plan, lateHint!, - info: AtomicEditInfo(description, {}, hintComment: lateHint)); - } - return plan; - } -} - -/// [ExpressionChange] describing the addition of a comment explaining that a -/// literal `null` could not be migrated. -class NoValidMigrationChange extends ExpressionChange { - NoValidMigrationChange(super.resultType); - - @override - NullabilityFixDescription get description => - NullabilityFixDescription.noValidMigrationForNull; - - @override - NodeProducingEditPlan applyExpression(FixAggregator aggregator, - NodeProducingEditPlan innerPlan, AtomicEditInfo? info) => - aggregator.planner.addCommentPostfix( - innerPlan, '/* no valid migration */', - info: info, isInformative: true); - - @override - String applyText(FixAggregator aggregator, String inner) => - '$inner /* no valid migration */'; - - @override - String describe() => 'NoValidMigrationChange'; -} - -/// Enum used by [NodeChangeForNullAware] to indicate how null awareness should -/// be handled. -enum NullAwarenessRemovalType { - /// Do not remove null awareness. - none, - - /// If warning on weak code, issue a warning; otherwise remove null awareness. - weak, - - /// Remove null awareness unconditionally. - strong, -} - -/// [ExpressionChange] describing the addition of an `!` after an expression. -class NullCheckChange extends ExpressionChange { - /// The hint that is causing this `!` to be added, if any. - final HintComment? hint; - - NullCheckChange(super.resultType, {this.hint}); - - @override - NullabilityFixDescription get description => - NullabilityFixDescription.checkExpression; - - @override - NodeProducingEditPlan applyExpression(FixAggregator aggregator, - NodeProducingEditPlan innerPlan, AtomicEditInfo? info) { - if (hint != null) { - return aggregator.planner.acceptSuffixHint(innerPlan, hint!, info: info); - } else { - return aggregator.planner - .addUnaryPostfix(innerPlan, TokenType.BANG, info: info); - } - } - - @override - String applyText(FixAggregator aggregator, String inner) => '$inner!'; - - @override - String describe() => 'NullCheckChange'; -} - -/// Visitor that creates an appropriate [NodeChange] object for the node being -/// visited. -class _NodeChangeVisitor extends GeneralizingAstVisitor> { - static final _instance = _NodeChangeVisitor(); - - @override - NodeChange visitAnnotation(Annotation node) => NodeChangeForAnnotation(); - - @override - NodeChange visitArgumentList(ArgumentList node) => - NodeChangeForArgumentList(); - - @override - NodeChange visitAsExpression(AsExpression node) => - NodeChangeForAsExpression(); - - @override - NodeChange visitAssignmentExpression(AssignmentExpression node) => - NodeChangeForAssignment(); - - @override - NodeChange visitCompilationUnit(CompilationUnit node) => - NodeChangeForCompilationUnit(); - - @override - NodeChange visitConditionalExpression(ConditionalExpression node) => - NodeChangeForConditionalExpression(); - - @override - NodeChange visitDefaultFormalParameter(DefaultFormalParameter node) => - NodeChangeForDefaultFormalParameter(); - - @override - NodeChange visitExpression(Expression node) => NodeChangeForExpression(); - - @override - NodeChange visitFieldFormalParameter(FieldFormalParameter node) => - NodeChangeForFieldFormalParameter(); - - @override - NodeChange visitFunctionTypedFormalParameter( - FunctionTypedFormalParameter node) => - NodeChangeForFunctionTypedFormalParameter(); - - @override - NodeChange visitGenericFunctionType(GenericFunctionType node) => - NodeChangeForTypeAnnotation(); - - @override - NodeChange visitIfElement(IfElement node) => NodeChangeForIfElement(); - - @override - NodeChange visitIfStatement(IfStatement node) => NodeChangeForIfStatement(); - - @override - NodeChange visitMethodDeclaration(MethodDeclaration node) => - NodeChangeForMethodDeclaration(); - - @override - NodeChange visitMethodInvocation(MethodInvocation node) => - NodeChangeForMethodInvocation(); - - @override - NodeChange visitNamedType(NamedType node) => NodeChangeForTypeAnnotation(); - - @override - NodeChange visitNode(AstNode node) => - throw StateError('Unexpected node type: ${node.runtimeType}'); - - @override - NodeChange visitPostfixExpression(PostfixExpression node) => - NodeChangeForPostfixExpression(); - - @override - NodeChange visitPrefixExpression(PrefixExpression node) => - NodeChangeForPrefixExpression(); - - @override - NodeChange visitPropertyAccess(PropertyAccess node) => - NodeChangeForPropertyAccess(); - - @override - NodeChange visitShowCombinator(ShowCombinator node) => - NodeChangeForShowCombinator(); - - @override - NodeChange visitSimpleFormalParameter(SimpleFormalParameter node) => - NodeChangeForSimpleFormalParameter(); - - @override - NodeChange? visitSimpleIdentifier(SimpleIdentifier node) { - var parent = node.parent; - if (parent is MethodInvocation && identical(node, parent.methodName)) { - return NodeChangeForMethodName(); - } else { - return super.visitSimpleIdentifier(node); - } - } - - @override - NodeChange visitVariableDeclarationList(VariableDeclarationList node) => - NodeChangeForVariableDeclarationList(); -} diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart deleted file mode 100644 index 472ad73055d1..000000000000 --- a/pkg/nnbd_migration/lib/src/fix_builder.dart +++ /dev/null @@ -1,1500 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart'; -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/error/listener.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/ast/ast.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; -import 'package:analyzer/src/dart/element/member.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/type_provider.dart'; -import 'package:analyzer/src/dart/element/type_system.dart'; -import 'package:analyzer/src/error/best_practices_verifier.dart'; -import 'package:analyzer/src/generated/element_type_provider.dart'; -import 'package:analyzer/src/generated/migration.dart'; -import 'package:analyzer/src/generated/resolver.dart'; -import 'package:analyzer/src/generated/utilities_dart.dart'; -import 'package:collection/collection.dart' show IterableExtension; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/fix_aggregator.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/utilities/built_value_transformer.dart'; -import 'package:nnbd_migration/src/utilities/permissive_mode.dart'; -import 'package:nnbd_migration/src/utilities/resolution_utils.dart'; -import 'package:nnbd_migration/src/utilities/where_not_null_transformer.dart'; -import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart'; -import 'package:nnbd_migration/src/variables.dart'; -import 'package:pub_semver/pub_semver.dart'; - -bool _isIncrementOrDecrementOperator(TokenType tokenType) { - switch (tokenType) { - case TokenType.PLUS_PLUS: - case TokenType.MINUS_MINUS: - return true; - default: - return false; - } -} - -/// Problem reported by [FixBuilder] when encountering a compound assignment -/// for which the combination result is nullable. This occurs if the compound -/// assignment resolves to a user-defined operator that returns a nullable type, -/// but the target of the assignment expects a non-nullable type. We need to -/// add a null check but it's nontrivial to do so because we would have to -/// rewrite the assignment as an ordinary assignment (e.g. change `x += y` to -/// `x = (x + y)!`), but that might change semantics by causing subexpressions -/// of the target to be evaluated twice. -/// -/// TODO(paulberry): consider alternatives. -/// See https://github.com/dart-lang/sdk/issues/38675. -class CompoundAssignmentCombinedNullable implements Problem { - const CompoundAssignmentCombinedNullable(); -} - -/// Problem reported by [FixBuilder] when encountering a compound assignment -/// for which the value read from the target of the assignment has a nullable -/// type. We need to add a null check but it's nontrivial to do so because we -/// would have to rewrite the assignment as an ordinary assignment (e.g. change -/// `x += y` to `x = x! + y`), but that might change semantics by causing -/// subexpressions of the target to be evaluated twice. -/// -/// TODO(paulberry): consider alternatives. -/// See https://github.com/dart-lang/sdk/issues/38676. -class CompoundAssignmentReadNullable implements Problem { - const CompoundAssignmentReadNullable(); -} - -/// This class runs the analyzer's resolver over the code being migrated, after -/// graph propagation, to figure out what changes need to be made. It doesn't -/// actually make the changes; it simply reports what changes are necessary -/// through abstract methods. -class FixBuilder { - final DecoratedClassHierarchy? _decoratedClassHierarchy; - - /// The type provider providing non-nullable types. - final TypeProvider typeProvider; - - final Map changes = {}; - - final Map> problems = {}; - - /// The NNBD type system. - final TypeSystemImpl _typeSystem; - - /// Variables for this migration run. - final Variables? _variables; - - /// The file being analyzed. - final Source? source; - - late ResolverVisitor _resolver; - - /// The listener to which exceptions should be reported. - final NullabilityMigrationListener? listener; - - /// The compilation unit for which fixes are being built. - final CompilationUnit? unit; - - final MigrationResolutionHooksImpl migrationResolutionHooks; - - /// Parameter elements for which an explicit type should be added, and what - /// that type should be. - final Map _addedParameterTypes = {}; - - final bool? warnOnWeakCode; - - final NullabilityGraph _graph; - - /// Helper that assists us in transforming Iterable methods to their "OrNull" - /// equivalents. - final WhereOrNullTransformer _whereOrNullTransformer; - - /// Helper that assists us in transforming calls to `Iterable.where` to - /// `Iterable.whereNotNull`. - final WhereNotNullTransformer _whereNotNullTransformer; - - /// The set of extensions that need to be imported from package:collection. - @visibleForTesting - final Set neededCollectionPackageExtensions = {}; - - /// Map of additional package dependencies that will be required by the - /// migrated code. Keys are package names; values indicate the minimum - /// required version of each package. - final Map _neededPackages; - - factory FixBuilder( - Source? source, - DecoratedClassHierarchy? decoratedClassHierarchy, - TypeProvider typeProvider, - TypeSystemImpl typeSystem, - Variables? variables, - LibraryElementImpl definingLibrary, - NullabilityMigrationListener? listener, - CompilationUnit? unit, - bool? warnOnWeakCode, - NullabilityGraph graph, - Map neededPackages) { - var migrationResolutionHooks = MigrationResolutionHooksImpl(); - return FixBuilder._( - decoratedClassHierarchy, - _makeNnbdTypeSystem( - (typeProvider as TypeProviderImpl).asNonNullableByDefault, - typeSystem, - migrationResolutionHooks), - variables, - source, - definingLibrary, - listener, - unit, - migrationResolutionHooks, - warnOnWeakCode, - graph, - neededPackages); - } - - FixBuilder._( - this._decoratedClassHierarchy, - this._typeSystem, - this._variables, - this.source, - LibraryElementImpl definingLibrary, - this.listener, - this.unit, - this.migrationResolutionHooks, - this.warnOnWeakCode, - this._graph, - this._neededPackages) - : typeProvider = _typeSystem.typeProvider, - _whereOrNullTransformer = - WhereOrNullTransformer(_typeSystem.typeProvider, _typeSystem), - _whereNotNullTransformer = - WhereNotNullTransformer(_typeSystem.typeProvider, _typeSystem) { - migrationResolutionHooks._fixBuilder = this; - assert(_typeSystem.isNonNullableByDefault); - assert((typeProvider as TypeProviderImpl).isNonNullableByDefault); - var inheritanceManager = InheritanceManager3(); - // TODO(paulberry): is it a bad idea to throw away errors? - var errorListener = AnalysisErrorListener.NULL_LISTENER; - _resolver = ResolverVisitorForMigration( - inheritanceManager, - definingLibrary, - source!, - typeProvider, - errorListener, - _typeSystem, - FeatureSet.fromEnableFlags2( - sdkLanguageVersion: Feature.non_nullable.releaseVersion!, - flags: [], - ), - migrationResolutionHooks); - } - - /// Visits the entire compilation [unit] using the analyzer's resolver and - /// makes note of changes that need to be made. - void visitAll() { - try { - ElementTypeProvider.current = migrationResolutionHooks; - unit!.accept(_FixBuilderPreVisitor(this)); - unit!.accept(_resolver); - unit!.accept(_FixBuilderPostVisitor(this)); - } catch (exception, stackTrace) { - if (listener != null) { - listener!.reportException(source, unit, exception, stackTrace); - } else { - rethrow; - } - } finally { - ElementTypeProvider.current = const ElementTypeProvider(); - } - } - - /// Called whenever an AST node is found that can't be automatically fixed. - void _addProblem(AstNode node, Problem problem) { - var newlyAdded = (problems[node] ??= {}).add(problem); - assert(newlyAdded); - } - - /// Computes the type that [element] will have after migration. - /// - /// If [targetType] is present, and [element] is a class member, it is the - /// type of the class within which [element] is being accessed; this is used - /// to perform the correct substitutions. - DartType _computeMigratedType(Element element) { - element = element.declaration!; - if (element is ClassElement || element is TypeParameterElement) { - return typeProvider.typeType; - } else if (element is PropertyAccessorElement && - element.isSynthetic && - !element.variable.isSynthetic) { - var variableType = _variables! - .toFinalType(_variables!.decoratedElementType(element.variable)); - if (element.isSetter) { - return FunctionTypeImpl( - returnType: typeProvider.voidType, - typeFormals: [], - parameters: [ - ParameterElementImpl.synthetic( - 'value', variableType, ParameterKind.REQUIRED) - ], - nullabilitySuffix: NullabilitySuffix.none); - } else { - return FunctionTypeImpl( - returnType: variableType, - typeFormals: [], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none); - } - } else { - return _variables!.toFinalType(_variables!.decoratedElementType(element)); - } - } - - /// If [node] is a property access or method invocation, returns the element - /// it invokes. Otherwise returns `null`. - Element? _findPropertyOrMethodElement(Expression node) { - if (node is PrefixedIdentifier) { - return node.identifier.staticElement; - } else if (node is PropertyAccess) { - return node.propertyName.staticElement; - } else if (node is MethodInvocation) { - return node.methodName.staticElement; - } else { - return null; - } - } - - /// Returns the [NodeChange] object accumulating changes for the given [node], - /// creating it if necessary. - NodeChange _getChange(AstNode node) => - changes[node] ??= NodeChange.create(node)!; - - /// Determines whether the given [node], which is a null-aware method - /// invocation, property access, or index expression, should remain null-aware - /// after migration. - bool _shouldStayNullAware(Expression node) { - Expression? target; - if (node is PropertyAccess) { - target = node.target; - } else if (node is MethodInvocation) { - target = node.target; - } else { - throw StateError('Unexpected expression type: ${node.runtimeType}'); - } - if (!_typeSystem.isPotentiallyNullable(target!.staticType!)) { - var element = _findPropertyOrMethodElement(target); - if (element != null) { - var library = element.library; - if (library!.isNonNullableByDefault && - !_graph.isBeingMigrated(library.source)) { - (_getChange(node) as NodeChangeForNullAware).nullAwarenessRemoval = - NullAwarenessRemovalType.strong; - return false; - } - } - (_getChange(node) as NodeChangeForNullAware).nullAwarenessRemoval = - NullAwarenessRemovalType.weak; - return false; - } - return true; - } - - static TypeSystemImpl _makeNnbdTypeSystem( - TypeProvider nnbdTypeProvider, - TypeSystemImpl typeSystem, - MigrationResolutionHooksImpl migrationResolutionHooks) { - // TODO(paulberry): do we need to test both possible values of - // strictInference? - return TypeSystemImpl( - isNonNullableByDefault: true, - strictCasts: typeSystem.strictCasts, - strictInference: typeSystem.strictInference, - typeProvider: nnbdTypeProvider); - } -} - -/// Fix reason object when adding a null check because of an explicit hint. -class FixReason_NullCheckHint implements SimpleFixReasonInfo { - @override - final CodeReference codeReference; - - FixReason_NullCheckHint(this.codeReference); - - @override - String get description => 'Null check hint'; -} - -/// Implementation of [MigrationResolutionHooks] that interfaces with -/// [FixBuilder]. -class MigrationResolutionHooksImpl - with ResolutionUtils - implements MigrationResolutionHooks { - late final FixBuilder _fixBuilder; - - final Expando> _collectionElements = Expando(); - - final Expando _shouldStayNullAware = Expando(); - - final Map - _assignmentLikeExpressionHandlers = {}; - - FlowAnalysis? - _flowAnalysis; - - /// Deferred processing that should be performed once we have finished - /// evaluating the type of an expression. - final Map - _deferredExpressionProcessing = {}; - - final Map _contextTypes = {}; - - TypeProvider get typeProvider => _fixBuilder.typeProvider; - - @override - void freshTypeParameterCreated(TypeParameterElement newTypeParameter, - TypeParameterElement oldTypeParameter) { - DecoratedTypeParameterBounds.current!.put(newTypeParameter, - DecoratedTypeParameterBounds.current!.get(oldTypeParameter)); - } - - @override - List getClassInterfaces(InterfaceElementImpl element) { - return _wrapExceptions( - _fixBuilder.unit, - () => element.interfacesInternal, - () => [ - for (var interface in element.interfacesInternal) - _getClassInterface(element, interface.element) - ]); - } - - @override - bool? getConditionalKnownValue(AstNode node) => - _wrapExceptions(node, () => null, () { - // TODO(paulberry): handle conditional expressions. - var conditionalDiscard = _fixBuilder._variables! - .getConditionalDiscard(_fixBuilder.source, node); - if (conditionalDiscard == null) { - return null; - } else { - if (conditionalDiscard.keepTrue && conditionalDiscard.keepFalse) { - return null; - } - var conditionValue = conditionalDiscard.keepTrue; - (_fixBuilder._getChange(node) as NodeChangeForConditional) - ..conditionValue = conditionValue - ..conditionReason = conditionalDiscard.reason; - // If we're just issuing warnings, instruct the resolver to go ahead - // and visit both branches of the conditional. - return _fixBuilder.warnOnWeakCode! ? null : conditionValue; - } - }); - - @override - List getExecutableParameters( - ExecutableElementImpl element) { - // Note: even if the element is part of a library that's being migrated, - // there's no guarantee that the parameter elements have been appropriately - // updated by the migration process, because they might be synthetic - // parameters. (This happens when the code being migrated contains a mixin - // application and there's a synthetic constructor). So we can't safely get - // the parameters out of the element. But it is always safe to defer to - // `getExecutableType` and get the parameter list from the function type. - return getExecutableType(element).parameters; - } - - @override - DartType getExecutableReturnType(Element element) => - getExecutableType(element as ElementImplWithFunctionType).returnType; - - @override - FunctionType getExecutableType(ElementImplWithFunctionType element) => - _wrapExceptions(_fixBuilder.unit, () => element.typeInternal, () { - var type = _fixBuilder._computeMigratedType(element); - Element baseElement = element; - if (baseElement is Member) { - type = baseElement.substitution.substituteType(type); - } - return type as FunctionType; - }); - - @override - DartType getExtendedType(ExtensionElementImpl element) { - return _wrapExceptions( - _fixBuilder.unit, - () => element.extendedTypeInternal, - () => _fixBuilder._variables!.toFinalType( - _fixBuilder._variables!.decoratedElementType(element))); - } - - @override - DartType getFieldType(PropertyInducingElementImpl element) => - _wrapExceptions(_fixBuilder.unit, () => element.typeInternal, () { - assert(!element.isSynthetic); - return _fixBuilder._computeMigratedType(element); - }); - - @override - List getListElements(ListLiteral node) => _wrapExceptions( - node, - () => node.elements, - () => _collectionElements[node] ??= - _transformCollectionElements(node.elements)); - - @override - List getSetOrMapElements(SetOrMapLiteral node) => - _wrapExceptions( - node, - () => node.elements, - () => _collectionElements[node] ??= - _transformCollectionElements(node.elements)); - - @override - DartType? getTypeParameterBound(TypeParameterElementImpl element) { - var decoratedBound = _fixBuilder._variables! - .decoratedTypeParameterBound(element, allowNullUnparentedBounds: true); - if (decoratedBound == null) return element.boundInternal; - var bound = _fixBuilder._variables!.toFinalType(decoratedBound); - if (bound is DynamicType) { - return null; - } else if (bound.isDartCoreObject && - bound.nullabilitySuffix == NullabilitySuffix.question) { - return null; - } else { - return bound; - } - } - - @override - DartType getVariableType(VariableElementImpl variable) => - _wrapExceptions(_fixBuilder.unit, () => variable.typeInternal, () { - if (variable.library == null) { - // This is a synthetic variable created during resolution (e.g. a - // parameter of a function type), so the type it currently has is the - // correct post-migration type. - return variable.typeInternal; - } - if (variable is ParameterElement) { - var enclosingElement = variable.enclosingElement; - if (enclosingElement is PropertyAccessorElement && - enclosingElement.isSynthetic) { - // This is the parameter of a synthetic getter, so it has the same - // type as the corresponding variable. - return _fixBuilder._computeMigratedType(enclosingElement.variable); - } - } - return _fixBuilder._computeMigratedType(variable); - }); - - @override - bool isIndexExpressionNullAware(IndexExpression node) { - // Null-aware index expressions weren't supported prior to NNBD. - assert(!node.isNullAware); - return false; - } - - @override - bool isLibraryNonNullableByDefault(LibraryElementImpl element) { - return _fixBuilder._graph.isBeingMigrated(element.source) || - element.isNonNullableByDefaultInternal; - } - - @override - bool isMethodInvocationNullAware(MethodInvocation node) { - return node.isNullAware && - (_shouldStayNullAware[node] ??= _fixBuilder._shouldStayNullAware(node)); - } - - /// Indicates whether the given [element] is a member of an extension on a - /// potentially nullable type (and hence the extension member can be invoked - /// on a nullable type without introducing a null check). - bool isNullableExtensionMember(Element? element) { - if (element != null) { - var enclosingElement = element.enclosingElement; - if (enclosingElement is ExtensionElement) { - return _fixBuilder._typeSystem - .isPotentiallyNullable(enclosingElement.extendedType); - } - } - return false; - } - - @override - bool isPropertyAccessNullAware(PropertyAccess node) { - return node.isNullAware && - (_shouldStayNullAware[node] ??= _fixBuilder._shouldStayNullAware(node)); - } - - @override - DartType modifyExpressionType( - Expression node, DartType type, DartType? contextType) => - _wrapExceptions(node, () => type, () { - _contextTypes[node] = contextType; - if (node is NamedExpression) { - // Do not attempt to modify named expressions. We should already have - // been called for [node.expression], and we should have made the - // necessary modifications then. - return type; - } - var parent = node.parent; - if (parent is AssignmentExpression) { - if (parent.leftHandSide == node) { - return type; - } - return _assignmentLikeExpressionHandlers[parent]! - .modifyAssignmentRhs(this, node, type); - } else if (parent is PrefixExpression) { - if (_isIncrementOrDecrementOperator(parent.operator.type)) { - return type; - } - } else if (parent is PostfixExpression) { - if (_isIncrementOrDecrementOperator(parent.operator.type)) { - return type; - } - } - return _modifyRValueType(node, type); - }); - - @override - DartType modifyInferredParameterType( - ParameterElement parameter, DartType type) { - var postMigrationType = parameter.type; - if (postMigrationType != type) { - // TODO(paulberry): test field formal parameters. - _fixBuilder._addedParameterTypes[parameter] = postMigrationType; - return postMigrationType; - } - return type; - } - - @override - void reportBinaryExpressionContext( - BinaryExpression node, DartType? contextType) { - _contextTypes[node] = contextType; - } - - @override - void setCompoundAssignmentExpressionTypes(CompoundAssignmentExpression node) { - assert(_assignmentLikeExpressionHandlers[node] == null); - if (node is AssignmentExpression) { - var handler = _AssignmentExpressionHandler(node); - _assignmentLikeExpressionHandlers[node] = handler; - handler.handleLValueType(this, node.readType, node.writeType!); - } else if (node is PrefixExpression) { - assert(_isIncrementOrDecrementOperator(node.operator.type)); - var handler = _PrefixExpressionHandler(node); - _assignmentLikeExpressionHandlers[node] = handler; - handler.handleLValueType(this, node.readType, node.writeType!); - handler.handleAssignmentRhs(this, _fixBuilder.typeProvider.intType); - } else if (node is PostfixExpression) { - assert(_isIncrementOrDecrementOperator(node.operator.type)); - var handler = _PostfixExpressionHandler(node); - _assignmentLikeExpressionHandlers[node] = handler; - handler.handleLValueType(this, node.readType, node.writeType!); - handler.handleAssignmentRhs(this, _fixBuilder.typeProvider.intType); - } else { - throw StateError('(${node.runtimeType}) $node'); - } - } - - @override - void setFlowAnalysis( - FlowAnalysis? - flowAnalysis) { - _flowAnalysis = flowAnalysis; - } - - DartType _addCastOrNullCheck( - Expression node, DartType expressionType, DartType contextType) { - var expressionFutureTypeArgument = _getFutureTypeArgument(expressionType); - var contextFutureOrTypeArgument = _getFutureOrTypeArgument(contextType); - var parent = node.parent; - if (parent is AwaitExpression && - expressionFutureTypeArgument != null && - contextFutureOrTypeArgument != null) { - // `node` is an expression inside an await expression, and is a Future of - // a nullable type. The context type is a FutureOr... - var nonNullType = _fixBuilder._typeSystem - .promoteToNonNull(expressionFutureTypeArgument); - if (_fixBuilder._typeSystem.isSubtypeOf(nonNullType, contextType)) { - // ... and a null-check will get us where we need to be; add a - // null-check to the outer await, instead of adding a cast to FutureOr - // inside the await. - var change = _createExpressionChange( - parent, expressionFutureTypeArgument, contextType); - var checks = _fixBuilder._variables! - .expressionChecks(_fixBuilder.source, parent); - var info = AtomicEditInfo(change.description, checks?.edges ?? {}); - (_fixBuilder._getChange(parent) as NodeChangeForExpression) - .addExpressionChange(change, info); - _deferredExpressionProcessing[parent] = (_) => change.resultType; - return expressionType; - } - } - - var checks = - _fixBuilder._variables!.expressionChecks(_fixBuilder.source, node); - var change = _createExpressionChange(node, expressionType, contextType); - var info = AtomicEditInfo(change.description, checks?.edges ?? {}); - (_fixBuilder._getChange(node) as NodeChangeForExpression) - .addExpressionChange(change, info); - return change.resultType; - } - - /// Ensures that the migrated file will contain an import of the extension - /// called [extensionName] from `package:collection/collection.dart`, and that - /// the pubspec will be appropriately updated (if necessary). - void _addCollectionPackageExtension(String extensionName) { - _fixBuilder.neededCollectionPackageExtensions.add(extensionName); - _fixBuilder._neededPackages['collection'] = - Version.parse('1.15.0-nullsafety.4'); - } - - DartType _addNullCheck(Expression node, DartType type, - {AtomicEditInfo? info, HintComment? hint}) { - var change = _createNullCheckChange(node, type, hint: hint); - var checks = - _fixBuilder._variables!.expressionChecks(_fixBuilder.source, node); - info ??= AtomicEditInfo(change.description, checks?.edges ?? {}); - var nodeChangeForExpression = - _fixBuilder._getChange(node) as NodeChangeForExpression; - nodeChangeForExpression.addExpressionChange(change, info); - return change.resultType; - } - - bool _canBeInvokedOnNullable(Identifier identifier) => - isDeclaredOnObject(identifier.name) || - isNullableExtensionMember(identifier.staticElement); - - ExpressionChange _createExpressionChange( - Expression node, DartType expressionType, DartType contextType) { - var expressionFutureTypeArgument = _getFutureTypeArgument(expressionType); - var contextFutureTypeArgument = _getFutureTypeArgument(contextType); - if (expressionFutureTypeArgument != null && - contextFutureTypeArgument != null) { - return IntroduceThenChange( - contextType, - _createExpressionChange( - node, expressionFutureTypeArgument, contextFutureTypeArgument)); - } - // Either a cast or a null check is needed. We prefer to do a null - // check if we can. - var nonNullType = _fixBuilder._typeSystem.promoteToNonNull(expressionType); - if (_fixBuilder._typeSystem.isSubtypeOf(nonNullType, contextType)) { - return _createNullCheckChange(node, expressionType); - } else { - _flowAnalysis!.asExpression_end(node, contextType); - var glb = _fixBuilder._typeSystem - .greatestLowerBound(contextType, expressionType); - - if (glb != _fixBuilder._typeSystem.typeProvider.neverType && - !identical(glb, expressionType)) { - return IntroduceAsChange(glb, isDowncast: true); - } - // there is no sense to do casts of unrelated types, let it fail at - // compile time, so users see it earlier. - return NoValidMigrationChange(contextType); - } - } - - ExpressionChange _createNullCheckChange(Expression node, DartType type, - {HintComment? hint}) { - var resultType = _fixBuilder._typeSystem.promoteToNonNull(type as TypeImpl); - _flowAnalysis!.nonNullAssert_end(node); - return type.isDartCoreNull && hint == null - ? NoValidMigrationChange(resultType) - : NullCheckChange(resultType, hint: hint); - } - - Expression _findNullabilityContextAncestor(Expression node) { - while (true) { - var parent = node.parent; - if (parent is BinaryExpression && - parent.operator.type == TokenType.QUESTION_QUESTION && - identical(node, parent.rightOperand)) { - node = parent; - continue; - } - return node; - } - } - - InterfaceType _getClassInterface( - InterfaceElement class_, InterfaceElement superclass) { - var decoratedSupertype = _fixBuilder._decoratedClassHierarchy! - .getDecoratedSupertype(class_, superclass); - var finalType = _fixBuilder._variables!.toFinalType(decoratedSupertype); - return finalType as InterfaceType; - } - - DartType? _getFutureOrTypeArgument(DartType type) { - if (type is InterfaceType && type.isDartAsyncFutureOr) { - var typeArguments = type.typeArguments; - if (typeArguments.isNotEmpty) { - return typeArguments.first; - } - } - return null; - } - - DartType? _getFutureTypeArgument(DartType type) { - if (type is InterfaceType && type.isDartAsyncFuture) { - var typeArguments = type.typeArguments; - if (typeArguments.isNotEmpty) { - return typeArguments.first; - } - } - return null; - } - - bool _isSubtypeOrCoercible(DartType type, DartType context) { - var typeSystem = _fixBuilder._typeSystem; - if (typeSystem.isSubtypeOf(type, context)) { - return true; - } - if (context is FunctionType && type is InterfaceType) { - var callMethod = type.lookUpMethod2( - 'call', _fixBuilder.unit!.declaredElement!.library); - if (callMethod != null) { - var variables = _fixBuilder._variables!; - var callMethodType = variables.toFinalType( - variables.decoratedElementType(callMethod.declaration)); - if (callMethod is MethodMember) { - callMethodType = - callMethod.substitution.substituteType(callMethodType); - } - if (typeSystem.isSubtypeOf(callMethodType, context)) { - return true; - } - } - } - return false; - } - - DartType _modifyRValueType(Expression node, DartType type, - {DartType? context}) { - var deferredProcessing = _deferredExpressionProcessing.remove(node); - if (deferredProcessing != null) { - type = deferredProcessing(type); - } - var hint = - _fixBuilder._variables!.getNullCheckHint(_fixBuilder.source, node); - if (hint != null) { - type = _addNullCheck(node, type, - info: AtomicEditInfo( - NullabilityFixDescription.checkExpressionDueToHint, - { - FixReasonTarget.root: - FixReason_NullCheckHint(CodeReference.fromAstNode(node)) - }, - hintComment: hint), - hint: hint); - } - if (type is DynamicType) return type; - var ancestor = _findNullabilityContextAncestor(node); - context ??= _contextTypes[ancestor] ?? DynamicTypeImpl.instance; - if (!_isSubtypeOrCoercible(type, context)) { - return _tryTransformOrElse(node, type) ?? - _tryTransformWhere(node, type) ?? - _addCastOrNullCheck(node, type, context); - } else { - // Even if the type is a subtype of its context, we still want to - // transform `.where`. - type = _tryTransformWhere(node, type) ?? type; - } - if (!_fixBuilder._typeSystem.isNullable(type)) return type; - if (_needsNullCheckDueToStructure(ancestor)) { - return _addNullCheck(node, type); - } - return type; - } - - bool _needsNullCheckDueToStructure(Expression node) { - var parent = node.parent; - - if (parent is BinaryExpression) { - if (identical(node, parent.leftOperand)) { - var operatorType = parent.operator.type; - if (operatorType == TokenType.QUESTION_QUESTION || - operatorType == TokenType.EQ_EQ || - operatorType == TokenType.BANG_EQ) { - return false; - } else { - return !isNullableExtensionMember(parent.staticElement); - } - } - } else if (parent is PrefixedIdentifier) { - if (_canBeInvokedOnNullable(parent.identifier)) { - return false; - } - return identical(node, parent.prefix); - } else if (parent is PropertyAccess) { - if (_canBeInvokedOnNullable(parent.propertyName)) { - return false; - } - return !parent.isNullAware && identical(node, parent.target); - } else if (parent is MethodInvocation) { - if (_canBeInvokedOnNullable(parent.methodName)) { - return false; - } - return !parent.isNullAware && identical(node, parent.target); - } else if (parent is CascadeExpression) { - if (parent.cascadeSections.every((e) => - e is MethodInvocation && _canBeInvokedOnNullable(e.methodName) || - e is PropertyAccess && _canBeInvokedOnNullable(e.propertyName))) { - return false; - } - return !parent.isNullAware && identical(node, parent.target); - } else if (parent is IndexExpression) { - if (identical(node, parent.target)) { - return !isNullableExtensionMember(parent.staticElement); - } else { - return false; - } - } else if (parent is ConditionalExpression) { - return identical(node, parent.condition); - } else if (parent is FunctionExpressionInvocation) { - if (identical(node, parent.function)) { - return !isNullableExtensionMember(parent.staticElement); - } else { - return false; - } - } else if (parent is PrefixExpression) { - // TODO(paulberry): for prefix increment/decrement, inserting a null check - // isn't sufficient. - return !isNullableExtensionMember(parent.staticElement); - } else if (parent is ThrowExpression) { - return true; - } - return false; - } - - CollectionElement? _transformCollectionElement(CollectionElement? node) { - while (node is IfElement) { - var conditionalDiscard = _fixBuilder._variables! - .getConditionalDiscard(_fixBuilder.source, node); - if (conditionalDiscard == null || - conditionalDiscard.keepTrue && conditionalDiscard.keepFalse) { - return node; - } - var conditionValue = conditionalDiscard.keepTrue; - var ifElement = node; - node = conditionValue ? ifElement.thenElement : ifElement.elseElement; - } - return node; - } - - List _transformCollectionElements( - NodeList elements) { - return elements - .map(_transformCollectionElement) - .whereType() - .toList(); - } - - /// If [node] is an `orElse` argument to an iterable method that should be - /// transformed, transforms it and returns [type] (this indicates to the - /// caller that no further transformations to this expression need to be - /// considered); otherwise returns `null`. - DartType? _tryTransformOrElse(Expression node, DartType type) { - var transformationInfo = - _fixBuilder._whereOrNullTransformer.tryTransformOrElseArgument(node); - if (transformationInfo != null) { - // We can fix this by dropping the node and changing the method call. - _addCollectionPackageExtension('IterableExtension'); - var info = AtomicEditInfo( - NullabilityFixDescription.changeMethodName( - transformationInfo.originalName, - transformationInfo.replacementName), - {}); - (_fixBuilder._getChange(transformationInfo.methodInvocation.methodName) - as NodeChangeForMethodName) - .replaceWith(transformationInfo.replacementName, info); - (_fixBuilder._getChange(transformationInfo.methodInvocation.argumentList) - as NodeChangeForArgumentList) - .dropArgument(transformationInfo.orElseArgument, info); - _deferredExpressionProcessing[transformationInfo.methodInvocation] = - (methodInvocationType) => _fixBuilder._typeSystem - .makeNullable(methodInvocationType as TypeImpl); - return type; - } - return null; - } - - /// If [node] is a call to `Iterable.where` that should be transformed, - /// transforms it and returns the type of the transformed method call (this - /// indicates to the caller that no further transformations to this expression - /// need to be considered); otherwise returns `null`. - DartType? _tryTransformWhere(Expression node, DartType type) { - var transformationInfo = - _fixBuilder._whereNotNullTransformer.tryTransformMethodInvocation(node); - if (transformationInfo != null) { - // We can fix this by dropping the method call's argument and changing the - // call. - _addCollectionPackageExtension('IterableNullableExtension'); - var info = AtomicEditInfo( - NullabilityFixDescription.changeMethodName( - transformationInfo.originalName, - transformationInfo.replacementName), - {}); - (_fixBuilder._getChange(transformationInfo.methodInvocation.methodName) - as NodeChangeForMethodName) - .replaceWith(transformationInfo.replacementName, info); - (_fixBuilder._getChange(transformationInfo.methodInvocation.argumentList) - as NodeChangeForArgumentList) - .dropArgument(transformationInfo.argument, info); - return _fixBuilder._whereNotNullTransformer - .transformPostMigrationInvocationType(type); - } - return null; - } - - /// Runs the computation in [compute]. If an exception occurs and - /// [_fixBuilder.listener] is non-null, the exception is reported to the - /// listener and [fallback] is called to produce a result. Otherwise the - /// exception is propagated normally. - T _wrapExceptions( - AstNode? node, T Function() fallback, T Function() compute) { - if (_fixBuilder.listener == null) return compute(); - try { - return compute(); - } catch (exception, stackTrace) { - _fixBuilder.listener! - .reportException(_fixBuilder.source, node, exception, stackTrace); - return fallback(); - } - } -} - -/// Problem reported by [FixBuilder] when encountering a non-nullable unnamed -/// optional parameter that lacks a default value. -class NonNullableUnnamedOptionalParameter implements Problem { - const NonNullableUnnamedOptionalParameter(); -} - -/// Common supertype for problems reported by [FixBuilder._addProblem]. -abstract class Problem {} - -/// Specialization of [_AssignmentLikeExpressionHandler] for -/// [AssignmentExpression]. -class _AssignmentExpressionHandler extends _AssignmentLikeExpressionHandler { - @override - final AssignmentExpression node; - - _AssignmentExpressionHandler(this.node); - - @override - MethodElement? get combiner => node.staticElement; - - @override - TokenType get combinerType => node.operator.type; - - @override - Expression get target => node.leftHandSide; -} - -/// Data structure keeping track of intermediate results when the fix builder -/// is handling an assignment expression, or an expression that desugars to an -/// assignment. -abstract class _AssignmentLikeExpressionHandler { - /// For compound and null-aware assignments, the type read from the LHS. - late final DartType? readType; - - /// The type that may be written to the LHS. - late final DartType writeType; - - /// The type that should be used as a context type when inferring the RHS. - DartType? rhsContextType; - - /// Gets the static element representing the combiner. - MethodElement? get combiner; - - /// Gets the operator type representing the combiner. - TokenType get combinerType; - - /// Gets the expression in question. - Expression get node; - - /// Gets the target of the assignment. - Expression get target; - - /// Called after visiting the RHS of the assignment, to verify that for - /// compound assignments, the return value of the assignment is assignable to - /// [writeType]. - void handleAssignmentRhs( - MigrationResolutionHooksImpl hooks, DartType rhsType) { - MethodElement? combiner = this.combiner; - if (combiner != null) { - var fixBuilder = hooks._fixBuilder; - var combinerReturnType = - fixBuilder._typeSystem.refineBinaryExpressionType( - readType!, - combinerType, - rhsType, - combiner.returnType, - combiner, - ); - if (!fixBuilder._typeSystem.isSubtypeOf(combinerReturnType, writeType)) { - (fixBuilder._getChange(node) as NodeChangeForAssignmentLike) - .hasBadCombinedType = true; - } - } - } - - /// Called after visiting the LHS of the assignment. Records the [readType], - /// [writeType], and [rhsContextType]. Also verifies that for compound - /// assignments, the [readType] is non-nullable, and that for null-aware - /// assignments, the [readType] is nullable. - void handleLValueType(MigrationResolutionHooksImpl hooks, - DartType? readTypeToSet, DartType writeTypeToSet) { - assert(writeTypeToSet.nullabilitySuffix != NullabilitySuffix.star); - writeType = writeTypeToSet; - // TODO(scheglov) Remove this after the analyzer breaking change that - // will top setting types for LHS. - (target as ExpressionImpl).staticType = writeTypeToSet; - var fixBuilder = hooks._fixBuilder; - if (combinerType == TokenType.EQ) { - rhsContextType = writeTypeToSet; - } else { - readType = readTypeToSet; - assert(readType!.nullabilitySuffix != NullabilitySuffix.star); - if (combinerType == TokenType.QUESTION_QUESTION_EQ) { - rhsContextType = writeTypeToSet; - if (fixBuilder._typeSystem.isNonNullable(readType!)) { - (fixBuilder._getChange(node) as NodeChangeForAssignment) - .isWeakNullAware = true; - } - } else { - if (readType is! DynamicType && - fixBuilder._typeSystem.isPotentiallyNullable(readType!)) { - (fixBuilder._getChange(node) as NodeChangeForAssignmentLike) - .hasNullableSource = true; - } - } - } - } - - /// Called after visiting the RHS of the assignment. - DartType modifyAssignmentRhs(MigrationResolutionHooksImpl hooks, - Expression subexpression, DartType type) { - type = - hooks._modifyRValueType(subexpression, type, context: rhsContextType); - handleAssignmentRhs(hooks, type); - return type; - } -} - -/// Visitor that computes additional migrations on behalf of [FixBuilder] that -/// should be run after resolution -class _FixBuilderPostVisitor extends GeneralizingAstVisitor - with PermissiveModeVisitor { - final FixBuilder _fixBuilder; - - _FixBuilderPostVisitor(this._fixBuilder); - - @override - NullabilityMigrationListener? get listener => _fixBuilder.listener; - - @override - Source? get source => _fixBuilder.source; - - @override - void visitAsExpression(AsExpression node) { - if (!_fixBuilder._variables!.wasUnnecessaryCast(_fixBuilder.source, node) && - BestPracticesVerifier.isUnnecessaryCast( - node, _fixBuilder._typeSystem)) { - (_fixBuilder._getChange(node) as NodeChangeForAsExpression).removeAs = - true; - } - } - - @override - void visitCompilationUnit(CompilationUnit node) { - if ((node as CompilationUnitImpl).languageVersionToken != null) { - (_fixBuilder._getChange(node) as NodeChangeForCompilationUnit) - .removeLanguageVersionComment = true; - } - if (_fixBuilder.neededCollectionPackageExtensions.isNotEmpty) { - var packageCollectionImport = - _findImportDirective(node, 'package:collection/collection.dart'); - if (packageCollectionImport != null) { - for (var combinator in packageCollectionImport.combinators) { - if (combinator is ShowCombinator) { - for (var extensionName - in _fixBuilder.neededCollectionPackageExtensions) { - _ensureShows(combinator, extensionName); - } - } - } - } else { - var change = - _fixBuilder._getChange(node) as NodeChangeForCompilationUnit; - for (var extensionName - in _fixBuilder.neededCollectionPackageExtensions) { - change.addImport('package:collection/collection.dart', extensionName); - } - } - } - super.visitCompilationUnit(node); - } - - @override - void visitSimpleFormalParameter(SimpleFormalParameter node) { - if (node.type == null) { - var typeToAdd = _fixBuilder._addedParameterTypes[node.declaredElement!]; - if (typeToAdd != null) { - (_fixBuilder._getChange(node) as NodeChangeForSimpleFormalParameter) - .addExplicitType = typeToAdd; - } - } - } - - @override - void visitVariableDeclarationList(VariableDeclarationList node) { - if (node.type == null) { - // TODO(paulberry): for fields, handle inference via override as well. - List neededTypes = []; - List inferredTypes = []; - bool explicitTypeNeeded = false; - for (var variableDeclaration in node.variables) { - var neededType = _fixBuilder - ._computeMigratedType(variableDeclaration.declaredElement!); - neededTypes.add(neededType); - var inferredType = variableDeclaration.initializer?.staticType ?? - _fixBuilder.typeProvider.dynamicType; - inferredTypes.add(inferredType); - if (neededType != inferredType) { - explicitTypeNeeded = true; - } - } - if (explicitTypeNeeded) { - var firstNeededType = neededTypes[0]; - if (neededTypes.any((t) => t != firstNeededType)) { - // Different variables need different types. We handle this by - // introducing casts, which is not great but gets the job done. - for (int i = 0; i < node.variables.length; i++) { - if (neededTypes[i] != inferredTypes[i]) { - // We only have to worry about variables with initializers because - // variables without initializers will get the type `dynamic`. - var initializer = node.variables[i].initializer; - if (initializer != null) { - (_fixBuilder._getChange(initializer) as NodeChangeForExpression) - .addExpressionChange( - IntroduceAsChange(neededTypes[i], isDowncast: false), - AtomicEditInfo( - NullabilityFixDescription.otherCastExpression, {})); - } - } - } - } else { - (_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList) - .addExplicitType = firstNeededType; - } - } - } - - // Check if the nullability node for a single variable declaration has been - // declared to be late. - if (node.variables.length == 1) { - var variableElement = node.variables.single.declaredElement!; - var lateCondition = _fixBuilder._variables! - .decoratedElementType(variableElement) - .node - .lateCondition; - switch (lateCondition) { - case LateCondition.possiblyLate: - (_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList) - .lateAdditionReason = LateAdditionReason.inference; - break; - case LateCondition.possiblyLateDueToTestSetup: - (_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList) - .lateAdditionReason = LateAdditionReason.testVariableInference; - break; - case LateCondition.lateDueToHint: - // Handled below. - case LateCondition.notLate: - // Nothing to do. - } - } - - var lateHint = _fixBuilder._variables!.getLateHint(source, node); - if (lateHint != null) { - (_fixBuilder._getChange(node) as NodeChangeForVariableDeclarationList) - .lateHint = lateHint; - } - super.visitVariableDeclarationList(node); - } - - /// Creates the necessary changes to ensure that [combinator] shows [name]. - void _ensureShows(ShowCombinator combinator, String name) { - if (combinator.shownNames.any((shownName) => shownName.name == name)) { - return; - } - (_fixBuilder._getChange(combinator) as NodeChangeForShowCombinator) - .addName(name); - } - - /// Searches [unit] for an unprefixed import directive whose URI matches - /// [uri], returning it if found, or `null` if not found. - ImportDirective? _findImportDirective(CompilationUnit unit, String uri) { - for (var directive in unit.directives) { - if (directive is ImportDirective && - directive.prefix == null && - directive.uri.stringValue == uri) { - return directive; - } - } - return null; - } -} - -/// Visitor that computes additional migrations on behalf of [FixBuilder] that -/// don't need to be integrated into the resolver itself, and should be run -/// prior to resolution -class _FixBuilderPreVisitor extends GeneralizingAstVisitor - with PermissiveModeVisitor { - final FixBuilder _fixBuilder; - - _FixBuilderPreVisitor(this._fixBuilder); - - @override - NullabilityMigrationListener? get listener => _fixBuilder.listener; - - @override - Source? get source => _fixBuilder.source; - - @override - void visitDefaultFormalParameter(DefaultFormalParameter node) { - var element = node.declaredElement; - if (node.defaultValue == null) { - var requiredHint = - _fixBuilder._variables!.getRequiredHint(_fixBuilder.source, node); - var nullabilityNode = - _fixBuilder._variables!.decoratedElementType(element!).node; - if (!nullabilityNode.isNullable) { - var enclosingElement = element.enclosingElement; - if (enclosingElement is ConstructorElement && - enclosingElement.isFactory && - enclosingElement.redirectedConstructor != null && - !node.declaredElement!.hasRequired) { - // Redirecting factory constructors inherit their parameters' default - // values from the constructors they redirect to, so the lack of a - // default value doesn't mean the parameter has to be nullable. - } else if (element.isNamed) { - _addRequiredKeyword(node, nullabilityNode, requiredHint); - } else { - _fixBuilder._addProblem( - node, const NonNullableUnnamedOptionalParameter()); - } - } else if (requiredHint != null || - element.metadata.any((m) => m.isRequired)) { - _addRequiredKeyword(node, nullabilityNode, requiredHint); - } - } - super.visitDefaultFormalParameter(node); - } - - @override - void visitFieldFormalParameter(FieldFormalParameter node) { - if (node.type == null) { - // Potentially add an explicit type to a field formal parameter. - var decl = node.declaredElement as FieldFormalParameterElement; - var decoratedType = _fixBuilder._variables!.decoratedElementType(decl); - var field = decl.field; - if (field != null) { - var decoratedFieldType = - _fixBuilder._variables!.decoratedElementType(field); - var typeToAdd = _fixBuilder._variables!.toFinalType(decoratedType); - var fieldFinalType = - _fixBuilder._variables!.toFinalType(decoratedFieldType); - if (typeToAdd is InterfaceType && - !_fixBuilder._typeSystem.isSubtypeOf(fieldFinalType, typeToAdd)) { - (_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter) - .addExplicitType = typeToAdd; - } - } - } else if (node.parameters != null) { - // Handle function-typed field formal parameters. - var decoratedType = - _fixBuilder._variables!.decoratedElementType(node.declaredElement!); - if (decoratedType.node.isNullable) { - (_fixBuilder._getChange(node) as NodeChangeForFieldFormalParameter) - .recordNullability(decoratedType, decoratedType.node.isNullable, - nullabilityHint: - _fixBuilder._variables!.getNullabilityHint(source, node)); - } - } - super.visitFieldFormalParameter(node); - } - - @override - void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { - var decoratedType = - _fixBuilder._variables!.decoratedElementType(node.declaredElement!); - if (decoratedType.node.isNullable) { - (_fixBuilder._getChange(node) - as NodeChangeForFunctionTypedFormalParameter) - .recordNullability(decoratedType, decoratedType.node.isNullable, - nullabilityHint: - _fixBuilder._variables!.getNullabilityHint(source, node)); - } - super.visitFunctionTypedFormalParameter(node); - } - - @override - void visitGenericFunctionType(GenericFunctionType node) { - var decoratedType = _fixBuilder._variables! - .decoratedTypeAnnotation(_fixBuilder.source, node); - if (!typeIsNonNullableByContext(node)) { - _makeTypeNameNullable(node, decoratedType); - } - (node as GenericFunctionTypeImpl).type = - _fixBuilder._variables!.toFinalType(decoratedType); - super.visitGenericFunctionType(node); - } - - @override - void visitMethodDeclaration(MethodDeclaration node) { - var nullableAnnotation = BuiltValueTransformer.findNullableAnnotation(node); - if (nullableAnnotation != null) { - var info = AtomicEditInfo( - NullabilityFixDescription.removeNullableAnnotation, {}); - (_fixBuilder._getChange(node) as NodeChangeForMethodDeclaration) - ..annotationToRemove = nullableAnnotation - ..removeAnnotationInfo = info; - } - super.visitMethodDeclaration(node); - } - - @override - void visitNamedType(NamedType node) { - var decoratedType = _fixBuilder._variables! - .decoratedTypeAnnotation(_fixBuilder.source, node); - if (!typeIsNonNullableByContext(node)) { - if (!_typeIsNaturallyNullable(decoratedType.type!)) { - _makeTypeNameNullable(node, decoratedType); - } - } - (node as NamedTypeImpl).type = - _fixBuilder._variables!.toFinalType(decoratedType); - super.visitNamedType(node); - } - - void _addRequiredKeyword(DefaultFormalParameter parameter, - NullabilityNode node, HintComment? requiredHint) { - // Change an existing `@required` annotation into a `required` keyword if - // possible. - final element = parameter.declaredElement!; - final method = element.enclosingElement!; - final cls = method.enclosingElement!; - var info = AtomicEditInfo( - NullabilityFixDescription.addRequired( - cls.name, method.name, element.name), - {FixReasonTarget.root: node}); - var metadata = parameter.metadata; - if (metadata.isNotEmpty) { - // Only the last annotation can be changed into a `required` keyword; - // changing an earlier annotation into a keyword would be illegal. - var lastAnnotation = metadata.last; - if (lastAnnotation.elementAnnotation!.isRequired) { - (_fixBuilder._getChange(lastAnnotation) as NodeChangeForAnnotation) - ..changeToRequiredKeyword = true - ..changeToRequiredKeywordInfo = info; - return; - } - } - // Otherwise create a new `required` keyword. - var nodeChange = (_fixBuilder._getChange(parameter) - as NodeChangeForDefaultFormalParameter) - ..addRequiredKeyword = true - ..addRequiredKeywordInfo = info - ..requiredHint = requiredHint; - var requiredAnnotation = metadata.firstWhereOrNull( - (annotation) => annotation.elementAnnotation!.isRequired); - if (requiredAnnotation != null) { - // If the parameter was annotated with `@required`, but it was not the - // last annotation, we remove the annotation in addition to adding the - // `required` keyword. - nodeChange - ..annotationToRemove = requiredAnnotation - ..removeAnnotationInfo = info; - } - } - - void _makeTypeNameNullable(TypeAnnotation node, DecoratedType decoratedType) { - bool makeNullable = decoratedType.node.isNullable; - if (decoratedType.type!.isDartAsyncFutureOr) { - var typeArguments = decoratedType.typeArguments; - if (typeArguments.length == 1) { - var typeArgument = typeArguments[0]!; - if (_typeIsNaturallyNullable(typeArgument.type!) || - typeArgument.node.isNullable) { - // FutureOr? is equivalent to FutureOr, so there is no need to - // make this type nullable. - makeNullable = false; - } - } - } - (_fixBuilder._getChange(node) as NodeChangeForTypeAnnotation) - .recordNullability( - decoratedType, makeNullable, - nullabilityHint: - _fixBuilder._variables!.getNullabilityHint(source, node)); - } - - bool _typeIsNaturallyNullable(DartType type) => - type is DynamicType || type is VoidType || type.isDartCoreNull; -} - -/// Specialization of [_AssignmentLikeExpressionHandler] for -/// [PostfixExpression]. -class _PostfixExpressionHandler extends _AssignmentLikeExpressionHandler { - @override - final PostfixExpression node; - - _PostfixExpressionHandler(this.node) - : assert(_isIncrementOrDecrementOperator(node.operator.type)); - - @override - MethodElement? get combiner => node.staticElement; - - @override - TokenType get combinerType => node.operator.type; - - @override - Expression get target => node.operand; -} - -/// Specialization of [_AssignmentLikeExpressionHandler] for -/// [PrefixExpression]. -class _PrefixExpressionHandler extends _AssignmentLikeExpressionHandler { - @override - final PrefixExpression node; - - _PrefixExpressionHandler(this.node) - : assert(_isIncrementOrDecrementOperator(node.operator.type)); - - @override - MethodElement? get combiner => node.staticElement; - - @override - TokenType get combinerType => node.operator.type; - - @override - Expression get target => node.operand; -} diff --git a/pkg/nnbd_migration/lib/src/front_end/charcodes.dart b/pkg/nnbd_migration/lib/src/front_end/charcodes.dart deleted file mode 100644 index 72c75ccc7947..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/charcodes.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2020, 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. - -/// "Carriage return" control character. -const int $cr = 0x0d; - -/// "Line feed" control character. -const int $lf = 0x0a; - -/// Space character. -const int $space = 0x20; diff --git a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart deleted file mode 100644 index 43e1421e5ad7..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/source/source.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart' - hide AnalysisError; -import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart'; -import 'package:pub_semver/src/version.dart'; - -class DartFixListener { - final DriverProviderImpl? server; - - final SourceChange sourceChange = SourceChange('null safety migration'); - - final List suggestions = []; - - final DartFixListenerClient client; - - DartFixListener(this.server, this.client); - - /// Record an edit to be sent to the client. - /// - /// The associated suggestion should be separately added by calling - /// [addSuggestion]. - void addEditWithoutSuggestion(Source source, SourceEdit edit) { - sourceChange.addEdit(source.fullName, -1, edit); - } - - /// Record a source change to be sent to the client. - void addSourceFileEdit( - String description, Location location, SourceFileEdit fileEdit) { - suggestions.add(DartFixSuggestion(description, location: location)); - for (var sourceEdit in fileEdit.edits) { - sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit); - } - } - - /// Record a suggestion to be sent to the client. - /// - /// The associated edits should be separately added by calling - /// [addEditWithoutRecommendation]. - void addSuggestion(String description, Location location) { - suggestions.add(DartFixSuggestion(description, location: location)); - } - - /// Reports to then user that they need to run `dart pub get` after the - /// migration finishes. - void reportPubGetNeeded(Map neededPackages) { - client.onMessage( - 'Your pubspec has been updated. Please run `dart pub get`.'); - } - - /// Reset this listener so that it can accrue a new set of changes. - void reset() { - suggestions.clear(); - sourceChange - ..edits.clear() - ..linkedEditGroups.clear() - ..selection = null - ..id = null; - } -} - -abstract class DartFixListenerClient { - /// Add the given [detail] to the list of details to be returned to the - /// client. - void onException(String detail); - - /// Callback that reports a fatal error to the client. - void onFatalError(String detail); - - /// Reports the given [detail] message to the client; not an error condition. - void onMessage(String detail); -} - -class DartFixSuggestion { - final String description; - - final Location location; - - DartFixSuggestion(this.description, {required this.location}); -} diff --git a/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart b/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart deleted file mode 100644 index ac3c21fcd3a0..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/driver_provider_impl.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/analysis/analysis_context.dart'; -import 'package:analyzer/dart/analysis/session.dart'; -import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; - -class DriverProviderImpl { - final ResourceProvider resourceProvider; - - final AnalysisContext? analysisContext; - - DriverProviderImpl(this.resourceProvider, this.analysisContext); - - /// Return the appropriate analysis session for the file with the given - /// [path]. - AnalysisSession getAnalysisSession(String? path) => - analysisContext!.currentSession; -} diff --git a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart deleted file mode 100644 index c0f99d62a30f..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart +++ /dev/null @@ -1,704 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:collection'; - -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/file_system/file_system.dart'; -import 'package:analyzer/src/dart/ast/utilities.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart' - show SourceFileEdit; -import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol; -import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart'; -import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart'; -import 'package:cli_util/cli_logging.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart'; -import 'package:nnbd_migration/src/front_end/instrumentation_information.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/offset_mapper.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:nnbd_migration/src/utilities/progress_bar.dart'; - -/// A builder used to build the migration information for a library. -class InfoBuilder { - /// The node mapper for the migration state. - NodeMapper? nodeMapper; - - /// The logger to use for showing progress when explaining the migration. - final Logger _logger; - - /// The resource provider used to access the file system. - ResourceProvider provider; - - String? includedPath; - - /// The instrumentation information gathered while the migration engine was - /// running. - final InstrumentationInformation info; - - /// The listener used to gather the changes to be applied. - final DartFixListener? listener; - - /// The [NullabilityMigration] instance for this migration. - final NullabilityMigration? migration; - - /// A map from the path of a compilation unit to the information about that - /// unit. - final Map unitMap = {}; - - /// A function which returns whether a file at a given path should be - /// migrated. - final bool Function(String?) shouldBeMigratedFunction; - - /// The set of files which are being considered for migration. - final Iterable? _pathsToProcess; - - /// Initialize a newly created builder. - InfoBuilder( - this.provider, - this.includedPath, - this.info, - this.listener, - this.migration, - this.nodeMapper, - this._logger, - this.shouldBeMigratedFunction, - this._pathsToProcess); - - /// The provider used to get information about libraries. - DriverProviderImpl? get driverProvider => listener!.server; - - /// Return the migration information for all of the libraries that were - /// migrated. - Future> explainMigration() async { - var sourceInfoMap = info.sourceInformation; - Set units = - SplayTreeSet((u1, u2) => u1.path!.compareTo(u2.path!)); - - // Collect all of the sources for which we have [SourceInformation], as well - // as all files which are being "processed" during this migration, which may - // include already migrated files. - var sources = { - ...sourceInfoMap.keys.map((source) => source!.fullName), - ..._pathsToProcess!, - }; - var progressBar = ProgressBar(_logger, sources.length); - - for (var filePath in sources) { - progressBar.tick(); - var session = driverProvider!.getAnalysisSession(filePath); - var result = await session.getResolvedLibrary(filePath!); - if (result is ResolvedLibraryResult) { - for (var unitResult in result.units) { - var sourceInfo = - sourceInfoMap[unitResult.unit.declaredElement!.source]; - // Note: there might have been no information for this unit in - // sourceInfoMap. That can happen if there's an already-migrated - // library being referenced by the code being migrated, but not all - // parts of that library are referenced. To avoid exceptions later - // on, we just create an empty SourceInformation object. - // TODO(paulberry): we don't do a good job of the case where the - // already-migrated library's defining compilation unit isn't - // referenced (we'll just skip the entire library because we'll only - // ever see its parts). - sourceInfo ??= SourceInformation(); - var edit = listener!.sourceChange.getFileEdit(unitResult.path); - var unit = _explainUnit(sourceInfo, unitResult, edit); - if (_pathsToProcess!.contains(unitResult.path)) { - units.add(unit); - } - } - } - } - progressBar.complete(); - return units; - } - - Iterable upstreamTriggeredEdges(NullabilityNodeInfo node, - {bool skipExactNullable = true}) { - var edges = []; - for (var edge in node.upstreamEdges) { - if (skipExactNullable && - node.isExactNullable && - edge.sourceNode!.isExactNullable) { - // When an exact nullable points here, the nullability propagated - // in the other direction. - continue; - } - if (edge.isTriggered) { - edges.add(edge); - } - } - for (final containerNode in node.outerCompoundNodes) { - // We must include the exact nullable edges in the upstream triggered - // edges of the container node. If this node is in a substitution node, - // then it's possible it was marked exact nullable because it's container - // was marked nullable. It's container could have been marked nullable by - // another exact nullable node. We cannot tell. Err on the side of - // surfacing too many reasons. - edges.addAll( - upstreamTriggeredEdges(containerNode, skipExactNullable: false)); - } - - return edges; - } - - void _addSimpleTrace(SimpleFixReasonInfo info, List traces) { - traces.add(TraceInfo( - 'Reason', [_makeTraceEntry(info.description, info.codeReference)])); - } - - /// Returns a list of edits that can be applied. - List _computeEdits( - AtomicEditInfo fixInfo, int offset, ResolvedUnitResult result) { - var content = result.content; - - EditDetail removeHint(String description) => EditDetail.fromSourceEdit( - description, - fixInfo.hintComment!.changesToRemove(content).toSourceEdits().single); - - EditDetail changeHint(String description, String replacement) => - EditDetail.fromSourceEdit( - description, - fixInfo.hintComment! - .changesToReplace(content, replacement) - .toSourceEdits() - .single); - - var edits = []; - var fixKind = fixInfo.description.kind; - switch (fixKind) { - case NullabilityFixKind.addLateDueToHint: - edits.add(removeHint('Remove /*late*/ hint')); - break; - case NullabilityFixKind.addLateFinalDueToHint: - edits.add(removeHint('Remove /*late final*/ hint')); - break; - case NullabilityFixKind.addRequired: - var metaImport = - _findImportDirective(result.unit, 'package:meta/meta.dart'); - if (metaImport == null) { - edits.add( - EditDetail('Add /*required*/ hint', offset, 0, '/*required*/ ')); - } else { - var prefix = metaImport.prefix?.name; - if (prefix == null) { - edits.add( - EditDetail("Mark with '@required'", offset, 0, '@required ')); - } else { - edits.add(EditDetail( - "Mark with '@required'", offset, 0, '@$prefix.required ')); - } - } - break; - case NullabilityFixKind.checkExpression: - // TODO(brianwilkerson) Determine whether we can know that the fix is - // associated with a parameter and insert an assert if it is. - edits.add(EditDetail('Add /*!*/ hint', offset, 0, '/*!*/')); - break; - case NullabilityFixKind.checkExpressionDueToHint: - edits.add(removeHint('Remove /*!*/ hint')); - break; - case NullabilityFixKind.downcastExpression: - case NullabilityFixKind.otherCastExpression: - // There's no useful hint to apply to casts. - break; - case NullabilityFixKind.removeAs: - case NullabilityFixKind.removeDeadCode: - case NullabilityFixKind.removeLanguageVersionComment: - // There's no need for hints around code that is being removed. - break; - case NullabilityFixKind.addType: - case NullabilityFixKind.replaceVar: - // There's no need for hints around inserted types. - break; - case NullabilityFixKind.makeTypeNullable: - case NullabilityFixKind.typeNotMadeNullable: - edits.add(EditDetail('Add /*!*/ hint', offset, 0, '/*!*/')); - edits.add(EditDetail('Add /*?*/ hint', offset, 0, '/*?*/')); - var declarationList = _findVariableDeclaration(result.unit, offset); - if (declarationList != null) { - var lateOffset = _offsetForPossibleLateModifier(declarationList); - if (lateOffset != null) { - edits.add(EditDetail('Add late hint', lateOffset, 0, '/*late*/')); - } - } - break; - case NullabilityFixKind.makeTypeNullableDueToHint: - edits.add(changeHint('Change to /*!*/ hint', '/*!*/')); - edits.add(removeHint('Remove /*?*/ hint')); - break; - case NullabilityFixKind.typeNotMadeNullableDueToHint: - edits.add(removeHint('Remove /*!*/ hint')); - edits.add(changeHint('Change to /*?*/ hint', '/*?*/')); - break; - case NullabilityFixKind.addLate: - case NullabilityFixKind.addLateDueToTestSetup: - // We could add an edit to add a `/*?*/` hint, but the offset is a - // little tricky. - break; - case NullabilityFixKind.conditionFalseInStrongMode: - case NullabilityFixKind.conditionTrueInStrongMode: - case NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode: - case NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode: - // We don't offer any edits around weak-only code. - // TODO(paulberry): offer edits to delete the code that would be dead in - // strong mode (https://github.com/dart-lang/sdk/issues/41554). - break; - case NullabilityFixKind.compoundAssignmentHasBadCombinedType: - case NullabilityFixKind.compoundAssignmentHasNullableSource: - // We don't offer any edits around bad compound assignments or bad - // increment/decrement operations. - break; - case NullabilityFixKind.addImport: - case NullabilityFixKind.changeMethodName: - // These fix kinds have to do with changing iterable method calls to - // their "OrNull" equivalents. We don't offer any hints around - // this transformation. - break; - case NullabilityFixKind.noValidMigrationForNull: - // We don't offer any edits around unmigratable `null`s. The user has - // to fix manually. - break; - case NullabilityFixKind.addThen: - // We don't offer any edits around addition of `.then` to a future. - break; - case NullabilityFixKind.removeNullableAnnotation: - // We don't offer any edits around removal of built_value `@nullable` - // annotations. - break; - } - return edits; - } - - /// Return the navigation sources for the unit associated with the [result]. - List _computeNavigationSources(ResolvedUnitResult result) { - var collector = NavigationCollectorImpl(); - computeDartNavigation( - result.session.resourceProvider, collector, result, null, null); - collector.createRegions(); - var files = collector.files; - var regions = collector.regions; - var rawTargets = collector.targets; - var convertedTargets = - List.filled(rawTargets.length, null); - return regions.map((region) { - var targets = region.targets; - if (targets.isEmpty) { - throw StateError('Targets is empty'); - } - var target = convertedTargets[targets[0]]; - if (target == null) { - var rawTarget = rawTargets[targets[0]]; - target = _targetForRawTarget(files[rawTarget.fileIndex], rawTarget); - convertedTargets[targets[0]] = target; - } - return NavigationSource( - region.offset, null /* line */, region.length, target); - }).toList(); - } - - void _computeTraceNonNullableInfo(NullabilityNodeInfo node, - List traces, FixReasonTarget target) { - var entries = []; - var description = 'Non-nullability reason${target.suffix}'; - var step = node.whyNotNullable; - if (step == null) { - if (node != info.never) { - // 'never' indicates we're describing an edge to never, such as a `!`. - traces.add(TraceInfo(description, [ - _nodeToTraceEntry(node, - description: 'No reason found to make nullable') - ])); - } - return; - } - assert(identical(step.node, node)); - while (step != null && !step.isStartingPoint) { - entries.add(_nodeToTraceEntry(step.node)); - if (step.codeReference != null) { - entries.add(_stepToTraceEntry(step)); - } - step = step.principalCause; - } - traces.add(TraceInfo(description, entries)); - } - - void _computeTraceNullableInfo(NullabilityNodeInfo node, - List traces, FixReasonTarget target) { - var entries = []; - var step = node.whyNullable; - if (step == null) { - return; - } - assert(identical(step.targetNode, node)); - while (step != null) { - entries.add(_nodeToTraceEntry(step.targetNode!)); - if (step.codeReference != null) { - entries.add(_stepToTraceEntry(step)); - } - step = step.principalCause; - } - var description = 'Nullability reason${target.suffix}'; - traces.add(TraceInfo(description, entries)); - } - - List _computeTraces( - Map fixReasons) { - var traces = []; - for (var entry in fixReasons.entries) { - var reason = entry.value; - if (reason is NullabilityNodeInfo) { - if (reason.isNullable) { - _computeTraceNullableInfo(reason, traces, FixReasonTarget.root); - } else { - _computeTraceNonNullableInfo(reason, traces, FixReasonTarget.root); - } - } else if (reason is EdgeInfo) { - if (reason.sourceNode!.isNullable && - !reason.destinationNode.isNullable) { - var target = entry.key!; - _computeTraceNullableInfo(reason.sourceNode!, traces, target); - _computeTraceNonNullableInfo(reason.destinationNode, traces, target); - } - } else if (reason is SimpleFixReasonInfo) { - _addSimpleTrace(reason, traces); - } else { - assert(false, 'Unrecognized reason type: ${reason.runtimeType}'); - } - } - return traces; - } - - /// Return the migration information for the unit associated with the - /// [result]. - UnitInfo _explainUnit(SourceInformation sourceInfo, ResolvedUnitResult result, - SourceFileEdit? fileEdit) { - var unitInfo = _unitForPath(result.path); - unitInfo.sources ??= _computeNavigationSources(result); - var content = result.content; - unitInfo.diskContent = content; - var alreadyMigrated = - result.unit.featureSet.isEnabled(Feature.non_nullable); - unitInfo.wasExplicitlyOptedOut = result.unit.languageVersionToken != null; - if (alreadyMigrated) { - unitInfo.migrationStatus = UnitMigrationStatus.alreadyMigrated; - unitInfo.migrationStatusCanBeChanged = false; - } else if (shouldBeMigratedFunction(result.path)) { - unitInfo.migrationStatus = UnitMigrationStatus.migrating; - unitInfo.migrationStatusCanBeChanged = true; - } else { - unitInfo.migrationStatus = UnitMigrationStatus.optingOut; - unitInfo.migrationStatusCanBeChanged = false; - } - var regions = unitInfo.regions; - - // There are certain rare conditions involving generated code in a bazel - // workspace that can cause a source file to get processed more than once by - // the migration tool (sometimes with a correct URI, sometimes with an - // incorrect URI that corresponds to a file path in the `bazel-out` - // directory). That can cause this method to get called twice for the same - // unit. To avoid this creating user-visible problems, we need to ensure - // that any regions left over from the previous invocation are cleared out - // before we re-populate the region list. - regions.clear(); - - var lineInfo = result.unit.lineInfo; - var insertions = >{}; - var infosSeen = Set.identity(); - - // Apply edits and build the regions. - var changes = sourceInfo.changes ?? {}; - var sourceOffsets = changes.keys.toList(); - sourceOffsets.sort(); - var offset = 0; - var sourceOffset = 0; - for (var nextSourceOffset in sourceOffsets) { - var changesForSourceOffset = changes[nextSourceOffset]!; - var unchangedTextLength = nextSourceOffset! - sourceOffset; - offset += unchangedTextLength; - sourceOffset += unchangedTextLength; - for (var edit in changesForSourceOffset) { - var length = edit.length; - var replacement = edit.replacement; - var end = offset + length; - // Insert the replacement text without deleting the replaced text. - if (replacement.isNotEmpty) { - content = content.replaceRange(end, end, replacement); - (insertions[sourceOffset] ??= []).add(AtomicEdit.insert(replacement)); - } - var info = edit.info; - var edits = info != null - ? _computeEdits(info, sourceOffset, result) - : []; - var lineNumber = lineInfo.getLocation(sourceOffset).lineNumber; - var traces = info == null - ? const [] - : _computeTraces(info.fixReasons); - var description = info?.description; - var isCounted = info != null && infosSeen.add(info); - var explanation = description?.appliedMessage; - var kind = description?.kind; - if (edit.isInsertion) { - regions.add(RegionInfo( - edit.isInformative ? RegionType.informative : RegionType.add, - offset, - replacement.length, - lineNumber, - explanation, - kind, - isCounted, - edits: edits, - traces: traces)); - } else if (edit.isDeletion) { - regions.add(RegionInfo( - edit.isInformative ? RegionType.informative : RegionType.remove, - offset, - length, - lineNumber, - explanation, - kind, - isCounted, - edits: edits, - traces: traces)); - } else if (edit.isReplacement) { - assert(!edit.isInformative); - regions.add(RegionInfo(RegionType.remove, offset, length, lineNumber, - explanation, kind, isCounted, - edits: edits, traces: traces)); - regions.add(RegionInfo(RegionType.add, end, replacement.length, - lineNumber, explanation, kind, isCounted, - edits: edits, traces: traces)); - } else { - throw StateError( - 'Edit is not an insertion, deletion, replacement, nor ' - 'informative: $edit'); - } - sourceOffset += length; - offset += length + replacement.length; - } - } - - // Build the map from source file offset to offset in the modified text. - // We only account for insertions because in the code above, we don't delete - // the modified text. - var edits = insertions.toSourceEdits(); - edits.sort((first, second) => first.offset.compareTo(second.offset)); - var mapper = OffsetMapper.forEdits(edits); - regions.sort((first, second) => first.offset.compareTo(second.offset)); - unitInfo.migrationOffsetMapper = mapper; - unitInfo.content = content; - return unitInfo; - } - - /// Searches [unit] for an import directive whose URI matches [uri], returning - /// it if found, or `null` if not found. - ImportDirective? _findImportDirective(CompilationUnit unit, String uri) { - for (var directive in unit.directives) { - if (directive is ImportDirective && directive.uri.stringValue == uri) { - return directive; - } - } - return null; - } - - /// Returns the variable declaration which covers [offset], or `null` if none - /// does. - VariableDeclarationList? _findVariableDeclaration( - CompilationUnit unit, int offset) { - var nodeLocator = NodeLocator2(offset); - var node = nodeLocator.searchWithin(unit); - if (node == null) { - return null; - } - return node.thisOrAncestorOfType(); - } - - TraceEntryInfo _makeTraceEntry( - String description, CodeReference? codeReference, - {List hintActions = const []}) { - var length = 1; // TODO(paulberry): figure out the correct value. - return TraceEntryInfo( - description, - codeReference?.function, - codeReference == null - ? null - : NavigationTarget(codeReference.path, codeReference.offset, - codeReference.line, length), - hintActions: hintActions); - } - - TraceEntryInfo _nodeToTraceEntry(NullabilityNodeInfo node, - {String? description}) { - description ??= node.toString(); // TODO(paulberry): improve this message - return _makeTraceEntry(description, node.codeReference, - hintActions: node.hintActions.keys - .map((kind) => HintAction(kind, nodeMapper!.idForNode(node))) - .toList()); - } - - /// Returns the offset for a possible `late` modifier which could be inserted - /// into [declarationList], or `null` if none is possible. - int? _offsetForPossibleLateModifier(VariableDeclarationList declarationList) { - if (declarationList.isLate || declarationList.isConst) { - // Don't offer an offset. - return null; - } - var keyword = declarationList.keyword; - if (keyword != null) { - // Offset for possible `late` is before `var`, `const`, or `final`. - return keyword.offset; - } - - var typeAnnotation = declarationList.type; - if (typeAnnotation != null) { - // Without a `keyword`, offset for possible `late` is before the type - // annotation. - return typeAnnotation.offset; - } - - assert( - false, - 'In this VariableDeclarationList, there is no `var`, ' - '`const`, or `final` keyword, nor any type annotation. This variable ' - 'declaration list is not valid: $declarationList'); - return null; - } - - TraceEntryInfo _stepToTraceEntry(PropagationStepInfo step) { - var description = step.edge?.description; - description ??= step.toString(); // TODO(paulberry): improve this message. - return _makeTraceEntry(description, step.codeReference); - } - - /// Return the navigation target in the file with the given [filePath] at the - /// given [offset] ans with the given [length]. - NavigationTarget _targetForRawTarget( - String filePath, protocol.NavigationTarget rawTarget) { - var unitInfo = _unitForPath(filePath); - var offset = rawTarget.offset; - var length = rawTarget.length; - var target = NavigationTarget(filePath, offset, null /* line */, length); - unitInfo.targets.add(target); - return target; - } - - /// Return the unit info for the file at the given [path]. - UnitInfo _unitForPath(String? path) { - return unitMap.putIfAbsent(path, () => UnitInfo(path)); - } - - /// Builds a description for [node]'s enclosing member(s). - /// - /// This may include a class and method name, for example, or the name of the - /// enclosing top-level member. - @visibleForTesting - static String buildEnclosingMemberDescription(AstNode? node) { - for (var enclosingNode = node; - enclosingNode != null; - enclosingNode = enclosingNode.parent) { - if (enclosingNode is ConstructorDeclaration) { - if (enclosingNode.name == null) { - return _describeClassOrExtensionMember( - enclosingNode.parent as CompilationUnitMember?, - 'the default constructor of', - ''); - } else { - return _describeClassOrExtensionMember( - enclosingNode.parent as CompilationUnitMember?, - 'the constructor', - enclosingNode.name!.lexeme); - } - } else if (enclosingNode is MethodDeclaration) { - var functionName = enclosingNode.name.lexeme; - String baseDescription; - if (enclosingNode.isGetter) { - baseDescription = 'the getter'; - } else if (enclosingNode.isOperator) { - baseDescription = 'the operator'; - } else if (enclosingNode.isSetter) { - baseDescription = 'the setter'; - functionName += '='; - } else { - baseDescription = 'the method'; - } - return _describeClassOrExtensionMember( - enclosingNode.parent as CompilationUnitMember?, - baseDescription, - functionName); - } else if (enclosingNode is FunctionDeclaration && - enclosingNode.parent is CompilationUnit) { - var functionName = enclosingNode.name.lexeme; - String baseDescription; - if (enclosingNode.isGetter) { - baseDescription = 'the getter'; - } else if (enclosingNode.isSetter) { - baseDescription = 'the setter'; - functionName += '='; - } else { - baseDescription = 'the function'; - } - return "$baseDescription '$functionName'"; - } else if (enclosingNode is VariableDeclaration) { - var description = _describeVariableDeclaration(enclosingNode); - if (description != null) return description; - } else if (enclosingNode is VariableDeclarationList) { - var description = - _describeVariableDeclaration(enclosingNode.variables[0]); - if (description != null) return description; - } - } - throw ArgumentError( - "Can't describe enclosing member of ${node.runtimeType}"); - } - - static String _describeClassOrExtensionMember(CompilationUnitMember? parent, - String baseDescription, String functionName) { - if (parent is NamedCompilationUnitMember) { - var parentName = parent.name.lexeme; - if (functionName.isEmpty) { - return "$baseDescription '$parentName'"; - } else { - return "$baseDescription '$parentName.$functionName'"; - } - } else if (parent is ExtensionDeclaration) { - if (parent.name == null) { - var extendedTypeString = parent.extendedType.type!.getDisplayString( - withNullability: false, - ); - return "$baseDescription '$functionName' in unnamed extension on $extendedTypeString"; - } else { - return "$baseDescription '${parent.name!.lexeme}.$functionName'"; - } - } else { - throw ArgumentError( - 'Unexpected class or extension type ${parent.runtimeType}'); - } - } - - static String? _describeVariableDeclaration(VariableDeclaration node) { - var variableName = node.name.lexeme; - var parent = node.parent!; - var grandParent = parent.parent; - if (grandParent is FieldDeclaration) { - return _describeClassOrExtensionMember( - grandParent.parent as CompilationUnitMember?, - 'the field', - variableName); - } else if (grandParent is TopLevelVariableDeclaration) { - return "the variable '$variableName'"; - } else { - return null; - } - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart b/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart deleted file mode 100644 index 3d8a20a11a36..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/instrumentation_information.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; - -/// The instrumentation information gathered from the migration engine. -class InstrumentationInformation { - /// The node used for type sources that are always `null`. - NullabilityNodeInfo? always; - - /// A map from the graph edges between nullability nodes, to information about - /// the edge that was created and why it was created. - final Map edgeOrigin = {}; - - /// The node used for type sources that are never `null`. - NullabilityNodeInfo? never; - - /// The instrumentation information that is specific to a single source. - final Map sourceInformation = {}; - - /// Initialize a newly created holder of instrumentation information. - InstrumentationInformation(); - - /// Return the type annotation associated with the [node] or `null` if the - /// node represents an implicit type. - TypeAnnotation? typeAnnotationForNode(NullabilityNodeInfo node) { - for (var sourceEntry in sourceInformation.entries) { - for (var typeEntry in sourceEntry.value.explicitTypeNullability.entries) { - if (typeEntry.value == node) { - return typeEntry.key; - } - } - } - return null; - } -} - -/// The instrumentation information about a [NullabilityNodeInfo]. -class NodeInformation { - final String filePath; - - final AstNode astNode; - - final Element element; - - NodeInformation(this.filePath, this.astNode, this.element); - - /// Return detail text for a fix built from an edge with this node as a - /// destination. - String get descriptionForDestination { - // TODO(paulberry): describe AST nodes. - return "A nullable value can't be used here"; - } -} - -/// The instrumentation information gathered from the migration engine that is -/// specific to a single source. -class SourceInformation { - /// A map from the type annotations found in the source code, to the - /// nullability nodes that are associated with that type. - /// - /// TODO(paulberry): we should probably get rid of this data structure. - final Map explicitTypeNullability = {}; - - /// A map from offsets within the source file to a list of changes to be - /// applied at that offset. - Map>? changes; - - /// Initialize a newly created holder of instrumentation information that is - /// specific to a single source. - SourceInformation(); -} diff --git a/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart b/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart deleted file mode 100644 index fe4ae197cc42..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/instrumentation_listener.dart +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/front_end/instrumentation_information.dart'; -import 'package:nnbd_migration/src/front_end/migration_summary.dart'; - -/// A listener used to gather instrumentation information from the migration -/// engine. -class InstrumentationListener implements NullabilityMigrationInstrumentation { - final MigrationSummary? migrationSummary; - - /// The instrumentation information being gathered. - InstrumentationInformation data = InstrumentationInformation(); - - /// Initialize a newly created listener. - InstrumentationListener({this.migrationSummary}); - - @override - void changes(Source source, Map> changes) { - assert(_sourceInfo(source).changes == null); - _sourceInfo(source).changes = changes; - migrationSummary?.recordChanges(source, changes); - } - - @override - void explicitTypeNullability(Source? source, TypeAnnotation typeAnnotation, - NullabilityNodeInfo? node) { - _sourceInfo(source).explicitTypeNullability[typeAnnotation] = node; - } - - @override - void externalDecoratedType( - Element element, DecoratedTypeInfo decoratedType) {} - - @override - void externalDecoratedTypeParameterBound( - TypeParameterElement typeParameter, DecoratedTypeInfo decoratedType) {} - - @override - void finished() { - migrationSummary?.write(); - } - - @override - void graphEdge(EdgeInfo edge, EdgeOriginInfo originInfo) { - data.edgeOrigin[edge] = originInfo; - } - - @override - void immutableNodes(NullabilityNodeInfo never, NullabilityNodeInfo always) { - data.never = never; - data.always = always; - } - - @override - void implicitReturnType( - Source? source, AstNode node, DecoratedTypeInfo? decoratedReturnType) {} - - @override - void implicitType( - Source? source, AstNode? node, DecoratedTypeInfo decoratedType) {} - - @override - void implicitTypeArguments( - Source? source, AstNode node, Iterable types) {} - - @override - void prepareForUpdate() { - for (var source in data.sourceInformation.keys) { - _sourceInfo(source).changes = null; - } - } - - /// Return the source information associated with the given [source], creating - /// it if there has been no previous information for that source. - SourceInformation _sourceInfo(Source? source) => - data.sourceInformation.putIfAbsent(source, () => SourceInformation()); -} diff --git a/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart deleted file mode 100644 index e7eaf6f84dd0..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/instrumentation_renderer.dart +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:io'; - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:path/path.dart' as path; - -String get _dartSdkVersion { - var version = Platform.version; - - // Remove the build date and OS. - if (version.contains(' ')) { - version = version.substring(0, version.indexOf(' ')); - } - - // Convert a git hash to 8 chars. - // '2.8.0-edge.fd992e423ef69ece9f44bd3ac58fa2355b563212' - var versionRegExp = RegExp(r'^.*\.([0-9a-f]+)$'); - var match = versionRegExp.firstMatch(version); - if (match != null && match.group(1)!.length == 40) { - var commit = match.group(1)!; - version = version.replaceAll(commit, commit.substring(0, 10)); - } - - return version; -} - -String substituteVariables(String content, Map variables) { - for (var variable in variables.keys) { - var value = variables[variable]!; - content = content.replaceAll('{{ $variable }}', value); - } - - return content; -} - -/// Instrumentation display output for a library that was migrated to use -/// non-nullable types. -class InstrumentationRenderer { - /// Information for a whole migration, so that libraries can reference each - /// other. - final MigrationInfo? migrationInfo; - - /// Whether the migration has been applied already or not. - final bool hasBeenApplied; - - /// Whether the migration needs to be rerun due to disk changes. - final bool needsRerun; - - /// An object used to map the file paths of analyzed files to the file paths - /// of the HTML files used to view the content of those files. - final PathMapper? pathMapper; - - /// Creates an output object for the given library info. - InstrumentationRenderer(this.migrationInfo, this.pathMapper, - this.hasBeenApplied, this.needsRerun); - - /// Returns the path context used to manipulate paths. - path.Context get pathContext => migrationInfo!.pathContext; - - /// Builds an HTML view of the instrumentation information. - String render() { - var variables = { - 'root': migrationInfo!.includedRoot, - 'dartPageScript': resources.migration_js, - 'dartPageStyle': resources.migration_css, - 'highlightJsPath': migrationInfo!.highlightJsPath, - 'highlightStylePath': migrationInfo!.highlightStylePath, - 'dartLogoPath': migrationInfo!.dartLogoPath, - 'sdkVersion': _dartSdkVersion, - 'migrationAppliedStyle': hasBeenApplied ? 'applied' : 'proposed', - 'needsRerunStyle': needsRerun ? 'needs-rerun' : '', - }; - - return substituteVariables(resources.index_html, variables); - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart b/pkg/nnbd_migration/lib/src/front_end/migration_info.dart deleted file mode 100644 index 1d10b6ae3bd0..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/migration_info.dart +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:collection/collection.dart'; -import 'package:crypto/crypto.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/offset_mapper.dart'; -import 'package:nnbd_migration/src/front_end/unit_link.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; -import 'package:path/path.dart' as path; - -/// A description of an edit that can be applied before rerunning the migration -/// in order to improve the migration results. -class EditDetail { - /// A description of the edit that will be performed. - final String description; - - /// The offset of the range to be replaced. - final int offset; - - /// The length of the range to be replaced. - final int length; - - /// The string with which the range will be replaced. - final String replacement; - - /// Initialize a newly created detail. - EditDetail(this.description, this.offset, this.length, this.replacement); - - /// Initializes a detail based on a [SourceEdit] object. - factory EditDetail.fromSourceEdit( - String description, SourceEdit sourceEdit) => - EditDetail(description, sourceEdit.offset, sourceEdit.length, - sourceEdit.replacement); -} - -/// A class storing rendering information for an entire migration report. -/// -/// This generally provides one [InstrumentationRenderer] (for one library) -/// with information about the rest of the libraries represented in the -/// instrumentation output. -class MigrationInfo { - /// The information about the compilation units that are migrated. - final Set? units; - - /// A map from file paths to the unit infos created for those files. The units - /// in this map is a strict superset of the [units] that were migrated. - final Map unitMap; - - /// The resource provider's path context. - final path.Context pathContext; - - /// The filesystem root used to create relative paths for each unit. - final String? includedRoot; - - MigrationInfo(this.units, this.unitMap, this.pathContext, this.includedRoot); - - /// The path of the Dart logo displayed in the toolbar. - String get dartLogoPath => PreviewSite.dartLogoPath; - - /// The path to the highlight.pack.js script, relative to [unitInfo]. - String get highlightJsPath => PreviewSite.highlightJsPath; - - /// The path to the highlight.pack.js stylesheet, relative to [unitInfo]. - String get highlightStylePath => PreviewSite.highlightCssPath; - - /// The path of the Material icons font. - String get materialIconsPath => PreviewSite.materialIconsPath; - - /// The path of the Roboto font. - String get robotoFont => PreviewSite.robotoFontPath; - - /// The path of the Roboto Mono font. - String get robotoMonoFont => PreviewSite.robotoMonoFontPath; - - /// Returns the absolute path of [path], as relative to [includedRoot]. - String absolutePathFromRoot(String? path) => - pathContext.join(includedRoot!, path); - - /// Return the path to [unit] from [includedRoot], to be used as a display - /// name for a library. - String computeName(UnitInfo unit) => relativePathFromRoot(unit.path!); - - /// Returns the relative path of [path] from [includedRoot]. - String relativePathFromRoot(String path) => - pathContext.relative(path, from: includedRoot); - - List unitLinks() { - var links = []; - for (var unit in units!) { - var count = unit.fixRegions.length; - links.add(UnitLink( - unit.path, - pathContext.split(computeName(unit)), - count, - unit.wasExplicitlyOptedOut, - unit.migrationStatus, - unit.migrationStatusCanBeChanged)); - } - return links; - } -} - -/// A location from or to which a user might want to navigate. -abstract class NavigationRegion { - /// The offset of the region. - final int offset; - - /// The line number of the region. - final int? line; - - /// The length of the region. - final int length; - - /// Initialize a newly created link. - NavigationRegion(int offset, this.line, this.length) - : assert(offset >= 0), - offset = offset < 0 ? 0 : offset; -} - -/// A location from which a user might want to navigate. -class NavigationSource extends NavigationRegion { - /// The target to which the user should be navigated. - final NavigationTarget target; - - /// Initialize a newly created link. - NavigationSource(super.offset, super.line, super.length, this.target); -} - -/// A location to which a user might want to navigate. -class NavigationTarget extends NavigationRegion { - /// The file containing the anchor. - final String filePath; - - /// Initialize a newly created anchor. - NavigationTarget(this.filePath, int offset, int? line, int length) - : super(offset, line, length); - - @override - int get hashCode => Object.hash(filePath, offset, length); - - @override - bool operator ==(Object other) { - return other is NavigationTarget && - other.filePath == filePath && - other.offset == offset && - other.length == length; - } - - @override - String toString() => 'NavigationTarget["$filePath", $line, $offset, $length]'; -} - -/// A description of an explanation associated with a region of code that was -/// modified. -class RegionInfo { - /// Type type of region. - final RegionType regionType; - - /// The offset to the beginning of the region. - final int offset; - - /// The length of the region. - final int length; - - /// The line number of the beginning of the region. - final int lineNumber; - - /// The explanation to be displayed for the region. - /// - /// `null` if this region doesn't represent a fix (e.g. it's just whitespace - /// change to preserve formatting). - final String? explanation; - - /// The kind of fix that was applied. - /// - /// `null` if this region doesn't represent a fix (e.g. it's just whitespace - /// change to preserve formatting). - final NullabilityFixKind? kind; - - /// Indicates whether this region should be counted in the edit summary. - final bool isCounted; - - /// A list of the edits that are related to this range. - List edits; - - /// A list of the nullability propagation traces that are related to this - /// range. - List traces; - - /// Initialize a newly created region. - RegionInfo(this.regionType, this.offset, this.length, this.lineNumber, - this.explanation, this.kind, this.isCounted, - {this.edits = const [], this.traces = const []}); -} - -/// Different types of regions that are called out. -enum RegionType { - /// This is a region of code that was added in migration. - add, - - /// This is a region of code that was removed in migration. - remove, - - /// This is a region of code that wasn't changed by migration, but is being - /// shown to give the user more information about the migration. - informative, -} - -/// Information about a single entry in a nullability trace. -class TraceEntryInfo { - /// Text description of the entry. - final String description; - - /// Name of the enclosing function, or `null` if not known. - String? function; - - /// Source code location associated with the entry, or `null` if no source - /// code location is known. - final NavigationTarget? target; - - /// The hint actions available on this trace entry, or `[]` if none. - final List hintActions; - - TraceEntryInfo(this.description, this.function, this.target, - {this.hintActions = const []}); -} - -/// Information about a nullability trace. -class TraceInfo { - /// Text description of the trace. - final String description; - - /// List of trace entries. - final List entries; - - TraceInfo(this.description, this.entries); -} - -/// The migration information associated with a single compilation unit. -class UnitInfo { - /// The absolute and normalized path of the unit. - final String? path; - - /// Hash of the original contents of the unit. - List? _diskContentHash; - - /// The preview content of unit. - String? content; - - /// The information about the regions that have an explanation associated with - /// them. The offsets in these regions are offsets into the post-edit content. - final List regions = []; - - /// The navigation sources that are located in this file. The offsets in these - /// sources are offsets into the pre-edit content. - List? sources; - - /// The navigation targets that are located in this file. The offsets in these - /// targets are offsets into the pre-edit content. - final Set targets = {}; - - /// An offset mapper reflecting changes made by the migration edits. - OffsetMapper migrationOffsetMapper = OffsetMapper.identity; - - /// An offset mapper reflecting changes made to disk since the migration was - /// run, which can be rebased on [migrationOffsetMapper] to create and - /// maintain an offset mapper from current disk state to migration result. - OffsetMapper diskChangesOffsetMapper = OffsetMapper.identity; - - /// Whether this compilation unit was explicitly opted out of null safety at - /// the start of this migration. - late bool wasExplicitlyOptedOut; - - late bool migrationStatusCanBeChanged; - - /// Indicates the migration status of this unit. - /// - /// After all migration phases have completed, this indicates that a file was - /// already migrated, or is being migrated during this migration. - /// - /// A user can change this migration status from the preview interface: - /// * An already migrated unit cannot be changed. - /// * During an initial migration, in which a package is migrated to null - /// safety, the user can toggle a file's migration status between - /// "migrating" and "opting out." - /// * During a follow-up migration, in which a package has been migrated to - /// null safety, but some files have been opted out, the user can toggle a - /// file's migration status between "migrating" and "keeping opted out." - UnitMigrationStatus? migrationStatus; - - /// Initialize a newly created unit. - UnitInfo(this.path); - - /// Set the original/disk content of this file to later use [hadDiskContent]. - /// This does not have a getter because it is backed by a private hash. - set diskContent(String? originalContent) { - _diskContentHash = md5.convert((originalContent ?? '').codeUnits).bytes; - } - - /// Returns the [regions] that represent a fixed (changed) region of code. - List get fixRegions => regions - .where((region) => - region.regionType != RegionType.informative && region.kind != null) - .toList(); - - /// Returns the [regions] that are informative. - List get informativeRegions => regions - .where((region) => - region.regionType == RegionType.informative && region.kind != null) - .toList(); - - /// The object used to map the pre-edit offsets in the navigation targets to - /// the post-edit offsets in the [content]. - OffsetMapper get offsetMapper => - OffsetMapper.rebase(diskChangesOffsetMapper, migrationOffsetMapper); - - /// Check if this unit's file had expected disk contents [checkContent]. - bool hadDiskContent(String? checkContent) { - assert(_diskContentHash != null); - return const ListEquality().equals( - _diskContentHash, md5.convert((checkContent ?? '').codeUnits).bytes); - } - - void handleSourceEdit(SourceEdit sourceEdit) { - final contentCopy = content; - final regionsCopy = List.from(regions); - final insertLength = sourceEdit.replacement.length; - final deleteLength = sourceEdit.length; - final migratedOffset = offsetMapper.map(sourceEdit.offset); - final diskOffset = diskChangesOffsetMapper.map(sourceEdit.offset); - if (migratedOffset == null || diskOffset == null) { - throw StateError('cannot apply replacement, offset has been deleted.'); - } - try { - content = content!.replaceRange(migratedOffset, - migratedOffset + deleteLength, sourceEdit.replacement); - regions.clear(); - regions.addAll(regionsCopy - .where((region) => region.offset + region.length <= migratedOffset)); - regions.addAll(regionsCopy - .where((region) => region.offset >= migratedOffset + deleteLength) - .map((region) => RegionInfo( - region.regionType, - // TODO: perhaps this should be handled by offset mapper instead, - // since offset mapper handles navigation, edits, and traces, and - // this is the odd ball out. - region.offset + insertLength - deleteLength, - region.length, - region.lineNumber, - region.explanation, - region.kind, - region.isCounted, - edits: region.edits, - traces: region.traces))); - - diskChangesOffsetMapper = OffsetMapper.sequence( - diskChangesOffsetMapper, - OffsetMapper.forReplacement( - diskOffset, deleteLength, sourceEdit.replacement)); - } catch (e) { - regions.clear(); - regions.addAll(regionsCopy); - content = contentCopy; - rethrow; - } - } - - /// Returns the [RegionInfo] at offset [offset]. - // TODO(srawlins): This is O(n), used each time the user clicks on a region. - // Consider changing the type of [regions] to facilitate O(1) searching. - RegionInfo regionAt(int offset) => regions - .firstWhere((region) => region.kind != null && region.offset == offset); -} diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart deleted file mode 100644 index 033550b88c21..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:cli_util/cli_logging.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/migration_cli.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/info_builder.dart'; -import 'package:nnbd_migration/src/front_end/instrumentation_listener.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:pub_semver/src/version.dart'; - -/// The state of an NNBD migration. -class MigrationState { - bool _hasBeenApplied = false; - - /// The migration associated with the state. - final NullabilityMigration? migration; - - /// The root directory that contains all of the files that were migrated. - final String? includedRoot; - - /// The mapper user to give nodes ids. - final NodeMapper nodeMapper = SimpleNodeMapper(); - - /// The listener used to collect fixes. - final DartFixListener? listener; - - /// The listener that collected information during the migration. - final InstrumentationListener? instrumentationListener; - - /// The information that was built from the rest of the migration state. - MigrationInfo? migrationInfo; - - /// The object used to map paths. - PathMapper? pathMapper; - - /// If there have been changes to disk so the migration needs to be rerun. - bool needsRerun = false; - - final AnalysisResult? analysisResult; - - late List? previewUrls; - - /// Map of additional package dependencies that will be required by the - /// migrated code. Keys are package names; values indicate the minimum - /// required version of each package. - final Map neededPackages; - - /// A function which returns whether a file at a given path should be - /// migrated. - final bool Function(String?) shouldBeMigratedFunction; - - /// Initialize a newly created migration state with the given values. - MigrationState( - this.migration, - this.includedRoot, - this.listener, - this.instrumentationListener, - this.neededPackages, - this.shouldBeMigratedFunction, - [this.analysisResult]); - - /// If the migration has been applied to disk. - bool get hasBeenApplied => _hasBeenApplied; - - bool get hasErrors => analysisResult?.hasErrors ?? false; - - /// Mark that the migration has been applied to disk. - void markApplied() { - assert(!hasBeenApplied); - _hasBeenApplied = true; - } - - /// Refresh the state of the migration after the migration has been updated. - Future refresh(Logger logger, Iterable? pathsToProcess) async { - assert(!hasBeenApplied); - var provider = listener!.server!.resourceProvider; - var infoBuilder = InfoBuilder( - provider, - includedRoot, - instrumentationListener!.data, - listener, - migration, - nodeMapper, - logger, - shouldBeMigratedFunction, - pathsToProcess); - var unitInfos = await infoBuilder.explainMigration(); - var pathContext = provider.pathContext; - migrationInfo = MigrationInfo( - unitInfos, infoBuilder.unitMap, pathContext, includedRoot); - pathMapper = PathMapper(provider); - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart deleted file mode 100644 index 2cfcecd779c9..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:convert'; - -import 'package:analyzer/file_system/file_system.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; - -/// Class with the capability of writing out a machine-readable summary of -/// migration results. -class MigrationSummary { - /// Path to which the summary should be written. - final String? summaryPath; - - final ResourceProvider resourceProvider; - - /// Path to the package being migrated. Entries in the summary will refer to - /// files relative to this root. - final String rootPath; - - /// Map from relative file path to a map from fix name to count. - final Map> _changesByRelativePath = {}; - - MigrationSummary(this.summaryPath, this.resourceProvider, this.rootPath); - - /// Records information about the [changes] made to a [source] file. - void recordChanges(Source source, Map> changes) { - var changeSummary = {}; - var hintsSeen = {}; - for (var entry in changes.entries) { - for (var edit in entry.value) { - var info = edit.info; - if (info != null) { - var hint = info.hintComment; - if (hint == null || hintsSeen.add(hint)) { - var description = info.description; - var key = _keyForKind(description.kind); - changeSummary[key] = (changeSummary[key] ?? 0) + 1; - } - } - } - } - _changesByRelativePath[resourceProvider.pathContext - .relative(source.fullName, from: rootPath)] = changeSummary; - } - - /// Writes out the summary data accumulated so far - void write() { - resourceProvider.getFile(summaryPath!).writeAsStringSync(jsonEncode({ - 'changes': {'byPath': _changesByRelativePath} - })); - } - - String _keyForKind(NullabilityFixKind kind) { - switch (kind) { - case NullabilityFixKind.addThen: - return 'addThen'; - case NullabilityFixKind.addImport: - return 'addImport'; - case NullabilityFixKind.addLate: - return 'addLate'; - case NullabilityFixKind.addLateDueToHint: - return 'addLateDueToHint'; - case NullabilityFixKind.addLateDueToTestSetup: - return 'addLateDueToTestSetup'; - case NullabilityFixKind.addLateFinalDueToHint: - return 'addLateFinalDueToHint'; - case NullabilityFixKind.addRequired: - return 'addRequired'; - case NullabilityFixKind.addType: - return 'addType'; - case NullabilityFixKind.changeMethodName: - return 'changeMethodName'; - case NullabilityFixKind.checkExpression: - return 'checkExpression'; - case NullabilityFixKind.checkExpressionDueToHint: - return 'checkExpressionDueToHint'; - case NullabilityFixKind.compoundAssignmentHasNullableSource: - return 'compoundAssignmentHasNullableSource'; - case NullabilityFixKind.compoundAssignmentHasBadCombinedType: - return 'compoundAssignmentHasBadCombinedType'; - case NullabilityFixKind.conditionFalseInStrongMode: - return 'conditionFalseInStrongMode'; - case NullabilityFixKind.conditionTrueInStrongMode: - return 'conditionTrueInStrongMode'; - case NullabilityFixKind.downcastExpression: - return 'downcastExpression'; - case NullabilityFixKind.makeTypeNullable: - return 'makeTypeNullable'; - case NullabilityFixKind.makeTypeNullableDueToHint: - return 'makeTypeNullableDueToHint'; - case NullabilityFixKind.noValidMigrationForNull: - return 'noValidMigrationForNull'; - case NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode: - return 'nullAwarenessUnnecessaryInStrongMode'; - case NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode: - return 'nullAwareAssignmentUnnecessaryInStrongMode'; - case NullabilityFixKind.otherCastExpression: - return 'otherCastExpression'; - case NullabilityFixKind.removeAs: - return 'removeAs'; - case NullabilityFixKind.removeDeadCode: - return 'removeDeadCode'; - case NullabilityFixKind.removeLanguageVersionComment: - return 'removeLanguageVersionComment'; - case NullabilityFixKind.replaceVar: - return 'replaceVar'; - case NullabilityFixKind.typeNotMadeNullable: - return 'typeNotMadeNullable'; - case NullabilityFixKind.typeNotMadeNullableDueToHint: - return 'typeNotMadeNullableDueToHint'; - case NullabilityFixKind.removeNullableAnnotation: - return 'removeNullableAnnotation'; - } - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart deleted file mode 100644 index a733aed7b70e..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/navigation_tree_renderer.dart +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/unit_link.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:path/path.dart' as path; - -/// Groups the items in [iterable] by the result of applying [groupFn] to each -/// item. -Map> _groupBy( - Iterable iterable, K Function(T item) groupFn) { - var result = >{}; - for (var item in iterable) { - var key = groupFn(item); - result.putIfAbsent(key, () => []).add(item); - } - return result; -} - -/// The HTML that is displayed for a region of code. -class NavigationTreeRenderer { - final MigrationInfo? migrationInfo; - - /// An object used to map the file paths of analyzed files to the file paths - /// of the HTML files used to view the content of those files. - final PathMapper? pathMapper; - - /// Initializes a newly created region page within the given [site]. The - /// [unitInfo] provides the information needed to render the page. - NavigationTreeRenderer(this.migrationInfo, this.pathMapper); - - /// Returns the path context used to manipulate paths. - path.Context get pathContext => migrationInfo!.pathContext; - - /// Renders the navigation link tree. - List render() { - var linkData = migrationInfo!.unitLinks(); - var tree = _renderNavigationSubtree(linkData, 0); - for (var node in tree) { - node.parent = null; - } - return tree; - } - - /// Renders the navigation link subtree at [depth]. - List _renderNavigationSubtree( - List links, int depth) { - var linksGroupedByDirectory = _groupBy( - links.where((link) => link.depth > depth), - (UnitLink link) => link.pathParts[depth]); - - return [ - for (var entry in linksGroupedByDirectory.entries) - NavigationTreeDirectoryNode( - name: entry.key, - path: pathContext - .dirname(pathContext.joinAll(entry.value.first.pathParts)), - subtree: _renderNavigationSubtree(entry.value, depth + 1), - )..setSubtreeParents(), - for (var link in links.where((link) => link.depth == depth)) - NavigationTreeFileNode( - name: link.fileName, - path: pathContext.joinAll(link.pathParts), - href: pathMapper!.map(link.fullPath!), - editCount: link.editCount, - wasExplicitlyOptedOut: link.wasExplicitlyOptedOut, - migrationStatus: link.migrationStatus, - migrationStatusCanBeChanged: link.migrationStatusCanBeChanged, - ), - ]; - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart deleted file mode 100644 index 92e2c782adc8..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:async'; -import 'dart:convert' show jsonDecode, JsonEncoder; - -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/file_system/file_system.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:cli_util/cli_logging.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/charcodes.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/instrumentation_listener.dart'; -import 'package:nnbd_migration/src/front_end/migration_state.dart'; -import 'package:nnbd_migration/src/front_end/migration_summary.dart'; -import 'package:nnbd_migration/src/preview/http_preview_server.dart'; -import 'package:nnbd_migration/src/utilities/json.dart' as json; -import 'package:pub_semver/pub_semver.dart'; -import 'package:source_span/source_span.dart'; -import 'package:yaml/yaml.dart'; - -/// [NonNullableFix] visits each named type in a resolved compilation unit -/// and determines whether the associated variable or parameter can be null -/// then adds or removes a '?' trailing the named type as appropriate. -class NonNullableFix { - static final List _allServers = []; - - final Version _intendedMinimumSdkVersion; - - /// The internet address the server should bind to. Should be suitable for - /// passing to HttpServer.bind, i.e. either a [String] or an - /// [InternetAddress]. - final Object? bindAddress; - - final Logger _logger; - - final int? preferredPort; - - final DartFixListener listener; - - /// The root of the included paths. - /// - /// The included paths may contain absolute and relative paths, non-canonical - /// paths, and directory and file paths. The "root" is the deepest directory - /// which all included paths share. - final String includedRoot; - - /// If non-null, the path to which a machine-readable summary of migration - /// results should be written. - final String? summaryPath; - - final ResourceProvider resourceProvider; - - /// The HTTP server that serves the preview tool. - HttpPreviewServer? _server; - - String? authToken; - - InstrumentationListener? instrumentationListener; - - NullabilityMigrationAdapter? adapter; - - NullabilityMigration? migration; - - late Future Function() rerunFunction; - - /// A list of the URLs corresponding to the included roots. - List? previewUrls; - - /// A function which returns whether a file at a given path should be - /// migrated. - final bool Function(String?) shouldBeMigratedFunction; - - /// The set of files which are being considered for migration. - Iterable? pathsToProcess; - - /// Completes when the server has been shutdown. - late Completer serverIsShutdown; - - NonNullableFix(this.listener, this.resourceProvider, this.bindAddress, - this._logger, this.shouldBeMigratedFunction, - {List included = const [], - this.preferredPort, - this.summaryPath, - required String sdkPath}) - : includedRoot = - _getIncludedRoot(included, listener.server!.resourceProvider), - _intendedMinimumSdkVersion = - _computeIntendedMinimumSdkVersion(resourceProvider, sdkPath) { - reset(); - } - - bool get isPreviewServerRunning => _server != null; - - /// In the package_config.json file, the patch number is omitted. - String get _intendedLanguageVersion => - '${_intendedMinimumSdkVersion.major}.${_intendedMinimumSdkVersion.minor}'; - - String get _intendedSdkVersionConstraint => - '>=$_intendedMinimumSdkVersion <3.0.0'; - - InstrumentationListener createInstrumentationListener( - {MigrationSummary? migrationSummary}) => - InstrumentationListener(migrationSummary: migrationSummary); - - Future finalizeUnit(ResolvedUnitResult result) async { - migration!.finalizeInput(result); - } - - Future finish() async { - var neededPackages = migration!.finish(); - final state = MigrationState(migration, includedRoot, listener, - instrumentationListener, neededPackages, shouldBeMigratedFunction); - await state.refresh(_logger, pathsToProcess); - return state; - } - - Future prepareUnit(ResolvedUnitResult result) async { - migration!.prepareInput(result); - } - - /// Processes the non-source files of the package rooted at [pkgFolder]. - /// - /// This means updating the pubspec.yaml file, the package_config.json - /// file, and the analysis_options.yaml file, each only if necessary. - /// - /// [neededPackages] is a map whose keys are the names of packages that should - /// be depended upon by the package's pubspec, and whose values are the - /// minimum required versions of those packages. - void processPackage(Folder pkgFolder, Map neededPackages) { - var pubspecFile = pkgFolder.getChildAssumingFile('pubspec.yaml'); - if (!pubspecFile.exists) { - // If the pubspec file cannot be found, we do not attempt to change the - // Package Config file, nor the analysis options file. - return; - } - - _YamlFile pubspec; - try { - pubspec = _YamlFile._parseFrom(pubspecFile); - } on FileSystemException catch (e) { - _processPubspecException('read', pubspecFile.path, e); - return; - } on FormatException catch (e) { - _processPubspecException('parse', pubspecFile.path, e); - return; - } - - var updated = _processPubspec(pubspec, neededPackages); - if (updated) { - _processConfigFile(pkgFolder, pubspec); - } - } - - Future processUnit(ResolvedUnitResult result) async { - migration!.processInput(result); - } - - Future rerun() async { - reset(); - var state = await rerunFunction(); - return state; - } - - void reset() { - instrumentationListener = createInstrumentationListener( - migrationSummary: summaryPath == null - ? null - : MigrationSummary(summaryPath, resourceProvider, includedRoot)); - adapter = NullabilityMigrationAdapter(listener); - migration = NullabilityMigration(adapter, - permissive: true, instrumentation: instrumentationListener); - } - - void shutdownServer() { - if (_server != null) { - _server!.close(); - _server = null; - serverIsShutdown.complete(); - } - } - - Future startPreviewServer( - MigrationState state, void Function() applyHook) async { - // This method may be called multiple times, for example during a re-run. - // But the preview server should only be started once. - if (_server == null) { - void wrappedApplyHookWithShutdown() { - shutdownServer(); - applyHook(); - } - - _server = HttpPreviewServer(state, rerun, wrappedApplyHookWithShutdown, - bindAddress, preferredPort, _logger); - _server!.serveHttp(); - _allServers.add(_server); - var serverHostname = await _server!.boundHostname; - var serverPort = await _server!.boundPort; - authToken = await _server!.authToken; - serverIsShutdown = Completer(); - - previewUrls = [ - // TODO(jcollins-g): Change protocol to only return a single string. - Uri( - scheme: 'http', - host: serverHostname, - port: serverPort, - path: state.pathMapper!.map(includedRoot), - queryParameters: {'authToken': authToken}).toString() - ]; - } - } - - /// Updates the Package Config file to specify a minimum Dart SDK version - /// which supports null safety. - void _processConfigFile(Folder pkgFolder, _YamlFile pubspec) { - var packageName = pubspec._getName(); - if (packageName == null) { - return; - } - - var packageConfigFile = pkgFolder - .getChildAssumingFolder('.dart_tool') - .getChildAssumingFile('package_config.json'); - - if (!packageConfigFile.exists) { - _processPackageConfigException( - 'Warning: Could not find the package configuration file.', - packageConfigFile.path); - return; - } - try { - var configText = packageConfigFile.readAsStringSync(); - var configMap = json.expectType(jsonDecode(configText), 'root'); - json.expectKey(configMap, 'configVersion'); - var configVersion = - json.expectType(configMap['configVersion'], 'configVersion'); - if (configVersion != 2) { - _processPackageConfigException( - 'Warning: Unexpected package configuration file version ' - '$configVersion (expected version 2). Cannot update this file.', - packageConfigFile.path); - return; - } - json.expectKey(configMap, 'packages'); - var packagesList = - json.expectType(configMap['packages'], 'packages'); - for (var package in packagesList) { - var packageMap = json.expectType(package, 'package'); - json.expectKey(packageMap, 'name'); - var name = json.expectType(packageMap['name'], 'name'); - if (name != packageName) { - continue; - } - json.expectKey(packageMap, 'languageVersion'); - packageMap['languageVersion'] = _intendedLanguageVersion; - // Pub appears to always use a two-space indent. This will minimize the - // diff between the previous text and the new text. - var newText = '${JsonEncoder.withIndent(' ').convert(configMap)}\n'; - - // TODO(srawlins): This is inelegant. We add an "edit" which replaces - // the entire content of the package config file with new content, while - // it is likely that only 1 character has changed. I do not know of a - // JSON parser that yields SourceSpans, so that I may know the proper - // index. One idea, another hack, would be to write a magic string in - // place of the version number, encode to JSON, and find the index of - // the magic string. - var line = 0; - var offset = 0; - var edit = SourceEdit(offset, configText.length, newText); - listener.addSourceFileEdit( - 'enable Null Safety language feature', - Location(packageConfigFile.path, offset, newText.length, line, 0, - endLine: 0, endColumn: 0), - SourceFileEdit(packageConfigFile.path, 0, edits: [edit])); - } - } on FormatException catch (e) { - _processPackageConfigException( - 'Warning: Encountered an error parsing the package configuration ' - 'file: $e\n\nCannot update this file.', - packageConfigFile.path); - } - } - - void _processPackageConfigException(String prefix, String packageConfigPath, - [Object error = '']) { - // TODO(#42138): This should use [listener.addRecommendation] when that - // function is implemented. - print('''$prefix - $packageConfigPath - $error - - Be sure to run `pub get` before examining the results of the migration. -'''); - } - - /// Updates the pubspec.yaml file to specify a minimum Dart SDK version which - /// supports null safety. - /// - /// Return value indicates whether the user's `package_config.json` file - /// should be updated. - bool _processPubspec(_YamlFile pubspec, Map neededPackages) { - bool packageConfigNeedsUpdate = false; - bool packageDepsUpdated = false; - var pubspecMap = pubspec.content; - YamlNode? environmentOptions; - if (pubspecMap is YamlMap) { - environmentOptions = pubspecMap.nodes['environment']; - } - if (environmentOptions == null) { - var start = SourceLocation(0, line: 0, column: 0); - var content = ''' -environment: - sdk: '$_intendedSdkVersionConstraint' -'''; - pubspec._insertAfterParent( - SourceSpan(start, start, ''), content, listener); - packageConfigNeedsUpdate = true; - } else if (environmentOptions is YamlMap) { - if (_updatePubspecConstraint(pubspec, environmentOptions, 'sdk', - "'$_intendedSdkVersionConstraint'", _intendedMinimumSdkVersion)) { - packageConfigNeedsUpdate = true; - } - } else { - // Odd malformed pubspec. Leave it alone, but go ahead and update the - // package_config.json file. - packageConfigNeedsUpdate = true; - } - if (neededPackages.isNotEmpty) { - YamlNode? dependencies; - if (pubspecMap is YamlMap) { - dependencies = pubspecMap.nodes['dependencies']; - } - if (dependencies == null) { - var depLines = [ - for (var entry in neededPackages.entries) - ' ${entry.key}: ^${entry.value}' - ]; - var start = SourceLocation(0, line: 0, column: 0); - var content = ''' -dependencies: -${depLines.join('\n')} -'''; - pubspec._insertAfterParent( - SourceSpan(start, start, ''), content, listener); - packageDepsUpdated = true; - } else if (dependencies is YamlMap) { - for (var neededPackage in neededPackages.entries) { - if (_updatePubspecConstraint(pubspec, dependencies, neededPackage.key, - '^${neededPackage.value}', neededPackage.value)) { - packageDepsUpdated = true; - } - } - } - } - if (packageDepsUpdated) { - listener.reportPubGetNeeded(neededPackages); - } - - return packageConfigNeedsUpdate; - } - - void _processPubspecException(String action, String pubspecPath, error) { - listener.client.onFatalError('''Failed to $action pubspec file - $pubspecPath - $error - - Manually update this file to enable the Null Safety language feature by - adding: - - environment: - sdk: '$_intendedSdkVersionConstraint'; -'''); - throw StateError('listener.reportFatalError should never return'); - } - - /// Updates a constraint in the given [pubspec] file. If [key] is found in - /// [map], and the corresponding value does has a minimum less than - /// [minimumVersion], it is updated to [fullVersionConstraint]. If it is not - /// found, then an entry is added. - /// - /// Return value indicates whether a change was made. - bool _updatePubspecConstraint(_YamlFile pubspec, YamlMap map, String key, - String fullVersionConstraint, Version minimumVersion) { - var node = map.nodes[key]; - if (node == null) { - var content = ''' - - $key: $fullVersionConstraint'''; - pubspec._insertAfterParent(map.span, content, listener); - return true; - } else if (node is YamlScalar) { - VersionConstraint currentConstraint; - if (node.value is String) { - currentConstraint = VersionConstraint.parse(node.value as String); - var invalidVersionMessage = - 'The current SDK constraint in pubspec.yaml is invalid. A ' - 'minimum version, such as ">=2.7.0", is required when launching ' - "'dart migrate'."; - if (currentConstraint is Version) { - // In this case, the constraint is an exact version, like 2.0.0. - _logger.stderr(invalidVersionMessage); - return false; - } else if (currentConstraint is VersionRange) { - if (currentConstraint.min == null) { - _logger.stderr(invalidVersionMessage); - return false; - } else if (currentConstraint.min! >= minimumVersion) { - // The current version constraint is already up to date. Do not - // edit. - return false; - } else { - // TODO(srawlins): This overwrites the current maximum version. In - // the uncommon situation that there is a special maximum, it should - // not. - pubspec._replaceSpan(node.span, fullVersionConstraint, listener); - return true; - } - } else { - // The constraint is something different, like a union, like - // '>=1.0.0 <2.0.0 >=3.0.0 <4.0.0', which is not valid. - _logger.stderr(invalidVersionMessage); - return false; - } - } else { - // Something is odd with the constraint we've found in pubspec.yaml; - // Best to leave it alone. - return false; - } - } else { - // Something is odd with the format of pubspec.yaml; best to leave it - // alone. - return false; - } - } - - /// Allows unit tests to shut down any rogue servers that have been started, - /// so that unit testing can complete. - @visibleForTesting - static void shutdownAllServers() { - for (var server in _allServers) { - try { - server!.close(); - } catch (_) {} - } - _allServers.clear(); - } - - static Version _computeIntendedMinimumSdkVersion( - ResourceProvider resourceProvider, String sdkPath) { - var versionFile = resourceProvider - .getFile(resourceProvider.pathContext.join(sdkPath, 'version')); - if (!versionFile.exists) { - throw StateError( - 'Could not find SDK version file at ${versionFile.path}'); - } - var sdkVersionString = versionFile.readAsStringSync().trim(); - var sdkVersion = Version.parse(sdkVersionString); - // Ideally, we would like to set the user's minimum SDK constraint to the - // version in which null safety was released to stable. But we only want to - // do so if we are sure that stable release exists. An easy way to check - // that is to see if the current SDK version is greater than or equal to the - // stable release of null safety. - var nullSafetyStableReleaseVersion = Feature.non_nullable.releaseVersion!; - if (sdkVersion >= nullSafetyStableReleaseVersion) { - // It is, so we can use it as the minimum SDK constraint. - return nullSafetyStableReleaseVersion; - } else { - // It isn't. This either means that null safety hasn't been released to - // stable yet (in which case it's definitely not safe to use - // `nullSafetyStableReleaseVersion` as a minimum SDK constraint), or it - // has been released but the user hasn't upgraded to it (in which case we - // don't want to use it as a minimum SDK constraint anyway, because we - // don't want to force the user to upgrade their SDK in order to be able - // to use their own package). Our next best option is to use the user's - // current SDK version as a minimum SDK constraint, assuming it's a proper - // beta release version. - if (sdkVersionString.contains('beta')) { - // It is, so we can use it. - return sdkVersion; - } else { - // It isn't. The user is probably either on a bleeding edge version of - // the SDK (e.g. `2.12.0-edge.`), a dev version - // (e.g. `2.12.0-X.Y.dev`), or an internally built version - // (e.g. `2.12.0-`). All of these version numbers are - // unsafe for the user to use as their minimum SDK constraint, because - // if they published their package, it wouldn't be usable with the - // latest beta release. So just fall back on using a version of - // `-0`. - return Version.parse('$nullSafetyStableReleaseVersion-0'); - } - } - } - - /// Get the "root" of all [included] paths. See [includedRoot] for its - /// definition. - static String _getIncludedRoot( - List included, ResourceProvider provider) { - var context = provider.pathContext; - // This step looks like it may be expensive (`getResource`, splitting up - // all of the paths, comparing parts, joining one path back together). In - // practice, this should be cheap because typically only one path is given - // to dartfix. - var rootParts = included - .map((p) => context.normalize(context.absolute(p))) - .map((p) => provider.getResource(p) is File ? context.dirname(p) : p) - .map((p) => context.split(p)) - .reduce((value, parts) { - var shorterPath = value.length < parts.length ? value : parts; - var length = shorterPath.length; - for (var i = 0; i < length; i++) { - if (value[i] != parts[i]) { - // [value] and [parts] are the same, only up to part [i]. - return value.sublist(0, i); - } - } - // [value] and [parts] are the same up to the full length of the shorter - // of the two, so just return that. - return shorterPath; - }); - return context.joinAll(rootParts); - } -} - -class NullabilityMigrationAdapter implements NullabilityMigrationListener { - final DartFixListener listener; - - NullabilityMigrationAdapter(this.listener); - - @override - void addEdit(Source source, SourceEdit edit) { - listener.addEditWithoutSuggestion(source, edit); - } - - @override - void addSuggestion(String descriptions, Location location) { - listener.addSuggestion(descriptions, location); - } - - @override - void reportException( - Source? source, AstNode? node, Object exception, StackTrace stackTrace) { - listener.client.onException(''' -$exception at offset ${node!.offset} in $source ($node) - -$stackTrace'''); - } -} - -class _YamlFile { - final String path; - final String textContent; - - final YamlNode content; - - _YamlFile._(this.path, this.textContent, this.content); - - String? _getName() { - YamlNode? packageNameNode; - - if (content is YamlMap) { - packageNameNode = (content as YamlMap).nodes['name']; - } else { - return null; - } - - if (packageNameNode is YamlScalar && packageNameNode.value is String) { - return packageNameNode.value as String?; - } else { - return null; - } - } - - /// Inserts [content] into this file, immediately after [parentSpan]. - void _insertAfterParent( - SourceSpan parentSpan, String content, DartFixListener listener) { - var line = parentSpan.end.line; - var offset = parentSpan.end.offset; - // Walk [offset] and [line] back to the first non-whitespace character - // before [offset]. - while (offset > 0) { - var ch = textContent.codeUnitAt(offset - 1); - if (ch == $space || ch == $cr) { - --offset; - } else if (ch == $lf) { - --offset; - --line; - } else { - break; - } - } - var edit = SourceEdit(offset, 0, content); - listener.addSourceFileEdit( - 'enable Null Safety language feature', - Location(path, offset, content.length, line, 0, - endLine: 0, endColumn: 0), - SourceFileEdit(path, 0, edits: [edit])); - } - - void _replaceSpan(SourceSpan span, String content, DartFixListener listener) { - var line = span.start.line; - var offset = span.start.offset; - var edit = SourceEdit(offset, span.length, content); - listener.addSourceFileEdit( - 'enable Null Safety language feature', - Location(path, offset, content.length, line, 0, - endLine: 0, endColumn: 0), - SourceFileEdit(path, 0, edits: [edit])); - } - - static _YamlFile _parseFrom(File file) { - var textContent = file.readAsStringSync(); - var content = loadYaml(textContent); - if (content is YamlNode) { - return _YamlFile._(file.path, textContent, content); - } else { - throw FormatException('pubspec.yaml is not a YAML map.'); - } - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart b/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart deleted file mode 100644 index 50ae63d10ae3..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/offset_mapper.dart +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer_plugin/protocol/protocol_common.dart'; - -/// An object that can map the offsets before a sequence of edits to the offsets -/// after applying the edits. -abstract class OffsetMapper { - /// A mapper used for files that were not modified. - static OffsetMapper identity = _IdentityMapper(); - - /// Return a mapper representing the file modified by the given [edits]. - factory OffsetMapper.forEdits(List edits) => _EditMapper(edits); - - /// Return a mapper representing the file modified by an insertion at [offset] - /// the given with [length]. - factory OffsetMapper.forInsertion(int offset, int length) => - _SimpleSourceEditMapper(offset, 0, length); - - /// Return a mapper representing the file modified by an insertion at [offset] - /// with [replacement] text overwriting the given [length]. - factory OffsetMapper.forReplacement( - int offset, int length, String replacement) => - _SimpleSourceEditMapper(offset, length, replacement.length); - - /// Return a mapper representing [rebased] rebased by [rebaser]. - /// - /// Warning: this does not currently handle cases where the [rebased] mapper - /// contains an insert or deletion in the middle of a range deleted by - /// [rebaser]. This is not a case that currently needs to be supported. - factory OffsetMapper.rebase(OffsetMapper rebaser, OffsetMapper rebased) { - return _RebasedOffsetMapper(rebaser, rebased); - } - - /// Return a mapper representing a sequence of edits made in order, with the - /// offsets coming out of [first] being the offsets passed into [second]. - factory OffsetMapper.sequence(OffsetMapper first, OffsetMapper second) { - return _OffsetMapperChain([first, second]); - } - - /// Return the post-edit offset that corresponds to the given pre-edit - /// [offset], or `null` when that offset has been deleted. - int? map(int? offset); -} - -/// A mapper used for files that were modified by a set of edits. -class _EditMapper implements OffsetMapper { - /// A list whose elements are the highest pre-edit offset for which the - /// corresponding element of [_deltas] should be applied. - final List _offsets = []; - - /// A list whose elements are the deltas to be applied for all pre-edit - /// offsets that are less than or equal to the corresponding element of - /// [_offsets]. - final List _deltas = []; - - /// Initialize a newly created mapper based on the given set of [edits]. - _EditMapper(List edits) { - _initializeDeltas(edits); - } - - @override - int map(int? offset) => offset! + _deltaFor(offset); - - /// Return the delta to be added to the pre-edit [offset] to produce the - /// post-edit offset. - int _deltaFor(int? offset) { - for (var i = 0; i < _offsets.length; i++) { - var currentOffset = _offsets[i]; - if (currentOffset >= offset! || currentOffset < 0) { - return _deltas[i]; - } - } - // We should never get here because [_initializeDeltas] always adds an - // offset/delta pair at the end of the list whose offset is less than zero. - return 0; - } - - /// Initialize the list of old offsets and deltas used by [_deltaFor]. - void _initializeDeltas(List edits) { - var previousDelta = 0; - for (var edit in edits) { - var offset = edit.offset; - var length = edit.length; - _offsets.add(offset); - _deltas.add(previousDelta); - previousDelta += edit.replacement.length - length; - } - _offsets.add(-1); - _deltas.add(previousDelta); - } -} - -/// A mapper used for files that were not modified. -class _IdentityMapper implements OffsetMapper { - @override - int? map(int? offset) => offset; -} - -class _OffsetMapperChain implements OffsetMapper { - final List innerMappers; - - _OffsetMapperChain(this.innerMappers); - - @override - int? map(int? offset) { - for (final mapper in innerMappers) { - offset = mapper.map(offset); - if (offset == null) { - break; - } - } - return offset; - } -} - -class _RebasedOffsetMapper implements OffsetMapper { - final OffsetMapper rebaser; - final OffsetMapper rebased; - - _RebasedOffsetMapper(this.rebaser, this.rebased); - - @override - int? map(int? offset) { - final rebasedOffset = rebased.map(offset); - final rebasingOffset = rebaser.map(offset); - if (rebasedOffset == null || rebasingOffset == null) { - return null; - } - final delta = rebasedOffset - offset!; - return rebasingOffset + delta; - } -} - -class _SimpleSourceEditMapper implements OffsetMapper { - /// The offset where the replacement begins. - final int offset; - - /// The length of text to be replaced. - final int replacedLength; - - /// The length of text to be inserted as a replacement. - final int replacementLength; - - _SimpleSourceEditMapper( - this.offset, this.replacedLength, this.replacementLength); - - @override - int? map(int? offset) { - if (offset! < this.offset) { - return offset; - } - if (offset < this.offset + replacedLength) { - return null; - } - return offset + replacementLength - replacedLength; - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/path_mapper.dart b/pkg/nnbd_migration/lib/src/front_end/path_mapper.dart deleted file mode 100644 index 9b001350800b..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/path_mapper.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/file_system/file_system.dart'; - -/// An object that can map the file paths of analyzed files to the file paths of -/// the HTML files used to view the content of those files. -class PathMapper { - /// The resource provider used to map paths. - ResourceProvider provider; - - /// The index to be used when creating the next synthetic file name. - int nextIndex = 1; - - /// Initialize a newly created path mapper. - PathMapper(this.provider); - - /// Gets the symbol used as a path separator on the local filesystem. - String get separator => provider.pathContext.separator; - - /// Return the path of the HTML file used to view the content of the analyzed - /// file with the given [path]. - String map(String path) { - return provider.pathContext.toUri(path).path; - } - - /// Returns the local filesystem path corresponding to the given [uri]. - String reverseMap(Uri uri) { - return provider.pathContext.fromUri(uri.replace(scheme: 'file')); - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart deleted file mode 100644 index 700a9e327ab5..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/web/edit_details.dart'; -import 'package:path/path.dart' as path; - -/// The HTML that is displayed for a region of code. -class RegionRenderer { - /// A flag indicating whether the incremental workflow is currently supported. - static const bool supportsIncrementalWorkflow = true; - - /// The region to render. - final RegionInfo region; - - /// The compilation unit information containing the region. - final UnitInfo unitInfo; - - final MigrationInfo? migrationInfo; - - /// An object used to map the file paths of analyzed files to the file paths - /// of the HTML files used to view the content of those files. - final PathMapper? pathMapper; - - /// The auth token for the current site, for use in generating URIs. - final String authToken; - - /// Initializes a newly created region page within the given [site]. The - /// [unitInfo] provides the information needed to render the page. - RegionRenderer(this.region, this.unitInfo, this.migrationInfo, - this.pathMapper, this.authToken); - - /// Returns the path context used to manipulate paths. - path.Context get pathContext => migrationInfo!.pathContext; - - EditDetails render() { - TargetLink linkForTarget(NavigationTarget target) { - var relativePath = - _relativePathToTarget(target, pathContext.dirname(unitInfo.path!)); - var targetUri = _uriForPath(target.filePath, target); - return TargetLink( - path: relativePath, - href: targetUri, - line: target.line, - ); - } - - EditLink linkForEdit(EditDetail edit) => EditLink( - description: edit.description, - href: Uri(path: pathContext.basename(unitInfo.path!), queryParameters: { - 'offset': edit.offset.toString(), - 'end': (edit.offset + edit.length).toString(), - 'replacement': edit.replacement - }).toString()); - - var response = EditDetails( - displayPath: unitInfo.path, - uriPath: pathMapper!.map(unitInfo.path!), - line: region.lineNumber, - explanation: region.explanation, - edits: supportsIncrementalWorkflow - ? [ - for (var edit in region.edits) linkForEdit(edit), - ] - : null, - traces: [ - for (var trace in region.traces) - Trace(description: trace.description, entries: [ - for (var entry in trace.entries) - TraceEntry( - description: entry.description, - function: entry.function, - link: entry.target == null - ? null - : linkForTarget(entry.target!), - hintActions: entry.hintActions) - ]) - ], - ); - return response; - } - - /// Returns the URL that will navigate to the given [target]. - String _relativePathToTarget(NavigationTarget target, String unitDir) { - return pathMapper! - .map(pathContext.relative(target.filePath, from: unitDir)); - } - - /// Return the URL that will navigate to the given [target] in the file at the - /// given [path]. - String _uriForPath(String path, NavigationTarget target) { - var queryParams = { - 'offset': target.offset, - if (target.line != null) 'line': target.line, - 'authToken': authToken, - }.entries.map((entry) => '${entry.key}=${entry.value}').join('&'); - return '${pathMapper!.map(path)}?$queryParams'; - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/.clang-format b/pkg/nnbd_migration/lib/src/front_end/resources/.clang-format deleted file mode 100644 index ace8841e1e65..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/.clang-format +++ /dev/null @@ -1,3 +0,0 @@ -# Don't format the JavaScript files in this directory. -Language: JavaScript -DisableFormat: true diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/MaterialIconsRegular.ttf b/pkg/nnbd_migration/lib/src/front_end/resources/MaterialIconsRegular.ttf deleted file mode 100644 index 7015564ad166..000000000000 Binary files a/pkg/nnbd_migration/lib/src/front_end/resources/MaterialIconsRegular.ttf and /dev/null differ diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/RobotoMonoRegular.ttf b/pkg/nnbd_migration/lib/src/front_end/resources/RobotoMonoRegular.ttf deleted file mode 100644 index 5919b5d1bf06..000000000000 Binary files a/pkg/nnbd_migration/lib/src/front_end/resources/RobotoMonoRegular.ttf and /dev/null differ diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/RobotoRegular.ttf b/pkg/nnbd_migration/lib/src/front_end/resources/RobotoRegular.ttf deleted file mode 100644 index 2b6392ffe871..000000000000 Binary files a/pkg/nnbd_migration/lib/src/front_end/resources/RobotoRegular.ttf and /dev/null differ diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/dart_192.png b/pkg/nnbd_migration/lib/src/front_end/resources/dart_192.png deleted file mode 100644 index 16ed071acfa6..000000000000 Binary files a/pkg/nnbd_migration/lib/src/front_end/resources/dart_192.png and /dev/null differ diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/highlight.css b/pkg/nnbd_migration/lib/src/front_end/resources/highlight.css deleted file mode 100644 index bc8e473b5934..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/highlight.css +++ /dev/null @@ -1,66 +0,0 @@ -/* -Date: 24 Fev 2015 -Author: Pedro Oliveira -*/ - -.hljs { - color: #a9b7c6; - background: #282b2e; - display: block; - overflow-x: auto; - padding: 0.5em; -} - -.hljs-number, -.hljs-literal, -.hljs-symbol, -.hljs-bullet { - color: #6897BB; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-deletion { - color: #cc7832; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-link { - color: #629755; -} - -.hljs-comment, -.hljs-quote { - color: #808080; -} - -.hljs-meta { - color: #bbb529; -} - -.hljs-string, -.hljs-attribute, -.hljs-addition { - color: #6A8759; -} - -.hljs-section, -.hljs-title, -.hljs-type { - color: #ffc66d; -} - -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #e8bf6a; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/highlight.md b/pkg/nnbd_migration/lib/src/front_end/resources/highlight.md deleted file mode 100644 index d23468a4418f..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/highlight.md +++ /dev/null @@ -1,6 +0,0 @@ -1. Build and download highlight.zip for Dart. [1] -2. Extract highlight.zip -3. find highlight.pack.js and copy into this directory -4. find styles/androidstudio.css and copy into the directory as highlight.css - -[1] https://highlightjs.org/download/ diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/highlight.pack.js b/pkg/nnbd_migration/lib/src/front_end/resources/highlight.pack.js deleted file mode 100644 index 102750aabbc1..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/highlight.pack.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - Highlight.js 10.1.0 (74de6eaa) - License: BSD-3-Clause - Copyright (c) 2006-2020, Ivan Sagalaev -*/ -var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.0";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("xml",function(){"use strict";return function(e){var n={className:"symbol",begin:"&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;"},a={begin:"\\s",contains:[{className:"meta-keyword",begin:"#?[a-z_][a-z1-9_-]+",illegal:"\\n"}]},s=e.inherit(a,{begin:"\\(",end:"\\)"}),t=e.inherit(e.APOS_STRING_MODE,{className:"meta-string"}),i=e.inherit(e.QUOTE_STRING_MODE,{className:"meta-string"}),c={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("dart",function(){"use strict";return function(e){const n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"}]},t={className:"subst",variants:[{begin:"\\${",end:"}"}],keywords:"true false null this is new super"},a={className:"string",variants:[{begin:"r'''",end:"'''"},{begin:'r"""',end:'"""'},{begin:"r'",end:"'",illegal:"\\n"},{begin:'r"',end:'"',illegal:"\\n"},{begin:"'''",end:"'''",contains:[e.BACKSLASH_ESCAPE,n,t]},{begin:'"""',end:'"""',contains:[e.BACKSLASH_ESCAPE,n,t]},{begin:"'",end:"'",illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n,t]},{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n,t]}]};t.contains=[e.C_NUMBER_MODE,a];const i=["Comparable","DateTime","Duration","Function","Iterable","Iterator","List","Map","Match","Object","Pattern","RegExp","Set","Stopwatch","String","StringBuffer","StringSink","Symbol","Type","Uri","bool","double","int","num","Element","ElementList"],r=i.map(e=>`${e}?`);return{name:"Dart",keywords:{keyword:"abstract as assert async await break case catch class const continue covariant default deferred do dynamic else enum export extends extension external factory false final finally for Function get hide if implements import in inferface is late library mixin new null on operator part required rethrow return set show static super switch sync this throw true try typedef var void while with yield",built_in:i.concat(r).concat(["Never","Null","dynamic","print","document","querySelector","querySelectorAll","window"]).join(" "),$pattern:/[A-Za-z][A-Za-z0-9_]*\??/},contains:[a,e.COMMENT("/\\*\\*","\\*/",{subLanguage:"markdown",relevance:0}),e.COMMENT("///+\\s*","$",{contains:[{subLanguage:"markdown",begin:".",end:"$",relevance:0}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"},{begin:"=>"}]}}}()); \ No newline at end of file diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/index.html b/pkg/nnbd_migration/lib/src/front_end/resources/index.html deleted file mode 100644 index f24e6a8a3879..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - Null Safety Preview - - - - - - - - -
-

Rerunning...

-
-
-

{{ root }}

-
- -

Dart

-

Proposed null safety changes

-

✓ Null safety migration applied

- - - - - - -
-
- -
-
-

 

- Migrate - check_box - -
-
-
- - -
-
- -

- Select a source file on the left to preview the proposed edits. -

-
-
-
-
-
-
Proposed Edits
-
-
-
-
Edit Details
-
-

See details about a proposed edit.

-
-
-
-
-
- - -
Based on {{ sdkVersion }}
-
- - diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/migration.css b/pkg/nnbd_migration/lib/src/front_end/resources/migration.css deleted file mode 100644 index 81900d0c16d7..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/migration.css +++ /dev/null @@ -1,774 +0,0 @@ -/* Copyright (c) 2019, 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. */ - -/* - * Colors mentioned in this file, e.g. "$dark-text-color" are from DartPad: - * https://github.com/dart-lang/dart-pad/blob/master/lib/scss/colors.scss - */ - -/* Text selection */ -::selection { - background: #6e8eb1; /* $dark-selection-color */ -} - -/* Material icons configuration */ -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - src: local('Material Icons'), - local('MaterialIcons-Regular'), - url(/MaterialIconsRegular.ttf) format('truetype'); -} - -/* - * Required for Material Icons: - * https://google.github.io/material-design-icons/ - */ -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; /* Preferred icon size */ - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - - /* Support for all WebKit browsers. */ - -webkit-font-smoothing: antialiased; - /* Support for Safari and Chrome. */ - text-rendering: optimizeLegibility; - - /* Support for Firefox. */ - -moz-osx-font-smoothing: grayscale; - - /* Support for IE. */ - font-feature-settings: 'liga'; -} - -body { - background-color: #12202f; - color: #ccc; - font-family: "Roboto", sans-serif; - font-size: 14px; - display: flex; - flex-direction: column; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - margin: 0; - padding: 0; - overflow: hidden; -} - -.proposed .after-apply { - display: none; -} - -.applied .before-apply { - display: none; -} - -.applied .apply-migration:not([disabled]), .needs-rerun .apply-migration:not([disabled]) { - display: none; -} - -.proposed:not(.needs-rerun) .apply-migration[disabled] { - display: none; -} - -header { - background-color: #1c2834; - height: 48px; - padding-left: 24px; - align-items: center; - z-index: 4; -} - -header h1, -header h2 { - display: inline-block; - font-family: "Google Sans","Roboto",sans-serif; - font-weight: 400; - margin-right: 24px; -} - -h1 { - font-size: 1.5em; -} - -header h2 { - font-size: 1.2em; - - /* Shift text up */ - position: relative; - top: -2px; -} - -header .action-button, header a { - right: 0px; - float: right; - margin: 10px; -} - -header img.logo { - height: 24px; - width: 24px; - margin-right: 8px; - position: relative; - top: 4px; -} - -footer .report-problem { - right: 0px; - margin: 4px 8px; -} - -.rerun-migration .required { - display: none; -} - -.needs-rerun .rerun-migration .required { - display: initial; -} - -.needs-rerun .rerun-migration .optional { - display:none; -} - -footer { - color: #ccc; - background-color: #27323a; - display: flex; - flex-direction: row; - align-items: center; - padding: 8px 0 8px 24px; -} - -footer .wide { - flex: 1; -} - -footer .sdk-version { - margin-right: 32px; -} - -.horizontal { - display: flex; -} - -.panels { - background-color: #121a25; - flex: 1; - overflow: hidden; -} - -.panel-heading { - color: #676767; - margin: 8px; -} - -.nav-link, -.region { - cursor: pointer; -} - -.nav-panel { - background-color: #12202f; - flex: 1 200px; - margin: 0; - overflow: scroll; - user-select: none; -} - -.nav-inner { - padding: 0 0 7px 7px; -} - -.fixed { - position: fixed; - top: 0; -} - -.root { - margin: 0; - display: none; -} - -.nav-tree > ul { - padding-left: 6px; -} - -.nav-tree .material-icons { - font-size: 20px; - position: relative; - top: 5px; - margin-right: 8px; - color: #676767; /* $secondary-color */ -} - -.status-icon.already-migrated { - color: #007a27; /* $light-green */ -} - -.status-icon.disabled { - cursor: not-allowed; -} - -.status-icon.opted-out { - color: #676767; /* $secondary-color */ -} - -.status-icon.opted-out:hover { - color: #ffffff; -} - -.status-icon.migrating { - color: #51c686; /* $dark-green */ -} - -.status-icon.migrating:hover { - color: #ffffff; -} - -.nav-inner ul { - padding-left: 12px; - margin: 0; -} - -.nav-inner li { - list-style-type: none; - white-space: nowrap; -} - -.nav-inner li:not(.dir) { - margin-left: 20px; - margin-bottom: 3px; -} - -.nav-inner li.dir .arrow { - cursor: pointer; - display: inline-block; - font-size: 10px; - margin-right: 4px; - transition: transform 0.5s ease-out; -} - -.nav-inner li.dir .arrow.collapsed { - transform: rotate(-90deg); -} - -.nav-inner ul { - /* a max-height is added to each element at runtime. */ - transition: max-height 0.25s ease-out; -} - -.nav-inner ul.collapsed { - max-height: 0 !important; - overflow: hidden; -} - -.nav-inner .selected-file { - color: white; - cursor: inherit; - font-weight: 600; - text-decoration: none; -} - -.edit-count { - background-color: #676767; - border-radius: 10px; - color: #fff; - display: inline-block; - font-size: 11px; - font-weight: 600; - margin-left: 5px; - min-width: 25px; - padding: 4px 0 2px 0; - text-align: center; - line-height: 1em; -} - -.file { - display: flex; - flex-direction: column; - flex: 4 300px; - font-family: "Google Sans","Roboto",sans-serif; - background: #12202f; - margin: 0 6px; -} - -.title-bar h3 { - display: inline-block; - font-weight: 400; - /* This aligns the title text with the content text, accounting for the width - * of the line numbers. - */ - margin: 0.5em 24px 0.5em 63px; -} - -.title-bar #migrate-unit-status-icon-label { - display: none; - user-select: none; -} - -.title-bar #migrate-unit-status-icon-label.visible { - display: inline; -} - -.title-bar #migrate-unit-status-icon { - vertical-align: text-bottom; -} - -.content { - flex-grow: 1; - font-family: "Roboto Mono",monospace; - overflow: scroll; - position: relative; - white-space: pre; -} - -.code { - padding: 0.5em; - position: absolute; - left: 0; - top: 0; - margin-left: 56px; -} - -.code .welcome { - font-family: "Google Sans","Roboto",sans-serif; - font-size: 18px; - margin-right: 62px; - color: #777; -} - -.code .nav-link { - color: #16adca; - text-decoration-line: none; -} - -.code .nav-link:visited { - color: #139bb5; /* #16adca darkened 10% */ - text-decoration-line: none; -} - -.code .nav-link:hover { - text-decoration-line: underline; - font-weight: 600; -} - -.regions { - padding: 0.5em; - position: absolute; - left: 0; - top: 0; -} - -.regions table { - border-spacing: 0; - font-size: inherit; -} - -.regions td { - border: none; - /* The content of the regions is not visible; the user instead will see the - * highlighted copy of the content. */ - color: rgba(255, 255, 255, 0); - padding: 0; - white-space: pre; -} - -.regions td:empty:after { - content: "\00a0"; -} - -.regions tr.highlight td:last-child { - background-color: #444444; - color: white; -} - -.regions td.line-no { - border-right: solid #12202f 2px; - color: #999999; - padding-right: 4px; - text-align: right; - visibility: visible; - width: 50px; - display: inline-block; -} - -.regions tr.highlight td.line-no { - border-right: solid #ccc 2px; -} - -.region { - display: inline-block; - position: relative; - visibility: visible; - z-index: 200; -} - -.region.added-region { - background-color: #178afd; - color: #fff; -} - -.region.removed-region { - background-color: #FA557d; /* $dark-pink */ - color: #fff; -} - -.region.informative-region { - background-color: #263952; - color: #fff; - display: inline-block; - height: 14px; - position: relative; -} - -.target { - background-color: #444; - position: relative; - visibility: visible; - font-weight: 600; -} - -.info-panel { - flex: 1 200px; - margin: 0; - height: 100%; - display: flex; - flex-direction: column; -} - -.info-panel .edit-panel { - background-color: #12202f; - overflow: auto; -} - -.info-panel .panel-content { - padding: 7px; -} - -.info-panel .panel-content> :first-child { - margin-top: 0; -} - -.info-panel .nowrap { - white-space: nowrap; -} - -.info-panel ul, -.info-panel ol { - padding-left: 20px; -} - -.info-panel li { - margin: 0 0 5px 0; -} - -.info-panel a { - color: #139bb5; -} - -.info-panel a:hover { - color: #1ec7e7; /* #139bb5 lightened 20% */ -} - -.info-panel .edit-list { - background-color: #12202f; - overflow: auto; -} - -.edit-panel { - margin-top: 6px; - flex: 1 100px; -} - -.edit-list { - flex: 2 100px; -} - -.edit-list .edit { - margin: 3px 0; -} - -.edit-list .edit-link { - cursor: pointer; -} - -.popup-pane { - display: none; - position: fixed; - top: 150px; - left: 150px; - right: 150px; - bottom: 150px; - border: 1px solid black; - border-top: 2px solid black; - border-radius: 7px; - box-shadow: 0px 0px 20px 2px #b4bfcb22; - z-index: 400; - background: #2b3036; - padding: 20px; -} - -.popup-pane .close { - position: absolute; - right: 10px; - top: 10px; - cursor: pointer; - text-shadow: 1px 1px 2px #888; - box-shadow: 1px 1px 2px #111; -} - -.popup-pane h2 { - padding: 21px; - height: 10%; - margin: 0px; - box-sizing: border-box; -} - -.popup-pane p { - height: 10%; - box-sizing: border-box; - padding: 0px 20px; -} - -.popup-pane pre { - background: #12202f; - padding: 20px; - bottom: 0px; - overflow: auto scroll; - height: 65%; - margin: 0px; - box-sizing: border-box; -} - -.popup-pane .button.bottom { - margin: 20px 0px; - display: block; - text-align: center; -} - -.rerunning-pane { - display: none; -} - -body.rerunning .rerunning-pane { - display: block; - position: fixed; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - background-color: #000000AA; /* translucent black */ - z-index: 400; -} - -.rerunning-pane h1 { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -.edit-panel .type-description { - /* From DartPad $dark-orange */ - color: #ff916e; - font-family: monospace; -} - -ul.trace { - font-size: 13px; - list-style-type: none; - padding-left: 0px; -} - -ul.trace li { - color: white; -} - -ul.trace li .function { - /* from .hljs-variable */ - color: #16adca; - font-family: monospace; - font-weight: 600; -} - -ul.trace li p.drawer { - margin: 3px 0px; - padding: 0px 0px 0px 14px; -} - -ul.trace li p.drawer button { - margin-right: 3px; -} - -.elevation-z4 { - box-shadow: 0px 2px 4px -1px rgba(0, 0, 0, 0.2), - 0px 4px 5px 0px rgba(0, 0, 0, 0.14), - 0px 1px 10px 0px rgba(0, 0, 0, .12); -} - -a { - color: #ccc; - fill: #ccc; - text-decoration: none; -} - -a:hover { - color: #dbdbdb; /* #ccc lightented 30%*/ - fill: #fff; -} - -.add-hint-link { - display: inline-block; - margin: 3px; -} - -.add-hint-link:hover { - color: #fff; -} - -header button { - text-transform: uppercase; -} - -header a { - margin: 0; -} - -/* Careful here. `a.button` is repetitive but required to get correct - * specificity */ -button, .button, a.button { - background-color: rgba(22, 138, 253, 0.15); - border: none; - border-radius: 3px; - padding: 3px 10px; - font-weight: 500; - font-font: Roboto, sans-serif; - color: #fff; -} - -button:hover, .button:hover { - background-color: rgba(22, 138, 253, 0.29); - cursor: pointer; -} - -button[disabled] { - background-color: rgba(255,255,255,.12); - color: rgba(255,255,255,.37); - cursor: not-allowed; -} - -/* Change edit panel button colors */ -.edit-panel .button, .edit-panel button { - background-color: rgba(63, 104, 148, 0.6); - color: white; -} -.edit-panel .button:hover, .edit-panel button:hover { - background-color: rgba(101, 153, 208, 0.6); - color: white; -} - -/* - * Adjustments to align material icons in the toolbar buttons. -*/ -.action-button > span { - position: relative; - top: -3px; -} - -.action-button .material-icons { - top: 4px; -} - -/* Don't shift the icon when it's a direct child of the button */ -.action-button > .material-icons { - top: 1px; -} - -/* Shift the text to center with the icon. */ -.action-button > span.label { - position:relative; - top: -4px; -} - -.action-button .material-icons { - font-size: 20px; - position: relative; -} - -.placeholder { - color: #777; - text-align: center; - margin-top: 3em !important; -} - -/** - * HLJS Overrides - */ -.hljs { - background-color: #12202f; /* $dark-code-background-color */ - color: #c0c2c5; /* $dark-editor-text */ - display: block; - overflow-x: auto; - padding: 0.5em; - /** - * This allows the per-line highlights to show. - */ - background: none; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-deletion { - color: #51c686; /* cm-keyword */ -} - -.hljs-number { - color: #627978; /* cm-number */ -} - -.hljs-comment { - color: #9198b4; /* cm-comment */ -} - -.hljs-literal { - color: #ee8666; /* cm-atom */ -} - -.hljs-string { - color: #e55074; /* cm-string */ -} - -.hljs-variable { - color: #16adca; /* cm-variable */ -} - -.hljs-link { - color: #e55074; /* cm-string */ -} -.hljs-section, -.hljs-type, -.hljs-built_in, -.hljs-title { - color: #ee8666; /* cm-variable-2 */ -} - -.hljs-addition { - color: #263952; /* $dark-selection-color */ -} - -.hljs-meta { - color: #627978; -} diff --git a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart b/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart deleted file mode 100644 index 23c8fb4e2c94..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart +++ /dev/null @@ -1,11673 +0,0 @@ -// Copyright (c) 2020, 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. - -// This file is generated; don't edit it directly. -// -// See pkg/nnbd_migration/tool/codegen/generate_resources.dart for how -// to edit the source content and for re-generation instructions. - -import 'dart:convert' as convert; - -String get MaterialIconsRegular_ttf { - return _MaterialIconsRegular_ttf ??= - _decode(_MaterialIconsRegular_ttf_base64); -} - -String get RobotoMonoRegular_ttf { - return _RobotoMonoRegular_ttf ??= _decode(_RobotoMonoRegular_ttf_base64); -} - -String get RobotoRegular_ttf { - return _RobotoRegular_ttf ??= _decode(_RobotoRegular_ttf_base64); -} - -String get dart_192_png { - return _dart_192_png ??= _decode(_dart_192_png_base64); -} - -String get highlight_css { - return _highlight_css ??= _decode(_highlight_css_base64); -} - -String get highlight_pack_js { - return _highlight_pack_js ??= _decode(_highlight_pack_js_base64); -} - -String get index_html { - return _index_html ??= _decode(_index_html_base64); -} - -String get migration_css { - return _migration_css ??= _decode(_migration_css_base64); -} - -String get migration_js { - return _migration_js ??= _decode(_migration_js_base64); -} - -String _decode(String data) { - data = data.replaceAll('\n', '').trim(); - return String.fromCharCodes(convert.base64Decode(data)); -} - -String? _MaterialIconsRegular_ttf; -// MaterialIconsRegular_ttf md5 is 'a37b0c01c0baf1888ca812cc0508f6e2' -String _MaterialIconsRegular_ttf_base64 = ''' -AAEAAAAPAIAAAwBwR0RFRgQAAFMAAYsIAAAAJEdQT1PgGO+cAAGLLAAAADZHU1VC5NKpUQABi2QAAGlQ -T1MvMgpzImMAAAF4AAAAYGNtYXDx8DHgAAAJeAAABgJjdnQgABEBRAAAD3wAAAAEZ2FzcP//AAMAAYsA -AAAACGdseWZEfewEAAAXHAABckhoZWFkBtqLZgAAAPwAAAA2aGhlYQQBAgQAAAE0AAAAJGhtdHhq5mlG -AAAB2AAAB6Bsb2NhOymWhQAAD4AAAAecbWF4cAQnAOEAAAFYAAAAIG5hbWUcDzXkAAGJZAAAAXpwb3N0 -/4YAMgABiuAAAAAgAAEAAAABAtBPvyKIXw889QAJAgAAAAAA0t6jygAAAADS3qPNAAD//gIAAgQAAAAI -AAIAAAAAAAAAAQAAAgAAAAAAAgAAAAAAAgAAAQAAAAAAAAAAAAAAAAAAAAMAAQAAA80AsAAYAAAAAAAC -AAAAAQABAAAAQAAuAAAAAAAEAgABkAAFAAABTAFmAAAARwFMAWYAAAD1ABkAhAAAAgAFAwAAAAAAAAAA -AAESAAAABAAAAAAAAAAAAAAAAIAAMP//AgAAAAAAAgAAAAAAAAEAAAAAAAAAAAAAACAAKwIAABEAAAAA -AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAACsAKwAVAEAAKwBAAEAAVQBAAFUANQArAEAAQABVAGsAawBAACsAKwAr -ACsAFQArAIAAKwArAKsAKwArACsAKwBAACsAFQBAAEAAVQBVAIAAgAArAIAAKwArACsAQAArAGsAlQBA -AEAAKwBAACIAFQBVAFUAVQBVAFUAVQAVABUAKwArACsAVQAVABUAKwArABUAFQAsAEAAKwAVABUAFQAV -ACsAQAAVACsAQAAAAFUAdwBAAGsAVQArAEAAKwArAEAAVQArACsAawBAAEAAKwBrACsAKwArADMAQAAr -AAAAAAAAAFsAFQBrABUAawArACsAAAAVAEAAVQArAD4AFQAVACsAAAAAAEAAVQBrAEAAKwArAEAAAAAr -AGsAKwArAEAAQAArAEAAawBVAD0AQAArACsAKwAhAGsAKwArAEAAAABAAEAAQAArAEAAawArACsAQABA -ACsAFQArACsAKwArACsAKwArACsAlQCVAJUAlQCVAGsAQABVAEAADwAPAA8ADwArAFUAAAAVABUAFQAV -ABUAFQBAACsACAArACsAQAAVAGsAFQABAFUAFQArACsAMwArABUACAAJAAgAKwBmAAAAKwCVAIcAQABA -AEAAAABAAEAAQABAAEAAQABAAEAAQABAAEAAQACVACsAAABVAAAAQABAAIAAIAA1ACsAVQBrAEAAQABr -AFUAawCAAEAAKwBVACsAQAArAEAAdwArAEAAawBVAEAAVQBVAFUAVQBXAFUAFQBLACsAVQA1ACsAawAr -AAAAKwAAAAAAAAAAAAAAawBrACsAKwArACsAFQAVAAAAFQAVACsAgAArAEAAQAArAIAAqwC3AIAAQACA -ACsAKwAVAGsAAAAAAAAAAABAAFUAawBrAAAAAABAAEAAQABVAGsAawBVABUAQAArABUAFQBVAEAAKwAV -ABUAKwArAEAAawCAACsAQAA1ADUAKwBrAIAADwAPAA8ADwBAACsAKwArAGsAawArAEAAQAArAEAAQABA -ACsAAABAAEAAFQBAAEAAQABAAEAAQABrAFUAKwBAAEAAKwBVACsAVQArAKgAFQAVABUAFQAVABUAFQAV -ABUAFQAVAEAAQAAAACsAFQAVACwAMQAVAEAAKwCVAEAAQABVAAAAKwAeAEAAFQAVACoAQAArAEAAFQBA -AEAAKwBAABUAQABAAEAAQABAACsAKwArAIAAawArAKsAtwBAABUAKwArAFUAKwBAAFUAKwArACsAQAAV -ABIAVwBVAEAAFQAYACsAKwArAEAAKwAAAFkAQAAlACsAKwBAAEAAQAAAAAAAFQAVACsAFQAVABUAFQBM -AAAAKwAAACsAQAAVAEAAKwAAAFUAQAAlAFUAVQBVAIAAKwAVAEAAKwArACsAKwBAACsAQAArAEAAQABV -ABUAQAAVAFUAQABAAFUAKwCAAEAAQABAACsAKwArADoAKwAVAEAAQABAABUAYABrAGsAKwA6AEAAQAAV -AFUAPgBrAGsAQABrAEAAQABVAEAAKwBVAGsAKwBVAFUAlQArAJUAVQArAEkAqwC3AGsAgACAAGsAawBA -AFUA1QBWAJ4AngBVAGsAVQBVAIAAdwBrAEAAKwArACsAQABAAEAAQAArACsAAAAVAEAAQABAAEAAAABA -AFUAVQArACsAVQA9AEAAawArAEAAAAArABUAKgAgABUAKwBAAEAAKwBVACsAFQAVABUAgABVABUAVQAV -AEAAVQAVACsA1QArACsAVQArACsAQAArABUAAABAACsAKwBVAFUAVQArAFUAQAArABUAFQBVABUAVQBV -AEAAKwAVAEAAVQArACsAKwArACsAQABAACsAKwArACsAKwABAEAAKwBAAEAAKwAVACsAKwAjACsAKwAr -ABUAQABAAEAAQABAAEAAQABVAAAAVQBrAGsAVQAOABUAKwArABUAVQArACsAQABrAFUAQABJAAkAQABA -ACsAKwArACsAKwArAFUAVQBAAEAAawArACsAKwArABUAKwCAAIAAVQArACsAFQBVAEAAQAArAEAAQABV -AFUAVQArACsAVQBAAEAAFQArACsAKwBAAAAAawBVAAAAQAAAABUACwBAACsAKwArAEAAKwAVABUAawAr -AEAALQBAAAAAawCAABUAEQAVABUAFQBrABUAFQBAAFUAWgBrACsAFQAVABUAKwA0ACsAQABVACsAQABr -ACsAFQAVABUAVQAVABUAAABAAEAAFQArABUAKwBAACsAawBrAEAAKwBVACsAVQArAFUAVQBVAFUAVQAr -ABUAFQArACsAKwArACsAawArACsAQABAAEAAFQArADUAJwCAAGsAQAArAFoAKwA+ABUAawAVAAAAZQBr -ACsAQAArACsAQABAAAAAVQArAKsAFQBAAFUAFQBAACsAFQBAACsAAABVAAAAawArABUAAAA/ACsAQAAV -ACsAKwBVAFUAKwBVACsAKwArACsAKwAAAAAABQAAAAMAAAAsAAAACgAAAhwAAQAAAAAE/AADAAEAAAAs -AAMACgAAAhwABAHwAAAAeABAAAUAOAA5AF8AeuAD4BngIeAk4CzgMeA54FPgceC84L/gxODl4W3hleGc -4cPhyOHQ4dvh4uJk4sTiyeLM4wjjGOM44+Dj7uQT5C7kPOU25XLl3eYg5kXmxebd5t/m4efp6AHoDugV -6DroUehz6LbozukA6QbpD+kr60z//wAAADAAXwBh4ADgGeAb4CPgKOAu4DPgO+BV4K/gvuDD4MbhReGQ -4Zzho+HI4c3h2OHg4ibivOLG4szjB+MK4xrjnePi4/HkFeQw5S3lOeXD5g7mI+bE5t3m3+bh5+nn7ugL -6BHoNOhN6FPodei46NDpAukI6RHrO////9P/rv+tICggEyASIBEgDiANIAwgCyAKH80fzB/JH8gfaR9H -H0EfOx83HzMfLB8oHuUejh6NHoseUR5QHk8d6x3qHegd5x3mHPYc9BykHHQcchv0G90b3BvbGtQa0BrH -GsUapxqVGpQakxqSGpEakBqPGo4YfwABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAuAAAAAAAAAAPAAAADAAAAA5AAAAAwAAAF8AAABf -AAAADQAAAGEAAAB6AAAADgAA4AAAAOADAAAAKAAA4BkAAOAZAAAALAAA4BsAAOAhAAAALQAA4CMAAOAk -AAAANAAA4CgAAOAsAAAANgAA4C4AAOAxAAAAOwAA4DMAAOA5AAAAPwAA4DsAAOBTAAAARgAA4FUAAOBx -AAAAXwAA4K8AAOC8AAAAfAAA4L4AAOC/AAAAigAA4MMAAODEAAAAjAAA4MYAAODlAAAAjgAA4UUAAOFt -AAAArgAA4ZAAAOGVAAAA1wAA4ZwAAOGcAAAA3QAA4aMAAOHDAAAA3gAA4cgAAOHIAAAA/wAA4c0AAOHQ -AAABAAAA4dgAAOHbAAABBAAA4eAAAOHiAAABCAAA4iYAAOJkAAABCwAA4rwAAOLEAAABSgAA4sYAAOLJ -AAABUwAA4swAAOLMAAABVwAA4wcAAOMIAAABWAAA4woAAOMYAAABWgAA4xoAAOM4AAABaQAA450AAOPg -AAABiAAA4+IAAOPuAAABzAAA4/EAAOQTAAAB2QAA5BUAAOQuAAAB/AAA5DAAAOQ8AAACFgAA5S0AAOU2 -AAACIwAA5TkAAOVyAAACLQAA5cMAAOXdAAACZwAA5g4AAOYgAAACggAA5iMAAOZFAAAClQAA5sQAAObF -AAACuAAA5t0AAObdAAACugAA5t8AAObfAAACuwAA5uEAAObhAAACvAAA5+kAAOfpAAACvQAA5+4AAOgB -AAACvgAA6AsAAOgOAAAC0gAA6BEAAOgVAAAC1gAA6DQAAOg6AAAC2wAA6E0AAOhRAAAC4gAA6FMAAOhz -AAAC5wAA6HUAAOi2AAADCAAA6LgAAOjOAAADSgAA6NAAAOkAAAADYQAA6QIAAOkGAAADkgAA6QgAAOkP -AAADlwAA6REAAOkrAAADnwAA6zsAAOtMAAADugAQ//0AEP/9AAADzAAAAQYAAAEAAAAAAAAAAQIAAAAC -AAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAMEBQYHCAkKCwwAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAADQAODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJwAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAARAUQAAAApACkAKQAxADkAQQBJAFEAWQBhAGkAcQB5AIEAiQCRAJkAoQCpALEAuQDBAMkA0QDZ -AOEA6QDxAPkBAQEJAREBGQEhASkBMQE5AUEBSQFRAW4BlQGtAeQCDAJVAqgCwALqAvwDDQMzA4QDygP5 -BCQEYASeBMUE/AU2BXEFogXLBd4F+wYjBi8GRgZoBo4GxQbwByYHZQeDB6oHywfwCAIIFQhNCFsIjAjZ -CQcJKAlQCWYJdgm0CdwKCQpFCnMKngsNC8AMIQyQDUYNqQ3fDjAOew6aDswO3Q8BDzUPYw+HD8EP7xA2 -EFkQmhC8EN8RBxEpEUgRdhGZEe8SGxJNEmASexKREqQSwRLsEwYTNBNxE74UNBRZFIQUnRTSFRQVTRV1 -FaMVvRXgFgMWLxaRFtoXGxdgF5gXuxfdGAAYIhhSGHwYsxjaGREZWRmTGg0aOhp0Gp8ayhsJGx4bRhta -G4MbohvMG/8cLhxXHHEcoBzxHSsdSh1sHYUdmR2qHg4eNx5rHpAetR7RHt4e9R8XHysfSR9sH5cgCiAb -IDMgUSBtIJsgziEAITQhbiGdIcoh/iIyIlkikiK/IuQjDyM2I1YjdiO2I9okDSQ4JHEkniTRJPglHCVM -JYkluSX4JjQmZSavJvknKidQJ10ncye2J/0oGyhiKKkpCSlOKXgptSnCKdsp/ioSKisqOiqJKqwq1isS -K1AroCvYLBIsNyydLRgtPC2gLfYuXi6PLvQvKi+RL/cwHDBCMGgwjzC6MN0xFjE7MVkxgjGsMcEx6DIj -Ml8yjDKlMsAy3TL/MyEzQTNYM4QzsjPSNA40PjRyNJY0sTTMNOs1ATUTNWI1eTWaNbE13DYUNiY2djac -Nsc22Tb1NzM3RTd3N5g3wzftOBY4WjiUOL441TjrOQk5LjlhOYw5xjoLOjA6WTqEOso68TsXOzw7ZTu9 -O8473zvvO/88FDwqPIY8njy5POQ9CT0mPVs9gj3NPe4+Fj5DPnM+sj79Pyg/Sz+HP6k/6EAxQFVAfkCs -QNRA/EEfQUVBY0GxQfBCJ0JQQn1CkUKsQ0JD1USzRadFuEXSRexGHEZDRmdGmkbIRu9HL0dlR6RH20gl -SGNIrEjdSSpJUkmDSa1J6EoJSi1KUkp0SpVKuUrsSw5LOktcS4BLmUusS8tMAUwXTElMZ0ysTOtNJE1t -TbxN9U4yTnlOx08BT2FPsFAJUDBQblCtUN9Q8lEjUYNR5FInUklSZ1J4UsJTJFODU+FUMVR3VLRU3FUE -VXlVnVXWVgtWHlZbVqxWvlb3VxxXRFd1V69X01gHWDVYeFifWLhY1VkLWRxZLVl6WZ5Zu1nuWh9aUlp2 -WqFa11sIW2NbnVvJW/1cN1xwXJlcyl0KXThdZ12jXdNd/F57XvpfLF93X71f62AgYGZgf2C1YNZhDmFT -YYNhp2IHYmxik2LIYxBjXWOfY+FkGmRJZG5kmmT2ZT1lgGXPZg5mPWZ8ZrBm1Wb5ZxdnSGdqZ7Jn7GgJ -aDBooGjPaPxpUmmdad9qCGosanJqkWrKawprNGtTa3trp2vMbAZsK2xabIdsvWz7bUJtd22dbdlt624W -bj5ubG6Zbs5u8m8Fb2hvnW/Ib/pwC3A/cHBwmXDlcRZxZ3GucexyNHJscoFyjXKkcrByxXLqcvtzDHMd -czdzSHNZc31zonO7c+N0CnQxdE10aHR9dJN0qXS+dNV07HUsdWV1j3W4deF2JHZZdpd2zXb7dx13WneX -d+J4GHhaeKx463kkeU55dHmeecR583o3enJ6nnrbex57V3t7e+B8CHw0fFR8hXyyfON9KX1nfaJ91n4E -fix+TH6KfrF+7X8zf2p/qX/Yf++ABoAmgFCAeoCkgLeBEoFogaCB5YIqgmaCooLMgv+DPYN6g7GD54Qs -hGSEwITchQaFOoVYhYSFwYXchhWGR4aIhsCHAYdNh4eHrYfRh+6IFogtiE6IdYjiiQOJKYlgiZGJuYoK -ij6Kd4rJiv6LaYuOi8GL7YwtjHKMrIzljR2NVY2Eja6N0Y3ojgeOVY5/jq6OwY7cjw6PMY9Mj3aPk4+0 -j9+QJZA2kFaQhpC5kN2RHJFYkXqRvpHkkhiSTZKrkvSTC5Mik1aTipO6k+iT/JQhlDaUcJSOlLaU45UB -lRuVPZWhlcyV9pYwlm2WrZbvlxOXQZdul5mXxpgAmCqYbZinmRuZSJl8mbCZ5JoCmiyaSZpwmp+axprx -mzObkpuqm9icAJwnnFGcs50rnVqdjp3AngKeLp5qnsqfKp9Un6yf5qAjoFmgmaDVoP+hN6Ftoa+h66IO -ojCiVKJyoqeiwKLZov2jLKNRo7aj9qQkpFKkmKTCpPOlIaVspaalvqXPpeel/qYdpjymbKaGpp+muabh -pwGnK6dSp2+ngqfGp/KoRaikqNKpEqk9qW+pgqm7qfaqFqpOqn6quarjqwurNatUq4SrpqvHq/esO60C -rSatYq2LrduuG65mroCuuq7rrxmvWq96r7+v37BOsHGwprDzsTKxfLGusciyBrI/sriy/7M5s3Czo7Pk -tCu0abSbtNe1K7Wmteq2HLZFtm223LcQt8G35rg5uIi4tLkkAAIAEQAAAJkBVQADAAcALrEBAC88sgcE -AO0ysQYF3DyyAwIA7TIAsQMALzyyBQQA7TKyBwYB/DyyAQIA7TIzETMRJzMRIxGId2ZmAVX+qxEBMwAB -AAAAAAAAAAAAAAAAMQAAAQAAAAAAAAAAAAAAADEAAAEAAAAAAAAAAAAAAAAxAAABAAAAAAAAAAAAAAAA -MQAAAQAAAAAAAAAAAAAAADEAAAEAAAAAAAAAAAAAAAAxAAABAAAAAAAAAAAAAAAAMQAAAQAAAAAAAAAA -AAAAADEAAAEAAAAAAAAAAAAAAAAxAAABAAAAAAAAAAAAAAAAMQAAAQAAAAAAAAAAAAAAADEAAAEAAAAA -AAAAAAAAAAAxAAABAAAAAAAAAAAAAAAAMQAAAQAAAAAAAAAAAAAAADEAAAEAAAAAAAAAAAAAAAAxAAAB -AAAAAAAAAAAAAAAAMQAAAQAAAAAAAAAAAAAAADEAAAEAAAAAAAAAAAAAAAAxAAABAAAAAAAAAAAAAAAA -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 -NCYnNRYWFRQHJzYnFAcnNRYBAC14AWUbLCUpGRdba1VlZQFVOy9BVBYgCzUBNDUBq1otQv6bGywdCiwH -EluQa4BlpTNODiwOaUQwKSEbHQkENC8aAAMAQABFAcABuwALAA8AFQAAARYWFAYHNTY2NCYnFhQHNQcz -NxEnIwErQVRUQS87Oy81NetVa2tVAbsOaYhpDiwOTmZODlN4GqwWa/6qawAABAArAFUB1QGrAAMABwAL -ABsAACU1IxUnNSMVFzUjFQEyFhURFAYjISImNRE0NjMBq1YV6+vrAVYRGRkR/qoRGRkRgMDAa1VVa1VV -ASsaEf8AERoaEQEAERoABABAAEABwAHAAAMADQAZACkAACU1MxUnFTMyNjU1NCYjBzUjFSM1IxUzNTMV -EzIWFREUBiMhIiY1ETQ2MwE1K0tWCQwMCYAgKyAgK8oRGhoR/tYSGRkS4EBAYIAMCVYJDICANTWAKysB -ABoR/tYRGhoRASoRGgAABQAiADEB0gHPAAkADAAUABcAGgAAJTMVIzU3IzUzFQUzJyczFyMnIwcjFzMH -EyM3AVCCtn59sf6cUyoRI2AnFG0UJ7ljMjNlMqgiG7ciG4JvLvQ0NCMyAWwyAAACABUAKwHrAcAAFwAa -AAABMhYVERQGIyM1MxEhETMVIyImNRE0NjMTNxcBwBEaGhFVVf6AVVURGhoRQICAAcAaEf8AERkqAQD/ -ACoZEQEAERr+a4CAAAQAVQBAAasB6wATADQAOwBPAAAlFDMyNzc2NTUmNCYjIgcHBhUVFjcUBwcGIyIG -IyInJiYnJjU1NDc3NjMyNjMyFx4CFxYVByM1BzU3Mwc0NjM1Fwc1IgYUFjI2NTMUBiImARoKBQIEAgIJ -AgMDBAMDKAIGBwQCCQIJBAIGAgkCBwYEAgkCCQQCBwQEAlkUFSYDlGRHa2s0TExoTCtljGW8BwIFBAIr -BAQFAwQEAisEEw0EDQYCAgEEAQUZDw0EDQYCAgEEAgwGCzVGBg8MG0ZkVmtrVktqS0s1R2RkAAQAVQBA -AasB6wATACcARwCJAAA3NDYzNRcHNSIGFBYyNjUzFAYiJjcUMzI3NzY1NSY0JiMiBwcGFRUWNxQHBwYj -IgYjIicmJyY1NTQ3NzYzMjYzMhceAhcWFSMyNTUmNCMjBiIVFSM0NjMyNjMyFxYVFQYVFCMiBxYXFhUU -BwYGBwYjIgYjIicmJicmNTMVFhQzMzYyNTUmNCMjNVVkR2trNExMaEwrZYxlxwoFAgQCAgkCAwMEAgIq -AgYHBAIJAgcQAgQDAwYHBAIIAgkEAgcEBAJxDwIECwIEFgsHAQgBDAwIAgQCBQkCBAIBBAEEBwIJAggC -AQgCCRICBAsCBAIEDetGZFZra1ZLaktLNUdkZBgHAgUEAisEBAUDBAQCKwQTDQQNBgIIAQwJCA8LBg0G -AgIBBAIMBgsNBAIEAgQECA8CBgQPBwQCBAUFAwgFCQIBBgEEAgIBAgEFEAQCBAIECwIEDwACAFUAQAGr -AesAMgBGAAA3BhUHIzczFSMHMjU0NjUzMzIXFhYXFhUUBwYGBwYjIicmJicmNTMUMzI3NzY1NScnJiMn -NDYzNRcHNSIGFBYyNjUzFAYiJvoHAg0FMyUCAgMEBAgDAQYBCQIBBAIIDwkCAQcCCRENBAIFAgIFBAKp -ZEdrazRMTGhMK2WMZd4DAQMvDxMCAQEBAwEEAQkOCQIBCAIIAgECAQUOCgIEBAINBAUCDUZkVmtrVktq -S0s1R2RkAAQAVQBAAasB6wATADQAOwBPAAAlFDMyNzc2NTUmNCYjIgcHBhUVFjcUBwcGIyIGIyInJiYn -JjU1NDc3NjMyNjMyFx4CFxYVByM1BzU3MzcyFhUUBiImNTMUFjI2NCYjFSc3ARoKBQIEAgIJAgMDBAMD -KgIGBwQCCQIJBAIGAgkCBwYEAgkCCQQCBwQEAlsUFSYDF0dkZYxlK0xoTEw0a2u8BwIFBAIrBAQFAwQE -AisEEw0EDQYCAgEEAQUZDw0EDQYCAgEEAgwGCzVGBg8Mj2RGR2RkRzVLS2pLVmtrAAQAVQBAAasB6wAV -ADUAdwCLAAAlFDMyNzc2NTU0JjU0JiMiBwcGFRUWNxQHBwYjIgYjIicmJyY1NTQ3NzYzMjYzMhceAhcW -FSMyNTUmNCMjBiIVFSM0NjMyNjMyFxYVFQYVFCMiBxYXFhUUBwYGBwYjIgYjIicmJicmNTMVFhQzMzYy -NTUmNCMjNTcyFhUUBiImNTMUFjI2NCYjFSc3AR4LBAIEAgIIAgQDBAICKAIGBwQCCQIHEAIEAwMGBwQC -CAIJBAIHBAQCcQ8CBAsCBBYLBwEIAQwMCAIEAgUJAgQCAQQBBAcCCQIIAgEIAgkSAgQLAgQCBA0zR2Rl -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 -EzMVIzUHJzcjwNUq+B74jQGV1Y34HvgAAAIAdwBNAYkBtQAIAAwAABM3FyMVByc3NRMnNxegYGBLgB50 -gEkeSQFVYGCIgB5zd/74SB5IAAEAQACNAcABawAKAAABFwcnFSM1MxUjFwGiHsCVK6tidwFrHsCVYqsr -dwAAAQBrAGsBqwGrAAgAAAEHMxUjNTMVNwGr+I3VKvgBjfgq1Y34AAACAFUAVQGrAasACAAPAAATBxcV -IzUnBzUzMxUnByc31TFxKmUx1oAxPh4+AasxcbSiZTGAgDE+Hj4ABAArACsB1QHVAAMABwALABkAAAE1 -IRUXNSMVNRUhNTcyFhURFAYjIQcRNDYzAYD/AKurAQArERkZEf7VVRkRAVUrK4ArK2srK5UZEf8AERpV -AYARGQADAEAAlQHAAWsAAwAHAAsAABMhFSEHNSEVJTUhFZUBK/7VVQEr/wABKgFrK6srK1YqKgAABAAr -ACsB1QHVAAMABwALABkAAAE1IRUFNSEVBTUhFSURJyEiJjURNDYzITIWAYD/AAEA/wABAP8AAVVV/tUR -GRkRAVYRGQFVKytAKytAKyvW/oBVGhEBABEZGQAFACsAAAHVAgAABwAPAB8AIwAnAAAlNTQmIgYVFTYi -BhQWMjY0NzIWFREUBiMhIiY1ETQ2MxE1IRURFSE1AWtJREl/KBwcKBx7ERkZEf6qERkZEQFW/qqVIBge -Hhgg2xwoHBwoVxoR/wARGhoRAQARGv5VKysCACsrAAUAQABAAcABwAAcACAAJgAyADYAACUyFhUVFAYj -IiY1NDYzMzIWFRQXFgcHFhc3NhcWNzUjFSczFSMVIycVIzUzNSM1MxUjFTcVIzUBqwkMDAmW1QwJSwkM -DAQJLy9eLwkNJCgWFUArFUBAKytAK1YWtQwJSwkM1ZYJDAwJKCQNCS9cMS8JBAzgFhYrQCtAQBYVQBUW -K2trAAAKAFUAFQGrAesABwAPABcAHwAnAC8ANwA/AEcATwAAEjIWFAYiJjQWMhYUBiImNDYyFhQGIiY0 -FjIWFAYiJjQmMhYUBiImNDYiJjQ2MhYUBDIWFAYiJjQ2MhYUBiImNDYyFhQGIiY0EjIWFAYiJjTvIhoa -IhoaIhoaIhqaIhoaIhoaIhoaIhpmIhoaIhq8IhoaIhr+xCIaGiIaGiIaGiIaGiIaGiIamiIaGiIaAesa -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 -NjMBq6urq6sRGRkR/qoRGRkRAVUra2srasAaEf8AERoaEQEAERoAAQAhAKsB1QFrAA4AAAE3FSM3JiMi -BgcnNjYzMgGJTMBOMD44WREyFnVJVQEeTcBNKEE0EERWAAABAGsA6wGVARUAAwAAJSE1IQGV/tYBKusq -AAIAKwArAdUB1QADAAsAACU1IxU2MhYUBiImNAFr1hOwfX2wfesqKup9sH19sAADACsAKwHVAdUABwAP -ABMAADYyNjQmIgYUEjIWFAYiJjQXMxUjuoxlZYxlU7B9fbB9atbWVWWMZWWMARt9sH19sEMqAAEAQABV -AcABlQAIAAATFhYXJiMVJzfVaHESTZ6VlQFAD4FbbVeVlQACAAAAVQIAAZUACAAOAAABFhYXJiMVJzcH -BxcVJzcBFWhxEk2elZWAVVWVlQFAD4FbbVeVlUBVVUCVlQADAEAAQAHAAcAAAwALABMAACU1IxUWMjY0 -JiIGFBMXFQcjJzU3ARUqChYRERYRbHBwoHBw64CAXBEWEBAWASBwoHBwoHAAAwBAAEABwAHAAAMACwAZ -AAABNSMVFjI2NCYiBhQTFxEUBiMhIiY1ETQ2MwFA1Xs0JiY0JqtVGhH+1hIZGRIBQFVV1SY0JiY0AS9V -/wARGhoRASoRGgASAEAAQAHAAcAAAwAHAAsADwATABcAHAAgACQAKAAsADEANgA6AD4AQgBGAEsAABMV -MzUHNTMVAzUzFQM1MxU3NTMVJzUzFQM1MxQGJzUzFQc1MxUDFSM1AzUzFRUiJjUzATIWFSMnFSM1BzUz -FRM1MxUnNTMVJzQ2MxXAgKvWKysrKyorKysrKxoRK9UqVStVKxEaKwEqERorgCqrKyorgCsrGhEBQICA -q9bWAQArK/6rKytVKyurKyv/ACsRGqsqKqsrKwGAKyv+1SsrVRoRAVUaESsrK4ArK/8AKyurKiqqERor -AAEAKwBAAesBwAAFAAA3NSUlNQUrAUD+wAHAQJUrK5XAAAADAEAAgAHAAYAAAwAHAAsAADc1IRUlIRUh -FTUzFUABAP8AAYD+gIDrKiqVK9UrKwADAGsAawGVAasAAgAKAA4AAAEHMwcHIzczFyMnByEVIQEAKFBd -FCxlIGUsFMoBKv7WAYBrJi/r6y9aKgABACsAqwHfAWsADgAAATIWFwcmJiMiBxcjNRc2AQtJdBcyEVk4 -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 -FREVNzMHFwcjNQcnNyc3FzUzBwcnNwGVKysqLigoPVxcehViHnd3HmIVayorKwErKysrXClRAQhRKVxc -eaJiHnd3HmKi1SsrKwAAAwBVACsBqwHVAAIADQAWAAAlNycnAQcnByM1Byc3JxcVJzUzFwcnNwEVKCii -ATgeMVwVYh53jcAqFXpBHiJ8KCne/sgeMVuiYh53jQlFK2t5QR4jAAAFAEAAKwHAAdUAAgAFABMAGwAh -AAAlJxURFTczBxcHIzUHJzcnNxc1MxcWFAcnNjQnBzcWFRQHARMoKDxcXHoVYh53dx5iFcwfIRkVFVYx -CgqkKVEBCFEpXFx5omIed3ceYqJkMno0GSpYKlYxGRgZGQADAA8ADwHxAfEABwAXABoAACUzJyMHMzcz -NxcHFSMHJyM1Jzc1MzcXMwc3FwExKUUqRSkPRIlGRmRHR2RGRmRHR2TEGRmrwMAqckdHZEZGZEdHZEZG -uU5OAAADAA8ADwHxAfEABwAPAB8AABIyFhQGIiY0FjI2NCYiBhQlFwcVIwcnIzUnNzUzNxcz3UYyMkYy -IGpLS2pLAStGRmRHR2RGRmRHR2QBVTJGMjJGo0tqS0tqfEdHZEZGZEdHZEZGAAACAA8ADwHxAfEABwAX -AAA2MjY0JiIGFAUVIwcnIzUnNzUzNxczFRfLaktLaksBK2RHR2RGRmRHR2RGgEtqS0tqEmRGRmRHR2RG -RmRHAAACAA8ADwHxAfEABQAVAAAlMjY0JiMXFSMHJyM1Jzc1MzcXMxUXAQA1S0s1q2RHR2RGRmRHR2RG -gEtqS8dkRkZkR0dkRkZkRwACACsAKwHVAdQAEQAdAAAlMjcXBiMiJjU0NjcVBgYVFBYTFhYVFAcnNjU0 -JicBAEgsOEBsWH1vUTZKV1NRbxI4Cko2azghV31YU3kIQAhUOD5XAWkIeVMwJyEcGjhUCAAEAFUAFQGr -AesADQATABkAJwAAJTUzFRQGIyMiJjU1MxU3Byc3FwcXJzcnNxclFSM1NDYzFzIWFRUjNQFrKhkR1hEZ -KkAeYmIeQ7ceQ0MeYv7qKhkR1hEZKmsqVREaGhFVKlEeYmIeRGIeREQeYpUqVREaARkRVSoAAAMAAABV -AgABqwADABMAIAAAJTUjFTcyFhUVFAYjIyImNTU0NjMlFTMVITUzNTQ2MyEVAdVVawkMDAmACQ0NCf7q -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 -AEMARwBLAE8AUwAAATUzFQc1MxUHNTMVAzUzFTczFSMHNTMVMzUzFQM1MxUnNTMVBzUzFSc1MxUnNTMV -BzUzFQc1MxUHNTMVBzUzFTM1MxUnNTMVBzUzFSc1MxUnNTMVAUArKysrK4AqgCsrqiqAKysrKysrK9Uq -1SsrKysrKysrK4AqKiqAKysrKysBlSsrqioqqysrAVUrKysrVSsrKyv/ACsrqyoqVisrVioqqisrVSsr -VSoqVisrVSsrKytVKytVKyurKiqqKysAAAMAAAAAAgACAAADAA0AEgAANSEVIQEHJzc2MhcXFhQHByM1 -NwIA/gABuipQKgYSBjIGRdZQ1lVVAaoqUCoGBjIGEkXWUNYAABEAQABAAcABwAADAAcACwAPABMAFwAb -AB8AIwAnACsALwAzADcAOwA/AEMAACU1MxUjNTMVJzUzFTc1MxUnMxUjBTUhFQc1MxU3NTMVAxUjNRcV -IzU3FSM1IxUjNSMVIzUTNTMVJzUzFTUVIzURNTMVAZUrgCuAKoArKysr/qsBgNUqgCurKioqgCuAKyor -VSuAKysrQCsrKytVKyurKyuAK6oqKqsrK1UrKwErKytVKytVKysrKysr/oArK1UrK9YrK/7VKysADQBA -AEABwAHAAAMABwATABcAGwAfACMAJwArAC8AMwA3ADsAACU1MxUHNTMVAxUzFSMVIzUjNTM1EzUzFRMz -FSMVNTMVJxUjNSMVIzUzFSM1AzUzFTUVIzUTNTMVIzUzFQGVKysrq6urKqurVSsqKysrVSvVK4ArVSsr -VSuAK5UrK1UrKwGAqyqrqyqr/oArKwGAK1UrK4ArKysrKyv+1Ssr1isr/tUrKysrABEAQABAAcABwAAD -AAcACwAPABMAFwAbAB8AIwAnACsALwAzADcAOwA/AEMAAAE1MxUHNTMVFzUzFSc1MxUnMxUjETUzFQc1 -MxUTNTMVAREzETc1MxUnNTMVAzUzFTc1MxUnNTMVJzUzFQM1MxUHNTMVAUArKysqKysrKysrK4ArKiv+ -gCsqKysrKysrKioqKioqKioqAZUrK6oqKqsrK6sqKtUr/wArK1UrKwEAKyv/AAGA/oCrKiqqKyv+qysr -qyoqVSsrVSsr/wArK1UrKwAHAEAAQAHAAcAAAwAHAAsADwATABcAGwAAExUjNRcVIzUXESERAyERISUV -IzUjFSM1NxUjNcArgCqq/tYrAYD+gAErKysqKioBFSoqVSsrVQEq/tYBVf6A1SoqKipWKysAABEAQABA -AcABwAADAAcACwAPABMAFwAbAB8AIwAnACsALwAzADcAOwA/AEMAABM1MxUnNTMVBzUzFTc1MxUDNTMV -EzMRIyc1MxUHNTMVJzUzFQc1MxUnNTMVFzUzFSM1MxU3NTMVJzUzFSM1MxUTNTMV6yoqKioqKysrKyor -K1UrgCrVKysrKyuAKtUrKisrK4ArKisBQCsrVSsrqioqqisr/qsrKwGA/oCrKipWKyurKyurKytWKiqr -KysrK6sqKqorKysr/qsrKwAIAEAAQAHAAcAAAwAJAA0AEQAVABkAHQAhAAABNTMVJSEVIREjJTUzFQc1 -MxUHNTMVIzUzFTM1MxUjNTMVAZUr/oABgP6rKwFVKysr1SqAK9UrgCsBQCsrgCv+q6sqKlYrK1UrKysr -KysrKwARAEAAQAHAAcAAAwAHAAsADwATABcAGwAfACMAJwArAC8AMwA3ADsAPwBDAAAlNTMVFzUzFQM1 -MxUTNTMVNzUzFQEhFSEFNTMVJzUzFQc1MxUnNTMVBzUzFQc1MxUnNTMVFzUzFSc1MxUjNTMVBzUzFQFA -Kyor1SorKyor/oABgP6AAVUrKyvVKtUrKysrKysrgCoqKoArKyvrKiqrKysBACsr/wArK1UrKwErK6oq -KlUrK6srK6srK1UqKqsrK1UrK1UrK6sqKioqqysrABEAQABAAcABwAADAAcACwAPABMAFwAbAB8AIwAn -ACsALwAzADcAOwA/AEMAACU1MxUHNTMVAzUzFRc1MxUnMxUjFTUzFQc1MxUjETMRNzUzFQE1MxUDNTMV -BzUzFSc1MxUzNTMVBzUzFQM1MxUHNTMVAUArKysrKyorKysrKysr1SqAK/7VK4ArKysrKyorKyuAKysr -6yoqqysrAVUrK1UrK4ArqioqqysrAYD+gFUrKwEAKyv/ACsrVSsrqyoqKiqrKysBVSsrVSsrAAUAQABA -AcABwAADAAcACwAPABMAABMhFSEXMxUjBzUhFQU1IRUlMxUjQAGA/oBV1tZVAYD+gAGA/tXW1gHAKyor -VSoqqysrgCsABQBAAEABwAHAAAMABwALAA8AEwAAEyEVIRU1IRUFNSEVBTUhFQU1IRVAAYD+gAGA/oAB -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 -ATMnJzMXERQGIyEiJjUTNDYBFXZ2lauAGhH/ABEaARkBQHUggP8AERkZEQFWERkAAAUAKwArAdUB1QAF -AA0AFQAdACUAACQiJiczBiYiJjQ2MhYUFiImNDYyFhQGMjY0JiIGFBIyFhQGIiY0ASVKOw3aDZ4aExMa -E4MaExMaE7GMZWWMZVOwfX2wfYspISFhExoTExoTExoTExrTZYxlZYwBG32wfX2wAAMAQABAAcAB6wAD -ABsAHwAAJTUhFRMzFTMyFhURFAYjISImNRE0NjMzNTMVMxcVIzUBlf7W6isVERoaEf7WEhkZEhUrqhZr -a+rqAYArGhH+1hEaGhEBKhEaKyvAa2sAAAMAKwCVAdUBawAPABMAIwAAATIWFAYjIzUzMjY0JiMjNQc1 -MxUkFBYzMxUjIiY0NjMzFSMiAWssPj4sVlYbJycbVmqq/v4nG1ZWLD4+LFZWGwFrP1g/KSc2JymAKiow -NicpP1g/KQAAAgBAAEABwAHAAAQAFAAANwchJwcXFAYjISImNRE0NjMhMhYVtUoBKmBK1RoR/tYRGhoR -ASoRGuBggGA1ERoaEQEqERoaEQAAAgB3AE0BiQG1AAgADAAAEzcXIxUHJzc1Eyc3F6BgYEuAHnSASR5J -AVVgYIiAHnN3/vhIHkgAAQArACsB1QHVAA0AAAERJyEiJjURNDYzITIWAdVV/tURGRkRAVYRGQGr/oBV -GhEBABEZGQAAAgBAAEABwAHAAAkADgAAAQcnNzYyFxcWFAU3FwcjAbonUCcGEgYyBv6A7FDsUAFqJ1An -BgYyBhLg7FDsAAACAGsAVQGVAasABgAKAAA3NxcjFSM1JyEVIWuVlVWAVQEq/tbVlpaAgNYrAAEAVQDA -AasBQAAHAAABMxUhNTMVIQGAK/6qKwEAAUCAgFUAAAIAQACAAcABwAAfADgAADcUMzI1NCYnIiYmIyM1 -IRUjFBYXFhUUBwYjIicmJyY1NzQjIgcGFRQXFhcjNCY1JjU0NzYzMhcWFcg+MQ0RAQUEAs0BgFMDAQdD -FRkQDyQUJ64zJAsDEBAOYgQIICIxNR4f6DYkEA4IAgIrKwEFARESPRMGAwcPHTJ4LRYGCA8LCgUBAwEN -FyUaGBscKQAAAgBVAEABqwHAAAMACgAANyEVISUHJzM1MxVVAVb+qgEAVVVAKmsrq1ZW1dUAAAMAVQAV -AasB6wADAAoAEQAAEyEVISUHJzM1MxUDNxcjFSM1VQFW/qoBAFVVQCpqVVVAKgEVKqpVVVZW/tZVVVZW -AAIAVQBAAasBwAADAAoAABMhFSEXNxcjFSM1VQFW/qpWVVVAKgHAK4BWVtXVAAADAFUAQAHAAZUAEgAW -ABoAAAEyFhQGIyMVJzcVMzI2NCYjITUlFSE1ETUzFQFrIzIyIytAQDARGhoR/uUBVv6qgAEVMkYyK0BA -KxoiGiqAKir+1ioqAAIAVwBAAakBwAAUACIAABMBBycGBxUjNSYmJzMWMzI3JyY1JxciByc2NzUzFRYW -FyMmcgE3Gy8TIUAhLAIvBDwlDktTSbQTDh8OEkAgIwEvAgGp/sgbMBEHLi4HKiEtFEoZO0khBh8HBS4v -CCwdLQACAFUAwAGrAUAAAwAHAAA3NSEVNRUhNVUBVv6qwCsrgCsrAAAIABUAFQHrAesAAgAKAA4AEgAe -ACIAJgA6AAA3MycXIwcjNzMXIzczNSMTNSMVJzUzNSM1IxUjFTMVBzUjFREVMzUFIxUzFSM1IxUjNTM1 -IzUzFTM1M+Q4HCVLDyNJHkgiYCsrKysqKirWKioqKysBgCsrgNaAKyuA1oDwUm0qwMDqK/6AKysrKtYq -KtYqKysrAYArK1XWgCsrgNaAKysABABLACsBtQHVAAMABwALABMAAAE3FwclNxcHNzMVIwc1IRUHFSM1 -AWotHi3+wx4tHnMqKmsBAECAAXQtHi4uHi0fgEDAa2tAamoAAAEAKwDLAdUBNQAbAAABMhYUBiMiJyMG -IicjBiMiJjQ2MzIXMzYyFzM2AaAWHx8WJA0+DUgNPg0kFh8fFiQNPg1IDT4NATUfLB8gICAgHywfICAg -IAAAAgBVAMABqwFAAAMABwAANzMVIzUhFSFV1tYBVv6q6yuAKwAAAgA1AGsBywGrAAcADwAAARUjFSM1 -IzUnIRUjESMRIwHLQEBA1gEWa0BrAUBAlZVAa0D/AAEAAAACACsAKwHVAdUAIgAqAAAlNjU0JyY1NDYz -MhczJic1IxUGBhUUFxYVFAYjIicjFhcVMwIyFhQGIiY0AR5DWjgbGC0CKgI7OR0lWTkXHDUEKgNDOXaw -fX2wfX4NNj0XDhsPFCg5DyoqBiMbOBYNIQ0VKDkPKgGAfbB9fbAAAQBrAGsBlQGrAAcAABMhFSMRIxEj -awEqdUB1AatA/wABAAAAAQArAIAB1QFrACUAADY0NjMzMhYUBiMjIiY0NjMzFSMiFDMzMjY0JiMjIgYU -FjMzFSMiK0Qx4CMyMiO1FiAgFqCiCQm3ERoaEeAfLCwfy8sxxGJFM0YyHywgKxUZIhosPisrAAEAAABV -AgABqwASAAABFhYVFAYjISImNTQ2NzY2MzIWAZ0pOj8s/us1S0IwFUwtOlgBKgM9Kiw/SzUxSQUnMEkA -AAIAKwArAdUB1QASABoAACUyNjQmIyM0JiMiBgcnIgYUFjMCMhYUBiImNAFgFh8fFgsyIx4tBwMaJiYa -A7B9fbB9qx8sHyMzJRwBJjQmASp9sH19sAAAAgAAAFUCAAGrAAUAGAAANzcnBycHJRYWFRQGIyEiJjU0 -Njc2NjMyFtWNHm8sHgESKTo/LP7rNUtCMBVMLTpYlY0ebiweSgM9Kiw/SzUxSQUnMEkAAgAAAFUCAAGr -AAYAGQAAJSM1IxUjFzcWFhUUBiMhIiY1NDY3NjYzMhYBa0BWQGudKTo/LP7rNUtCMBVMLTpY61VVa6oD -PSosP0s1MUkFJzBJAAMAAAArAgABqwAHABMAKwAAEyMiBhQWMzMBNwEHJyMiJjU0NjcFFhYVFAcnNjU0 -JiMjNTQmIyIHJzYzMhalJSMyMiPQ/vAbAWUbK/o1S0gzASIpOi0fISYaIEQxHxcgJy86WAErM0YyARAb -/psbKks1NEoCKwM9KjcgHxImGiYLMUQNHxlJAAACAAAAVQIAAasAEwAmAAAlMjY0JiMjNTQmIyIGByMi -BhQWMyUWFhUUBiMhIiY1NDY3NjYzMhYBlRomJhogRDEoPgsPIzIyIwEdKTo/LP7rNUtCMBVMLTpYgCY0 -JgsxRDAlM0YyqgM9Kiw/SzUxSQUnMEkAAAIAAABVAgABqwAGABkAACUzJwczFTM3FhYVFAYjISImNTQ2 -NzY2MzIWAStAa2tAVnIpOj8s/us1S0IwFUwtOljrampWlQM9Kiw/SzUxSQUnMEkAAAIAawBVAZUBwAAD -AAoAADchFSElByczNTMVawEq/tYBKpWVVYCAK+uVlYCAAAACAGsAVQGVAcAAAwAKAAA3IRUhNzUjNxcj -FWsBKv7WVVWVlVWAK1aAlZWAAAEAKwBVAdUBqwARAAATFzMyFhUVFAYjISImNRE0NjPVK6sRGRkR/qoR -GRkRAasrGhHVERoaEQEAERoAAAIAKwBVAdUBqwADABUAACU1IRUBMhYVFRQGIyEiJjURNDYzMxcBq/6q -AVYRGRkR/qoRGRkRgCuA1dUBABoR1REaGhEBABEaKwAAAwArAFUB1QGrAAcADwAhAAAlNTQmIgYVFTYi -BhQWMjY0NzIWFRUUBiMhIiY1ETQ2MzMXAZU7NDtmIhoaIhpAERkZEf6qERkZEYArlRYTFxcTFqsaIhkZ -IloaEdURGhoRAQARGisAAgArAFUB1QGrAAsAHQAAJTUjNSMVIxUzFTM1NzIWFRUUBiMhIiY1ETQ2MzMX -AZVAKkBAKlYSGBgS/qoSGBgSgCvVK0BAK0BAqxkS1RIZGRIBABIZKwAEABUAQAHrAcAABwAPABQAJwAA -EzIWFSM0JiMVMhYVIzQmIxUyFhUjATIWFREUBiMjNTMRIRUjNTQ2MxVhiitxTz5YKz8sGiZAAasRGhoR -lZX+gCsaEQErimFQcCtXPiw/KyYaAYAaEf7WERorASpAQBEaAAUAFQBAAesBwAASABoAIQApAC4AAAEy -FhURFAYjIzUzESEVIzU0NjMHMhYVIzQmIyUVIyYmJzUHMhYVIzQmIxUyFhUjAcARGhoRlZX+gCsaESth -iitxTwGAeBRfP1Y+WCs/LBomQAHAGhH+1hEaKwEqQEARGpWKYVBwa9Y/YBQjllc+LD8rJhoAAAIAAABV -AgABqwADABcAABMVITURMxUhNTMiJjU1NDYzITIWFRUUBlUBVlX+AFURGRkRAVYRGRkBgNXV/wArKxoR -1REaGhHVERoAAgAVACsB6wHVAAMAGQAAJTUhFQEyFhURFAYjIxcVIzU3IyImNRE0NjMBwP6AAYARGhoR -lSqqKpURGhoR1dbWAQAZEf8AERpAFRVAGhEBABEZAAIAFQArAesB1QADABsAACURIREBMhYVERQGIyMV -MxUjNTM1IyImNRE0NjMBwP6AAYARGhoRlSqqKpURGhoRqwEA/wABKhkR/wARGisqKisaEQEAERkABgAr -AEAB1QHAAAMABwALAA8AEwAvAAABMxUjJzMVIzczFSMHMxUjBREhESUjFTMVIxUzFSMVFAYjISImNRE0 -NjMhMhYVFTMBAFVVgGtrgFVVgGtrAQD+1QGAKioqKioaEf7VERkZEQErERoqARWA1mtrQEBWKgEq/tbV -KyorKyoRGhoRASoRGhoRKgADAIAAFQGAAesAAwATABcAACU1IxUTMhYVERQGIyMiJjURNDYzETUzFQFV -qqoRGhoRqhEaGhGqwNXVASoZEf7VERkZEQErERr+KisrAAAEACsAKwHVAdUABAAJAA4AEwAAATMVIycH -NxcVIwMXByM1JQcnNTMBYHV1QGBAQIAgQEB1ARVAQIABQIBAYEBAdQEVQECAIEBAdQAAAQBAAFUBwAHr -ABkAABIyFhUVFAYjIzUzNTQmIgYVFTMVIyImNTU0sKBwJhpAVVd8V1VAGiYB63FPlhomqys+V1c+K6sm -GpZPAAABAEAAFQHAAesAHQAAEjIWFRUUBiMjNTM1IzUzNTQmIgYVFTMVIyImNTU0sKBwJhqAlVVVV3xX -VUAaJgHrcU/WGiYrFasrPldXPiurJhqWTwAADAArAGsB1QGVAAMABwALAA8AEwAXABsAHwAjACcAKwA7 -AAABNSMVFzUjFSc1IxUXNSMVFzUjFSc1IxUXNSMVNxUzNScVMzUXFTM1JxUzNTcyFhUVFAYjISImNTU0 -NjMBlSoqKhYqKioqqhYqKipAKioqFioqKpYRGRkR/qoRGRkRASsqKkAqKkAqKkAqKlYrK5YqKkAqKioq -KkAqKkAqKkAqKkAZEdYRGRkR1hEZAAABAIAAuwGAAVkABQAAExc3FwcnnmJiHoCAAVliYh6AgAAAAQCr -AIsBSQGLAAUAACUHJzcXBwFJHoCAHmKpHoCAHmIAAAEAtwCFAVUBhQAFAAA3Nyc3Fwe3YmIegICjYmIe -gIAAAQCAALcBgAFVAAUAADcnNxcHJ54egIAeYrcegIAeYgABAEAAgAHAAYAACAAAARUhFwcnNxcHAcD+ -0kwegIAeTAEVKk0egIAeTQACAIAAgAGAAYkAAwAJAAA3NSEVJwcnNxcHgAEAgGIegIAegCsrzWIegIAe -AA0AKwAVAdUBwAACAAYACgAOABIAFgAaAB4AIgAmACoALgA+AAAlJzM3NSMVFzUjFSc1IxUXNSMVFzUj -FSc1IxUXNSMVNxUzNScVMzUXFTM1JxUzNTcyFhUVFAYjISImNTU0NjMBAFWqQCoqKhYqKioqqhYqKipA -KioqFioqKpYRGRkR/qoRGRkRFVbqKytAKytAKytAKytVKyuVKytAKysrKytAKytAKytAKytAGhHVERoa -EdURGgABACsAgAHAAYAACgAAATMVIRcHJzcXByEBlSv+vE0egIAeTQEZAWuATR6AgB5NAAACABUAgAHV -AYAAAwAMAAABMxEjJzcXByc3ITUhAasqKrQegIAeTf7RAS8BgP8A4h6AgB5NKgACAGsAKwGVAcAADwAb -AAABMxQGBxUjNSYmNTMUFjI2BiImNTU0NjIWFRUUAXEkSzUqNUskQ1xDVzQmJjQmAQA2UQhGRghRNi8+ -PhEmGoAaJiYagBoAAAIAAABVAgABqwADABcAABMVITURMxUhNTMiJjU1NDYzITIWFRUUBlUBVlX+AFUR -GRkRAVYRGRkBgNXV/wArKxoR1REaGhHVERoAAwAAAFUCAAHAAAMABwAPAAAlNSEVFzUjFSEzFSE1MxEh -Aav+qtZWAQAr/gArAarA1dVAFRUrKwFAAAMAAABVAgABwAAHAAsAIwAANjI2NCYiBhQDFSE1ETMUBiMh -IiY1MyImNTU0NjMhMhYVFRQG9xIMDBIMlgFWVRoR/lYRGlURGRkRAVYRGRlrDBIMDBIBHurq/usRGhoR -GhHqERoaEeoRGgACAAAAVQIAAcAAAwAZAAATFSE1ETMVITUzNSImNTU0NjMhMhYVFRQGI1UBVlX+AFUR -GRkRAVYRGRkRAZXV1f7rKysVGhHVERoaEdURGgAEAEAAQAHAAcAAAwAzADcAOwAAJTUjFSUjFTMVIxUU -BiMjFSM1IxUjNSMiJjU1IzUzNSM1MzU0NjMzNTMVMzUzFTMyFhUVMwc1IxU3FSM1AWvWASsrKysZESsr -KisrERkrKysrGRErKyorKxEZK6sqVYCV1taAKisrERkrKysrGRErKyorKxEZKysrKxkRK1UqKlWAgAAD -AFUAFQGrAekABAAMABEAABMVIzQ2AzUhFRQGIiYTFhYVI+uWV1cBVmWMZcA/V5YB6alBYP7fVVVGZWUB -bwhgQQADAGsAFQGVAesAAwAHABcAACURIxEXNSMVEzIWFREUBiMjIiY1ETQ2MwFw4JtWgBomJhqqGiYm -GoABK/7VQBUVAasmGv6qGiYmGgFWGiYAAAMAawAVAYAB6wADAAsAGwAAJREjERYyNjQmIgYUEzIWFREU -BiMjIiY1ETQ2MwFVwFMaExMaE3YWHx8WqxYfHxaAASv+1VUTGhMTGgGtIBb+lhYgIBYBahYgAAMAAABV -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 -FhUVFAYjISImNTU0NjMBkhwSEhwSQhoTExoTQEArQEArARURGhoR/oARGhoRAQASHBISHFISHBISHBkq -QEAqQECVGhGqERoaEaoRGgADACsAKwHVAdUACwAbACQAAAE1IzUjFSMVMxUzNTcyFhURFAYjISImNRE0 -NjMHESEVISImNREBlVUrVVUraxEZGRH/ABEaGhFWASv+1REZARUrVVUrVVXAGRH/ABEaGhEBABEZVf7V -KhkRASsAAAMAKwArAdUB1QAHAA8AFwAAABQGIiY0NjIGMjY0JiIGFBIyFhQGIiY0AUAmNCYmNGCMZWWM -ZVOwfX2wfQEaNCYmNCbrZYxlZYwBG32wfX2wAAACAEAAFQHAAdUABwAaAAAlNycnBwcXFxMyFhURFAYj -IwcnIyImNRE0NjMBKFhYKChYWCiVERoaEVVAQFURGhoR7SgoWFgoKFgBQBkR/tURGkBAGhEBKxEZAAAB -AGsAQAGrAasACQAAATMVIycjFSMRMwEzeJYIeCrAAYDVKpUBawAAAQCAAEABlQHAAA8AAAEzFSMVIwYG -IyImNDYzMhcBAJVVAQQ2JSg4OCgRDwHAQOskMThQOAYADgArACsB1QHVAAcADwAXAB8AJwAvADcAPwBH -AE8AVwBfAGcAbwAAJDIWFAYiJjQXMhUUIyI1NAYyNjQmIgYUEjIWFAYiJjQFMhUUIyI1NBcyFRQjIjU0 -JyI1NDMyFRQGMhYUBiImNCciNTQzMhUUBzIVFCMiNTQXMhUUIyI1NCcyFRQjIjU0FjIWFAYiJjQ2MhYU -BiImNAEiEgwMEg0WCgoLZoxlZYxlU7B9fbB9AUAKCgsLCgoLNQsLChMSDAwSDUAKCgtLCwsKSgsLCjYL -CwpBEg0NEgwMEg0NEgzrDRIMDBI+CwoKC0tljGVljAEbfbB9fbAjCgsLClULCgoLgAsKCgsgDBINDRIs -CwoKC4ALCgoLQAsKCguVCgsLCkoNEgwMEmIMEg0NEgAADgBAAEABwAHAAAcADwAXAB0AJQApADEAOQBB -AEkATQBVAF0AZQAAJCImNDYyFhQmIiY0NjIWFCYiJjQ2MhYUFyI0MzIUJyI1NDMyFRQlIRUhBSI1NDMy -FRQGIiY0NjIWFCYiJjQ2MhYUJiImNDYyFhQDNSEVAiImNDYyFhQGIiY0NjIWFAYiJjQ2MhYUAR4SDAwS -DQ0SDAwSDQ0SDAwSDUALCwoKCwsK/ssBgP6AASsLCwqsEgwMEgxdGhMTGhMTGhMTGhNLAYD3EgwMEgwM -EgwMEgxdGhMTGhOVDRIMDBJJDBIMDBJJDBINDRJXFhZWCgsLCnUr9QsKCgsLDRIMDBI+EhwSEhxDExoT -Exr++CsrAQAMEg0NEmEMEgwMEmwTGhMTGgAAEgA1ADUBywHLAAUADQATABkAIQAnAC8AVABaAGIAagBy -AHoAgACGAIwAmACgAAA2MhUUIjUWMhYUBiImNBcyFCMiNCYyFRQiNRYyFhQGIiY0JDIVFCI1BjIWFAYi -JjQDNwEHJxYVFAYiJjQ2MzIXJwYGIyImNTQ2NycWFRQGIiY0NjMXFzIUIyI0EiImNDYyFhQGIiY0NjIW -FAYiJjQ2MhYUJiImNDYyFhQWIjU0MhUnIjQzMhQzIjQzMhQHJiYnNTQ2MhYUBiM2IiY0NjIWFDUWFkIS -DAwSDGoLCwqWFhZCEgwMEgwBShYW6RINDRIMixsBWxxQAQwSDQ0JBAI8AhIMDRMQCzwBDBIMDAkGpQoK -C2kSDAwSDAwSDAwSDAwSDAwSDLcSDAwSDeAWFvYKCgtLCwsKDwoQARMaExMNCRINDRIM4AsKCkAMEgwM -Ej4WFuoKCwtADRIMDBICCwoKQAwSDAwSAQcb/qUbUQIECQwMEgwBPAsQEw0MEgI8AgQJDQ0SDAH0FhYB -IAwSDAwSYg0SDAwSYgwSDQ0SnwwSDAwSVwsKCooWFhYWqgEQCgUNExMaE2AMEgwMEgAAGAA1ADUBywHL -AAcADwAXAB8AJQAtADMAOwBDAEsAUwBbAGMAaQBvAHUAewCDAIkAkQCXAJ8ApwCvAAAAMhYUBiImNBYy -FhQGIiY0BjIWFAYiJjQ2MhYUBiImNBcyFCMiNDYyFhQGIiY0NjIVFCI1JjIWFAYiJjQWMhYUBiImNBYy -FhQGIiY0NjIWFAYiJjQmMhYUBiImNDYiJjQ2MhYUJyI0MzIUAzIUIyI0JjIVFCI1NyI0MzIUBiImNDYy -FhQWIjU0MhUkMhYUBiImNAYyFRQiNTYyFhQGIiY0FjIWFAYiJjQ2MhYUBiImNAEeGhMTGhMTGhMTGhM/ -Eg0NEgwIGhMTGhN2CgoLAhIMDBINoBYWPhIMDBIMDBIMDBIMDBIMDBIMDBIMDBIMoxoTExoTKRIMDBIN -FgoKCwsLCwqWFhb2CwsKARINDRIMixYW/qwSDAwSDDYWFkISDAwSDAwSDAwSDAwSDAwSDAFLExoTExpD -ExoTExpNDBIMDBLCExoTExrtFhZKDBIMDBJXCwoKwAwSDAwSSQwSDQ0SnwwSDAwSYg0SDAwSFxMaExMa -iQwSDAwSPhYW/pYWFpULCgrgFhZKDBIMDBJXCwoKagwSDAwSVAoLCxUMEg0NEp8MEgwMEmINEgwMEgAB -ACsAKwHVAdUABwAANjQ2MhYUBiIrfbB9fbCosH19sH0AAQBrACsBqwHVAA0AABMyFhQGIyInNjY0Jic2 -1Vl9fVk6MDE5OTEwAdV9sH0cHGN0YxwcAAEAgAArAZUB1QANAAATMhYUBiMiJzY2NCYnNsBYfX1YIh5C -U1NCHgHVfbB9CRRxjnEUCQACAA8ADwHxAfEADQAdAAAlMjY0JiMiBxYWFAYHFjcXBxUjBycjNSc3NTM3 -FzMBADVLSzUaGyEpKSEbxUZGZEdHZEZGZEdHZIBLaksMDz9MPw8Mx0dHZEZGZEdHZEZGAAACAA8ADwHx -AfEABwAXAAA2MjY0JiIGFAUVIwcnIzUnNzUzNxczFRfLaktLaksBK2RHR2RGRmRHR2RGgEtqS0tqEmRG -RmRHR2RGRmRHAAACAA8ADwHxAfEABQAVAAAlMjY0JiMXFSMHJyM1Jzc1MzcXMxUXAQA1S0s1q2RHR2RG -RmRHR2RGgEtqS8dkRkZkR0dkRkZkRwADAA8ADwHxAfEABwAPAB8AABIyFhQGIiY0FjI2NCYiBhQlFwcV -IwcnIzUnNzUzNxcz3UYyMkYyIGpLS2pLAStGRmRHR2RGRmRHR2QBVTJGMjJGo0tqS0tqfEdHZEZGZEdH -ZEZGAAACAEAAQAHAAcAADQAbAAABFxUUBiMhIiY1NRc3FzcVJwcnByc1NDYzITIWAYBAGhH+1hEaQFVW -lUBVVlVAGhEBKhEaAQxAYREaGhGMQFZW3oxAVlZWQWERGhoAAAIAKwBAAcABwAAJABYAAAEWFAcHJzc2 -MhcEMhYVFAYjIicyNjU0AboGBr87vwYSBv7eNCYyIzQhDxsBnQYSBr87vwYG5SYaIzIrFxMaAAYAKwAr -AdUB1QAHAAwAEgAZAB4AJQAANzY2NxcGIyInMwcmJjcXIyY1NAUWFRQHJyc3IzcWFgcHJzYzMhfSBE4V -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 -JjURAcD+1QErKyorKyorERoaEf7VERkZEVYVFUAaEUBAFREaGhEVERrrAVX+qxEaAUCA/tWAKiorKyur -GhH+1REZGREBKxEaqxVVEhkrFRkSFRIZGRJA/qsrGhEBVQADAEAAQAHAAcAAAwATABYAACURIxU3MhYV -ERQGIyEiJjURNDYzFwczAZWVlREaGhH+1hEaGhGVlZVrASqAqxoR/tYRGhoRASoRGquqAAAFAEAAQAHA -AcAABwAQABkAIgArAAASMhYUBiImNBc1MxUUBiMjNRMyFhUVIzUjNQcVIzU0NjMzFQcVMxUjIiY1NeY0 -JiY0JtUrGhFVVREaK1XVKxoRVVVVVREaAUAmNCYmNK9VVREaKwFVGhFVVSsrVVURGivVVSsaEVUAAgAA -AFUCAAGrABcAKgAAJTI2NCYjIzU0JiMiBxYWFSM0JiIGFBYzJRYWFRQGIyEiJjU0Njc2NjMyFgGVGiYm -GiBEMTokKTUrMkYyMiMBHSk6Pyz+6zVLQjAVTC06WIAmNCYLMUQvC0UsIzMzRjKqAz0qLD9LNTFJBScw -SQAAAwArACsB1QIAAAMACgAdAAABIRUhFxEjJwcjEQEyFhURFAYjISImNRE0NjMzNxcBgP8AAQArYEpL -YQFWERkZEf6qERkZEVZVVQFV1SsBK0tL/tUBVhoR/tURGRkRASsRGlVVAAEAFQCAAesBgAAGAAABEyE3 -FzcnASvA/iqAYCI8AYD/AKuAGVEAAwAVABUB6wHrAAMAEwAcAAAlESERATIWFREUBiMhIiY1ETQ2MwcR -IRUhIiY1EQHA/tUBKxEaGhH+1REZGRFVAVX+qxEalQEr/tUBVhoR/tURGRkRASsRGlb+qysaEQFVAAAJ -ACwALAHUAdQABQALABEAGQAfACUAKwAxADcAADc3FhcVJjc2NxcGBzc2NzMGByYUBiImNDYyBxYXByYn -NwYHIzY3BSYnNxYXJwcmJzUWBwYHJzY3eR4lLz9pLyQfMz9yHAYrBillJjQmJjTDBhweKQZNHAYrBikB -TgYcHikGTR4lLz9pLyUeMz9bHhwGKwYlBhweKQZrJi0+M6E0JiY0JlUvJB8zP34lLz8zci8lHjM/kB4c -BisGJQYcHikGAAACADEAEQHPAe8ABwBAAAA2MjY0JiIGFAc0NjcnJiY1NhcWFyY1NDY3FhYVFAc2NzYX -FAYHDgIHHgIXFhYVBicmJxYVFAYHJiY1NDcGBwbdRjIyRjJ6LSUSHiJAQAkIAiMdHSMCCAlAQCIeAgcG -AwMGBwIeIkBACQgCIx0dIwIICUCrMkYyMkZUJkUMCRI7ISUlBAcOBiM7ERE7IwYOBwQlJSE7EgEEAwEB -AwQBEjshJSUEBw4GIzsRETsjBg4HBCUAAAkAFQAVAesB6wADAAcACwATABcAGwAfACMAJwAANzUzFSc3 -Fwc3NxcHJjIWFAYiJjQXMxUjNwcnNycVIzUHByc3BxUjNesqnS4eLqYeLh6ENCYmNCargIAdLh4uVSon -Hi4eAYAVgICBLh4uLh4uHsgmNCYmNAUqfy4eLmOAgJEeLh5zKioAAwBAABUB2gHVAAIACgARAAABMyc3 -FyMnIwcjNyEzBzMDNSMBZzIZFUUpD0QPKUX+1dVVVZVAAV1OKsArK8DA/wDAAAACACsAKwGVAdUABQAO -AAABByc1MwcnAQcnBzUjNScBayG11lbPAU8bWE1AagErObUuqpX+sBtZg8BPawABAJUAKwFrAdUABgAA -EzMHMwM1I5XWVlaWQAHVqv8AwAAACQBAABUBwAHrAAQACAAMABAAFAAZACcAKwAvAAAlNTMUBic1MxUn -NTMVEzUzFQcRMxETMhYVIyE0NjMzFSMRMxUjIiY1JTUzFQM1MxUBlSsaESuAKyor1SqAERor/qsaEVVV -VVURGgFVK4ArQCsRGqsqKqorK/8AKyuAAdb+KgGrGhERGiv+1isaEdUrK/8AKysAAAoAQABAAcABwAAP -ABMAFwAbACsALwAzAD8AQwBHAAABNSEVMxUzFTM1MxUzNTM1FTUjFSM1IxUjNSMVATIWFREUBiMhIiY1 -ETQ2MxczFSM3MxUjJzMVMxUjNSMVIzUzFxUzNSEjFTMBlf7WKisrKisrKysqKysBABEaGhH+1hEaGhEq -KyurKytVKisrKisrgCr/ACoqARWAgCorKysrKpUrKysrKysBQBoR/tYRGhoRASoRGoArKysrKyoqKioq -KysrAAAIAFUAVQGrAasABwAPABcAHwAnAC8ANwA/AAASMhYUBiImNBYyFhQGIiY0FjIWFAYiJjQGMhYU -BiImNDYiJjQ2MhYUBDIWFAYiJjQ2MhYUBiImNBYyFhQGIiY0xCIaGiIZbyIZGSIabyIaGiIaOyIZGSIa -kSIaGiIa/sQiGhoiGhoiGhoiGm8iGhoiGQGrGiIaGiI8GSIaGiI8GiIZGSI7GiIaGiLEGiIaGiLEGiIa -GiLEGSIaGiI8GiIZGSIACQAAAAAB5QHlAAIABwAMABAAEwAWACAAJAA9AAAlMycHNScjFSc1JyMVFzUj -FREVMxcVMwMBBychIiY1EScFFTM1ISMnITIWFREnNSMnMzUjFSc1IyczNSMVJwFVHx8qDEoqDEpWVh9h -H9kByhsr/rYRGSsBVVb/AB8rAUoRGSofK0pWKh8rSlYqVR8fSgxWgEoMVoBWVgEfH2EfARD+NhsrGREB -SisfVlYqGRH+tisfKlZKKx8qVkorAAAKACsAKwHVAdUAAwAHAAsADwATABcAGwAfACMAMwAAATUjFRc1 -IxUXNSMVAzUjFRc1IxUXNSMVAzUjFRc1IxUXNSMVATIWFREUBiMhIiY1ETQ2MwGrVlZWVlYqVlZWVlYq -VlZWVlYBVhEZGRH+qhEZGREBVVZWgFZWgFZWAQBWVoBWVoBWVgEAVlaAVlaAVlYBgBkR/qoRGRkRAVYR -GQAABAAeAB4BzQHLABQAHQAhADAAABMWAQcnIzUnFSM1IxUjNTMVMzUzJxcjJzMyFhUVJzcVMzUHIyc1 -MzIWFRUUBxcjJyM1YgE2GKJIICArICArCHX3CCAoDRMgYCsrCBhLDRMTEyATGAHLY/7NF6JJIGk1NYAr -K3WVIBMNKSAJFRVgF2kTDRUXBy0rAAAFAEAAwAHAAUAAAwANABkAHQArAAAlNSMVNzIWFRUUBiMjNQc1 -MxUjNSMVIzUzFQU1IxUzFAcXIycjFSM1MzIWFQEVKioNExMNSkAgICsgIAFAK0sTEyATGCBLDRPgQEBg -Ew1ADROAKyuANTWAKwoVFRQKLSsrgBMNAAMAFQCAAesBgAAHAA8AFwAANjI2NCYiBhQ2MhYUBiImNCQy -FhQGIiY0WiIZGSIaCEYyMkYzASFqS0tqS9UaIhoaImYyRjIyRl1LaktLagAAAwAVAIAB6wGAAAcADwAX -AAAkMjY0JiIGFDYyFhQGIiY0JjIWFAYiJjQBSEYyMkYzIWpLS2pLo0YyMkYzqzJGMjJGo0tqS0tqIDJG -MjJGAAAHACoAKgHVAdUAAwALABMAGwAfACcATAAAJTcnBzYiBhQWMjY0BjI2NCYiBhQmMjY0JiIGFCc3 -JwcWIgYUFjI2NBcXFhQHBwYjIicnBwYiJycmNDc3JyY0Nzc2MhcXNzYyFxcWFAcBY05OTR4SDQ0SDEkS -DAwSDB8SDQ0SDCRNTU67EgwMEgxlVQYGXAYJCgZUVQYSBl0GBlVVBgZdBhIGVVQGEwZcBgZOTk1OegwS -DAwSSQwSDQ0SHwwSDAwSH05NTiMMEg0NEjRVBhIGXQYGVVUGBl0GEgZVVAYTBlwGBlVVBgZcBhMGAAAC -AEAAQAHAAcAABAAUAAA3ByEnBxcUBiMhIiY1ETQ2MyEyFhW1SgEqYErVGhH+1hEaGhEBKhEa4GCAYDUR -GhoRASoRGhoRAAAGACsAVQHVAasAAwATABcAGwAfACMAACURIREBMhYVERQGIyEiJjURNDYzFxUjNSMV -IzUXFSM1NxUjNQGr/qoBVhEZGRH+qhEZGRGrKyor1SoqKoABAP8AASsaEf8AERoaEQEAERqAKysrK1Yq -KlYrKwAEAEAAQAHAAcAAAwAGABIAIgAAJSM1MxcRATcVMxUzNTM1IzUjFTcyFhURFAYjISImNRE0NjMB -a2trKv7WCisgKysg9REaGhH+1hEaGhGVIEoBKv7W9SArKyArK2AaEf7WERoaEQEqERoAAQAVAIAB6wGA -AAYAAAETITcXNycBK8D+KoBgIjwBgP8Aq4AZUQAGAEAAQAHAAcAABwAMABQAHAAkACkAACU0NjMVIgYV -MzQ2MxUjNDYzFSIGFQMUBiM1MjY1MxQGIzUyNjUjFAYjNQErVz4sPysmGuuKYVBwK1c+LD+AimFQcIAm -GkA+Vyo/LBomQGGKK3BQAYA+Vyo/LGGKK3BQGiZAAAUAQABAAcABwAAFAAsAEQAtADMAAAE2MxUiBxcn -NjMVIgMUByc2NQc3AQcnBhUjNDcnBhUjNDcnBiM1MjcnBiM1Mjc3FAcnNjUBSDk/LypCIhseDIkiHxbA -GwFlGz0TKh8fKys4NUJWRTUfKTIiG1gLIgMBCSIrFkIiCyoBFT85HyovGxv+mxs9GyIyKR41RFZCNTgr -Kx8fKhNYHhsiCwwAAAEAKwArAdUB1QAHAAASMhYUBiImNKiwfX2wfQHVfbB9fbAAAAIAQABAAcABwAAY -ACgAAAE1NCYjIxUzFSMVMxUjFTMyNjU1NCYjMjY3MhYVERQGIyEiJjURNDYzAUAZEVZWKytWVhEZEg4O -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 -jGVTsH19sH0BCUxKaiZaVWWMZWWMARt9sH19sChMaEwmWoAAAAQAAAB+AgABggASAB4AVQBcAAAlNTQn -JiMiBwYVFRQXFhYyNzY2JzQzMhcWFRUUIyI1JSIVFB4FMxYXFhcWFxYXFhUUBwYjIicmNTMUMzI1NC4D -JyYnJjU0NzYzMhYVIzQnJiU3MxEjNQcBEwoHFwoIFwIBECwIBgR7UykTFlJTARweAQICBAIHAQkKCRMI -DggFBSwMEjkRBSkmIQYGDiMGCAwTKgwRISoqEgz+SWUGK0DmNiQRDwMONDUYCQUgEAwSR20YHDkpbm41 -FwMFAwMCAQMEAQIGAwkFCwoLJQ8EJwwKHhcGCQMGCQIDCA0XJg4EHh0VBAM6Jf8AzRYAAgBZAH4BwgGC -ADgAXgAAASIVFB4EMhYzFhcWFxYXFhcWFRQHBiMiJyY1MxQWMzI1NCYmJyYnJicmNTQ3NjMyFhUjNCcm -BxYVFAcGIiY1MxQXFjMyNCMjNTMyNzY1NCMiBwYVIzQ3NjMyFhQBdh8BAQMCBAIGAQkJCxIIDgcGBSwM -EzgRBSgWESEGBgcFEBwUEioMESEqKhMMoSoYF0ovKwsNESouGhkgCQImGgoDKjARECYqASEXAwQDAwED -AwQBAgYDCQULCgslDwQnDAoPDxcGCQMDAgQGDQwYJg4EHh0UBQMeEColEhQmIhQIClIhFwULJhQGCS0T -BSRIAAAEAEAAKwHAAesABwAWABoAHgAANjI2NCYiBhQlFhUUBiImNDYzMhc3FhcHNTMVExUjNcJ8V1d8 -VwErKnCgcHBQQTceEA7JKiuAVVh8V1d8tTVCT3FxnnErHw0RrICAARYrKwAABQAlABUBwAHrAAcAEwAX -ABsALwAAJTI3JwYVFBYDAQcnBiMiJjU0NycXNTMVNxUjNRcXBxYVFAcnNjU0JiMiByc2MzIXAQAnJMwU -V4IBexs2MjhQcCA7xiorgNYeHiogHxRXPigiIDA6QzVVFcwiKT5YAVb+hRs2IHFPOjA7WR5J3ysrTB4f -NUI6MB8iKT5XFB8gKgAACAArACsB1QHVAAQACQANABIAFwAbACEAKQAAJTY3IxUXNjcjFRU2NyM1FTMm -JycVMyYnJxUzJgMRBgYUFgIyFhQGIiY0AaUCApRwDAN/IB4+lAICkH8DDHA+Hko/V1cEsH19sH3VBhAW -QBEFFj4EEMAWEAZAFgURPhQQ/rIBUghggmABdn2wfX2wAAACACsAFQHVAesACAAfAAATNTMyFhUVIzUX -IxUzByczNSMiJjU1IzUzNSM3FyMRIdWAERorgFUrQEAqqhEaVVUrQEAqASoBVSsaEYCA1StAQCsaEaor -K0BA/wAAAAYAQABAAcABwAAHAAsAEwAbAB8AIwAAATUzFTMVIxUXIzUzJTMVIzUjNTMXIzUzFTMVIwMz -FSMVMxUjAUArVVVV1dX+1SsrVVWAKiqrq9XV1YCAAUCAKyorVSorgCsq1YArKgEqKtYqAAAMAEAAawHV -AZUAAwAHAAsADwATABcAGwAfACMAJwArAC8AAAEzFSMVNTMVIzUzFSM1MxUjNTMVNzUzFSczFSMjNTMV -FzUzFSM1MxUjNTMVJzUzFQGAVVVVwFbAVcBV61XAVlZqVRVWwFXAVVVVAZVV1VVVVVVVVVVValZWwFVV -VWtWVlZWVlZrVVUAAAMAQABrAdUBlQADAAcACwAAEyEVIRc1IRUhNTMVQAGV/muVAQD+a4ABlYCqlZWV -lQAAAwAAAFUB/AGrAAcAHgAhAAA3MycjBzM3MyUzByMnByMnBgYjIiY0NjMyFzMXNzMXJTcX3ChEK0Qp -D0QBCCcsJSAgJgIVUjFHZGRHUjMQGiAiIP7WGRirwMAqlsCCggkrNGWMZUCHh4cOTk4AAQAAAFUCAAGr -ABIAAAEWFhUUBiMhIiY1NDY3NjYzMhYBnSk6Pyz+6zVLQjAVTC06WAEqAz0qLD9LNTFJBScwSQAABgAV -ACEB6wHgAAMABwAVABkAHQAhAAAlNxcHNzMVIycWFhUUBiImNTQ2NzUzBxUjNRc1MxUnNxcHAXAeJh4V -QEBrHSNLaksjHYDrQNYqySYeJn0dJh7KK4QROiM1S0s1IzoRZ8ArK/8/P1MnHicAAAkAFQAhAesB9AAD -AAcADwATABcAGwAfACMAJwAANzcXBxc1MxUCMhYUBiImNAUzFSMHNxcHEwcnNycVIzUHFSM1NwcnN0wm -HiaBKkpqS0tqSwErQEA7HiYeHiYeJoEqlkB7HiYedCceJzU/PwFqS2pLS2ogK3gdJh4BSyYeJjU/P9Qr -K3keJh4AAAMAKwArAdUB1QAEABQAHQAAATUjFTc3MhYVERQGIyEiJjURNDYzBxEhFSEiJjURAatrNTYR -GRkR/wARGhoRVgEr/tURGQEAq6sgtRkR/wARGhoRAQARGVX+1SoZEQErAAIAFQBAAesBwAAEABQAADch -JwcnJTIWFREUBiMhIiY1ETQ2M2sBKmBKNgELEBsbEP6AERobEJWAYEDLGxD+1hAbGhEBKhAbAA4AFQBA -AesBwAAEAAsADwATABgAHAAgACQAKQAtADEANgA6AD4AADczJwcnJyEVISImNRMzFSM3MxUjJxUjNDYB -MxUjETMVIwUzFSMlMhYVIxUzFSMnMxUjExQGIzU1MxUjFTMVI0DVRDUnYAEr/wARGlYqKlUrK4ArGwE7 -KioqKv6qKysBqxAbKysrqysr1hsQKysrK2tbRS5m1RoRAVUrKysrKxAb/qsrAYArKiuAGxAqK4Ar/tYQ -GyuqKisrAA8AFQBAAesBwAADAAcACwAQABQAGAAcACAAJwAsADAANAA5AD0AQQAAExUjNTMVIzUHFSM1 -NxUjNDYBFSM1ExUjNQMVIzUDFSM1EyImNTUzFRMyFhUjFxUjNScVIzUTFAYjNTcVIzUXFSM1lSqAK4Ar -KysbAWUqKiorK9UrKxEa1tUQGysrK4Ar1hsQKysrKwHAKysrK6sqKqsrEBv+qysrAVUrK/6rKysBACsr -/tUaEVWAAYAbECorK1UrK/6rEBsrqioqVSsrAAACABUAQAHrAcAABwAXAAA2MjY0JiIGFCUyFhURFAYj -ISImNRE0NjO5jmRkjmQBaxEaGhH+gBEaGhGAS2pLS2r1GhH+1hEaGhEBKhEaAAAHAEwAIQG0AfQAAwAH -AAsADwATABcAGwAANyc3FwM3FwcBByc3ByM1MxMXBycnMxUjBzUhFWoeJh5EHiYeAUIeJh55KiqBHiYe -hSoqgAEqVh8mHgEkHiYe/vkeJx56PwFfHiYeWz/qgIAABAAAAAACAAIAABAAGQAlADEAADchFSMVIzUj -IiY1NSM1MzUzFzUjNTMyFhUVAzIWFyMmJicHJzI2AzcXIgYjIiYnMxYWqwEAKyuqEhkrKyuqgIARGn9k -kwggBkQ1HVEDCF8dUQMIA2STCCAGRKsrKysaEaorK9aAKxkSgAEriGM8YBkcUQH+NhxRAYhjPGAABQAr -ACsB1QHVAAgAEAAkACwANAAAATIWFTM0JiMVAjI2NCYiBhQ3MxUUBiMhIiY1ETQ2MzM3MxUyFic1MhYV -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 -FxgkIDwgIBgXIQQrLv5WLisrorvV1QoMEg0NEokNEgwMEq2TJhoaJpMXIRgIICAIGCEBLhBELf7oARgt -RBARlWszEgwMEg0NEgwMEg0AAAUAVQBAAasB1QAHAAsADwAXADAAACQyNjQmIgYUJzM1Iwc1IxUWMjY0 -JiIGFBMyFhUVFAYjFxUjJyMHIzU3IiY1NTQ+AgFSHBISHBIra2sqaxIcEhIcEoBTWCwfICsqUSowIB8s -HDU1lRMaExMag1VVVVWWExoTExoBLSIzyx8rIAsrKwsgKx/LGyQQBgADAGsAKwGVAdUAAwALACoAACU1 -IxUWMjY0JiIGFDcUBiMzFxUjJyMHIzU3JiY1NTQ2NzcjNTMVIwcWFhUBa9ZdHBISHBK1HRoCICsqUSow -IhcgRDsRZtZGED9B1WtrYBMaExMaDxwmIAoqKgoiBSQXtCkfAiAgICACHioABAArABUB1QHgABkAIQAo -AC8AABMDMzcXFTM1JzcWMzUiJicnJiMiBwcVMzU3NiImNDYyFhQTNRcHNSM1NzMVIxUnN3s7LSUuKywN -LUcdMQ8UCxkJB3AqJmEiGhoiGas1NXU1dXU1NQFC/tOrK4ChLEA3Kh0YIhQDLmRHEEkZIhoaIv63JTU2 -JiBaICU1NgAACQBVAFUBqwGrAAMABwALAA8AEwAXABsAHwAjAAAlNTMVJzUzFSc1MxU3MxUjBzUzFSM1 -MxUHNTMVMzUzFQM1MxUBVVZWVtZWKlZWgFbWVlZWKlbWVlVWVoBWVoBWVlZWgFZWVlaAVlZWVgEAVlYA -AAEAVQBVAasBqwAIAAABFSEXByc3FwcBq/78dx6rqx53ARUqeB6rqx54AAEAlQDAAWsBKwACAAATMweV -1msBK2sAAAIAKwArAdUB1QACAAoAACU3IyYyFhQGIiY0AQBVqgOwfX2wfdVWqn2wfX2wAAABAJUA1QFr -AUAAAgAANzcXlWtr1WtrAAABAFUAVQGrAasACAAAARcHJzchNSEnAQCrqx53/vwBBHcBq6urHngqeAAC -ACsAKwHVAdUACwATAAAlJzcnBycHFwcXNxcCMhYUBiImNAFrTU0eTU0eTU0eTU2lsH19sH2zTU0eTU0e -TU0eTU0BQH2wfX2wAAEASQBrAcABiQAFAAA3NxcBJzfA4h7/AHcep+Ie/wB3HgABAKsAgAFJAYAABQAA -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 -AAwAKwBAAdUBwAADAAcAEwAXABsAHwAjACcAKwAvADMAOQAAJRUjNTcVIzUXNSMVMxUjFTMVIxUDNSMV -FzUjFRc1IxUXNSMVAzUjFRc1IxUXNSMVFzUjFRMzESERMwGAKysrVqsrKysrKyoqKioqKiorKysrKysr -K6vV/lbVwCsrVSoqqtUrKisrKgEAKipWKytVKytVKioBACoqVisrVSsrVSoqAQD+1QGAAAAEABUAawHr -AZUACgAUABwAJAAAJTIWFhUVIzU0JzYiMhYWFRUhNTQ2NiImNDYyFhQWIiY0NjIWFAFVHEI4gCoHuThC -N/7VOHg0JiY0JYU0JiY0JusQJBc1NSweARAkFzU1FyQ6JjQmJjQmJjQmJjQABQAAAIACAAGVAAcADwAX -ACMALwAANjIWFRUhNTQlFhYVFSM1NCYiJjQ2MhYUFyInNjQnNjMyFhQGJxUjFSM1IzUzNTMV7VBY/wAB -DiU4QJE0JiY0JisKCRMTCQoaJibvQCtAQCvrIx0rKx0fBh8XKysiSCY0JiY0JgMbRBsDJjQmFitAQCtA -QAAACgBAAEABwAHVAAMABwALAA8AEwAXABsAHwAjACwAACU1IxUXNSMVAzUjFRc1IxUXNSMVFzUjFSc1 -IxUXNSMVFzUjFTczFSERMzU3FwGVKioqVioqKioqKipWKioqKirVgP6AgEBAwCsrVSoqAQAqKlYrK1Ur -K1UqKqorK1UrK1UqKqrVASsqQEAABQArACsB1QHVAAUADQAVAB0AJQAAJCImJzMGJiImNDYyFhQWIiY0 -NjIWFAYyNjQmIgYUEjIWFAYiJjQBJUo7DdoNnhoTExoTgxoTExoTsYxlZYxlU7B9fbB9iykhIWETGhMT -GhMTGhMTGtNljGVljAEbfbB9fbAABQArACsB1QHVAAUADQAVAB0AJQAANjIWFyM2NiImNDYyFhQWIiY0 -NjIWFAYyNjQmIgYUEjIWFAYiJjTbSjsN2g0iGhMTGhODGhMTGhOxjGVljGVTsH19sH3VKSEhaRMaExMa -ExMaExMa02WMZWWMARt9sH19sAAAAgBVACsBqwHLABMAGgAAJRcVITU3NTQ2NzU0NjIWFRUWFhUHIiY1 -MxQGAYAr/qorMy0SHBItM4ASGVYaqysVFStqMkoLDw0TEw0PC0oy6hkRERkAAwBVACsBqwHLAAcAGwAh -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 -AdUACQAAJQc3Jzc3FxcHFwEAhCN0mTw8mXQjkFCWZQ2NjQ1llgAAAgArAEAB1QHVAAUADwAAJRcnNycn -FwcXJwc3Jzc3FwEAUBVHXiTVdCOEhCN0mTw8tzBbPghWQ2WWUFCWZQ2NjQAAAgArAEAB1QHVAAkAEwAA -JRcnNycnBwcXByUHFycHNyc3NxcBAFAVR14kJF5HFQEldCOEhCN0mTw8tzBbPghWVgg+W7RlllBQlmUN -jY0ABQABAAAB/wIAAAkAEwAfAEQATgAAATIWFyMmJicHJxc0IyMVMzI3NjUnMhcWFRUUBwYjIzUHFhUU -BwYHBiMiJjUzFBYzMjQjIzUzMjQjIhUjNDc2MzIXFhUUBzcXByImJzMWFgEAZJMIIAVFNR1Rby8UEyQK -Ai81EwUVFiMxMRwFBgUPGRgeGw8MHB8QEB0aGRwPEBYjDgRIHVEOZJMIIAZEAgCHYztgGRxR+zl7IgcR -WS8MFggmFRaqUwscCgoMBAwYFwsONhYyFxEPDRsIDBfZHFEBiGM8YAACAEAAKwHAAdUACwATAAABIxEj -NSMVIxEjNSEmMhYUBiImNAHAgCsqK4ABgNEiGhoiGgFA/uuAgAEVK2oZIhoaIgAFACsAKwHAAesABAAI -AAwAEAAUAAATFxUhNQUzFSMFNSEVAzMVIyczFSP1y/5rASpAQP7WAZXrQECAQEAB62srK1WWakBAAQCW -lpYAAwBAAEAB1QHAAAcACwAlAAAkMjY0JiIGFAc1MxUHFRQGIyEiJjURNDYzITIWFRUjIgYVFRQWMwFI -GhMTGhM11RUaEf7WEhkZEgEqERrAEhkZEuASHBISHEeqqisVERoaEQEqERoaERUaEaoRGgADAEAAQAHA -AcAABwAPAB8AADcVITU0JiIGNjQmIgYUFjInNDYzITIWFREUBiMhIiY1gAEAWFBYwCY0JiY02hkSASoR -GhoR/tYSGZUVFR0lJXQ0JiY0JpURGhoR/tYRGhoRAAMAKwArAdUB1QAHAA8AFwAANjI3JiYiBgc2IgYU -FjI2NCYyFhQGIiY0r6IvAVhOWAGaNCYmNCaYsH19sH1mRRwmJR3qJjQmJjRmfbB9fbAABAAVACsBwwHr -AB0AJQAtADkAADcUMzMVISImNTQ3NycjNTMWFxYWFzM2NxcHBiMjBxYyFhQGIiY0JjIWFAYiJjQ3NSM1 -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 -ABIAGgAiAAA3MhcGFRUjNTQ2NhYyFhUVIzU0JiImNDYyFhQWIiY0NjIWFMAWHTOVN0KXSlDqETQmJjQm -diwfHywf6wYcLjA1FyQQFiAaMDAaYCY0JiY0Ox8sICAsAAACAEAAawHAAZUABgANAAABBzUjNTM1BxUz -FSMVJwHAVZaW1paWVQFAVUAqQIBAKkBVAAIAawBAAZUBwAAGAA0AABMXIxUjNSMXMwcnMzUzwFVAKkDq -QFVVQCoBwFWWltZVVZYAAwArACsB1QHVAAYADQAVAAAlIzUjFSMXJzMVMzUzJyYyFhQGIiY0AXU1KzVL -oDUrNUstsH19sH3AVVVLy1VVS0p9sH19sAAAAgAVADUB6wG1ABcAHgAAATIWFREUBiMhIiY1ETQ2MzMV -IxEhESM1AyczNTMVMwHAERoaEf6AERoaEYCAAYCAQFVAKkABtRkR/tURGhoRASsRGSr+1QErKv7rVcDA -AAIAFQBAAesBwAAFABUAACU1IzUjEQEyFhURFAYjISImNRE0NjMBwKvVAYARGhoR/oARGhoRa9VV/tYB -VRoR/tYRGhoRASoRGgAADwAVAEAB6wHAAAMABwALABAAFAAYABwAIAAnACwAMAA0ADkAPQBBAAAlNTMV -IzUzFTc1MxUHNTMUBgE1MxUDNTMVEzUzFRM1MxUDMhYVFSM1AyImNTMnNTMVFzUzFQM0NjMVBzUzFSc1 -MxUBayqAK4ArKysa/poqKiorK9UrKxEa1tURGisrK4Ar1hoRKysrK0ArKysrqyoqqysRGgFVKyv+qysr -AVUrK/8AKysBKxoRVYD+gBoRKisrVSsrAVURGiuqKipVKysAAAcAVQBAAasBwAADAAcACwAPABMAFwAr -AAABNSMVFzUjFRc1IxUnNSMVFzUjFRc1IxUBMxEjNSMVIzUjFSMRMxUzNTMVMwGAKysrKyuqKysrKysB -ACsrK6orKysrqisBQCsrVSoqVisrqysrVSoqVisrASv+gCsrKysBgCsrKwAAAgAVABUB6wHAAAMAHAAA -ATMRIwMyFhUVFAcHJyY1NTcjIiY1NyM1NDc3NjMBlVZWVREaDYwXCRWHERoBAQNBCh0BwP8AAQAaEdUR -DY0XCQ0HYhkRAikICJYaAAACABUAQAHrAesAGAAcAAABBzMVFAcHBiMjIiY1NTQ3NxcWFRUHMzIWBREz -EQHrAQEDQQodwBEaDYwXCRWHERr+KlYBKwIpCAiWGhoR1RENjRcJDQdiGfwBAP8AAAIAAAAAAgACAAAX -AC8AAAEyFhUVFAcHJyY1NjY3IyImNTU0Nzc2MycVFAcHBiMjIiY1NTQ3NxcWFQYGBzMyFgHgDhIJahEH -AwsBbwkMAjEJFFACMQkUkA4SCWoRBwMLAW8JDAErEw2LDglpEQcKDjQHDAkbAwhxFFUbAwhxFBMNiw4J -aREHCg40BwwABgBAAJUBwAFrAAMABwALAA8AEwAXAAAlNTMVJzMVIxU1MxUhNSEVJTUhFSU1IRUBlSsr -Kysr/oABK/7VASv+1QEr6yoqgCurKysrK1YqKlUrKwAAAwBAAEABwAHrAAMABwAfAAATMxUjBTUhFQEy -FhURFAYjISImNRE0NjMzNTMVMzUzFZVrawEA/tYBKhEaGhH+1hIZGRIVK6orAStrVerqAVUaEf7WERoa -EQEqERorKysrAAADABUAVQHrAasACwATABsAABIUFhcVJiY0NjcVBhYyNjQmIgYUNjIWFAYiJjRALyY4 -SEg4JpxqS0tqSzqMZWWMZQEqVEINLA5cdlwOLA3sS2pLS2rgZYxlZYwAAAEAKwArAdUB1QA1AAABFhUU -BiImNDYzMxUWFRQGIiY1NDc1BgYVFBYyNjU0JzcWFRQGIiY1NDY3NQYGFRQWMjY1NCcBlz59sH19WBUW -GiIaFhwkMkYyGR4mS2pLPS4/V2WMZTIBlz5ZWH19sH2wDBkRGhoRGQwtCCweIzIyIyEbHiY0NUtLNS9H -CCsIYEFGZWVGRzIAAAMAFQArAesB1QACAAoAIwAAJTMnNxMjJyMHIxMHBycHJzcmJzMWFzY3IzUzNTMV -MxUjBgcHAVNFIxZgKxhlGCtgTRFCax5tKBgrFRwuFu+WKpY/GjUBlV05/wBAQAEAbCxCah5rLDUoHzM/ -KyoqK1A7AQAAAQArAIAB1QGAAAoAACU3JwcnNxc3FzcVAVUxaFWeHoBVhjGAMWhVnh6AVYYxgAAAAQBA -AKsB1QFVAAYAAAEHNSE1ITUB1VX+wAFAAQBVQCpAAAEAKwCAAdUBgAAKAAABMxUnBycHJzcXNwFVgDGG -VYAenlVoAYCAMYZVgB6eVWgAAAEAawBAAZUBwAAKAAABMhYVEScHETQ2MwFrERmVlRkRAcAaEf6rQEAB -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 -NjIWFAYiAaw+PiMsLCMgICQODrhsdf6qVjJGMjJGAdVBqz4iM4MwJSNYICQTLROqLyYrKyaMRjIyRjIA -AAsAQABAAcABwAALAA8AEwAXABsAHwAjACcAKwAvADMAAAEVIzU0JiMjNTMyFgE1MxUzNTMVMzUzFQM1 -MxUjNTMVBzUzFQc1MxUnNTMVBTUzFQczFSMBwCsmGmpqLD/+gCsqKysqgCuAKysrKysrKwEqKysrKwFV -amoaJis//r8rKysrKysBVSsrKytVKyurKytWKipWKysqKwADAFUAAAHAAesAGgAiACcAACUHJzUnBiM1 -FjY3NzYzMzIWFRUUByc1BgcXMwIyFhQGIiY0AxcjBycBwEBAlwcNGjgSHhATARMdFEwUHYYgUSIaGiIa -YDYrSyBAQEAglwEuARkTIRAcFHsaFEwxEQ2GAWsaIhoaIv76NkogAAABABUAgAHrAYAAMwAAABQGIyIn -BxYVFAYiJjU0NycGIicHFhUUBiImNDYzMhc3JjU0NjIWFRQHFzYyFzcmNTQ2MgHrGhEIA0wCGiIaAjcG -CgZhAhoiGhoRCANhARkiGgE2AxADTAIaIgFmIhkBTAYFERkZEQUGNwICYQYFERoaIhkBYQMIERoaEQgD -NgEBSwYFERoAAgBAAEABwAHAAAUAHgAAARUXByc1FyM3JiYHBhQWMjc2NTMUBwYiJyY0NjIXNwELSg9b -1ZE7LHssK1Z8LCsrODigODhwnjg6AVVaLRo3ai08LAErLHhYLCs9Tzc4ODedcDg8AAIAKwArAdUB1QAF -AA0AACU3JzUjFSYyFhQGIiY0AVoRYCBDsH19sH2mHDpvgOp9sH19sAABABUAAAHrAgAALAAAAREUBiMj -IicnNjM2MzIXFzU0NjIWFRUzNTQ2MhYVFTM1NDYyFhUVMzU0NjIWAeszI5skGagbAQcKBwZcExoTFRIc -EhUTGhMWExoTAYv+yiMyGasaBgM0/g0TEw2Wyw4SEg7Ltg0TEw22dg0TEwAAAQBAAEABwAHAACcAACUy -NxcGIyImJyM1MyY0NyM1MzY2MzIXByYjIgYHMxUjBhQXMxUjFhYBQDMnJjZKPmMUS0EBAUFLFGM+SjYm -JzMnQhJ7iQICiXsSQXUiJjFIOCsHHAcrOEgxJiIpIisODg4rIikABQArACsB1QHVABgAIQAlAEEAVQAA -JRE0JiMjFzM1MxUzFSMGBxcHJwcXBzMyNicWFzY2NzcjHwI3Jic0JyMVMwYGIyImNDYzMhcXNycmIyIG -FBYzMjY3MhYVERQGIyMnIyImNRE0NjMzFwHADAm8GSkXTRsKHzoPOhMRK5YJDJYJDwwQAgNVBwUNDA44 -AlQvAxYUFR4eFRMNAhoCGSEkMzMkJS++ERkZEasVlhEZGRGAE1UBFgkMVhYWFickORA5EzsrDLETEQ4d -CAgXEC8LD0YLBCEMFB8qHw0BGQEXM0gzMJwZEf7qERlAGREBFhEZQAAEAAAAAAHlAeUABwARABgALQAA -NjIWFAYiJjQ3JyEyFhUUBwcGBzMnIwcHFAUHJwYjIiY1NDcnIyImNTQ3NycnN4QiGhoiGeHAAR8JDANM -DMdsKzITAQFMGz0NFREaEh6fERkFHS9eG4AaIhkZIoXADQkFBYoWKysjAwWlGz0SGREWDR0aEQoKNWNe -GwAAAgBVACsBqwHVABcAJQAAJTI2NCYjIgcnFTMnNjMyFhQGIyInIxYWExcRFAYjISImNRM0NjMBACw/ -Pyw6IBtVIhQuHywsHycWJQw1TIAaEf8AERoBGRGAP1g+MRxVIigrPiwgHSMBVYD/ABEZGREBVhEZAAAE -AAAABgHVAdsAEQAUABgAIAAAATIWFREUBgcnMzUjJzM1IxUnFzMnFzUjFQMBBycjBxEnAasRGRgRlWmU -FamrfikrKysrZQG6G3rAVSsB1RkR/wARGQGVKxUrKX7AK2srKwEG/kYbelUBaisAAAMAawBAAZUBwAAH -ABMAHQAAATMVITUzNzMHFwcXNxc3JzcnBycHESERFAYjIyImAUtK/tZKFmqBLi0eLS0eLS0eLS1TAQAa -EaoRGgGrKysVvS4tHi0tHi0uHi4utgEA/wARGhoAAAEAKwArAdUB1QArAAABFSMXBycjFRcHJxUjNQcn -NzUjByc3IzUzJzcXMzUnNxc1MxU3FwcVMzcXBwHVWUUeYytkH0UqRR9kK2MeRVlZRR5jK2QfRSpFH2Qr -Yx5FARUqRR9kK2MeRVlZRR5jK2QfRSpFH2QrYx5FWVlFHmMrZB9FAAAGABUAawHrAZUAAwALAA8AFwAb -AC8AAAEzJyMWMjY0JiIGFCc1IxUGMjY0JiIGFCczNSMlFxUjFAYiJjUjFAYiJjUjNTQ2MwFAa1YVKBoT -ExoTQFVOHBISHBIgVVUBK4A2JjQmdSY0JisZEgEVVuATGhMTGndWVooTGhMTGndWKoBqGiYmGhomJhrA -EhgAAQAAAI0CAAFzACcAAAAyFhQGIicnNxcWMjY0JiIHBgcHBiImNDYyFxcHJyYiBhQWMjc2NzcBXWBD -Q2AiGyAZFjwqKjwVRxM8IWBDQ2AiGyEYFjwqKjwVRxM8AXNEXkQiGBwVFio8KhU+EjUhRF5EIhgcFRYq -PCoVPhI1AAIAQABAAcABxwAXABsAAAEmJgc2FhcHJiY3BhYXByY0NzQzNhcWFwc3FwcBdDKDPjBzMXox -MQYVIDI9Pz8BRmJROJkfiR8BRDIgFQYxMXoxczA+gzI9P7M/AUYIBji4H4oeAAADACsAQAHVAcAAAwAd -ACsAAAE1IxUzMhYVFRQGIyM1IxUjIiY1NTQ2MzM1NzMXFQczNTMVFAYjISImNTUzAStW1hEZGRGAVoAS -GBkRVStVK4BWlRkS/tYSGZUBayoqGhFAERorKxkSQBEaKisrKsAVVRIZGRJVAAAGAEAAQAHAAcAABwAP -ABcAHwAnADcAAAAyNjQmIgYUFjI2NCYiBhQmMjY0JiIGFCYyNjQmIgYUFjI2NCYiBhQBMhYVERQGIyEi -JjURNDYzAVIcEhIcEhIcEhIcEk4cEhIcEk4cEhIcEhIcEhIcEgEVERoaEf7WERoaEQFAEhwSEhzSEhwS -EhxOEhwSEhxOEhwSEhzSEhwSEhwBLhoR/tYRGhoRASoRGgAFABUAQAHrAcAABQAlAEMASwBTAAA3MwYG -IiYWMjY3MhYzMjY0JiMiBiMmJiIGByImIyIGFBYzMjYzFiUGBgcGBiImJyYmJyY0NzY2NzY3NjMyFhcW -FhcWFCQ0NjIWFAYiNjQ2MhYUBiKgwAw0QDQhZk8NAQQBERoaEQEEAQ1PZk8NAQQBERoaEQEEAQ0BawQg -FxZfcl4WFyEEAgIEIRcRHTZJOl0WFyEEAv7FEBYPDxZbDxYQEBbVHSMjTTwvARoiGgEvPDwvARoiGgEv -SxglBy8/Py8HJRgIDAgYJQcmGDA+MAclGAgMGxYQEBYQEBYQEBYQAAAEACsAKwHAAdUABwAPACgALQAA -JDI2NCYiBhQGMjY0JiIGFCUWFRQGIyImJyMGBiMiJjU0NycjNTMXIRQnMhYVIwFeGhMTGhOtGhMTGhMB -ERkrHxwqBC0EKRwfLCctL0oUATerR2SrVRMaExMaExMaExMaRRchHyslGxslKx8sFl4rKzn5ZEYAAQAr -ACsB1QHVABsAACUXBxcHJwcnByc3JwcnNyc3JzcXNxc3FwcXNxcBtx4tHh4fLR8eH0y3TB4eHi0eHh8t -Hx4fTLdMHsMfLR8eHi0eHh5Mt0wfHh8tHx4eLR4eHky3TB8AAAMAVQBAAdUBwAADAAcAGQAANyEVIQE1 -IxU3MhYVFRQGIyMVFAYjIyImNTVVAVb+qgFWKysSGBgSKzIjgCMzaysBFUBAaxkSQBEZQCMzMyPVAAAC -AFUAKwHAAdUADwAXAAABBxUWFhUUBiImNTQ3FTMREjQ2MhYUBiIBa4AuPEtqS0ArwBIcEhIcAYJCwQIY -EBEZGREZDCUBgP59HBISHBIAAAgAKwArAdUBwAAOAB4AIgAmACoALgBCAEoAAAEWBwcjNzYnJjc3MwcG -FxcWBwcjNzYnJyY3NzMHBhcTNSMVIzUjFSM1IxUjNSMVNzMVFAYjISImNTUzNTQ2MzIXFxYmNDYyFhQG -IgE5IQYBKQMFFSIGASkCBRNXIgYCKAIFEwIiBgIoAgUTCSorKyorKyqD5xkR/qoRGUAdExQQHQR1GCQZ -GSQBgyAvCQwbFSIuCQ0bFAEhLgkMGxQBIi4JDRsU/tGAgICAgICAgKurERkZEasQFBwQIQVkJBkZJBkA -AAUAVQArAasB1QADAAcACwAPAB8AABMzFSMRMxUjFzUhFQU1IRUBMhYVERQGIyEiJjURNDYzqyoqKirV -/wABAP8AAQASGRoR/wARGhkSAQBrAQBAFWtr68HBAYAYEv6qERkZEQFWEhgABAArAEAB1QHAAAcAHwBR -AIEAAAA0NjIWFAYiByInJic3JyYjNTIWFxcGBwYjIicmIgcGBSInJiMiBwYjIicmIyIHBiMiJyYjIgcG -IzUyNzYzMhcWMzI3NjMyFxYzMjc2MzIXFjMVIicmIyIHBiMiJyYjIgcGIicmIyIHBiM1Mjc2MzIXFjMy -NzYyFxYzMjc2MzIXFjMBKx8sHx8skQwNBAxFFSA1KTQYiQYDDQwLDRYyFg0BERcXCw4NCxcYFxcLDg0L -FxgXFwsODQsXGA0LFxgXFwsODQsXGBcXCw4NCxcYFxcLDhcXCw4NCxcYFxcLDg0LFzAXCw0OCxcXDQsX -GBcXCw4NCxcwFwsNDgsXFxgXCw0BdSwfHywgVQgDBUUWIDUTGIgEAQgIDQ0IYA4HBw4OBwcODgcHDisH -Dg4HBw4OBwcODgeLDgcHDg4HBw4OBwcOKwcODgcHDg4HBw4OBwAAAgArAGsB1QGVAA8AEwAAARYWFyE2 -NjcmNTQ2MhYVFAUhFSEBJz9WBP6ABFY/BBoiGv8AAar+VgFaDWFBQWENCgcRGRkRB88qAAAGACsAFQHV -AdUAAgAZACkALQAxADkAACUnMyciJjQ2MxUiBhUUFjMzMhYVFSM1NCYjNxYWFRUjNTQmIzUyNjUzFAcz -FSM3MxUjJTcBBycjNTMBaz8/Nh0qKh0RFhcQIR4sIBkRPB4lIDEkEBcgJyAgNSAg/nYaAWsbldWVrD9b -KzoqIBUQERonHCIbFBdSDjokLCwkMyAXER7CQEBA1Rv+lRuWQAAABQArAGsB1QHVABYALAAwADQAOAAA -ATIWFRUjNTQmIyMiJjQ2MxUiBhUUFjM3FhYVFSM1NCYjNTI2NTQmIzUyFhUUBzMVIzczFSMlIRUhAVYf -KyAZESEdKiodERYXEF0eJSAxJBAXFxAdKicgIDUgIP52AUD+wAEmJh0jHBQXKzoqIBUQERo1DjojMDAk -MiAXERAXICodHsVAQEBAQAACACsAKwHVAdUABwAXAAABBgcmJzY3FgUyFhc2NjMUBgcGByYnJiYBSiog -ICoIQ0P+50RwISFwRF9MERkVFUxfATMXIiIXX0NDZ0IzM0JUhhsGBQMIG4YAAQAAACsCAAHVAE4AAAEW -FAcHBiMnBwYiLwIHFxQjIyIvAgcUIyMiNTcjBxcWIyMiJycHFxYjIyI1Jzc1JjUhFjcmNxYXFjYnJici -JyY3NDMWFhcXHgIHFBcB/wEBGQICDwcCCQIOFy8RBBQCAhQKGQQVBBeHIwgCBhQDARwiBgIGFgQNFRcB -EjAvCA4cCAcJAgkkAQIMAgQdKQcEAwcFAQIBPgEDASACAxYEBB4FcIgEA1ERYgMEh0JEBQJmJzwFAjhS -kAkVASIWDhQFBAsHHA4BAwUDBCURAwQIDwgFAgAAAAcAWgADAAEECQAAAGAAAAADAAEECQABABwAYAAD -AAEECQACAA4AfAADAAEECQADAFIAigADAAEECQAEABwAYAADAAEECQAFABoA3AADAAEECQAGACoA9gBD -AG8AcAB5AHIAaQBnAGgAdAAgADIAMAAxADUAIABHAG8AbwBnAGwAZQAsACAASQBuAGMALgAgAEEAbABs -ACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgBNAGEAdABlAHIAaQBhAGwAIABJAGMAbwBu -AHMAUgBlAGcAdQBsAGEAcgBGAG8AbgB0AEYAbwByAGcAZQAgADIALgAwACAAOgAgAE0AYQB0AGUAcgBp -AGEAbAAgAEkAYwBvAG4AcwAgADoAIAA4AC0AMgAtADIAMAAxADYAVgBlAHIAcwBpAG8AbgAgADEALgAw -ADEAMQBNAGEAdABlAHIAaQBhAGwASQBjAG8AbgBzAC0AUgBlAGcAdQBsAGEAcgAAAAMAAAAAAAD/gwAy -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAgABAAAADAAAABwAAAACAAIAAwAnAAEAKAPMAAIABAAA -AAEAAAABAAAACgAeADQAAWxhdG4ACAAEAAAAAP//AAEAAAABc2l6ZQAIAAQAAACgAAAAAAAAAAAAAAAA -AAEAAAAKAB4ALAABbGF0bgAIAAQAAAAA//8AAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAWj8 -ABkAOABUCOIO7BegHJAfqCngK24tTC+yMW45jj0wQApAqkq6S55QEl5aYjJipGaSaIpouAABAAQC4gAL -ABEADQAfABwAIQAOACEAFgAcABsASACSAM4BCAFAAXgBrgHkAhYCRAJyAp4CyALwAxgDPgNiA4YDqgPM -A+wEDAQsBEwEbASMBKoEyATmBQIFHgU6BVYFcgWOBagFwgXcBfYGEAYqBkQGXAZ0BowGpAa6BtAG5gb8 -BxIHKAc+B1IHZgd6B44Hoge2B8oH3AfuB/4IDggeCC4IPghOCFwIaAh0CH4IhgKkAB0AFgAfABkAFgAb -ABIADQAgABIADgAhAA0AFgAbABEAFgAjABYAEQAiAA4AGQANACAAIgAWACEAEgKnABwAFgAfABkAFgAb -ABIADQAgABIADgAhAA0AGQASABQAHwAcABwAGgANAB8AEgARACIAEAASABECpgAbABYAHwAZABYAGwAS -AA0AIAASAA4AIQANABkAEgAUAB8AHAAcABoADQAbABwAHwAaAA4AGQKpABsAFgAfABkAFgAbABIADQAg -ABIADgAhAA0AHwASABAAGQAWABsAEgANABsAHAAfABoADgAZAqUAGgAWAB8AGQAWABsAEgANACAAEgAO -ACEADQAZABIAFAAfABwAHAAaAA0AEgAlACEAHwAOAqgAGgAWAB8AGQAWABsAEgANACAAEgAOACEADQAf -ABIAEAAZABYAGwASAA0AEgAlACEAHwAOAqMAGAAWAB8AGQAWABsAEgANACAAEgAOACEADQATABkADgAh -AA0ADgAbABQAGQASABECagAWAB8AHwAcACQADQARAB8AHAAdAA0AEQAcACQAGwANABAAFgAfABAAGQAS -AuUAFgAQABAAHAAiABsAIQANAA8ADgAZAA4AGwAQABIADQAkAA4AGQAZABIAIQDbABUAFgAfAB0AGQAO -ABsAEgAaABwAEQASAA0AFgAbAA4AEAAhABYAIwASAvYAFAAgACAAFgAUABsAGgASABsAIQANACEAIgAf -ABsAEgARAA0AFgAbAvUAEwAgACAAFgAUABsAGgASABsAIQANAB8AEgAhACIAHwAbABIAEQDcABMAFgAf -AB0AGQAOABsAEgAaABwAEQASAA0ADgAQACEAFgAjABIAsQASABEAEQANABAAFgAfABAAGQASAA0AHAAi -ACEAGQAWABsAEgLoABEAEQARAA0AIAAVABwAHQAdABYAGwAUAA0AEAAOAB8AIQL0ABEAIAAgABYAFAAb -ABoAEgAbACEADQAfABIAIQAiAB8AGwKiABEAFgAfABkAFgAbABIADQAgABIADgAhAA0AEwAZAA4AIQDb -ABAAFgAfAB0AGQAOABsAEgAaABwAEQASAA0AHAATABMC8wAPACAAIAAWABQAGwAaABIAGwAhAA0AGQAO -ACEAEgGLAA8AIAAgABYAIAAhAA4AGwAhAA0AHQAVABwAIQAcAmkADwAfAB8AHAAkAA0AEQAfABwAHQAN -ABEAHAAkABsA3AAPABYAHwAdABkADgAbABIAGgAcABEAEgANABwAGwO7AA8AFgAfAB0AHAAfACEADQAg -ABUAIgAhACEAGQASAuQADwAQABAAHAAiABsAIQANAA8ADgAZAA4AGwAQABIC5wAOABAAEAAcACIAGwAh -AA0AEAAWAB8AEAAZABICfwAOAB8AHwAcACQADQARABwAJAAbACQADgAfABEC8gAOACAAIAAWABQAGwAa -ABIAGwAhAA0AFgAbABEDvAANABkAGQANABYAGwAQABkAIgAgABYAIwASAmsADQAfAB8AHAAkAA0AEQAf -ABwAHQANACIAHQGIAA0AEQARAA0AIQAcAA0AHQAVABwAIQAcACACbAANAB8AHwAcACQADQATABwAHwAk -AA4AHwARAuMADQAQABAAEgAgACAAFgAPABYAGQAWACEAJgDYAA0AEAAQABIAIAAgAA0ADgAZAA4AHwAa -ACACfAAMAB8AHwAcACQADQAiAB0AJAAOAB8AEQEMAAwAIQAhAA4AEAAVAA0AGgAcABsAEgAmAGYADAAR -ABEADQAhABwADQAeACIAEgAiABIC7wAMACAAHQASABAAIQANAB8ADgAhABYAHALuAAwAGwAbABwAIgAb -ABAAEgAaABIAGwAhAlsADAARABEADQAZABwAEAAOACEAFgAcABsA1wAMABAAEAASACAAIAANAA4AGQAO -AB8AGgLmAAsAEAAQABwAIgAbACEADQAPABwAJQDZAAsAEAAQABIAIAAgAA0AIQAWABoAEgIfAAsAEQAR -AA0ADgANAB0AFQAcACEAHAELAAsAIQAhAA4AEAAVAA0AEwAWABkAEgLxAAoAIAAgABYAFAAbABoAEgAb -ACECaAAKAB8AHwAcACQADQAPAA4AEAAYA6IACgAQABAAEgAgACAAFgAPABkAEgCwAAoAEQARAA0AEAAW -AB8AEAAZABIC8AAKACAAIAASACAAIAAaABIAGwAhAYwACgAiABEAFgAcACEAHwAOABAAGAFKAAoAIQAh -AA4AEAAVABoAEgAbACEAagAJAB8AIQANACEAHwAOABAAGALrAAkAGQAOAB8AGgANABwAEwATACsACQAR -ABEADQAOABkAEgAfACEA2gAJABEAEQANAA4AGQAOAB8AGgLqAAkAGQAOAB8AGgANAA4AEQARAvcACQAi -ACEAHAAfABIAGwASACQBigAJACAAIAAWACAAIQAOABsAIQLsAAgAGQAOAB8AGgANABwAGwAtAAgAIwAN -ACEAFgAaABIAHwOaAAcAGQAZAA0AHAAiACEAsgAHAB8AEAAVABYAIwASAK8ABwARABEADQAPABwAJQLt -AAcAGwARAB8AHAAWABEAXwAHABYAHwAdABkADgAmA7oABwAQAA0AIgAbABYAIQGJAAYAEQAXACIAIAAh -AukABQAZAA4AHwAaACwABQAZAA8AIgAaAmcABAAdAB0AIACuAAMAEQARAoIAAwARAA8ANwBwAJwAxADs -ARIBOAFcAYABogHCAeICAgIiAkICYgKCAqACvALYAvQDDgMoA0IDXAN2A5ADqgPEA94D+AQSBCwERgRg -BHoElASuBMYE3gT2BQwFIgU4BU4FYgV2BYgFmgWsBb4FzgXcBegF9AYAAN4AFQAOACEAIQASAB8AJgAN -ABAAFQAOAB8AFAAWABsAFAANABMAIgAZABkA4wATABkAIgASACEAHAAcACEAFQANABAAHAAbABsAEgAQ -ACEAEgARAOUAEwAZACIAEgAhABwAHAAhABUADQAgABIADgAfABAAFQAWABsAFAB1ABIAHwAOABsAEQAW -ABsAFAANACQADgAhABIAHwAaAA4AHwAYAOQAEgAZACIAEgAhABwAHAAhABUADQARABYAIAAOAA8AGQAS -ABEA6QARAB8AFgAUABUAIQAbABIAIAAgAA0AGgASABEAFgAiABoBEQARABwAHwARABIAHwANABUAHAAf -ABYAJwAcABsAIQAOABkC+wAQABwAHAAYABoADgAfABgADQAcACIAIQAZABYAGwASAOYADwAfABYAFAAV -ACEAGwASACAAIAANAA4AIgAhABwDvgAPACIAIAAWABsAEgAgACAADQAQABIAGwAhABIAHwEYAA8AHAAf -ABEAEgAfAA0AIwASAB8AIQAWABAADgAZAOcADwAfABYAFAAVACEAGwASACAAIAANABUAFgAUABUCgwAP -ABkAIgASACEAHAAcACEAFQANAA4AIgARABYAHADhAA8ADgAhACEAEgAfACYADQAiABsAGAAbABwAJAAb -AvsADwAcABwAGAAaAA4AHwAYAA0ADwAcAB8AEQASAB8A6AAOAB8AFgAUABUAIQAbABIAIAAgAA0AGQAc -ACQBDgANABwAHwARABIAHwANAA8AHAAhACEAHAAaAY0ADQAZACIAHwANABAAFgAfABAAIgAZAA4AHwDd -AA0ADgAhACEAEgAfACYADQAOABkAEgAfACEBEgAMABwAHwARABIAHwANABYAGwAbABIAHwGXAAwAHwAW -ABQAFQAhABsAEgAgACAADQAKARAADAAcAB8AEQASAB8ADQAQABwAGQAcAB8BDwAMABwAHwARABIAHwAN -ABAAGQASAA4AHwGWAAwAHwAWABQAFQAhABsAEgAgACAADQAJAZUADAAfABYAFAAVACEAGwASACAAIAAN -AAgBlAAMAB8AFgAUABUAIQAbABIAIAAgAA0ABwGTAAwAHwAWABQAFQAhABsAEgAgACAADQAGAZIADAAf -ABYAFAAVACEAGwASACAAIAANAAUBkQAMAB8AFgAUABUAIQAbABIAIAAgAA0ABAK6AAwAIgAPAA8AGQAS -AA0AEAAVAA4AHwAhAZgADAAfABwAGAASABsADQAWABoADgAUABIDvQAMABIADgAQABUADQAOABAAEAAS -ACAAIAEWAAwAHAAfABEAEgAfAA0AIAAhACYAGQASAN8ADAAOACEAIQASAB8AJgANABMAIgAZABkBFQAM -ABwAHwARABIAHwANAB8AFgAUABUAIQEUAAwAHAAfABEAEgAfAA0AHAAiACEAEgAfAY4ACwAZACIAHwAN -ABkAFgAbABIADgAfARMACwAcAB8AEQASAB8ADQAZABIAEwAhAOAACwAOACEAIQASAB8AJgANACAAIQAR -ARcACgAcAB8AEQASAB8ADQAhABwAHQENAAoAHAAfABEAEgAfAA0ADgAZABkC/AAKACIAFAANAB8AEgAd -ABwAHwAhAiIACgAiAB8AIAAhAA0AGgAcABEAEgDiAAkAGQAiABIAIQAcABwAIQAVALMACQAOABAAGAAg -AB0ADgAQABICIwAIABIAEgAbABUAEgAfABIC+gAIABwAHAAYABoADgAfABgBjwAIABkAIgAfAA0AHAAT -ABMAfAAIACIAIAAWABsAEgAgACABkAAHABkAIgAfAA0AHAAbAvgABgAOABAAGAAiAB0BmQAFAB8AIgAg -ABUC/QAFACIAFgAZABEAtAAFABkAHAAQABgC+QAEABwAHAAYAFYArgDeAQ4BOAFiAYoBsgHaAgACJAJI -AmoCigKoAsYC5AMCAyADPgNcA3oDmAO0A9AD7AQIBCQEQARcBHgEkgSsBMYE4AT6BRQFLgVGBV4FdgWO -BaYFvgXWBe4GBgYeBjYGTAZiBngGjgakBroGzgbiBvYHCgceBzIHRgdYB2oHfAeOB6AHsgfEB9YH5gf2 -CAQIEgggCC4IPAhICFQIYAhsCHgIggiMCJYIoAiqAaYAFwAcABsAIQAfABwAGQANAB0AHAAWABsAIQAN -ABEAIgAdABkAFgAQAA4AIQASAtwAFwAVABIAEAAYAA0ADwAcACUADQAcACIAIQAZABYAGwASAA0ADwAZ -AA4AGwAYAhcAFAAcABkAGQASABAAIQAWABwAGwAgAA0ADwAcABwAGAAaAA4AHwAYAKwAFAAOABkAGQAN -ABoAFgAgACAAEgARAA0AHAAiACEAFAAcABYAGwAUAJMAEwAVAA4AIQANAA8AIgAPAA8AGQASAA0AHAAi -ACEAGQAWABsAEgGfABMAEgAbACEAEgAfAA0AEwAcABAAIgAgAA0AIAAhAB8AHAAbABQCqgATABwAGwAT -ABYAHwAaAA4AIQAWABwAGwANABsAIgAaAA8AEgAfAwEAEgAVAB8AHAAaABIADQAfABIADgARABIAHwAN -ABoAHAARABIBVwARAB8AEgAOACEAEgANABsAEgAkAA0AEwAcABkAEQASAB8BoAARABIAGwAhABIAHwAN -ABMAHAAQACIAIAANACQAEgAOABgCqgAQABwAGwATABYAHwAaAA4AIQAWABwAGwANABsAIgAaA4gADwAO -AB8AEQANABoAEgAaAA8AEgAfACAAFQAWAB0BTgAOABkAHAAiABEADQARABwAJAAbABkAHAAOABEC/wAO -ABUADgAbABQAEgANABUAFgAgACEAHAAfACYALgAOABkAHAAgABIAEQANABAADgAdACEAFgAcABsDowAO -ABwAGgAdAA4AHwASAA0ADgAfAB8AHAAkACABWQAOAA4AIAAhAA0AEAAcABsAGwASABAAIQASABEDwQAO -ABUAFgAZABEADQATAB8AFgASABsAEQAZACYDjQAOAA4AGgASAB8ADgANABIAGwAVAA4AGwAQABIAdgAO -AA4AGQAZAA0AIQAcAA0ADgAQACEAFgAcABsBrgAOAB8AHAAdAA0AGQAOABsAEQAgABAADgAdABIBpQAN -ABwAGwAhAB8AHAAZAA0AHQAcABYAGwAhAnAADQAVABIAIwAfABwAGwANAB8AFgAUABUAIQC4AA0AHAAb -ACEAEgAbACEADQAdAA4AIAAhABIDhwANAA4AHwARAA0AFAAWABMAIQAQAA4AHwARAJcADQAcABsAIQAO -ABAAIQANAB0AFQAcABsAEgGvAA0AHwAcAB0ADQAcAB8AFgAUABYAGwAOABkAggANAA4AGQAZAA0AHwAS -ABAAEgAWACMAEgARAbAADQAfABwAHQANAB0AHAAfACEAHwAOABYAIQCYAAwAHAAbACEADgAQACEADQAa -AA4AFgAZAVEADAAZABwAIgARAA0AIgAdABkAHAAOABEBnAAMAA4AGgASAB8ADgANABMAHwAcABsAIQC2 -AAwAHAAbACEAEgAbACEADQAQABwAHQAmAUwADAAZABwAIgARAA0AEAAWAB8AEAAZABICbwAMABUAEgAj -AB8AHAAbAA0AGQASABMAIQMAAAwAFQASABAAGAANABAAFgAfABAAGQASA4kACwAOAB8AEQANACEAHwAO -ACMAEgAZAh0ACwAfABwAHQANAB8AHAAhAA4AIQASAaEACwAcABkAGQASABAAIQAWABwAGwAgAZ4ACwAO -ABoAEgAfAA4ADQAfABwAGQAZAZ0ACwAOABoAEgAfAA4ADQAfABIADgAfAJIACwAVAA4AIQANAA8AIgAP -AA8AGQASALcACwAcABsAIQASABsAIQANABAAIgAhAVAACwAZABwAIgARAA0AHgAiABIAIgASAbEACwAf -ABwAHQANACAAHgAiAA4AHwASAwQACwAfABIAEQAWACEADQAQAA4AHwARAIEACwAOABkAGQANABoAFgAg -ACAAEgARA8AACgAVABYAGQARAA0AEAAOAB8AEgCDAAoADgAZABkADQAgAB0AGQAWACEBogAKABwAGQAc -AB8ADQAZABIAGwAgAU0ACgAZABwAIgARAA0AEQAcABsAEgGbAAoADgAaABIAHwAOAA0ADgAZACEAgAAK -AA4AGQAZAA0AGgASAB8AFAASAIUACQAZABIADgAfAA0ADgAZABkBrQAJAB8AHAAdAA0AEwAfABIAEgGn -AAkAHwAcAB0ADQAEAAkADQAMAtsACQAVABIAEAAYAA0ADwAcACUDmwAJABwAHQAmAB8AFgAUABUAIQFP -AAkAGQAcACIAEQANABwAEwATAH8ACQAOABkAGQANABoADgARABIAhwAIABwAGwAhAA4AEAAhACABowAI -ABwAGQAcAB8AFgAnABIBqwAIAB8AHAAdAA0ACgANAAgBqgAIAB8AHAAdAA0ACAANAAcBqAAIAB8AHAAd -AA0ABgANAAUBrAAIAB8AHAAdAA0AEQAWABsBWgAIABwAGgAdACIAIQASAB8AfgAIAA4AGQAZAA0AEgAb -ABEBpAAHABwAGgAdAA4AHwASAIYABwAcABoAGgASABsAIQJtAAYADgAbABAAEgAZA78ABgAOACAAFgAb -ABwAuQAGAB8AEgAOACEAEgGaAAYADgAaABIAHwAOAv4ABgAOABAAFQASABEBSwAFABkAHAAiABEAtQAF -ABkAEgAOAB8DAgAFABkADgAgACACbgAFABUAEgAQABgCcQAFABkAHAAgABIBqQAEAB8AHAAdAIQABAAV -AA4AIQMDAAQAHAARABIAfQAEAA4AGQAZAr0ABAAOABgAEgFYAAQADgAgACEALwBgAIYArADSAPgBHAFA -AWIBhAGmAcYB5gIGAiYCRgJkAoICoAK+AtwC+gMWAzADSANgA3gDkAOoA74D1APqBAAEFgQqBD4EUgRk -BHQEhASUBKIEsAS+BMwE1gTgBOgCtQASABwADQAbABwAIQANABEAFgAgACEAIgAfAA8ADQAcABMAEwKF -ABIAHAANABsAHAAhAA0AEQAWACAAIQAiAB8ADwANAA4AGQAhAisAEgAWAB8AEgAQACEAFgAcABsAIAAN -ACEAHwAOABsAIAAWACECKgASABYAHwASABAAIQAWABwAGwAgAA0AHwAOABYAGQAkAA4AJgIpABEAFgAf -ABIAEAAhABYAHAAbACAADQAgACIADwAkAA4AJgK2ABEAHAANABsAHAAhAA0AEQAWACAAIQAiAB8ADwAN -ABwAGwKFABAAGwARAA0AEwAcAB8AJAAOAB8AEQAgABkADgAgABUCKgAQABYAHwASABAAIQAWABwAGwAg -AA0AIQAfAA4AFgAbAigAEAAWAB8AEgAQACEAFgAcABsAIAANABMAEgAfAB8AJgIoAA8AFgAfABIAEAAh -ABYAHAAbACAADQAPABwADgAhAiwADwAWAB8AEgAQACEAFgAcABsAIAANACQADgAZABgCJQAPABYAHwAS -ABAAIQAWABwAGwAgAA0ADwAWABgAEgFdAA8AEgAjABIAGQAcAB0AEgAfAA0ADwAcAA4AHwARAVwADwAS -ACAAGAAhABwAHQANACQAFgAbABEAHAAkACACWgAOABYAHwASABAAIQAWABwAGwAgAA0AHwAiABsCJwAO -ABYAHwASABAAIQAWABwAGwAgAA0AEAAOAB8A6wAOABIAIwASABkAHAAdABIAHwANABoAHAARABICJgAO -ABYAHwASABAAIQAWABwAGwAgAA0ADwAiACAChgAOABwADQAbABwAIQANABEAFgAgACEAIgAfAA8DuQAO -ABIAGQASACEAEgANABMAHAAfABIAIwASAB8BhgANABIAIwAWABAAEgAgAA0AHAAhABUAEgAfANUADAAS -ABkAEgAhABIADQAgACQAEgASAB0DpgALABwAGwAiACEADQAgABoADgAZABkBWwALABIAIAAYACEAHAAd -AA0AGgAOABADBwALABIAIAAQAB8AFgAdACEAFgAcABsDpQALABwAGwAiACEADQAZAA4AHwAUABIBQgAL -AB8ADgAUAA0AFQAOABsAEQAZABICJAAKABYAHwASABAAIQAWABwAGwAgAYQACgASACMAFgAQABIADQAV -ACIADwCIAAoAFgAOABkAEgAfAA0AIAAWAB0DpAAKAA4AIQASAA0AHwAOABsAFAASAOoACgAOACEADgAN -ACIAIAAOABQAEgKHAAkAHwAWACMAEgANABIAIQAOAoQACQAWACAAEAANABMAIgAZABkDBQAJAA4AIAAV -AA8AHAAOAB8AEQMKAAgAHAAbABIADQAOABkAGQGzAAcAEgAhAA4AFgAZACAA7AAHABIAIwAWABAAEgAg -AIkABwAWAA4AGQAdAA4AEQGyAAYAEgAVAA4AJwASAr4ABgAcABoADgAWABsAugAGAB8ADgATACEAIAMG -AAYAEgAZABIAIQASAwkABAAcABsAEgFeAAQAHAAQABgDCAADABsAIADtAAMAIwAfAB4APgBuAJYAuADa -APoBGgE6AVgBdgGSAa4BygHiAfoCEgIqAkACVgJsAoIClgKqArwCzgLeAuoC9gMCAw4DjQAXABsAFQAO -ABsAEAASAA0AHQAVABwAIQAcAA0AIQAfAA4AGwAgABkADgAhABICsQATABsAFQAOABsAEAASABEADQAS -ABsAEAAfACYAHQAhABYAHAAbAbcAEAAlAB0AHAAgACIAHwASAA0AGgAWABsAIgAgAA0ABQG2ABAAJQAd -ABwAIAAiAB8AEgANABoAFgAbACIAIAANAAQCiAAPACMAEgAbACEADQAOACMADgAWABkADgAPABkAEgG5 -AA8AJQAdABwAIAAiAB8AEgANAB0AGQAiACAADQAFAbgADwAlAB0AHAAgACIAHwASAA0AHQAZACIAIAAN -AAQBtwAOACUAHQAcACAAIgAfABIADQAbABIAFAANAAUBtgAOACUAHQAcACAAIgAfABIADQAbABIAFAAN -AAQBugANACUAHQAcACAAIgAfABIADQAnABIAHwAcAlwADQARABYAIQANABkAHAAQAA4AIQAWABwAGwAp -AA0AHwAfABwAHwANABwAIgAhABkAFgAbABICcgALACUAHQAOABsAEQANABkAEgAgACADDAALACUAFgAh -AA0AIQAcAA0ADgAdAB0CcwALACUAHQAOABsAEQANABoAHAAfABIDtAALACIAHwAcAA0AIAAmABoADwAc -ABkCiQAKACMAEgAbACEADQAPACIAIAAmA5MACgAjABIAGwAhAA0AIAASAA4AIQJhAAoAIwANACAAIQAO -ACEAFgAcABsCigAKACMAEgAbACEADQAbABwAIQASAC8ACQAeACIADgAZABYAJwASAB8DDgAJACUAIQAS -ABsAIAAWABwAGwG1AAgAJQAdABwAIAAiAB8AEgAwAAgAJQAdABkAFgAQABYAIQMNAAcAJQAdABkAHAAf -ABIAigAFABoADgAWABkDjAAFABcAEgAQACEDCwAFACMAEgAbACEAKAAFAB8AHwAcAB8BtAAEABEAFgAh -AFoAtgDuASYBVAGCAawB1gIAAioCUgJ6AqICygLwAxYDPANiA4YDqgPOA/IEFgQ4BFoEegSaBLgE1gT0 -BRIFMAVOBWwFiAWkBcAF3AX4BhQGMAZMBmYGgAaaBrQGzgboBwIHHAc0B0wHZAd8B5QHrAfEB9wH9AgK -CCAINghMCGIIeAiMCKAItAjICNwI8AkCCRQJJgk4CUoJXAluCYAJkgmkCbYJyAnYCeYJ9AoCCg4KGgok -Ci4BLQAbABwAHwAaAA4AIQANACEAEgAlACEAEQAWAB8AEgAQACEAFgAcABsADQAfAA0AIQAcAA0AGQEs -ABsAHAAfABoADgAhAA0AIQASACUAIQARABYAHwASABAAIQAWABwAGwANABkADQAhABwADQAfASMAFgAc -AB8AGgAOACEADQAWABsAEQASABsAIQANABYAGwAQAB8AEgAOACAAEgEiABYAHAAfABoADgAhAA0AFgAb -ABEAEgAbACEADQARABIAEAAfABIADgAgABIBJgAUABwAHwAaAA4AIQANABkAFgAgACEADQAPACIAGQAZ -ABIAIQASABEBKwAUABwAHwAaAA4AIQANACAAIQAfABYAGAASACEAFQAfABwAIgAUABUBJwAUABwAHwAa -AA4AIQANABkAFgAgACEADQAbACIAGgAPABIAHwASABEBGgAUABwAHwAaAA4AIQANAA4AGQAWABQAGwAN -ABcAIgAgACEAFgATACYBJQATABwAHwAaAA4AIQANABkAFgAbABIADQAgAB0ADgAQABYAGwAUARkAEwAc -AB8AGgAOACEADQAOABkAFgAUABsADQAQABIAGwAhABIAHwHHABMAFgAZACEAEgAfAA0AEAASABsAIQAS -AB8ADQATABwAEAAiACAAawATABYADwASAB8ADQAaAA4AGwAiAA4AGQANAB8AEgAQABwAHwARASAAEgAc -AB8AGgAOACEADQAQABwAGQAcAB8ADQAfABIAIAASACEAbAASABYADwASAB8ADQAgABoADgAfACEADQAf -ABIAEAAcAB8AEQEcABIAHAAfABoADgAhAA0ADgAZABYAFAAbAA0AHwAWABQAFQAhAHcAEgASAA4AIQAi -AB8AEgARAA0AHQAZAA4AJgANABkAFgAgACEBzAARABYAGQAhABIAHwANACEAFgAZACEADQAgABUAFgAT -ACEBIQARABwAHwAaAA4AIQANABAAHAAZABwAHwANACEAEgAlACEBGwARABwAHwAaAA4AIQANAA4AGQAW -ABQAGwANABkAEgATACEBLgARABwAHwAaAA4AIQANACIAGwARABIAHwAZABYAGwASABEBHwARABwAHwAa -AA4AIQANABAAHAAZABwAHwANABMAFgAZABkBLgAQABwAHwAaAA4AIQANACIAGwARABIAHwAZABYAGwAS -AxEAEAAOACMAHAAfABYAIQASAA0AHAAiACEAGQAWABsAEgMRAA8ADgAjABwAHwAWACEAEgANAA8AHAAf -ABEAEgAfAnUADwAiABkAGQAgABAAHwASABIAGwANABIAJQAWACEBxgAOABYAGQAhABIAHwANAA8ADQAO -ABsAEQANACQDlQAOABkAFgAUABUAIQANACEADgAYABIAHAATABMDwgAOABYAIQAbABIAIAAgAA0AEAAS -ABsAIQASAB8AeAAOABIADgAhACIAHwASABEADQAjABYAEQASABwBzQAOABYAGQAhABIAHwANACMAFgAb -ACEADgAUABIDwwAOAB8AEgASAA0ADwAfABIADgAYABMADgAgACECiwAOABwAGQARABIAHwANACAAHQAS -ABAAFgAOABkBxQANABYAGQAhABIAHwANAAwADQAdABkAIgAgAVIADQAWABkAEgANABEAHAAkABsAGQAc -AA4AEQDQAA0AHAAbACEADQARABwAJAAbABkAHAAOABEBJAANABwAHwAaAA4AIQANABYAIQAOABkAFgAQ -AVYADQAcABkAEQASAB8ADQAgABUADgAfABIAEQHJAA0AFgAZACEAEgAfAA0AEwAfAA4AGgASACADFgAN -ABkAFgAdAA0AIQAcAA0AEwAfABwAGwAhAUMADQAcAB8AGgAOACEADQAgABUADgAdABIAIAEpAAwAHAAf -ABoADgAhAA0AHgAiABwAIQASAxUADAAZABYAHQANACEAHAANAA8ADgAQABgBHgAMABwAHwAaAA4AIQAN -ABAAGQASAA4AHwHIAAwAFgAZACEAEgAfAA0AEQAfAA4AGgAOASgADAAcAB8AGgAOACEADQAdAA4AFgAb -ACEDFAAMABYAGwARAA0AHwASAB0AGQAOABAAEgMTAAwAFgAbABEADQAWABsADQAdAA4AFAASADEADAAO -ACAAIQANABMAHAAfACQADgAfABEBywALABYAGQAhABIAHwANABsAHAAbABIAuwALABYAGQAhABIAHwAN -ABkAFgAgACEDnAALABYAGwAUABIAHwAdAB8AFgAbACEBKgALABwAHwAaAA4AIQANACAAFgAnABIBVQAL -ABwAGQARABIAHwANABwAHQASABsDlAALABkAFgAUABUAIQANABkADgAbABEBHQALABwAHwAaAA4AIQAN -AA8AHAAZABEAMgALAA4AIAAhAA0AHwASACQAFgAbABEBUwALABYAGQASAA0AIgAdABkAHAAOABECdAAK -ACIAGQAZACAAEAAfABIAEgAbAGEACgAcAB8AJAAOAB8AEQANAAYAAwHKAAoAFgAZACEAEgAfAA0AFQAR -AB8BzwAKABkADgAgABUADQAOACIAIQAcAGAACgAcAB8AJAAOAB8AEQANAAQAAwKAAAoAFgAfACAAIQAN -AB0ADgAUABIB0AAJABkADgAgABUADQAcABMAEwEvAAkAIgAbABAAIQAWABwAGwAgAHQACQAWAA8AEgAf -AA0AHQAWABsAaAAJABYADwASAB8ADQAbABIAJABiAAkAHAAfACQADgAfABEADQAIAGcACQAWAA8AEgAf -AA0AEQAjAB8BwgAIABYAGQAhABIAHwANAAoBwQAIABYAGQAhABIAHwANAAkBwAAIABYAGQAhABIAHwAN -AAgBvwAIABYAGQAhABIAHwANAAcDEgAIABIAEgARAA8ADgAQABgBvQAIABYAGQAhABIAHwANAAYBvAAI -ABYAGQAhABIAHwANAAUBuwAIABYAGQAhABIAHwANAAQB0QAIABkADgAgABUADQAcABsDEAAIAA4AIwAc -AB8AFgAhABIBxAAIABYAGQAhABIAHwANAAwBwwAIABYAGQAhABIAHwANAAsAvQAHABwAHwAkAA4AHwAR -Ai0ABgAZABYAFAAVACEBVAAGABwAGQARABIAHwG+AAYAFgAZACEAEgAfAIsABQAcAB8AIgAaAc4ABQAZ -AA4AHwASAdIABAAZABYAHQC8AAQAGQAOABQDDwAEAA4AEAASABUALABIAGAAeACOAKQAuADMAN4A8AEA -ARABIAEwAUABTAFYAWQBcAF8AYYA7wANAB0AIAANABsAHAAhAA0AEwAWACUAEgARA8QACwAcABkAEwAN -ABAAHAAiAB8AIAASA7UACwANACEAHwAOABsAIAAZAA4AIQASAPMACgAfAA4AHQAVABYAEAANABIAHgMZ -AAoAHwAcACIAHQANACQAHAAfABgCwAAJAB8AHAAiAB0ADQAOABEAEQDuAAkAHQAgAA0AEwAWACUAEgAR -AdUACAAfABYAEQANABwAEwATAdMACAAfAA4AEQAWABIAGwAhAdYABwAfABYAEQANABwAGwMXAAcAEgAh -AA0ADgAdAB0AvgAHABIAIAAhACIAHwASAV8ABwAOABoAEgAdAA4AEQDwAAcAHQAgAA0AHAATABMDGAAF -AB8ADgARABIDnQAFAA4AIwASABkAMwAFAA4AGgASACAB1AAFAB8ADgAWABsCvwAFAB8AHAAiAB0DzAAE -ABwADgAhA5cAAwAWABMAFwAwAFIAcgCQAKwAxgDgAPgBDgEiATQBRAFUAWQBdAGEAZQBogGuAboBxAHO -AdgDGwAQABYAFAAVABkAFgAUABUAIQANAB8AEgAaABwAIwASAx4ADwAcACIAHwAUABkADgAgACAADQAS -ABoAHQAhACYDHwAOABwAIgAfABQAGQAOACAAIAANABMAIgAZABkDGwANABYAFAAVABkAFgAUABUAIQAN -ABwAEwATADUADAAWABQAFQANAB4AIgAOABkAFgAhACYDjgAMABIAGQAdAA0AHAAiACEAGQAWABsAEgFh -AAsAEgAOABEAIAASACEADQAaABYAEAHZAAoAEQAfAA0AIAAhAB8AHAAbABQBRAAJABYAFAAVABkAFgAU -ABUAIQHaAAgAEQAfAA0AJAASAA4AGAHbAAcAEgAOABkAFgAbABQDHAAHABYAIAAhABwAHwAmAWAABwAS -AA4AEQAgABIAIQPFAAcAHAAhAA0AIQAiAA8ANAAHABIADgAfABYAGwAUAdcABwARAB8ADQAcABMAEwHY -AAYAEQAfAA0AHAAbAi4ABQAcACEAEgAZAyAABQAhACEAHQAgAxoABAASABkAHQOSAAQAIQAhAB0DHQAE -ABwAGgASAF0AAgARABUALABcAIIApgDKAO4BEgE0AVQBdAGSAa4BygHkAf4CGAIwAjwCSAJUAl4DmAAX -ABsAEQASACEAEgAfABoAFgAbAA4AIQASAA0AEAAVABIAEAAYAA0ADwAcACUB3QASABoADgAUABIADQAO -ACAAHQASABAAIQANAB8ADgAhABYAHAEyABEAGwAgABIAHwAhAA0AEQAfABYAIwASAA0AEwAWABkAEgCN -ABEAGwAjABIAHwAhAA0AEAAcABkAHAAfACAADQAcABMAEwOgABEAGgAdABwAHwAhAA4AGwAhAA0AEQAS -ACMAFgAQABIAIAE0ABEAGwAgABIAHwAhAA0AFgAbACMAFgAhAA4AIQAWABwAGwMkABAAGwAjABIAHwAh -AA0AEAAcABkAHAAfACAADQAcABsAqAAPABoAHQAcAB8AIQANABAAHAAbACEADgAQACEAIAEzAA8AGwAg -ABIAHwAhAA0AEgAaABwAIQAWABAAHAAbATEADgAbACAAEgAfACEADQAQABwAGgAaABIAGwAhAyQADQAb -ACMAEgAfACEADQAQABwAGQAcAB8AIACMAA0AGgAdABwAHwAhAA0AEgAlAB0AHAAfACEDIgAMABsAEwAc -AA0AHAAiACEAGQAWABsAEgE2AAwAGwAgABIAHwAhAA0AHQAVABwAIQAcATAADAAbACAAEgAfACEADQAQ -ABUADgAfACEBNQALABsAIAASAB8AIQANABkAFgAbABgDIwAFABsAHQAiACEAvwAFABsADwAcACUB3AAF -ABoADgAUABIDIQAEABsAEwAcAd4AAwAgABwADQAcAEYAbgCWALwA4AEEASYBRgFkAYABmgGsAWUAFAAS -ACYADwAcAA4AHwARAA0ADgAfAB8AHAAkAA0AHwAWABQAFQAhAWQAEwASACYADwAcAA4AHwARAA0ADgAf -AB8AHAAkAA0AGQASABMAIQFjABMAEgAmAA8AHAAOAB8AEQANAA4AHwAfABwAJAANABEAHAAkABsBZwAS -ABIAJgAPABwADgAfABEADQAPAA4AEAAYACAAHQAOABAAEgFmABEAEgAmAA8AHAAOAB8AEQANAA4AHwAf -ABwAJAANACIAHQFoABEAEgAmAA8AHAAOAB8AEQANABAADgAdACAAGQAcABAAGAJ3ABAAEgAmAA8AHAAO -AB8AEQANABAAHAAbACEAHwAcABkBagAPABIAJgAPABwADgAfABEADQAfABIAIQAiAB8AGwFsAA4AEgAm -AA8AHAAOAB8AEQANACMAHAAWABAAEgFpAA0AEgAmAA8AHAAOAB8AEQANABUAFgARABIBawAMABIAJgAP -ABwADgAfABEADQAhAA4ADwFiAAgAEgAmAA8AHAAOAB8AEQPGAAcAFgAhABAAFQASABsATwCgANAA/AEk -AUoBbgGSAbYB2gH+AiACQgJkAoYCpgLEAuIDAAMeAzwDWgN2A5IDrgPKA+YEAgQeBDoEVgRwBIoEpAS+ -BNgE8gUMBSQFPAVUBWwFhAWcBbQFzAXkBfoGEAYmBjwGUgZoBnwGkAakBrgGzAbgBvQHCAccBy4HQAdQ -B2AHcAeAB5AHoAeuB7wHygfWB+IH7gf4CAIIDAgWAjcAFwAcABAADgAZAA0AEAAcABsAIwASABsAFgAS -ABsAEAASAA0AIAAhABwAHwASAj4AFQAcABAADgAZAA0AGQAOACIAGwARAB8AJgANACAAEgAfACMAFgAQ -ABICOwATABwAEAAOABkADQAUAB8AHAAQABIAHwAmAA0AIAAhABwAHwASAPIAEgAcABAADgAhABYAHAAb -AA0AIAASAA4AHwAQABUAFgAbABQCSAARABwAEAAOABkADQAdABwAIAAhAA0AHAATABMAFgAQABICOgAR -ABwAEAAOABkADQAUAA4AIAANACAAIQAOACEAFgAcABsA8QARABwAEAAOACEAFgAcABsADQARABYAIAAO -AA8AGQASABEDngARABYAFAAVACEADwAiABkADwANABwAIgAhABkAFgAbABIBbgARAA4AHQAhABwAHQAN -ABAAFQAfABwAGgASAA8AHAAcABgCTgAQABwAEAAOACEAFgAcABsADQAVABYAIAAhABwAHwAmAjMAEAAc -ABAADgAZAA0ADgAhACEAHwAOABAAIQAWABwAGwJJABAAHAAQAA4AGQANAB0AHwAWABsAIQANACAAFQAc -AB0CSgAQABwAEAAOABkADQAfABIAIAAhAA4AIgAfAA4AGwAhAkkADwAcABAADgAZAA0AHQAfABYAGwAh -ACAAFQAcAB0CNgAOABwAEAAOABkADQAQAA4AHwANACQADgAgABUCMwAOABwAEAAOABkADQAOABAAIQAW -ACMAFgAhACYCTAAOABwAEAAOABkADQAgABUAFgAdAB0AFgAbABQCPAAOABwAEAAOABkADQAVABwAIAAd -ABYAIQAOABkBcAAOAA4AHQAhABwAHQANACQAFgAbABEAHAAkACACRAAOABwAEAAOABkADQAdABUADgAf -ABoADgAQACYCOQANABwAEAAOABkADQATABkAHAAfABYAIAAhAkMADQAcABAADgAZAA0AHQAOAB8AGAAW -ABsAFAIeAA0AFgAbABgAEgARAA0AEAAOABoAEgAfAA4CPwANABwAEAAOABkADQAZABYADwAfAA4AHwAm -AD0ADQAWAA8AHwAOAB8AJgANABoAIgAgABYAEAA8AA0AFgAPAB8ADgAfACYADQAPABwAHAAYACACwQAN -ABwAEAAOACEAFgAcABsADQAQABYAIQAmAjEADQAcABAADgAZAA0ADgAWAB8AHQAcAB8AIQMmAA0ADgAP -ABIAGQANABwAIgAhABkAFgAbABIAjwAMABwAEAAOACEAFgAcABsADQAcABMAEwMsAAwAHAAQABgADQAc -ACIAIQAZABYAGwASAkoADAAcABAADgAZAA0AEQAWABsAFgAbABQCMAAMAA4AJgASAB8AIAANABAAGQAS -AA4AHwDWAAwAHAAkAA0AHQAfABYAHAAfABYAIQAmAkEADAAcABAADgAZAA0AGgAcACMAFgASACABRQAM -ABYAGwASAA4AHwANACAAEAAOABkAEgCQAAsAHAAQAA4AIQAWABwAGwANABwAGwJGAAsAHAAQAA4AGQAN -AB0AFgAnACcADgI4AAsAHAAQAA4AGQANABEAHwAWABsAGAA7AAsAFgAPAB8ADgAfACYADQAOABEAEQHh -AAsAEgAOABgADQAfABIAGgAcACMAEgI9AAsAHAAQAA4AGQANABUAHAAhABIAGQJCAAsAHAAQAA4AGQAN -ABwAEwATABIAHwJFAAsAHAAQAA4AGQANAB0AFQAcABsAEgOoAAsAFgAbABIADQAkABIAFgAUABUAIQOn -AAoAFgAbABIADQAgACEAJgAZABICQAAKABwAEAAOABkADQAaAA4AGQAZAjUACgAcABAADgAZAA0AEAAO -ABMAEgFvAAoADgAdACEAHAAdAA0AGgAOABACTQAKABwAEAAOABkADQAhAA4AJQAWAkcACgAcABAADgAZ -AA0AHQAZAA4AJgJLAAkAHAAQAA4AGQANACAAEgASAjQACQAcABAADgAZAA0ADwAOAB8CMgAJABwAEAAO -ABkADQAOACEAGgKBAAkADgAgACEADQAdAA4AFAASAekACQAcABwAGAAgAA0AIQAkABwDKwAJABwAEAAY -AA0AHAAdABIAGwHoAAkAHAAcABgAIAANABwAGwASAd8ACQAOABsAEQAgABAADgAdABIAjgAJABYAIwAS -AA0AFQASABkAHQHgAAgAEgAOABgADQAOABEAEQMnAAgADgAbABQAIgAOABQAEgMtAAcAHAAmAA4AGQAh -ACYB5wAHABwAHAAYACAADQAJAeYABwAcABwAGAAgAA0ACAHlAAcAHAAcABgAIAANAAcCqwAHABYAIwAS -AA0AIQAjAeMABwAcABwAGAAgAA0ABgIvAAYADgAmABIAHwAgAygABgAOACIAGwAQABUBbQAGAA4AHQAh -ABwAHQHqAAUAHAAiAB0AEgHkAAUAHAAcABgAIAMlAAUADgAPABIAGQHiAAQAEgAbACAAwAAEABYAGwAY -ADYABAAcABwAHQMpAAQAFgAgACEDKgAEABwAEAAYACcAUAB2AJoAvgDgAQIBJAFEAWQBggGgAbwB1gHw -AgoCIgI6AlACZgJ8ApICqAK8AtAC5AL4AwoDHAMsAzwDSgNWA2IDbAN2A4ADigOSA5oDLgASAA4AHwAY -ACIAGwAfABIADgARAA0AGgAOABYAGQAPABwAJQHrABEAHAAbABwAEAAVAB8AHAAaABIADQAdABUAHAAh -ABwAIACTABEAEgAgACAAEgAbABQAEgAfAA0AHAAiACEAGQAWABsAEgA8ABAAJgANABkAFgAPAB8ADgAf -ACYADQAPABwAHAAYACAA8wAQACIAGQAhABYAIQAfAA4AEAAYAA0ADgAiABEAFgAcAD0AEAAmAA0AGQAW -AA8AHwAOAB8AJgANABoAIgAgABYAEAFIAA8AHAAbABIAIQAWACcADgAhABYAHAAbAA0AHAAbArsADwAi -ABkAIQAWABkAFgAbABIADQAQABUADgAfACEB7AAOABwAIwAWABIADQAQAB8AEgAOACEAFgAcABsAOwAO -ACYADQAZABYADwAfAA4AHwAmAA0ADgARABEA0QANABwAIwASAA0AIQAcAA0AFgAbAA8AHAAlAKkADAAO -ABYAGQANABwAIgAhABkAFgAbABIBOAAMABwAEQASAA0AEAAcABoAGgASABsAIQIgAAwAHAAjABYAEgAN -ABMAFgAZACEAEgAfAlAACwAmAA0AGQAcABAADgAhABYAHAAbAG0ACwAiACAAFgAQAA0AIwAWABEAEgAc -AncACgAcAB8AEgANABUAHAAfABYAJwE3AAoAEgAfABQAEgANACEAJgAdABIB7QAKACIAIAAWABAADQAb -ABwAIQASAMIACgAOAB8AGAAiABsAHwASAA4AEQOpAAoAHAAhABwAHwAQACYAEAAZABICeAAJABwAHwAS -AA0AIwASAB8AIQE5AAkAHAARABIADQASABEAFgAhAUEACQAcABsAEgAmAA0AHAATABMAkgAJABIAIAAg -ABIAGwAUABIAHwLDAAgAHAAcABEADQAPAA4AEQA4AAgAFgAQAA0AGwAcABsAEgA5AAcAFgAQAA0AHAAT -ABMAkQAHABIAIAAgAA4AFAASAXEABgASABoAHAAfACYBcgAFABwAIgAgABIAOgAFABwAIwAWABICwgAE -ABwAHAARAMEABAAOABYAGQKNAAQAHAAfABICdgAEABIAGwAiAk8AAwAOAB0CjAADABoAIAA3AAMAFgAQ -ABoANgBgAIoAsADUAPYBFgE0AVIBbgGKAaYBwgHeAfoCFAIuAkgCYAJ2AooCnAKsAroCyALSAsgAFAAc -ACEAFgATABYAEAAOACEAFgAcABsAIAANAB0ADgAiACAAEgARAscAFAAcACEAFgATABYAEAAOACEAFgAc -ABsAIAANAA4AEAAhABYAIwASAsUAEgAcACEAFgATABYAEAAOACEAFgAcABsAIAANABsAHAAbABICxgAR -ABwAIQAWABMAFgAQAA4AIQAWABwAGwAgAA0AHAATABMCxwAQABwAIQAWABMAFgAQAA4AIQAWABwAGwAg -AA0AHAAbAfAADwAOACMAFgAUAA4AIQASAA0ADwASABMAHAAfABICjgAOABIAIQAkABwAHwAYAA0AGQAc -ABAAGAASABEAPwAOABwAIQANABYAGwAhABIAHwASACAAIQASABECswANABwADQASABsAEAAfACYAHQAh -ABYAHAAbAsQADQAcACEAFgATABYAEAAOACEAFgAcABsAIAKyAA0AEgAhACQAHAAfABgADQAQABUAEgAQ -ABgB8QANAA4AIwAWABQADgAhABIADQAbABIAJQAhAPcADQAcACQADQAkAA4AGQAZAB0ADgAdABIAHwHv -AA0ADgAhACIAHwASAA0AHQASABwAHQAZABIAPgAMABIAJAANAB8AEgAZABIADgAgABIAIAD1AAwAEgAh -ACQAHAAfABgADQAkABYAEwAWAPQADAASACEAJAAcAB8AGAANABAAEgAZABkA+AALABwAJAANACQAFgAR -ABQAEgAhACACUQAKAA4AIwAWABQADgAhABYAHAAbANMACQASACUAIQANACQAEgASABgDLwAIABwAIQAS -AA0ADgARABECXQAHABIADgAfAA0AGgASAJQABgAcAA0AIAAWABoB7gAGAA4AIQAiAB8AEgB5AAQAHAAh -ABIA9gADABMAEAAGAA4ALgBMAGQAfACQAzAADwAdABIAGwANABYAGwANAA8AHwAcACQAIAASAB8CrAAO -ABsAEQASABoADgAbABEADQAjABYAEQASABwDMQALAB0AEgAbAA0AFgAbAA0AGwASACQDmQALABMAEwAZ -ABYAGwASAA0AHQAWABsDMgAJAB0AEgAbAA0AJAAWACEAFQOqAAcAHQAOABAAFgAhACYAVwCwAOIBEgFC -AXIBogHQAfwCJgJOAnYCngLGAuwDEgM4A14DhAOoA8wD8AQUBDgEWgR8BJ4EwATgBQAFIAVABV4FfAWa -BbgF1gX0BhIGMAZOBmwGiAakBsAG3Ab4BxQHMAdMB2YHgAeaB7QHzgfoCAIIGggyCEgIXgh0CIoIoAi0 -CMgI2gjsCP4JEAkiCTQJRAlUCWQJdAmCCZAJngmqCbYJwgnOCdoJ5gnyCfwKBgIYABgAFQAcACEAHAAN -ACAAFgAnABIADQAgABIAGQASABAAIQANAA4AEAAhACIADgAZAhkAFwAVABwAIQAcAA0AIAAWACcAEgAN -ACAAEgAZABIAEAAhAA0AGQAOAB8AFAASAhoAFwAVABwAIQAcAA0AIAAWACcAEgANACAAEgAZABIAEAAh -AA0AIAAaAA4AGQAZAo8AFwAVABwAGwASAA0ADwAZACIAEgAhABwAHAAhABUADQAgAB0AEgAOABgAEgAf -AzgAFwASAB8AGgANABEAEgAjABYAEAASAA0AFgAbABMAHAAfABoADgAhABYAHAAbA58AFgAWABAAIQAi -AB8AEgANABYAGwANAB0AFgAQACEAIgAfABIADQAOABkAIQM2ABUAEgAfABoADQAQABwAGwAhAA4AEAAh -AA0AEAAOABkAEgAbABEADgAfAEIAFAAOACIAIAASAA0AEAAWAB8AEAAZABIADQAcACIAIQAZABYAGwAS -AEUAEwAZAA4AJgANABAAFgAfABAAGQASAA0AHAAiACEAGQAWABsAEgBBABMADgAiACAAEgANABAAFgAf -ABAAGQASAA0AEwAWABkAGQASABEB9wATAA4AGwAcAB8ADgAaAA4ADQAkABYAEQASAA0ADgAbABQAGQAS -AfUAEwAOABsAHAAfAA4AGgAOAA0AFQAcAB8AFgAnABwAGwAhAA4AGQM/ABIAHAAkABIAHwANACAAEgAh -ACEAFgAbABQAIAANABsAEgAkAEQAEgAZAA4AJgANABAAFgAfABAAGQASAA0AEwAWABkAGQASABECuQAS -ABYAEgANABAAFQAOAB8AIQANABwAIgAhABkAFgAbABIAEQBvABIAGQAOACYAGQAWACAAIQANAA4AEQAR -AA0AEAAVABIAEAAYAz0AEgAWABAAIQAiAB8AEgANABYAGwANAB0AFgAQACEAIgAfABIDNwARABIAHwAa -AA0AEQAOACEADgANACAAEgAhACEAFgAbABQCXgARABIAHwAgABwAGwANAB0AFgAbAA0AEAAWAB8AEAAZ -ABIB9gARAA4AGwAcAB8ADgAaAA4ADQAjABIAHwAhABYAEAAOABkAlgARABwAHwAhAA4ADwAZABIADQAk -ABYAEwAWAA0AHAATABMB9AARAA4AGwAcAB8ADgAaAA4ADQATABYAIAAVAA0AEgAmABIARAAQABkADgAm -AA0AEAAWAB8AEAAZABIADQATABYAGQAZAzgAEAASAB8AGgANABEAEgAjABYAEAASAA0AFgAbABMAHAM2 -ABAAEgAfABoADQAQABwAGwAhAA4AEAAhAA0AEAAOABkB9AAQAA4AGwAcAB8ADgAaAA4ADQATABYAIAAV -ABIAJgASAzUADwASAB8AGgANABAADgAaABIAHwAOAA0AGgAWABAAowAPABUAHAAbABIAGQAWABsAGAAN -ABIAHwAOACAAEgKQAA8AFQAcABsAEgANABMAHAAfACQADgAfABEAEgARAKYADwAVABwAGwASABkAFgAb -ABgADQAgABIAIQAiAB0CrQAOABIAHwAgABwAGwAOABkADQAjABYAEQASABwApQAOABUAHAAbABIAGQAW -ABsAGAANAB8AFgAbABQCzAAOABIAHAAdABkAEgANABwAIgAhABkAFgAbABICzwAOABIAHwAgABwAGwAN -ABwAIgAhABkAFgAbABIDPAAOABIAHwAaAA0AIAAQAA4AGwANACQAFgATABYDOwAOABIAHwAaAA0AHQAV -ABwAGwASAA0AGgAgABQApAAOABUAHAAbABIAGQAWABsAGAANABkAHAAQABgApwAOAB8AEgAgABIAGwAh -AA0AIQAcAA0ADgAZABkB/AAOABYAEAAhACIAHwASAA0ADgAgAA0AHQARABMDrAAOAB8AEgAUABsADgAb -ACEADQAkABwAGgAOABsCtwANAB8AFgAcAB8AFgAhACYADQAVABYAFAAVAzkADQASAB8AGgANABYAEQAS -ABsAIQAWACEAJgOWAA0AGQAOACYADQATABwAHwANACQAHAAfABgBcwANABUAHAAbABIADQAOABsAEQAf -ABwAFgARAXYADQAVABwAGwASABkAFgAbABgADQAcABMAEwBpAA0AGQAOACYAGQAWACAAIQANAB0AGQAO -ACYB+wANABUAHAAhABwADQAZABYADwAfAA4AHwAmApEADQAVABwAGwASAA0AFgAbAA0AIQAOABkAGAKU -AAwAFQAcABsAEgANAB0ADgAiACAAEgARApMADAAVABwAGwASAA0AGgAWACAAIAASABECkgAMABUAHAAb -ABIADQAZABwAEAAYABIAEQF0AAwAFQAcABsAEgANABYAHQAVABwAGwASAfoADAAVABwAIQAcAA0AEAAO -ABoAEgAfAA4ARgAMABkADgAmABkAFgAgACEADQAOABEAEQIhAAwAFQAcACEAHAANABMAFgAZACEAEgAf -AfkACwAVABwAIQAcAA0ADgAZAA8AIgAaAYUACwAcACQAEgAfAA0AFgAbAB0AIgAhAEMACgAZAA4AJgAN -AA4AHwAfABwAJAM6AAoAEgAfABoADQAaABIAEQAWAA4CzgAKABIAHwAgABwAGwANAA4AEQARAk4ACgAS -AB8AIAAcABsADQAdABYAGwLKAAoADgAfACEAJgANABoAHAARABICuAAJABYAEgANABAAFQAOAB8AIQF1 -AAkAFQAcABsAEgAZABYAGwAYAlIACAAWABsADQARAB8AHAAdAf0ACAAcAB8AIQAfAA4AFgAhAtAACAAZ -ACIAIAANABwAGwASAfMACAAOABsAHAAfAA4AGgAOA7MACAAOABsADQAhABwAHAAZAzMACAAOABQAEgAj -ABYAEgAkAzQABwAOACYAGgASABsAIQE6AAcAIgAPABkAFgAgABUB8gAHAA4AGQASACEAIQASAz4ABwAc -ABkAJgAaABIAHwLSAAYAIgAPABkAFgAQAssABgASABwAHQAZABICzQAGABIAHwAgABwAGwNAAAUAHwAW -ABsAIQBAAAUADgAiACAAEgH4AAUAFQAcACEAHAKuAAUAHAAkABIAHwJTAAUAGQAOABAAEgCVAAUAFQAc -ABsAEgLJAAUADgAUABIAIALRAAQAHAAZABkDxwAEABwAHAAZA6sABAASACEAIAAHABAAPABkAIQApADA -ANgAlwAVACIAFgAQABgADQAQABwAGwAhAA4AEAAhACAADQARABYADgAZABIAHwCYABMAIgAWABAAGAAN -ABAAHAAbACEADgAQACEAIAANABoADgAWABkAcAAPACIAEgAiABIADQAdABkADgAmAA0AGwASACUAIQNC -AA8AIgASACAAIQAWABwAGwANAA4AGwAgACQAEgAfA0EADQAiABIAHwAmAA0ADwAiABYAGQARABIAHwBI -AAsAIgASACIAEgANABoAIgAgABYAEABHAAUAIgASACIAEgAsAFoAiAC0AOABCgE0AVgBfAGeAb4B3gH8 -AhoCOAJUAnACigKkAr4C1gLuAwYDHAMyA0YDWgNuA4IDlAOmA7YDxgPWA+YD9AQCBBAEHgQsBDoESARU -BGAEagLdABYADgARABYAHAANAA8AIgAhACEAHAAbAA0AIgAbABAAFQASABAAGAASABEAxgAVABIAGgAc -ACMAEgANABAAFgAfABAAGQASAA0AHAAiACEAGQAWABsAEgH/ABUAHAAhAA4AIQASAA0ADAADAA0AEQAS -ABQAHwASABIAIAANABAAEAAkA7YAFAASABoAHAAjABIADQAgABUAHAAdAB0AFgAbABQADQAQAA4AHwAh -At4AFAAOABEAFgAcAA0ADwAiACEAIQAcABsADQAQABUAEgAQABgAEgARAHEAEQASABoAHAAjABIADQAT -AB8AHAAaAA0AHgAiABIAIgASA60AEQASABAAHAAfABEADQAjABwAFgAQABIADQAcACMAEgAfAt0AEAAO -ABEAFgAcAA0ADwAiACEAIQAcABsADQAcABMAEwJVAA8AEgAgACEADgAiAB8ADgAbACEADQAaABIAGwAi -At4ADwAOABEAFgAcAA0ADwAiACEAIQAcABsADQAcABsDrgAOABwAIgAbABEAEgARAA0AEAAcAB8AGwAS -AB8B/gAOABIAGgAcACMAEgANAB8AEgARAA0AEgAmABIDRQAOABIAHQAcAB8AIQANAB0AHwAcAA8AGQAS -ABoAxQANABIAGgAcACMAEgANABAAFgAfABAAGQASAEoADQASABAAEgAbACEADQAOABAAIQAcAB8AIAPI -AAwAHAAcABoADQAgABIAHwAjABYAEAASA7cADAASACAAIQAcAB8AEgANAB0ADgAUABICAQAMABwAIQAO -ACEAEgANAB8AFgAUABUAIQCZAAsAFgAbABQADQAjABwAGQAiABoAEgJUAAsADgAhABIADQAfABIAIwAW -ABIAJAIAAAsAHAAhAA4AIQASAA0AGQASABMAIQJgAAoAEgAgACEADgAiAB8ADgAbACEATAAKABIAHQAS -AA4AIQANABwAGwASAMgACQASAB0AGQAmAA0ADgAZABkAZAAJABIAHQAZAA4AJgANAAYAAwBjAAkAEgAd -ABkADgAmAA0ABAADArQACQAjAA0AFQAcABwAGAAiAB0AZQAIABIAHQAZAA4AJgANAAgArQAIACAAIAAN -ABMAEgASABEDjwAHABIAHAAfABEAEgAfA0YABwASACAAIQAcAB8AEgNDAAcAEgAQABIAFgAdACECeQAH -ABIAEwAfABIAIAAVA68ABgAcACQAFgAbABQASwAGABIAHQASAA4AIQDJAAYAEgAdABwAHwAhAMQABgAS -ABoAHAAjABIBdwAGABwAIgAhABIAHwNEAAYAEgARABIAEgAaAE0ABgASAB0AGQAOACYAxwAFABIAHQAZ -ACYASQAFAA4AEQAWABwDRwAEABwAHAAaAMMABAASABEAHAB1AOwBRAF8Aa4B4AISAkQCdgKmAtYDBAMy -A2ADjgO8A+oEFgRCBG4EmgTGBPIFHAVGBXAFmgXEBewGFAY8BmQGigawBtQG+AccB0AHZAeIB6wH0Afy -CBQINghWCHYIlgi2CNYI9AkSCTAJTglsCYgJpAnACdwJ+AoUCjAKTApoCoQKoAq8CtYK8AsKCyQLPAtS -C2gLfguUC6oLwAvWC+wMAgwYDC4MRAxYDGwMgAyUDKgMvAzQDOIM9A0GDRgNKg06DUoNWg1qDXoNig2Y -DaYNtA3CDc4N2g3mDfIN/A4GDhAOGg4kDi4OOA5AAQAAKwAWABQAGwAOABkADQAQABIAGQAZACIAGQAO -AB8ADQAQABwAGwAbABIAEAAhABIAEQANABsAHAANABYAGwAhABIAHwAbABIAIQANAAcADQAPAA4AHwLZ -ABsAEgAbACEAFgAaABIAGwAhAA0AIwASAB8AJgANABEAFgAgACAADgAhABYAIAATABYAEgARAP4AGAAS -ACEAIQAWABsAFAAgAA0AIAAmACAAIQASABoADQARAA4AJgARAB8AEgAOABoDUwAYABIAIQAhABYAGwAU -ACAADQAWABsAHQAiACEADQAQABwAGgAdABwAIAAWACEAEgNSABgAEgAhACEAFgAbABQAIAANABYAGwAd -ACIAIQANABAAHAAaAB0AHAAbABIAGwAhAtoAGAASABsAIQAWABoAEgAbACEADQAjABIAHwAmAA0AIAAO -ACEAFgAgABMAFgASABECfgAYACIADwARABYAHwASABAAIQAcAB8AJgANAA4AHwAfABwAJAANAB8AFgAU -ABUAIQJ9ABcAIgAPABEAFgAfABIAEAAhABwAHwAmAA0ADgAfAB8AHAAkAA0AGQASABMAIQNMABcAEgAh -ACEAFgAbABQAIAANAA8ADgAQABgAIgAdAA0AHwASACAAIQAcAB8AEgEBABYAFgAUABsADgAZAA0AEAAS -ABkAGQAiABkADgAfAA0AGwAcAA0AIAAWABoDUQAWABIAIQAhABYAGwAUACAADQAWABsAHQAiACEADQAO -ABsAIQASABsAGwAOAJ0AFgAhAA4AJgANAB0AHwAWABoADgAfACYADQAZAA4AGwARACAAEAAOAB0AEgCb -ABYAIQAOACYADQAQACIAHwAfABIAGwAhAA0AGQAOABsAEQAgABAADgAdABIBBQAWABYAFAAbAA4AGQAN -ACQAFgATABYADQAHAA0ADwAOAB8ADQAZABwAEAAYAtYAFgASABsAIQAWABoAEgAbACEADQARABYAIAAg -AA4AIQAWACAAEwAWABIAEQNVABUAEgAhACEAFgAbABQAIAANABYAGwAdACIAIQANACAAIwAWABEAEgAc -A0sAFQASACEAIQAWABsAFAAgAA0ADgAdAB0AGQAWABAADgAhABYAHAAbACAAnAAVACEADgAmAA0AEAAi -AB8AHwASABsAIQANAB0AHAAfACEAHwAOABYAIQD/ABUAFgAUABsADgAZAA0AEAASABkAGQAiABkADgAf -AA0ABwANAA8ADgAfAJ4AFQAhAA4AJgANAB0AHwAWABoADgAfACYADQAdABwAHwAhAB8ADgAWACEA+QAV -ABAAHwASABIAGwANABkAHAAQABgADQAZAA4AGwARACAAEAAOAB0AEgECABQAFgAUABsADgAZAA0AEAAS -ABkAGQAiABkADgAfAA0AGwAiABkAGQJXABQAIQAcAB8AEgANABoADgAZABkADQARABYAHwASABAAIQAc -AB8AJgD7ABQAEAAfABIAEgAbAA0AGQAcABAAGAANAB8AHAAhAA4AIQAWABwAGwD6ABQAEAAfABIAEgAb -AA0AGQAcABAAGAANAB0AHAAfACEAHwAOABYAIQNnABQAJAAOAB0ADQAjABIAHwAhABYAEAAOABkADQAQ -ABYAHwAQABkAEgEDABMAFgAUABsADgAZAA0AEAASABkAGQAiABkADgAfAA0AHAATABMDTwATABIAIQAh -ABYAGwAUACAADQAPAB8AFgAUABUAIQAbABIAIAAgA1QAEwASACEAIQAWABsAFAAgAA0AFgAbAB0AIgAh -AA0AFQARABoAFgLYABMAEgAbACEAFgAaABIAGwAhAA0AIAAOACEAFgAgABMAFgASABEDTQASABIAIQAh -ABYAGwAUACAADQAPABkAIgASACEAHAAcACEAFQNkABIAIgAdABIAHwAjABYAIAAcAB8ADQAOABAAEAAc -ACIAGwAhAQQAEQAWABQAGwAOABkADQAkABYAEwAWAA0ABwANAA8ADgAfAKsAEQAhABwAHQANACAAEAAf -ABIAEgAbAA0AIAAVAA4AHwASA1AAEQASACEAIQAWABsAFAAgAA0AEgAhABUAEgAfABsAEgAhAtcAEQAS -ABsAIQAWABoAEgAbACEADQAbABIAIgAhAB8ADgAZA1YAEQASACEAIQAWABsAFAAgAA0AHAAjABIAHwAg -ABAADgAbA7gAEQAdABIADgAYABIAHwANABsAHAAhABIAIAANABwAEwATAHIAEQAZABwAJAANABoAHAAh -ABYAHAAbAA0AIwAWABEAEgAcA2gAEQAmACAAIQASABoADQAiAB0AEQAOACEAEgANAA4AGQAhA08AEAAS -ACEAIQAWABsAFAAgAA0AEQAWACAAHQAZAA4AJgNnABAAJAAOAB0ADQAjABIAHwAhAA0AEAAWAB8AEAAZ -ABIDaAAQACYAIAAhABIAGgANACIAHQARAA4AIQASAA0AIQAjATwADwAhAB8AFgAYABIAIQAVAB8AHAAi -ABQAFQANACAA/AAPABAAHwASABIAGwANAB8AHAAhAA4AIQAWABwAGwNdAA8AFQAcAB0AHQAWABsAFAAN -AA8ADgAgABgAEgAhAQYADwAWABQAGwAOABkADQAkABYAEwAWAA0AHAATABMDWQAPABIAIQAhABYAGwAU -ACAADQAfABIAGgAcACEAEgKWAA4AFgAaAA0AEAAOAB8AEQANAA4AGQASAB8AIQBUAA4AIgAfAB8AHAAi -ABsAEQANACAAHAAiABsAEQNYAA4AEgAhACEAFgAbABQAIAANAB0AHAAkABIAHwNXAA4AEgAhACEAFgAb -ABQAIAANAB0AFQAcABsAEgNaAA4AEgAhACEAFgAbABQAIAANACMAHAAWABAAEgIFAA0AJAAWACEAEAAV -AA0AEAAOABoAEgAfAA4DXwANAB0AEgAOABgAEgAfAA0AGwAcACEAEgAgAX0ADQAdABIADgAYABIAHwAN -ABQAHwAcACIAHQBeAA0AHAAfACEADQAPACYADQAOABkAHQAVAA4DygANABoAHAAYABYAGwAUAA0AHwAc -ABwAGgAgApwADQAmACAAIQASABoADQAiAB0AEQAOACEAEgNeAA0AFQAcAB0AHQAWABsAFAANABAADgAf -ACEAUAANABgAFgAdAA0AHQAfABIAIwAWABwAIgAgAG4ADQAiAA8AIAAQAB8AFgAdACEAFgAcABsAIANO -AA0AEgAhACEAFgAbABQAIAANABAAEgAZABkCmgANACYAGwAQAA0AEQAWACAADgAPABkAEgARAJoADQAd -ABIADgAYABIAHwANAB0AFQAcABsAEgIGAAwAJAAWACEAEAAVAA0AIwAWABEAEgAcApsADAAmABsAEAAN -AB0AHwAcAA8AGQASABoC4QAMACEADgAfAA0AHAAiACEAGQAWABsAEgCqAAwAEAAfABIAEgAbAA0AIAAV -AA4AHwASAuEACwAhAA4AHwANAA8AHAAfABEAEgAfA2AACgAdABIAGQAZABAAFQASABAAGAKYAAoAGgAg -AA0AEwAOABYAGQASABEDZQAKACQADgAdAA0AFQAcAB8AFgAnA8kACgAaABwAGAASAA0AEwAfABIAEgF7 -AAoAGgAOAB8AIQAdABUAHAAbABIAywAKABIAGQASABAAIQANAA4AGQAZAP0ACgARAA0AIAAhABwAHwAO -ABQAEgCfAAoAJAAOAB0ADQAQAA4AGQAZACACYgAKACEAHwASABIAIQAjABYAEgAkAgMACgAhAB8ADgAW -ABQAFQAhABIAGwK8AAoAFQAcACQADQAQABUADgAfACEBRgAKABUAHAAfACEADQAhABIAJQAhAFMACQAi -AA8AIQAWACEAGQASACAC4AAJACEADgAfAA0AFQAOABkAEwICAAkAGQAWABEAEgAgABUAHAAkA2YACQAk -AA4AHQANACMAEgAfACEATwAJABgAFgAdAA0AGwASACUAIQE7AAkAHQAOABAAEgANAA8ADgAfAlYACQAO -ACEAEgAZABkAFgAhABIBeQAIABIAEAAiAB8AFgAhACYBegAIABYAGgANABAADgAfABEDXAAIABUAHAAd -AA0AIQAkABwDSAAIABAAFQASABEAIgAZABIDSgAIABIAIQAhABYAGwAUACADYwAHACIADwAXABIAEAAh -AE4ABwAVACIAEwATABkAEgKVAAcAEQANABAADgAfABEBfAAHAB0AEgAOABgAEgAfAXgABwAQAA4AGwAb -ABIAHwEHAAcAIQAcAB8ADgAUABIC0wAGABAAFQAcABwAGQBRAAYAGwAcABwAJwASA0kABgASAA4AHwAQ -ABUCYwAGACIADwAkAA4AJgNhAAUAIQAOAB8AIALUAAUAFQAOAB8AEgIEAAUAIQAmABkAEgNiAAUAIQAc -AB8AEgDNAAQAHAAfACEC3wAEACEADgAfA1sABAAVABwAHQDMAAQAEgAbABECmQAEACYAGwAQAMoABAAO -ACMAEgBSAAQAIQAcAB0ClwADABoAIAPLAAMAHQAOAC0AXACQALIA0ADuAQwBKAFEAWABfAGYAbIBygHi -AfoCEAImAjoCTgJiAnYCigKeArICxALWAugC+gMMAxwDLAM8A0wDXANqA3YDggOOA5oDpAOuA7gDwgPK -A9ICZgAZAB8ADgAbACAAEwASAB8ADQAkABYAIQAVABYAGwANAA4ADQAgACEADgAhABYAHAAbA3UAEAAf -ABIAGwARABYAGwAUAA0AGwASACIAIQAfAA4AGQNuAA4AFQAiABoADwAgAA0AIgAdAA0AEQAcACQAGwF/ -AA4ADgAPABkAEgAhAA0ADgAbABEAHwAcABYAEQNqAA4ADgAPAA0AIgAbACAAEgAZABIAEAAhABIAEQNy -AA0AHwAOABAAGAANABAAFQAOABsAFAASACACngANABYAGgASAA0AIQAcAA0AGQASAA4AIwASA3gADQAi -AB8AGwASABEADQAWABsADQAbABwAIQN0AA0AHwASABsAEQAWABsAFAANABEAHAAkABsDdQANAB8AEgAb -ABEAFgAbABQADQATABkADgAhAp0ADAAOAB0ADQAOABsAEQANAB0AGQAOACYDdgALAB8AEgAbABEAFgAb -ABQADQAiAB0AzgALABIAJQAhAA0AEwAcAB8AGgAOACEBRwALABIAJQAhAA0AEwAWABIAGQARACADbAAK -ABUAIgAaAA8ADQARABwAJAAbAYAACgAOAA8AGQASACEADQAaAA4AEAOhAAkAHAAiABAAFQANAA4AHQAd -Ag0ACQAWABoAEgAfAA0AHAATABMCCQAJABYAGgASABkADgAdACAAEgIHAAkADgAUAA0AEwAOABAAEgAg -Ag8ACQAfAA4AGwAgABMAHAAfABoDdwAJACIAHwAbABIAEQANABYAGwNzAAkAHwAOABsAIAAZAA4AIQAS -A20ACAAVACIAGgAPAA0AIgAdAg4ACAAcABsADgAZABYAIQAmA2sACAAVABIADgAhABIAHwAgAgoACAAW -ABoAEgAfAA0ABAADA7AACAAWABoAEgAZABYAGwASAggABwASACUAIQAiAB8AEgCgAAcAEgAlACEAIAAa -ACACWQAHAB8ADgATABMAFgAQAgsABwAWABoAEgAfAA0ABgJYAAcAEgAfAB8ADgAWABsBfgAGAA4ADwAZ -ABIAIQFJAAUAFgAhABkAEgIMAAUAFgAaABIAHwJkAAUAHwAOABYAGwNwAAUAHAARAA4AJgGBAAQAHAAm -ACACZQAEAB8ADgAaAhAABAAiABsAEgNxAAQAHAAZABkDbwADABwAEANpAAMADgAPAYIAAgAjAAYADgAm -AD4AUgBgAGoCewALABsAEwAcABkAEQANABoAHAAfABICegALABsAEwAcABkAEQANABkAEgAgACAA0gAJ -ABsADgAfABAAFQAWACMAEgOxAAYAHQARAA4AIQASAM8ABAAbABEAHAEIAAMAIAAPACUATAB4AKQAygDs -AQ4BLgFMAWgBhAGgAbwB1gHwAggCIAI4AlACaAKAApgCrgLEAtoC8AMGAxwDMgNGA1oDbgOCA5YDqAO6 -A8wD3gE+ABUAEgAfACEAFgAQAA4AGQANAA4AGQAWABQAGwANABAAEgAbACEAEgAfAT0AFQASAB8AIQAW -ABAADgAZAA0ADgAZABYAFAAbAA0ADwAcACEAIQAcABoBPwASABIAHwAhABYAEAAOABkADQAOABkAFgAU -ABsADQAhABwAHQBVABAAFgARABIAHAANABAAHAAZABkAEgAQACEAFgAcABsCEQAQABYAEgAkAA0AEAAc -ABoAEwAcAB8AIQAOAA8AGQASAYcADwAWABEAEgAcABQADgAaABIADQAOACAAIAASACEDhgAOABYAIAAW -AA8AFgAZABYAIQAmAA0AHAATABMDfwANABYAEgAkAA0AFQASAA4AEQAZABYAGwASAFUADQAWABEAEgAc -AA0AGQAWAA8AHwAOAB8AJgN8AA0AFgASACQADQAQAA4AHwAcACIAIAASABkDeQANABIAHwAWABMAFgAS -ABEADQAiACAAEgAfAhIADAAWABIAJAANABAAHAAaAB0ADgAQACEAVwAMABYAEQASABwAEAAOABoADQAc -ABMAEwODAAsAFgASACQADQAgACEAHwASAA4AGgN9AAsAFgASACQADQAQABwAGQAiABoAGwB7AAsAFgAR -ABIAHAANABkADgAPABIAGQOBAAsAFgASACQADQAaABwAEQAiABkAEgN6AAsAFgASACQADQAOABQAEgAb -ABEADgBYAAsAHAAZACIAGgASAA0AEQAcACQAGwBZAAsAHAAZACIAGgASAA0AGgAiACEAEgOFAAoAFgAg -ABYADwAWABkAFgAhACYDewAKABYAEgAkAA0ADgAfAB8ADgAmAHoACgAWABEAEgAcAA0AEAAOABkAGQKg -AAoAHAAWABAAEgANABAAFQAOACECEQAKABYAEgAkAA0AEAAcABoAEwAmA4IACgAWABIAJAANAB4AIgAW -ABkAIQBaAAoAHAAZACIAGgASAA0AHAATABMCnwAJABYADwAfAA4AIQAWABwAGwOEAAkAFgASACQADQAk -ABIAEgAYA4AACQAWABIAJAANABkAFgAgACEAoQAJABwAFgAQABIAGgAOABYAGQBbAAkAHAAZACIAGgAS -AA0AIgAdAhsACAAWABQAGwASACEAIQASA34ACAAWABIAJAANABEADgAmAFYACAAWABEAEgAcABAADgAa -AqEACAAdABsADQAZABwAEAAYAKIABwAdABsADQAYABIAJgAXADAAVAB0AJQAsgDOAOoBAgEWASoBPgFS -AWYBeAGKAZoBqgG6AcoB1gHgAeoB8gOIABEADgAZABkAEgAhAA0AGgASABoADwASAB8AIAAVABYAHQIV -AA8ADwANABYAGwAQAA4AGwARABIAIAAQABIAGwAhA4cADwAOABkAGQASACEADQAUABYAEwAhABAADgAf -ABEBCgAOABYAEwAWAA0AIQASACEAFQASAB8AFgAbABQCHAANAA8ADQAWAB8AFgARABIAIAAQABIAGwAh -A4kADQAOABkAGQASACEADQAhAB8ADgAjABIAGQOyAAsADgAhABAAFQANABkADgAhABIAHwBzAAkAEgAP -AA0ADgAgACAAEgAhAPcACQAOABkAGQAdAA4AHQASAB8BQAAJAB8ADgAdAA0AIQASACUAIQEJAAkAFgAT -ABYADQAZABwAEAAYAhQACQAPAA0AEAAZABwAIgARACYC1QAIABUADgAhACAAFQAcACECFgAIAA8ADQAg -ACIAGwAbACYA+AAHABYAEQAUABIAIQAgAhMABwAPAA0ADgAiACEAHADUAAcAEgASABgAEgAbABEAKgAH -AA4AHwAbABYAGwAUAYMABQAOACEAEAAVA4oABAAcAB8AGAKwAAQAFgATABYAXAADABIADwKvAAIAEAAB -AAQDiwAUABwAIgAhACIADwASAA0AIAASAA4AHwAQABUAEgARAA0AEwAcAB8AAwAIACIANAJfAAwAHAAc -ABoADQAcACIAIQANABoADgAdA5EACAAcABwAGgANABwAIgAhA5AABwAcABwAGgANABYAGwACAAQABgAG -AAAADgAWAAEAGAAkAAoAJgAnABc= -'''; - -String? _RobotoMonoRegular_ttf; -// RobotoMonoRegular_ttf md5 is 'b4618f1f7f4cee0ac09873fcc5a966f9' -String _RobotoMonoRegular_ttf_base64 = ''' -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 -BHIF3gImAE8AAAEHAHMAnAAVAAixAQGwFbAzK///AFH+hgSQBbACJgAjAAAABgCkJQD//wCc/oYENgRO -AiYAQwAAAAYApOoA////if/sBGEGVgImADEAAAEHAb/9lACSAAixAgKwkrAzK///ALYAAAQ0ByMCJgAn -AAABBwBC/48BWgAJsQEBuAFasDMrAP//AKIAAAQqBxcCJgDZAAABBwBC/4IBTgAJsQEBuAFOsDMrAP// -AIf/7ARFBeICJgBHAAABBgBCkBkACLECAbAZsDMr//8ApQAABCcFzAImAOwAAAEHAEL/fQADAAixAQGw -A7AzK///AGUAAARyBbACBgC2AAD//wBh/igEgAQ6AgYAygAA//8AGgAABOEHQgImARUAAAEHAKkEWQFU -AAmxAQK4AVSwMysA//8ANQAABGAGGgImARYAAAEHAKkEMAAsAAixAQKwLLAzK///AFn+LwRwBcQCJgDY -AAABBgHB8ZYACbEBAbj/lrAzKwD//wCH/jkESgRNAiYA6wAAAQYBwQigAAmxAQG4/6CwMysA//8Aa/45 -BF0FxAImACUAAAEGAcH/oAAJsQEBuP+gsDMrAP//AI/+OQQzBE4CJgBFAAABBgHBE6AACbEBAbj/oLAz -KwD//wA9AAAEeQWwAgYAOwAA//8AR/5gBJYEOgIGALoAAP//AK4AAAQeBbACBgArAAD//wAdAAAErgdK -AiYA1wAAAQcAngANAZgACbEBAbgBmLAzKwD//wARAAAErAXzAiYA6gAAAQYAnvRBAAixAQGwQbAzK/// -AK4AAAQeBbACBgArAAD//wBRAAAEkAdKAiYAIwAAAQcAngAPAZgACbECAbgBmLAzKwD//wCc/+wENgYI -AiYAQwAAAQYAngtWAAixAgGwVrAzK///AFEAAASQByACJgAjAAABBwBoAA8BWwAJsQICuAFbsDMrAP// -AJz/7AQ2Bd4CJgBDAAABBgBoCxkACLECArAZsDMr//8AIAAABKsFsAIGAH8AAP//ACv/7ASpBE4CBgCE -AAD//wC2AAAENAdKAiYAJwAAAQcAngAFAZgACbEBAbgBmLAzKwD//wCH/+wERQYJAiYARwAAAQYAngZX -AAixAgGwV7AzK///AFr/6wRXBvICJgFDAAABBwBo//sBLQAJsQICuAEtsDMrAP//ALH/7ARfBE8CBgCb -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 -KzorvywfLHAszy0LLW4tkC2xLeMuAy5GLnwu0y70L0QvWi9wL3kvsi/TL/UwDzBNMFUwcjCXMQMxIjFR -MXExpjH+Mj8ymDNLM940BzSFNP41UjWiNgc2QTdUN8k4KziFOOE5MzlzObQ6FjphOsQ7XDvYPEA82z0/ -PZ0+Az5HPnE+uj78Pz0/tD/aQA5AUEB6QLtA5kEcQWNBrUHuQlJCx0MRQ4pD5EQBRElEi0UDRShFV0WO -RbhF4EYARiFGfkaoRuBHC0dBR4hH0kgTSHdI7Uk4SaFKBUpjSrJK/EsqS3pLyEwcTMxNWk2PTcdOC05P -TqpPBE9gT7tQSFDTUUZRpFHrUkBSyVNoVBtU8VWgVnFW8FdwV85YLFhrWMRZD1koWUBayFtVW6dcH1xC -XGVcp1zrXS9dcV2mXdteCl45Xn9eyV9JX8Nf+mAtYJZhDGFMYcFiNWJ4YrxjBmNQY7ZkCmR1ZIpkoGTm -ZVxl3GZLZrFnIGeJZ/xodWjiaUtprGoJalRqnmsaaxprGmsaaxprGmsaaxprGmsaaxprGmsaayVrMGtC -a2Jrgmuha6xruWvGa/hsImxfbI5sm2ysbX9tom3Lbd1uEW5sbttveG/lcGhwsnEOcTlxenGSca5xynHi -cjtyUXJycoxyqHMoc11zxXPsdAV0HnRFdF90X3VFdZ915XYjdlR2cXaPdrx213cGd3B3xXgHeDR4W3i4 -eOB5CXk/eWt5h3m1edt6KnqGes57RXtle6F7xnvzfCJ8R3xxfK98w3zufTR9Z32nfb5+Y350frB+0n8b -fzR/Zn+pf9iAToBWgF6Ar4EAgWCBkYHuggCCEoIkgjaCSIJagmyCd4KJgpuCrYK/gtGC44L1gweDGYMr -gz2DT4Nhg3ODhYOXg6mDu4PNg92D7oP/hBCEIIQwhECES4RbhGuEe4SLhJuEp4S4hMiE2ITohPiFCIUZ -hSmFOYVJhVmFaYV6hYqFnIWshb6FzoYmhsKG1IblhveHCIcahyqHPIdMh16HcIeCh5KHpIe0h8aH1ogs -iKyIvojOiOCI8IkCiRKJJIk0iUaJVoloiXqJjImdia+JwInSieKKNYqbiq2Kv4rRiuOK9YsHixmLK4s9 -i0+LYYtzi4WLl4uni7mLy4vdi+2L+YwLjBuMLYw9jE+MYIxyjIKMlIymjLiMyIzajOuM/Y0OjR+NMI1C -jVSNZo12jYiNmo2ljbWNwY3TjeSN9o4HjhmOKo48jkyOXo5ujoCOkY8Hj2aPeI+Ij5qPq4+9j8+P4I/y -kAKQFJAkkDaQR5BZkGmQe5CNkJ6QrpC+kM6Q35DvkQCREZEikTORQ5FTkWORc5GEkZWRpZG2kcaR15Ho -kfmSCZIZkiqSO5JLkluSa5J7ktWS5ZL1kwWTFZMlkzWTRZOak6qTupPKk9yT7ZP+lA6UHpRwlICUkZSj -lLSUxpTYlOqU+5UNlR2VLZU9lU6VXpVwlYCVkJWglauVu5XLldyV7JX8lg2WHpaVlqaWtpbGlteW55b3 -lwOXD5cglzGXPZdJl1WXZpdul3aXfpeGl46Xlpeel6aXrpe2l76XxpfOl9aX6Jf6mAqYGpgqmDuYTJhU -mFyYZJhsmHSYhJiUmKSYtZjGmNiY6plamWKZdJl8mYSZlpmombCZuJnAmciZ2pnimeqZ8pn6mgKaCpoS -mhqaIpoqmjKaQppKmlKarJq0mryazJrXmt+a55r3mv+bCpsamyybPJtOm16bcJuAm5Kbopuqm7KbvpvP -m9eb6Zv6nAWcEJwhnDOcRZxVnGacbpx2nIicmZyqnLuczJzdnOWc7Zz1nQedF50fnTGdQZ1TnWOda51z -nYWdlZ2nna+dv53RneGd854DnhWeJp44nkieWp5qnnKeep6Mnpyerp6+ntCe4Z7znwOfFZ8mnzifSJ9a -n2qfdp+Cn4qflp+in66fup/Mn92f75//oBGgIaAzoESgVqBnoH2gkqCkoLSgxqDWoOig+KEKoRqhMKFE -oVWhYaFzoYShlqGnobmhyaHboeuh/aIOoiCiMaJLol+icaKDopSipaK3osmi26Lsov6jDqMgozCjQqNT -o2WjdqOQo6mju6PLo92j7aP/pBCkIqQzpD+kUaRdpGmke6SNpJ+kqqS8pMyk3qTwpQKlE6UlpTGlQqVU -pWaleKWKpZumOaZSpmOmb6Z7poemk6afpqumt6bDps+m26bnpvOm/6cLpxenKac7p0OnlafmqDeoZaiT -qNOpE6ktqUapWKlqqXypjqmgqbKp5qodqluqmaqhqrOqvqq+qr6rGAABAAAAAgBBdgxeKl8PPPUACwgA -AAAAAMTwES4AAAAA0YKzW/wF/dUHmQhzAAAACQACAAAAAAAABM0AAAAAAAAEzQAABM0AAATNAWIEzQA9 -BM0AogTNACwEzQBrBM0B7gTNAWUEzQFABM0AoATNAHcEzQFiBM4A2gTNAfAEzQD8BM0AkQTNANAEzQBV -BM0AXgTNAEsEzQC7BM0AjQTNAHAEzQCxBM0AlQTNAiIEzQHmBM0AqgTNAK0EzQCyBM0AvwTNAEAEzQBR -BM0ArATNAGsEzQCbBM0AtgTNAL8EzQBkBM0AjQTNAK4EzQBiBM0ArATNAMYEzQCUBM0AjwTNAGoEzQC/ -BM0AXgTNALUEzQB2BM0ATATNAIsEzQBHBM0ASQTNAFcEzQA9BM0AcgTNAaoEzQDnBM0BlQTNAOcEzQCb -BM0BnwTNAJwEzQCvBM0AjwTNAIsEzQCHBM4AmATNAIwEzQCuBM0AywTNANMEzQCwBM0AywTNAF0EzQCu -BM0AegTNAK0EzQCMBM0BSQTNAK8EzQCOBM0AtATNAGIEzQAwBM0AbgTNAEQEzQCgBM0BQwTNAhwEzQFD -BM0AMATNAfIEzQCTBM0AcQTNAGcEzQAhBM0B/wTNAFcEzQEfBM0AWgTNARwEzQDUBM0AvQTNAFcEzQEB -BM0BaQTNAJwEzQE8BM0BQwTNAZoEzQC8BM0A0wTNAfgEzQHNBM0BggTNARAEzQDxBM0AMATNACQEzQAl -BM0AzATNACAEzQC1BM0ARwTNAKgEzQCpBM0AKwTNAEkEzQBzBM0AegTNAK0EzQAYBM0AywTNAIAEzQBQ -BM0AugTNADoEzQDLBM0ArwTNALgEzQBPBM0ALgTNAcgEzQCgBM0AYwTNAHcEzQCLBM0AtATNALAEzQCx -BM0AwQTNATAEzQE7BM0B8gTNAZoEzQGOBM0AigTNAPYEzQGtBFL8ygRS/WgEUvyIBFL9WQRS/AUEUv0n -BM0CKQTNARMEzQIwBM0AtQTNAC4EzQBqBM0ANgTNAJEEzQCiBM0AcATNAEUEzQBlBM0AYQTNAIEEzQCu -BM0ARwTNAHgEzQCLBM0AdQTNAKQEzQC5BM0AuATNADkEzQCvBM0AWQTNAKUEzQB4BM0AbQTNAK0EzQCe -BM0AbgTNAGEEzQBPBM0AmATNADYEzQAuBM0AKgTNAIEEzQAeBM0AgwTNAEMEzQCiBM0AogTNAEYEzQAd -BM0AWQTNAKIEzQAvBM0AKwTNAKYEzQCrBM0AfQTNAH0EzQAyBM0AkATNAKgEzQByBM0AdwTNAEEEzQCB -BM0ApATNALcEzQA2BM0AEQTNAIcEzQClBM0ApATNADcEzQCJBM0ApQTNAKUEzQBoBM0AegTNAKoEzQCN -BM0AgQTNAHYEzQA5BM0AkATNAKUEzQCBBM0AcQTNAE8Ezf/pBM0AjwTNACYEzQCCBM0AHATNAKUEzQBr -BM0AXwTNABwEzQB9BM0AmwTNACcEzQBXBM0AcQTNAHAEzQBVBM0AaQTNAFAEzQBRBM0AygTNAN4EzQBj -BM0AXQTNABoEzQBRBM0ARQTNADcEzQBqBM0AegTNAE0EzQBnBM0AcQTNAF8EzQCXBM0AvwTNAHYAAADR -AAAA/AAAAcMAAAI8AAD+5wAAAE4EzQC/BM0ArQTNALYEzQC2BM0AuQTNALgEzQCuBM0AowTNAC0EzQA4 -BM0AcgTNAG4EzQBtBM0AdATNAGgEzQBcBM0AOQTNADQEzQCrBM0AkgTNAOMEzQAmBM0AJgTNAMgEzQC0 -BM0AtgTNALMEzQBaBM0AlATNAIkEzQBCBM0AdATNAGEEzQBNBM0AZQTNADcEzQBQBM0AswTNANAEzQAU -BM0ALwTNAG8EzQB1BM0AjgTNAKAEzQBMBM0ASQTNAGwEzQAABM0AAATNAAAEzQAABM0AAATNAAAEzQAA -BM0AAATNAAAEzQAABM0AAATNAAAEzQBJBM0ATgTNAKYEzQHsBM0BzQTNAbwEzQHPBM0BSQTNAS0EzQEv -BM0AdwTNAHkEzQGaBM0BXATNAQkEzQA2BM0BjATNAYwEzQErBM0BDwTNAUsEzQBpBM0AfwTNAH8EzQBJ -BM0A3QTNAHkEzQBnBM0AmQTNAFAEzQAyBM0AJATNAEAEzQB+BM0ApwTNADMEzQCpBM0AOQTNADUEzQD4 -BM0AjQTNAKkEzQC7BM0AwgTNAI4EzQEuBM0AAATNAFwEzQAPBM0BOgTNAREEzQD8BM0AEATNAQsEzQEw -BM0B+QTNADYEzQDQBM0AbgTNALcEzQDIBM0A5wTNAHwEzQCbBM0A2QTNAJYEzQC0BM0A0QTNAJsEzQDC -BM0AggTNAF4EzQCQBM0AigTNAF0EzQC1BM0AVgTNAC8EzQBgBM0ATQTNALkEzQFSBM0BWwTNAUIEzQE4 -BM0BIwTNAUAEzQEwBM0BPgTNATUEzQHRBM0B9QTNAXQEzQIGBM0BYATNAMwEzQCpBM0A0gTNAAAEzgDa -BOv/1ATr/9QE6wAaBM0ATATNAI4EzQBRBM0AUQTNAFEEzQBRBM0AUQTNAFEEzQBRBM0AawTNALYEzQC2 -BM0AtgTNALYEzQCuBM0ArgTNAK4EzQCuBM0AjwTNAGoEzQBqBM0AagTNAGoEzQBqBM0AiwTNAIsEzQCL -BM0AiwTNAD0EzQCcBM0AnATNAJwEzQCcBM0AnATNAJwEzQCcBM0AjwTNAIcEzQCHBM0AhwTNAIcEzQDL -BM0AywTNAMsEzQDLBM0ArgTNAHoEzQB6BM0AegTNAHoEzQB6BM0AtATNALQEzQC0BM0AtATNAEQEzQBE -BM0AUQTNAJwEzQBRBM0AnATNAFEEzQCcBM0AawTNAI8EzQBrBM0AjwTNAGsEzQCPBM0AawTNAI8EzQCb -BWMAiwTNALYEzQCHBM0AtgTNAIcEzQC2BM0AhwTNALYEzQCHBM0AtgTNAIcEzQBkBM0AjATNAGQEzQCM -BM0AZATNAIwEzQBkBM0AjATNAI0EzQCuBM0ArgTNAMsEzQCuBM0AywTNAK4EzQDLBM0ArgTNAMsEzQCu -BM0AYgTNALAEzQCsBM0AsATNAMYEzQDLBM0AxgTNAMsEzQDGBWMAywTNAMYFqQDLBM0AjwTNAK4EzQCP -BM0ArgTNAI8EzQCuBM3/ugTNAGoEzQB6BM0AagTNAHoEzQBqBM0AegTNALUEzQFJBM0AtQTNARAEzQC1 -BM0BFATNAHYEzQCvBM0AdgTNAK8EzQB2BM0ArwTNAHYEzQCvBM0AdgTNAK8EzQBMBM0AjgTNAEwEzQBd -BM0AjgTNAEwE9QCOBM0AiwTNALQEzQCLBM0AtATNAIsEzQC0BM0AiwTNALQEzQCLBM0AtATNAIsEzQC0 -BM0ASQTNADAEzQA9BM0ARATNAD0EzQByBM0AoATNAHIEzQCgBM0AcgTNAKAEzQAgBM0AKwTNAEcEzQB6 -BM3/5wTN/+cEzQBdBM0ANgTNADYEzQA2BM0ANgTNADYEzQA2BM0ANgTNAG4EzQDIBM0AyATNAMgEzQDI -BM0A2QTNANkEzQDZBM0A2QTNAMIEzQCCBM0AggTNAIIEzQCCBM0AggTNALUEzQC1BM0AtQTNALUEzQBN -BM0ANgTNADYEzQA2BM0AbgTNAG4EzQBuBM0AtwTNAMgEzQDIBM0AyATNAMgEzQDIBM0AfATNAHwEzQB8 -BM0AmwTNANkEzQDZBM0A2QTNANkEzQDZBM0AlgTNALQEzQC2BM0A0QTNANEEzQDRBM0AwgTNAMIEzQDC -BM0AggTNAIIEzQCCBM0AkATNAJAEzQCQBM0AigTNAIoEzQCKBM0AigTNAF0EzQC1BM0AtQTNALUEzQC1 -BM0AtQTNALUEzQAvBM0ATQTNAE0EzQC5BM0AuQTNALkEzQBRBTEADwUx//0FMf/sBOEACQUx/7IE4QAF -BM0AuATNAFEEzQCsBM0AtgTNAHIEzQCNBM0ArgTNAKwEzQCUBM0AjwTNAGoEzQC/BM0ATATNAD0EzQBX -BM0ArgTNAD0EzQCBBM0AiwTNAKQEzQC4BM0AngTNALoEzQB6BM0AvATNAGIEzQBuBM0AuATNAJ4EzQB6 -BM0AngTNAE8EzQC2BM0AtQTNAHYEzQCuBM0ArgTNAGIEzQCpBM0ArATNACsEzQBRBM0ArATNALUEzQC2 -BM0AogTNAJQEzQCNBM0AagTNAKIEzQC/BM0AawTNAEwEzQBFBM0AVwTNAJwEzQCHBM0ApQTNAHoEzQCt -BM0AjwTNAEQEzQBuBM0AhwTNALcEzQCvBM0AywTNAMsEzQDTBM0ApATNAEQEzQBJBM0AMATNAEkEzQAw -BM0ASQTNADAEzQA9BM0ARATNAe4EzQFiCZoB5gTNALAEzQHNBM0AlATNAF0EzQBRBM0AnATN/4kEzQC2 -BM0AogTNAIcEzQClBM0AZQTNAGEEzQAaBM0ANQTNAFkEzQCHBM0AawTNAI8EzQA9BM0ARwTNAK4EzQAd -BM0AEQTNAK4EzQBRBM0AnATNAFEEzQCcBM0AIATNACsEzQC2BM0AhwTNAFoEzQCxBM0AsQTNAB0EzQAR -BM0AWQTNAIcEzQCiBM0ApQTNAKIEzQClBM0AagTNAHoEzQBjBM0AXQTNAGMEzQBdBM0AcgTNAIEEzQAr -BM0ARATNACsEzQBEBM0AKwTNAEQEzQCrBM0AjQTNAJAEzQCQBM0AVwTNAG4EzQCLBM0ALwTNADcEzQBR -BM0AnATNAFEEzQCcBM0AUQTNAJwEzf/8BM3/+ATNAFEEzQCcBM0AUQTNAJwEzQBRBM0AnATNAFEEzQCc -BM0AUQTNAJwEzQBRBM0AnATNAFEEzQCcBM0AUQTNAJwEzQC2BM0AhwTNALYEzQCHBM0AtgTNAIcEzQC2 -BM0AhwTN//IEzf/zBM0AtgTNAIcEzQC2BM0AhwTNALYEzQCHBM0ArgTNAMsEzQCuBM0AywTNAGoEzQB6 -BM0AagTNAHoEzQBqBM0AegTNAAAEzf/tBM0AagTNAHoEzQBqBM0AegTNAGoEzQB6BM0AYwTNAHcEzQBj -BM0AdwTNAGMEzQB3BM0AYwTNAHcEzQBjBM0AdwTNAIsEzQC0BM0AiwTNALQEzQCLBM0AtATNAIsEzQC0 -BM0AiwTNALQEzQCLBM0AtATNAIsEzQC0BM0APQTNAEQEzQA9BM0ARATNAD0EzQBEBOsAiwTrAIsEzQCp -BM0ApATNAI0EzQClBM0ATATNAGgEzQBXBM0AbgTNAKsEzQCNBM0AqwTNAI0EzQC1BM0AtwTNAB0EzQAR -BM0AJgTNACYEzQCuBM0AEgTN/9QEzf/UBM3//QTN//sE4QAJBOH/8wTNAKIApQCNAKUAlACJAC8ANwA9 -AEcAVwBuAIsAEQBOAAAAAAHmAAAAAQAACGL91QAACZr8BfhyB5kAAQAAAAAAAAAAAAAAAAAAA+AAAwTO -AZAABQAABZoFMwAAAR8FmgUzAAAD0QBmAgAAAAAAAAAAAAAAAADgAAL/EAAgWwAAACAAAAAAcHlycwBA -AAD//Qhi/dUAAAhiAisgAAGfTwEAAAQ6BbAAAAAgAAEAAAACAAAAAwAAABQAAwABAAAAFAAEBkQAAADg -AIAABgBgAAAAAgAJAA0AIQB+AKAArACtAL8AxgDPAOYA7wD+AQ8BEQElAScBMAFTAV8BZwF+AX8BkgGh -AbAB8AH/AhsCNwJZArwCxwLJAt0C8wMBAwMDCQMPAyMDigOMA5IDoQOwA7kDyQPOA9ID1gQlBC8ERQRP -BGIEbwR3BIYEzgTXBOEE9QUBBRAFEx4BHj8ehR7xHvMe+R9NIAsgFSAeICIgJiAwIDMgOiA8IEQgdCB/ -IKQgpyCsIQUhEyEWISIhJiEuIV4iAiIGIg8iEiIaIh4iKyJIImAiZSXK9sP+///9//8AAAAAAAIACQAN -ACAAIgCgAKEArQCuAMAAxwDQAOcA8AD/ARABEgEmASgBMQFUAWABaAF/AZIBoAGvAfAB+gIYAjcCWQK8 -AsYCyQLYAvMDAAMDAwkDDwMjA4QDjAOOA5MDowOxA7oDygPRA9YEAAQmBDAERgRQBGMEcAR4BIgEzwTY -BOIE9gUCBREeAB4+HoAeoB7yHvQfTSAAIBMgFyAgICUgMCAyIDkgPCBEIHQgfyCjIKcgqyEFIRMhFiEi -ISYhLiFbIgIiBiIPIhEiGiIeIisiSCJgImQlyvbD/v///P//AAED7APm//UAAP/iASb/wAEa/78AAAEN -AAABCAAAAQQAAAECAAABAAAAAPYAAAD5/xX/A/72/ukBOAAAAAD+Y/5CAG391v2l/cb9sf2l/aT9n/2a -/YcAAP9H/0YAAAAA/QcAAP8n/Pv8+AAA/LYAAPyuAAD8owAA/J8AAP5xAAD+bgAA/EcAAOUs5OzkneTL -5DHkyePh4VgAAOFP4U7hTOFD4vPhO+Lr4TLhA+D5AADg0wAA4HfgauBo4F3fkeBS4Cbfg96p33ffdt9v -32zfYN9E3y3fKtvGCs4CkwGXAAEAAAAAAAAAAADYAAAAAAAAAAAAAADQAAAA2gAAAQQAAAEeAAABHgAA -AR4AAAFgAAAAAAAAAAAAAAAAAWIBbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFaAAAAAAFiAX4AAAGW -AAAAAAAAAa4AAAH2AAACHgAAAkAAAAJMAAAC1gAAAuYAAAL6AAAAAAAAAAAAAAAAAAAAAALuAAAAAAAA -AAAAAAAAAAAAAAAAAAAC3gAAAt4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAADA/ABzQHOAc8B0AHRAdIAfwHJAd0B3gHfAeAB4QHiAIAAgQHjAeQB5QHmAecAggCDAegB6QHq -AesB7AHtAIQAhQH4AfkB+gH7AfwB/QCGAIcB/gH/AgACAQICAIgByAPDAIkBygCKAIsAjAIxAjICMwI0 -AI0CNQI2AjcCOAI5AjoCOwI8AI4AjwI9Aj4CPwJAAkECQgJDAJAAkQJEAkUCRgJHAkgCSQCSAJMCWAJZ -AlwCXgJfAmABywHMAdMB7gJ4AnkCegJ7AlYCVwJaAlsAqwCsAs8ArQLQAtEC0gCuAK8C2QLaAtsAsALc -At0AsQLeAt8AsgLgALMC4QC0AuIC4wC1AuQAtgC3AuUC5gLnAugC6QLqAusC7ADBAu4C7wDCAu0AwwDE -AMUAxgDHAMgAyQLwAMoAywMvAvYAzwL3ANAC+AL5AvoC+wDRANIA0wL9AzAC/gDUAv8A1QMAAwEA1gMC -ANcA2ADZAwMC/ADaAwQDBQMGAwcDCAMJAwoA2wMLAwwDDQDmAOcA6ADpAw4A6gDrAOwDDwDtAO4A7wDw -AxAA8QMRAxIA8gMTAPMDFAMxAxUA/gMWAP8DFwMYAxkDGgEAAQEBAgMbAzIDHAEDAQQBBQPaAzMDNAET -ARQBFQEWAzUDNgEmAScD3wPgA9kD2AEoASkBKgErA9sD3AEsAS0D0wPUAzcDOAPFA8YBLgEvA90D3gEw -ATEDxwPIATIBMwE0ATUBNgE3AzkDOgPJA8oDOwM8A+cD6APLA8wBOAE5A80DzgE6ATsBPAPXAT0BPgPV -A9YDPQM+Az8BPwFAA+UD5gFBAUID4QPiA88D0APjA+QBQwNKA0kDSwNMA00DTgNPAUQBRQPRA9IDZANl -AUYBRwNmA2cD6QPqAUgDaAPrA2kDagFkAWUD7QPsAXkDxAF7sAAsILAAVVhFWSAgS7gADlFLsAZTWliw -NBuwKFlgZiCKVViwAiVhuQgACABjYyNiGyEhsABZsABDI0SyAAEAQ2BCLbABLLAgYGYtsAIsIGQgsMBQ -sAQmWrIoAQpDRWNFUltYISMhG4pYILBQUFghsEBZGyCwOFBYIbA4WVkgsQEKQ0VjRWFksChQWCGxAQpD -RWNFILAwUFghsDBZGyCwwFBYIGYgiophILAKUFhgGyCwIFBYIbAKYBsgsDZQWCGwNmAbYFlZWRuwAStZ -WSOwAFBYZVlZLbADLCBFILAEJWFkILAFQ1BYsAUjQrAGI0IbISFZsAFgLbAELCMhIyEgZLEFYkIgsAYj -QrEBCkNFY7EBCkOwBGBFY7ADKiEgsAZDIIogirABK7EwBSWwBCZRWGBQG2FSWVgjWSEgsEBTWLABKxsh -sEBZI7AAUFhlWS2wBSywB0MrsgACAENgQi2wBiywByNCIyCwACNCYbACYmawAWOwAWCwBSotsAcsICBF -ILALQ2O4BABiILAAUFiwQGBZZrABY2BEsAFgLbAILLIHCwBDRUIqIbIAAQBDYEItsAkssABDI0SyAAEA -Q2BCLbAKLCAgRSCwASsjsABDsAQlYCBFiiNhIGQgsCBQWCGwABuwMFBYsCAbsEBZWSOwAFBYZVmwAyUj -YUREsAFgLbALLCAgRSCwASsjsABDsAQlYCBFiiNhIGSwJFBYsAAbsEBZI7AAUFhlWbADJSNhRESwAWAt -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 -BbAAAAXhBDoAAP5gCGL91QXE/+wF4QRO/+z+Swhi/dUAMgAyAAAADQCiAAMAAQQJAAAAXgAAAAMAAQQJ -AAEAFgBeAAMAAQQJAAIADgB0AAMAAQQJAAMALgCCAAMAAQQJAAQAFgBeAAMAAQQJAAUAVACwAAMAAQQJ -AAYAJAEEAAMAAQQJAAcASgEoAAMAAQQJAAkADAFyAAMAAQQJAAsAFAF+AAMAAQQJAAwAJgGSAAMAAQQJ -AA0AXAG4AAMAAQQJAA4AVAIUAEMAbwBwAHkAcgBpAGcAaAB0ACAAMgAwADEANQAgAEcAbwBvAGcAbABl -ACAASQBuAGMALgAgAEEAbABsACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgBSAG8AYgBv -AHQAbwAgAE0AbwBuAG8AUgBlAGcAdQBsAGEAcgBHAG8AbwBnAGwAZQA6AFIAbwBiAG8AdABvACAATQBv -AG4AbwA6ADIAMAAxADUAVgBlAHIAcwBpAG8AbgAgADIALgAwADAAMAA5ADgANQA7ACAAMgAwADEANQA7 -ACAAdAB0AGYAYQB1AHQAbwBoAGkAbgB0ACAAKAB2ADEALgAzACkAUgBvAGIAbwB0AG8ATQBvAG4AbwAt -AFIAZQBnAHUAbABhAHIAUgBvAGIAbwB0AG8AIABNAG8AbgBvACAAaQBzACAAYQAgAHQAcgBhAGQAZQBt -AGEAcgBrACAAbwBmACAARwBvAG8AZwBsAGUALgBHAG8AbwBnAGwAZQBHAG8AbwBnAGwAZQAuAGMAbwBt -AEMAaAByAGkAcwB0AGkAYQBuACAAUgBvAGIAZQByAHQAcwBvAG4ATABpAGMAZQBuAHMAZQBkACAAdQBu -AGQAZQByACAAdABoAGUAIABBAHAAYQBjAGgAZQAgAEwAaQBjAGUAbgBzAGUALAAgAFYAZQByAHMAaQBv -AG4AIAAyAC4AMABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBwAGEAYwBoAGUALgBvAHIAZwAvAGwAaQBj -AGUAbgBzAGUAcwAvAEwASQBDAEUATgBTAEUALQAyAC4AMAAAAAIAAAAAAAD/agBkAAAAAAAAAAAAAAAA -AAAAAAAAAAAD8QAAAQIAAgADAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZ -ABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3 -ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABV -AFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAKMAhACFAL0AlgDoAIYAjgCLAJ0AqQCkAIoA2gCDAJMA8gDz -AI0AlwCIAMMA3gDxAJ4AqgD1APQA9gCiAJAA8ACRAO0AiQCgAOoAuAChAO4BAwDXAQQBBQEGAOIA4wEH -AQgAsACxAQkApgEKAQsBDAENAQ4BDwDYAOEA2wDcAN0A4ADZAN8BEAERARIBEwEUARUBFgEXARgBGQEa -AKgBGwEcAR0BHgEfASABIQCfASIBIwEkASUBJgEnASgBKQEqASsBLACbAS0BLgEvATABMQEyATMBNAE1 -ATYBNwE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFT -AVQBVQFWAVcBWAFZAVoBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAFx -AXIBcwF0AXUBdgF3AXgBeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGP -AZABkQGSAZMBlAGVAZYBlwGYAZkBmgGbAZwBnQGeAZ8BoAGhAaIBowGkAaUBpgGnAagBqQGqAasBrAGt -Aa4BrwGwAbEBsgGzAbQBtQG2AbcBuAG5AboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHL -AcwAsgCzAc0AtgC3AMQBzgC0ALUAxQCCAMIAhwHPAKsAxgC+AL8AvAHQAdEB0gHTAdQB1QHWAdcAjAHY -AdkB2gHbAdwAmACaAJkA7wClAJIAnACnAI8AlACVALkB3QHeAd8B4AHhAeIB4wHkAeUB5gHnAegB6QHq -AesB7AHtAe4B7wHwAfEB8gHzAfQB9QH2AfcB+AH5AfoB+wH8Af0B/gH/AgACAQICAgMCBAIFAgYCBwII -AgkCCgILAgwCDQIOAg8CEAIRAKwCEgITAOkCFAIVAhYArQDJAMcArgBiAGMCFwBkAMsAZQDIAMoAzwDM -AM0AzgBmANMA0ADRAK8AZwDWANQA1QBoAOsAagBpAGsAbQBsAG4CGABvAHEAcAByAHMAdQB0AHYAdwB4 -AHoAeQB7AH0AfAB/AH4AgACBAOwAugIZAhoCGwIcAh0CHgD9AP4CHwIgAiECIgD/AQACIwIkAiUCJgIn -AigCKQIqAisCLAItAi4CLwIwAPgA+QIxAjICMwI0AjUCNgI3AjgCOQI6AjsCPAI9Aj4A+gI/AkACQQJC -AkMCRAJFAkYCRwJIAkkCSgJLAkwCTQJOAk8CUAJRAlICUwJUAlUCVgJXAlgCWQJaAlsCXAJdAl4CXwJg -AmEA+wD8AmICYwDkAOUCZAJlAmYCZwJoAmkCagJrAmwCbQJuAm8CcAJxAnICcwJ0AnUCdgJ3AngCeQJ6 -ALsCewJ8An0CfgDmAOcCfwKAAoECggKDAoQChQKGAocCiAKJAooCiwKMAo0CjgKPApACkQKSApMClAKV -ApYClwKYApkCmgKbApwCnQKeAp8CoAKhAqICowKkAqUCpgKnAqgCqQKqAqsCrAKtAq4CrwKwArECsgKz -ArQCtQK2ArcCuAK5AroCuwK8Ar0CvgK/AsACwQLCAsMCxALFAsYCxwLIAskCygLLAswCzQLOAs8C0ALR -AtIC0wLUAtUC1gLXAtgC2QLaAtsC3ALdAt4C3wLgAuEC4gLjAuQC5QLmAucC6ALpAuoC6wLsAu0C7gLv -AvAC8QLyAvMC9AL1AvYC9wL4AvkC+gL7AvwC/QL+Av8DAAMBAwIDAwMEAwUDBgMHAwgDCQMKAwsDDAMN -Aw4DDwMQAxEDEgMTAxQDFQMWAxcDGAMZAxoDGwMcAx0DHgMfAyADIQMiAyMDJAMlAyYDJwMoAykDKgMr -AywDLQMuAy8DMAMxAzIDMwM0AzUDNgM3AzgDOQM6AzsDPAM9Az4DPwNAA0EDQgNDA0QDRQNGA0cDSANJ -A0oDSwNMA00DTgNPA1ADUQNSA1MDVANVA1YDVwNYA1kDWgNbA1wDXQNeA18DYANhA2IDYwNkA2UDZgNn -A2gDaQNqA2sDbANtA24DbwNwA3EDcgNzA3QDdQN2A3cDeAN5A3oDewN8A30DfgN/A4ADgQOCA4MDhAOF -A4YDhwOIA4kDigOLA4wDjQOOA48DkAORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDogOj -A6QDpQOmA6cDqAOpA6oDqwOsA60DrgOvA7ADsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+A78DwAPB -A8IDwwPEA8UDxgPHA8gDyQEBA8oDywPMA80DzgPPA9AD0QPSA9MD1APVA9YD1wPYA9kD2gPbA9wD3QPe -A98D4APhA+ID4wPkA+UD5gPnA+gD6QPqA+sD7APtA+4D7wPwA/EA9wPyA/MD9AAEBE5VTEwESGJhcgJJ -SgJpagxrZ3JlZW5sYW5kaWMDRW5nA2VuZwVsb25ncwVPaG9ybgVvaG9ybgVVaG9ybgV1aG9ybgd1bmkw -MjM3BXNjaHdhB3VuaTAyRjMJZ3JhdmVjb21iCWFjdXRlY29tYgl0aWxkZWNvbWIEaG9vawd1bmkwMzBG -CGRvdGJlbG93BXRvbm9zDWRpZXJlc2lzdG9ub3MJYW5vdGVsZWlhBUdhbW1hBVRoZXRhBkxhbWJkYQJY -aQJQaQVTaWdtYQNQaGkDUHNpBWFscGhhBGJldGEFZ2FtbWEFZGVsdGEHZXBzaWxvbgR6ZXRhA2V0YQV0 -aGV0YQRpb3RhBmxhbWJkYQJ4aQNyaG8Gc2lnbWExBXNpZ21hA3RhdQd1cHNpbG9uA3BoaQNwc2kFb21l -Z2EHdW5pMDNEMQd1bmkwM0QyB3VuaTAzRDYHdW5pMDQwMgd1bmkwNDA0B3VuaTA0MDkHdW5pMDQwQQd1 -bmkwNDBCB3VuaTA0MEYHdW5pMDQxMQd1bmkwNDE0B3VuaTA0MTYHdW5pMDQxNwd1bmkwNDE4B3VuaTA0 -MUIHdW5pMDQyMwd1bmkwNDI2B3VuaTA0MjcHdW5pMDQyOAd1bmkwNDI5B3VuaTA0MkEHdW5pMDQyQgd1 -bmkwNDJDB3VuaTA0MkQHdW5pMDQyRQd1bmkwNDJGB3VuaTA0MzEHdW5pMDQzMgd1bmkwNDMzB3VuaTA0 -MzQHdW5pMDQzNgd1bmkwNDM3B3VuaTA0MzgHdW5pMDQzQQd1bmkwNDNCB3VuaTA0M0MHdW5pMDQzRAd1 -bmkwNDNGB3VuaTA0NDIHdW5pMDQ0NAd1bmkwNDQ2B3VuaTA0NDcHdW5pMDQ0OAd1bmkwNDQ5B3VuaTA0 -NEEHdW5pMDQ0Qgd1bmkwNDRDB3VuaTA0NEQHdW5pMDQ0RQd1bmkwNDRGB3VuaTA0NTIHdW5pMDQ1NAd1 -bmkwNDU5B3VuaTA0NUEHdW5pMDQ1Qgd1bmkwNDVGB3VuaTA0NjAHdW5pMDQ2MQd1bmkwNDYzB3VuaTA0 -NjQHdW5pMDQ2NQd1bmkwNDY2B3VuaTA0NjcHdW5pMDQ2OAd1bmkwNDY5B3VuaTA0NkEHdW5pMDQ2Qgd1 -bmkwNDZDB3VuaTA0NkQHdW5pMDQ2RQd1bmkwNDZGB3VuaTA0NzIHdW5pMDQ3Mwd1bmkwNDc0B3VuaTA0 -NzUHdW5pMDQ3OAd1bmkwNDc5B3VuaTA0N0EHdW5pMDQ3Qgd1bmkwNDdDB3VuaTA0N0QHdW5pMDQ3RQd1 -bmkwNDdGB3VuaTA0ODAHdW5pMDQ4MQd1bmkwNDgyB3VuaTA0ODMHdW5pMDQ4NAd1bmkwNDg1B3VuaTA0 -ODYHdW5pMDQ4OAd1bmkwNDg5B3VuaTA0OEUHdW5pMDQ4Rgd1bmkwNDkwB3VuaTA0OTEHdW5pMDQ5NAd1 -bmkwNDk1B3VuaTA0OUMHdW5pMDQ5RAd1bmkwNEEwB3VuaTA0QTEHdW5pMDRBNAd1bmkwNEE1B3VuaTA0 -QTYHdW5pMDRBNwd1bmkwNEE4B3VuaTA0QTkHdW5pMDRCNAd1bmkwNEI1B3VuaTA0QjgHdW5pMDRCOQd1 -bmkwNEJBB3VuaTA0QkMHdW5pMDRCRAd1bmkwNEMzB3VuaTA0QzQHdW5pMDRDNwd1bmkwNEM4B3VuaTA0 -RDgHdW5pMDRFMAd1bmkwNEUxB3VuaTA0RkEHdW5pMDRGQgd1bmkwNTAwB3VuaTA1MDIHdW5pMDUwMwd1 -bmkwNTA0B3VuaTA1MDUHdW5pMDUwNgd1bmkwNTA3B3VuaTA1MDgHdW5pMDUwOQd1bmkwNTBBB3VuaTA1 -MEIHdW5pMDUwQwd1bmkwNTBEB3VuaTA1MEUHdW5pMDUwRgd1bmkwNTEwB3VuaTIwMDAHdW5pMjAwMQd1 -bmkyMDAyB3VuaTIwMDMHdW5pMjAwNAd1bmkyMDA1B3VuaTIwMDYHdW5pMjAwNwd1bmkyMDA4B3VuaTIw -MDkHdW5pMjAwQQd1bmkyMDBCDXVuZGVyc2NvcmVkYmwNcXVvdGVyZXZlcnNlZAd1bmkyMDI1B3VuaTIw -NzQJbnN1cGVyaW9yBGxpcmEGcGVzZXRhBEV1cm8HdW5pMjEwNQd1bmkyMTEzB3VuaTIxMTYJZXN0aW1h -dGVkCW9uZWVpZ2h0aAx0aHJlZWVpZ2h0aHMLZml2ZWVpZ2h0aHMMc2V2ZW5laWdodGhzC2NvbW1hYWNj -ZW50B3VuaUZFRkYHdW5pRkZGQwd1bmlGRkZEB3R3by5zdXATY2lyY3VtZmxleHRpbGRlY29tYhJjaXJj -dW1mbGV4aG9va2NvbWITY2lyY3VtZmxleGdyYXZlY29tYhNjaXJjdW1mbGV4YWN1dGVjb21iDmJyZXZl -Z3JhdmVjb21iEWNvbW1hYWNjZW50cm90YXRlBkEuc21jcAZCLnNtY3AGQy5zbWNwBkQuc21jcAZFLnNt -Y3AGRi5zbWNwBkcuc21jcAZILnNtY3AGSS5zbWNwBkouc21jcAZLLnNtY3AGTC5zbWNwBk0uc21jcAZO -LnNtY3AGTy5zbWNwBlEuc21jcAZSLnNtY3AGUy5zbWNwBlQuc21jcAZVLnNtY3AGVi5zbWNwBlcuc21j -cAZYLnNtY3AGWS5zbWNwBlouc21jcA1icmV2ZWhvb2tjb21iB29uZS5zdXAOYnJldmVhY3V0ZWNvbWIJ -dGhyZWUuc3VwCGZvdXIuc3VwCGZpdmUuc3VwCXNldmVuLnN1cAllaWdodC5zdXAIY3Jvc3NiYXIJcmlu -Z2FjdXRlCWRhc2lhb3hpYQ5icmV2ZXRpbGRlY29tYgtjeXJpbGxpY3RpYwxjeXJpbGxpY2hvb2sGUC5z -bWNwBUsuYWx0D0dlcm1hbmRibHMuc21jcAd1bmkwMEFEBkRjcm9hdARoYmFyBFRiYXIEdGJhcgpBcmlu -Z2FjdXRlCmFyaW5nYWN1dGUHQW1hY3JvbgdhbWFjcm9uBkFicmV2ZQZhYnJldmUHQW9nb25lawdhb2dv -bmVrC0NjaXJjdW1mbGV4C2NjaXJjdW1mbGV4B3VuaTAxMEEHdW5pMDEwQgZEY2Fyb24GZGNhcm9uB0Vt -YWNyb24HZW1hY3JvbgZFYnJldmUGZWJyZXZlCkVkb3RhY2NlbnQKZWRvdGFjY2VudAdFb2dvbmVrB2Vv -Z29uZWsGRWNhcm9uBmVjYXJvbgtHY2lyY3VtZmxleAtnY2lyY3VtZmxleAd1bmkwMTIwB3VuaTAxMjEM -R2NvbW1hYWNjZW50DGdjb21tYWFjY2VudAtIY2lyY3VtZmxleAtoY2lyY3VtZmxleAZJdGlsZGUGaXRp -bGRlB0ltYWNyb24HaW1hY3JvbgZJYnJldmUGaWJyZXZlB0lvZ29uZWsHaW9nb25lawtKY2lyY3VtZmxl -eAtqY2lyY3VtZmxleAxLY29tbWFhY2NlbnQMa2NvbW1hYWNjZW50BkxhY3V0ZQZsYWN1dGUMTGNvbW1h -YWNjZW50DGxjb21tYWFjY2VudAZMY2Fyb24GbGNhcm9uBExkb3QEbGRvdAZOYWN1dGUGbmFjdXRlDE5j -b21tYWFjY2VudAxuY29tbWFhY2NlbnQGTmNhcm9uBm5jYXJvbgtuYXBvc3Ryb3BoZQdPbWFjcm9uB29t -YWNyb24GT2JyZXZlBm9icmV2ZQ1PaHVuZ2FydW1sYXV0DW9odW5nYXJ1bWxhdXQGUmFjdXRlBnJhY3V0 -ZQxSY29tbWFhY2NlbnQMcmNvbW1hYWNjZW50BlJjYXJvbgZyY2Fyb24GU2FjdXRlBnNhY3V0ZQtTY2ly -Y3VtZmxleAtzY2lyY3VtZmxleAd1bmkwMjE4B3VuaTAyMTkHdW5pMDIxQQd1bmkwMjFCB3VuaTAxNjIM -dW5pMDE2Mi5zbWNwB3VuaTAxNjMGVGNhcm9uBnRjYXJvbgZVdGlsZGUGdXRpbGRlB1VtYWNyb24HdW1h -Y3JvbgZVYnJldmUGdWJyZXZlBVVyaW5nBXVyaW5nDVVodW5nYXJ1bWxhdXQNdWh1bmdhcnVtbGF1dAdV -b2dvbmVrB3VvZ29uZWsLV2NpcmN1bWZsZXgLd2NpcmN1bWZsZXgLWWNpcmN1bWZsZXgLeWNpcmN1bWZs -ZXgGWmFjdXRlBnphY3V0ZQpaZG90YWNjZW50Cnpkb3RhY2NlbnQHQUVhY3V0ZQdhZWFjdXRlC09zbGFz -aGFjdXRlC29zbGFzaGFjdXRlC0Rjcm9hdC5zbWNwCEV0aC5zbWNwCVRiYXIuc21jcAtBZ3JhdmUuc21j -cAtBYWN1dGUuc21jcBBBY2lyY3VtZmxleC5zbWNwC0F0aWxkZS5zbWNwDkFkaWVyZXNpcy5zbWNwCkFy -aW5nLnNtY3APQXJpbmdhY3V0ZS5zbWNwDUNjZWRpbGxhLnNtY3ALRWdyYXZlLnNtY3ALRWFjdXRlLnNt -Y3AQRWNpcmN1bWZsZXguc21jcA5FZGllcmVzaXMuc21jcAtJZ3JhdmUuc21jcAtJYWN1dGUuc21jcBBJ -Y2lyY3VtZmxleC5zbWNwDklkaWVyZXNpcy5zbWNwC050aWxkZS5zbWNwC09ncmF2ZS5zbWNwC09hY3V0 -ZS5zbWNwEE9jaXJjdW1mbGV4LnNtY3ALT3RpbGRlLnNtY3AOT2RpZXJlc2lzLnNtY3ALVWdyYXZlLnNt -Y3ALVWFjdXRlLnNtY3AQVWNpcmN1bWZsZXguc21jcA5VZGllcmVzaXMuc21jcAtZYWN1dGUuc21jcAxB -bWFjcm9uLnNtY3ALQWJyZXZlLnNtY3AMQW9nb25lay5zbWNwC0NhY3V0ZS5zbWNwEENjaXJjdW1mbGV4 -LnNtY3ALQ2Nhcm9uLnNtY3ALRGNhcm9uLnNtY3AMRW1hY3Jvbi5zbWNwC0VicmV2ZS5zbWNwD0Vkb3Rh -Y2NlbnQuc21jcAxFb2dvbmVrLnNtY3ALRWNhcm9uLnNtY3AQR2NpcmN1bWZsZXguc21jcAtHYnJldmUu -c21jcBFHY29tbWFhY2NlbnQuc21jcBBIY2lyY3VtZmxleC5zbWNwC0l0aWxkZS5zbWNwDEltYWNyb24u -c21jcAtJYnJldmUuc21jcAxJb2dvbmVrLnNtY3APSWRvdGFjY2VudC5zbWNwEEpjaXJjdW1mbGV4LnNt -Y3ARS2NvbW1hYWNjZW50LnNtY3ALTGFjdXRlLnNtY3ARTGNvbW1hYWNjZW50LnNtY3ALTGNhcm9uLnNt -Y3AJTGRvdC5zbWNwC05hY3V0ZS5zbWNwEU5jb21tYWFjY2VudC5zbWNwC05jYXJvbi5zbWNwDE9tYWNy -b24uc21jcAtPYnJldmUuc21jcBJPaHVuZ2FydW1sYXV0LnNtY3ALUmFjdXRlLnNtY3ARUmNvbW1hYWNj -ZW50LnNtY3ALUmNhcm9uLnNtY3ALU2FjdXRlLnNtY3AQU2NpcmN1bWZsZXguc21jcA1TY2VkaWxsYS5z -bWNwC1NjYXJvbi5zbWNwC1RjYXJvbi5zbWNwC1V0aWxkZS5zbWNwDFVtYWNyb24uc21jcAtVYnJldmUu -c21jcApVcmluZy5zbWNwElVodW5nYXJ1bWxhdXQuc21jcAxVb2dvbmVrLnNtY3AQV2NpcmN1bWZsZXgu -c21jcBBZY2lyY3VtZmxleC5zbWNwDllkaWVyZXNpcy5zbWNwC1phY3V0ZS5zbWNwD1pkb3RhY2NlbnQu -c21jcAtaY2Fyb24uc21jcApBbHBoYXRvbm9zDEVwc2lsb250b25vcwhFdGF0b25vcwlJb3RhdG9ub3MM -T21pY3JvbnRvbm9zDFVwc2lsb250b25vcwpPbWVnYXRvbm9zEWlvdGFkaWVyZXNpc3Rvbm9zBUFscGhh -BEJldGEHRXBzaWxvbgRaZXRhA0V0YQRJb3RhBUthcHBhAk11Ak51B09taWNyb24DUmhvA1RhdQdVcHNp -bG9uA0NoaQxJb3RhZGllcmVzaXMPVXBzaWxvbmRpZXJlc2lzCmFscGhhdG9ub3MMZXBzaWxvbnRvbm9z -CGV0YXRvbm9zCWlvdGF0b25vcxR1cHNpbG9uZGllcmVzaXN0b25vcwVrYXBwYQdvbWljcm9uB3VuaTAz -QkMCbnUDY2hpDGlvdGFkaWVyZXNpcw91cHNpbG9uZGllcmVzaXMMb21pY3JvbnRvbm9zDHVwc2lsb250 -b25vcwpvbWVnYXRvbm9zB3VuaTA0MDEHdW5pMDQwMwd1bmkwNDA1B3VuaTA0MDYHdW5pMDQwNwd1bmkw -NDA4B3VuaTA0MUEHdW5pMDQwQwd1bmkwNDBFB3VuaTA0MTAHdW5pMDQxMgd1bmkwNDEzB3VuaTA0MTUH -dW5pMDQxOQd1bmkwNDFDB3VuaTA0MUQHdW5pMDQxRQd1bmkwNDFGB3VuaTA0MjAHdW5pMDQyMQd1bmkw -NDIyB3VuaTA0MjQHdW5pMDQyNQd1bmkwNDMwB3VuaTA0MzUHdW5pMDQzOQd1bmkwNDNFB3VuaTA0NDAH -dW5pMDQ0MQd1bmkwNDQzB3VuaTA0NDUHdW5pMDQ1MQd1bmkwNDUzB3VuaTA0NTUHdW5pMDQ1Ngd1bmkw -NDU3B3VuaTA0NTgHdW5pMDQ1Qwd1bmkwNDVFBldncmF2ZQZ3Z3JhdmUGV2FjdXRlBndhY3V0ZQlXZGll -cmVzaXMJd2RpZXJlc2lzBllncmF2ZQZ5Z3JhdmUGbWludXRlBnNlY29uZAlleGNsYW1kYmwHdW5pMDFG -MAd1bmkwMkJDB3VuaTFFM0UHdW5pMUUzRgd1bmkxRTAwB3VuaTFFMDEHdW5pMUY0RAd1bmkwNDAwB3Vu -aTA0MEQHdW5pMDQ1MAd1bmkwNDVEB3VuaTA0NzAHdW5pMDQ3MQd1bmkwNDc2B3VuaTA0NzcHdW5pMDQ5 -OAd1bmkwNDk5B3VuaTA0QUEHdW5pMDRBQgd1bmkwNEFFB3VuaTA0QUYHdW5pMDRDMAd1bmkwNEMxB3Vu -aTA0QzIHdW5pMDRDRgd1bmkwNEQwB3VuaTA0RDEHdW5pMDREMgd1bmkwNEQzB3VuaTA0RDQHdW5pMDRE -NQd1bmkwNEQ2B3VuaTA0RDcHdW5pMDREQQd1bmkwNEQ5B3VuaTA0REIHdW5pMDREQwd1bmkwNEREB3Vu -aTA0REUHdW5pMDRERgd1bmkwNEUyB3VuaTA0RTMHdW5pMDRFNAd1bmkwNEU1B3VuaTA0RTYHdW5pMDRF -Nwd1bmkwNEU4B3VuaTA0RTkHdW5pMDRFQQd1bmkwNEVCB3VuaTA0RUMHdW5pMDRFRAd1bmkwNEVFB3Vu -aTA0RUYHdW5pMDRGMAd1bmkwNEYxB3VuaTA0RjIHdW5pMDRGMwd1bmkwNEY0B3VuaTA0RjUHdW5pMDRG -OAd1bmkwNEY5B3VuaTA0RkMHdW5pMDRGRAd1bmkwNTAxB3VuaTA1MTIHdW5pMDUxMwd1bmkxRUEwB3Vu -aTFFQTEHdW5pMUVBMgd1bmkxRUEzB3VuaTFFQTQHdW5pMUVBNQd1bmkxRUE2B3VuaTFFQTcHdW5pMUVB -OAd1bmkxRUE5B3VuaTFFQUEHdW5pMUVBQgd1bmkxRUFDB3VuaTFFQUQHdW5pMUVBRQd1bmkxRUFGB3Vu -aTFFQjAHdW5pMUVCMQd1bmkxRUIyB3VuaTFFQjMHdW5pMUVCNAd1bmkxRUI1B3VuaTFFQjYHdW5pMUVC -Nwd1bmkxRUI4B3VuaTFFQjkHdW5pMUVCQQd1bmkxRUJCB3VuaTFFQkMHdW5pMUVCRAd1bmkxRUJFB3Vu -aTFFQkYHdW5pMUVDMAd1bmkxRUMxB3VuaTFFQzIHdW5pMUVDMwd1bmkxRUM0B3VuaTFFQzUHdW5pMUVD -Ngd1bmkxRUM3B3VuaTFFQzgHdW5pMUVDOQd1bmkxRUNBB3VuaTFFQ0IHdW5pMUVDQwd1bmkxRUNEB3Vu -aTFFQ0UHdW5pMUVDRgd1bmkxRUQwB3VuaTFFRDEHdW5pMUVEMgd1bmkxRUQzB3VuaTFFRDQHdW5pMUVE -NQd1bmkxRUQ2B3VuaTFFRDcHdW5pMUVEOAd1bmkxRUQ5B3VuaTFFREEHdW5pMUVEQgd1bmkxRURDB3Vu -aTFFREQHdW5pMUVERQd1bmkxRURGB3VuaTFFRTAHdW5pMUVFMQd1bmkxRUUyB3VuaTFFRTMHdW5pMUVF -NAd1bmkxRUU1B3VuaTFFRTYHdW5pMUVFNwd1bmkxRUU4B3VuaTFFRTkHdW5pMUVFQQd1bmkxRUVCB3Vu -aTFFRUMHdW5pMUVFRAd1bmkxRUVFB3VuaTFFRUYHdW5pMUVGMAd1bmkxRUYxB3VuaTFFRjQHdW5pMUVG -NQd1bmkxRUY2B3VuaTFFRjcHdW5pMUVGOAd1bmkxRUY5B3VuaTIwQUIHdW5pMDQ5QQd1bmkwNDlCB3Vu -aTA0QTIHdW5pMDRBMwd1bmkwNEFDB3VuaTA0QUQHdW5pMDRCMgd1bmkwNEIzB3VuaTA0QjYHdW5pMDRC -Nwd1bmkwNENCB3VuaTA0Q0MHdW5pMDRGNgd1bmkwNEY3B3VuaTA0OTYHdW5pMDQ5Nwd1bmkwNEJFB3Vu -aTA0QkYHdW5pMDRCQgd1bmkwNDhEB3VuaTA0OEMHdW5pMDQ2Mgd1bmkwNDkyB3VuaTA0OTMHdW5pMDQ5 -RQd1bmkwNDlGB3VuaTA0OEEHdW5pMDQ4Qgd1bmkwNEM5B3VuaTA0Q0EHdW5pMDRDRAd1bmkwNENFB3Vu -aTA0QzUHdW5pMDRDNgd1bmkwNEIwB3VuaTA0QjEHdW5pMDRGRQd1bmkwNEZGB3VuaTA1MTEHdW5pMjAx -NQd1bmkwMDAyB3VuaTAwMDkAAAAAAQAB//8ADwABAAAADAAAAAAAAAACAAcAzADMAAEBIQEnAAEBWAFj -AAEBeAF4AAEBfQF+AAEBgAGAAAEBkgGUAAEAAAABAAAACgAeACwAAURGTFQACAAEAAAAAP//AAEAAAAB -c21jcAAIAAAAAQAAAAEABAABAAAAAQAIAAIBvgDcAZwBnQGeAZ8BoAGhAaIBowGkAaUBpgGnAagBqQGq -AcMBqwGsAa0BrgGvAbABsQGyAbMBtAGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgHDAasBrAGt -Aa4BrwGwAbEBsgGzAbQBxQJ9AnwCfQJ+An4CfwKAAoECggKDAoQChQKGAocCiAKJAooCiwKMAo0CjgKP -ApACkQKSApMClAKVApYClwKYApkCfwKAAoECggKDAoQChQKGAocCiAKJAooCiwKMAo0CjgKPApACkQKS -ApMClAKVApYClwKYApkCywKaApoCmwKbApwCnAKdAp0CngKeAp8CnwKgAqACoQKhAqICogKjAqMCpAKk -AqUCpQKmAqYCpwKnAqgCqAKpAqkCqgKqAqsCqwKsAqwCrQKtAq4CrwKvArACsAKxArECsgKyArMCswK0 -ArQCtQK1ArYCtgK3ArcCuAK4ArkCuQK6AroCuwK7ArwCvAK9Ar0CvgK+Ar8CvwLAAsACwQLBAl0CXQLC -AsICwwLDAsQCxALFAsUCxgLGAscCxwLIAsgCyQLJAsoCygLLAswCzALNAs0CzgLOAnwAAgANACMAPAAA -AEMAXAAaAIMAgwA0AIUAhQA1AcgByQA2AcsCDQA4AhACIQB7AiQCQgCNAkQCVQCsAlgCWQC+AlwCXADA -Al4CdwDBA8MDwwDb -'''; - -String? _RobotoRegular_ttf; -// RobotoRegular_ttf md5 is '11eabca2251325cfc5589c9c6fb57b46' -String _RobotoRegular_ttf_base64 = ''' -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 -MhYVIzQmIyIGFf0Ic750cDMwLjMB3nRdX3VxNS4sM0h1XV90cDVcM/7LdF1fdHA1Li0z/U9zvnRwMzAu -M/1NdL50cDMwLjP+3nVdX3RwNVwzNXVdX3VxNS4tMwTzVGhoVC43NTD+61RoZ1UxNDUw/glVZ2hUMTQ3 -Lv35VGhoVDE0Ny7+5FRoaFQuNzcuBRpUaGhULjc1MP4JVWdoVDE0Ny79+VVnZ1UxNDUwAAAI+iz+YwFr -BcYABAAJAA4AEwAYAB0AIgAnADkAsCEvsBIvsAsvsBsvsCYvsABFWLAHLxuxBx4+WbAARViwFi8bsRYc -PlmwAEVYsAIvG7ECFD5ZMDEFFwMjEwMnEzMDATcFFSUFByU1BQE3JRcFAQcFJyUDJwM3EwEXEwcD/i8L -emBGOgx6YEYCHQ0BTf6m+3UN/rMBWgOcAgFARP7b/PMC/sBFASYrEZRBxgNgEZRCxDwO/q0BYQSiDgFS -/qD+EQx8Ykc7DHxiRwGuEJlEyPyOEZlFyALkAgFGRf7V/OMC/rtHASsA//8Asf6bBbMHGQAmANwAAAAn -AKEBMQFCAQcAEAR//70AEwCwAEVYsAgvG7EIHj5ZsA3cMDEA//8AnP6bBLUFwwAmAPAAAAAnAKEAof/s -AQcAEAOB/70AEwCwAEVYsAgvG7EIGj5ZsA3cMDEAAAL/3AAAA/wGcQARABoAd7IUGxwREjmwFBCwA9AA -sABFWLAMLxuxDB4+WbAARViwEC8bsRAePlmwAEVYsAgvG7EIEj5ZsBAQsQABsAorWCHYG/RZsgIMCBES -ObACL7AAELAK0LAL0LACELESAbAKK1gh2Bv0WbAIELETAbAKK1gh2Bv0WTAxASERIRYWEAYHIREjNTM1 -MxUhAREhMjY1NCYnApb+vwEYu9TUt/4qv7+6AUH+vwESaXFvZAUY/dICyv620QMFGJjBwfyi/kV3ZGF9 -AgAAAgCoAAAE1wWwAA4AGwBWsgQcHRESObAEELAX0ACwAEVYsAMvG7EDHj5ZsABFWLABLxuxARI+WbIW -AwEREjmwFi+xAAGwCitYIdgb9FmyCQADERI5sAMQsRQBsAorWCHYG/RZMDEBESMRITIEFRQHFwcnBiMB -NjU0JichESEyNyc3AWnBAhnsARNnfm2LdqgBGSWlkf6gAVhiRW5uAjr9xgWw8su6cIpnmTcBG0Fbgp0C -/cUdeWYAAAIAjP5gBCMETgATACIAd7IcIyQREjmwHBCwENAAsABFWLAQLxuxEBo+WbAARViwDS8bsQ0a -PlmwAEVYsAovG7EKFD5ZsABFWLAHLxuxBxI+WbICBxAREjmyCRAHERI5sg4QBxESObAQELEXAbAKK1gh -2Bv0WbAHELEcAbAKK1gh2Bv0WTAxARQHFwcnBiMiJxEjETMXNjMyEhEnNCYjIgcRFjMyNyc3FzYEHmpv -bm5Zc8VxuakJccnD47mciKhUU6tSPGZuWjICEe6XfWZ7OH399wXaeIz+2v76BLfUlf37lCdzZ2diAAAB -AKIAAAQjBwAACQA2sgMKCxESOQCwCC+wAEVYsAYvG7EGHj5ZsABFWLAELxuxBBI+WbAGELECAbAKK1gh -2Bv0WTAxASMVIREjESERMwQjA/1CwALIuQUYBvruBbABUAABAJEAAANCBXYABwAvALAGL7AARViwBC8b -sQQaPlmwAEVYsAIvG7ECEj5ZsAQQsQABsAorWCHYG/RZMDEBIREjESERMwNC/gm6Afi5A6H8XwQ6ATwA -AAEAsf7fBHwFsAAVAF6yChYXERI5ALAJL7AARViwFC8bsRQePlmwAEVYsBIvG7ESEj5ZsBQQsQABsAor -WCHYG/RZsgMUCRESObADL7AJELEKAbAKK1gh2Bv0WbADELEQAbAKK1gh2Bv0WTAxASERMyAAERACIycy -NjUmJiMjESMRIQQw/UKyARwBPPXkApGQAczOtcEDfwUS/i/+z/7w/vj+55PDy8vU/WEFsAABAJH+5QO+ -BDoAFgBesgsXGBESOQCwCi+wAEVYsBUvG7EVGj5ZsABFWLATLxuxExI+WbAVELEAAbAKK1gh2Bv0WbID -FQoREjmwAy+wChCxCwGwCitYIdgb9FmwAxCxEQGwCitYIdgb9FkwMQEhETMyABUUBgYHJzY2NTQmIyMR -IxEhAz7+DWzvARhiqnUwgHiymHC6Aq0Dof7k/vzXYsiGFZIhmXmRqP4dBDr//wAb/pkHggWwACYA2gAA -AAcCUQZhAAD//wAV/pkGPQQ6ACYA7gAAAAcCUQUcAAD//wCy/pcFRAWwACYCLAAAAAcCUQQj//7//wCc -/pkEgQQ6ACYA8QAAAAcCUQNgAAAAAQCjAAAE/wWwABQAYwCwAEVYsAAvG7EAHj5ZsABFWLAMLxuxDB4+ -WbAARViwAi8bsQISPlmwAEVYsAovG7EKEj5ZsA/QsA8vsi8PAV2yzw8BXbEIAbAKK1gh2Bv0WbIBCA8R -EjmwBdCwDxCwEtAwMQkCIwEjFSM1IxEjETMRMxEzETMBBNL+cAG98f6iUJRowcFolE0BQwWw/U79AgKO -9PT9cgWw/X8BAP8AAoEAAQCaAAAEfwQ6ABQAfACwAEVYsA0vG7ENGj5ZsABFWLAULxuxFBo+WbAARViw -Ci8bsQoSPlmwAEVYsAMvG7EDEj5ZsAoQsA7QsA4vsp8OAV2y/w4BXbKfDgFxtL8Ozw4CXbIvDgFdsm8O -AXKxCQGwCitYIdgb9FmyAQkOERI5sAXQsA4QsBLQMDEJAiMBIxUjNSMRIxEzETM1MxUzAQRa/q4Bd+v+ -6zKUZbq6ZZQqAQMEOv3+/cgBzcLC/jMEOv421dUBygAAAQBEAAAGiwWwAA4AbQCwAEVYsAYvG7EGHj5Z -sABFWLAKLxuxCh4+WbAARViwAi8bsQISPlmwAEVYsA0vG7ENEj5ZsggGAhESObAIL7IvCAFdss8IAV2x -AQGwCitYIdgb9FmwBhCxBAGwCitYIdgb9FmyDAEIERI5MDEBIxEjESE1IREzATMBASMDkLDB/iUCnJYB -/O/91AJW7AKO/XIFGJj9fgKC/T/9EQABAD4AAAV9BDoADgCCALAARViwBi8bsQYaPlmwAEVYsAovG7EK -Gj5ZsABFWLACLxuxAhI+WbAARViwDS8bsQ0SPlmwAhCwCdCwCS+ynwkBXbL/CQFdsp8JAXG0vwnPCQJd -si8JAV2ybwkBcrEAAbAKK1gh2Bv0WbAGELEEAbAKK1gh2Bv0WbIMAAkREjkwMQEjESMRITUhETMBMwEB -IwMbiLr+ZQJVegFr4f5TAdHrAc3+MwOhmf42Acr9+P3OAP//AKn+mQWpBbAAJgAsAAAABwJRBIgAAP// -AJz+mQSiBDoAJgD0AAAABwJRA4EAAAABAKgAAAeEBbAADQBgALAARViwAi8bsQIePlmwAEVYsAwvG7EM -Hj5ZsABFWLAGLxuxBhI+WbAARViwCi8bsQoSPlmwAdCwAS+yLwEBXbACELEEAbAKK1gh2Bv0WbABELEI -AbAKK1gh2Bv0WTAxASERIRUhESMRIREjETMBaQLeAz39g8D9IsHBAz4Ccpj66AKh/V8FsAABAJEAAAVp -BDoADQCdALAARViwAi8bsQIaPlmwAEVYsAwvG7EMGj5ZsABFWLAGLxuxBhI+WbAARViwCi8bsQoSPlmw -BhCwAdCwAS+ybwEBXbS/Ac8BAl2yPwEBcbTPAd8BAnGyDwEBcrSfAa8BAnGy/wEBXbIPAQFxsp8BAV2y -LwEBXbRvAX8BAnKwAhCxBAGwCitYIdgb9FmwARCxCAGwCitYIdgb9FkwMQEhESEVIREjESERIxEzAUsB -8QIt/oy5/g+6ugJlAdWZ/F8Bzv4yBDoAAAEAsP7fB80FsAAXAGuyERgZERI5ALAHL7AARViwFi8bsRYe -PlmwAEVYsBQvG7EUEj5ZsABFWLARLxuxERI+WbIBFgcREjmwAS+wBxCxCAGwCitYIdgb9FmwARCxDgGw -CitYIdgb9FmwFhCxEgGwCitYIdgb9FkwMQEzIAAREAIjJzI2NSYmIyMRIxEhESMRIQT/dgEcATz15AKR -kAHMznnB/TLABE8DQf7P/vD++P7nk8PLy9T9YQUS+u4FsAABAJH+5QawBDoAGABrshIZGhESOQCwCC+w -AEVYsBcvG7EXGj5ZsABFWLAVLxuxFRI+WbAARViwEi8bsRISPlmyARcIERI5sAEvsAgQsQkBsAorWCHY -G/RZsAEQsQ8BsAorWCHYG/RZsBcQsRMBsAorWCHYG/RZMDEBMzIAFQcGBgcnNjY1NCYjIxEjESERIxEh -A/ag+AEiAxTRmTB8e7ygpLn+DroDZQKF/vzXJqPhG5Igln2Sp/4dA6H8XwQ6AAACAHH/5AWiBcUAKAA2 -AKCyGDc4ERI5sBgQsCnQALAARViwDS8bsQ0ePlmwAEVYsB8vG7EfHj5ZsABFWLAELxuxBBI+WbAA0LAA -L7ICBB8REjmwAi+wDRCxDgGwCitYIdgb9FmwBBCxFQGwCitYIdgb9FmwAhCxLAGwCitYIdgb9FmyFwIs -ERI5siYsAhESObAAELEoAbAKK1gh2Bv0WbAfELEzAbAKK1gh2Bv0WTAxBSInBiMiJAI1NTQSNjMXIgYV -FRQSMzI3JgI1NTQ2NjMyEhUVFAIHFjMBFBYXNjY1NTQmIyIGFQWi17OOrLL+5J910oQBdpTsv0Y4eYRo -vXa25m9maHn9fXh1Ymh5Y2F6HElCsgFCxKyxASKjpf7Zpuz+1w1hARWq45r9jf7M/eue/vZfGgI0mO1K -SOeN+bHO0rIAAgBt/+sEnARPACQALwCnsgQwMRESObAEELAl0ACwAEVYsAwvG7EMGj5ZsABFWLAcLxux -HBo+WbAARViwBC8bsQQSPlmwAEVYsAAvG7EAEj5ZsgIEHBESObACL7AMELENAbAKK1gh2Bv0WbAEELEU -AbAKK1gh2Bv0WbACELEnAbAKK1gh2Bv0WbIWFCcREjmwABCxJAGwCitYIdgb9FmyIickERI5sBwQsSwB -sAorWCHYG/RZMDEFIicGIyImAjU1NBIzFSIGFRUUFjMyNyYRNTQ2MzIWFRUUBxYzARQXNjc1NCYiBgcE -nLKMdo+M4X/Fm0ldqYkuLMGtj4yygE9h/g+fZgNJeEYBDDlClQESpzrNAQ6erZI4wfALogERXsDr+c5i -450VAanWdHO6dYKejXr//wA5/pkE+AWwACYAPAAAAAcCUQPXAAD//wAp/pkEBgQ6ACYAXAAAAAcCUQLl -AAAAAQA0/qEGkwWwABMAXQCwES+wAEVYsAcvG7EHHj5ZsABFWLAMLxuxDB4+WbAARViwEy8bsRMSPlmw -BxCxCAGwCitYIdgb9FmwANCwBxCwBdCwA9CwAtCwExCxCgGwCitYIdgb9FmwDtAwMQEhNSE1MxUhFSER -IREzETMDIxEhAav+iQF3wQGB/n8CzsGYEqz71gUYlwEBl/uFBRP68f4AAV8AAQAf/r8FFgQ6AA8ATQCw -DS+wAEVYsAMvG7EDGj5ZsABFWLAPLxuxDxI+WbADELEEAbAKK1gh2Bv0WbAA0LAPELEGAbAKK1gh2Bv0 -WbADELAI0LAGELAK0DAxASE1IRUjESERMxEzAyMRIQEx/u4CxPkB8rqAEqX80gOjl5f89AOj/F3+KAFB -//8Alv6ZBWcFsAAmAOEAAAAHAlEERgAA//8AZ/6ZBF8EOwAmAPkAAAAHAlEDPgAAAAEAlgAABMgFsAAX -AFCyBBgZERI5ALAARViwAC8bsQAePlmwAEVYsAovG7EKHj5ZsABFWLAMLxuxDBI+WbIHAAwREjmwBy+w -BNCwBxCxEAGwCitYIdgb9FmwE9AwMQERFhYzETMRNjcRMxEjEQYHFSM1IiYnEQFXAYmglXl4wcFyf5X4 -7wQFsP4ymoQBNv7SDSECtvpQAlsiDe7o2doB1wABAIMAAAPZBDsAFgBQsgYXGBESOQCwAEVYsAsvG7EL -Gj5ZsABFWLAVLxuxFRo+WbAARViwAC8bsQASPlmyDxUAERI5sA8vsQcBsAorWCHYG/RZsATQsA8QsBLQ -MDEhIxEGBxUjNSYmJxEzERYXETMRNjcRMwPZukZTlrC7ArkFr5ZURboBiBMJh4UNzLUBQ/610xoBGP7q -ChECGgABAIkAAAS6BbAAEQBHsgUSExESOQCwAEVYsAEvG7EBHj5ZsABFWLAALxuxABI+WbAARViwCS8b -sQkSPlmyBQEAERI5sAUvsQ4BsAorWCHYG/RZMDEzETMRNjMyFhcRIxEmJiMiBxGJwLnL+PIDwAGJo7zI -BbD9pDXY3/4uAc2Yhjf9TAACAD//6gW9BcMAHQAlAGeyFyYnERI5sBcQsCTQALAARViwDy8bsQ8ePlmw -AEVYsAAvG7EAEj5Zsh8PABESObAfL7ETAbAKK1gh2Bv0WbAE0LAfELAL0LAAELEYAbAKK1gh2Bv0WbAP -ELEjAbAKK1gh2Bv0WTAxBSAAETUmJjUzFBYXNBI2MyAAERUhFRQWMzI3FwYGASE1NCYjIgID6f7i/rOZ -pphQV479lgECARz8gt7Ms6YvQNL94AK+s6uewhYBUQEpWxPFolp9FLQBH6L+o/6+bF3c91OPLTUDWiHZ -5f79AAAC/97/7ARjBE4AGQAhAHWyFCIjERI5sBQQsBvQALAARViwDS8bsQ0aPlmwAEVYsAAvG7EAEj5Z -sh4NABESObAeL7S/Hs8eAl2xEQGwCitYIdgb9FmwA9CwHhCwCdCwABCxFQGwCitYIdgb9FmyFw0AERI5 -sA0QsRoBsAorWCHYG/RZMDEFIgA1JiY1MxQXPgIzMhIRFSEWFjMyNxcGASIGByE1JiYCvdz+7Hh3k2UU -hMhw0+r9IwSziq5vcYj+2XCYEgIeCIgUASH6Ha6GkzCCyW7+6v79TaDFkljRA8qjkw6NmwABAKP+1gTM -BbAAFgBfshUXGBESOQCwDi+wAEVYsAIvG7ECHj5ZsABFWLAGLxuxBh4+WbAARViwAC8bsQASPlmyBAAC -ERI5sAQvsAjQsA4QsQ8BsAorWCHYG/RZsAQQsRYBsAorWCHYG/RZMDEhIxEzETMBMwEWABUQAiMnMjY1 -JiYnIQFkwcGFAgHi/fj4AQ355gKQkALHx/7sBbD9jwJx/YgW/tL6/vj+5JjBycrSAQAAAQCa/v4EGQQ6 -ABYAe7INFxgREjkAsAcvsABFWLARLxuxERo+WbAARViwFS8bsRUaPlmwAEVYsA8vG7EPEj5ZsBPQsBMv -sp8TAV2y/xMBXbKfEwFxtL8TzxMCXbIvEwFdss8TAXGwANCwBxCxCAGwCitYIdgb9FmwExCxDgGwCitY -Idgb9FkwMQEWFhUUBgYHJzY1NCYnIxEjETMRMwEzAn/DzmSscDD4raWyurpbAYrgAmQf4rRdxXwTkjnm -ipIC/jMEOv42AcoA//8AL/6bBagFsAAmAN0AAAAHABAEdP+9//8ALP6bBLcEOgAmAPIAAAAHABADg/+9 -AAEAsf5LBP4FsAAVAKmyChYXERI5ALAARViwAC8bsQAePlmwAEVYsAMvG7EDHj5ZsABFWLAILxuxCBQ+ -WbAARViwEy8bsRMSPlmwAtCwAi+yXwIBXbLPAgFdsh8CAXG0bwJ/AgJxtL8CzwICcbQPAh8CAnKy7wIB -cbKfAgFxsk8CAXGy/wIBXbKvAgFdsi8CAV2yPwIBcrAIELENAbAKK1gh2Bv0WbACELERAbAKK1gh2Bv0 -WTAxAREhETMRFAYjIic3FjMyNjURIREjEQFyAszAq5w8Ng4lPUFI/TTBBbD9bgKS+f2ouhKaDmdcAtX9 -fwWwAAABAJH+SwP1BDoAFgChsgoXGBESOQCwAEVYsAAvG7EAGj5ZsABFWLADLxuxAxo+WbAARViwCC8b -sQgUPlmwAEVYsBQvG7EUEj5ZsALQsAIvsm8CAV20vwLPAgJdsj8CAXG0zwLfAgJxsg8CAXK0nwKvAgJx -sv8CAV2yDwIBcbKfAgFdsi8CAV20bwJ/AgJysAgQsQ4BsAorWCHYG/RZsAIQsRIBsAorWCHYG/RZMDEB -ESERMxEUBiMiJzcWFxcyNjURIREjEQFLAfG5q5g8NA8RPBRCSP4PugQ6/isB1fttqrISkwcFAWhcAif+ -MgQ6AP//AKn+mwW7BbAAJgAsAAAABwAQBIf/vf//AJz+mwS0BDoAJgD0AAAABwAQA4D/vf//AKn+mwb5 -BbAAJgAxAAAABwAQBcX/vf//AJ3+mwYHBDoAJgDzAAAABwAQBNP/vQACAF3/7AUSBcQAFwAfAGGyCCAh -ERI5sAgQsBjQALAARViwAC8bsQAePlmwAEVYsAgvG7EIEj5Zsg0ACBESObANL7AAELERAbAKK1gh2Bv0 -WbAIELEYAbAKK1gh2Bv0WbANELEbAbAKK1gh2Bv0WTAxASAAERUUAgQjIAARNSE1EAIjIgcHJzc2ATIS -NyEVFBYCgAEuAWSc/uqn/uP+wQP09N2liz0vFp4BIaneD/zP0wXE/of+sVTF/r+2AVkBRXUHAQIBHDoa -jw1Y+sYBBdsi2uQAAAEAaP/rBCwFsAAbAGqyCxwdERI5ALAARViwAi8bsQIePlmwAEVYsAsvG7ELEj5Z -sAIQsQABsAorWCHYG/RZsATQsgUCCxESObAFL7ALELAQ0LALELETAbAKK1gh2Bv0WbAFELEZAbAKK1gh -2Bv0WbAFELAb0DAxASE1IRcBFhYVFAQjIiYmNTMUFjMyNjU0JiMjNQMd/XYDawH+a9np/vPghtt2wJx7 -iaOmno0FEp59/h4O58bD6Gm+gnKaknidjpcAAQBp/nUEKAQ6ABoAXbILGxwREjkAsAsvsABFWLACLxux -Aho+WbEAAbAKK1gh2Bv0WbAE0LIFAgsREjmwBS+wCxCwENCwCxCxEwGwCitYIdgb9FmwBRCxGAOwCitY -Idgb9FmwBRCwGtAwMQEhNSEXARYWFRQEIyImJjUzFBYzMjY1ECUjNQMM/YgDZQH+ctTo/vTehNd6up59 -jaT+yaADoZl2/hEQ4cXD52a/g3GflXkBIgiXAP//ADr+SwR0BbAAJgCxRAAAJgImq0AABwJUAPAAAP// -ADv+SwOWBDoAJgDsTwAAJgImrI4BBwJUAOEAAAAIALIABgFdMDH//wA5/ksFDgWwACYAPAAAAAcCVAOn -AAD//wAp/ksEHAQ6ACYAXAAAAAcCVAK1AAAAAgBXAAAEZQWwAAoAEwBSsgQUFRESObAEELAN0ACwAEVY -sAEvG7EBHj5ZsABFWLADLxuxAxI+WbIAAQMREjmwAC+wAxCxCwGwCitYIdgb9FmwABCxDAGwCitYIdgb -9FkwMQERMxEhIiQ1NDY3AREhIgYVFBYXA6PC/d/k/vf/4AFt/qGMoZ+KA3MCPfpQ8svH6wT9KgI4loCC -nwEAAgBZAAAGZwWwABcAHwBcsgcgIRESObAHELAY0ACwAEVYsAgvG7EIHj5ZsABFWLAALxuxABI+WbIH -CAAREjmwBy+wABCxGAGwCitYIdgb9FmwCtCyEAAIERI5sAcQsRkBsAorWCHYG/RZMDEhIiQ1NCQ3IREz -ETc2Njc2JzMXFgcGBiMlESEiBhQWFwJH5f73AQHjAWrBWG9yAwRAuhYvAwTlw/7v/qCOnpiF9MnG7QMC -PfrrAQKSe6KnRJduw+idAjiX/p8EAAACAGT/5wZuBhgAHwArAIayGiwtERI5sBoQsCrQALAARViwBi8b -sQYgPlmwAEVYsAMvG7EDGj5ZsABFWLAYLxuxGBI+WbAARViwHC8bsRwSPlmyBQMYERI5sBgQsQsBsAor -WCHYG/RZshEDGBESObIaAxgREjmwAxCxIgGwCitYIdgb9FmwHBCxKAGwCitYIdgb9FkwMRMQEjMyFxEz -EQYWMzY2NzYnNxYWBw4CIwYnBiMiAjUBJiMiBhUUFjMyNydk4sS3arkCX06JlwQEQbMcKQICedmJ8k5s -28DkAsdSoYeUkYinUwUCCQEIAT2DAk37QV94AtC9utgBZsdmqfmEBLq2ARv0ATGG396tv5M+AAEANv/j -BdUFsAAnAGayECgpERI5ALAARViwCS8bsQkePlmwAEVYsCEvG7EhEj5ZsgEoCRESObABL7EAAbAKK1gh -2Bv0WbAJELEHAbAKK1gh2Bv0WbIPAAEREjmwIRCxFQGwCitYIdgb9FmyGiEJERI5MDETNTM2NjU0ISE1 -IRYWFRQHFhMVFBYzNjY3NiczFxYHBgIjBAM1NCYn/pufk/7L/qABa+/87dsFU0F0hgQEQboXMAME9sf+ -vQ+HdQJ5ngJ7g/ueAdHJ6GJF/vxQT1sCzrm72Fi7gP3+1wgBTUB4kAEAAAEAMf/jBOgEOgAnAGOyDygp -ERI5ALAARViwHy8bsR8aPlmwAEVYsA4vG7EOEj5ZsQIBsAorWCHYG/RZsgcOHxESObIXKB8REjmwFy+x -FAGwCitYIdgb9FmwHxCxHQGwCitYIdgb9FmyJRQXERI5MDElBjM2Njc2JzMWFgcGBiMGJic1NCMjJzM2 -NjU0JiMhJyEWFhUUBxYXAucCX3B2AwRCtC0YAQTnuIeJB9jNAsB6bn11/vsGARjE3Ly2BNVYApuJmaaG -gDnN8ANwg0edlgFXSlVdlgOnmJ1KNLIAAAEAUv7XA/UFrwAhAGCyICIjERI5ALAXL7AARViwCS8bsQke -PlmwAEVYsBovG7EaEj5ZsgEiCRESObABL7EAAbAKK1gh2Bv0WbAJELEHAbAKK1gh2Bv0WbIPAAEREjmw -GhCwErAKK1jYG9xZMDETNTM2NjUQISE1IRYWFRQHFhMVMxUUBgcnNjcjJic1NCYjr6mkm/7K/vEBIej0 -5d4EqWFNalEOazwDkncCeZcBfYUBBZcD0sniZEb++KmUYchASHNuNKuPfo0AAQB5/scD2QQ6ACAAYLIg -ISIREjkAsBcvsABFWLAILxuxCBo+WbAARViwGi8bsRoSPlmyASEIERI5sAEvsQABsAorWCHYG/RZsAgQ -sQYBsAorWCHYG/RZsg8AARESObAaELASsAorWNgb3FkwMRMnMzY1NCMhNSEWFxYVFAcWFxUzFRQGByc2 -NyMmJzU0I8IB2+n1/ukBJ91sVr69AZpiTWlUDWczAtoBuJcCobKWA2dThKFJNcpMlGHKPkh0fSGFXrQA -AAEARP/rB3AFsAAjAGWyACQlERI5ALAARViwDi8bsQ4ePlmwAEVYsCAvG7EgEj5ZsABFWLAHLxuxBxI+ -WbAOELEAAbAKK1gh2Bv0WbAHELEIAbAKK1gh2Bv0WbAgELETAbAKK1gh2Bv0WbIZDiAREjkwMQEhAwIC -BgcjNTc+AjcTIREUFjMyNjc2JzcWFgcGAgcHIiY1BCf+GhoPWayQPyhdZDQLHgNfWU+ClwQCP7ocKQID -6cMus7cFEv2//t7+3IkCnQIHa+rzAsL7rGB0zbzA0gFmx2bs/toSArq0AAEAP//rBjoEOgAhAGWyICIj -ERI5ALAARViwDC8bsQwaPlmwAEVYsB4vG7EeEj5ZsABFWLAGLxuxBhI+WbAMELEAAbAKK1gh2Bv0WbAG -ELEHAbAKK1gh2Bv0WbAeELERAbAKK1gh2Bv0WbIWHgwREjkwMQEhAwIGByM1NzY2NxMhERQWMzI2NzYn -MxcWBw4CIyImJwMx/rsXFJylQTZVTQ0XAq9aT2x7BARBsxYwAwJsvniuswEDof5a/uvkAqMECqfTAg/9 -IWB5t6uyy1CxfJrmebixAAABAKn/5wdxBbAAHQCwshQeHxESOQCwAEVYsAAvG7EAHj5ZsABFWLAZLxux -GR4+WbAARViwES8bsRESPlmwAEVYsBcvG7EXEj5ZsBEQsQQBsAorWCHYG/RZsgoAERESObAXELAc0LAc -L7LvHAFxsl8cAV2yzxwBXbIfHAFxtG8cfxwCcbS/HM8cAnGynxwBcbJPHAFxsv8cAV2yrxwBXbIvHAFd -tA8cHxwCcrI/HAFysRUBsAorWCHYG/RZMDEBERQWMzY2NzYnNxYWBw4CIwYmJxEhESMRMxEhEQTpXUqG -lAQEQrsbKwICe9iKq7UI/ULBwQK+BbD7rGVvAs26t9sBYspnqPuDBLi7ASf9fwWw/W4CkgABAJD/5wZN -BDoAHAClshsdHhESOQCwAEVYsAQvG7EEGj5ZsABFWLAILxuxCBo+WbAARViwGS8bsRkSPlmwAEVYsAIv -G7ECEj5ZsAfQsAcvsm8HAV20vwfPBwJdsj8HAXG0zwffBwJxsg8HAXK0nwevBwJxsv8HAV2yDwcBcbKf -BwFdsi8HAV20bwd/BwJysQABsAorWCHYG/RZsBkQsQ0BsAorWCHYG/RZshIZCBESOTAxASERIxEzESER -MxEUFjM2Njc2JzMXFgcGAiMGJicDQ/4GubkB+rlcTWx8BARBshcwAwTmu6ezCAHN/jMEOv4qAdb9IWR1 -ArWrrNFTsXnq/vEEt7sAAQB2/+sEoAXFACIASbIVIyQREjkAsABFWLAJLxuxCR4+WbAARViwAC8bsQAS -PlmwCRCxDgGwCitYIdgb9FmwABCxFgGwCitYIdgb9FmyGwAJERI5MDEFIiQCJxE0EiQzMhcHJiMiAhUV -FBYWMzY2NzYnMxcWBw4CArmk/viVApQBCqXchzuGoqzXYrBxjZYDAzW6JhMBAnveFZsBGK0BEK8BHp1Y -ikT+/tL+g9V1ApmGms+zW1uIyW0AAQBl/+sDxwROAB4ARrITHyAREjkAsABFWLATLxuxExo+WbAARViw -Cy8bsQsSPlmxAAGwCitYIdgb9FmyBQsTERI5sBMQsRgBsAorWCHYG/RZMDElNjY3NCczFgcGBiMiADU1 -NDY2MzIXByYjIgYVFRQWAlFgWgIUshwBBMSt3P7wdtaLuWAsY4qDm6aCAlBZenKWVpmpATL3Hpf5jEKQ -OtyzH6vbAAEAI//nBUcFsAAYAE+yBRkaERI5ALAARViwAi8bsQIePlmwAEVYsBUvG7EVEj5ZsAIQsQAB -sAorWCHYG/RZsATQsAXQsBUQsQkBsAorWCHYG/RZsg4CFRESOTAxASE1IRUhERQWMzY2Eic3FhYHDgIj -BiYnAf7+JQSA/hxcTIaUCEK6GysDAnnZiaq3CAUSnp78SGByAtABbtsBYspnqfmEBLe8AAABAEb/5wS3 -BDoAGABPshYZGhESOQCwAEVYsAIvG7ECGj5ZsABFWLAVLxuxFRI+WbACELEAAbAKK1gh2Bv0WbAE0LAF -0LAVELEJAbAKK1gh2Bv0WbIOFQIREjkwMQEhNSEVIREUFjM2Njc2JzMWFgcGBiMGJicBrP6aA4v+lV5N -cXcDBECyKhsBBOi5qrMIA6SWlv21Y3QCnYmXrn2MPNDvBLm5AAEAlv/sBP8FxQApAHKyJCorERI5ALAA -RViwFi8bsRYePlmwAEVYsAsvG7ELEj5ZsQMBsAorWCHYG/RZsAsQsAbQsiULFhESObAlL7LPJQFdsp8l -AXGxJgGwCitYIdgb9FmyECYlERI5sBYQsBvQsBYQsR4BsAorWCHYG/RZMDEBFBYzMjY1MxQGBiMgJDU0 -JSYmNTQkITIWFhUjNCYjIgYVFBYXMxUjBgYBWM+wm8zBjf6d/vv+xAEUeIYBJQEGk/WMwcGSp8Kto8TE -sbUBkniSmHSDvmflxf9WMKZlxNtlunVnj4h2dX0CngJ+AP//AC/+SwWsBbAAJgDdAAAABwJUBEUAAP// -ACz+SwS7BDoAJgDyAAAABwJUA1QAAAACAG8EcALJBdYABQANACMAsAsvsAfQsAcvsAHQsAEvsAsQsATQ -sAQvsAXQGbAFLxgwMQETMxUDIwEzFRYXByY1AZF0xN9Z/t6oA1BJsgSUAUIV/sMBUlt7VTtfuwD//wAl -Ah8CDQK2AAYAEQAA//8AJQIfAg0CtgAGABEAAP//AKMCiwSNAyIARgGv2QBMzUAA//8AkQKLBckDIgBG -Aa+EAGZmQAAAAgAN/msDoQAAAAMABwAIALIFAgMrMDEBITUhNSE1IQOh/GwDlPxsA5T+a5dnlwAAAQBg -BDEBeAYTAAgAIbIICQoREjkAsABFWLAALxuxACA+WbIFCQAREjmwBS8wMQEXBgcVIzU0NgEOal0DuGEG -E0h/k4h0ZsgAAQAwBBYBRwYAAAgAIbIICQoREjkAsABFWLAELxuxBCA+WbIACQQREjmwAC8wMRMnNjc1 -MxUGBplpXQO3AWEEFkiCkJCCZMcAAQAk/uUBOwC1AAgAH7IICQoREjkAsAkvsQQFsAorWCHYG/RZsADQ -sAAvMDETJzY3NTMVFAaNaVsDuWP+5Ul/knZkZcoAAAEATwQWAWcGAAAIAAwAsAgvsATQsAQvMDEBFRYX -ByYmJzUBBgRdak1fAgYAk5B/SEDCYYcA//8AaAQxArsGEwAmAYQIAAAHAYQBQwAA//8APAQWAoYGAAAm -AYUMAAAHAYUBPwAAAAIAJP7TAmQA9gAIABEAMbIKEhMREjmwChCwBdAAsBIvsQQFsAorWCHYG/RZsADQ -sAAvsAnQsAkvsAQQsA3QMDETJzY3NTMVFAYXJzY3NTMVFAaNaVsDuWPdaVsDumH+00iJmbmkbNNASImZ -uaRr0QABAEYAAAQkBbAACwBMALAARViwCC8bsQgePlmwAEVYsAYvG7EGGj5ZsABFWLAKLxuxCho+WbAA -RViwAi8bsQISPlmwChCxAAGwCitYIdgb9FmwBNCwBdAwMQEhESMRITUhETMRIQQk/my6/nABkLoBlAOh -/F8DoZkBdv6KAAABAFf+YAQ0BbAAEwB+ALAARViwDC8bsQwePlmwAEVYsAovG7EKGj5ZsABFWLAOLxux -Dho+WbAARViwAi8bsQIUPlmwAEVYsAAvG7EAEj5ZsABFWLAELxuxBBI+WbEGAbAKK1gh2Bv0WbAOELEI -AbAKK1gh2Bv0WbAJ0LAQ0LAR0LAGELAS0LAT0DAxISERIxEhNSERITUhETMRIRUhESEENP5quv5zAY3+ -cwGNugGW/moBlv5gAaCXAwqZAXb+ipn89gAAAQCKAhcCIgPLAA0AF7IKDg8REjkAsAMvsAqwCitY2Bvc -WTAxEzQ2MzIWFRUUBiMiJjWKb1xbcm5eXW8DBFdwbV0lV25vWAD//wCU//UDLwDRACYAEgQAAAcAEgG5 -AAD//wCU//UEzgDRACYAEgQAACcAEgG5AAAABwASA1gAAAABAFICAgEsAtUACwAZsgMMDRESOQCwAy+x -CQWwCitYIdgb9FkwMRM0NjMyFhUUBiMiJlI2NjY4ODY2NgJrLT09LS08PAAABgBE/+sHVwXFABUAIwAn -ADUAQwBRALyyAlJTERI5sAIQsBvQsAIQsCbQsAIQsCjQsAIQsDbQsAIQsEnQALAARViwGS8bsRkePlmw -AEVYsBIvG7ESEj5ZsAPQsAMvsAfQsAcvsBIQsA7QsA4vsBkQsCDQsCAvsiQSGRESObAkL7ImGRIREjmw -Ji+wEhCxKwSwCitYIdgb9FmwAxCxMgSwCitYIdgb9FmwKxCwOdCwMhCwQNCwIBCxRwSwCitYIdgb9Fmw -GRCxTgSwCitYIdgb9FkwMQE0NjMyFzYzMhYVFRQGIyInBiMiJjUBNDYzMhYVFRQGIyImNQEnARcDFBYz -MjY1NTQmIyIGFQUUFjMyNjU1NCYjIgYVARQWMzI2NTU0JiMiBhUDN6eDmE1Pl4Oop4KZT0yXgqr9DaeD -hKelhIKqAWloAsdos1hKSFZXSUdZActYSUhWV0lIV/tCWEpHV1ZKSFgBZYOpeXmoi0eDqXh4p4sDe4Oq -qohIgaqni/wcQgRyQvw3T2VjVUpPZGNUSk9lZlJKT2RkUwLqTmViVUlOZmVTAAABAGwAmQIgA7UABgAQ -ALAFL7ICBwUREjmwAi8wMQEBIwE1ATMBHgECjf7ZASeNAib+cwGEEwGFAAEAWQCYAg4DtQAGABAAsAAv -sgMHABESObADLzAxEwEVASMBAecBJ/7ZjgEC/v4Dtf57E/57AY4BjwABADsAbgNqBSIAAwAJALAAL7AC -LzAxNycBF6NoAsdobkIEckIA//8ANgKbArsFsAMHAiAAAAKbABMAsABFWLAJLxuxCR4+WbAN0DAxAAAB -AHoCiwL4BboADwBUsgoQERESOQCwAEVYsAAvG7EAHj5ZsABFWLADLxuxAx4+WbAARViwDS8bsQ0WPlmw -AEVYsAYvG7EGFj5ZsgENAxESObADELEKA7AKK1gh2Bv0WTAxExc2MyARESMRJiMiBxEjEfoeSpIBBKoD -jW4sqgWre4r+xv4LAea5bf3OAyAAAQBbAAAEaAXEACkAmrIhKisREjkAsABFWLAZLxuxGR4+WbAARViw -Bi8bsQYSPlmyKRkGERI5sCkvsQACsAorWCHYG/RZsAYQsQQBsAorWCHYG/RZsAjQsAnQsAAQsA7QsCkQ -sBDQsCkQsBXQsBUvtg8VHxUvFQNdsRICsAorWCHYG/RZsBkQsB3QsBkQsSABsAorWCHYG/RZsBUQsCTQ -sBIQsCbQMDEBIRcUByEHITUzNjY3NScjNTMnIzUzJzQ2MzIWFSM0JiMiBhUXIRUhFyEDFf6xAz4C3QH7 -+E0oMgIDqqYEop0G9ci+3r9/b2mCBgFc/qkEAVMB1kSaW52dCYNgCEV9iH23x+7UsWt8mn23fYgABQAf -AAAGNgWwABsAHwAjACYAKQCzALAARViwFy8bsRcePlmwAEVYsBovG7EaHj5ZsABFWLAMLxuxDBI+WbAA -RViwCS8bsQkSPlmyEAwXERI5sBAvsBTQsBQvtA8UHxQCXbAk0LAkL7AY0LAYL7AA0LAAL7AUELETAbAK -K1gh2Bv0WbAf0LAj0LAD0LAQELAc0LAcL7Ag0LAgL7AE0LAEL7AQELEPAbAKK1gh2Bv0WbAL0LAp0LAH -0LImFwwREjmyJwkaERI5MDEBMxUjFTMVIxEjASERIxEjNTM1IzUzETMBIREzASEnIwUzNSElMycBNSMF -V9/f39/C/sH+YsDZ2dnZwAFRAY+//GEBO2HaAhTM/tT+THd3AuBoA6yYlJj+GAHo/hgB6JiUmAIE/fwC -BPzQlJSUmLb8558AAAIAp//sBgMFsAAfACgAprIjKSoREjmwIxCwEdAAsABFWLAWLxuxFh4+WbAARViw -Gi8bsRoaPlmwAEVYsB4vG7EeGj5ZsABFWLAKLxuxChI+WbAARViwFC8bsRQSPlmwHhCxAAGwCitYIdgb -9FmwChCxBQGwCitYIdgb9FmwABCwDtCwD9CyIRQWERI5sCEvsRIBsAorWCHYG/RZsB4QsB3QsB0vsBYQ -sScBsAorWCHYG/RZMDEBIxEUFjMyNxcGIyImNREjBgYHIxEjESEyFhczETMRMwEzMjY1NCYnIwX+yjZB -IzQBSUZ8fo8U58fJuQF5yu0Uj7rK+2LAi4uHhMsDq/1hQUEMlhSWigKft70C/csFsMC2AQb++v6SjZeY -jgL//wCo/+wIEAWwACYANgAAAAcAVwRVAAAABwAfAAAFzAWwAB8AIwAnACsAMAA1ADoA/rI5OzwREjmw -ORCwHtCwORCwItCwORCwJ9CwORCwK9CwORCwLdCwORCwM9AAsABFWLACLxuxAh4+WbAARViwDC8bsQwS -PlmwAEVYsBAvG7EQEj5ZsggCDBESObAIL7AE0LAEL7AA0LAEELEGAbAKK1gh2Bv0WbAIELEKAbAKK1gh -2Bv0WbAO0LAKELAS0LAIELAU0LAGELAW0LAEELAY0LACELAa0LAEELAc0LACELAe0LAIELAg0LAGELAi -0LAIELAk0LAGELAm0LAIELAo0LAGELAq0LAKELAt0LIwAgwREjmwChCwMtCyNQIMERI5sAQQsDbQsjkC -DBESOTAxATMTMwMzFSMHMxUjAyMDIwMjAyM1MycjNTMDMxMzEzMBMzcjBTM3IwUzJyMDNyMXFyU3IxcX -ATMnJwcDp+pYwWWHqCnR8Wa4VuVYuGfszCmjgmXAW/FWs/5IcCO4AnFsJLP+3K4iaNYCNwEXAmUBNQIb -/sAyARgYA9QB3P4kmMKY/h4B4v4eAeKYwpgB3P4kAdz8ysLCwsLC/pwKBtLSBgfLAsQHrbEAAAIAjAAA -BZ4EOgANABsAZgCwAEVYsBYvG7EWGj5ZsABFWLAALxuxABo+WbAARViwCy8bsQsSPlmwAEVYsA4vG7EO -Ej5ZsREBsAorWCHYG/RZsgURABESObAFL7AAELEKAbAKK1gh2Bv0WbIPCgsREjmwDy8wMQEyFhcRIxE0 -JichESMRAREzESEyNjcRMxEGBgcCuq+oBLllb/69uQGJuQE+cWcBuQKlrQQ6wb/+owFMf3gB/F8EOvvG -At39u3V+Aq/9TsLEAgAAAQBf/+wEHAXEACMAi7IVJCUREjkAsABFWLAWLxuxFh4+WbAARViwCS8bsQkS -PlmyIwkWERI5sCMvsQACsAorWCHYG/RZsAkQsQQBsAorWCHYG/RZsAAQsAzQsCMQsA/QsCMQsB/QsB8v -tg8fHx8vHwNdsSACsAorWCHYG/RZsBDQsB8QsBPQsBYQsRsBsAorWCHYG/RZMDEBIRYWMzI3FwYjIgAD -IzUzNSM1MxIAMzIXByYjIgYHIRUhFSEDUf6ABLSldGYUeHj4/uMGsrKysgoBHfNqhxRtbqSxBgF//oAB -gAIdw9IioB4BJQEMfIl9AQYBHx+iI8u8fYkABAAfAAAFvAWwABkAHgAjACgAvACwAEVYsAsvG7ELHj5Z -sABFWLABLxuxARI+WbALELEoAbAKK1gh2Bv0WbIkKAEREjmwJC+ycCQBcbYAJBAkICQDXbEcAbAKK1gh -2Bv0WbAd0LAdL7JwHQFxtgAdEB0gHQNdsSABsAorWCHYG/RZsCHQsCEvsnAhAXGyICEBXbEAAbAKK1gh -2Bv0WbAgELAD0LAdELAG0LAGL7AcELAH0LAkELAK0LAkELAP0LAcELAS0LAdELAU0LAULzAxAREjESM1 -MzUjNTM1ITIWFzMVIxcHMxUjBiEBJyEVIQchFSEyASEmIyEBpcDGxsbGAhmx6zbswwMCwuVr/owBRAT9 -bQKVP/2qAVms/fsCSlSe/qgCOv3GAzCXXpf0hHCXMiyX9gG3NF6XWQHlVgAAAQAqAAAD+AWwABoAaQCw -AEVYsBkvG7EZHj5ZsABFWLAMLxuxDBI+WbAZELEYAbAKK1gh2Bv0WbAB0LAYELAU0LAUL7AD0LAUELET -AbAKK1gh2Bv0WbAG0LATELAO0LAOL7EJAbAKK1gh2Bv0WbINCQ4REjkwMQEjFhczByMGBiMBFSMBJzM2 -NjchNyEmJyE3IQPK7EARyS6YEvbbAe3j/e4B+X2cFf29LgITMPb+5y8DnQUSUXWesrT9xAwCaX0Ba1ye -vgieAAABACD/7gQaBbAAHgCQALAARViwES8bsREePlmwAEVYsAUvG7EFEj5ZshMRBRESObATL7AX0LAX -L7IAFwFdsRgBsAorWCHYG/RZsBnQsAjQsAnQsBcQsBbQsAvQsArQsBMQsRQBsAorWCHYG/RZsBXQsAzQ -sA3QsBMQsBLQsA/QsA7QsAUQsRoBsAorWCHYG/RZsh4FERESObAeLzAxARUGAgQjIicRBzU3NQc1NxEz -ETcVBxU3FQcRNhIRNQQaApD+969QbPT09PTA+/v7+77JAwNk0v7HphICWm+yb5lvsm8BWf7/c7JzmXOy -c/3eAgEQAQlYAAABAF0AAATrBDoAFwBdsgAYGRESOQCwAEVYsBYvG7EWGj5ZsABFWLAELxuxBBI+WbAA -RViwCi8bsQoSPlmwAEVYsBAvG7EQEj5ZsgAKFhESObAAL7EJAbAKK1gh2Bv0WbAM0LAAELAV0DAxARYA -ERUjNSYCJxEjEQYCBxUjNRIANzUzAv/nAQW5Ap6TuY+fArkDAQffuQNxIf6N/tq3yN8BBSD9NALKIf71 -2MbFAR0BbSLJAAACAB8AAAUDBbAAFgAfAHAAsABFWLAMLxuxDB4+WbAARViwAy8bsQMSPlmyBgMMERI5 -sAYvsQUBsAorWCHYG/RZsAHQsAYQsArQsAovtA8KHwoCXbEJAbAKK1gh2Bv0WbAU0LAGELAV0LAKELAX -0LAMELEfAbAKK1gh2Bv0WTAxASERIxEjNTM1IzUzESEyBBUUBAchFSEBITI2NTQmJyEC/P6xv8/Pz88C -GeoBEv758v6jAU/+sQFam6Koj/6gARP+7QETnomdAtnuy9XnAYkBJpKMf50BAAAEAHr/6wWDBcUAGwAn -ADUAOQC7shw6OxESObAcELAA0LAcELAo0LAcELA40ACwAEVYsAovG7EKHj5ZsABFWLAlLxuxJRI+WbAK -ELAD0LADL7IOCgMREjm2Kg46DkoOA12wChCxEQSwCitYIdgb9FmwAxCxGASwCitYIdgb9FmyGwMKERI5 -tDYbRhsCXbIlGwFdsCUQsB/QsB8vsCUQsSsEsAorWCHYG/RZsB8QsTIEsAorWCHYG/RZsjYlChESObA2 -L7I4CiUREjmwOC8wMQEUBiMiJjU1NDYzMhYVIzQmIyIGFRUUFjMyNjUBNDYgFhUVFAYgJjUXFBYzMjY1 -NTQmIyIGFQUnARcCqJh7eqGee3mciklCQU1PQT1MARCnAQaop/78qopYSkhWV0lHWf4GaQLHaQQebpCo -iUeCq5FvOk1mUklOZUw6/UeDqaiLR4Opp4sGT2VjVUpPZGNU80IEckIAAAIAaP/rA2oGEwAXACEAZ7IT -IiMREjmwExCwGNAAsABFWLAMLxuxDCA+WbAARViwAC8bsQASPlmyBgwAERI5sAYvsQUBsAorWCHYG/RZ -sBPQsAAQsRcBsAorWCHYG/RZsAYQsBjQsAwQsR8BsAorWCHYG/RZMDEFIiY1BiM1MjcRNjYzMhYVFRQC -BxUUFjMDNjY1NTQmIyIHAszC0mJucV8BnYV4l86ra3DbWWcwJmcDFerrHLAjAiSyxq2TJcH+j2timo0C -Y1X1eydSTNEABACiAAAHxgXAAAMAEAAeACgAprIfKSoREjmwHxCwAdCwHxCwBNCwHxCwEdAAsABFWLAn -LxuxJx4+WbAARViwJS8bsSUePlmwAEVYsAcvG7EHHj5ZsABFWLAiLxuxIhI+WbAARViwIC8bsSASPlmw -BxCwDdCwAtCwAi+yEAIBXbEBA7AKK1gh2Bv0WbANELEUA7AKK1gh2Bv0WbAHELEbA7AKK1gh2Bv0WbIh -JSAREjmyJiAlERI5MDEBITUhATQ2IBYVFRQGIyImNRcUFjMyNjc1NCYjIgYVASMBESMRMwERMwek/ZkC -Z/11ugE4u7mcnrqjX1ZUXQFfVVRf/rzM/a+5ywJUtwGcjgI9m767o12duruhBWJramBlYWtrY/ubBG77 -kgWw+48EcQAAAgBnA5cEOAWwAAwAFABuALAARViwBi8bsQYePlmwAEVYsAkvG7EJHj5ZsABFWLATLxux -Ex4+WbIBFQYREjmwAS+yAAkBERI5sgMBBhESObAE0LIIAQkREjmwARCwC9CwBhCwDbAKK1jYG9xZsAEQ -sA/QsA0QsBHQsBLQMDEBAyMDESMRMxMTMxEjASMRIxEjNSED3ow0jFpwkJBwWv4Lk1uUAYIFIf52AYn+ -dwIZ/nEBj/3nAcj+OAHIUQACAJj/7ASTBE4AFQAcAGWyAh0eERI5sAIQsBbQALAARViwCi8bsQoaPlmw -AEVYsAIvG7ECEj5ZshoKAhESObAaL7EPCrAKK1gh2Bv0WbACELETCrAKK1gh2Bv0WbIVCgIREjmwChCx -FgqwCitYIdgb9FkwMSUGIyImAjU0EjYzMhYWFxUhERYzMjcBIgcRIREmBBa3u5H0h5D4hIXjhAP9AHea -xKz+kJd6AhxzXnKdAQGTjwEDn4vzkD7+uG56Ayp6/usBHnEA//8AVP/1BbMFmwAnAcb/2gKGACcBlADm -AAABBwIkAxQAAAAQALAARViwBS8bsQUePlkwMf//AGT/9QZTBbQAJwIfACYClAAnAZQBpQAAAQcCJAO0 -AAAAEACwAEVYsA4vG7EOHj5ZMDH//wBj//UGSQWkACcCIQAIAo8AJwGUAYMAAAEHAiQDqgAAABAAsABF -WLABLxuxAR4+WTAx//8AWf/1Bf0FpAAnAiMAHwKPACcBlAEgAAABBwIkA14AAAAQALAARViwBS8bsQUe -PlkwMQACAGr/6wQyBewAGwAqAF6yFSssERI5sBUQsCPQALANL7AARViwFS8bsRUSPlmyAA0VERI5sAAv -sgMAFRESObANELEHAbAKK1gh2Bv0WbAAELEcAbAKK1gh2Bv0WbAVELEjAbAKK1gh2Bv0WTAxATIWFy4C -IyIHJzc2MyAAERUUAgYjIgA1NTQAFyIGFRUUFjMyNjU1JyYmAjxdpjoOaaZggZsQMXSXAQcBH3jekNr+ -+AEA5Iyfn4qOnwQcoAP+TUSM2Xk7lxUw/k7+bjK8/talASP2DtwBEJi7oBCqz/nbPQ9aagABAKn/KwTl -BbAABwAoALAEL7AARViwBi8bsQYePlmwBBCwAdCwBhCxAgGwCitYIdgb9FkwMQUjESERIxEhBOW5/Ta5 -BDzVBe36EwaFAAABAEX+8wSrBbAADAA3ALADL7AARViwCC8bsQgePlmwAxCxAgGwCitYIdgb9FmwBdCw -CBCxCgGwCitYIdgb9FmwB9AwMQEBIRUhNQEBNSEVIQEDa/27A4X7mgJh/Z8EGfzHAkYCQf1KmI8CzALS -kJj9QgABAKgCiwPrAyIAAwAcALAARViwAi8bsQIYPlmxAQGwCitYIdgb9FkwMQEhNSED6/y9A0MCi5cA -AAEAPwAABJgFsAAIAD2yAwkKERI5ALAHL7AARViwAS8bsQEePlmwAEVYsAMvG7EDEj5ZsgABAxESObAH -ELEGAbAKK1gh2Bv0WTAxAQEzASMDIzUhAjABq7394o31uQE7ARwElPpQAnSaAAADAGL/6wfLBE4AHAAs -ADwAcbIHPT4REjmwBxCwJNCwBxCwNNAAsABFWLAELxuxBBI+WbAARViwCi8bsQoSPlmwE9CwEy+wGdCw -GS+yBxkEERI5shYZBBESObAKELEgAbAKK1gh2Bv0WbATELEpAbAKK1gh2Bv0WbAw0LAgELA50DAxARQC -BiMiJicGBiMiJgI1NTQSNjMyFhc2NjMyABUFFBYzMjY3NzUuAiMiBhUlNCYjIgYHBxUeAjMyNjUHy37f -iZHuUFHskInegH7fiJHtUVDvks4BFvlQpohyuTQLGHKSUIamBfemhXO8NQkWdZBQiKUCD5P/AJG4sbO2 -jwEAlxiTAQCSt7Oxuf7B8w2x3LyjJypjwGHcuQiu372oHyphxWDeuAAB/7D+SwKOBhUAFQA/sgIWFxES -OQCwAEVYsA4vG7EOID5ZsABFWLADLxuxAxQ+WbEIAbAKK1gh2Bv0WbAOELETAbAKK1gh2Bv0WTAxBRQG -IyInNxYzMjURNDYzMhcHJiMiFQFlpJ45OhIuIZuxoTxUGCU2tmuiqBSRDbEFGaq+FY4L2wACAGUBGAQL -A/QAFQArAJGyHCwtERI5sBwQsAXQALADL7IPAwFdsA3QsA0vsgANAV2xCAGwCitYIdgb9FmwAxCwCtCw -Ci+wAxCxEgGwCitYIdgb9FmwDRCwFdCwFS+wDRCwGdCwGS+wI9CwIy+yACMBXbEeAbAKK1gh2Bv0WbAZ -ELAg0LAgL7AZELEoAbAKK1gh2Bv0WbAjELAr0LArLzAxEzY2MzYXFxYzMjcVBiMiJycmByIGBwc2NjM2 -FxcWMzI3FwYjIicnJgciBgdmMINCUkqYQk6GZmeFTkKhRE9CgzABMIJCUkqVRFCFZgFnhU5CmEpSQoMw -A4UzOgIjTh+Avm0fUx8CRDzlMzsCI00hgL1tH04jAkQ8AAABAJgAmwPaBNUAEwA5ALATL7EAAbAKK1gh -2Bv0WbAE0LATELAH0LATELAP0LAPL7EQAbAKK1gh2Bv0WbAI0LAPELAL0DAxASEHJzcjNSE3ITUhExcH -MxUhByED2v3tjl9srgELlf5gAf6ZX3fD/t+UAbUBj/Q7uaD/oQEGO8uh/wD//wA+AAIDgQQ9AGYAIABh -QAA5mgEHAa//lv13AB0AsABFWLAFLxuxBRo+WbAARViwCC8bsQgSPlkwMQD//wCFAAED3ARQAGYAIgBz -QAA5mgEHAa//3f12AB0AsABFWLACLxuxAho+WbAARViwCC8bsQgSPlkwMQAAAgArAAAD3AWwAAUACQA4 -sggKCxESObAIELAB0ACwAEVYsAAvG7EAHj5ZsABFWLADLxuxAxI+WbIGAAMREjmyCAADERI5MDEBMwEB -IwkEAbyMAZT+cI3+bAHW/ukBHAEYBbD9J/0pAtcCD/3x/fICDgD//wC1AKcBmwT1ACcAEgAlALIABwAS -ACUEJAACAG4CeQIzBDoAAwAHACwAsABFWLACLxuxAho+WbAARViwBi8bsQYaPlmwAhCwANCwAC+wBNCw -BdAwMRMjETMBIxEz+42NATiNjQJ5AcH+PwHBAAABAFz/XwFXAO8ACAAgsggJChESOQCwCS+wBNCwBC+0 -QARQBAJdsADQsAAvMDEXJzY3NTMVFAbFaUgCsU+hSG1/XExbswD//wA8AAAE9gYVACYASgAAAAcASgIs -AAAAAgAfAAADzQYVABUAGQCFsggaGxESObAIELAX0ACwAEVYsAgvG7EIID5ZsABFWLADLxuxAxo+WbAA -RViwES8bsREaPlmwAEVYsBgvG7EYGj5ZsABFWLAALxuxABI+WbAARViwFi8bsRYSPlmwAxCxAQGwCitY -Idgb9FmwCBCxDQGwCitYIdgb9FmwARCwE9CwFNAwMTMRIzUzNTQ2MzIXByYjIgYVFTMVIxEhIxEzyqur -z71wqx99cXdp3d0CSbq6A6uPXLXKPZwya2tej/xVBDoAAQA8AAAD6QYVABYAXgCwAEVYsBIvG7ESID5Z -sABFWLAGLxuxBho+WbAARViwCS8bsQkSPlmwAEVYsBYvG7EWEj5ZsBIQsQIBsAorWCHYG/RZsAYQsQcB -sAorWCHYG/RZsAvQsAYQsA7QMDEBJiMiFRUzFSMRIxEjNTM1NjYzMgURIwMwfEzI5+e5q6sBwLFlASu5 -BWMU0muP/FUDq492rbg9+igAAAIAPAAABjIGFQAnACsAnwCwAEVYsBYvG7EWID5ZsABFWLAILxuxCCA+ -WbAARViwIC8bsSAaPlmwAEVYsBIvG7ESGj5ZsABFWLAELxuxBBo+WbAARViwKi8bsSoaPlmwAEVYsCkv -G7EpEj5ZsABFWLAjLxuxIxI+WbAARViwJy8bsScSPlmwIBCxIQGwCitYIdgb9FmwJdCwAdCwCBCxDQGw -CitYIdgb9FmwG9AwMTMRIzUzNTQ2MzIXByYjIgYVFSE1NDYzMhcHJiMiBhUVMxUjESMRIREhIxEz56ur -uqpAPwovNVpiAZDPvXCrH31yd2ne3rn+cASSubkDq49vrr4RlglpYnJctco9nDJqbF6P/FUDq/xVBDoA -AAEAPAAABjIGFQAoAGwAsABFWLAILxuxCCA+WbAARViwIS8bsSEaPlmwAEVYsCgvG7EoEj5ZsCEQsSIB -sAorWCHYG/RZsCbQsAHQsCEQsBLQsATQsAgQsQ0BsAorWCHYG/RZsAgQsBbQsCgQsCXQsBrQsA0QsB3Q -MDEzESM1MzU0NjMyFwcmIyIGFRUhNTY2MzIFESMRJiMiFRUzFSMRIxEhEeerq7qqQD8KLzVaYgGQAcCx -ZQEruXxMyOfnuf5wA6uPb66+EZYJaWJydq24PfooBWMU0muP/FUDq/xVAAEAPP/sBJsGFQAmAHYAsABF -WLAhLxuxISA+WbAARViwHS8bsR0aPlmwAEVYsBgvG7EYEj5ZsABFWLAKLxuxChI+WbAdELAQ0LAl0LEB -AbAKK1gh2Bv0WbAKELEFAbAKK1gh2Bv0WbABELAO0LAhELEVAbAKK1gh2Bv0WbAOELAa0DAxASMRFBYz -MjcXBiMiJjURIzUzESYnJyIVESMRIzUzNTQ2MzIWFxEzBJbKNkEjNAFJRnx+xcU9Zhi3uaurs6Bd21rK -A6v9YUFBDJYUlooCn48BHxwHAd37YAOrj3Ctvjks/ooAAQBf/+wGVAYRAEwAzbIWTU4REjkAsABFWLBH -LxuxRyA+WbAARViwDy8bsQ8aPlmwAEVYsEsvG7FLGj5ZsABFWLBALxuxQBo+WbAARViwCS8bsQkSPlmw -AEVYsCwvG7EsEj5ZsEsQsQEBsAorWCHYG/RZsAkQsQQBsAorWCHYG/RZsAEQsA3QsEcQsRQBsAorWCHY -G/RZsh1ALBESObBAELEgAbAKK1gh2Bv0WbI6LEAREjmwOhCxJQGwCitYIdgb9FmyMSxAERI5sCwQsTQB -sAorWCHYG/RZMDEBIxEUMzI3FwYjIiY1ESM1MzU0JiMiBhUUHgIVIzQmIyIGFRQWBBYWFRQGIyImJjUz -FhYzMjY1NCYkJiY1NDYzMhcmNTQ2MzIWFRUzBk/KdyM0AU1CdoS8vGZiWFwfJR66gWJlcmoBFaxT6LmC -yHG5BYtyaX9x/uelT+GvYFYsypu5ycoDq/1+nwyWFKaXAoKPVXJ1WEY7aXB8TExuWEdDRD5WeVeRr1yl -YF1tVUdLUzxUdFCFuB5uUnylx8NNAAAWAFv+cgfuBa4ADQAaACgANwA9AEMASQBPAFYAWgBeAGIAZgBq -AG4AdgB6AH4AggCGAIoAjgHGshCPkBESObAQELAA0LAQELAb0LAQELAw0LAQELA80LAQELA+0LAQELBG -0LAQELBK0LAQELBQ0LAQELBX0LAQELBb0LAQELBh0LAQELBj0LAQELBn0LAQELBt0LAQELBw0LAQELB3 -0LAQELB70LAQELB/0LAQELCE0LAQELCI0LAQELCM0ACwPS+wAEVYsEYvG7FGHj5Zsn5JAyuyensDK7KC -dwMrsn86AyuyCj1GERI5sAovsAPQsAMvsA7QsA4vsAoQsA/QsA8vslAODxESObBQL7FvB7AKK1gh2Bv0 -WbIVUG8REjmwChCxHgewCitYIdgb9FmwAxCxJQewCitYIdgb9FmwDxCwKdCwKS+wDhCwLtCwLi+xNAew -CitYIdgb9FmwPRCxPAqwCitYIdgb9FmwPRCwa9CwZ9CwY9CwPtCwPBCwbNCwaNCwZNCwP9CwOhCwQdCw -RhCwYNCwXNCwWNCwS9CxSgqwCitYIdgb9FmwWtCwXtCwYtCwR9CwSRCwTtCwDhCxUQewCitYIdgb9Fmw -DxCxdgewCitYIdgb9FmwdxCwhNCwehCwhdCwexCwiNCwfhCwidCwfxCwjNCwghCwjdAwMQEUBiMiJic1 -NDYzMhYXExEzMhYVFAcWFhUUIwE0JiMiBhUVFBYzMjY1ATMRFAYjIiY1MxQzMjY1AREzFTMVITUzNTMR -AREhFSMVJTUhESM1ARUzMjU0JxM1IRUhNSEVITUhFQE1IRUhNSEVITUhFRMzMjU0JiMjASM1MzUjNTMR -IzUzJSM1MzUjNTMRIzUzAzmBZGaAAn5oZYACQ7xiclQyNND+j0pBQEpKQkBJA7pcaVJYbV1oKTb5xHHE -BSjHb/htATXEBewBNm/8XH5nYssBFv1bARX9XAEUAgoBFv1bARX9XAEUvF12Ojxd/PFxcXFxcXEHIm9v -b29vbwHUYnl4XnVffHhe/rMCJUlNVCANRi2bAUhFTk5FcEVOTkUBT/6GTl1RU1s2LPzJATvKcXHK/sUG -HwEddKmpdP7jqfy2qVNSBANKdHR0dHR0+ThxcXFxcXEDxFApHv7T/H76/BX5fvx++vwV+QAFAFz91QfX -CHMAAwAcACAAJAAoAFKzEREQBCuzBBEcBCuzChEXBCuwBBCwHdCwHBCwHtAAsCEvsCUvshweAyuwJRCw -ANCwAC+wIRCwAtCwAi+yDQACERI5sA0vsh8eAhESObAfLzAxCQMFNDY3NjY1NCYjIgYHMzY2MzIWFRQH -BgYVFyMVMwMzFSMDMxUjBBgDv/xB/EQEDx4kSlynlZCgAssCOis5OF1bL8rKyksEBAIEBAZS/DH8MQPP -8To6GCeHSoCXi38zNEA0XzxBXExbqv1MBAqeBAABAEIAAAKrAyAAFgBWsggXGBESOQCwAEVYsA4vG7EO -GD5ZsABFWLAALxuxABI+WbEVArAKK1gh2Bv0WbAC0LIUFQ4REjmyAw4UERI5sA4QsQgCsAorWCHYG/RZ -sA4QsAvQMDEhITUBNjU0JiMiBhUjNDYgFhUUDwIhAqv9qQEsbUA8S0edpwEImmtUsAGPbAEaZkUxPUw5 -cpR/bmhrT5EAAQB6AAAB7wMVAAYANgCwAEVYsAUvG7EFGD5ZsABFWLABLxuxARI+WbIEBQEREjmwBC+x -AwKwCitYIdgb9FmwAtAwMSEjEQc1JTMB753YAWMSAlk5gHUAAAIAUP/1Ap0DIAANABcASLIDGBkREjmw -AxCwENAAsABFWLAKLxuxChg+WbAARViwAy8bsQMSPlmwChCxEAKwCitYIdgb9FmwAxCxFQKwCitYIdgb -9FkwMQEUBiMiJic1NDYzMhYXJzQjIgcVFDMyNwKdmI2LnAGbi42YAp2KhQSLhAQBRaKurKCOo66snQfA -tLPCtQACAFX/+gOaBJ0AEwAgAFQAsABFWLAILxuxCBw+WbAARViwEC8bsRASPlmyAhAIERI5sAIvsBAQ -sREBsAorWCHYG/RZsAIQsRQBsAorWCHYG/RZsAgQsRsBsAorWCHYG/RZMDEBBiMiJjU0NjMyFhUVEAAF -IzUzJAMyNjc1NCYjIgYVFBYC32Wrrszlusbg/sz+1CkjAZTXT4MehGlof3wB7G7XsLTk/uI//sH+wAWY -BwF4T0BChJ6PbG2LAAMAYP/wA60EnQAVACEALABlALAARViwEy8bsRMcPlmwAEVYsAkvG7EJEj5ZsCrQ -sCovst8qAV2yHyoBXbEZAbAKK1gh2Bv0WbIDKhkREjmyDhkqERI5sAkQsR8BsAorWCHYG/RZsBMQsSUB -sAorWCHYG/RZMDEBFAYHFhYVFAYgJjU0NjcmJjU0NiAWAzQmIyIGFRQWMzI2AzQmIyIGFRQWMjYDkGNV -YnPo/oTpcWJVYNYBYtqcg2xrgH9ubYAedF1ebm++cANaVocmJpNil7WzmWOSJyaGVpSvr/1YVm5sWFtk -ZwJlTmRhUVBiYwABAEIAAAPABI0ABgA6sgEHCBESOQCwAEVYsAUvG7EFHD5ZsABFWLABLxuxARI+WbAF -ELEDAbAKK1gh2Bv0WbIABQMREjkwMQEBIwEhNSEDwP3owwIX/UYDfgQk+9wD9JkAAAIAcv/wA7sEkwAV -ACAAZbIHISIREjmwBxCwFtAAsABFWLAALxuxABw+WbAARViwDi8bsQ4SPlmwABCxAQGwCitYIdgb9Fmy -CA4AERI5sAgvsgUIDhESObEWAbAKK1gh2Bv0WbAOELEcAbAKK1gh2Bv0WTAxARUjBgYHNjYzMhYVFAYj -IiY1NRAAIQMiBgcVFBYyNjQmAwAeyOAONJZOrsnfvsLqAUABPNBQgyCJ0n57BJOcA7ixOT/XrrDe+9RL -AT8BSv3YTUAoiqSF2IYAAQCA//ADxQSNAB0Aa7IaHh8REjkAsABFWLABLxuxARw+WbAARViwDS8bsQ0S -PlmwARCxAwGwCitYIdgb9FmyBwENERI5sAcvsRoBsAorWCHYG/RZsgUHGhESObANELAR0LANELEUAbAK -K1gh2Bv0WbAHELAd0DAxExMhFSEDNjMyFhUUBiMiJiczFhYzMjY1NCYjIgcHpEUCqP30JWNzuNffxKvq -DbIOgGJweYxzaUIpAkMCSqL+3zDStLLSsZdbVoJxan8qGwACADAAAAPkBI0ACgAOAFCyDg8QERI5sA4Q -sAnQALAARViwCS8bsQkcPlmwAEVYsAQvG7EEEj5ZsgEJBBESObABL7ECAbAKK1gh2Bv0WbAG0LABELAL -0LINCQQREjkwMQEzFSMRIxEhJwEzASERBwM1r6+6/bgDAkLD/cEBhRoBnZf++gEGcwMU/RAB/C8AAQBO -//ADnwSdACYAj7IgJygREjkAsABFWLAOLxuxDhw+WbAARViwGS8bsRkSPlmyAQ4ZERI5sAEvsr8BAV20 -rwG/AQJxtN8B7wECXbQfAS8BAl20bwF/AQJysA4QsQcBsAorWCHYG/RZsA4QsArQsAEQsSUBsAorWCHY -G/RZshQlARESObAZELAd0LAZELEgAbAKK1gh2Bv0WTAxATMyNjU0JiMiBhUjNDYzMhYVFAYHFhUUBiMi -JjUzFBYzMjY1NCEjAWB6doFscGJ/ueazvNplW9Xpwb3quYNscH/+7HECm2NUU2BbTYy0r5xPiSVA0Zq6 -s5ZPY2JbwwAAAQBOAAADygSdABgAVrIJGRoREjkAsABFWLAQLxuxEBw+WbAARViwAC8bsQASPlmxFwGw -CitYIdgb9FmwAtCyAxAAERI5sBAQsQkBsAorWCHYG/RZsBAQsAzQshYAEBESOTAxISE1ATY2NTQmIyIG -FSM0NjMyFhUUBgcBIQPK/J8Bq2dddF55hbr1w7bWY5v+uAJ+gwGdXotBUmlwa6XOupVRrqH+6QAAAQCY -AAACnQSQAAYAQbIBBwgREjkAsABFWLAFLxuxBRw+WbAARViwAC8bsQASPlmyBAAFERI5sAQvsQMBsAor -WCHYG/RZsgIDBRESOTAxISMRBTUlMwKduv61AesaA69jn6UAAAIAY//wA6sEnQANABgASLIDGRoREjmw -AxCwENAAsABFWLAKLxuxChw+WbAARViwAy8bsQMSPlmwChCxEAGwCitYIdgb9FmwAxCxFgGwCitYIdgb -9FkwMQEUAiMiAic1NBIzMhIXJxAjIhEVFBYzMhEDq9jLydoC2crL1wO66+p6cukB8fj+9wEF9Lb5AQX+ -/u8PAUn+s+GnqAFTAAEARwAAA+AEjQAJAEYAsABFWLAHLxuxBxw+WbAARViwAi8bsQISPlmxAAGwCitY -Idgb9FmyBAACERI5sAcQsQUBsAorWCHYG/RZsgkFBxESOTAxJSEVITUBITUhFQEvArH8ZwKY/XEDeJeX -fAN4mXkAAAEADQAABBwEjQAIADEAsABFWLABLxuxARw+WbAARViwBy8bsQccPlmwAEVYsAQvG7EEEj5Z -sgABBBESOTAxAQEzAREjEQEzAhQBOND+Urn+WNACSgJD/Qr+aQGiAusAAAEAJgAABDEEjQALAFMAsABF -WLABLxuxARw+WbAARViwCi8bsQocPlmwAEVYsAQvG7EEEj5ZsABFWLAHLxuxBxI+WbIAAQQREjmyBgEE -ERI5sgMABhESObIJBgAREjkwMQEBMwEBIwEBIwEBMwIoAR/c/nUBmdz+1f7Y3AGW/nPbAtoBs/2+/bUB -u/5FAksCQgAAAQAxAAAF8QSNABIAYLIOExQREjkAsABFWLADLxuxAxw+WbAARViwCC8bsQgcPlmwAEVY -sBEvG7ERHD5ZsABFWLAKLxuxChI+WbAARViwDy8bsQ8SPlmyAQMKERI5sgYDChESObINAwoREjkwMQEX -NxMzExc3EzMBIwEnBwEjATMBrwsP+KX0DQzGuP7Wrv78AQH+9K3+17cBJlBAA3f8hjtQA2X7cwOVBQX8 -awSNAAABABQAAARTBI0ACAAxALAARViwAy8bsQMcPlmwAEVYsAcvG7EHHD5ZsABFWLAFLxuxBRI+WbIB -AwUREjkwMQEXNwEzASMBMwIaGRoBQMb+N63+N8cBJF5cA2v7cwSNAAABAHT/8AQKBI0AEQA9sgQSExES -OQCwAEVYsAAvG7EAHD5ZsABFWLAILxuxCBw+WbAARViwBC8bsQQSPlmxDQGwCitYIdgb9FkwMQERFAYj -IiYnETMRFBYzMjY1EQQK+tHS9gO3j4WDjwSN/PS229O2AxT89HmBf3sDDAAAAQAoAAAD/QSNAAcALwCw -AEVYsAYvG7EGHD5ZsABFWLACLxuxAhI+WbAGELEAAbAKK1gh2Bv0WbAE0DAxASERIxEhNSED/f5xuf5z -A9UD9PwMA/SZAAABAEP/8APdBJ0AJQBdALAARViwCS8bsQkcPlmwAEVYsBwvG7EcEj5ZsgIcCRESObAJ -ELAN0LAJELEQAbAKK1gh2Bv0WbACELEWAbAKK1gh2Bv0WbAcELAg0LAcELEjAbAKK1gh2Bv0WTAxATQm -JCcmNTQ2MzIWFSM0JiMiBhUUFgQWFhUUBiMiJDUzFBYzMjYDI3n+2lbD87/E+bmNeXGGewE4sFbzx8/+ -77qajH6CASpQWEorYrOPssicYmtZUEFYUGWIW5Opy6JmclsAAAIAigAABCUEjQANABYAY7IVFxgREjmw -FRCwBdAAsABFWLAELxuxBBw+WbAARViwAi8bsQISPlmwAEVYsAwvG7EMEj5Zsg8EAhESObAPL7EAAbAK -K1gh2Bv0WbIKAAQREjmwBBCxFQGwCitYIdgb9FkwMQEhESMRITIWFRQHARUjATMyNjU0JiMjAlr+6bkB -qtXn6wEgxv3k9nWJhn7wAcH+PwSNuqrkWf4eCgJYbV1kbgACAFn/NgRXBJ0AEwAhAE+yCCIjERI5sAgQ -sB7QALAARViwEC8bsRAcPlmwAEVYsAgvG7EIEj5ZsgMIEBESObAQELEXAbAKK1gh2Bv0WbAIELEeAbAK -K1gh2Bv0WTAxARQGBxcHJQYjIgARNTQSNjMyABEnNCYjIgYHFRQWMzI2NQRVcGbYfP75Nkbk/uV/6Jbq -ARW3rJyUrASumJyqAiSm80agb8cNATEBCD6pAQOK/s3++QbG0s+5VcLY08cAAgCKAAAEGwSNAAoAEwBP -sgoUFRESObAKELAM0ACwAEVYsAMvG7EDHD5ZsABFWLABLxuxARI+WbILAwEREjmwCy+xAAGwCitYIdgb -9FmwAxCxEgGwCitYIdgb9FkwMQERIxEhMhYVFAYjJSEyNjU0JichAUO5AdPM8urW/ugBGnyIiHf+4QG2 -/koEjceoqr6YamRgdwEAAgBg//AEWgSdAA0AGwBIsgMcHRESObADELAR0ACwAEVYsAovG7EKHD5ZsABF -WLADLxuxAxI+WbAKELERAbAKK1gh2Bv0WbADELEYAbAKK1gh2Bv0WTAxARAAIyIAETUQADMyABcHNCYj -IgYVFRQWMzI2NQRa/uzo5f7nARfl6QETAresm5avsJecqQIk/vv+0QEyAQc+AQIBNP7Q/wXG0tbFQsPX -08cAAQCKAAAEWASNAAkARQCwAEVYsAUvG7EFHD5ZsABFWLAILxuxCBw+WbAARViwAC8bsQASPlmwAEVY -sAMvG7EDEj5ZsgIFABESObIHBQAREjkwMSEjAREjETMBETMEWLj9o7m5Al24A2z8lASN/JMDbQAAAQCK -AAAFdwSNAA4AYLIBDxAREjkAsABFWLAALxuxABw+WbAARViwAi8bsQIcPlmwAEVYsAQvG7EEEj5ZsABF -WLAILxuxCBI+WbAARViwDC8bsQwSPlmyAQAEERI5sgcABBESObIKAAQREjkwMQkCMxEjERMBIwETESMR -AXoBhwGF8bgT/nKI/nMTuASN/HEDj/tzAZECFfxaA6L97/5vBI0AAQCKAAADiwSNAAUAKQCwAEVYsAQv -G7EEHD5ZsABFWLACLxuxAhI+WbEAAbAKK1gh2Bv0WTAxJSEVIREzAUMCSPz/uZeXBI0AAQCKAAAEVwSN -AAwATACwAEVYsAQvG7EEHD5ZsABFWLAILxuxCBw+WbAARViwAi8bsQISPlmwAEVYsAsvG7ELEj5ZsgAC -CBESObIGAgQREjmyCgIIERI5MDEBBxEjETMRNwEzAQEjAdaTubmCAY3j/iECAeECB47+hwSN/dWQAZv9 -+f16AAABACv/8ANNBI0ADwA2sgUQERESOQCwAEVYsAAvG7EAHD5ZsABFWLAFLxuxBRI+WbAJ0LAFELEM -AbAKK1gh2Bv0WTAxATMRFAYjIiY1MxQWMzI2NQKSu9Sxwtu6cXJcbgSN/MWdxbekXmZtXwABAJcAAAFR -BI0AAwAdALAARViwAi8bsQIcPlmwAEVYsAAvG7EAEj5ZMDEhIxEzAVG6ugSNAAABAIoAAARYBI0ACwBU -ALAARViwBi8bsQYcPlmwAEVYsAovG7EKHD5ZsABFWLAALxuxABI+WbAARViwBC8bsQQSPlmyCQAKERI5 -fLAJLxiyowkBXbECAbAKK1gh2Bv0WTAxISMRIREjETMRIREzBFi5/aS5uQJcuQHy/g4Ejf39AgMAAQBj -//AENQSdAB0AYrIKHh8REjkAsABFWLAKLxuxChw+WbAARViwAy8bsQMSPlmyHQoDERI5sB0vsg0dChES -ObAKELEQAbAKK1gh2Bv0WbADELEXAbAKK1gh2Bv0WbAdELEaA7AKK1gh2Bv0WTAxJQYGIyIAJzUQADMy -FhcjJiMiBhUVFBYzMjc1ITUhBDVC6Zfu/uACAQvyyPIbuCb1n6a5oLZR/ucB0ZZTUwEq/FoBBgEnvLXZ -zsdUvtdK7pAAAQCKAAADmwSNAAkAQwCwAEVYsAQvG7EEHD5ZsABFWLACLxuxAhI+WbAJ0LAJL7IfCQFd -sQABsAorWCHYG/RZsAQQsQYBsAorWCHYG/RZMDEBIREjESEVIREhA0v9+LkDEf2oAggB8/4NBI2Z/pgA -AAEAQ/8TA90FcwArAGkAsABFWLAJLxuxCRw+WbAARViwIi8bsSISPlmyAiIJERI5sAkQsAzQsAkQsBDQ -sAkQsRMBsAorWCHYG/RZsAIQsRkBsAorWCHYG/RZsCIQsB/QsCIQsCbQsCIQsSkBsAorWCHYG/RZMDEB -NCYkJyY1NDY3NTMVFhYVIzQmIyIGFRQWBBYWFRQGBxUjNSYmNTMUFjMyNgMjef7aVsPLppWjxrmNeXGG -ewE4sFbDqZW637qajH6CASpQWEorYrOCrBDZ2xXCiGJrWVBBWFBliFuCphDh4RPClGZyWwABADAAAAPv -BJ0AIABjALAARViwFC8bsRQcPlmwAEVYsAcvG7EHEj5Zsg8HFBESObAPL7EOBLAKK1gh2Bv0WbAB0LAH -ELEEAbAKK1gh2Bv0WbAI0LAUELAY0LAUELEbAbAKK1gh2Bv0WbAPELAf0DAxASEXFgchByE1MzY3Nycj -NTMnJjYzMhYVIzQmIyIGFxchAx3+cAEFOAKUAfyECk8JAQGkoAQGy7W3yrloYF1oBAQBlAH0IstvmJgX -3UYieXvJ7My3cHePinsAAQANAAADkgSNABcAbbIAGBkREjkAsABFWLABLxuxARw+WbAARViwDC8bsQwS -PlmyAAwBERI5sggBDBESObAIL7AD0LADL7AFsAorWNgb3FmwCBCwCrAKK1jYG9xZsA7QsAgQsBDQsAUQ -sBLQsAMQsBTQsAEQsBbQMDEBEzMBMxUhBxUhFSEVIzUhNSE1ITUzATMB0f3E/tTV/vEDARL+7rn+7gES -/u7b/tTHAk0CQP2MeQdEeN3deEt5AnQAAAEAigAAA4UEjQAFADOyAQYHERI5ALAARViwBC8bsQQcPlmw -AEVYsAIvG7ECEj5ZsAQQsQABsAorWCHYG/RZMDEBIREjESEDhf2+uQL7A/T8DASNAAIAFAAABFMEjQAD -AAgAPbIFCQoREjmwBRCwAtAAsABFWLACLxuxAhw+WbAARViwAC8bsQASPlmyBQIAERI5sQcBsAorWCHY -G/RZMDEhIQEzAycHASEEU/vBAcmtPRoZ/vgCQwSN/t1cXv0wAAMAYP/wBFoEnQADABEAHwBhALAARViw -Di8bsQ4cPlmwAEVYsAcvG7EHEj5ZsgIHDhESOXywAi8YtGACcAICcbRgAnACAl2xAQGwCitYIdgb9Fmw -DhCxFQGwCitYIdgb9FmwBxCxHAGwCitYIdgb9FkwMQEhNSEFEAAjIgARNRAAMzIAFwc0JiMiBhUVFBYz -MjY1A1X+HwHhAQX+7Ojl/ucBF+XpARMCt6yblq+wl5ypAfmZbv77/tEBMgEHPgECATT+0P8FxtLWxULD -19PHAAABABQAAARTBI0ACAA4sgcJChESOQCwAEVYsAIvG7ECHD5ZsABFWLAALxuxABI+WbAARViwBC8b -sQQSPlmyBwIAERI5MDEzIwEzASMBJwfbxwHJrQHJxv7AGhkEjftzA2pcXgAAAwA+AAADSwSNAAMABwAL -AGayBAwNERI5sAQQsAHQsAQQsAnQALAARViwCi8bsQocPlmwAEVYsAAvG7EAEj5ZsQIBsAorWCHYG/RZ -sgcKABESObAHL7K/BwFdsQQBsAorWCHYG/RZsAoQsQgBsAorWCHYG/RZMDEhITUhAyE1IRMhNSEDS/zz -Aw1D/XcCiUP88wMNmAF7mAFJmQAAAQCKAAAERASNAAcAQLIBCAkREjkAsABFWLAGLxuxBhw+WbAARViw -AC8bsQASPlmwAEVYsAQvG7EEEj5ZsAYQsQIBsAorWCHYG/RZMDEhIxEhESMRIQREuv25uQO6A/T8DASN -AAEAPwAAA8gEjQAMAEWyBg0OERI5ALAARViwCC8bsQgcPlmwAEVYsAMvG7EDEj5ZsQEBsAorWCHYG/RZ -sAXQsAgQsQoBsAorWCHYG/RZsAfQMDEBASEVITUBATUhFSEBAm/+tgKj/HcBUf6vA1f9jwFKAjr+X5mQ -AbcBtpCZ/l8AAwBgAAAFBgSNABEAFwAeAF4AsABFWLAQLxuxEBw+WbAARViwCC8bsQgSPlmyDxAIERI5 -sA8vsADQsgkIEBESObAJL7AG0LAJELEUAbAKK1gh2Bv0WbAPELEVAbAKK1gh2Bv0WbAb0LAUELAc0DAx -ARYEFRQEBxUjNSYkNTQkNzUzARAFEQYGBTQmJxE2NgMQ5gEQ/u3juer+8wEQ57n+CAE/mqUDNqaYmKYE -Fg36y838DW5uDf3KzPwNdv21/tgRAnIJlpiZlQn9jgqWAAABAGAAAAS2BI0AFQBdsgAWFxESOQCwAEVY -sAMvG7EDHD5ZsABFWLAPLxuxDxw+WbAARViwFC8bsRQcPlmwAEVYsAkvG7EJEj5ZshMDCRESObATL7AA -0LATELELAbAKK1gh2Bv0WbAI0DAxASQRETMRBgIHESMRJgInETMREAURMwLoARW5A/LZutnwBboBFLoB -uzMBawE0/r3z/uIY/t8BHxQBHfIBS/7L/o4tAtQAAAEAdQAABH4EnQAhAF6yByIjERI5ALAARViwGC8b -sRgcPlmwAEVYsA8vG7EPEj5ZsABFWLAgLxuxIBI+WbAPELERAbAKK1gh2Bv0WbAO0LAA0LAYELEHAbAK -K1gh2Bv0WbARELAe0LAf0DAxJTY2NTU0JiMiBhUVFBYXFSE1MyYRNTQAMzIAFRUQBzMVIQK7iH+unZys -jX/+Pq+zARvn6AEcsrX+PZ0f380ms8DBtyHM3yCdl50BOh7uASP+3PUc/suclwABACb/7AUsBI0AGQBu -shYaGxESOQCwAEVYsAIvG7ECHD5ZsABFWLAOLxuxDhI+WbAARViwGC8bsRgSPlmwAhCxAAGwCitYIdgb -9FmwBNCwBdCyCAIOERI5sAgvsA4QsQ8BsAorWCHYG/RZsAgQsRUBsAorWCHYG/RZMDEBITUhFSERNjMy -FhUUBiM1MjY1NCYjIgcRIwGK/pwDif6Ul5zU4uXgjX99gJ2WuQP0mZn+1zHQxL6+l214g3ky/c4AAQBg -//AEMASdAB4AgLIDHyAREjkAsABFWLALLxuxCxw+WbAARViwAy8bsQMSPlmyDwsDERI5sAsQsRIBsAor -WCHYG/RZshYLAxESOXywFi8YsqAWAV20YBZwFgJdsjAWAXG0YBZwFgJxsRcBsAorWCHYG/RZsAMQsRsB -sAorWCHYG/RZsh4DCxESOTAxAQYGIyIAETU0NjYzMhYXIyYmIyIGByEVIRYWMzI2NwQwFPzR4P7xe+eY -zPcTuRKNfpmiBgG//kEEoZGHjRQBebvOAScBA16k+YjTu4J0w6+YssJvgwAAAgAnAAAG+wSNABcAIAB6 -sgQhIhESObAEELAY0ACwAEVYsBIvG7ESHD5ZsABFWLADLxuxAxI+WbAARViwCy8bsQsSPlmwEhCxBQGw -CitYIdgb9FmwCxCxDgGwCitYIdgb9FmyFBIDERI5sBQvsRgBsAorWCHYG/RZsAMQsRkBsAorWCHYG/RZ -MDEBFAYHIREhAw4CByM3NzY2ExMhESEWFiURITI2NTQmIwb75sP+K/5eDwtNl3s7BC5gUQoUAw4BJMHg -/TsBFXKEg3MBbqXHAgP0/mXt9nUBpQEEvgEJAhz+SgTBLf5ZdWNfcAACAIoAAAcJBI0AEgAbAIyyARwd -ERI5sAEQsBPQALAARViwAi8bsQIcPlmwAEVYsBEvG7ERHD5ZsABFWLALLxuxCxI+WbAARViwDy8bsQ8S -PlmyAQILERI5fLABLxiyoAEBXbIEAgsREjmwBC+wARCxDQGwCitYIdgb9FmwBBCxEwGwCitYIdgb9Fmw -CxCxFAGwCitYIdgb9FkwMQEhETMRIRYWFRQGByERIREjETMBESEyNjU0JicBQwJIuQEkweDmw/4r/bi5 -uQMBARVzhH1uAooCA/5KBMGkpccCAfL+DgSN/bL+WXdhW3EDAAEAKAAABS4EjQAVAFyyBxYXERI5ALAA -RViwAi8bsQIcPlmwAEVYsAwvG7EMEj5ZsABFWLAULxuxFBI+WbACELEAAbAKK1gh2Bv0WbAE0LAF0LII -AgwREjmwCC+xEQGwCitYIdgb9FkwMQEhNSEVIRE2MzIWFxEjETQmIyIHESMBi/6dA4n+lJOg1N4Eun1/ -nZa6A/SZmf7XMcrB/o8BZId5Mv3OAAABAIr+mwRDBI0ACwBQsgMMDRESOQCwAi+wAEVYsAYvG7EGHD5Z -sABFWLAKLxuxChw+WbAARViwAC8bsQASPlmwAEVYsAQvG7EEEj5ZsQgBsAorWCHYG/RZsAnQMDEhIREj -ESERMxEhETMEQ/6Buf5/uQJHuf6bAWUEjfwLA/UAAAIAigAABAgEjQAMABUAYbIDFhcREjmwAxCwDdAA -sABFWLALLxuxCxw+WbAARViwCS8bsQkSPlmwCxCxAAGwCitYIdgb9FmyAwsJERI5sAMvsAkQsQ0BsAor -WCHYG/RZsAMQsRMBsAorWCHYG/RZMDEBIREhMhYVFAYHIREhATI2NTQmJyERA5X9rgERzubkxf4rAwv+ -w3OEfW7+3wP3/uDEpaTIAgSN/At3YVtxA/5ZAAACAC7+rATnBI0ADwAVAF2yExYXERI5sBMQsAXQALAJ -L7AARViwBS8bsQUcPlmwAEVYsAsvG7ELEj5ZsQABsAorWCHYG/RZsAfQsAjQsAkQsA3QsAgQsBDQsBHQ -sAUQsRIBsAorWCHYG/RZMDE3NzY2NxMhETMRIxEhESMTISERIQMChSlHRwcOAwePufy6ugEBLgJC/mQM -EZgxVv3YAZn8C/4UAVT+rQHrA1z+yP6ZAAEAHwAABesEjQAVAJKyARYXERI5ALAARViwCS8bsQkcPlmw -AEVYsA0vG7ENHD5ZsABFWLARLxuxERw+WbAARViwAi8bsQISPlmwAEVYsAYvG7EGEj5ZsABFWLAULxux -FBI+WbIQCQIREjl8sBAvGLKgEAFdtGAQcBACXbEAAbAKK1gh2Bv0WbAE0LITEAAREjmwExCwCNCwEBCw -C9AwMQEjESMRIwEjAQEzATMRMxEzATMBASMDxWO6ZP7F6gGG/p7gASxZulkBLOD+nAGI6gH2/goB9v4K -AlECPP4DAf3+AwH9/c39pgABAEf/8APUBJ0AKACAsiQpKhESOQCwAEVYsAovG7EKHD5ZsABFWLAWLxux -FhI+WbAKELEDAbAKK1gh2Bv0WbIGChYREjmyJwoWERI5sCcvtB8nLycCXbK/JwFdtN8n7ycCXbEkAbAK -K1gh2Bv0WbIQJCcREjmyHBYKERI5sBYQsR8BsAorWCHYG/RZMDEBNCYjIgYVIzQ2MzIWFRQGBxYWFRQG -IyImJyY1MxYWMzI2NTQlIzUzNgMIin1ugbrtvNPubmd2cf7VW6k9ebkFg3mIkv7/nZzvA1BUXVhPjrWo -llaNKSSSW560LC5ZnVZgYFjBBZgFAAABAIoAAARhBI0ACQBMsgAKCxESOQCwAEVYsAAvG7EAHD5ZsABF -WLAHLxuxBxw+WbAARViwAi8bsQISPlmwAEVYsAUvG7EFEj5ZsgQAAhESObIJAAIREjkwMQEzESMRASMR -MxEDqLm5/Zu5uQSN+3MDdPyMBI38jAABAIsAAAQsBI0ADABpsgoNDhESOQCwAEVYsAQvG7EEHD5ZsABF -WLAILxuxCBw+WbAARViwAi8bsQISPlmwAEVYsAsvG7ELEj5ZsgYCBBESOXywBi8YsqAGAV20YAZwBgJd -sQEBsAorWCHYG/RZsgoBBhESOTAxASMRIxEzETMBMwEBIwGuarm5ZAGF3/41AevvAfb+CgSN/gMB/f3F -/a4AAQAnAAAENgSNAA8AT7IEEBEREjkAsABFWLAALxuxABw+WbAARViwAS8bsQESPlmwAEVYsAgvG7EI -Ej5ZsAAQsQMBsAorWCHYG/RZsAgQsQoBsAorWCHYG/RZMDEBESMRIQMCAgcjNzc2NjcTBDa5/l4PDaSw -RAQpXlANGQSN+3MD9P6C/qr+5QWlAwee4gJeAAABACL/7AQLBI0AEQBEsgESExESOQCwAEVYsAIvG7EC -HD5ZsABFWLAQLxuxEBw+WbAARViwCC8bsQgSPlmyAQgCERI5sQwBsAorWCHYG/RZMDEBFwEzAQcGBwci -JzcXMjY3ATMB9S0BFNX+XiVQqiZQFAZcMUkg/mbWAjB4AtX8RUmRCwEIkwUxOwOfAAEAiv6sBPEEjQAL -AEayCQwNERI5ALACL7AARViwBi8bsQYcPlmwAEVYsAovG7EKHD5ZsABFWLAELxuxBBI+WbEAAbAKK1gh -2Bv0WbAI0LAJ0DAxJTMDIxEhETMRIREzBEStEqX8ULkCR7qY/hQBVASN/AsD9QABAD0AAAPfBI0AEQBH -sgQSExESOQCwAEVYsAgvG7EIHD5ZsABFWLAQLxuxEBw+WbAARViwAC8bsQASPlmyDQgAERI5sA0vsQQB -sAorWCHYG/RZMDEhIxEGIyImJxEzERQWMzI3ETMD37mQo9TeBLl+f52WuQHCMMrBAXD+nYd5MgIxAAAB -AIoAAAXGBI0ACwBQsgUMDRESOQCwAEVYsAIvG7ECHD5ZsABFWLAGLxuxBhw+WbAARViwCi8bsQocPlmw -AEVYsAAvG7EAEj5ZsQQBsAorWCHYG/RZsAjQsAnQMDEhIREzESERMxEhETMFxvrEuQGIugGIuQSN/AsD -9fwLA/UAAAEAiv6sBnUEjQAPAFmyCxARERI5ALACL7AARViwBi8bsQYcPlmwAEVYsAovG7EKHD5ZsABF -WLAOLxuxDhw+WbAARViwBC8bsQQSPlmxAAGwCitYIdgb9FmwCNCwCdCwDNCwDdAwMSUzAyMRIREzESER -MxEhETMFx64SpvrNuQGIugGIupj+FAFUBI38CwP1/AsD9QACAAgAAATWBI0ADQAWAGGyCBcYERI5sAgQ -sBXQALAARViwBy8bsQccPlmwAEVYsAMvG7EDEj5ZsAcQsQUBsAorWCHYG/RZsgoHAxESObAKL7ADELEO -AbAKK1gh2Bv0WbAKELEUAbAKK1gh2Bv0WTAxARQGByERITUhESEyFhYBMjY1NCYjIREE1uTE/ir+sAIK -ARaEwmj+UXKEg3P+6wFupMgCA/SZ/kpYo/68dWNfcP5ZAP//AIoAAAVnBI0AJgIIAAAABwHjBBYAAAAC -AIoAAAQIBI0ACgATAFKyCBQVERI5sAgQsAvQALAARViwBS8bsQUcPlmwAEVYsAMvG7EDEj5ZsggFAxES -ObAIL7ADELELAbAKK1gh2Bv0WbAIELERAbAKK1gh2Bv0WTAxARQGByERMxEhMhYBMjY1NCYnIREECOTF -/iu5ARHO5v5Qc4R9bv7fAW6kyAIEjf5KxP6Fd2FbcQP+WQABAEv/8AQbBJ0AHgB9sgMfIBESOQCwAEVY -sBMvG7ETHD5ZsABFWLAbLxuxGxI+WbIAGxMREjmxAwGwCitYIdgb9FmyCRMbERI5fLAJLxiyoAkBXbRg -CXAJAl2yMAkBcbRgCXAJAnGxBgGwCitYIdgb9FmwExCxDAGwCitYIdgb9FmyDxMbERI5MDEBFhYzMjY3 -ITUhJiYjIgYHIzY2MzIAFxUUBgYjIiYnAQQUjYeNogf+QQG+BaOYfo0SuRP3zOQBEQV44pXP/hQBeYNv -u7mYr8N0grvT/t/0daP5h867AAIAiv/wBhUEnQATACEAjbIEIiMREjmwBBCwGNAAsABFWLAQLxuxEBw+ -WbAARViwCy8bsQscPlmwAEVYsAMvG7EDEj5ZsABFWLAILxuxCBI+WbINCAsREjl8sA0vGLRgDXANAnGy -oA0BXbRgDXANAl2xBgGwCitYIdgb9FmwEBCxFwGwCitYIdgb9FmwAxCxHgGwCitYIdgb9FkwMQEQACMi -ACcjESMRMxEzNgAzMgAXBzQmIyIGFRUUFjMyNjUGFf7s6N3+6wzYubnYDgEU2ukBEwK3rJuWr7CXnKkC -JP77/tEBHPL+AgSN/gnxARb+0P8FxtLWxULD19PHAAIAUAAAA/wEjQANABQAY7ITFRYREjmwExCwB9AA -sABFWLAHLxuxBxw+WbAARViwAC8bsQASPlmwAEVYsAkvG7EJEj5ZshEHABESObARL7ELAbAKK1gh2Bv0 -WbIBCwcREjmwBxCxEgGwCitYIdgb9FkwMTMBJiY1NDY3IREjESEDExQXIREhIlABInpx3MgB0bn+0P8u -5gEb/u/wAg0mnWihsgL7cwHf/iEDMLQEAXwAAQALAAAD5wSNAA0AUrIBDg8REjkAsABFWLAILxuxCBw+ -WbAARViwAi8bsQISPlmyDQgCERI5sA0vsQABsAorWCHYG/RZsATQsA0QsAbQsAgQsQoBsAorWCHYG/RZ -MDEBIxEjESM1MxEhFSERMwKH4rnh4QL7/b7iAf3+AwH9lwH5mf6gAAABAB/+rAYiBI0AGQCssggaGxES -OQCwAEVYsBAvG7EQHD5ZsABFWLAULxuxFBw+WbAARViwGC8bsRgcPlmwAEVYsA0vG7ENEj5ZsABFWLAK -LxuxChI+WbAARViwBS8bsQUSPlmyFwoYERI5fLAXLxiyoBcBXbRgF3AXAl20YBdwFwJxsQcBsAorWCHY -G/RZsgAHFxESObAFELEBAbAKK1gh2Bv0WbAHELAL0LIPFwcREjmwFxCwEtAwMQEBMxEjESMBIxEjESMB -IwEBMwEzETMRMwEzBGMBJpmnev7EY7pk/sXqAYb+nuABLFm6WQEs4AJa/jz+FgFUAfb+CgH2/goCUQI8 -/gMB/f4DAf0AAQCL/qwETgSNABAAgrIAERIREjkAsAMvsABFWLALLxuxCxw+WbAARViwDy8bsQ8cPlmw -AEVYsAkvG7EJEj5ZsABFWLAFLxuxBRI+WbINCQsREjl8sA0vGLRgDXANAnGyoA0BXbRgDXANAl2xCAGw -CitYIdgb9FmyAAgNERI5sAUQsQEBsAorWCHYG/RZMDEBATMRIxEjASMRIxEzETMBMwJBAW+eqGn+cWq5 -uWQBhd8CUv5E/hYBVAH2/goEjf4DAf0AAAEAiwAABOcEjQAUAHmyCxUWERI5ALAARViwBi8bsQYcPlmw -AEVYsBMvG7ETHD5ZsABFWLAJLxuxCRI+WbAARViwES8bsRESPlmyABETERI5fLAALxiyoAABXbRgAHAA -Al20YABwAAJxsATQsAAQsRABsAorWCHYG/RZsggQABESObAM0DAxATM1MxUzATMBASMBIxUjNSMRIxEz -AURQlDwBhOD+NAHr7/5xQZRQubkCkOTkAf39xf2uAfbOzv4KBI0AAQAjAAAFFQSNAA4Af7IADxAREjkA -sABFWLAGLxuxBhw+WbAARViwCi8bsQocPlmwAEVYsAIvG7ECEj5ZsABFWLANLxuxDRI+WbIIAgYREjl8 -sAgvGLKgCAFdtGAIcAgCXbRgCHAIAnGxAQGwCitYIdgb9FmwBhCxBAGwCitYIdgb9FmyDAEIERI5MDEB -IxEjESE1IREzATMBASMCl2m6/q8CC2MBheD+NAHr7wH2/goD9Zj+AwH9/cX9rgACAGD/6wVbBJ8AIwAu -AJiyFC8wERI5sBQQsCTQALAARViwCy8bsQscPlmwAEVYsBsvG7EbHD5ZsABFWLAALxuxABI+WbAARViw -BC8bsQQSPlmyAgQbERI5sAIvsAsQsQwBsAorWCHYG/RZsAQQsRMBsAorWCHYG/RZsAIQsSYBsAorWCHY -G/RZshUTJhESObIhAiYREjmwGxCxLAGwCitYIdgb9FkwMQUiJwYjIAARNRASMxciBhUVFBYzMjcmAzU0 -EjMyEhUVEAcWMwEQFzYRNTQmIyIDBVvZpomj/ur+xvTSAX6Q0Mc2MuMBz7W4zbZedv2S4bZiasYFFDs8 -AUUBKhoBAwEonsPIIejlCLIBRSfrAQT+//E4/tqyEgH9/sx5gQEeOKyj/sP//wANAAAEHASNACYB0wAA -AQcCJgBE/t4ACACyAAoBXTAxAAEAJv6sBHEEjQAQAGyyCxESERI5ALAHL7AARViwAS8bsQEcPlmwAEVY -sA8vG7EPHD5ZsABFWLAJLxuxCRI+WbAARViwDC8bsQwSPlmyAAEMERI5sgsMARESObIDCwAREjmwCRCx -BAGwCitYIdgb9FmyDgALERI5MDEBATMBATUzESMRIwEBIwEBMwIoAR/c/nUBMaiodP7V/tjcAZb+c9sC -2gGz/b7+SgH+FgFUAbv+RQJLAkIAAQAm/qwF8gSNAA8AXrIJEBEREjkAsAIvsABFWLAILxuxCBw+WbAA -RViwDi8bsQ4cPlmwAEVYsAQvG7EEEj5ZsQABsAorWCHYG/RZsAgQsQYBsAorWCHYG/RZsArQsAvQsAAQ -sAzQsA3QMDElMwMjESERITUhFSERIREzBUSuEqX8UP6bA4n+lQJGupj+FAFUA/SZmfykA/UAAAEAPQAA -A98EjQAXAFCyBBgZERI5ALAARViwCy8bsQscPlmwAEVYsBYvG7EWHD5ZsABFWLAALxuxABI+WbIQCwAR -EjmwEC+xBwGwCitYIdgb9FmwBNCwEBCwE9AwMSEjEQYHFSM1JiYnETMRFBYXNTMVNjcRMwPfuWNplbzJ -A7lnaJVnZbkBwiELxsMKyboBbf6de3gL8O0LIgIxAAEAigAABCwEjQARAEeyBBITERI5ALAARViwAC8b -sQAcPlmwAEVYsAgvG7EIEj5ZsABFWLAQLxuxEBI+WbIEAAgREjmwBC+xDQGwCitYIdgb9FkwMRMzETYz -MhYXESMRNCYjIgcRI4q5mpnU3gS5fn+Ym7kEjf4+McrB/o8BZId5M/3PAAIAAv/wBWsEnQAcACQAbLIV -JSYREjmwFRCwHtAAsABFWLAOLxuxDhw+WbAARViwAC8bsQASPlmyIQ4AERI5sCEvsr8hAV2xEgGwCitY -Idgb9FmwA9CwIRCwCtCwABCxFgGwCitYIdgb9FmwDhCxHQGwCitYIdgb9FkwMQUiADUmJjUzFBYXPgIz -MgARFSEUFjMyNjcXBgYDIgYHITU0JgOR//7OpriZX2YFh+mO+AEQ/K7Bt0yHUDk8uJaPtQYCma4QASLz -C8aoXncMk+yB/uv+/YKxwB8okigvBBHCpBuhqgACAF7/8ARpBJ0AFgAeAGGyCB8gERI5sAgQsBfQALAA -RViwAC8bsQAcPlmwAEVYsAgvG7EIEj5Zsg0ACBESObANL7AAELERAbAKK1gh2Bv0WbAIELEXAbAKK1gh -2Bv0WbANELEaAbAKK1gh2Bv0WTAxATIAFxUUBgYjIgARNSE1NCYjIgcnNjYTMjY3IRUUFgJH9wEpAoTs -k/j+8ANSwbeTkDlBwImRswb9Z60Enf7g74iZ9IkBFQEBggGxwUiSKS/77cahG6CsAAEAR//tA9QEjQAc -AHCyGh0eERI5ALAARViwAi8bsQIcPlmwAEVYsAsvG7ELEj5ZsAIQsQABsAorWCHYG/RZsgQAAhESObIF -CwIREjmwBS+yEQsCERI5sAsQsRQBsAorWCHYG/RZsAUQsRoBsAorWCHYG/RZshwFGhESOTAxASE1IRcB -FhYVFAYjIiYnJjUzFhYzMjY1NCYjIzUCs/28AzgC/qmx0fzXWas8erkFiXOIkoqGgAP0mXb+mxDFi6e+ -LS5anllkaGpfaqUAAAMAYP/wBFoEnQANABQAGwB2sgMcHRESObADELAO0LADELAV0ACwAEVYsAovG7EK -HD5ZsABFWLADLxuxAxI+WbEOAbAKK1gh2Bv0WbIZCgMREjl8sBkvGLKgGQFdtGAZcBkCXbRgGXAZAnGx -EQGwCitYIdgb9FmwChCxFQGwCitYIdgb9FkwMQEQACMiABE1EAAzMgAXATI2NyEWFhMiBgchJiYEWv7s -6OX+5wEX5ekBEwL+BJOoCf12Cq2NkasIAooJqgIk/vv+0QEyAQc+AQIBNP7Q//4cvLSwwAN3w6yzvAAB -ADAAAAPvBJ0AJwCysh0oKRESOQCwAEVYsB0vG7EdHD5ZsABFWLAMLxuxDBI+WbIGHQwREjmwBi+yDwYB -cbIPBgFdsk8GAXGwAdCwAS9ACR8BLwE/AU8BBF2yAAEBXbECBLAKK1gh2Bv0WbAGELEHBLAKK1gh2Bv0 -WbAMELEKAbAKK1gh2Bv0WbAO0LAP0LAHELAR0LAGELAT0LACELAW0LABELAY0LIhAR0REjmwHRCxJAGw -CitYIdgb9FkwMQEhFSEXFSEVIQYHIQchNTM2NyM1MzUnIzUzJyY2MzIWFSM0JiMiBhcBhwGW/m4DAY/+ -bAokApQB/IQKPxSfpQOingIGy7W3yrloYF1oBAKoeV0QeWpHmJgSn3kQXXlAyezMt3B3j4oAAAEAQv/w -A54EnQAhAKKyFCIjERI5ALAARViwFS8bsRUcPlmwAEVYsAgvG7EIEj5ZsiEVCBESObAhL7IPIQFdtBAh -ICECXbEABLAKK1gh2Bv0WbAIELEDAbAKK1gh2Bv0WbAAELAL0LAhELAN0LAhELAS0LASL0AJHxIvEj8S -TxIEXbIAEgFdsQ8EsAorWCHYG/RZsBUQsRoBsAorWCHYG/RZsBIQsBzQsA8QsB7QMDEBIRIhMjcXBiMi -JicjNTM1IzUzNjYzMhcHJiMgAyEVIRUhAy/+aCABAmJoG3Zv0/UUm5eXmxb1z2CHFVl5/wAgAZj+ZAGc -AZb+8RyVHtrMeW15zNwflRz+8HltAAAEAIoAAAetBJ0AAwAQAB4AKACrsh8pKhESObAfELAB0LAfELAE -0LAfELAR0ACwAEVYsCcvG7EnHD5ZsABFWLAlLxuxJRw+WbAARViwBy8bsQccPlmwAEVYsCIvG7EiEj5Z -sABFWLAgLxuxIBI+WbAHELAN0LANL7AC0LACL7QAAhACAl2xAQOwCitYIdgb9FmwDRCxFAOwCitYIdgb -9FmwBxCxGwOwCitYIdgb9FmyIScgERI5siYgJxESOTAxJSE1IQE0NiAWFRUUBiMiJjUXFBYzMjY1NTQm -IyIGFQEjAREjETMBETMHbv3TAi39krwBNL2+l5m/o15XVF5hU1Jh/rW4/aO5uQJduL2OAgOVuribUJi2 -t5wFWWppXFJaaGde/LUDbPyUBI38kwNtAAIAKAAABGYEjQAWAB8AhrIAICEREjmwGNAAsABFWLAMLxux -DBw+WbAARViwAi8bsQISPlmyFgwCERI5sBYvsQABsAorWCHYG/RZsATQsBYQsAbQsBYQsAvQsAsvQAkP -Cx8LLws/CwRdtL8LzwsCXbEIAbAKK1gh2Bv0WbAT0LALELAX0LAMELEeAbAKK1gh2Bv0WTAxJSEVIzUj -NTM1IzUzESEyFhUUBgchFSElITI2NTQmIyECpP7+usDAwMABz8Xq477+3QEC/v4BFXKDhHD+6rS0tJhZ -mAJQzKilywRZ8XhiZHoAAQA+//UCmgMgACYAdACwAEVYsA4vG7EOGD5ZsABFWLAZLxuxGRI+WbIAGQ4R -Ejl8sAAvGLaAAJAAoAADXbAOELEHArAKK1gh2Bv0WbIKAAcREjmwABCxJgKwCitYIdgb9FmyFCYAERI5 -sBkQsSACsAorWCHYG/RZsh0mIBESOTAxATMyNjU0JiMiBhUjNDYzMhYVFAYHFhUUBiMiJjUzFBYzMjY1 -NCcjAQlUSkg/RjlLnaN8iZxGQpWqiISmnk9DRkmcWAHLPTAtOjMpYnt5aDdbGSmPan1+ay08PDNxAgAC -ADYAAAK7AxUACgAOAEoAsABFWLAJLxuxCRg+WbAARViwBC8bsQQSPlmyAQkEERI5sAEvsQICsAorWCHY -G/RZsAbQsAEQsAvQsggLBhESObINCQQREjkwMQEzFSMVIzUhJwEzATMRBwJQa2ud/okGAXmh/oTfEQEr -gqmpZgIG/hYBIRwAAAEAW//1AqcDFQAbAGQAsABFWLABLxuxARg+WbAARViwDS8bsQ0SPlmwARCxBAmw -CitYIdgb9FmyBw0BERI5sAcvsRkCsAorWCHYG/RZsgUHGRESObANELAR0LANELETArAKK1gh2Bv0WbAH -ELAb0DAxExMhFSEHNjMyFhUUBiMiJiczFjMyNjU0JiMiB3AyAd7+oxZBSoCPoIZ5pwabCoFBSE5KSTsB -gwGShKodiXl8kX5lY0tEPk0rAAIAVv/1AqsDHgATAB8AUQCwAEVYsAAvG7EAGD5ZsABFWLAMLxuxDBI+ -WbAAELEBArAKK1gh2Bv0WbIGDAAREjmwBi+xFAKwCitYIdgb9FmwDBCxGwKwCitYIdgb9FkwMQEVIwQH -NjMyFhUUBiMiJjU1NDY3AyIGBxUUFjMyNjQmAigR/vQXSHJ2h5+Ei6fezX4zTRFTPz1ORwMegwLbTZF3 -dJqmlzPQ5AX+biwgIlRVT3xMAAEAOgAAAqUDFQAGADMAsABFWLAFLxuxBRg+WbAARViwAi8bsQISPlmw -BRCxBAKwCitYIdgb9FmyAAUEERI5MDEBASMBITUhAqX+o6YBXf47AmsCu/1FApOCAAMAT//1Ap8DIAAT -AB4AKAB9ALAARViwES8bsREYPlmwAEVYsAYvG7EGEj5ZsiQGERESObAkL7bfJO8k/yQDXbYPJB8kLyQD -XbL/JAFxtA8kHyQCcrEXArAKK1gh2Bv0WbICJBcREjmyDBckERI5sAYQsR0CsAorWCHYG/RZsBEQsR8C -sAorWCHYG/RZMDEBFAcWFRQGICY1NDY3JjU0NjMyFgM0JiMiBhUUFjI2AyIGFRQWMjY0JgKLd4ug/vCg -SkB3l31+l4lOPj9LTH5MjDc/P3A/QAJDdjc7g2p5eWpCYRs3dmd2dv46NDo6NDU6OgHwNTAuODhcNwAC -AEn/+QKVAyAAEgAeAF0AsABFWLAILxuxCBg+WbAARViwDy8bsQ8SPlmyAg8IERI5sAIvtg8CHwIvAgNd -sA8QsRACsAorWCHYG/RZsAIQsRMCsAorWCHYG/RZsAgQsRkCsAorWCHYG/RZMDEBBiMiJjU0NjMyFhcV -EAUHNTI2JzI3NTQmIyIGFRQWAfZFZXaNo4GJnAP+czeWhHteKk88O0xKAUBBin55oKWUPf5kFAF/Yp5H -PFNQVENBTgAAAQCPAosDCwMiAAMAEgCwAi+xAQGwCitYIdgb9FkwMQEhNSEDC/2EAnwCi5cAAAMAngRA -Am4GcgADAA8AGwB0ALAARViwDS8bsQ0aPlmwB9CwBy9ACT8HTwdfB28HBF2wAtCwAi+2PwJPAl8CA12w -ANCwAC9AEQ8AHwAvAD8ATwBfAG8AfwAIXbACELAD0BmwAy8YsA0QsRMHsAorWCHYG/RZsAcQsRkHsAor -WCHYG/RZMDEBMwcjBzQ2MzIWFRQGIyImNxQWMzI2NTQmIyIGAbG93HKCZEhEY2FGSGRVMyQjMDAjJTIG -crjXRmFeSUdcXkUjMjEkJjI0AAEAigAAA64EjQALAFcAsABFWLAGLxuxBhw+WbAARViwBC8bsQQSPlmw -C9CwCy+y3wsBXbIfCwFdsQABsAorWCHYG/RZsAQQsQIBsAorWCHYG/RZsAYQsQgBsAorWCHYG/RZMDEB -IREhFSERIRUhESEDV/3sAmv83AMe/ZsCFAIO/omXBI2Z/rIAAAMAHv5KBBEETgApADcARACUALAARViw -Ji8bsSYaPlmwAEVYsBYvG7EWFD5ZsCYQsCnQsCkvsQADsAorWCHYG/RZsggWJhESObAIL7IOCBYREjmw -Di+0kA6gDgJdsTcBsAorWCHYG/RZshw3DhESObIgCCYREjmwFhCxMAGwCitYIdgb9FmwCBCxOwGwCitY -Idgb9FmwJhCxQgGwCitYIdgb9FkwMQEjFhcVFAYGIyInBhUUFzMWFhUUBgYjIiY1NDY3JjU0NyY1NTQ2 -MzIXIQEGBhUUFjMyNjU0JicjAxQWMzI2NTU0JiIGFQQRlzoBb8N4T0k0erfIzo30l9H/XlQ4c67xu1BH -AW/9PDg8lIOSzWhs73SMaWeKitKKA6dUaRlipl4VKkBQAgGVj1ShYJt6U4oqL0p8UmrFC53KFPv4Gl03 -SllyTEpBAgKlU3t6WBJXeHhaAAIAZP/rBFgETgAQABwAYwCwAEVYsAkvG7EJGj5ZsABFWLAMLxuxDBo+ -WbAARViwAi8bsQISPlmwAEVYsBAvG7EQEj5ZsgACCRESObILCQIREjmwAhCxFAGwCitYIdgb9FmwCRCx -GgGwCitYIdgb9FkwMSUCISICNTUQEjMgEzczAxMjARQWMzITNSYmIyIGA4Js/vLA5OLEAQlsIrBqcbD9 -dZKH00gckmuGlfH++gEb9A8BCAE9/v/t/eL95AH0r8MBhyS+y+MAAgCxAAAE4wWvABYAHgBjshgfIBES -ObAYELAE0ACwAEVYsAMvG7EDHj5ZsABFWLABLxuxARI+WbAARViwDy8bsQ8SPlmyFwMBERI5sBcvsQAB -sAorWCHYG/RZsgkXABESObADELEdAbAKK1gh2Bv0WTAxAREjESEyFhUUBxYTFRYXFSMmJzU0JiMlITI2 -NRAhIQFywQIO8Pvt3gUCQcY7A4x//p4BOaKd/s/+uQJ0/YwFr9LM5WNF/vqcjT0YNqyLeI+dfIQBAAAB -ALIAAAUdBbAADABpALAARViwBC8bsQQePlmwAEVYsAgvG7EIHj5ZsABFWLACLxuxAhI+WbAARViwCy8b -sQsSPlmyBgIEERI5fLAGLxi0YwZzBgJdtDMGQwYCXbKTBgFdsQEBsAorWCHYG/RZsgoBBhESOTAxASMR -IxEzETMBMwEBIwIjscDAlgH97/3UAlXrAo79cgWw/X4Cgv0+/RIAAQCSAAAEFAYAAAwAVACwAEVYsAQv -G7EEID5ZsABFWLAILxuxCBo+WbAARViwAi8bsQISPlmwAEVYsAsvG7ELEj5ZsgcIAhESObAHL7EAAbAK -K1gh2Bv0WbIKAAcREjkwMQEjESMRMxEzATMBASMBzIC6un4BO9v+hgGu2wH1/gsGAPyOAaz+E/2zAAAB -ALIAAAT6BbAACwBMALAARViwAy8bsQMePlmwAEVYsAcvG7EHHj5ZsABFWLABLxuxARI+WbAARViwCi8b -sQoSPlmyAAMBERI5sgUDARESObIJAAUREjkwMQERIxEzETMBMwEBIwFywMAMAmPx/WsCve0Ctf1LBbD9 -eQKH/Tv9FQAAAQCSAAAD8QYYAAwATACwAEVYsAQvG7EEID5ZsABFWLAILxuxCBo+WbAARViwAi8bsQIS -PlmwAEVYsAsvG7ELEj5ZsgAIAhESObIGCAIREjmyCgYAERI5MDEBIxEjETMRMwEzAQEjAVAEuroBAYrw -/isB/+QB8/4NBhj8dQGt/g39uQAAAgCKAAAEHwSNAAoAFABIsgIVFhESObACELAU0ACwAEVYsAEvG7EB -HD5ZsABFWLAALxuxABI+WbABELELAbAKK1gh2Bv0WbAAELEMAbAKK1gh2Bv0WTAxMxEhMhYWFxUUACED -ETMyNjU1NCYjigFpovuMA/7J/vmepLrGvbcEjYX2n038/tYD9Pyj0MBAwM0AAQBg//AEMASdABwATrID -HR4REjkAsABFWLALLxuxCxw+WbAARViwAy8bsQMSPlmwCxCwD9CwCxCxEgGwCitYIdgb9FmwAxCxGQGw -CitYIdgb9FmwAxCwHNAwMQEGBiMiABE1NDY2MzIWFyMmJiMiBgcVFBYzMjY3BDAU/NHg/vF755jM9xO5 -Eo1+macBn5eHjRQBebvOAScBA16k+YjTu4J0y71qvc9vgwADAIoAAAPvBI0ADgAWAB4AawCwAEVYsAEv -G7EBHD5ZsABFWLAALxuxABI+WbIXAAEREjmwFy+yvxcBXbQfFy8XAl203xfvFwJdsQ8BsAorWCHYG/RZ -sggPFxESObAAELEQAbAKK1gh2Bv0WbABELEeAbAKK1gh2Bv0WTAxMxEhMhYVFAYHFhYVFAYHAREhMjY1 -NCMlMzI2NTQnI4oBltHeX1hjdNrJ/vcBBnN66/746mx85e0EjaObUX4hGJVlnq4BAhL+hWJVxI1VU6gF -AAIAEwAABHAEjQAHAAoARwCwAEVYsAQvG7EEHD5ZsABFWLACLxuxAhI+WbAARViwBi8bsQYSPlmyCQQC -ERI5sAkvsQABsAorWCHYG/RZsgoEAhESOTAxASEDIwEzASMBIQMDRv34br0B36YB2Lz9xgGRxwEX/ukE -jftzAa4B/QAAAQCfBI4BlgY7AAgADACwAC+wBNCwBC8wMQEXBgcVIzU0NgErazsDuVQGO1Njb4iCTa0A -AAIAgQTfAuAGigANABEAYACwAy+wB9CwBy9ADQ8HHwcvBz8HTwdfBwZdsAMQsQoEsAorWCHYG/RZsAcQ -sA3QsA0vsAcQsBHQsBEvsA/QsA8vQA8PDx8PLw8/D08PXw9vDwddsBEQsBDQGbAQLxgwMQEUBiMiJjUz -FBYzMjY1JTMXIwLgqIeIqJhPSUdP/qaacGUFsF9ycl83PT812sYAAvykBLz+zAaTABQAGACaALADL7IP -AwFdsv8DAV2ycAMBXbAH0LAHL0ALDwcfBy8HPwdPBwVdsAMQsArQsAovsAcQsQ4DsAorWCHYG/RZsAMQ -sREDsAorWCHYG/RZsA4QsBTQsA4QsBfQsBcvQBk/F08XXxdvF38XjxefF68XvxfPF98X7xcMXbAV0LAV -L0ALDxUfFS8VPxVPFQVdsBcQsBjQGbAYLxgwMQEUBiMiJiYjIgYVJzQ2MzIWMzI2NSczByP+zGBGNXEi -FCMvVGBGL4EsIzCNq7Z4BX1KaUIJMyYVS2tLMyb+4QAAAgBuBOEEWAaVAAYACgBdALADL7IPAwFdsAXQ -sAUvsADQsAAvtg8AHwAvAANdsAMQsALQGbACLxiyBAMAERI5sAbQGbAGLxiwAxCwCdCwCS+wB9CwBy+2 -DwcfBy8HA12wCRCwCtAZsAovGDAxATMBIycHIwEzAyMBkpgBIsWpqsYDIsjJjQXo/vmfnwG0/v0AAv9e -BM8DRgaCAAYACgBdALADL7IPAwFdsATQGbAELxiwANAZsAAvGLADELAB0LABL7AG0LAGL7YPBh8GLwYD -XbICAwYREjmwAxCwCNCwCC+wB9AZsAcvGLAIELAK0LAKL7YPCh8KLwoDXTAxASMnByMBMwUjAzMDRsWq -qsQBIpj+j4zIxwTPnp4BBlUBAgAAAgBpBOQD7AbPAAYAFQBzALADL7AF0LAFL7YPBR8FLwUDXbIEAwUR -EjkZsAQvGLAA0LADELAB0LABL7ICBQMREjmwB9B8sAcvGEANDwcfBy8HPwdPB18HBl2wDtCwDi9ADQ8O -Hw4vDj8OTw5fDgZdsA3QsggHDRESObIUDgcREjkwMQEjJwcjATMXJzY2NTQjNzIWFRQGBwcDRqrFxakB -ELy+AUE7jQWAhko8AQTkuroBBnyDBBohQ1xYSTtCBzwAAgBpBOQDRgbUAAYAGgCHALADL7AB0LABL7AG -0LAGL0AJDwYfBi8GPwYEXbIEAwYREjkZsAQvGLAA0LICBgEREjmwBhCwCtCwCi+0PwpPCgJdsA3QsA0v -QA0PDR8NLw0/DU8NXw0GXbAKELAQ0LAQL7ANELEUBLAKK1gh2Bv0WbAKELEXBLAKK1gh2Bv0WbAUELAa -0DAxASMnByMlMzcUBiMiJiMiBhUnNDYzMhYzMjY1A0aqxcWpAS2Dw2BBNm4oHTZNYEAqfCYfNATknp70 -5T5eRy4dEz9iRi0cAAEAigAAA4UFxAAHADOyAwgJERI5ALAARViwBi8bsQYcPlmwAEVYsAQvG7EEEj5Z -sAYQsQIBsAorWCHYG/RZMDEBMxEhESMRIQLMuf2+uQJCBcT+MPwMBI0AAAIAgQTfAuAGigANABEAYACw -Ay+wB9CwBy9ADQ8HHwcvBz8HTwdfBwZdsAMQsQoEsAorWCHYG/RZsAcQsA3QsA0vsAcQsBDQsBAvsA/Q -sA8vQA8PDx8PLw8/D08PXw9vDwddsBAQsBHQGbARLxgwMQEUBiMiJjUzFBYzMjY1JzMHIwLgqIeIqJhP -SUdPYJmkZgWwX3JyXzc9PzXaxgAAAgCBBOACygcDAA0AHABmALADL7AH0LAHL0ANDwcfBy8HPwdPB18H -Bl2wAxCxCgSwCitYIdgb9FmwBxCwDdCwDS+wBxCwDtCwDi+wFdCwFS9ADw8VHxUvFT8VTxVfFW8VB12w -FNCyDxQOERI5shsOFRESOTAxARQGIyImNTMUFjMyNjUnJzY2NTQjNzIWFRQGBwcCyqGDhKGSSklFTMkB -SkKgB5CUUUQBBbBecnNdNT49NhF8BBgdO1JOQjI7Bz7//wBQAo0CnQW4AwcBxwAAApgAEwCwAEVYsAov -G7EKHj5ZsBDQMDEA//8ANgKYArsFrQMHAiAAAAKYABMAsABFWLAJLxuxCR4+WbAN0DAxAP//AFsCjQKn -Ba0DBwIhAAACmAAQALAARViwAS8bsQEePlkwMf//AFYCjQKrBbYDBwIiAAACmAATALAARViwAC8bsQAe -PlmwFNAwMQD//wA6ApgCpQWtAwcCIwAAApgAEACwAEVYsAUvG7EFHj5ZMDH//wBPAo0CnwW4AwcCJAAA -ApgAGQCwAEVYsBEvG7ERHj5ZsBfQsBEQsB/QMDEA//8ASQKRApUFuAMHAiUAAAKYABMAsABFWLAILxux -CB4+WbAZ0DAxAAABAH7/6wUdBcUAHgBOsgwfIBESOQCwAEVYsAwvG7EMHj5ZsABFWLADLxuxAxI+WbAM -ELAQ0LAMELETAbAKK1gh2Bv0WbADELEbAbAKK1gh2Bv0WbADELAe0DAxAQYAIyIkAic1NBIkMzIAFyMm -JiMiAhEVFBIWMzI2NwUcGP7b7rH+4aIBnQEbsu0BLxnBGL+dwOpuyH2hsBoBzt/+/LQBR8tE0wFKs/76 -46Oo/sv+/jeh/wCQnakAAQB+/+sFHgXEACIAcLIMIyQREjkAsABFWLAMLxuxDB4+WbAARViwAy8bsQMS -PlmyEAMMERI5sBAvsAwQsRMBsAorWCHYG/RZsAMQsRsBsAorWCHYG/RZsiIMAxESObAiL7Q/Ik8iAl20 -DyIfIgJdsR8BsAorWCHYG/RZMDElBgQjIiQCJzU0EiQzMgQXIyYmIyICBwcUEhYzMjY3ESE1IQUeQ/7j -sLv+1qgDmwEctfEBISLAHrqctewKAXjThXK1Kv6wAg++YXK0AUfSLdsBTrbl2pWM/tzyRqz+9ow6MAFG -mwAAAgCyAAAFEQWwAAsAFQBIsgMWFxESObADELAV0ACwAEVYsAEvG7EBHj5ZsABFWLAALxuxABI+WbAB -ELEMAbAKK1gh2Bv0WbAAELENAbAKK1gh2Bv0WTAxMxEhMgQSFxUUAgQHAxEzMgARNTQAI7IBscEBOLEE -rf7Cy+nf6gET/vfoBbCs/sTIPtD+wbECBRL7iwEqAQMk/AEoAAIAfv/rBV8FxQARACIASLIEIyQREjmw -BBCwH9AAsABFWLANLxuxDR4+WbAARViwBC8bsQQSPlmwDRCxFgGwCitYIdgb9FmwBBCxHwGwCitYIdgb -9FkwMQEUAgQjIiQCJzU0EiQzMgQSFwc0AiYjIgYGBxUUEhYzMhI1BV+i/uKvq/7hpgKkASGrrQEgowG/ -bsd9eMZyAXHJecHvAsLO/rC5uQFKyDfNAU+8uf60zAWiAQCPj/6cNaD+/pIBO/8AAAIAfv8EBV8FxQAV -ACYAT7IIJygREjmwCBCwI9AAsABFWLARLxuxER4+WbAARViwCC8bsQgSPlmyAwgRERI5sBEQsRoBsAor -WCHYG/RZsAgQsSMBsAorWCHYG/RZMDEBFAIHFwclBiMiJAInNTQSJDMyBBIVJzQCJiMiBgYHFRQSFjMy -EjUFX6mU+oP+zDk8q/7gpAOiASKsrgEhor9ux314x3EBccl5we8CwtT+rFrDefMMugFGxjrMAVC+u/6w -zgGjAQGPkP+cM6D+/pIBO/8AAAEAoAAAAskEjQAGADMAsABFWLAFLxuxBRw+WbAARViwAC8bsQASPlmy -BAAFERI5sAQvsQMBsAorWCHYG/RZMDEhIxEFNSUzAsm5/pACCh8DpouoygAAAQCDAAAEIASgABgAVrIJ -GRoREjkAsABFWLARLxuxERw+WbAARViwAC8bsQASPlmxFwGwCitYIdgb9FmwAtCyFhcRERI5sgMRFhES -ObARELEJAbAKK1gh2Bv0WbARELAM0DAxISE1ATY3NzQmIyIGFSM0NjYzMhYVFAcBIQQg/IcB/X0KA31m -epW5eNJ+u+HF/oYCeIMByXNUNVRsjnVwv2y4mLG0/qwAAQAP/qMD3gSNABgAUQCwCy+wAEVYsAIvG7EC -HD5ZsQEBsAorWCHYG/RZsATQsgULAhESObAFL7ALELEQAbAKK1gh2Bv0WbAFELEXAbAKK1gh2Bv0WbIY -FwUREjkwMQEhNSEVARYWFRQAIyInNxYzMjY1NCYjIzUC5P10A3L+gLLi/sz/ytI0pbG017nAPAP0mXb+ -bBj2s/n+2meLWMqlq6VnAAACAD7+tgSgBI0ACgAOAEwAsABFWLAJLxuxCRw+WbAARViwAi8bsQISPlmw -AEVYsAYvG7EGEj5ZsQABsAorWCHYG/RZsAYQsAXQsAUvsAAQsAzQsg0JAhESOTAxJTMVIxEjESE1ATMB -IREHA9vFxbr9HQLWx/08Agoclpf+twFJbQQh/AkC/DUAAQBl/qAEBQSMABsAUQCwDS+wAEVYsAEvG7EB -HD5ZsQQBsAorWCHYG/RZsgcNARESObAHL7EYAbAKK1gh2Bv0WbIFBxgREjmwDRCxEgGwCitYIdgb9Fmw -BxCwG9AwMRMTIRUhAzY3NhIVFAAjIic3FjMyNjU0JiMiBgeGZgMU/X42b5XI8f7g8eCvOoLTmb+lh2p1 -IgF0Axir/nRAAgL+9eHv/uJyi2XPpI+2OlMAAQBK/rYD8gSNAAYAJgCwAS+wAEVYsAUvG7EFHD5ZsQMB -sAorWCHYG/RZsgADBRESOTAxAQEjASE1IQPy/aC6Alf9GwOoBCP6kwU/mAAAAgCDBNkC0gbQAA0AIQB+ -ALADL7AH0LAHL0ANDwcfBy8HPwdPB18HBl2wAxCxCgSwCitYIdgb9FmwBxCwDdCwDS+wBxCwEdCwES+w -FNCwFC9ACw8UHxQvFD8UTxQFXbARELAX0LAXL7AUELEbBLAKK1gh2Bv0WbARELEeBLAKK1gh2Bv0WbAb -ELAh0DAxARQGIyImNTMUFjMyNjUTFAYjIiYjIgYVJzQ2MzIWMzI2NQLSoYaHoZZKSEdKjWBGOncsIjBT -YEUwgSwjMAWuX3Z2XzZAQDYBCkppSzMmFUtrSzMmAAEAZ/6ZASEAmQADABIAsAQvsALQsAIvsAHQsAEv -MDEBIxEzASG6uv6ZAgAAAgBg//AGbQSdABMAHQCfshUeHxESObAVELAK0ACwAEVYsAkvG7EJHD5ZsABF -WLALLxuxCxw+WbAARViwAi8bsQISPlmwAEVYsAAvG7EAEj5ZsAsQsQwBsAorWCHYG/RZsAAQsA/QsA8v -sh8PAV2y3w8BXbEQAbAKK1gh2Bv0WbAAELETAbAKK1gh2Bv0WbACELEUAbAKK1gh2Bv0WbAJELEXAbAK -K1gh2Bv0WTAxISEFIgARNRAAMwUhFSERIRUhESEFNxEnIgYVFRQWBm39Y/6O5f7nARflAVsCr/2bAhT9 -7AJs+/Hq7JavsBABMgEHPgECATQQmf6ymP6JDQcDZwnWxULD1wAAAgCC/qkEPwShABgAJQBOALAUL7AA -RViwDC8bsQwcPlmwFBCxAAGwCitYIdgb9FmyBRQMERI5sAUvsgMFDBESObEaAbAKK1gh2Bv0WbAMELEg -AbAKK1gh2Bv0WTAxBTI2NwYjIgI1NDY2MzIAExUUAgQjIic3FhMyNjc1NCYjIgYVFBYB37HcFXe30v91 -0oTrAQUCkv7zr592JnrgaZ8ioZJ/mKO/9NlpARTinOx+/tz+9vrc/rquPI4yAfxcUpTFxcOrlckAAf+2 -/ksBZwCYAAwAKACwDS+wAEVYsAQvG7EEFD5ZsQkBsAorWCHYG/RZsA0QsAzQsAwvMDElFQYGIyInNxYz -MjU1AWcBqpc7NA4eQ4mY9aiwEp0NwukA//8AO/6jBAoEjQEGAkwsAAAQALAARViwAi8bsQIcPlkwMf// -AHP+oAQTBIwBBgJODgAAEACwAEVYsAEvG7EBHD5ZMDH//wAj/rYEhQSNAQYCTeUAABMAsABFWLAGLxux -BhI+WbAM0DAxAP//AHcAAAQUBKABBgJL9AAAEACwAEVYsBEvG7ERHD5ZMDH//wB2/rYEHgSNAQYCTywA -ABAAsABFWLAFLxuxBRw+WTAx//8AN//rBEgEoQEGAmW/AAATALAARViwCC8bsQgcPlmwD9AwMQD//wB+ -/+wEFgWxAQYAGvoAABMAsABFWLAALxuxAB4+WbAV0DAxAP//AF/+qQQcBKEBBgJT3QAAEwCwAEVYsAwv -G7EMHD5ZsCDQMDEA//8AcP/sBA4FxAEGABwAAAAZALAARViwFS8bsRUePlmwG9CwFRCwItAwMQD//wD0 -AAADHQSNAAYCSlQA////tP5LAWUEOgAGAJwAAP///7T+SwFlBDoABgCcAAD//wCbAAABVQQ6AQYAjQAA -ABAAsABFWLACLxuxAho+WTAx////+v5ZAVoEOgAmAI0AAAAGAKTICv//AJsAAAFVBDoABgCNAAAAAQCK -/+wD+QSdACEAZgCwAEVYsBUvG7EVHD5ZsABFWLAQLxuxEBI+WbAARViwHy8bsR8SPlmxAgGwCitYIdgb -9FmyGR8VERI5sBkvtB8ZLxkCXbAIsAorWNgb3FmwGRCwCtCwFRCxDQGwCitYIdgb9FkwMSUWMzI2NTQm -IyM1EyYjIgMRIxE2NjMyFhcBFhYVFAYjIicBw1JYYXKIh1TtTmPTBLgBxclrw2X+7qm217V3aLUze2Ni -VYkBJz7+9f0GAvXS1lVi/rYPo4aszDEAAAIAeP/rBIkEoQALABkAOwCwAEVYsAgvG7EIHD5ZsABFWLAD -LxuxAxI+WbAIELEPAbAKK1gh2Bv0WbADELEWAbAKK1gh2Bv0WTAxARAAIAADNRAAIAATJzQmIyIGBxUU -FjMyNjcEif7o/iL+5gEBGQHeARkBurKdm7ICtpuasQICPP7q/sUBPAEUFAEUAT7+xP7rDcri4MU0yeXd -ygAAAQA7AAAD0gWwAAYAMwCwAEVYsAUvG7EFHj5ZsABFWLABLxuxARI+WbAFELEDAbAKK1gh2Bv0WbIA -AwUREjkwMQEBIwEhNSED0v2+ugJA/SUDlwVI+rgFGJgAAgCM/+wENAYAABAAGwBmshQcHRESObAUELAN -0ACwCS+wAEVYsA0vG7ENGj5ZsABFWLAELxuxBBI+WbAARViwBy8bsQcSPlmyBg0EERI5sgsNBBESObAN -ELEUAbAKK1gh2Bv0WbAEELEZAbAKK1gh2Bv0WTAxARQGBiMiJwcjETMRNjMyEhEnNCYjIgcRFjMyNgQ0 -b8mA0XAPoLlwxcnxuaOMt1BVtIqjAhKf/IuVgQYA/cOL/tP+/we01qr+LKvYAAABAFz/7APvBE4AHQBL -sgAeHxESOQCwAEVYsBAvG7EQGj5ZsABFWLAILxuxCBI+WbEAAbAKK1gh2Bv0WbAIELAD0LAQELAU0LAQ -ELEXAbAKK1gh2Bv0WTAxJTI2NzMOAiMiADU1NDY2MzIWFyMmJiMiBhUVFBYCQGOUCLAFeMRu3/77dtuT -tvEIsAiPaI+bnYN4Wl6oYwEq/CCd+YbarmmHzr8hvMkAAgBb/+wEAAYAABEAHABmshodHhESObAaELAE -0ACwBy+wAEVYsAQvG7EEGj5ZsABFWLANLxuxDRI+WbAARViwCS8bsQkSPlmyBgQNERI5sgsEDRESObAN -ELEVAbAKK1gh2Bv0WbAEELEaAbAKK1gh2Bv0WTAxEzQ2NjMyFxEzESMnBiMiJiYnNxQWMzI3ESYjIgZb -cc6Avm+5oQ5vynzLdQG5qIqvUlOsjacCJp/8jYICNPoAeIyM+5gGsdifAfGZ1gACAFv+VgQABE4AGwAm -AH+yHycoERI5sB8QsAvQALAARViwAy8bsQMaPlmwAEVYsAYvG7EGGj5ZsABFWLALLxuxCxQ+WbAARViw -GC8bsRgSPlmyBQMYERI5sAsQsRIBsAorWCHYG/RZshYDGBESObAYELEfAbAKK1gh2Bv0WbADELEkAbAK -K1gh2Bv0WTAxEzQSMzIXNzMRBgIjIiYnNxYWMzI2NTUGIyICNRcUFjMyNxEmIyIGW/jGzG8PnQL04FbI -SDc/n0+Vim/Bwvq5pouvU1OtjqUCJvYBMpSA/A7v/v03MooqMrCoKIEBOPQHsNmhAeud1wACAFr/7ARE -BE4AEAAcADgAsABFWLAELxuxBBo+WbAARViwDC8bsQwSPlmxFAGwCitYIdgb9FmwBBCxGgGwCitYIdgb -9FkwMRM0NjYzMgAVFRQGBiMiJiYnNxQWMzI2NTQmIyIGWoDjkN0BGn7lko/jgQK5r42OrrGNi68CJ5z/ -jP7M+w6d/IyI+ZoKsN7gxK/g3gAAAgCM/mAEMgROABAAGwBwshkcHRESObAZELAN0ACwAEVYsA0vG7EN -Gj5ZsABFWLAKLxuxCho+WbAARViwBy8bsQcUPlmwAEVYsAQvG7EEEj5ZsgYNBBESObILDQQREjmwDRCx -FAGwCitYIdgb9FmwBBCxGQGwCitYIdgb9FkwMQEUBgYjIicRIxEzFzYzMhIXBzQmIyIHERYzMjYEMm7I -gcVxuZ8PdMrB7gq4qY+oVFOrjKoCEZ78i3399wXafZH+6eonsNuV/fuU3wAAAgBb/mAD/wROAA8AGgBt -shgbHBESObAYELAD0ACwAEVYsAMvG7EDGj5ZsABFWLAGLxuxBho+WbAARViwCC8bsQgUPlmwAEVYsAwv -G7EMEj5ZsgUDDBESObIKAwwREjmxEwGwCitYIdgb9FmwAxCxGAGwCitYIdgb9FkwMRM0EjMyFzczESMR -BiMiAjUXFBYzMjcRJiMiBlv3zMRvDqC5cLrH+rmqjKZWWKKOqgIl9QE0hnL6JgIEeAE19geu35MCEY/f -AAIAXf/sA/METgAUABwAZbIIHR4REjmwCBCwFdAAsABFWLAILxuxCBo+WbAARViwAC8bsQASPlmyGQgA -ERI5sBkvtL8ZzxkCXbEMAbAKK1gh2Bv0WbAAELEQAbAKK1gh2Bv0WbAIELEVAbAKK1gh2Bv0WTAxBSIA -Jyc0NjYzMhIVFSEWFjMyNxcGASIGByE1NCYCceX+3QsBfN2A1ej9JAjCmaB4OYP+7nOYEQIgiRQBF+NO -m/WK/v7wdJ3IWn9yA8qglhmDmgAAAgBg/lYD8gROABoAJQB/siMmJxESObAjELAL0ACwAEVYsAMvG7ED -Gj5ZsABFWLAGLxuxBho+WbAARViwCy8bsQsUPlmwAEVYsBcvG7EXEj5ZsgUDFxESObALELERAbAKK1gh -2Bv0WbIVAxcREjmwFxCxHgGwCitYIdgb9FmwAxCxIwGwCitYIdgb9FkwMRM0EjMyFzczERQGIyImJzcW -MzI2NTUGIyICNRcUFjMyNxEmIyIGYOjDynAQnfXhUq9BN3qPlYlvwL7rupWIr1JVqomWAiX6AS+Tf/wF -6v8tKYpJp546gAEy+gi106AB7pvQAP//AFcAAAKGBbcABgAVrQAAAwBn//AEkQSdAB0AJgAyAJqyLDM0 -ERI5sCwQsA7QsCwQsB/QALAARViwDS8bsQ0cPlmwAEVYsAAvG7EAEj5ZsABFWLAaLxuxGhI+WbIqDRoR -EjmyIQ0aERI5sgcqIRESObITISoREjmwABCxHgGwCitYIdgb9FmyFB4NERI5shYNABESObIcAA0REjmy -GRQcERI5siAeFBESObANELEwAbAKK1gh2Bv0WTAxBSImNTQ2NzcnJjU0NjMyFhUUBwcBNjUzFAcXIycG -JzI3AQcGFRQWAxQXFzc2NTQmIyIGAeir1k5oS0tdrZCGsZtJAQxFqH/H0l6X0ZFq/ttkTGsVPzZCU0hC -OEgQpYFWhks2T2hsc5SWcJBvNP7jdJ3gptJhcZlLATNJO1RJXQMAOkY5MDxNNEVGAAEAAAAAA4sEjQAN -AGGyAA4PERI5ALAARViwCi8bsQocPlmwAEVYsAQvG7EEEj5Zsg0EChESObANL7EAArAKK1gh2Bv0WbAB -0LAEELECAbAKK1gh2Bv0WbABELAG0LAH0LANELAM0LAJ0LAI0DAxAQURIRUhEQc1NxEzESUCTf72Akj8 -/4qKuQEKApFV/luXAgIsfSwCDv4sVQACAAkAAAXxBI0ADwASAIiyBRMUERI5sAUQsBHQALAARViwCi8b -sQocPlmwAEVYsAQvG7EEEj5ZsABFWLAILxuxCBI+WbIPCgQREjmwDy+xAAGwCitYIdgb9FmwBBCxAgGw -CitYIdgb9FmyEQoEERI5sBEvsQYBsAorWCHYG/RZsAoQsQwBsAorWCHYG/RZshIKBBESOTAxASETIRUh -AyEDIwEhFSETIQUhAwWI/jUOAib9Jgv+ZqPGApYDKf3kDAHQ/DsBRBMCFf6AlQEt/tMEjZb+tOcCMgAC -AIoAAAO3BI0ADAAVAFmyFRYXERI5sBUQsAnQALAARViwAC8bsQAcPlmwAEVYsAsvG7ELEj5ZsgIACxES -ObACL7IPAAsREjmwDy+xCQGwCitYIdgb9FmwAhCxDQGwCitYIdgb9FkwMRMzFTMWFhUUBiMjFSMTETMy -NjU0JieKucXE6+rWtLm5toCEiHcEjcsExaapvuwDKv5abGJgdwEAAwBg/8cEWgS2ABUAHgAnAGqyBigp -ERI5sAYQsBvQsAYQsCTQALAARViwES8bsREcPlmwAEVYsAYvG7EGEj5ZshgRBhESObIZEQYREjmwERCx -GwGwCitYIdgb9FmyIREGERI5siIGERESObAGELEkAbAKK1gh2Bv0WTAxARYRFRAAIyInByM3JhE1EAAz -Mhc3MwEUFwEmIyIGFSU0JwEWMzI2NQPWhP7s6Jp0S5V/jwEX5aF7RZX8xT0ByU9ylq8CjDT+O0pqnKkD -/Jn+/z7++/7RR3C+mgEJPwECATROZ/1un2kCqjvWxQOXYv1cNNPHAAACADAAAASzBI0AEwAXAI2yAxgZ -ERI5sAMQsBTQALAARViwDC8bsQwcPlmwAEVYsBAvG7EQHD5ZsABFWLACLxuxAhI+WbAARViwBi8bsQYS -PlmyEwwCERI5sBMvsg8TAV2xAAGwCitYIdgb9FmyFQwCERI5sBUvsQQBsAorWCHYG/RZsAAQsAjQsBMQ -sArQsBMQsA7QsAAQsBbQMDEBIxEjESERIxEjNTM1MxUhNTMVMwEhNSEEs1u5/aS5Wlq5Aly5W/yQAlz9 -pANP/LEB8v4OA0+Xp6enp/6kxQAAAQCK/ksEWASNABMAW7ICFBUREjkAsABFWLAMLxuxDBw+WbAARViw -Dy8bsQ8cPlmwAEVYsAAvG7EAFD5ZsABFWLAKLxuxChI+WbAAELEFAbAKK1gh2Bv0WbIJDAoREjmyDgoM -ERI5MDEBIic3FjMyNTUBESMRMwERMxEUBgMXPDQNI0CI/aS5uQJduKr+SxKdDcNRA2v8lASN/JMDbfsa -qbP//wAlAh8CDQK2AgYAEQAAAAIABwAABOQFsAAPAB0AaQCwAEVYsAUvG7EFHj5ZsABFWLAALxuxABI+ -WbIEAAUREjmwBC+yzwQBXbIvBAFdsp8EAXGxAQGwCitYIdgb9FmwEdCwABCxEgGwCitYIdgb9FmwBRCx -GwGwCitYIdgb9FmwBBCwHNAwMTMRIzUzESEyBBIXFRQCBAcTIxEzMhI3NTQCJyMRM8fAwAGbvgEknwGf -/tnEKfzJ3vcB6dbg/AKalwJ/qP7KyV3O/sqmAgKa/gMBEvld+AETAv4fAAIABwAABOQFsAAPAB0AaQCw -AEVYsAUvG7EFHj5ZsABFWLAALxuxABI+WbIEAAUREjmwBC+yzwQBXbIvBAFdsp8EAXGxAQGwCitYIdgb -9FmwEdCwABCxEgGwCitYIdgb9FmwBRCxGwGwCitYIdgb9FmwBBCwHNAwMTMRIzUzESEyBBIXFRQCBAcT -IxEzMhI3NTQCJyMRM8fAwAGbvgEknwGf/tnEKfzJ3vcB6dbg/AKalwJ/qP7KyV3O/sqmAgKa/gMBEvld -+AETAv4fAAH/4gAAA/0GAAAZAGwAsBcvsABFWLAELxuxBBo+WbAARViwEC8bsRASPlmwAEVYsAgvG7EI -Ej5Zsi8XAV2yDxcBXbIVEBcREjmwFS+xEgGwCitYIdgb9FmwAdCyAhAEERI5sAQQsQwBsAorWCHYG/RZ -sBUQsBjQMDEBIxE2MyATESMRJiYjIgYHESMRIzUzNTMVMwJe+3vFAVcDuQFpb1qIJrnIyLn7BNL+5Zf+ -ff01Asx1cGBO/P0E0peXlwABADEAAASXBbAADwBOALAARViwCi8bsQoePlmwAEVYsAIvG7ECEj5Zsg8K -AhESObAPL7EAAbAKK1gh2Bv0WbAE0LAPELAG0LAKELEIAbAKK1gh2Bv0WbAM0DAxASMRIxEjNTMRITUh -FSERMwOq57/W1v4tBGb+LOcDN/zJAzeXAUSenv68AAH/9P/sAnAFQAAdAHYAsABFWLABLxuxARo+WbAA -RViwES8bsRESPlmwARCwANCwAC+wARCxBAGwCitYIdgb9FmwARCwBdCwBS+yAAUBXbEIAbAKK1gh2Bv0 -WbARELEMAbAKK1gh2Bv0WbAIELAV0LAFELAY0LAEELAZ0LABELAc0DAxAREzFSMVMxUjERQWMzI3FQYj -IiY1ESM1MzUjNTMRAYfKyunpNkEgOElFfH7a2sXFBUD++o+6l/6yQUEMlhSWigFOl7qPAQYA//8AHAAA -BR0HNgImACUAAAEHAEQBMAE2ABQAsABFWLAELxuxBB4+WbEMCPQwMf//ABwAAAUdBzYCJgAlAAABBwB1 -Ab8BNgAUALAARViwBS8bsQUePlmxDQj0MDH//wAcAAAFHQc2AiYAJQAAAQcAngDJATYAFACwAEVYsAQv -G7EEHj5ZsQ8G9DAx//8AHAAABR0HIgImACUAAAEHAKUAxQE6ABQAsABFWLAFLxuxBR4+WbEOBPQwMf// -ABwAAAUdBvsCJgAlAAABBwBqAPkBNgAXALAARViwBC8bsQQePlmxEQT0sBvQMDEA//8AHAAABR0HkQIm -ACUAAAEHAKMBUAFBABcAsABFWLAELxuxBB4+WbEOBvSwGNAwMQD//wAcAAAFHQeUAiYAJQAAAAcCJwFa -ASL//wB3/kQE2AXEAiYAJwAAAAcAeQHS//f//wCpAAAERgdCAiYAKQAAAQcARAD7AUIAFACwAEVYsAYv -G7EGHj5ZsQ0I9DAx//8AqQAABEYHQgImACkAAAEHAHUBigFCABQAsABFWLAGLxuxBh4+WbEOCPQwMf// -AKkAAARGB0ICJgApAAABBwCeAJQBQgAUALAARViwBi8bsQYePlmxEAb0MDH//wCpAAAERgcHAiYAKQAA -AQcAagDEAUIAFwCwAEVYsAYvG7EGHj5ZsRIE9LAb0DAxAP///+AAAAGBB0ICJgAtAAABBwBE/6cBQgAU -ALAARViwAi8bsQIePlmxBQj0MDH//wCwAAACUQdCAiYALQAAAQcAdQA1AUIAFACwAEVYsAMvG7EDHj5Z -sQYI9DAx////6QAAAkYHQgImAC0AAAEHAJ7/QAFCABQAsABFWLACLxuxAh4+WbEIBvQwMf///9UAAAJe -BwcCJgAtAAABBwBq/3ABQgAXALAARViwAi8bsQIePlmxCgT0sBTQMDEA//8AqQAABQgHIgImADIAAAEH -AKUA+wE6ABQAsABFWLAGLxuxBh4+WbENBPQwMf//AHb/7AUJBzgCJgAzAAABBwBEAVIBOAAUALAARViw -DS8bsQ0ePlmxIQj0MDH//wB2/+wFCQc4AiYAMwAAAQcAdQHhATgAFACwAEVYsA0vG7ENHj5ZsSII9DAx -//8Adv/sBQkHOAImADMAAAEHAJ4A6wE4ABQAsABFWLANLxuxDR4+WbEiBvQwMf//AHb/7AUJByQCJgAz -AAABBwClAOcBPAAUALAARViwDS8bsQ0ePlmxIwT0MDH//wB2/+wFCQb9AiYAMwAAAQcAagEbATgAFwCw -AEVYsA0vG7ENHj5ZsScE9LAw0DAxAP//AIz/7ASqBzYCJgA5AAABBwBEASsBNgAUALAARViwCi8bsQoe -PlmxFAj0MDH//wCM/+wEqgc2AiYAOQAAAQcAdQG6ATYAFACwAEVYsBIvG7ESHj5ZsRUI9DAx//8AjP/s -BKoHNgImADkAAAEHAJ4AxAE2ABQAsABFWLAKLxuxCh4+WbEXBvQwMf//AIz/7ASqBvsCJgA5AAABBwBq -APQBNgAXALAARViwCi8bsQoePlmxGQT0sCPQMDEA//8ADwAABLsHNgImAD0AAAEHAHUBiAE2ABQAsABF -WLABLxuxAR4+WbELCPQwMf//AG3/7APqBgACJgBFAAABBwBEANUAAAAUALAARViwFy8bsRcaPlmxKgn0 -MDH//wBt/+wD6gYAAiYARQAAAQcAdQFkAAAAFACwAEVYsBcvG7EXGj5ZsSsJ9DAx//8Abf/sA+oGAAIm -AEUAAAEGAJ5uAAAUALAARViwFy8bsRcaPlmxKwH0MDH//wBt/+wD6gXsAiYARQAAAQYApWoEABQAsABF -WLAXLxuxFxo+WbEsAfQwMf//AG3/7APqBcUCJgBFAAABBwBqAJ4AAAAXALAARViwFy8bsRcaPlmxMAH0 -sDnQMDEA//8Abf/sA+oGWwImAEUAAAEHAKMA9QALABcAsABFWLAXLxuxFxo+WbEsBPSwNtAwMQD//wBt -/+wD6gZfAiYARQAAAAcCJwD//+3//wBc/kQD7AROAiYARwAAAAcAeQE///f//wBd/+wD8wYAAiYASQAA -AQcARADFAAAAFACwAEVYsAgvG7EIGj5ZsR8J9DAx//8AXf/sA/MGAAImAEkAAAEHAHUBVAAAABQAsABF -WLAILxuxCBo+WbEgCfQwMf//AF3/7APzBgACJgBJAAABBgCeXgAAFACwAEVYsAgvG7EIGj5ZsSAB9DAx -//8AXf/sA/MFxQImAEkAAAEHAGoAjgAAABcAsABFWLAILxuxCBo+WbElAfSwLtAwMQD////GAAABZwX/ -AiYAjQAAAQYARI3/ABQAsABFWLACLxuxAho+WbEFCfQwMf//AJYAAAI3Bf8CJgCNAAABBgB1G/8AFACw -AEVYsAMvG7EDGj5ZsQYJ9DAx////zwAAAiwF/wImAI0AAAEHAJ7/Jv//ABQAsABFWLACLxuxAho+WbEI -AfQwMf///7sAAAJEBcQCJgCNAAABBwBq/1b//wAXALAARViwAi8bsQIaPlmxCwH0sBTQMDEA//8AjAAA -A98F7AImAFIAAAEGAKVhBAAUALAARViwAy8bsQMaPlmxFQH0MDH//wBb/+wENAYAAiYAUwAAAQcARADP -AAAAFACwAEVYsAQvG7EEGj5ZsR0J9DAx//8AW//sBDQGAAImAFMAAAEHAHUBXgAAABQAsABFWLAELxux -BBo+WbEeCfQwMf//AFv/7AQ0BgACJgBTAAABBgCeaAAAFACwAEVYsAQvG7EEGj5ZsR4B9DAx//8AW//s -BDQF7AImAFMAAAEGAKVkBAAUALAARViwBC8bsQQaPlmxHwH0MDH//wBb/+wENAXFAiYAUwAAAQcAagCY -AAAAFwCwAEVYsAQvG7EEGj5ZsSMB9LAs0DAxAP//AIj/7APcBgACJgBZAAABBwBEAMcAAAAUALAARViw -By8bsQcaPlmxEgn0MDH//wCI/+wD3AYAAiYAWQAAAQcAdQFWAAAAFACwAEVYsA0vG7ENGj5ZsRMJ9DAx -//8AiP/sA9wGAAImAFkAAAEGAJ5gAAAUALAARViwBy8bsQcaPlmxFQH0MDH//wCI/+wD3AXFAiYAWQAA -AQcAagCQAAAAFwCwAEVYsAcvG7EHGj5ZsRgB9LAh0DAxAP//ABb+SwOwBgACJgBdAAABBwB1ARsAAAAU -ALAARViwAS8bsQEaPlmxEgn0MDH//wAW/ksDsAXFAiYAXQAAAQYAalUAABcAsABFWLAPLxuxDxo+WbEX -AfSwINAwMQD//wAcAAAFHQbjAiYAJQAAAQcAcADHAT4AEwCwAEVYsAQvG7EEHj5ZsAzcMDEA//8Abf/s -A+oFrQImAEUAAAEGAHBsCAATALAARViwFy8bsRcaPlmwKtwwMQD//wAcAAAFHQcOAiYAJQAAAQcAoQD0 -ATcAEwCwAEVYsAQvG7EEHj5ZsA3cMDEA//8Abf/sA+oF2AImAEUAAAEHAKEAmQABABMAsABFWLAXLxux -Fxo+WbAr3DAxAAACABz+TwUdBbAAFgAZAGkAsABFWLAWLxuxFh4+WbAARViwFC8bsRQSPlmwAEVYsAEv -G7EBEj5ZsABFWLAMLxuxDBQ+WbEHA7AKK1gh2Bv0WbABELAR0LARL7IXFBYREjmwFy+xEwGwCitYIdgb -9FmyGRYUERI5MDEBASMHBhUUMzI3FwYjIiY1NDcDIQMjAQMhAwLwAi0mOnFOMDQNRlpZZ6mH/Z6JxgIs -owHv+AWw+lAtW1ZIGnksaFaQbAFz/oQFsPxqAqkAAAIAbf5PA+oETgAtADcAlACwAEVYsBcvG7EXGj5Z -sABFWLAELxuxBBI+WbAARViwHi8bsR4SPlmwAEVYsCkvG7EpFD5ZsB4QsADQsAAvsgIEFxESObILFwQR -EjmwCy+wFxCxDwGwCitYIdgb9FmyEgsXERI5sCkQsSQDsAorWCHYG/RZsAQQsS4BsAorWCHYG/RZsAsQ -sTMBsAorWCHYG/RZMDElJicGIyImNTQkMzM1NCYjIgYVIzQ2NjMyFhcRFBcVIwcGFRQzMjcXBiMiJjU0 -JzI2NzUjIBUUFgMkDweBs6DNAQHptHRxY4a6c8V2u9QEJiE6cU4wNA1GWllniFecI5H+rHQHJkWGtYup -u1Vhc2RHUZdYu6T+DpVYEC1bVkgaeSxoVpDwWkjex1diAP//AHf/7ATYB1cCJgAnAAABBwB1AcYBVwAU -ALAARViwCy8bsQsePlmxHwj0MDH//wBc/+wD7AYAAiYARwAAAQcAdQEzAAAAFACwAEVYsBAvG7EQGj5Z -sSAJ9DAx//8Ad//sBNgHVwImACcAAAEHAJ4A0AFXABQAsABFWLALLxuxCx4+WbEfBvQwMf//AFz/7APs -BgACJgBHAAABBgCePQAAFACwAEVYsBAvG7EQGj5ZsSAB9DAx//8Ad//sBNgHGQImACcAAAEHAKIBrQFX -ABQAsABFWLALLxuxCx4+WbEjBPQwMf//AFz/7APsBcICJgBHAAABBwCiARoAAAAUALAARViwEC8bsRAa -PlmxJAH0MDH//wB3/+wE2AdXAiYAJwAAAQcAnwDlAVgAFACwAEVYsAsvG7ELHj5ZsSEG9DAx//8AXP/s -A+wGAAImAEcAAAEGAJ9SAQAUALAARViwEC8bsRAaPlmxIgH0MDH//wCpAAAExgdCAiYAKAAAAQcAnwCe -AUMAFACwAEVYsAEvG7EBHj5ZsRsG9DAx//8AX//sBSsGAgAmAEgAAAEHAboD1AUTAEgAsvAfAXKyHx8B -XbKfHwFdsh8fAXG0zx/fHwJxst8fAXKyXx8BcrJPHwFxss8fAV20Tx9fHwJdsmAfAV2y4B8BcbLgHwFd -MDH//wCpAAAERgbvAiYAKQAAAQcAcACSAUoAEwCwAEVYsAYvG7EGHj5ZsA3cMDEA//8AXf/sA/MFrQIm -AEkAAAEGAHBcCAATALAARViwCC8bsQgaPlmwH9wwMQD//wCpAAAERgcaAiYAKQAAAQcAoQC/AUMAEwCw -AEVYsAYvG7EGHj5ZsA/cMDEA//8AXf/sA/MF2AImAEkAAAEHAKEAiQABABMAsABFWLAILxuxCBo+WbAh -3DAxAP//AKkAAARGBwQCJgApAAABBwCiAXEBQgAUALAARViwBi8bsQYePlmxEwT0MDH//wBd/+wD8wXC -AiYASQAAAQcAogE7AAAAFACwAEVYsAgvG7EIGj5ZsSUB9DAxAAEAqf5PBEYFsAAbAHoAsABFWLAWLxux -Fh4+WbAARViwFS8bsRUSPlmwAEVYsA8vG7EPFD5ZsABFWLAELxuxBBI+WbIaFRYREjmwGi+xAQGwCitY -Idgb9FmwFRCxAgGwCitYIdgb9FmwDxCxCgOwCitYIdgb9FmwFhCxGQGwCitYIdgb9FkwMQEhESEVIwcG -FRQzMjcXBiMiJjU0NyERIRUhESED4P2JAt1JOnFOMDQNRlpZZ5v9XQOT/S0CdwKh/fydLVtWSBp5LGhW -imkFsJ7+LAAAAgBd/mgD8wROACUALQB+ALAARViwGi8bsRoaPlmwAEVYsA0vG7ENFD5ZsABFWLASLxux -EhI+WbAE0LANELEIA7AKK1gh2Bv0WbIqEhoREjmwKi+0vyrPKgJdsR4BsAorWCHYG/RZsBIQsSIBsAor -WCHYG/RZsiUSGhESObAaELEmAbAKK1gh2Bv0WTAxJQYHMwcGFRQzMjcXBiMiJjU0NyYANTU0NjYzMhIR -FSEWFjMyNjcBIgYHITUmJgPlR3MBOnFOMDQNRlpZZ2La/vV73YHT6v0jBLOKYogz/sJwmBICHgiIvW42 -LVtWSBp5LGhWbFoEASHvIaH9j/7q/v1NoMVQQgKho5MOjZsA//8AqQAABEYHQgImACkAAAEHAJ8AqQFD -ABQAsABFWLAGLxuxBh4+WbERBvQwMf//AF3/7APzBgACJgBJAAABBgCfcwEAFACwAEVYsAgvG7EIGj5Z -sSIB9DAx//8Aev/sBNwHVwImACsAAAEHAJ4AyAFXABQAsABFWLALLxuxCx4+WbEiBvQwMf//AGD+VgPy -BgACJgBLAAABBgCeVQAAFACwAEVYsAMvG7EDGj5ZsScB9DAx//8Aev/sBNwHLwImACsAAAEHAKEA8wFY -ABMAsABFWLALLxuxCx4+WbAi3DAxAP//AGD+VgPyBdgCJgBLAAABBwChAIAAAQATALAARViwAy8bsQMa -PlmwJ9wwMQD//wB6/+wE3AcZAiYAKwAAAQcAogGlAVcAFACwAEVYsAsvG7ELHj5ZsScE9DAx//8AYP5W -A/IFwgImAEsAAAEHAKIBMgAAABQAsABFWLADLxuxAxo+WbEsAfQwMf//AHr99gTcBcQCJgArAAAABwG6 -Adr+l///AGD+VgPyBpMCJgBLAAABBwI0ASsAWAATALAARViwAy8bsQMaPlmwKtwwMQD//wCpAAAFCAdC -AiYALAAAAQcAngDxAUIAFACwAEVYsAcvG7EHHj5ZsRAG9DAx//8AjAAAA98HQQImAEwAAAEHAJ4AHQFB -AAkAsBEvsBTcMDEA////twAAAnoHLgImAC0AAAEHAKX/PAFGABQAsABFWLADLxuxAx4+WbEHBPQwMf// -/50AAAJgBeoCJgCNAAABBwCl/yIAAgAUALAARViwAy8bsQMaPlmxBwH0MDH////MAAACbAbvAiYALQAA -AQcAcP8+AUoAEwCwAEVYsAIvG7ECHj5ZsAXcMDEA////sgAAAlIFqwImAI0AAAEHAHD/JAAGABMAsABF -WLACLxuxAho+WbAF3DAxAP///+wAAAJDBxoCJgAtAAABBwCh/2sBQwATALAARViwAi8bsQIePlmwB9ww -MQD////SAAACKQXXAiYAjQAAAQcAof9RAAAAEwCwAEVYsAIvG7ECGj5ZsAfcMDEA//8AGP5YAXgFsAIm -AC0AAAAGAKTmCf////v+TwFoBcQCJgBNAAAABgCkyQD//wCpAAABhAcEAiYALQAAAQcAogAcAUIAFACw -AEVYsAIvG7ECHj5ZsQsE9DAx//8At//sBfkFsAAmAC0AAAAHAC4CLQAA//8Ajf5LA0oFxAAmAE0AAAAH -AE4B8QAA//8ANf/sBIIHNQImAC4AAAEHAJ4BfAE1ABQAsABFWLAALxuxAB4+WbEUBvQwMf///7T+SwI5 -BdgCJgCcAAABBwCe/zP/2AAUALAARViwDS8bsQ0aPlmxEgT0MDH//wCp/lgFBQWwAiYALwAAAAcBugGU -/vn//wCN/kUEDAYAAiYATwAAAAcBugER/ub//wChAAAEHAcxAiYAMAAAAQcAdQAmATEAFACwAEVYsAUv -G7EFHj5ZsQgI9DAx//8AkwAAAjQHlgImAFAAAAEHAHUAGAGWABQAsABFWLADLxuxAyA+WbEGCfQwMf// -AKn+CQQcBbACJgAwAAAABwG6AWz+qv//AFf+CQFVBgACJgBQAAAABwG6//v+qv//AKkAAAQcBbECJgAw -AAABBwG6AdUEwgAQALAARViwCi8bsQoePlkwMf//AJwAAAKtBgIAJgBQAAABBwG6AVYFEwBQALIfCAFd -sp8IAV20HwgvCAJxsq8IAXG0Lwg/CAJyst8IAXK2XwhvCH8IA3K0zwjfCAJxsk8IAXGyzwgBXbRPCF8I -Al2yYAgBXbLwCAFyMDH//wCpAAAEHAWwAiYAMAAAAAcAogG8/cX//wCcAAACoAYAACYAUAAAAAcAogE4 -/bb//wCpAAAFCAc2AiYAMgAAAQcAdQH1ATYAFACwAEVYsAgvG7EIHj5ZsQwI9DAx//8AjAAAA98GAAIm -AFIAAAEHAHUBWwAAABQAsABFWLADLxuxAxo+WbEUCfQwMf//AKn+CQUIBbACJgAyAAAABwG6AdD+qv// -AIz+CQPfBE4CJgBSAAAABwG6ATP+qv//AKkAAAUIBzYCJgAyAAABBwCfARQBNwAUALAARViwBi8bsQYe -PlmxDwb0MDH//wCMAAAD3wYAAiYAUgAAAQYAn3oBABQAsABFWLADLxuxAxo+WbEWAfQwMf///7wAAAPf -BgQCJgBSAAABBwG6/2AFFQAQALAXL7JPFwFdsp8XAV0wMf//AHb/7AUJBuUCJgAzAAABBwBwAOkBQAAT -ALAARViwDS8bsQ0ePlmwIdwwMQD//wBb/+wENAWtAiYAUwAAAQYAcGYIABMAsABFWLAELxuxBBo+WbAd -3DAxAP//AHb/7AUJBxACJgAzAAABBwChARYBOQATALAARViwDS8bsQ0ePlmwItwwMQD//wBb/+wENAXY -AiYAUwAAAQcAoQCTAAEAEwCwAEVYsAQvG7EEGj5ZsB/cMDEA//8Adv/sBQkHNwImADMAAAEHAKYBawE4 -ABcAsABFWLANLxuxDR4+WbEmCPSwItAwMQD//wBb/+wENAX/AiYAUwAAAQcApgDoAAAAFwCwAEVYsAQv -G7EEGj5ZsSIJ9LAe0DAxAP//AKgAAATJBzYCJgA2AAABBwB1AYABNgAUALAARViwBC8bsQQePlmxGgj0 -MDH//wCMAAAC0gYAAiYAVgAAAQcAdQC2AAAAFACwAEVYsAsvG7ELGj5ZsRAJ9DAx//8AqP4JBMkFsAIm -ADYAAAAHAboBY/6q//8AU/4JApcETgImAFYAAAAHAbr/9/6q//8AqAAABMkHNgImADYAAAEHAJ8AnwE3 -ABQAsABFWLAELxuxBB4+WbEdBvQwMf//AGMAAALNBgACJgBWAAABBgCf1gEAFACwAEVYsAsvG7ELGj5Z -sRIB9DAx//8AUP/sBHIHOAImADcAAAEHAHUBjQE4ABQAsABFWLAGLxuxBh4+WbEpCPQwMf//AF//7AO7 -BgACJgBXAAABBwB1AVEAAAAUALAARViwCS8bsQkaPlmxKQn0MDH//wBQ/+wEcgc4AiYANwAAAQcAngCX -ATgAFACwAEVYsAYvG7EGHj5ZsSkG9DAx//8AX//sA7sGAAImAFcAAAEGAJ5bAAAUALAARViwCS8bsQka -PlmxKQH0MDH//wBQ/k0EcgXEAiYANwAAAAcAeQGfAAD//wBf/kUDuwROAiYAVwAAAAcAeQFd//j//wBQ -/f8EcgXEAiYANwAAAAcBugF1/qD//wBf/fYDuwROAiYAVwAAAAcBugEz/pf//wBQ/+wEcgc4AiYANwAA -AQcAnwCsATkAFACwAEVYsAYvG7EGHj5ZsSsG9DAx//8AX//sA7sGAAImAFcAAAEGAJ9wAQAUALAARViw -CS8bsQkaPlmxKwH0MDH//wAx/f8ElwWwAiYAOAAAAAcBugFm/qD//wAJ/f8CVgVAAiYAWAAAAAcBugDF -/qD//wAx/k0ElwWwAiYAOAAAAAcAeQGQAAD//wAJ/k0CmQVAAiYAWAAAAAcAeQDvAAD//wAxAAAElwc2 -AiYAOAAAAQcAnwChATcAFACwAEVYsAYvG7EGHj5ZsQ0G9DAx//8ACf/sAuwGeQAmAFgAAAEHAboBlQWK -ABIAsg8aAV2ynxoBXbJPGgFdMDH//wCM/+wEqgciAiYAOQAAAQcApQDAAToAFACwAEVYsBIvG7ESHj5Z -sRYE9DAx//8AiP/sA9wF7AImAFkAAAEGAKVcBAAUALAARViwDS8bsQ0aPlmxFAH0MDH//wCM/+wEqgbj -AiYAOQAAAQcAcADCAT4AEwCwAEVYsBIvG7ESHj5ZsBPcMDEA//8AiP/sA9wFrQImAFkAAAEGAHBeCAAT -ALAARViwBy8bsQcaPlmwEtwwMQD//wCM/+wEqgcOAiYAOQAAAQcAoQDvATcAEwCwAEVYsAovG7EKHj5Z -sBbcMDEA//8AiP/sA9wF2AImAFkAAAEHAKEAiwABABMAsABFWLAHLxuxBxo+WbAU3DAxAP//AIz/7ASq -B5ECJgA5AAABBwCjAUsBQQAXALAARViwCi8bsQoePlmxFgb0sCDQMDEA//8AiP/sA9wGWwImAFkAAAEH -AKMA5wALABcAsABFWLAHLxuxBxo+WbEUBPSwHtAwMQD//wCM/+wEqgc1AiYAOQAAAQcApgFEATYAFwCw -AEVYsBIvG7ESHj5ZsRUI9LAZ0DAxAP//AIj/7AQMBf8CJgBZAAABBwCmAOAAAAAXALAARViwDS8bsQ0a -PlmxEwn0sBfQMDEAAAEAjP57BKoFsAAgAFUAsABFWLAYLxuxGB4+WbAARViwDS8bsQ0UPlmwAEVYsBMv -G7ETEj5ZsBgQsCDQsgQTIBESObANELEIA7AKK1gh2Bv0WbATELEcAbAKK1gh2Bv0WTAxAREGBgcGFRQz -MjcXBiMiJjU0NwciACcRMxEUFjMyNjURBKoBioObTjA0DUZaWWdPFu/+5AK+rqGjrQWw/CGU4jtyYEga -eSxoVmFTAQEC4gPg/Caer66eA9sAAQCI/k8D5gQ6AB8AbwCwAEVYsBcvG7EXGj5ZsABFWLAdLxuxHRo+ -WbAARViwHy8bsR8SPlmwAEVYsBIvG7ESEj5ZsABFWLAKLxuxChQ+WbEFA7AKK1gh2Bv0WbAfELAP0LAP -L7IQEh0REjmwEhCxGgGwCitYIdgb9FkwMSEHBhUUMzI3FwYjIiY1NDcnBiMiJicRMxEUMzI3ETMRA9I6 -cU4wNA1GWllnpgRs0a21AbnI1Ea5LVtWSBp5LGhWj2plf8nFAsD9RfaeAxP7xv//AD0AAAbtBzYCJgA7 -AAABBwCeAcUBNgAUALAARViwAy8bsQMePlmxFwb0MDH//wArAAAF0wYAAiYAWwAAAQcAngEkAAAAFACw -AEVYsAwvG7EMGj5ZsQ8B9DAx//8ADwAABLsHNgImAD0AAAEHAJ4AkgE2ABQAsABFWLABLxuxAR4+WbEL -BvQwMf//ABb+SwOwBgACJgBdAAABBgCeJQAAFACwAEVYsA8vG7EPGj5ZsRQB9DAx//8ADwAABLsG+wIm -AD0AAAEHAGoAwgE2ABcAsABFWLAILxuxCB4+WbEQBPSwGdAwMQD//wBWAAAEegc2AiYAPgAAAQcAdQGH -ATYAFACwAEVYsAcvG7EHHj5ZsQwI9DAx//8AWAAAA7MGAAImAF4AAAEHAHUBIQAAABQAsABFWLAHLxux -Bxo+WbEMCfQwMf//AFYAAAR6BvgCJgA+AAABBwCiAW4BNgAUALAARViwBy8bsQcePlmxEQT0MDH//wBY -AAADswXCAiYAXgAAAQcAogEIAAAAFACwAEVYsAcvG7EHGj5ZsREB9DAx//8AVgAABHoHNgImAD4AAAEH -AJ8ApgE3ABQAsABFWLAHLxuxBx4+WbEPBvQwMf//AFgAAAOzBgACJgBeAAABBgCfQAEAFACwAEVYsAcv -G7EHGj5ZsQ8B9DAx////8gAAB1cHQgImAIEAAAEHAHUCyQFCABQAsABFWLAGLxuxBh4+WbEVCPQwMf// -AE7/7AZ8BgECJgCGAAABBwB1AnoAAQAUALAARViwHS8bsR0aPlmxQAn0MDH//wB2/6MFHQeAAiYAgwAA -AQcAdQHpAYAAFACwAEVYsBAvG7EQHj5ZsSwI9DAx//8AW/96BDQGAAImAIkAAAEHAHUBNwAAABQAsABF -WLAELxuxBBo+WbEpCfQwMf///74AAAQfBI0CJgIwAAABBwIm/y//eAAsALIfGAFxtN8Y7xgCcbQfGC8Y -Al2yHxgBcrJPGAFxtO8Y/xgCXbJfGAFdMDH///++AAAEHwSNAiYCMAAAAQcCJv8v/3gANgC07xf/FwJd -sk8XAXGyHxcBcrLfFwFysm8XAXK03xfvFwJxsh8XAXGyXxcBXbQfFy8XAl0wMf//ACgAAAP9BI0CJgHY -AAABBgImReAADQCyAwoBXbKwCgFdMDEA//8AEwAABHAGHgImAjMAAAEHAEQA1QAeABQAsABFWLAELxux -BBw+WbEMBvQwMf//ABMAAARwBh4CJgIzAAABBwB1AWQAHgAUALAARViwBS8bsQUcPlmxDQb0MDH//wAT -AAAEcAYeAiYCMwAAAQYAnm4eABQAsABFWLAELxuxBBw+WbEPBPQwMf//ABMAAARwBgoCJgIzAAABBgCl -aiIAFACwAEVYsAUvG7EFHD5ZsQ4C9DAx//8AEwAABHAF4wImAjMAAAEHAGoAngAeABcAsABFWLAELxux -BBw+WbESAvSwG9AwMQD//wATAAAEcAZ5AiYCMwAAAQcAowD1ACkAFwCwAEVYsAQvG7EEHD5ZsQ4G9LAY -0DAxAP//ABMAAARwBnwCJgIzAAAABwInAP8ACv//AGD+SgQwBJ0CJgIxAAAABwB5AXT//f//AIoAAAOu -Bh4CJgIoAAABBwBEAKgAHgAUALAARViwBi8bsQYcPlmxDQb0MDH//wCKAAADrgYeAiYCKAAAAQcAdQE3 -AB4AFACwAEVYsAcvG7EHHD5ZsQ4G9DAx//8AigAAA64GHgImAigAAAEGAJ5BHgAUALAARViwBi8bsQYc -PlmxEAT0MDH//wCKAAADrgXjAiYCKAAAAQYAanEeABcAsABFWLAGLxuxBhw+WbETAvSwHNAwMQD///++ -AAABXwYeAiYB4wAAAQYARIUeABQAsABFWLACLxuxAhw+WbEFBvQwMf//AI4AAAIvBh4CJgHjAAABBgB1 -Ex4AFACwAEVYsAMvG7EDHD5ZsQYG9DAx////xwAAAiQGHgImAeMAAAEHAJ7/HgAeABQAsABFWLACLxux -Ahw+WbEIBPQwMf///7MAAAI8BeMCJgHjAAABBwBq/04AHgAXALAARViwAi8bsQIcPlmxCwL0sBTQMDEA -//8AigAABFgGCgImAd4AAAEHAKUAlQAiABQAsABFWLAGLxuxBhw+WbENAvQwMf//AGD/8ARaBh4CJgHd -AAABBwBEAO4AHgAUALAARViwCi8bsQocPlmxHQb0MDH//wBg//AEWgYeAiYB3QAAAQcAdQF9AB4AFACw -AEVYsAovG7EKHD5ZsR4G9DAx//8AYP/wBFoGHgImAd0AAAEHAJ4AhwAeABQAsABFWLAKLxuxChw+WbEg -BPQwMf//AGD/8ARaBgoCJgHdAAABBwClAIMAIgAUALAARViwCi8bsQocPlmxHwL0MDH//wBg//AEWgXj -AiYB3QAAAQcAagC3AB4AFwCwAEVYsAovG7EKHD5ZsSMC9LAs0DAxAP//AHT/8AQKBh4CJgHXAAABBwBE -AM8AHgAUALAARViwCS8bsQkcPlmxEwb0MDH//wB0//AECgYeAiYB1wAAAQcAdQFeAB4AFACwAEVYsBEv -G7ERHD5ZsRQG9DAx//8AdP/wBAoGHgImAdcAAAEGAJ5oHgAUALAARViwCS8bsQkcPlmxFgT0MDH//wB0 -//AECgXjAiYB1wAAAQcAagCYAB4AFwCwAEVYsAkvG7EJHD5ZsRkC9LAi0DAxAP//AA0AAAQcBh4CJgHT -AAABBwB1ATMAHgAUALAARViwAS8bsQEcPlmxCwb0MDH//wATAAAEcAXLAiYCMwAAAQYAcGwmABMAsABF -WLAELxuxBBw+WbAM3DAxAP//ABMAAARwBfYCJgIzAAABBwChAJkAHwAUALAARViwBC8bsQQcPlmxDgj0 -MDEAAgAT/k8EcASNABYAGQBpALAARViwAC8bsQAcPlmwAEVYsBQvG7EUEj5ZsABFWLABLxuxARI+WbAA -RViwDC8bsQwUPlmxBwOwCitYIdgb9FmwARCwEdCwES+yFxQAERI5sBcvsRMBsAorWCHYG/RZshkAFBES -OTAxAQEjBwYVFDMyNxcGIyImNTQ3AyEDIwEDIQMCmAHYJjpxTjA0DUZaWWewaP34br0B33gBkccEjftz -LVtWSBp5LGhWlGwBCv7pBI39IQH9AP//AGD/8AQwBh4CJgIxAAABBwB1AWkAHgAUALAARViwCy8bsQsc -PlmxHwb0MDH//wBg//AEMAYeAiYCMQAAAQYAnnMeABQAsABFWLALLxuxCxw+WbEhBPQwMf//AGD/8AQw -BeACJgIxAAABBwCiAVAAHgAUALAARViwCy8bsQscPlmxIwL0MDH//wBg//AEMAYeAiYCMQAAAQcAnwCI -AB8AFACwAEVYsAsvG7ELHD5ZsSEG9DAx//8AigAABB8GHgImAjAAAAEGAJ8xHwAUALAARViwAS8bsQEc -PlmxGgb0MDH//wCKAAADrgXLAiYCKAAAAQYAcD8mABMAsABFWLAGLxuxBhw+WbAN3DAxAP//AIoAAAOu -BfYCJgIoAAABBgChbB8AFACwAEVYsAYvG7EGHD5ZsQ8I9DAx//8AigAAA64F4AImAigAAAEHAKIBHgAe -ABQAsABFWLAGLxuxBhw+WbETAvQwMQABAIr+TwOuBI0AGwB8ALAARViwFi8bsRYcPlmwAEVYsBQvG7EU -Ej5ZsABFWLAPLxuxDxQ+WbAUELAb0LAbL7IfGwFdst8bAV2xAAGwCitYIdgb9FmwFBCxAgGwCitYIdgb -9FmwFBCwBdCwDxCxCgOwCitYIdgb9FmwFhCxGQGwCitYIdgb9FkwMQEhESEVIwcGFRQzMjcXBiMiJjU0 -NyERIRUhESEDV/3sAms9OnFOMDQNRlpZZ5v9ygMe/ZsCFAIO/omXLVtWSBp5LGhWimkEjZn+sgD//wCK -AAADrgYeAiYCKAAAAQYAn1YfABQAsABFWLAGLxuxBhw+WbERBvQwMf//AGP/8AQ1Bh4CJgHlAAABBgCe -cR4AFACwAEVYsAovG7EKHD5ZsSAE9DAx//8AY//wBDUF9gImAeUAAAEHAKEAnAAfABQAsABFWLAKLxux -Chw+WbEgCPQwMf//AGP/8AQ1BeACJgHlAAABBwCiAU4AHgAUALAARViwCi8bsQocPlmxJQL0MDH//wBj -/fwENQSdAiYB5QAAAAcBugFP/p3//wCKAAAEWAYeAiYB5AAAAQcAngCQAB4AFACwAEVYsAcvG7EHHD5Z -sRAE9DAx////lQAAAlgGCgImAeMAAAEHAKX/GgAiABQAsABFWLADLxuxAxw+WbEHAvQwMf///6oAAAJK -BcsCJgHjAAABBwBw/xwAJgATALAARViwAi8bsQIcPlmwBdwwMQD////KAAACIQX2AiYB4wAAAQcAof9J -AB8AFACwAEVYsAIvG7ECHD5ZsQcI9DAx//8ABv5PAWYEjQImAeMAAAAGAKTUAP//AIgAAAFjBeACJgHj -AAABBgCi+x4AFACwAEVYsAIvG7ECHD5ZsQsC9DAx//8AK//wBA0GHgImAeIAAAEHAJ4BBwAeABQAsABF -WLAALxuxABw+WbEUBPQwMf//AIr+BQRXBI0CJgHhAAAABwG6ART+pv//AIIAAAOLBh4CJgHgAAABBgB1 -Bx4AFACwAEVYsAUvG7EFHD5ZsQgG9DAx//8Aiv4HA4sEjQImAeAAAAAHAboBEP6o//8AigAAA4sEjgIm -AeAAAAEHAboBfgOfABAAsABFWLAKLxuxChw+WTAx//8AigAAA4sEjQImAeAAAAAHAKIBZv03//8AigAA -BFgGHgImAd4AAAEHAHUBjwAeABQAsABFWLAILxuxCBw+WbEMBvQwMf//AIr+AwRYBI0CJgHeAAAABwG6 -AWz+pP//AIoAAARYBh4CJgHeAAABBwCfAK4AHwAUALAARViwBi8bsQYcPlmxDwb0MDH//wBg//AEWgXL -AiYB3QAAAQcAcACFACYAEwCwAEVYsAovG7EKHD5ZsB3cMDEA//8AYP/wBFoF9gImAd0AAAEHAKEAsgAf -ABQAsABFWLAKLxuxChw+WbEfCPQwMf//AGD/8ARaBh0CJgHdAAABBwCmAQcAHgAXALAARViwCi8bsQoc -PlmxHgb0sCLQMDEA//8AigAABCUGHgImAdoAAAEHAHUBJwAeABQAsABFWLAFLxuxBRw+WbEZBvQwMf// -AIr+BwQlBI0CJgHaAAAABwG6AQ3+qP//AIoAAAQlBh4CJgHaAAABBgCfRh8AFACwAEVYsAQvG7EEHD5Z -sRwG9DAx//8AQ//wA90GHgImAdkAAAEHAHUBPgAeABQAsABFWLAJLxuxCRw+WbEoBvQwMf//AEP/8APd -Bh4CJgHZAAABBgCeSB4AFACwAEVYsAkvG7EJHD5ZsSoE9DAx//8AQ/5NA90EnQImAdkAAAAHAHkBUwAA -//8AQ//wA90GHgImAdkAAAEGAJ9dHwAUALAARViwCS8bsQkcPlmxKgb0MDH//wAo/gED/QSNAiYB2AAA -AAcBugEU/qL//wAoAAAD/QYeAiYB2AAAAQYAn1AfABQAsABFWLAGLxuxBhw+WbENBvQwMf//ACj+TwP9 -BI0CJgHYAAAABwB5AT4AAv//AHT/8AQKBgoCJgHXAAABBgClZCIAFACwAEVYsBEvG7ERHD5ZsRUC9DAx -//8AdP/wBAoFywImAdcAAAEGAHBmJgATALAARViwCS8bsQkcPlmwE9wwMQD//wB0//AECgX2AiYB1wAA -AQcAoQCTAB8AFACwAEVYsAkvG7EJHD5ZsRUI9DAx//8AdP/wBAoGeQImAdcAAAEHAKMA7wApABcAsABF -WLAJLxuxCRw+WbEVBvSwH9AwMQD//wB0//AEFAYdAiYB1wAAAQcApgDoAB4AFwCwAEVYsBEvG7ERHD5Z -sRQG9LAY0DAxAAABAHT+dAQKBI0AIABVALAARViwGC8bsRgcPlmwAEVYsA4vG7EOFD5ZsABFWLATLxux -ExI+WbAYELAg0LIFEyAREjmwDhCxCQOwCitYIdgb9FmwExCxHAGwCitYIdgb9FkwMQERFAYHBwYVFDMy -NxcGIyImNTQ3IiYnETMRFBYzMjY1EQQKeG8ybE4wNA1GWllnWs35BLePhYOPBI3883q6MChbUkgaeSxo -VmhWzrgDF/z0eYF/ewMMAP//ADEAAAXxBh4CJgHVAAABBwCeATsAHgAUALAARViwAy8bsQMcPlmxFwT0 -MDH//wANAAAEHAYeAiYB0wAAAQYAnj0eABQAsABFWLAILxuxCBw+WbENBPQwMf//AA0AAAQcBeMCJgHT -AAABBgBqbR4AFwCwAEVYsAgvG7EIHD5ZsRAC9LAZ0DAxAP//AEcAAAPgBh4CJgHSAAABBwB1ATMAHgAU -ALAARViwCC8bsQgcPlmxDAb0MDH//wBHAAAD4AXgAiYB0gAAAQcAogEaAB4AFACwAEVYsAcvG7EHHD5Z -sREC9DAx//8ARwAAA+AGHgImAdIAAAEGAJ9SHwAUALAARViwBy8bsQccPlmxDwb0MDH//wAcAAAFHQY/ -AiYAJQAAAAYArgQA////KQAABEYGPwImACkAAAAHAK7+cgAA////NwAABQgGQQImACwAAAAHAK7+gAAC -////PQAAAXcGQAImAC0AAAAHAK7+hgAB////5v/sBR0GPwAmADMUAAAHAK7/LwAA////FAAABR8GPwAm -AD1kAAAHAK7+XQAA////6QAABN8GPwAmALoUAAAHAK7/MgAA////m//0Aq0GdAImAMMAAAEHAK//Kv/s -AB0AsABFWLAMLxuxDBo+WbEYAfSwD9CwGBCwIdAwMQD//wAcAAAFHQWwAgYAJQAA//8AqQAABIgFsAIG -ACYAAP//AKkAAARGBbACBgApAAD//wBWAAAEegWwAgYAPgAA//8AqQAABQgFsAIGACwAAP//ALcAAAF3 -BbACBgAtAAD//wCpAAAFBQWwAgYALwAA//8AqQAABlIFsAIGADEAAP//AKkAAAUIBbACBgAyAAD//wB2 -/+wFCQXEAgYAMwAA//8AqQAABMAFsAIGADQAAP//ADEAAASXBbACBgA4AAD//wAPAAAEuwWwAgYAPQAA -//8AOQAABM4FsAIGADwAAP///9UAAAJeBwcCJgAtAAABBwBq/3ABQgAXALAARViwAi8bsQIePlmxCwT0 -sBTQMDEA//8ADwAABLsG+wImAD0AAAEHAGoAwgE2ABcAsABFWLAILxuxCB4+WbEQBPSwGdAwMQD//wBk -/+sEdwY6AiYAuwAAAQcArgF1//sAFACwAEVYsBMvG7ETGj5ZsSQB9DAx//8AY//sA+wGOQImAL8AAAEH -AK4BK//6ABQAsABFWLAVLxuxFRo+WbEoAfQwMf//AJH+YQPwBjoCJgDBAAABBwCuAUb/+wAUALAARViw -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 -AAD//wAC/lEFawSdAiYCFwAAAAcCUQK8/7j//wCKAAAEYQXLAiYB/gAAAQcAcACcACYAEwCwAEVYsAgv -G7EIHD5ZsAvcMDEA//8AIv/sBAsFywImAgEAAAEGAHA6JgATALAARViwES8bsREcPlmwE9wwMQD//wBg -AAAFBgSNAgYB8QAA//8Al//wBTUEjQAmAeMAAAAHAeIB6AAA//8ACQAABfEGAAImAnMAAAAHAHUCngAA -//8AYP/HBFoGHgImAnUAAAAHAHUBfQAe//8AQ/3/A90EnQImAdkAAAAHAboBKf6g//8AMQAABfEGHgIm -AdUAAAAHAEQBogAe//8AMQAABfEGHgImAdUAAAAHAHUCMQAe//8AMQAABfEF4wImAdUAAAAHAGoBawAe -//8ADQAABBwGHgImAdMAAAAHAEQApAAe//8AHP5PBR0FsAImACUAAAAHAKQBfAAA//8Abf5PA+oETgIm -AEUAAAAHAKQAxAAA//8Aqf5ZBEYFsAImACkAAAAHAKQBOgAK//8AXf5PA/METgImAEkAAAAHAKQBBgAA -//8AE/5PBHAEjQImAjMAAAAHAKQBHgAA//8Aiv5XA64EjQImAigAAAAHAKQA5wAI//8Ahf6sAWAEOgIm -AI0AAAAHAK0DTQAKAAEAAAUOAI8AFgBUAAUAAQAAAAAADgAAAgACJAAGAAEAAABhAGEAYQBhAGEAlAC5 -AToBrgJAAtQC6wMVAz8DcgOYA7cDzgPwBAcEVQSDBNMFSgWOBfAGUQZ+BvMHWwdwB4UHpAfMB+sISgjv -CTUJlQnqCjAKcgqpCxYLYQt8C68MBAwoDHYMsg0IDVQNug4XDoMOrg7wDyAPdQ/KD/oQMxBYEG8QlRC8 -ENcQ9xFxEdASJBKDEuwTPxO6FAAUORSGFN0U+BVkFa8V/hZjFsUXAxdvF8IYCRg5GIcYzhkUGU0Zjhml -GeUaLRphGr4bMRuVG/ccFhy9HOwdlB4EHhAeLh7oHwIfPx+DH9QgUCBwILog5iEGIUIhdCG/Icsh5SH/ -IhkieyLgIx4jmiPvJGAlICWQJeMmVSa1JywniyemJ/YoQSh/KNApLCmxKkwqfSrkK0wrtywYLGwsxiz1 -LVotiC2sLbot5i4GLj8udS66Lu0vKy9IL2Uvbi+hL9Iv7jAKME4wWjCBMK8xLDFZMZ0xzDIJMn4y2DNB -M7c0LjRhNNQ1QjWfNeo2azaZNvM3Yze1OBA4bDjEOQg5Sjm0OhE6eDrwO0Q7uzwXPJI9Cj1+PdM+ED5p -PsI/MT+oP+1AOECAQPJBKEFtQatB9EJNQrFC/kN9RA9Ea0TcRVRFe0XSRkZGwUb6R1JHmkfiSD9Ibkia -SSZJXEmdSdtKIEp4SttLJkuZTCBMfEz1TXdN7k5dTsVPAU9kT8VQLlCyUU5RmlHpUlRSw1M5U6lUNVTA -VVJV7VZwVupXL1d1V+JYSlkFWcFaQVrBWxNbYVuWW7Jb6lwAXBZc6l1dXXhdk139XllezV79Xyhffl/U -X+Bf7F/4YARgW2C+YRNhc2F/YYth1mJAYp9i/2OgZDlkRWRRZKJk5mTyZP5lTmWcZd5mUGbCZxtngGeM -Z5hoEmiKaJZoomiuaLppJGmFaeBp72oDag9qG2ppas1rVWvHbDZsmmz8bWtt1m5gbuNvQG+Tb+ZwOHCv -cLtwx3D2cPZw9nD2cPZw9nD2cPZw9nD2cPZw9nD2cPZw/nEGcRBxGnEycVZxenGdcbhxxHHQcghyR3Kp -cs1y2XLpcwxz33P7dBh0K3Q/dIZ1EHWudj92S3crd494DXiseRB5i3nlelF7A3tqfAB8XnzCfNx89n0Q -fSp9nH3Dffx+GH5NfuB/In+vf/CADoAsgGWAcoCcgL+Ay4E0gYeCFIKDgvaDw4PDhXaF4oYyhl6GqIcG -h32HrogViHmIwIk+iZKJxIoSikuKe4rEixyLTIuKi7WMHIx1jNSNH41zjayN/Y4hjmSOmo61jvaPVo+O -kAKQZ5DGkPCRJpGOkcCSDpJAkoCS55M/k6GUAJRylOiVXpWxlfGWSpailxaXkZfNmB2YZpismOeZKZlp -mbOaDZoZmmea15tVm62b8Jx2nNidOZ2XniyePZ6YnuWfM591n+agSqCwoSGhtaI7otKjRaO1o/ikVaSv -pNylWaW4pc+mNaZ6pyWniaftqD2og6jEqQapTqmjqgqqSqpkqrOrKKtwq7isGKyGrLOtAq1irXatiq2c -rbCtwq3Zre2uSa67rwivaK/Rr/ywULCisOaxPbFksdWx67JvstKy/rMPsyCzM7NEs1WzaLN7s46zpLOs -s7SzvLPNs9iz4LRItJe0xLUltXi12bZUtp63BLdmt8q4Q7hLuOa5M7mfue+6aLrWuye7J7svu5W7+7xa -vJ29A70avTG9SL1fvXi9kb2dvam9wL3Xve6+B74evjW+TL5lvny+k76qvsG+2L7xvwi/H782v0+/Zr99 -v5S/qr/Av9m/8r/+wArAIcA4wE7AZ8B9wJPAqsDDwNnA8MEHwR3BM8FMwWPBesGQwanBwMHYwe/CBcIc -wjPCl8Mvw0bDXcN0w4rDocO4w8/D5cP8xC3ERMRaxHHEiMSfxLbFIMWmxb3F08XqxgDGF8YuxkXGXMZo -xn/Glsaoxr/G1sbtxwTHG8cyxz3HSMdfx2vHd8eOx6XHsce9x9TH68f3yAPIGMhNyFnIZch8yJPIn8ir -yMLI2MjtyQTJGskxyUjJYcl6yZHJqMm0ycDJ18ntygTKG8oyykjKVMpgymzKeMqPyqXKscq9ysnK1crs -ywLLGcsvy0bLXMtzy4rLo8u8y9XL7sxMzLPMyszhzPjNDs0nzT7NVc1szYPNms2wzcfN3s31zgzOL85X -zmrOgc6Yzq7OxM7dzvbPAs8OzyXPPM9Sz2rPgM+Wz63Pxs/dz/TQC9Ai0DnQUtBp0IDQltCv0MbQ3NDz -0VfRbtGE0ZvRstHI0d7R9NIL0nbSjNKi0rnS0NLc0vPTCtMh0zjTQ9NZ03DTfNOS057Ts9O/09bT4tP5 -1BDUJ9RA1FfUY9R51JDUptSy1MjU1NTq1PbVDNUi1TnVUtVr1cjV39X11g3WJNY71lHWXNZo1nTWgNaM -1pjWpNbA1sjW0NbY1uDW6Nbw1vjXANcI1xDXGNcg1yjXMNdJ12LXedeQ16fXvdfY1+DX6Nfw1/jYY9h7 -2JPYqtjB2NjY8dkI2XTZfNmV2Z3Zpdm82dPZ29nj2evZ89oK2hLaGtoi2iraMto62kLaStpS2lracdp5 -2oHa1drd2uXa/tsV2x3bJds+20bbXdtz24rbodu428/b6NwB3BjcL9w33D/cS9xi3GrcgdyY3KTcsNzH -3N7c9d0M3RTdHN013U7dWt1m3XLdft2K3Zbdnt2m3a7dxd3c3eTd+94S3iveRN5M3lTea96C3pveo968 -3tXe7t8H3x/fNt9M32Xfft+X37DfuN/A39nf8uAL4CPgOuBQ4GnggeCa4LPgzODk4QHhHuEm4TLhPuFV -4WzhheGd4bbhzuHn4f/iGOIw4kviZeJ+4pfisOLJ4uLi++MU4y3jSONj42/je+OS46njwOPW4+/kB+Qg -5DjkUeRp5ILkmuS15M/k5uT95QnlFeUh5S3lROVb5XTljOWl5b3l1uXu5gfmH+Y65lTma+aC5pnmsObH -5t7m9ecL5xfnI+cv5zvnUudp54Dnl+eu58Xn3Ofz6AroIOgs6DjoROhQ6GfofuiV6KvowOjM6Njo5Ojw -6PzpCOkU6SDpKOmI6ejqK+pr6s/rLut468jsIex47IDsjOyW7J7spuyu7LbsvuzG7M7s1uzt7QTtG+0y -7UvtZO197Zbtr+3I7eHt+u4T7izuRe5e7mrudu6C7o7umu6r7rfuw+7P7ubu+O8E7xDvHO8o7zTvQO9M -71jveu+R76jvtO/A78zv2O/k7/DwCPAf8DXwQfBN8FnwZfBx8H3wifCV8KHwrfC58MXw0fDd8OXw7fD1 -8P3xBfEN8RXxHfEl8S3xNfE98UXxTfFm8X7xlvGt8bXxvfHW8d7x9fIL8hPyG/Ij8ivyQvJK8lLyWvJi -8mrycvJ68oLzDfNa87nzwfPN8+Tz+vQC9A70GvQm9DL0PvRK9Fb0YvRu9Hr0hvSS9J70qvS2AAAAAQAA -AAIjEutvwDJfDzz1ABkIAAAAAADE8BEuAAAAANUBUvT6G/3VCTAIcwAAAAkAAgAAAAAAAAOMAGQAAAAA -AAAAAAH7AAAB+wAAAg8AoAKPAIgE7QB3BH4AbgXcAGkE+QBlAWUAZwK8AIUCyAAmA3IAHASJAE4BkgAd -AjUAJQIbAJADTAASBH4AcwR+AKoEfgBdBH4AXgR+ADUEfgCaBH4AhAR+AE0EfgBwBH4AZAHwAIYBsQAp -BBEASARkAJgELgCGA8cASwcvAGoFOAAcBPsAqQU1AHcFPwCpBIwAqQRsAKkFcwB6BbQAqQItALcEagA1 -BQQAqQROAKkG/ACpBbQAqQWAAHYFDACpBYAAbQTtAKgEvwBQBMYAMQUwAIwFFwAcBxkAPQUEADkEzgAP -BMoAVgIfAJIDSAAoAh8ACQNYAEADnAAEAnkAOQRaAG0EfQCMBDAAXASDAF8EPQBdAscAPAR9AGAEaACM -AfEAjQHp/78EDgCNAfEAnAcDAIsEagCMBJAAWwR9AIwEjABfArUAjAQgAF8CnQAJBGkAiAPgACEGAwAr -A/cAKQPJABYD9wBYArUAQAHzAK8CtQATBXEAgwHzAIsEYABpBKYAWwW0AGkEMwAPAesAkwToAFoDWABl -BkkAWwOTAJMDwQBmBG4AfwZKAFoDqgCOAv0AggRGAGEC7wBCAu8APgKCAHsEiACaA+kAQwIWAJMB+wB0 -Au8AegOjAHoDwABmBdwAVQY1AFAGOQBvA8kARAd6//IERABZBYAAdgS6AKYEwgCLBsEATgSwAH4EkQBH -BIgAWwScAJUExwBfBZoAHQH6AJsEcwCaBE8AIgIpACIFiwCiBIgAkQehAGgHRABhAfwAoAWHAF0Cuf/k -BX4AZQSSAFsFkACMBPMAiAID/7QENwBiA8QAqQONAI0DqwCOA2oAgQHxAI0CrQB5AioAMgPGAHsC/ABe -AloAfgAA/KcAAP1vAAD8iwAA/V4AAPwnAAD9OAINALcECwBxAhcAkwRzALEFpAAfBXEAZwU+ADIEkQB4 -BbUAsgSRAEUFuwBNBYkAWgVSAHEEhQBkBL0AoAQCAC4EiABgBFAAYwQlAG0EiACRBI4AegKXAMMEbgAl -A+wAZQTEACkEiACRBE0AZQSIAGAELABRBF0AjwWjAFcFmgBfBpcAegShAHkEQv/aBkgASgX/ACoFZAB7 -CJEAMQikALEGggA+BbQAsAULAKIGBAAyB0MAGwS/AFAFtACxBakALwUHAE0GLABTBdkArwV6AJYHhwCw -B8AAsAYSABAG6wCyBQUAowVkAJMHJwC3BRgAWQRsAGEEkgCdA1sAmgTUAC4GIAAVBBAAWASeAJwEUgCc -BKAALAXvAJ0EnQCcBJ4AnAPYACgFzQBkBL0AnARZAGcGeACcBp4AkQT3AB4GNgCdBFgAnQRNAGQGhwCd -BGQALwRo/+gETQBnBskAJwbkAJwEif/9BJ4AnAcIAJwGKwCBBFb/3AcrALcF+ACZBNIAKARGAA8HCwDJ -BgsAvAbRAJMF4QCWCQQAtgfRAJsEIwBQA9sATAVxAGcEiwBbBQoAFgQDAC4FcQBnBIgAWwcBAJwGJAB+ -BwgAnAYrAIEFMgB1BEcAZAT9AHQAAPxnAAD8cQAA/WYAAP2kAAD6GwAA+iwGCQCxBO0AnARW/9wFGwCo -BIkAjARjAKIDkACRBNsAsQQFAJEHogAbBmEAFQWaALIEuACcBQkAowR+AJoGjABEBYMAPgX/AKkE2QCc -B88AqAW0AJEIMQCwBvQAkQXuAHEE0wBtBRgAOQQqACkHLAA0BVwAHwW8AJYElgBnBW8AlgRqAIMFbwCJ -Bi8APwS9/94FCQCjBFoAmgX+AC8E7wAsBbIAsQSIAJEGEgCpBOwAnAdPAKkGPgCdBYcAXQSoAGgEqABp -BLcAOgOrADsFLgA5BEAAKQT2AFcGlABZBuQAZAZWADYFKwAxBEkAUgQHAHkHwQBEBnUAPwf7AKkGoQCQ -BPYAdgQdAGUFrQAjBSAARgVkAJYGAgAvBPIALAMgAG8EFAAACCkAAAQUAAAIKQAAArkAAAIKAAABXAAA -BH8AAAIwAAABogAAAQAAAADRAAAAAAAAAjQAJQI0ACUFQACiBj8AkAOlAA0BmQBgAZkAMAGXACQBmQBP -AtQAaALbADwCwQAkBGkARgSPAFcCsgCKA8QAlAVaAJQBfgBSB6oARAJmAGwCZgBZA6MAOwLvADYDYAB6 -BKYAWwZVAB8GkACnCHYAqAXrAB8GKwCMBH4AXwXaAB8EIgAqBHQAIAVIAF0FTwAfBecAegPOAGgIOgCi -BQEAZwUXAJgGJgBUBtcAZAbPAGMGagBZBI8AagWOAKkErwBFBJIAqATFAD8IOgBiAgz/sASCAGUEZACY -BBEAPgQvAIUECAArAkwAtQKPAG4CAwBcBPMAPARuAB8EiwA8BtQAPAbUADwE7gA8BpsAXwAAAAAIMwBb -CDUAXALvAEIC7wB6Au8AUAQPAFUEDwBgBA8AQgQOAHIEDwCABA8AMAQPAE4EDwBOBA8AmAQPAGMEIwBH -BCsADQRUACYGFQAxBGcAFAR8AHQEJgAoBCAAQwRKAIoEuwBZBFwAigS7AGAE4wCKBgIAigO0AIoEVACK -A88AKwHoAJcE4wCKBKwAYwPLAIoEIABDBDMAMAOhAA0DrwCKBGcAFAS7AGAEZwAUA4kAPgTOAIoD7wA/ -BWcAYAUXAGAE8gB1BXIAJgR8AGAHQQAnB08AigV0ACgEzQCKBFkAigUkAC4GCwAfBD8ARwTsAIoETgCL -BMEAJwQfACIFKACKBGoAPQZRAIoGrACKBR0ACAXxAIoETgCKBHsASwZ2AIoEhwBQBBEACwZHAB8EeQCL -BQkAiwU3ACMFwgBgBF8ADQSoACYGYQAmBGoAPQRqAIoFwwACBMoAXgQ/AEcEuwBgBDMAMAPjAEIIIgCK -BKsAKALvAD4C7wA2Au8AWwLvAFYC7wA6Au8ATwLvAEkDlgCPArUAngPmAIoEOgAeBMMAZAVMALEFJACy -BBMAkgU9ALIEDwCSBIAAigR8AGAEUACKBIUAEwH9AJ8DpACBAAD8pAPvAG4D8/9eBA4AaQP0AGkDrwCK -A58AgQOeAIEC7wBQAu8ANgLvAFsC7wBWAu8AOgLvAE8C7wBJBYEAfgWuAH4FkwCyBeAAfgXjAH4D1QCg -BIIAgwRYAA8EzwA+BGsAZQQuAEoDpACDAZEAZwakAGAEuQCCAfz/tgR/ADsEfwBzBH8AIwR/AHcEfwB2 -BH8ANwR/AH4EfwBfBH8AcAR/APQCBv+0AgT/tAH7AJsB+//6AfsAmwRQAIoFAAB4BCAAOwR9AIwEMgBc -BJMAWwSMAFsEngBaBI0AjAScAFsEPQBdBH0AYAN5AFcE1gBnA7QAAAY5AAkD+ACKBLsAYATjADAE4wCK -AfsAAAI1ACUFXQAHBV0ABwSG/+IExgAxAp3/9AU4ABwFOAAcBTgAHAU4ABwFOAAcBTgAHAU4ABwFNQB3 -BIwAqQSMAKkEjACpBIwAqQIt/+ACLQCwAi3/6QIt/9UFtACpBYAAdgWAAHYFgAB2BYAAdgWAAHYFMACM -BTAAjAUwAIwFMACMBM4ADwRaAG0EWgBtBFoAbQRaAG0EWgBtBFoAbQRaAG0EMABcBD0AXQQ9AF0EPQBd -BD0AXQH6/8YB+gCWAfr/zwH6/7sEagCMBJAAWwSQAFsEkABbBJAAWwSQAFsEaQCIBGkAiARpAIgEaQCI -A8kAFgPJABYFOAAcBFoAbQU4ABwEWgBtBTgAHARaAG0FNQB3BDAAXAU1AHcEMABcBTUAdwQwAFwFNQB3 -BDAAXAU/AKkFGQBfBIwAqQQ9AF0EjACpBD0AXQSMAKkEPQBdBIwAqQQ9AF0EjACpBD0AXQVzAHoEfQBg -BXMAegR9AGAFcwB6BH0AYAVzAHoEfQBgBbQAqQRoAIwCLf+3Afr/nQIt/8wB+v+yAi3/7AH6/9ICLQAY -AfH/+wItAKkGlwC3A9oAjQRqADUCA/+0BQQAqQQOAI0ETgChAfEAkwROAKkB8QBXBE4AqQKHAJwETgCp -As0AnAW0AKkEagCMBbQAqQRqAIwFtACpBGoAjARq/7wFgAB2BJAAWwWAAHYEkABbBYAAdgSQAFsE7QCo -ArUAjATtAKgCtQBTBO0AqAK1AGMEvwBQBCAAXwS/AFAEIABfBL8AUAQgAF8EvwBQBCAAXwS/AFAEIABf -BMYAMQKdAAkExgAxAp0ACQTGADECxQAJBTAAjARpAIgFMACMBGkAiAUwAIwEaQCIBTAAjARpAIgFMACM -BGkAiAUwAIwEaQCIBxkAPQYDACsEzgAPA8kAFgTOAA8EygBWA/cAWATKAFYD9wBYBMoAVgP3AFgHev/y -BsEATgWAAHYEiABbBID/vgSA/74EJgAoBIUAEwSFABMEhQATBIUAEwSFABMEhQATBIUAEwR8AGAD5gCK -A+YAigPmAIoD5gCKAej/vgHoAI4B6P/HAej/swTjAIoEuwBgBLsAYAS7AGAEuwBgBLsAYAR8AHQEfAB0 -BHwAdAR8AHQEKwANBIUAEwSFABMEhQATBHwAYAR8AGAEfABgBHwAYASAAIoD5gCKA+YAigPmAIoD5gCK -A+YAigSsAGMErABjBKwAYwSsAGME4wCKAej/lQHo/6oB6P/KAegABgHoAIgDzwArBFQAigO0AIIDtACK -A7QAigO0AIoE4wCKBOMAigTjAIoEuwBgBLsAYAS7AGAESgCKBEoAigRKAIoEIABDBCAAQwQgAEMEIABD -BCYAKAQmACgEJgAoBHwAdAR8AHQEfAB0BHwAdAR8AHQEfAB0BhUAMQQrAA0EKwANBCMARwQjAEcEIwBH -BTgAHASM/ykFtP83Ai3/PQWU/+YFMv8UBWb/6QKX/5sFOAAcBPsAqQSMAKkEygBWBbQAqQItALcFBACp -BvwAqQW0AKkFgAB2BQwAqQTGADEEzgAPBQQAOQIt/9UEzgAPBIUAZARQAGMEiACRApcAwwRdAI8EcwCa -BJAAWwSIAJoD4AAhA/cAKQKX/+UEXQCPBJAAWwRdAI8GlwB6BIwAqQRzALEEvwBQAi0AtwIt/9UEagA1 -BSQAsgUEAKkFBwBNBTgAHAT7AKkEcwCxBIwAqQW0ALEG/ACpBbQAqQWAAHYFtQCyBQwAqQU1AHcExgAx -BQQAOQRaAG0EPQBdBJ4AnASQAFsEfQCMBDAAXAPJABYD9wApBD0AXQNbAJoEIABfAfEAjQH6/7sB6f+/ -BFIAnAPJABYHGQA9BgMAKwcZAD0GAwArBxkAPQYDACsEzgAPA8kAFgFlAGcCjwCIBB4AoAID/7QBmQAw -BvwAqQcDAIsFOAAcBFoAbQSMAKkFtACxBD0AXQSeAJwFiQBaBZoAXwUKABYEA//7CFkAWwlJAHYEvwBQ -BBAAWAU1AHcEMABcBM4ADwQCAC4CLQC3B0MAGwYgABUCLQC3BTgAHARaAG0FOAAcBFoAbQd6//IGwQBO -BIwAqQQ9AF0FhwBdBDcAYgQ3AGIHQwAbBiAAFQS/AFAEEABYBbQAsQSeAJwFtACxBJ4AnAWAAHYEkABb -BXEAZwSLAFsFcQBnBIsAWwVkAJMETQBkBQcATQPJABYFBwBNA8kAFgUHAE0DyQAWBXoAlgRZAGcG6wCy -BjYAnQSDAF8FOAAcBFoAbQU4ABwEWgBtBTgAHARaAG0FOAAcBFr/ygU4ABwEWgBtBTgAHARaAG0FOAAc -BFoAbQU4ABwEWgBtBTgAHARaAG0FOAAcBFoAbQU4ABwEWgBtBTgAHARaAG0EjACpBD0AXQSMAKkEPQBd -BIwAqQQ9AF0EjACpBD0AXQSM//AEPf+6BIwAqQQ9AF0EjACpBD0AXQSMAKkEPQBdAi0AtwH6AJsCLQCj -AfEAhQWAAHYEkABbBYAAdgSQAFsFgAB2BJAAWwWAAEcEkP/EBYAAdgSQAFsFgAB2BJAAWwWAAHYEkABb -BX4AZQSSAFsFfgBlBJIAWwV+AGUEkgBbBX4AZQSSAFsFfgBlBJIAWwUwAIwEaQCIBTAAjARpAIgFkACM -BPMAiAWQAIwE8wCIBZAAjATzAIgFkACMBPMAiAWQAIwE8wCIBM4ADwPJABYEzgAPA8kAFgTOAA8DyQAW -BKEAXwTGADED2AAoBXoAlgRZAGcEcwCxA1sAmgYvAD8Evf/eBGgAjAUF/9QFBf/UBHMAAwNb//wFOP/3 -BCf/vwTOAA8EAgAuBQQAOQP3ACkEUABjBGwAEgY/AJAEfgBdBH4AXgR+ADUEfgCaBJIAmASmAIQEkgBk -BKYAhwVzAHoEfQBgBbQAqQRqAIwFOAAcBFoAOQSMAF8EPQApAi3/CgH6/vAFgAB2BJAAMwTtAFUCtf+L -BTAAjARpACsEpv7WBPsAqQR9AIwFPwCpBIMAXwU/AKkEgwBfBbQAqQRoAIwFBACpBA4AjQUEAKkEDgCN -BE4AqQHxAIYG/ACpBwMAiwW0AKkEagCMBYAAdgUMAKkEfQCMBO0AqAK1AIIEvwBQBCAAXwTGADECnQAJ -BTAAjAUXABwD4AAhBRcAHAPgACEHGQA9BgMAKwTKAFYD9wBYBcb+MgSFABMEIv9jBR//gAIk/4QExf/V -BGf/GwT8/+4EhQATBFAAigPmAIoEIwBHBOMAigHoAJcEVACKBgIAigTjAIoEuwBgBFwAigQmACgEKwAN -BFQAJgHo/7MEKwANA+YAigOvAIoEIABDAegAlwHo/7MDzwArBFQAigQfACIEhQATBFAAigOvAIoD5gCK -BOwAigYCAIoE4wCKBLsAYATOAIoEXACKBHwAYAQmACgEVAAmBD8ARwTjAIoEfABgBCsADQXDAAIE7ACK -BB8AIgVnAGAFtwCXBjkACQS7AGAEIABDBhUAMQYVADEGFQAxBCsADQU4ABwEWgBtBIwAqQQ9AF0EhQAT -A+YAigH6AIUAAQAAB2z+DAAACUn6G/5KCTAAAQAAAAAAAAAAAAAAAAAABQ4AAwSGAZAABQAABZoFMwAA -AR8FmgUzAAAD0QBmAgAAAAIAAAAAAAAAAADgAAL/UAAgWwAAACAAAAAAR09PRwBAAAD//QYA/gAAZgea -AgAgAAGfAAAAAAQ6BbAAIAAgAAMAAAABAAAFEAkKBAAAAgICAwYFBwYCAwMEBQICAgQFBQUFBQUFBQUF -AgIFBQUECAYGBgYFBQYGAgUGBQgGBgYGBgUFBgYIBgUFAgQCBAQDBQUFBQUDBQUCAgUCCAUFBQUDBQMF -BAcEBAQDAgMGAgUFBgUCBgQHBAQFBwQDBQMDAwUEAgIDBAQHBwcECAUGBQUIBQUFBQUGAgUFAgYFCQgC -BgMGBQYGAgUEBAQEAgMCBAMDAAAAAAAAAgUCBQYGBgUGBQYGBgUFBQUFBQUFAwUEBQUFBQUFBgYHBQUH -BwYKCgcGBgcIBQYGBgcHBggJBwgGBggGBQUEBQcFBQUFBwUFBAcFBQcHBgcFBQcFBQUICAUFCAcFCAcF -BQgHCAcKCQUEBgUGBQYFCAcIBwYFBgAAAAAAAAcGBQYFBQQFBQkHBgUGBQcGBwUJBgkIBwUGBQgGBgUG -BQYHBQYFBwYGBQcGCAcGBQUFBAYFBgcIBwYFBQkHCQcGBQYGBgcGBAUJBQkDAgIFAgIBAQACAgYHBAIC -AgIDAwMFBQMEBgIJAwMEAwQFBwcKBwcFBwUFBgYHBAkGBgcICAcFBgUFBQkCBQUFBQUDAwIGBQUICAYH -AAkJAwMDBQUFBQUFBQUFBQUFBQcFBQUFBQUFBQYHBAUEAgYFBAUFBAQFBQUEBQQGBgYGBQgIBgUFBgcF -BgUFBQYFBwgGBwUFBwUFBwUGBgYFBQcFBQYFBQUFBAkFAwMDAwMDAwQDBAUFBgYFBgUFBQUFAgQABAQF -BAQEBAMDAwMDAwMGBgYHBwQFBQUFBQQCBwUCBQUFBQUFBQUFBQICAgICBQYFBQUFBQUFBQUFBAUEBwQF -BgYCAgYGBQUDBgYGBgYGBgYFBQUFAgICAgYGBgYGBgYGBgYFBQUFBQUFBQUFBQUFAgICAgUFBQUFBQUF -BQUEBAYFBgUGBQYFBgUGBQYFBgYFBQUFBQUFBQUFBgUGBQYFBgUGBQICAgICAgICAgcEBQIGBQUCBQIF -AwUDBgUGBQYFBQYFBgUGBQYDBgMGAwUFBQUFBQUFBQUFAwUDBQMGBQYFBgUGBQYFBgUIBwUEBQUEBQQF -BAgIBgUFBQUFBQUFBQUFBQQEBAQCAgICBgUFBQUFBQUFBQUFBQUFBQUFBQQEBAQEBQUFBQYCAgICAgQF -BAQEBAYGBgUFBQUFBQUFBQUFBQUFBQUFBQUHBQUFBQUGBQYCBgYGAwYGBQUGAgYIBgYGBQUGAgUFBQUD -BQUFBQQEAwUFBQcFBQUCAgUGBgYGBgUFBggGBgYGBgUGBQUFBQUFBAQFBAUCAgIFBAgHCAcIBwUEAgMF -AgIICAYFBQYFBQYGBgUJCgUFBgUFBQIIBwIGBQYFCAgFBQYFBQgHBQUGBQYFBgUGBQYFBgUGBAYEBgQG -BQgHBQYFBgUGBQYFBgUGBQYFBgUGBQYFBgUGBQUFBQUFBQUFBQUFBQUFBQUCAgICBgUGBQYFBgUGBQYF -BgUGBQYFBgUGBQYFBgUGBQYGBgYGBgYGBgYFBAUEBQQFBQQGBQUEBwUFBgYFBAYFBQUGBAUFBwUFBQUF -BQUFBgUGBQYFBQUCAgYFBgMGBQUGBQYFBgUGBQYFBgUFAggIBgUGBgUGAwUFBQMGBgQGBAgHBQQHBQUG -AgUFBgUFBAUGAgUHBgUFBQUFAgUEBAUCAgQFBQUFBAQGBwYFBQUFBQUFBgUFBgYFBgYHBQUHBwcFBgUF -BQUEAgAAAAMAAAADAAAAHAADAAEAAAAcAAMACgAABooABAZuAAAA9ACAAAYAdAAAAAIADQB+AKAArACt -AL8AxgDPAOYA7wD+AQ8BEQElAScBMAFTAV8BZwF+AX8BjwGSAaEBsAHwAf8CGwI3AlkCvALHAskC3QLz -AwEDAwMJAw8DIwOKA4wDkgOhA7ADuQPJA84D0gPWBCUELwRFBE8EYgRvBHkEhgSfBKkEsQS6BM4E1wTh -BPUFAQUQBRMeAR4/HoUe8R7zHvkfTSAJIAsgESAVIB4gIiAnIDAgMyA6IDwgRCB0IH8gpCCqIKwgsSC6 -IL0hBSETIRYhIiEmIS4hXiICIgYiDyISIhoiHiIrIkgiYCJlJcruAvbD+wT+///9//8AAAAAAAIADQAg -AKAAoQCtAK4AwADHANAA5wDwAP8BEAESASYBKAExAVQBYAFoAX8BjwGSAaABrwHwAfoCGAI3AlkCvALG -AskC2ALzAwADAwMJAw8DIwOEA4wDjgOTA6MDsQO6A8oD0QPWBAAEJgQwBEYEUARjBHAEegSIBKAEqgSy -BLsEzwTYBOIE9gUCBREeAB4+HoAeoB7yHvQfTSAAIAogECATIBcgICAlIDAgMiA5IDwgRCB0IH8goyCm -IKsgsSC5ILwhBSETIRYhIiEmIS4hWyICIgYiDyIRIhoiHiIrIkgiYCJkJcruAfbD+wH+///8//8AAQAA -//b/5AHY/8IBzP/BAAABvwAAAboAAAG2AAABtAAAAbIAAAGqAAABrP8W/wf/Bf74/usB7gAAAAD+Zf5E -ASP92P3X/cn9tP2o/af9ov2d/YoAAP/+//0AAAAA/QoAAP/e/P78+wAA/LoAAPyyAAD8pwAA/KEAAPyZ -AAD8kQAA/ygAAP8lAAD8XgAA5eLlouVT5X7k5+V85X3hcuFz4W8AAOFs4WvhaeFh46nhWeOh4VDhIeEX -AADg8gAA4O3g5uDl4J7gkeCP4ITflOB54E3fqt6s357fnd+W35Pfh99r31TfUdvtE7cK9wa7AsMBxwAB -AAAAAAAAAAAAAAAAAAAAAADkAAAA7gAAARgAAAEyAAABMgAAATIAAAF0AAAAAAAAAAAAAAAAAAABdAF+ -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWwAAAAAAXQBkAAAAagAAAAAAAABwAAAAggAAAIwAAACUgAA -AmIAAAKOAAACmgAAAr4AAALOAAAC4gAAAAAAAAAAAAAAAAAAAAAAAAAAAtIAAAAAAAAAAAAAAAAAAAAA -AAAAAALCAAACwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAJ/AoACgQKCAoMChACBAnsCjwKQApECkgKTApQAggCDApUClgKXApgCmQCEAIUCmgKbApwCnQKe -Ap8AhgCHAqoCqwKsAq0CrgKvAIgAiQKwArECsgKzArQAigJ6AIsAjAJ8AI0C4wLkAuUC5gLnAugAjgLp -AuoC6wLsAu0C7gLvAvAAjwCQAvEC8gLzAvQC9QL2AvcAkQCSAvgC+QL6AvsC/AL9AJMAlAMMAw0DEAMR -AxIDEwJ9An4ChQKgAysDLAMtAy4DCgMLAw4DDwCuAK8DhgCwA4cDiAOJALEAsgOQA5EDkgCzA5MDlAC0 -A5UDlgC1A5cAtgOYALcDmQOaALgDmwC5ALoDnAOdA54DnwOgA6EDogOjAMQDpQOmAMUDpADGAMcAyADJ -AMoAywDMA6cAzQDOA+QDrQDSA64A0wOvA7ADsQOyANQA1QDWA7QD5QO1ANcDtgDYA7cDuADZA7kA2gDb -ANwDugOzAN0DuwO8A70DvgO/A8ADwQDeAN8DwgPDAOoA6wDsAO0DxADuAO8A8APFAPEA8gDzAPQDxgD1 -A8cDyAD2A8kA9wPKA+YDywECA8wBAwPNA84DzwPQAQQBBQEGA9ED5wPSAQcBCAEJBIED6APpARcBGAEZ -ARoD6gPrA+0D7AEoASkBKgErBIABLAEtAS4BLwEwBIIEgwExATIBMwE0A+4D7wE1ATYBNwE4BIQEhQPw -A/EEdwR4A/ID8wSGBIcEfwFMAU0EfQR+A/QD9QP2AU4BTwFQAVEBUgFTAVQBVQR5BHoBVgFXAVgEAQQA -BAIEAwQEBAUEBgFZAVoEewR8BBsEHAFbAVwBXQFeBIgEiQFfBB0EigFvAXABgQGCBIwEiwGXBHYBnQAM -AAAAAAu8AAAAAAAAAPkAAAAAAAAAAAAAAAEAAAACAAAAAgAAAAIAAAANAAAADQAAAAMAAAAgAAAAfgAA -AAQAAACgAAAAoAAAAngAAAChAAAArAAAAGMAAACtAAAArQAAAnkAAACuAAAAvwAAAG8AAADAAAAAxQAA -An8AAADGAAAAxgAAAIEAAADHAAAAzwAAAoYAAADQAAAA0AAAAnsAAADRAAAA1gAAAo8AAADXAAAA2AAA -AIIAAADZAAAA3QAAApUAAADeAAAA3wAAAIQAAADgAAAA5QAAApoAAADmAAAA5gAAAIYAAADnAAAA7wAA -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 -BbAAFAYYABUBpgARBsAADgbZAAYAAAAAAAAADQCiAAMAAQQJAAAAXgAAAAMAAQQJAAEADABeAAMAAQQJ -AAIADgBqAAMAAQQJAAMADABeAAMAAQQJAAQADABeAAMAAQQJAAUAJgB4AAMAAQQJAAYAHACeAAMAAQQJ -AAcAQAC6AAMAAQQJAAkADAD6AAMAAQQJAAsAFAEGAAMAAQQJAAwAJgEaAAMAAQQJAA0AXAFAAAMAAQQJ -AA4AVAGcAEMAbwBwAHkAcgBpAGcAaAB0ACAAMgAwADEAMQAgAEcAbwBvAGcAbABlACAASQBuAGMALgAg -AEEAbABsACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgBSAG8AYgBvAHQAbwBSAGUAZwB1 -AGwAYQByAFYAZQByAHMAaQBvAG4AIAAyAC4AMQAzADcAOwAgADIAMAAxADcAUgBvAGIAbwB0AG8ALQBS -AGUAZwB1AGwAYQByAFIAbwBiAG8AdABvACAAaQBzACAAYQAgAHQAcgBhAGQAZQBtAGEAcgBrACAAbwBm -ACAARwBvAG8AZwBsAGUALgBHAG8AbwBnAGwAZQBHAG8AbwBnAGwAZQAuAGMAbwBtAEMAaAByAGkAcwB0 -AGkAYQBuACAAUgBvAGIAZQByAHQAcwBvAG4ATABpAGMAZQBuAHMAZQBkACAAdQBuAGQAZQByACAAdABo -AGUAIABBAHAAYQBjAGgAZQAgAEwAaQBjAGUAbgBzAGUALAAgAFYAZQByAHMAaQBvAG4AIAAyAC4AMABo -AHQAdABwADoALwAvAHcAdwB3AC4AYQBwAGEAYwBoAGUALgBvAHIAZwAvAGwAaQBjAGUAbgBzAGUAcwAv -AEwASQBDAEUATgBTAEUALQAyAC4AMAAAAAMAAAAAAAD/agBkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAC -AAgAAv//AA8AAQACAA4AAAAAAAACKAACAFkAJQA+AAEARQBeAAEAeQB5AAEAgQCBAAEAgwCDAAEAhgCG -AAEAiQCJAAEAiwCWAAEAmACdAAEApACkAAEAqACtAAMAsQCxAAEAugC7AAEAvwC/AAEAwQDBAAEAwwDD -AAEAxwDHAAEAywDLAAEAzQDOAAEA0ADRAAEA0wDTAAEA2gDeAAEA4QDhAAEA5QDlAAEA5wDpAAEA6wD7 -AAEA/QD9AAEA/wEBAAEBAwEDAAEBCAEJAAEBFgEaAAEBHAEcAAEBIAEiAAEBJAElAAMBKgErAAEBMwE0 -AAEBNgE2AAEBOwE8AAEBQQFEAAEBRwFIAAEBSwFNAAEBUQFRAAEBVAFYAAEBXQFeAAEBYgFiAAEBZAFk -AAEBaAFoAAEBagFsAAEBbgFuAAEBcAFwAAEBugG6AAMBuwHBAAIB0gHmAAEB6gHqAAEB8wHzAAEB9QH1 -AAEB/AH+AAECAAIBAAECAwIDAAECBwIHAAECCQILAAECEQIRAAECFgIYAAECGgIaAAECKAIoAAECKwIr -AAECLQItAAECMAIzAAECXwJjAAECegLiAAEC5QOLAAEDjQOkAAEDpgOyAAEDtAO9AAEDvwPaAAED3gPe -AAED4APnAAED6QPrAAED7gPyAAED9AR8AAEEfwR/AAEEggSDAAEEhQSGAAEEiASLAAEElQTQAAEE0gTx -AAEE8wT6AAEE/AT9AAEFBwUNAAEAAQACAAAADAAAACwAAQAOAKgAqACpAKkAqgCqAKsAqwCsAKwBJAEl -ASYBJwABAAUAeQCkAK0ArQG6AAAAAQAAAAoAMgBMAARERkxUABpjeXJsABpncmVrABpsYXRuABoABAAA -AAD//wACAAAAAQACY3BzcAAOa2VybgAUAAAAAQAAAAAAAQABAAIABgIQAAEAAAABAAgAAQAKAAUAJABI -AAEA+gAIAAoAFAAVABYAFwAYABkAGgAbABwAHQAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0 -ADUANgA3ADgAOQA6ADsAPAA9AD4AZQBnAIEAgwCEAIwAjwCRAJMAsQCyALMAtAC1ALYAtwC4ALkAugDS -ANMA1ADVANYA1wDYANkA2gDbANwA3QDeAN8A4ADhAOIA4wDkAOUA5gDnAOgA6QEvATMBNQE3ATkBOwFB -AUMBRQFJAUsBTAFYAVkBlwGdAaIBpQJ6AnsCfQJ/AoACgQKCAoMChAKFAoYChwKIAokCigKLAowCjQKO -Ao8CkAKRApICkwKUApUClgKXApgCmQK2ArgCugK8Ar4CwALCAsQCxgLIAsoCzALOAtAC0gLUAtYC2ALa -AtwC3gLgAuIC4wLlAucC6QLrAu0C7wLxAvMC9QL4AvoC/AL+AwADAgMEAwYDCAMKAwwDDgMQAxIDFAMW -AxgDGgMcAx4DIAMiAyQDJQMnAykDKwMtA4YDhwOIA4kDigOLA4wDjgOPA5ADkQOSA5MDlAOVA5YDlwOY -A5kDmgObA5wDnQOtA64DrwOwA7EDsgOzA7QDtQO2A7cDuAO5A7oDuwO8A70DvgO/A8ADwQPCA9MD1QPX -A9kD7gPwA/IEBwQNBBMEfQSCBIYFBwUJAAIAAAACAAo6GAABA/IABAAAAfQHzjTGNMYH/AheNv43rjTM -Ocw3eghkOBg4GDe4OAI4GDgYOcw4RAwCDNA4ijlYOZQ03jaEObINRjdcOGY1jA2MODoOwjg6ODo3iDhm -OHwPxDl2ECY1PDl2EEA4ZjnMEIY1xjb+Ocw2/hEIEgYTCBPqFIw5dhSSFJw4OheGGXgaahtwG4YbjBuS -Howekh7MHwIfjDWgNaAhvjgYImAjXjTeJcA4GDgYNUI4GDgYOBgmljWgOBg1oChAKQYpmCn6KuA1litu -NTwzRiuYLXI4ZjEAMTozJDMkOGYycDL6MyQzJDMkNv43iDlYOXYzRjhmNcY1ljTeNTw3uDe4N7g4GDTe -NTw4GDgYOcw1ljTeNTw0xjNwNMY0xjTGOgg0EjRgOgI0vDnqOfA6AjnwOeo56jnqOeo0rjnwNMw5zDnM -Ocw5zDiKNv42/jb+Nv42/jb+Nv40zDd6N3o3ejd6OBg4GDgYOBg4GDnMOcw5zDnMOcw2hDdcN1w3XDdc -N1w3XDdcNYw1jDWMNYw4OjeIN4g3iDeIN4g5djl2Nv43XDb+N1w2/jdcNMw0zDTMNMw5zDd6NYw3ejWM -N3o1jDd6NYw3ejWMOBg4OjgYOBg4GDgYOBg3uDgCOAI4AjgCOBg4OjgYODo4GDg6ODo5zDeIOcw3iDnM -N4g4fDh8OHw4ijiKOIo5lDaEOXY2hDmyObI5sjoCOgI6CDnwOfA58DnwOfA58DnwOgI6AjoCOgI6Ajnw -OfA58DoCOeo0vDS8NLw0vDoCOgI6AjoINv43ejgYOBg5zDaENv43rjd6ObI4GDgYN7g4GDgYOcw4RDiK -NoQ03jgYNoQ4OjeIOXY3iDd6NcY4GDgYN7g3uDVCNv43rjXGN3o4GDgYOcw4RDTMOIo03jdcNYw3iDhm -OXY1PDWMNZY5djmUOZQ5lDaEOXY0xjTGNMY4GDg6Nv43XDd6NYw5WDl2NMw2hDl2OBg03jU8OBg2/jdc -Nv43XDd6NYw1jDWMNN41PDnMN4g3iDhmNUI5djVCOXY1Qjl2Nv43XDb+N1w2/jdcNv43XDb+N1w2/jdc -Nv43XDb+N1w2/jdcNv43XDb+N1w2/jdcN3o1jDd6NYw3ejWMN3o1jDd6NYw3ejWMN3o1jDd6NYw4GDgY -Ocw3iDnMN4g5zDeIOcw3iDnMN4g5zDeIOcw3iDeINoQ5djaEOXY2hDl2OIo1xjWWODo1oDXGN7g2hDgY -ODo2/jdcN3o4GDnMN4g4fDeuOGY5zDnMOBg4Oje4N7g4AjgYODo4GDg6Ocw4RDhmOHw4ijlYOXY5WDl2 -OZQ5sjnMOfA6AjnwOeo6CDnqOfA6AjoIAAIApAAEAAQAAAAGAAYAAQALAAwAAgATABMABAAlACoABQAs -AC0ACwAvADYADQA4ADgAFQA6AD8AFgBFAEYAHABJAEoAHgBMAEwAIABPAE8AIQBRAFQAIgBWAFYAJgBY -AFgAJwBaAF0AKABfAF8ALACKAIoALQCWAJYALgCdAJ0ALwCxALUAMAC3ALkANQC7ALsAOAC9AL4AOQDA -AMEAOwDDAMUAPQDHAM4AQADSANIASADUAN4ASQDgAO8AVADxAPEAZAD2APgAZQD7APwAaAD+AQAAagED -AQUAbQEKAQoAcAENAQ0AcQEYARoAcgEiASIAdQEuATAAdgEzATUAeQE3ATcAfAE5ATkAfQE7ATsAfgFD -AUQAfwFUAVQAgQFWAVYAggFYAVgAgwFcAV4AhAGEAYUAhwGHAYkAiQHYAdgAjAHaAdsAjQHdAd0AjwHg -AeEAkAHrAe0AkgH/Af8AlQIOAhAAlgIwAjAAmQIzAjMAmgJFAkUAmwJHAkgAnAJ6AnsAngJ9An0AoAJ/ -ApQAoQKZAqAAtwKiAqUAvwKqAq8AwwK0ArwAyQK+Ar4A0gLAAsAA0wLCAsIA1ALEAsQA1QLGAs8A1gLY -AtoA4ALcAtwA4wLeAt4A5ALgAuAA5QLiAuIA5gLnAucA5wLpAukA6ALrAusA6QLtAu0A6gLvAu8A6wLx -Av0A7AL/Av8A+QMBAwEA+gMDAwMA+wMOAw4A/AMQAxAA/QMSAxIA/gMgAyAA/wMiAyUBAAMnAycBBAMp -AykBBQMvAzgBBgNDA0cBEANNA08BFQNUA1QBGANlA2kBGQNtA28BHgN4A3gBIQOGA4sBIgOOA50BKAOg -A6ABOAOkA6QBOQOmA6YBOgOqA6oBOwOtA64BPAOwA7EBPgOzA7kBQAO7A70BRwO/A8QBSgPGA8cBUAPJ -A8wBUgPSA9MBVgPVA9UBWAPXA9cBWQPZA9wBWgPfA+QBXgPmA+YBZAPqA+sBZQPwA/ABZwPyA/sBaAP+ -A/8BcgQBBAQBdAQLBAwBeAQQBBABegQSBBgBewQeBEYBggRIBEgBqwRKBFcBrARfBF8BugRwBHUBuwR3 -BHcBwQR7BHwBwgR/BH8BxASBBIIBxQSEBIQBxwSGBIYByASXBJsByQSdBJ0BzgSfBKABzwSiBKIB0QSm -BKgB0gSqBKoB1QSsBK4B1gSwBLAB2QSyBLIB2gS0BLoB2wS8BLwB4gS/BL8B4wTCBMYB5ATIBMgB6QTK -BMsB6gTPBM8B7ATSBNIB7QTYBNgB7gTdBN0B7wToBOgB8ATqBOoB8QTxBPEB8gT1BPUB8wALADj/2ADS -/9gA1v/YATn/2AFF/9gDDv/YAxD/2AMS/9gDwf/YBHf/2AS//9gAGAA6ABQAOwASAD0AFgEZABQCmQAW -AyAAEgMiABYDJAAWA4sAFgOaABYDnQAWA9MAEgPVABID1wASA9kAFgPqABQD8gAWBHAAFgRyABYEdAAW -BIYAFgTCABQExAAUBMYAEgABABP/IADnABD/FgAS/xYAJf9WAC7++AA4ABQARf/eAEf/6wBI/+sASf/r -AEv/6wBT/+sAVf/rAFb/5gBZ/+oAWv/oAF3/6ACU/+sAmf/rAJv/6gCy/1YAtP9WALv/6wC9/+gAyP/r -AMn/6wDL/+oA0gAUANYAFAD3/+sBA//rAQ3/VgEY/+sBGv/oAR7/6wEi/+sBOQAUAUL/6wFFABQBYP/r -AWH/6wFr/+sBhv8WAYr/FgGO/xYBj/8WAev/wAHt/8ACM//AAn//VgKA/1YCgf9WAoL/VgKD/1YChP9W -AoX/VgKa/94Cm//eApz/3gKd/94Cnv/eAp//3gKg/94Cof/rAqL/6wKj/+sCpP/rAqX/6wKr/+sCrP/r -Aq3/6wKu/+sCr//rArD/6gKx/+oCsv/qArP/6gK0/+gCtf/oArb/VgK3/94CuP9WArn/3gK6/1YCu//e -Ar3/6wK//+sCwf/rAsP/6wLF/+sCx//rAsn/6wLL/+sCzf/rAs//6wLR/+sC0//rAtX/6wLX/+sC5f74 -Avn/6wL7/+sC/f/rAw4AFAMQABQDEgAUAxX/6gMX/+oDGf/qAxv/6gMd/+oDH//qAyP/6AMy/8ADM//A -AzT/wAM1/8ADNv/AAzf/wAM4/8ADTf/AA07/wANP/8ADhv9WA47/VgOe/+sDov/qA6T/6wOm/+gDqf/q -A6r/6wOr/+oDsv74A7b/VgPBABQDw//eA8T/6wPG/+sDyP/rA8n/6APL/+sD0v/oA9r/6APi/1YD4//e -A+b/6wPr/+gD7P/rA/H/6wPz/+gD+P9WA/n/3gP6/1YD+//eA///6wQB/+sEAv/rBAz/6wQO/+sEEP/r -BBT/6AQW/+gEGP/oBB3/6wQe/1YEH//eBCD/VgQh/94EIv9WBCP/3gQk/1YEJf/eBCb/VgQn/94EKP9W -BCn/3gQq/1YEK//eBCz/VgQt/94ELv9WBC//3gQw/1YEMf/eBDL/VgQz/94ENP9WBDX/3gQ3/+sEOf/r -BDv/6wQ9/+sEP//rBEH/6wRD/+sERf/rBEv/6wRN/+sET//rBFH/6wRT/+sEVf/rBFf/6wRZ/+sEW//r -BF3/6wRf/+sEYf/rBGP/6gRl/+oEZ//qBGn/6gRr/+oEbf/qBG//6gRx/+gEc//oBHX/6AR3ABQEmf9W -BJr/3gSc/+sEoP/rBKT/6gSp/+sEq//rBL8AFATD/+gExf/oBMv/wATS/8AE6v/AADMAOP/VADr/5AA7 -/+wAPf/dANL/1QDW/9UBGf/kATn/1QFF/9UB6wAOAe0ADgIzAA4Cmf/dAw7/1QMQ/9UDEv/VAyD/7AMi -/90DJP/dAzIADgMzAA4DNAAOAzUADgM2AA4DNwAOAzgADgNNAA4DTgAOA08ADgOL/90Dmv/dA53/3QPB -/9UD0//sA9X/7APX/+wD2f/dA+r/5APy/90EcP/dBHL/3QR0/90Ed//VBIb/3QS//9UEwv/kBMT/5ATG -/+wEywAOBNIADgTqAA4AHQA4/7AAOv/tAD3/0ADS/7AA1v+wARn/7QE5/7ABRf+wApn/0AMO/7ADEP+w -AxL/sAMi/9ADJP/QA4v/0AOa/9ADnf/QA8H/sAPZ/9AD6v/tA/L/0ARw/9AEcv/QBHT/0AR3/7AEhv/Q -BL//sATC/+0ExP/tABEALv/uADn/7gKV/+4Clv/uApf/7gKY/+4C5f/uAxT/7gMW/+4DGP/uAxr/7gMc -/+4DHv/uA7L/7gRi/+4EZP/uBMH/7gBNAAYAEAALABAADQAUAEEAEgBH/+gASP/oAEn/6ABL/+gAVf/o -AGEAEwCU/+gAmf/oALv/6ADI/+gAyf/oAPf/6AED/+gBHv/oASL/6AFC/+gBYP/oAWH/6AFr/+gBhAAQ -AYUAEAGHABABiAAQAYkAEAKh/+gCov/oAqP/6AKk/+gCpf/oAr3/6AK//+gCwf/oAsP/6ALF/+gCx//o -Asn/6ALL/+gCzf/oAs//6ALR/+gC0//oAtX/6ALX/+gDnv/oA8T/6API/+gDy//oA9sAEAPcABAD3wAQ -A+b/6APs/+gD8f/oA///6AQB/+gEAv/oBA7/6AQd/+gEN//oBDn/6AQ7/+gEPf/oBD//6ARB/+gEQ//o -BEX/6ARZ/+gEW//oBF3/6ARh/+gEnP/oBKn/6ASr/+gAQABH/+wASP/sAEn/7ABL/+wAVf/sAJT/7ACZ -/+wAu//sAMj/7ADJ/+wA9//sAQP/7AEe/+wBIv/sAUL/7AFg/+wBYf/sAWv/7AKh/+wCov/sAqP/7AKk -/+wCpf/sAr3/7AK//+wCwf/sAsP/7ALF/+wCx//sAsn/7ALL/+wCzf/sAs//7ALR/+wC0//sAtX/7ALX -/+wDnv/sA8T/7API/+wDy//sA+b/7APs/+wD8f/sA///7AQB/+wEAv/sBA7/7AQd/+wEN//sBDn/7AQ7 -/+wEPf/sBD//7ARB/+wEQ//sBEX/7ARZ/+wEW//sBF3/7ARh/+wEnP/sBKn/7ASr/+wAGABT/+wBGP/s -Aqv/7AKs/+wCrf/sAq7/7AKv/+wC+f/sAvv/7AL9/+wDpP/sA6r/7APG/+wEDP/sBBD/7ARL/+wETf/s -BE//7ARR/+wEU//sBFX/7ARX/+wEX//sBKD/7AAGABD/hAAS/4QBhv+EAYr/hAGO/4QBj/+EABEALv/s -ADn/7AKV/+wClv/sApf/7AKY/+wC5f/sAxT/7AMW/+wDGP/sAxr/7AMc/+wDHv/sA7L/7ARi/+wEZP/s -BMH/7AAgAAb/8gAL//IAWv/zAF3/8wC9//MA9v/1ARr/8wGE//IBhf/yAYf/8gGI//IBif/yArT/8wK1 -//MDI//zA6b/8wPJ//MD0v/zA9r/8wPb//ID3P/yA9//8gPr//MD8//zBBT/8wQW//MEGP/zBHH/8wRz -//MEdf/zBMP/8wTF//MAPwAn//MAK//zADP/8wA1//MAg//zAJP/8wCY//MAs//zAMQADQDT//MBCP/z -ARf/8wEb//MBHf/zAR//8wEh//MBQf/zAWr/8wJF//MCRv/zAkj/8wJJ//MChv/zApD/8wKR//MCkv/z -ApP/8wKU//MCvP/zAr7/8wLA//MCwv/zAtD/8wLS//MC1P/zAtb/8wL4//MC+v/zAvz/8wMt//MDiv/z -A5f/8wO9//MDwP/zA+3/8wPw//MEC//zBA3/8wQP//MESv/zBEz/8wRO//MEUP/zBFL/8wRU//MEVv/z -BFj/8wRa//MEXP/zBF7/8wRg//MEn//zBLj/8wBAACf/5gAr/+YAM//mADX/5gCD/+YAk//mAJj/5gCz -/+YAuP/CAMQAEADT/+YBCP/mARf/5gEb/+YBHf/mAR//5gEh/+YBQf/mAWr/5gJF/+YCRv/mAkj/5gJJ -/+YChv/mApD/5gKR/+YCkv/mApP/5gKU/+YCvP/mAr7/5gLA/+YCwv/mAtD/5gLS/+YC1P/mAtb/5gL4 -/+YC+v/mAvz/5gMt/+YDiv/mA5f/5gO9/+YDwP/mA+3/5gPw/+YEC//mBA3/5gQP/+YESv/mBEz/5gRO -/+YEUP/mBFL/5gRU/+YEVv/mBFj/5gRa/+YEXP/mBF7/5gRg/+YEn//mBLj/5gA4ACX/5AA8/9IAPf/T -ALL/5AC0/+QAxP/iANr/0gEN/+QBM//SAUP/0gFd/9ICf//kAoD/5AKB/+QCgv/kAoP/5AKE/+QChf/k -Apn/0wK2/+QCuP/kArr/5AMi/9MDJP/TA4b/5AOL/9MDjv/kA5r/0wOb/9IDnf/TA7b/5APC/9ID2f/T -A+L/5APy/9MD9f/SA/j/5AP6/+QEA//SBB7/5AQg/+QEIv/kBCT/5AQm/+QEKP/kBCr/5AQs/+QELv/k -BDD/5AQy/+QENP/kBHD/0wRy/9MEdP/TBIb/0wSZ/+QAKAAQ/x4AEv8eACX/zQCy/80AtP/NAMf/8gEN -/80Bhv8eAYr/HgGO/x4Bj/8eAn//zQKA/80Cgf/NAoL/zQKD/80ChP/NAoX/zQK2/80CuP/NArr/zQOG -/80Djv/NA7b/zQPi/80D+P/NA/r/zQQe/80EIP/NBCL/zQQk/80EJv/NBCj/zQQq/80ELP/NBC7/zQQw -/80EMv/NBDT/zQSZ/80AAQDEAA4AAgDK/+0A9v/AALoAR//cAEj/3ABJ/9wAS//cAFH/8wBS//MAU//W -AFT/8wBV/9wAWf/dAFr/4QBd/+EAlP/cAJn/3ACb/90Au//cAL3/4QC+/+4Av//mAMH/8wDC/+sAw//p -AMX/8ADG/+cAyP/cAMn/3ADK/+MAy//dAMz/zgDN/9QAzv/bAOz/8wDw//MA8f/zAPP/8wD0//MA9f/z -APf/3AD4//MA+v/zAPv/8wD+//MBAP/zAQP/3AEF//MBGP/WARr/4QEe/9wBIv/cASv/8wE2//MBPP/z -AT7/8wFC/9wBU//zAVX/8wFX//MBXP/zAWD/3AFh/9wBa//cAqH/3AKi/9wCo//cAqT/3AKl/9wCqv/z -Aqv/1gKs/9YCrf/WAq7/1gKv/9YCsP/dArH/3QKy/90Cs//dArT/4QK1/+ECvf/cAr//3ALB/9wCw//c -AsX/3ALH/9wCyf/cAsv/3ALN/9wCz//cAtH/3ALT/9wC1f/cAtf/3ALy//MC9P/zAvb/8wL3//MC+f/W -Avv/1gL9/9YDFf/dAxf/3QMZ/90DG//dAx3/3QMf/90DI//hA57/3AOg//MDov/dA6T/1gOm/+EDqf/d -A6r/1gOr/90DxP/cA8X/8wPG/9YDx//zA8j/3APJ/+EDy//cA8z/8wPR//MD0v/hA9r/4QPh//MD5v/c -A+f/8wPr/+ED7P/cA/H/3APz/+ED///cBAH/3AQC/9wECP/zBAr/8wQM/9YEDv/cBBD/1gQU/+EEFv/h -BBj/4QQc//MEHf/cBDf/3AQ5/9wEO//cBD3/3AQ//9wEQf/cBEP/3ARF/9wES//WBE3/1gRP/9YEUf/W -BFP/1gRV/9YEV//WBFn/3ARb/9wEXf/cBF//1gRh/9wEY//dBGX/3QRn/90Eaf/dBGv/3QRt/90Eb//d -BHH/4QRz/+EEdf/hBHz/8wSY//MEnP/cBKD/1gSk/90Eqf/cBKv/3AS1//MEt//zBMP/4QTF/+EAfAAG -/9oAC//aAEf/8ABI//AASf/wAEv/8ABV//AAWf/vAFr/3ABd/9wAlP/wAJn/8ACb/+8Au//wAL3/3ADC -/+wAxAAPAMb/6gDI//AAyf/wAMr/xADL/+8AzP/nAPf/8AED//ABGv/cAR7/8AEi//ABQv/wAWD/8AFh -//ABa//wAYT/2gGF/9oBh//aAYj/2gGJ/9oCof/wAqL/8AKj//ACpP/wAqX/8AKw/+8Csf/vArL/7wKz -/+8CtP/cArX/3AK9//ACv//wAsH/8ALD//ACxf/wAsf/8ALJ//ACy//wAs3/8ALP//AC0f/wAtP/8ALV -//AC1//wAxX/7wMX/+8DGf/vAxv/7wMd/+8DH//vAyP/3AOe//ADov/vA6b/3AOp/+8Dq//vA8T/8API -//ADyf/cA8v/8APS/9wD2v/cA9v/2gPc/9oD3//aA+b/8APr/9wD7P/wA/H/8APz/9wD///wBAH/8AQC -//AEDv/wBBT/3AQW/9wEGP/cBB3/8AQ3//AEOf/wBDv/8AQ9//AEP//wBEH/8ARD//AERf/wBFn/8ARb -//AEXf/wBGH/8ARj/+8EZf/vBGf/7wRp/+8Ea//vBG3/7wRv/+8Ecf/cBHP/3AR1/9wEnP/wBKT/7wSp -//AEq//wBMP/3ATF/9wAPAAG/6AAC/+gAEr/6QBZ//EAWv/FAF3/xQCb//EAvf/FAML/7gDEABAAxv/s -AMr/IADL//EBGv/FAYT/oAGF/6ABh/+gAYj/oAGJ/6ACsP/xArH/8QKy//ECs//xArT/xQK1/8UDFf/x -Axf/8QMZ//EDG//xAx3/8QMf//EDI//FA6L/8QOm/8UDqf/xA6v/8QPJ/8UD0v/FA9r/xQPb/6AD3P+g -A9//oAPr/8UD8//FBBT/xQQW/8UEGP/FBGP/8QRl//EEZ//xBGn/8QRr//EEbf/xBG//8QRx/8UEc//F -BHX/xQSk//EEw//FBMX/xQBBAEf/5wBI/+cASf/nAEv/5wBV/+cAlP/nAJn/5wC7/+cAxAAPAMj/5wDJ -/+cA9//nAQP/5wEe/+cBIv/nAUL/5wFg/+cBYf/nAWv/5wKh/+cCov/nAqP/5wKk/+cCpf/nAr3/5wK/ -/+cCwf/nAsP/5wLF/+cCx//nAsn/5wLL/+cCzf/nAs//5wLR/+cC0//nAtX/5wLX/+cDnv/nA8T/5wPI -/+cDy//nA+b/5wPs/+cD8f/nA///5wQB/+cEAv/nBA7/5wQd/+cEN//nBDn/5wQ7/+cEPf/nBD//5wRB -/+cEQ//nBEX/5wRZ/+cEW//nBF3/5wRh/+cEnP/nBKn/5wSr/+cABQDK/+oA7f/uAPb/qwE6/+wBbf/s -AAEA9v/VAAEAygALAL4ABgAMAAsADABH/+gASP/oAEn/6ABKAAwAS//oAFP/6gBV/+gAWgALAF0ACwCU -/+gAmf/oALv/6AC9AAsAvv/tAMYACwDI/+gAyf/oAMoADAD3/+gBA//oARj/6gEaAAsBHv/oASL/6AFC -/+gBYP/oAWH/6AFr/+gBhAAMAYUADAGHAAwBiAAMAYkADAHTAA0B1gANAdgADgHZ//UB2//sAd3/7QHl -/+wB6/+/Aez/7QHt/78B9AAOAfX/7QH4AA4CEAAOAhH/7QISAA0CFAAOAhr/7QIx/+4CM/+/AqH/6AKi -/+gCo//oAqT/6AKl/+gCq//qAqz/6gKt/+oCrv/qAq//6gK0AAsCtQALAr3/6AK//+gCwf/oAsP/6ALF -/+gCx//oAsn/6ALL/+gCzf/oAs//6ALR/+gC0//oAtX/6ALX/+gC+f/qAvv/6gL9/+oDIwALAzL/vwMz -/78DNP+/AzX/vwM2/78DN/+/Azj/vwM5/+0DQ//tA0T/7QNF/+0DRv/tA0f/7QNMAA0DTf+/A07/vwNP -/78DUP/tA1H/7QNS/+0DU//tA1r/7QNb/+0DXP/tA13/7QNt/+0Dbv/tA2//7QNz//UDdP/1A3X/9QN2 -//UDeAAOA4EADQOCAA0Dnv/oA6T/6gOmAAsDqv/qA8T/6APG/+oDyP/oA8kACwPL/+gD0gALA9oACwPb -AAwD3AAMA98ADAPm/+gD6wALA+z/6APx/+gD8wALA///6AQB/+gEAv/oBAz/6gQO/+gEEP/qBBQACwQW -AAsEGAALBB3/6AQ3/+gEOf/oBDv/6AQ9/+gEP//oBEH/6ARD/+gERf/oBEv/6gRN/+oET//qBFH/6gRT -/+oEVf/qBFf/6gRZ/+gEW//oBF3/6ARf/+oEYf/oBHEACwRzAAsEdQALBJz/6ASg/+oEqf/oBKv/6ATD -AAsExQALBMv/vwTP/+0E0AANBNL/vwTeAA0E4QANBOr/vwTx/+0E9P/tBPUADgT5/+0E+gANAAEA9v/Y -AA4AXP/tAF7/7QDu/+0A9v+qATT/7QFE/+0BXv/tAyb/7QMo/+0DKv/tA8r/7QP2/+0EBP/tBMn/7QAN -AFz/8gBe//IA7v/yATT/8gFE//IBXv/yAyb/8gMo//IDKv/yA8r/8gP2//IEBP/yBMn/8gAiAFr/9ABc -//IAXf/0AF7/8wC9//QA7v/yARr/9AE0//IBRP/yAV7/8gK0//QCtf/0AyP/9AMm//MDKP/zAyr/8wOm -//QDyf/0A8r/8gPS//QD2v/0A+v/9APz//QD9v/yBAT/8gQU//QEFv/0BBj/9ARx//QEc//0BHX/9ATD -//QExf/0BMn/8wCMAAb/ygAL/8oAOP/SADr/1AA8//QAPf/TAFH/0QBS/9EAVP/RAFr/5gBc/+8AXf/m -AL3/5gDB/9EA0v/SANb/0gDa//QA3v/tAOH/4QDm/9QA7P/RAO7/7wDw/9EA8f/RAPP/0QD0/9EA9f/R -APb/yQD4/9EA+v/RAPv/0QD+/9EBAP/RAQX/0QEJ/+UBGf/UARr/5gEg/+MBK//RATP/9AE0/+8BNv/R -ATn/0gE6/8QBPP/RAT7/0QFD//QBRP/vAUX/0gFH/+EBSf/hAVP/0QFV/9EBV//RAVz/0QFd//QBXv/v -AWL/1AFj//UBZP/nAWz/0gFt/8kBhP/KAYX/ygGH/8oBiP/KAYn/ygKZ/9MCqv/RArT/5gK1/+YC8v/R -AvT/0QL2/9EC9//RAw7/0gMQ/9IDEv/SAyL/0wMj/+YDJP/TA4v/0wOa/9MDm//0A53/0wOg/9EDpv/m -A7X/7QPB/9IDwv/0A8X/0QPH/9EDyf/mA8r/7wPM/9ED0f/RA9L/5gPZ/9MD2v/mA9v/ygPc/8oD3//K -A+H/0QPn/9ED6v/UA+v/5gPy/9MD8//mA/X/9AP2/+8EA//0BAT/7wQI/9EECv/RBBP/7QQU/+YEFf/t -BBb/5gQX/+0EGP/mBBn/4QQc/9EEcP/TBHH/5gRy/9MEc//mBHT/0wR1/+YEd//SBHn/4QR8/9EEhv/T -BJj/0QS1/9EEt//RBL//0gTC/9QEw//mBMT/1ATF/+YAKAA4/74AWv/vAF3/7wC9/+8A0v++ANb/vgDm -/8kA9v/fAQn/7QEa/+8BIP/rATn/vgE6/98BRf++AUz/6QFj//UBbf/gArT/7wK1/+8DDv++AxD/vgMS -/74DI//vA6b/7wPB/74Dyf/vA9L/7wPa/+8D6//vA/P/7wQU/+8EFv/vBBj/7wRx/+8Ec//vBHX/7wR3 -/74Ev/++BMP/7wTF/+8APwA4/+YAOv/nADz/8gA9/+cAXP/xANL/5gDW/+YA2v/yAN7/7gDh/+gA5v/m -AO7/8QD2/9ABGf/nATP/8gE0//EBOf/mATr/zgFD//IBRP/xAUX/5gFH/+gBSf/oAV3/8gFe//EBYv/n -AWT/7QFs/+YBbf/QApn/5wMO/+YDEP/mAxL/5gMi/+cDJP/nA4v/5wOa/+cDm//yA53/5wO1/+4Dwf/m -A8L/8gPK//ED2f/nA+r/5wPy/+cD9f/yA/b/8QQD//IEBP/xBBP/7gQV/+4EF//uBBn/6ARw/+cEcv/n -BHT/5wR3/+YEef/oBIb/5wS//+YEwv/nBMT/5wCYACUAEAAn/+gAK//oADP/6AA1/+gAOP/gADr/4AA9 -/98Ag//oAJP/6ACY/+gAsgAQALP/6AC0ABAA0v/gANP/6ADUABAA1v/gANkAFADdABAA4f/hAOb/4ADt -ABMA8gAQAPn/4AEEABABCP/oAQ0AEAEX/+gBGf/gARv/6AEd/+gBH//oASH/6AE5/+ABQf/oAUX/4AFH -/+EBSP/gAUn/4QFK/+ABTf/hAVAAEAFRABABWP/pAWL/3wFk/94BZgAQAWr/6AFs/98Bbv/yAW8AEAFw -ABACRf/oAkb/6AJI/+gCSf/oAn8AEAKAABACgQAQAoIAEAKDABAChAAQAoUAEAKG/+gCkP/oApH/6AKS -/+gCk//oApT/6AKZ/98CtgAQArgAEAK6ABACvP/oAr7/6ALA/+gCwv/oAtD/6ALS/+gC1P/oAtb/6AL4 -/+gC+v/oAvz/6AMO/+ADEP/gAxL/4AMi/98DJP/fAy3/6AOGABADiv/oA4v/3wOOABADl//oA5r/3wOd -/98DtgAQA73/6APA/+gDwf/gA9n/3wPiABAD6v/gA+3/6APw/+gD8v/fA/gAEAP6ABAEC//oBA3/6AQP -/+gEGf/hBBr/4AQeABAEIAAQBCIAEAQkABAEJgAQBCgAEAQqABAELAAQBC4AEAQwABAEMgAQBDQAEARK -/+gETP/oBE7/6ARQ/+gEUv/oBFT/6ARW/+gEWP/oBFr/6ARc/+gEXv/oBGD/6ARw/98Ecv/fBHT/3wR3 -/+AEef/hBHr/4ASG/98EmQAQBJ//6AS4/+gEv//gBML/4ATE/+AANQAb//IAOP/xADr/9AA8//QAPf/w -ANL/8QDU//UA1v/xANr/9ADd//UA3v/zAOb/8QEZ//QBM//0ATn/8QFD//QBRf/xAVD/9QFd//QBYv/y -AWT/8gFm//UBbP/yAW//9QKZ//ADDv/xAxD/8QMS//EDIv/wAyT/8AOL//ADmv/wA5v/9AOd//ADtf/z -A8H/8QPC//QD2f/wA+r/9APy//AD9f/0BAP/9AQT//MEFf/zBBf/8wRw//AEcv/wBHT/8AR3//EEhv/w -BL//8QTC//QExP/0AGoAJQAPADj/5gA6/+YAPAAOAD3/5gCyAA8AtAAPANL/5gDUAA4A1v/mANkAEwDa -AA4A3QAOAN4ACwDh/+UA5v/mAOf/9ADtABIA8gAPAPb/5wD5/+gBBAAPAQ0ADwEZ/+YBMwAOATn/5gE6 -/+cBQwAOAUX/5gFH/+UBSP/oAUn/5QFK/+gBTP/kAVAADgFRAA8BXQAOAWL/5gFk/+YBZgAOAWz/5gFt -/+cBbwAOAXAADwJ/AA8CgAAPAoEADwKCAA8CgwAPAoQADwKFAA8Cmf/mArYADwK4AA8CugAPAw7/5gMQ -/+YDEv/mAyL/5gMk/+YDhgAPA4v/5gOOAA8Dmv/mA5sADgOd/+YDtQALA7YADwPB/+YDwgAOA9n/5gPi -AA8D6v/mA/L/5gP1AA4D+AAPA/oADwQDAA4EEwALBBUACwQXAAsEGf/lBBr/6AQeAA8EIAAPBCIADwQk -AA8EJgAPBCgADwQqAA8ELAAPBC4ADwQwAA8EMgAPBDQADwRw/+YEcv/mBHT/5gR3/+YEef/lBHr/6ASG -/+YEmQAPBL//5gTC/+YExP/mADEAOP/jADz/5QA9/+QA0v/jANT/5QDW/+MA2f/iANr/5QDd/+UA3v/p -APL/6gEE/+oBM//lATn/4wFD/+UBRf/jAVD/5QFR/+oBXf/lAWb/5QFs/+QBb//lAXD/6gKZ/+QDDv/j -AxD/4wMS/+MDIv/kAyT/5AOL/+QDmv/kA5v/5QOd/+QDtf/pA8H/4wPC/+UD2f/kA/L/5AP1/+UEA//l -BBP/6QQV/+kEF//pBHD/5ARy/+QEdP/kBHf/4wSG/+QEv//jACQAOP/iADz/5ADS/+IA1P/kANb/4gDZ -/+EA2v/kAN3/5ADe/+kA7f/kAPL/6wEE/+sBM//kATn/4gFD/+QBRf/iAVD/5AFR/+sBXf/kAWb/5AFv -/+QBcP/rAw7/4gMQ/+IDEv/iA5v/5AO1/+kDwf/iA8L/5AP1/+QEA//kBBP/6QQV/+kEF//pBHf/4gS/ -/+IAGAA4/+sAPf/zANL/6wDW/+sBOf/rAUX/6wKZ//MDDv/rAxD/6wMS/+sDIv/zAyT/8wOL//MDmv/z -A53/8wPB/+sD2f/zA/L/8wRw//MEcv/zBHT/8wR3/+sEhv/zBL//6wA5AFH/7wBS/+8AVP/vAFz/8ADB -/+8A7P/vAO3/7gDu//AA8P/vAPH/7wDz/+8A9P/vAPX/7wD2/+4A+P/vAPr/7wD7/+8A/v/vAQD/7wEF -/+8BCf/0ASD/8QEr/+8BNP/wATb/7wE6/+8BPP/vAT7/7wFE//ABU//vAVX/7wFX/+8BXP/vAV7/8AFt -/+8Cqv/vAvL/7wL0/+8C9v/vAvf/7wOg/+8Dxf/vA8f/7wPK//ADzP/vA9H/7wPh/+8D5//vA/b/8AQE -//AECP/vBAr/7wQc/+8EfP/vBJj/7wS1/+8Et//vACMABv/yAAv/8gBa//UAXf/1AL3/9QD2//QBCf/1 -ARr/9QE6//UBbf/1AYT/8gGF//IBh//yAYj/8gGJ//ICtP/1ArX/9QMj//UDpv/1A8n/9QPS//UD2v/1 -A9v/8gPc//ID3//yA+v/9QPz//UEFP/1BBb/9QQY//UEcf/1BHP/9QR1//UEw//1BMX/9QAKAO0AFAD2 -/+0A+f/tAPz/4gE6/+0BSP/tAUr/7QFt/+0EGv/tBHr/7QB2AEf/8ABI//AASf/wAEv/8ABT/+sAVf/w -AJT/8ACZ//AAu//wAMj/8ADJ//AA9//wAQP/8AEY/+sBHP/rAR7/8AEi//ABQv/wAWD/8AFh//ABa//w -Adv/6wHd/+sB5f/pAez/6wH1/+sCEf/rAhr/6wIx/+sCof/wAqL/8AKj//ACpP/wAqX/8AKr/+sCrP/r -Aq3/6wKu/+sCr//rAr3/8AK///ACwf/wAsP/8ALF//ACx//wAsn/8ALL//ACzf/wAs//8ALR//AC0//w -AtX/8ALX//AC+f/rAvv/6wL9/+sDOf/rA0P/6wNE/+sDRf/rA0b/6wNH/+sDUP/rA1H/6wNS/+sDU//r -A1r/6wNb/+sDXP/rA13/6wNt/+sDbv/rA2//6wOe//ADpP/rA6r/6wPE//ADxv/rA8j/8APL//AD5v/w -A+z/8APx//AD///wBAH/8AQC//AEDP/rBA7/8AQQ/+sEHf/wBDf/8AQ5//AEO//wBD3/8AQ///AEQf/w -BEP/8ARF//AES//rBE3/6wRP/+sEUf/rBFP/6wRV/+sEV//rBFn/8ARb//AEXf/wBF//6wRh//AEnP/w -BKD/6wSp//AEq//wBM//6wTx/+sE9P/rBPn/6wDjAAYADQALAA0ARf/wAEf/sABI/7AASf+wAEoADQBL -/7AAU//WAFX/sABaAAsAXQALAJT/sACZ/7AAu/+wAL0ACwC+/7AAx/+rAMj/wADJ/7AAzP/VAO3/qgDy -/68A9/+wAQP/sAEE/68BGP/WARoACwEc/+IBHv+wASAADAEi/7ABQv+wAVH/rwFg/7ABYf+wAWMACwFl -AAsBa/+wAXD/rwGEAA0BhQANAYcADQGIAA0BiQANAdMADQHWAA0B2AAOAdn/9QHb/+wB3f/tAeX/7AHr -/78B7P/tAe3/vwH0AA4B9f/tAfgADgIQAA4CEf/tAhIADQIUAA4CGv/tAjH/7gIz/78Cmv/wApv/8AKc -//ACnf/wAp7/8AKf//ACoP/wAqH/sAKi/7ACo/+wAqT/sAKl/7ACq//WAqz/1gKt/9YCrv/WAq//1gK0 -AAsCtQALArf/8AK5//ACu//wAr3/sAK//7ACwf+wAsP/sALF/7ACx/+wAsn/sALL/7ACzf+wAs//sALR -/7AC0/+wAtX/sALX/7AC+f/WAvv/1gL9/9YDIwALAzL/vwMz/78DNP+/AzX/vwM2/78DN/+/Azj/vwM5 -/+0DQ//tA0T/7QNF/+0DRv/tA0f/7QNMAA0DTf+/A07/vwNP/78DUP/tA1H/7QNS/+0DU//tA1r/7QNb -/+0DXP/tA13/7QNt/+0Dbv/tA2//7QNz//UDdP/1A3X/9QN2//UDeAAOA4EADQOCAA0Dnv+wA6T/1gOm -AAsDqv/WA8P/8APE/7ADxv/WA8j/sAPJAAsDy/+wA9IACwPaAAsD2wANA9wADQPfAA0D4//wA+b/sAPr -AAsD7P+wA/H/sAPzAAsD+f/wA/v/8AP//7AEAf+wBAL/sAQM/9YEDv+wBBD/1gQUAAsEFgALBBgACwQd -/7AEH//wBCH/8AQj//AEJf/wBCf/8AQp//AEK//wBC3/8AQv//AEMf/wBDP/8AQ1//AEN/+wBDn/sAQ7 -/7AEPf+wBD//sARB/7AEQ/+wBEX/sARL/9YETf/WBE//1gRR/9YEU//WBFX/1gRX/9YEWf+wBFv/sARd -/7AEX//WBGH/sARxAAsEcwALBHUACwSa//AEnP+wBKD/1gSp/7AEq/+wBMMACwTFAAsEy/+/BM//7QTQ -AA0E0v+/BN4ADQThAA0E6v+/BPH/7QT0/+0E9QAOBPn/7QT6AA0ADgDtABQA8gAQAPb/8AD5//ABAQAM -AQQAEAE6//ABSP/wAUr/5gFRABABbf/wAXAAEAQa//AEev/wAE0ARwAMAEgADABJAAwASwAMAFUADACU -AAwAmQAMALsADADIAAwAyQAMAO0AOgDyABgA9v/jAPcADAD5//cBAwAMAQQAGAEeAAwBIgAMATr/4gFC -AAwBSP/3AUr/4wFRABgBYAAMAWEADAFrAAwBbf/jAXAAGAKhAAwCogAMAqMADAKkAAwCpQAMAr0ADAK/ -AAwCwQAMAsMADALFAAwCxwAMAskADALLAAwCzQAMAs8ADALRAAwC0wAMAtUADALXAAwDngAMA8QADAPI -AAwDywAMA+YADAPsAAwD8QAMA/8ADAQBAAwEAgAMBA4ADAQa//cEHQAMBDcADAQ5AAwEOwAMBD0ADAQ/ -AAwEQQAMBEMADARFAAwEWQAMBFsADARdAAwEYQAMBHr/9wScAAwEqQAMBKsADAAiAFr/9ABc//AAXf/0 -AL3/9ADt/+8A7v/wAPL/8wEE//MBGv/0ATT/8AFE//ABUf/zAV7/8AFw//MCtP/0ArX/9AMj//QDpv/0 -A8n/9APK//AD0v/0A9r/9APr//QD8//0A/b/8AQE//AEFP/0BBb/9AQY//QEcf/0BHP/9AR1//QEw//0 -BMX/9AAKAAb/1gAL/9YBhP/WAYX/1gGH/9YBiP/WAYn/1gPb/9YD3P/WA9//1gAIAPb/ugEJ/88BIP/b -ATr/UAFK/50BY//wAWX/8gFt/0wACgAG//UAC//1AYT/9QGF//UBh//1AYj/9QGJ//UD2//1A9z/9QPf -//UAKABMACAATwAgAFAAIABT/4AAV/+QAFsACwEY/4ABwf+QAqv/gAKs/4ACrf+AAq7/gAKv/4AC+f+A -Avv/gAL9/4ADBf+QAwf/kAMJ/5ADC/+QAw3/kAOk/4ADqv+AA8b/gAPN/5AEDP+ABBD/gARL/4AETf+A -BE//gARR/4AEU/+ABFX/gARX/4AEX/+ABKD/gAStACAErwAgBLEAIAS+/5AAEwHT/+4B1f/1Adb/8QHY -//IB9P/yAfj/8gIQ//ICEv/uAhT/8gNM/+4DeP/yA4D/9QOB/+4Dgv/uBND/7gTe/+4E4f/uBPX/8gT6 -/+4AEwHT/+UB1f/xAdb/6wHY/+kB9P/pAfj/6QIQ/+kCEv/lAhT/6QNM/+UDeP/pA4D/8QOB/+UDgv/l -BND/5QTe/+UE4f/lBPX/6QT6/+UAAwHV//UB1v/uA4D/9QACAdb/twHb//AAAQBbAAsABAAN/+YAQf/0 -AGH/7wFN/+0AFwC4/9QAvv/wAML/7QDEABEAyv/gAMz/5wDN/+UAzv/uANkAEgDq/+kA9v/XATr/1wFK -/9MBTP/WAU3/xQFY/+cBYgANAWQADAFt/9YBbv/yAdv/6QHl/+cCMf/pAAEBHP/xABIA2f+uAOYAEgDr -/+AA7f+tAO//1gD9/98BAf/SAQf/4AEc/84BLv/dATD/4gE4/+ABQP/gAUr/6QFN/9oBX/+9AWn/3wFs -ABEAAgD2//UBhf+wAAIA7f/JARz/7gAJAOb/wwD2/88BOv/OAUn/5wFM/98BYv/RAWT/7AFs/6ABbf/R -AC8AVv9tAFv/jABt/b8AfP59AIH+vACG/ysAif9LALj/YQC+/48Av/8PAMP+6ADG/x8Ax/7lAMr/RgDM -/u0Azf79AM7+2QDZ/1IA5gAFAOr/vQDr/0kA7f7+AO//EwD2/2gA/f8OAP//EwEB/wcBB/8OAQn/EQEc -/zwBIP+sAS7/FQEw/zwBOP8OATr/agFA/0kBSv8MAUz/PwFN/vEBWP/AAV/+7wFj/zEBZf9fAWn/CgFs -AAUBbf8wAW7/1QAeAAr/4gANABQADv/PAEEAEgBK/+oAVv/YAFj/6gBhABMAbf+uAHz/zQCB/6AAhv/B -AIn/wAC4/9AAvP/qAL7/7gC//8YAwAANAML/6QDD/9YAxv/oAMf/ugDK/+kAzP/LAM3/2gDO/8cBjf/T -Adv/ywHl/8sCMf/NABcAI//DAFj/7wBb/98Amv/uALj/5QC5/9EAxAARAMr/yADZABMA5v/FAPb/ygE6 -/58BSf9RAUr/ewFM/8oBTf/dAVj/8gFi/3UBZP/KAWz/TwFt/4wB1v/NAeX/9QAHAPb/8AEJ//EBIP/z -ATr/8QFj//MBZf/pAW3/0wADAEr/7gBb/+oB1v/wAAkAyv/qAO3/uAD2/+oBCf/wASD/8QE6/+sBY//1 -AW3/7AGF/7AAAgERAAsBbP/mABIAW//BALj/xQDK/7QA6v/XAPb/uQEJ/7IBHP/SASD/yAE6/6ABSv/F -AVj/5AFj/8wBZf/MAW3/ywFu/+8B2//nAeX/5gIx/+gABQBb/6QB1v9UAdv/8QHl//ECMf/zAAgA2QAV -AO0AFQFJ/+QBSv/lAUz/5AFi/+MBZP/iAWz/5AACAPb/wAGF/7AACABYAA4Agf+fAL7/9QDE/94Ax//l -ANn/qADt/8oBX//jAAUAyv/qAO3/7gD2/7ABOv/sAW3/7AADAEoADwBYADIAWwARADMABP/YAFb/tQBb -/8cAbf64AHz/KACB/00Ahv+OAIn/oQC4/64Avv/JAL//fgDD/2cAxv+HAMf/ZQDK/54AzP9qAM3/cwDO -/14A2f+lAOYADwDq/+QA6/+gAO3/dADv/4AA9v+yAP3/fQD//4ABAf95AQf/fQEJ/38BHP+YASD/2gEu -/4EBMP+YATj/fQE6/7MBQP+gAUr/fAFM/5oBTf9sAVj/5gFf/2sBY/+SAWX/rQFp/3sBbAAPAW3/kQFu -//IB2/+5AeX/uQIx/7kABwANABQAQQARAFb/4gBhABMB2//ZAeX/2QIx/9kABwBKAA0Avv/1AMYACwDH -/+oAygAMAO3/yAEc//EABwANAA8AQQAMAFb/6wBhAA4B2//nAeX/5wIx/+kABgBb/+UAuP/LAM3/5AHb -/+wB5f/rAjH/7QAHAIH/3wC1//MAt//wAMT/6gDZ/98A5v/gAWz/4AABAdv/6wAEAdb/xwHb//IB5f/y -AjH/8gABAdb/8QABAdYADQACCwwABAAADqwXaAAmACUAAAAAAAAAAAAAAAAAEgAAAAAAAAAA/+P/5AAA -AAAAAAAAABEAAAAAAAAAAAAAAAAAAAARAAAAEQAAAAAAAAAA/+T/5QAAAAAAAAAAAAAAAAAAAAAAAP/r -AAAAAAAAAAD/5f/V/+0AAAAAAAD/6gAA/+kAAAAAAAAAAAAA/+H/mgAA//X/6gAAAAAAAAAAAAAAAAAA -AAAAAP/1AAD/9P/1AAAAAP/1/87/7/9//6IAAAAAAAwAAAAA//EAAP+IAAD/u//E/8cAEQAAABIAAP+p -AAAAAP/J/48AAAAA/90AAAAAAAAAAAAAAAAAAAAAAAD/8QAAAAAAAAAAAAD/8AAAAAAAAAAA/3j/6wAA -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 -AOwAPgDuAO4APwD3APcAQAD8APwAQQD+AP8AQgEEAQUARAEKAQoARgENAQ0ARwEYARoASAEuATAASwEz -ATUATgE3ATcAUQE5ATkAUgE7ATsAUwFDAUQAVAFUAVQAVgFWAVYAVwFYAVgAWAFcAV4AWQGEAYoAXAGO -AY8AYwHYAdgAZQHdAd0AZgHgAeEAZwHrAe0AaQH/Af8AbAIOAhAAbQIwAjAAcAIzAjMAcQJFAkUAcgJH -AkgAcwJ6AnsAdQJ9An0AdwJ/AqUAeAKqAq8AnwK0AsQApQLGAs8AtgLYAtoAwALcAtwAwwLeAt4AxALg -AuAAxQLiAuIAxgLlAuUAxwLnAucAyALpAukAyQLrAusAygLtAu0AywLvAu8AzALxAv0AzQL/Av8A2gMB -AwEA2wMDAwMA3AMOAw4A3QMQAxAA3gMSAxIA3wMUAxQA4AMWAxYA4QMYAxgA4gMaAxoA4wMcAxwA5AMe -Ax4A5QMgAyAA5gMiAyoA5wMvAzgA8ANDA0cA+gNNA08A/wNUA1QBAgNlA2kBAwNtA28BCAN4A3gBCwOG -A4sBDAOOA50BEgOgA6ABIgOkA6QBIwOmA6YBJAOqA6oBJQOtA64BJgOwA7kBKAO7A70BMgO/A8QBNQPG -A8wBOwPSA9MBQgPVA9UBRAPXA9cBRQPZA9wBRgPfA+QBSgPmA+YBUAPqA+sBUQPwA/sBUwP+A/8BXwQB -BAQBYQQLBAwBZQQQBBABZwQSBBgBaAQeBEYBbwRIBEgBmARKBFcBmQRfBF8BpwRiBGIBqARkBGQBqQRw -BHUBqgR3BHcBsAR7BHwBsQR/BH8BswSBBIIBtASEBIQBtgSGBIYBtwSXBJsBuASdBJ0BvQSfBKABvgSi -BKIBwASmBKgBwQSqBKoBxASsBK4BxQSwBLAByASyBLIByQS0BLoBygS8BLwB0QS/BL8B0gTBBMYB0wTI -BMsB2QTPBM8B3QTSBNIB3gTYBNgB3wTdBN0B4AToBOgB4QTqBOoB4gTxBPEB4wT1BPUB5AACAXQABgAG -ABkACwALABkAEAAQACEAEgASACEAJQAlAAIAJgAmABwAJwAnABMAKAAoAAEAKQApAAUALgAuAAoALwAv -AAsAMAAwABgAMwAzAAEANAA0ABYAOAA4AA4AOQA5AAoAOgA6AB0AOwA7ABsAPAA8ABIAPQA9AAwAPgA+ -ABEARQBFAAYARgBGAAcARwBHABcASQBJAAgATABMAAQAUQBSAAQAUwBTAAMAVABUAAcAVgBWABUAWgBa -AAkAXABcABQAXQBdAAkAXgBeABAAigCKAAcAlgCWAAEAsQCxACIAsgCyAAIAswCzAAEAtAC0AAIAvQC9 -AAkAwQDBAAQAxwDHAAcA1ADVACAA2gDaABIA3gDeACUA5ADkACAA5gDmACAA7ADsABoA7gDuABQA9wD3 -AAcA/AD8AB8A/gD+AB8A/wD/AAcBBAEFAB8BCgEKAB8BDQENAAIBGAEYAAMBGQEZAB0BGgEaAAkBLgEu -AAcBLwEvACIBMAEwABoBMwEzABIBNAE0ABQBNQE1AAsBNwE3AAsBOQE5AAsBQwFDABIBRAFEABQBWAFY -AAEBXAFcABoBXQFdABIBXgFeABQBhAGFABkBhgGGACEBhwGJABkBigGKACEBjgGPACEB2AHYACMB3QHd -AA0B4AHgACQB4QHhAB4B6wHrAA8B7AHsAA0B7QHtAA8B/wH/AB4CDgIQAB4CMAIwAA0CMwIzAA8CRQJF -ABMCRwJIAAECegJ7AAECfQJ9AA4CfwKFAAIChgKGABMChwKKAAUCkAKUAAEClQKYAAoCmQKZAAwCmgKg -AAYCoQKhABcCogKlAAgCqgKqAAQCqwKvAAMCtAK1AAkCtgK2AAICtwK3AAYCuAK4AAICuQK5AAYCugK6 -AAICuwK7AAYCvAK8ABMCvQK9ABcCvgK+ABMCvwK/ABcCwALAABMCwQLBABcCwgLCABMCwwLDABcCxALE -AAECxgLGAAUCxwLHAAgCyALIAAUCyQLJAAgCygLKAAUCywLLAAgCzALMAAUCzQLNAAgCzgLOAAUCzwLP -AAgC2QLZAAQC5QLlAAoC5wLnAAsC6QLpABgC6wLrABgC7QLtABgC7wLvABgC8gLyAAQC9AL0AAQC9gL3 -AAQC+AL4AAEC+QL5AAMC+gL6AAEC+wL7AAMC/AL8AAEC/QL9AAMC/wL/ABUDAQMBABUDAwMDABUDDgMO -AA4DEAMQAA4DEgMSAA4DFAMUAAoDFgMWAAoDGAMYAAoDGgMaAAoDHAMcAAoDHgMeAAoDIAMgABsDIgMi -AAwDIwMjAAkDJAMkAAwDJQMlABEDJgMmABADJwMnABEDKAMoABADKQMpABEDKgMqABADLwMwAA0DMQMx -ACMDMgM4AA8DQwNHAA0DTQNPAA8DVANUAA0DZQNlAB4DZgNpACQDbQNvAA0DeAN4ACMDhgOGAAIDhwOH -AAUDigOKAAEDiwOLAAwDjgOOAAIDjwOPABwDkAOQAAUDkQORABEDlAOUAAsDlwOXAAEDmAOYABYDmQOZ -AA4DmgOaAAwDmwObABIDnQOdAAwDoAOgAAQDpAOkAAMDpgOmAAkDqgOqAAMDrQOtAAUDrgOuACIDsgOy -AAoDswO0AAsDtQO1ACUDtgO2AAIDtwO3ABwDuAO4ACIDuQO5AAUDvQO9AAEDvwO/ABYDwAPAABMDwQPB -AA4DwgPCABIDwwPDAAYDxAPEAAgDxgPGAAMDxwPHAAcDyAPIABcDyQPJAAkDygPKABQDywPLAAgDzAPM -ABoD0gPSAAkD0wPTABsD1QPVABsD1wPXABsD2QPZAAwD2gPaAAkD2wPcABkD3wPfABkD4QPhAAQD4gPi -AAID4wPjAAYD5APkAAUD5gPmAAgD6gPqAB0D6wPrAAkD8APwABMD8QPxABcD8gPyAAwD8wPzAAkD9QP1 -ABID9gP2ABQD+AP4AAID+QP5AAYD+gP6AAID+wP7AAYD/gP+AAUD/wP/AAgEAQQCAAgEAwQDABIEBAQE -ABQECwQLAAEEDAQMAAMEEAQQAAMEEgQSAAcEEwQTACUEFAQUAAkEFQQVACUEFgQWAAkEFwQXACUEGAQY -AAkEHgQeAAIEHwQfAAYEIAQgAAIEIQQhAAYEIgQiAAIEIwQjAAYEJAQkAAIEJQQlAAYEJgQmAAIEJwQn -AAYEKAQoAAIEKQQpAAYEKgQqAAIEKwQrAAYELAQsAAIELQQtAAYELgQuAAIELwQvAAYEMAQwAAIEMQQx -AAYEMgQyAAIEMwQzAAYENAQ0AAIENQQ1AAYENgQ2AAUENwQ3AAgEOAQ4AAUEOQQ5AAgEOgQ6AAUEOwQ7 -AAgEPAQ8AAUEPQQ9AAgEPgQ+AAUEPwQ/AAgEQARAAAUEQQRBAAgEQgRCAAUEQwRDAAgERAREAAUERQRF -AAgESgRKAAEESwRLAAMETARMAAEETQRNAAMETgROAAEETwRPAAMEUARQAAEEUQRRAAMEUgRSAAEEUwRT -AAMEVARUAAEEVQRVAAMEVgRWAAEEVwRXAAMEXwRfAAMEYgRiAAoEZARkAAoEcARwAAwEcQRxAAkEcgRy -AAwEcwRzAAkEdAR0AAwEdQR1AAkEdwR3AA4EewR7ACIEfAR8ABoEfwR/AAQEgQSBACAEggSCACIEhASE -AAsEhgSGAAwEmASYAAQEmQSZAAIEmgSaAAYEmwSbAAUEnwSfAAEEoASgAAMEogSiABUEpgSmABwEpwSn -AAcEqASoAAEEqgSqAAEErQStAAQErgSuAAsEsASwAAsEsgSyABgEtQS1AAQEtwS3AAQEuAS4AAEEuQS5 -ABYEugS6AAcEvAS8ABUEvwS/AA4EwQTBAAoEwgTCAB0EwwTDAAkExATEAB0ExQTFAAkExgTGABsEyATI -ABEEyQTJABAEygTKAAEEywTLAA8EzwTPAA0E0gTSAA8E2ATYAB4E3QTdACME6AToAB4E6gTqAA8E8QTx -AA0E9QT1ACMAAQAGBPUAFAAAAAAAAAAAABQAAAAAAAAAAAAaAB8AGgAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAGAAAAAgAAAAAAAAACAAAAAAAjAAAAAAAAAAAAAgAAAAIAAAAQAAsACgAd -ABYAEQAMABMAAAAAAAAAAAAAAAAABwAAAAEAAQABAAAAAQAAAAAAAAAAAAAAAwADAAQAAwABAAAADgAA -AAUACQAAABUACQAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAB -AAAAAAAAAAIAAQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG -AAIABgAAAAAAAAAAAAAAAAABAAAACQAAAAAAAAADAAAAAAAAAAAAAAAAAAEAAQAAAAUAAAAAAAAAAAAA -AAAACwACABkAAAALAAAAAAAAABEAAAAAABkAIgAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAV -AAAAAwADABsAAwADAAMAAAABAAMAIQADAAMAAAAAAAMAAAADAAAAAAABABsAAwAAAAAAAgAAAAAAAAAA -AAYAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAB0ACQACAAAAAgABAAIAAAACAAEAAAAAAAAAAAAAAAAAAAAA -AAMAAAAAAAAAAAAAAAAAAAARABUAAAADAAAAAAALAAAAAAADAAAAAwAAAAAAAgABABEAFQALAAAAIAAh -AAAAAAAAAAAAAAAAAAAAGQAbAAAAAwAAAAMAAAADAAAAAAAAAAAAAwARABUAAAABAAEAAAAAAAAAAAAZ -AAAAAAAAAAIAAQAAAAAAAAAZABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAB8AAAAU -ABQAGgAUABQAFAAaAAAAAAAAABoAGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAHAAkAAAAEgAYAB4AAAAIAAAACAAA -AAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAA0ACAANAAAAAAAAAAAAAAAAABgACAAAAAAAGAAAAAAAAAAc -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAGAAIABcAHAAYAAAAAAAAAAAAAAAI -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA0AAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgAAAAIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAHwAAAAAAAAAAAAAABgAGAAYABgAGAAYABgACAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgAC -AAIAAgAKAAoACgAKAAwABwAHAAcABwAHAAcABwABAAEAAQABAAEAAAAAAAAAAAADAAQABAAEAAQABAAF -AAUABQAFAAkACQAGAAcABgAHAAYABwACAAEAAgABAAIAAQACAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA -AAEAAgABAAIAAQACAAEAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIwAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAADAAAAAwAAAAMAAwACAAQAAgAEAAIABAAAAAAAAAAAAAAAAAAQAA4AEAAOABAADgAQ -AA4AEAAOAAsAAAALAAAACwAAAAoABQAKAAUACgAFAAoABQAKAAUACgAFABYAAAAMAAkADAATAA8AEwAP -ABMADwAAAAAAAgAAAAAAAAAAAA0ADQANAA0ADQANAA0ACAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgACAAI -AAgAEgASABIAEgAXAA0ADQANAAgACAAIAAgAAAAAAAAAAAAAAAAACAAIAAgACAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAIAAgACAAAAAAAAAAeAB4AHgAeAAAAGAAAABIAEgASABIAEgASACQAFwAX -AAAAAAAAAAYAAAAAAAAAAgAMAAAAAAAGAAAAAAATAAAAAAAAAAAAAAACAAAAAAAMABEAAAAMAAEAAAAD -AAAABQAAAAQAAAAJAAAAAAAFAAQABQAAAAAAAAAAAAAAAAAjAAAAAAAiAAYAAAAAAAAAAAAAAAAAAgAA -AAAAAgALABEABwABAAMABAADAAEACQAVAAEAAwAOAAAAAAAAAAMACQAWAAAAFgAAABYAAAAMAAkAFAAU -AAAAAAAUAAAAAwAGAAcAAAAAAAEAAwAAAAAAHQAJAAEAAgAAAAAAAgABAAwACQAAABEAFQAAAAYABwAG -AAcAAAAAAAAAAQAAAAEAAQARABUAAAAAAAAAAwAAAAMAAgAEAAIAAQACAAQAAAAAACIACQAiAAkAIgAJ -ACAAIQAAAAMAAQAGAAcABgAHAAYABwAGAAcABgAHAAYABwAGAAcABgAHAAYABwAGAAcABgAHAAYABwAA -AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAAAAAgAEAAIABAACAAQAAgAEAAIABAAC -AAQAAgAEAAIAAQACAAEAAgABAAIABAACAAEACgAFAAoABQAAAAUAAAAFAAAABQAAAAUAAAAFAAwACQAM -AAkADAAJAAAACwAAACAAIQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAHwAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAwAGAAcAAAABAAAAAAACAAQAAAAAAAAABQAAAAAAAAAAAAEAAAABAAAAAAAA -AAAAAAAAAAAAAAAAAAMAAAADAAIAAAAAAAAAAAAQAA4ACwAAAAoAHQAJAB0ACQAWAAAAEwAPAAAADQAA -AAAAAAAIABcAAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXABwAAAAXAAAAAAAAAAAAAAAAAAAAAAAN -AAAAAAAAAAAAAAAAAAgAAAAAAAgAGAAcAAAAAAAIABcAAQAAAAoBYgKSAARERkxUABpjeXJsABpncmVr -ABpsYXRuAEgABAAAAAD//wASAAAAAQACAAMABAAIAAwADQAOAA8AEAARABIAEwAUABUAFgAXAC4AB0Fa -RSAA5ENSVCAA5EZSQSAAWk1PTCAAtk5BViAAiFJPTSAAtlRSSyAA5AAA//8AEwAAAAEAAgADAAQABwAI -AAwADQAOAA8AEAARABIAEwAUABUAFgAXAAD//wAUAAAAAQACAAMABAAGAAgACQAMAA0ADgAPABAAEQAS -ABMAFAAVABYAFwAA//8AFAAAAAEAAgADAAQABgAIAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAAP// -ABQAAAABAAIAAwAEAAYACAAKAAwADQAOAA8AEAARABIAEwAUABUAFgAXAAD//wATAAAAAQACAAMABAAF -AAgADAANAA4ADwAQABEAEgATABQAFQAWABcAGGMyc2MAkmNjbXAAmGRsaWcAoGRub20ApmZyYWMArGxp -Z2EAtmxpZ2EAvGxpZ2EAyGxudW0A0GxvY2wA1mxvY2wA3GxvY2wA4m51bXIA6G9udW0A7nBudW0A9HNt -Y3AA+nNzMDEBAHNzMDIBBnNzMDMBDHNzMDQBEnNzMDUBGHNzMDYBHnNzMDcBJHRudW0BKgAAAAEAAAAA -AAIAAgAEAAAAAQAKAAAAAQAYAAAAAwAWABcAGQAAAAEACQAAAAQACAAJAAgACQAAAAIACAAJAAAAAQAV -AAAAAQAHAAAAAQAFAAAAAQAGAAAAAQAZAAAAAQASAAAAAQATAAAAAQABAAAAAQALAAAAAQAMAAAAAQAN -AAAAAQAOAAAAAQAPAAAAAQAQAAAAAQARAAAAAQAUABoANgQwB+4IoAjKD24PhA+uD8IP5hAQEEwQYBB0 -EIgQmhC0EPYRFBFmEawSDhJsEoASsBLSAAEAAAABAAgAAgH6APoB5wJxAdEB0AHPAc4BzQHMAcsBygHJ -AcgCMwIyAjECMAIoAeYB5QHkAeMB4gHhAeAB3wHeAd0B3AHbAdoB2QHYAdcB1gHVAdQB0wHSAegB6QJz -AnUCdAJ2AnICdwJSAeoB6wHsAe0B7gHvAfAB8QHyAfMB9AH1AfYB9wH4AfkB+gH7AfwB/QH+AgACAQT+ -AgICAwIEAgUCBgIHAggCCQIKAgsCOwINAg4CDwIQBPgCEQITAhQCFQIWAhcCGAIZAhsCHAIeAh0DLwMw -AzEDMgMzAzQDNQM2AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNO -A08DUANRA1IDUwNUA1UDVgNXA1gDWQNaA1sDXANdA14DXwNgA2EDYgNjBP8DZANlA2YDZwNoA2kDagNr -A2wDbQNuA28DcANxA3IDcwN0A3UFAgN2A3cDeQN4A3oDewN8A30DfgN/A4ADgQOCA4MDhAOFBQAFAQTL -BMwEzQTOBM8E0ATRBNIE0wTUBNUE1gTXBNgE2QTaBNsE3ATdBN4E3wTgBOEE4gTjBOQE5QTmBOcB/wTo -BOkE6gTrBOwE7QTuBO8E8ATxBPIE8wT0BPUE9gUDBQQFBQUGBPcE+QT6BPwCGgT9BPsCDAISBQsFDAAB -APoACAAKABQAFQAWABcAGAAZABoAGwAcAB0AJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1 -ADYANwA4ADkAOgA7ADwAPQA+AGUAZwCBAIMAhACMAI8AkQCTALEAsgCzALQAtQC2ALcAuAC5ALoA0gDT -ANQA1QDWANcA2ADZANoA2wDcAN0A3gDfAOAA4QDiAOMA5ADlAOYA5wDoAOkBLwEzATUBNwE5ATsBQQFD -AUUBSQFLAUwBWAFZAZcBnQGiAaUCegJ7An0CfwKAAoECggKDAoQChQKGAocCiAKJAooCiwKMAo0CjgKP -ApACkQKSApMClAKVApYClwKYApkCtgK4AroCvAK+AsACwgLEAsYCyALKAswCzgLQAtIC1ALWAtgC2gLc -At4C4ALiAuMC5QLnAukC6wLtAu8C8QLzAvUC+AL6AvwC/gMAAwIDBAMGAwgDCgMMAw4DEAMSAxQDFgMY -AxoDHAMeAyADIgMkAyUDJwMpAysDLQOGA4cDiAOJA4oDiwOMA44DjwOQA5EDkgOTA5QDlQOWA5cDmAOZ -A5oDmwOcA50DrQOuA68DsAOxA7IDswO0A7UDtgO3A7gDuQO6A7sDvAO9A74DvwPAA8EDwgPTA9UD1wPZ -A+4D8APyBAcEDQQTBH0EggSGBQcFCQABAAAAAQAIAAIB3ADrAnECMwIyAjECMAIoAeYB5QHkAeMB4gHh -AeAB3wHeAd0B3AHbAdoB2QHYAdcB1gHVAdQB0wHSAmQCcwMwAnUCdAMvAeMCcgJ3AlIE0gTTAeoB6wTU -BNUE1gHsBNcB7QHuAe8E3AHwAfAE3QTeAfEB8gHzAfoE6wTsAfsB/AH9Af4B/wIABO8E8ATyBPUE/gIC -AgMCBAIFAgYCBwIIAgkCCgILAfQB9QH2AfcB+AH5AjsCDQIOAg8CEAT4AhECEwIUAhUCFwIZAnYDMQMy -AzMDNAM1AzYDNwM4AzkDOgM7AzwDPQM+Az8DQANBA0IDQwNEA0UDRgNHA0gDSQNKA0sDTAOCA00DTgNP -A1ADUQNSA1MDVANVA1YDVwNYA1kDWgNbA1wDXQNeA18DYANhA2IE/wNkA2UDZgNnA2gDaQNqA2sDbANt -A24DbwNwA3EDcgNzA3QDdQUCA3YDdwN5A3gDegN7A3wDfQN+A38DgAOBA4MDhAOFBQAFAQTLBMwEzQTO -BNgE2wTZBNoE3wTgBOEEzwTQBNEE6gTtBO4E8QTzBPQCAQT2BOIE4wTkBOUE5gTnBOgE6QUDBQQFBQUG -BPcE+QT6AhgE/AIaBP0E+wIWAgwCEgULBQwAAQDrAAoARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBS -AFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAIUAhgCHAIkAigCLAI0AkACSAJQAuwC8AL0AvgC/AMAAwQDC -AMMAxADFAMYAxwDIAMkAygDLAMwAzQDOAOoA6wDsAO0A7gDvAPAA8QDyAPMA9AD1APYA9wD4APkA+gD7 -APwA/QD+AP8BAAEBAQIBAwEEAQUBBgEHATABNAE2ATgBOgE8AUIBRAFGAUoBTQFaAnwCfgKaApsCnAKd -Ap4CnwKgAqECogKjAqQCpQKmAqcCqAKpAqoCqwKsAq0CrgKvArACsQKyArMCtAK1ArcCuQK7Ar0CvwLB -AsMCxQLHAskCywLNAs8C0QLTAtUC1wLZAtsC3QLfAuEC5ALmAugC6gLsAu4C8ALyAvQC9gL5AvsC/QL/ -AwEDAwMFAwcDCQMLAw0DDwMRAxMDFQMXAxkDGwMdAx8DIQMjAyYDKAMqAywDLgOeA58DoAOhA6MDpAOl -A6YDpwOoA6kDqgOrA6wDwwPEA8UDxgPHA8gDyQPKA8sDzAPNA84DzwPQA9ED0gPUA9YD2APaA+8D8QPz -BAEECAQOBBQEfgR/BIMEhwUIBQoABgAAAAYAEgAqAEIAWgByAIoAAwAAAAEAEgABAJAAAQAAAAMAAQAB -AE0AAwAAAAEAEgABAHgAAQAAAAMAAQABAE4AAwAAAAEAEgABAGAAAQAAAAMAAQABAuEAAwAAAAEAEgAB -AEgAAQAAAAMAAQABA84AAwAAAAEAEgABADAAAQAAAAMAAQABA9AAAwAAAAEAEgABABgAAQAAAAMAAQAB -BEkAAgACAKgArAAAASQBJwAFAAEAAAABAAgAAgASAAYCYQJfAmICYwJgBQ0AAQAGAE0ATgLhA84D0ARJ -AAQAAAABAAgAAQYyADYAcgCkAK4AuADKAPwBDgEYAUoBZAF+AZABugH2AgACIgI8Ak4CigKcArYC4ALy -AyQDLgM4A0oDfAOGA5ADmgO0A84D4AQKBDwERgRoBIIElATGBNgE8gUcBS4FOAVCBUwFVgWABaoF1AX+ -BigABgAOABQAGgAgACYALAKAAAIAqQQeAAIArQJ/AAIAqAQgAAIAqwKCAAIAqgSZAAIArAABAAQEpgAC -AK0AAQAEArwAAgCpAAIABgAMBKoAAgG6BKgAAgCtAAYADgAUABoAIAAmACwCiAACAKkENgACAK0ChwAC -AKgEOAACAKsEOgACAKoEmwACAKwAAgAGAAwElQACAKkC1gACAboAAQAEBKwAAgCtAAYADgAUABoAIAAm -ACwCjAACAKkESAACAK0CiwACAKgERgACAKsC2gACAKoEnQACAKwAAwAIAA4AFASuAAIAqQLnAAIBugSw -AAIArQADAAgADgAUAukAAgCpAusAAgG6BLIAAgCtAAIABgAMA+AAAgCpBLQAAgCtAAUADAASABgAHgAk -AvEAAgCpAvMAAgG6BLYAAgCtBJcAAgCoAo8AAgCqAAcAEAAYAB4AJAAqADAANgS4AAMAqgCpApEAAgCp -BEoAAgCtApAAAgCoBEwAAgCrApMAAgCqBJ8AAgCsAAEABAS5AAIAqQAEAAoAEAAWABwC/gACAKkDAAAC -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 -'''; - -String? _dart_192_png; -// dart_192_png md5 is '7d80a0adfe72aa9597d5e135b70b42c8' -String _dart_192_png_base64 = ''' -iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAYAAABS3GwHAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAA -CxMBAJqcGAAABCRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRv -YmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRm -PSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpE -ZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFk -b2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5j -b20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1l -bnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4w -LyI+CiAgICAgICAgIDx0aWZmOlJlc29sdXRpb25Vbml0PjI8L3RpZmY6UmVzb2x1dGlvblVuaXQ+CiAg -ICAgICAgIDx0aWZmOkNvbXByZXNzaW9uPjA8L3RpZmY6Q29tcHJlc3Npb24+CiAgICAgICAgIDx0aWZm -OlhSZXNvbHV0aW9uPjcyPC90aWZmOlhSZXNvbHV0aW9uPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlv -bj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZ -UmVzb2x1dGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjE5MjwvZXhpZjpQaXhlbFhE -aW1lbnNpb24+CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+MTwvZXhpZjpDb2xvclNwYWNlPgogICAg -ICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+MTkyPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAg -ICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6U2VxLz4KICAgICAgICAgPC9kYzpzdWJqZWN0 -PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOToxMToyNyAxMzoxMToyMDwveG1wOk1vZGlmeURh -dGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjk8L3htcDpDcmVhdG9yVG9v -bD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CnQAoPMA -AB6pSURBVHgB7V1rsBzFde6Z3b26usKAhMSVEAkGLCQQpqgKktDLCDBVBMcCh7omAvIAJF0hAXbwr/xI -hR+pVFKVSlIkIEsC/Kj8QODYQRTlpCog8TAxhcs2JBgEhjLBDwwmNiiSru4+JufMbs/2PHqmp6d7XttD -iZ3p6cfpc76vz+memb6EmMNowGjAaMBoYDQ1YI1mt8vX63scx37yWTJpN8iZtkOWOIQsIhZZDL+ng7Rv -WxZ5q2eRw5MnkzcfXWnNlq8H1ZTIEKBAu2141pnfbJDLANxXEYesgX9nA+Dn202Avj0QDBLc8y4hvQ6Z -gTtvQMq/Q5lvHFxrvVCg+LVo2hCgADNe9ZxzRrtBbgfl32zZ5ONWgxAHAO7+Q8Djv6gDCmBeuwVkOEE6 -jkUOkg6599BG8gTc4ZWKqsmkDTRgCJAjFDY+4yxp2mQbjOjT9hxyRg8CGQS91AGWQyK4pCHk206P/MXT -660Xpeoa4UKGADkY3wV+E4BPyHZ7jCzttTMAP0JeIBNxOuQYeI6/WXgK+WszR4hQEifJEICjGBXJHvAt -AH5LPfBZGSGUIkAu0m2Tb7e7ZOt3Nlg/Z++b82gNGAJE6yVTap7ADwraGIf5QZu83OiQz/3HRuut4H1z -7deAIYBfH5muigQ+K7gbEs2SV9o9svk5QwJWNaFzQ4CQStInIPAbDbIdwpBtukMdUelcErTJK40u2Ww8 -AV9rhgB83STe2fg9AP4JAD7G+GOwqqN4cpsoQEIGSgKYExhPwNGVIQBHMXHJ62Edv9Ug22DlvZTAZ2Wn -JDCegNXK8NwQYKiLxLMqAZ/tDCWB8QSsVvrnhgBhnYRSBk9uKzHih4QfJAxI8KMTx8nm56+03uTlG7V0 -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 -AAAAAElFTkSuQmCC -'''; - -String? _highlight_css; -// highlight_css md5 is 'fb012626bafd286510d32da815dae448' -String _highlight_css_base64 = ''' -LyoKRGF0ZTogMjQgRmV2IDIwMTUKQXV0aG9yOiBQZWRybyBPbGl2ZWlyYSA8a2FueXR1QGdtYWlsIC4g -Y29tPgoqLwoKLmhsanMgewogIGNvbG9yOiAjYTliN2M2OwogIGJhY2tncm91bmQ6ICMyODJiMmU7CiAg -ZGlzcGxheTogYmxvY2s7CiAgb3ZlcmZsb3cteDogYXV0bzsKICBwYWRkaW5nOiAwLjVlbTsKfQoKLmhs -anMtbnVtYmVyLAouaGxqcy1saXRlcmFsLAouaGxqcy1zeW1ib2wsCi5obGpzLWJ1bGxldCB7CiAgY29s -b3I6ICM2ODk3QkI7Cn0KCi5obGpzLWtleXdvcmQsCi5obGpzLXNlbGVjdG9yLXRhZywKLmhsanMtZGVs -ZXRpb24gewogIGNvbG9yOiAjY2M3ODMyOwp9CgouaGxqcy12YXJpYWJsZSwKLmhsanMtdGVtcGxhdGUt -dmFyaWFibGUsCi5obGpzLWxpbmsgewogIGNvbG9yOiAjNjI5NzU1Owp9CgouaGxqcy1jb21tZW50LAou -aGxqcy1xdW90ZSB7CiAgY29sb3I6ICM4MDgwODA7Cn0KCi5obGpzLW1ldGEgewogIGNvbG9yOiAjYmJi -NTI5Owp9CgouaGxqcy1zdHJpbmcsCi5obGpzLWF0dHJpYnV0ZSwKLmhsanMtYWRkaXRpb24gewogIGNv -bG9yOiAjNkE4NzU5Owp9CgouaGxqcy1zZWN0aW9uLAouaGxqcy10aXRsZSwKLmhsanMtdHlwZSB7CiAg -Y29sb3I6ICNmZmM2NmQ7Cn0KCi5obGpzLW5hbWUsCi5obGpzLXNlbGVjdG9yLWlkLAouaGxqcy1zZWxl -Y3Rvci1jbGFzcyB7CiAgY29sb3I6ICNlOGJmNmE7Cn0KCi5obGpzLWVtcGhhc2lzIHsKICBmb250LXN0 -eWxlOiBpdGFsaWM7Cn0KCi5obGpzLXN0cm9uZyB7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0K -'''; - -String? _highlight_pack_js; -// highlight_pack_js md5 is 'e2de0572f5426885d35e366c9bf60f05' -String _highlight_pack_js_base64 = ''' -LyoKICBIaWdobGlnaHQuanMgMTAuMS4wICg3NGRlNmVhYSkKICBMaWNlbnNlOiBCU0QtMy1DbGF1c2UK -ICBDb3B5cmlnaHQgKGMpIDIwMDYtMjAyMCwgSXZhbiBTYWdhbGFldgoqLwp2YXIgaGxqcz1mdW5jdGlv -bigpeyJ1c2Ugc3RyaWN0IjtmdW5jdGlvbiBlKG4pe09iamVjdC5mcmVlemUobik7dmFyIHQ9ImZ1bmN0 -aW9uIj09dHlwZW9mIG47cmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKG4pLmZvckVhY2go -KGZ1bmN0aW9uKHIpeyFPYmplY3QuaGFzT3duUHJvcGVydHkuY2FsbChuLHIpfHxudWxsPT09bltyXXx8 -Im9iamVjdCIhPXR5cGVvZiBuW3JdJiYiZnVuY3Rpb24iIT10eXBlb2YgbltyXXx8dCYmKCJjYWxsZXIi -PT09cnx8ImNhbGxlZSI9PT1yfHwiYXJndW1lbnRzIj09PXIpfHxPYmplY3QuaXNGcm96ZW4obltyXSl8 -fGUobltyXSl9KSksbn1jbGFzcyBue2NvbnN0cnVjdG9yKGUpe3ZvaWQgMD09PWUuZGF0YSYmKGUuZGF0 -YT17fSksdGhpcy5kYXRhPWUuZGF0YX1pZ25vcmVNYXRjaCgpe3RoaXMuaWdub3JlPSEwfX1mdW5jdGlv -biB0KGUpe3JldHVybiBlLnJlcGxhY2UoLyYvZywiJmFtcDsiKS5yZXBsYWNlKC88L2csIiZsdDsiKS5y -ZXBsYWNlKC8+L2csIiZndDsiKS5yZXBsYWNlKC8iL2csIiZxdW90OyIpLnJlcGxhY2UoLycvZywiJiN4 -Mjc7Iil9ZnVuY3Rpb24gcihlLC4uLm4pe3ZhciB0PXt9O2Zvcihjb25zdCBuIGluIGUpdFtuXT1lW25d -O3JldHVybiBuLmZvckVhY2goKGZ1bmN0aW9uKGUpe2Zvcihjb25zdCBuIGluIGUpdFtuXT1lW25dfSkp -LHR9ZnVuY3Rpb24gYShlKXtyZXR1cm4gZS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpfXZhciBpPU9iamVj -dC5mcmVlemUoe19fcHJvdG9fXzpudWxsLGVzY2FwZUhUTUw6dCxpbmhlcml0OnIsbm9kZVN0cmVhbTpm -dW5jdGlvbihlKXt2YXIgbj1bXTtyZXR1cm4gZnVuY3Rpb24gZSh0LHIpe2Zvcih2YXIgaT10LmZpcnN0 -Q2hpbGQ7aTtpPWkubmV4dFNpYmxpbmcpMz09PWkubm9kZVR5cGU/cis9aS5ub2RlVmFsdWUubGVuZ3Ro -OjE9PT1pLm5vZGVUeXBlJiYobi5wdXNoKHtldmVudDoic3RhcnQiLG9mZnNldDpyLG5vZGU6aX0pLHI9 -ZShpLHIpLGEoaSkubWF0Y2goL2JyfGhyfGltZ3xpbnB1dC8pfHxuLnB1c2goe2V2ZW50OiJzdG9wIixv -ZmZzZXQ6cixub2RlOml9KSk7cmV0dXJuIHJ9KGUsMCksbn0sbWVyZ2VTdHJlYW1zOmZ1bmN0aW9uKGUs -bixyKXt2YXIgaT0wLHM9IiIsbz1bXTtmdW5jdGlvbiBsKCl7cmV0dXJuIGUubGVuZ3RoJiZuLmxlbmd0 -aD9lWzBdLm9mZnNldCE9PW5bMF0ub2Zmc2V0P2VbMF0ub2Zmc2V0PG5bMF0ub2Zmc2V0P2U6bjoic3Rh -cnQiPT09blswXS5ldmVudD9lOm46ZS5sZW5ndGg/ZTpufWZ1bmN0aW9uIGMoZSl7cys9IjwiK2EoZSkr -W10ubWFwLmNhbGwoZS5hdHRyaWJ1dGVzLChmdW5jdGlvbihlKXtyZXR1cm4iICIrZS5ub2RlTmFtZSsn -PSInK3QoZS52YWx1ZSkrJyInfSkpLmpvaW4oIiIpKyI+In1mdW5jdGlvbiB1KGUpe3MrPSI8LyIrYShl -KSsiPiJ9ZnVuY3Rpb24gZChlKXsoInN0YXJ0Ij09PWUuZXZlbnQ/Yzp1KShlLm5vZGUpfWZvcig7ZS5s -ZW5ndGh8fG4ubGVuZ3RoOyl7dmFyIGc9bCgpO2lmKHMrPXQoci5zdWJzdHJpbmcoaSxnWzBdLm9mZnNl -dCkpLGk9Z1swXS5vZmZzZXQsZz09PWUpe28ucmV2ZXJzZSgpLmZvckVhY2godSk7ZG97ZChnLnNwbGlj -ZSgwLDEpWzBdKSxnPWwoKX13aGlsZShnPT09ZSYmZy5sZW5ndGgmJmdbMF0ub2Zmc2V0PT09aSk7by5y -ZXZlcnNlKCkuZm9yRWFjaChjKX1lbHNlInN0YXJ0Ij09PWdbMF0uZXZlbnQ/by5wdXNoKGdbMF0ubm9k -ZSk6by5wb3AoKSxkKGcuc3BsaWNlKDAsMSlbMF0pfXJldHVybiBzK3Qoci5zdWJzdHIoaSkpfX0pO2Nv -bnN0IHM9Ijwvc3Bhbj4iLG89ZT0+ISFlLmtpbmQ7Y2xhc3MgbHtjb25zdHJ1Y3RvcihlLG4pe3RoaXMu -YnVmZmVyPSIiLHRoaXMuY2xhc3NQcmVmaXg9bi5jbGFzc1ByZWZpeCxlLndhbGsodGhpcyl9YWRkVGV4 -dChlKXt0aGlzLmJ1ZmZlcis9dChlKX1vcGVuTm9kZShlKXtpZighbyhlKSlyZXR1cm47bGV0IG49ZS5r -aW5kO2Uuc3VibGFuZ3VhZ2V8fChuPWAke3RoaXMuY2xhc3NQcmVmaXh9JHtufWApLHRoaXMuc3Bhbihu -KX1jbG9zZU5vZGUoZSl7byhlKSYmKHRoaXMuYnVmZmVyKz1zKX12YWx1ZSgpe3JldHVybiB0aGlzLmJ1 -ZmZlcn1zcGFuKGUpe3RoaXMuYnVmZmVyKz1gPHNwYW4gY2xhc3M9IiR7ZX0iPmB9fWNsYXNzIGN7Y29u -c3RydWN0b3IoKXt0aGlzLnJvb3ROb2RlPXtjaGlsZHJlbjpbXX0sdGhpcy5zdGFjaz1bdGhpcy5yb290 -Tm9kZV19Z2V0IHRvcCgpe3JldHVybiB0aGlzLnN0YWNrW3RoaXMuc3RhY2subGVuZ3RoLTFdfWdldCBy -b290KCl7cmV0dXJuIHRoaXMucm9vdE5vZGV9YWRkKGUpe3RoaXMudG9wLmNoaWxkcmVuLnB1c2goZSl9 -b3Blbk5vZGUoZSl7Y29uc3Qgbj17a2luZDplLGNoaWxkcmVuOltdfTt0aGlzLmFkZChuKSx0aGlzLnN0 -YWNrLnB1c2gobil9Y2xvc2VOb2RlKCl7aWYodGhpcy5zdGFjay5sZW5ndGg+MSlyZXR1cm4gdGhpcy5z -dGFjay5wb3AoKX1jbG9zZUFsbE5vZGVzKCl7Zm9yKDt0aGlzLmNsb3NlTm9kZSgpOyk7fXRvSlNPTigp -e3JldHVybiBKU09OLnN0cmluZ2lmeSh0aGlzLnJvb3ROb2RlLG51bGwsNCl9d2FsayhlKXtyZXR1cm4g -dGhpcy5jb25zdHJ1Y3Rvci5fd2FsayhlLHRoaXMucm9vdE5vZGUpfXN0YXRpYyBfd2FsayhlLG4pe3Jl -dHVybiJzdHJpbmciPT10eXBlb2Ygbj9lLmFkZFRleHQobik6bi5jaGlsZHJlbiYmKGUub3Blbk5vZGUo -biksbi5jaGlsZHJlbi5mb3JFYWNoKG49PnRoaXMuX3dhbGsoZSxuKSksZS5jbG9zZU5vZGUobikpLGV9 -c3RhdGljIF9jb2xsYXBzZShlKXsic3RyaW5nIiE9dHlwZW9mIGUmJmUuY2hpbGRyZW4mJihlLmNoaWxk -cmVuLmV2ZXJ5KGU9PiJzdHJpbmciPT10eXBlb2YgZSk/ZS5jaGlsZHJlbj1bZS5jaGlsZHJlbi5qb2lu -KCIiKV06ZS5jaGlsZHJlbi5mb3JFYWNoKGU9PntjLl9jb2xsYXBzZShlKX0pKX19Y2xhc3MgdSBleHRl -bmRzIGN7Y29uc3RydWN0b3IoZSl7c3VwZXIoKSx0aGlzLm9wdGlvbnM9ZX1hZGRLZXl3b3JkKGUsbil7 -IiIhPT1lJiYodGhpcy5vcGVuTm9kZShuKSx0aGlzLmFkZFRleHQoZSksdGhpcy5jbG9zZU5vZGUoKSl9 -YWRkVGV4dChlKXsiIiE9PWUmJnRoaXMuYWRkKGUpfWFkZFN1Ymxhbmd1YWdlKGUsbil7Y29uc3QgdD1l -LnJvb3Q7dC5raW5kPW4sdC5zdWJsYW5ndWFnZT0hMCx0aGlzLmFkZCh0KX10b0hUTUwoKXtyZXR1cm4g -bmV3IGwodGhpcyx0aGlzLm9wdGlvbnMpLnZhbHVlKCl9ZmluYWxpemUoKXtyZXR1cm4hMH19ZnVuY3Rp -b24gZChlKXtyZXR1cm4gZT8ic3RyaW5nIj09dHlwZW9mIGU/ZTplLnNvdXJjZTpudWxsfWNvbnN0IGc9 -IigtPykoXFxiMFt4WF1bYS1mQS1GMC05XSt8KFxcYlxcZCsoXFwuXFxkKik/fFxcLlxcZCspKFtlRV1b -LStdP1xcZCspPykiLGg9e2JlZ2luOiJcXFxcW1xcc1xcU10iLHJlbGV2YW5jZTowfSxmPXtjbGFzc05h -bWU6InN0cmluZyIsYmVnaW46IiciLGVuZDoiJyIsaWxsZWdhbDoiXFxuIixjb250YWluczpbaF19LHA9 -e2NsYXNzTmFtZToic3RyaW5nIixiZWdpbjonIicsZW5kOiciJyxpbGxlZ2FsOiJcXG4iLGNvbnRhaW5z -OltoXX0sYj17YmVnaW46L1xiKGF8YW58dGhlfGFyZXxJJ218aXNuJ3R8ZG9uJ3R8ZG9lc24ndHx3b24n -dHxidXR8anVzdHxzaG91bGR8cHJldHR5fHNpbXBseXxlbm91Z2h8Z29ubmF8Z29pbmd8d3RmfHNvfHN1 -Y2h8d2lsbHx5b3V8eW91cnx0aGV5fGxpa2V8bW9yZSlcYi99LG09ZnVuY3Rpb24oZSxuLHQ9e30pe3Zh -ciBhPXIoe2NsYXNzTmFtZToiY29tbWVudCIsYmVnaW46ZSxlbmQ6bixjb250YWluczpbXX0sdCk7cmV0 -dXJuIGEuY29udGFpbnMucHVzaChiKSxhLmNvbnRhaW5zLnB1c2goe2NsYXNzTmFtZToiZG9jdGFnIixi -ZWdpbjoiKD86VE9ET3xGSVhNRXxOT1RFfEJVR3xPUFRJTUlaRXxIQUNLfFhYWCk6IixyZWxldmFuY2U6 -MH0pLGF9LHY9bSgiLy8iLCIkIikseD1tKCIvXFwqIiwiXFwqLyIpLEU9bSgiIyIsIiQiKTt2YXIgXz1P -YmplY3QuZnJlZXplKHtfX3Byb3RvX186bnVsbCxJREVOVF9SRToiW2EtekEtWl1cXHcqIixVTkRFUlND -T1JFX0lERU5UX1JFOiJbYS16QS1aX11cXHcqIixOVU1CRVJfUkU6IlxcYlxcZCsoXFwuXFxkKyk/IixD -X05VTUJFUl9SRTpnLEJJTkFSWV9OVU1CRVJfUkU6IlxcYigwYlswMV0rKSIsUkVfU1RBUlRFUlNfUkU6 -IiF8IT18IT09fCV8JT18JnwmJnwmPXxcXCp8XFwqPXxcXCt8XFwrPXwsfC18LT18Lz18L3w6fDt8PDx8 -PDw9fDw9fDx8PT09fD09fD18Pj4+PXw+Pj18Pj18Pj4+fD4+fD58XFw/fFxcW3xcXHt8XFwofFxcXnxc -XF49fFxcfHxcXHw9fFxcfFxcfHx+IixTSEVCQU5HOihlPXt9KT0+e2NvbnN0IG49L14jIVsgXSpcLy87 -cmV0dXJuIGUuYmluYXJ5JiYoZS5iZWdpbj1mdW5jdGlvbiguLi5lKXtyZXR1cm4gZS5tYXAoZT0+ZChl -KSkuam9pbigiIil9KG4sLy4qXGIvLGUuYmluYXJ5LC9cYi4qLykpLHIoe2NsYXNzTmFtZToibWV0YSIs -YmVnaW46bixlbmQ6LyQvLHJlbGV2YW5jZTowLCJvbjpiZWdpbiI6KGUsbik9PnswIT09ZS5pbmRleCYm -bi5pZ25vcmVNYXRjaCgpfX0sZSl9LEJBQ0tTTEFTSF9FU0NBUEU6aCxBUE9TX1NUUklOR19NT0RFOmYs -UVVPVEVfU1RSSU5HX01PREU6cCxQSFJBU0FMX1dPUkRTX01PREU6YixDT01NRU5UOm0sQ19MSU5FX0NP -TU1FTlRfTU9ERTp2LENfQkxPQ0tfQ09NTUVOVF9NT0RFOngsSEFTSF9DT01NRU5UX01PREU6RSxOVU1C -RVJfTU9ERTp7Y2xhc3NOYW1lOiJudW1iZXIiLGJlZ2luOiJcXGJcXGQrKFxcLlxcZCspPyIscmVsZXZh -bmNlOjB9LENfTlVNQkVSX01PREU6e2NsYXNzTmFtZToibnVtYmVyIixiZWdpbjpnLHJlbGV2YW5jZTow -fSxCSU5BUllfTlVNQkVSX01PREU6e2NsYXNzTmFtZToibnVtYmVyIixiZWdpbjoiXFxiKDBiWzAxXSsp -IixyZWxldmFuY2U6MH0sQ1NTX05VTUJFUl9NT0RFOntjbGFzc05hbWU6Im51bWJlciIsYmVnaW46Ilxc -YlxcZCsoXFwuXFxkKyk/KCV8ZW18ZXh8Y2h8cmVtfHZ3fHZofHZtaW58dm1heHxjbXxtbXxpbnxwdHxw -Y3xweHxkZWd8Z3JhZHxyYWR8dHVybnxzfG1zfEh6fGtIenxkcGl8ZHBjbXxkcHB4KT8iLHJlbGV2YW5j -ZTowfSxSRUdFWFBfTU9ERTp7YmVnaW46Lyg/PVwvW14vXG5dKlwvKS8sY29udGFpbnM6W3tjbGFzc05h -bWU6InJlZ2V4cCIsYmVnaW46L1wvLyxlbmQ6L1wvW2dpbXV5XSovLGlsbGVnYWw6L1xuLyxjb250YWlu -czpbaCx7YmVnaW46L1xbLyxlbmQ6L1xdLyxyZWxldmFuY2U6MCxjb250YWluczpbaF19XX1dfSxUSVRM -RV9NT0RFOntjbGFzc05hbWU6InRpdGxlIixiZWdpbjoiW2EtekEtWl1cXHcqIixyZWxldmFuY2U6MH0s -VU5ERVJTQ09SRV9USVRMRV9NT0RFOntjbGFzc05hbWU6InRpdGxlIixiZWdpbjoiW2EtekEtWl9dXFx3 -KiIscmVsZXZhbmNlOjB9LE1FVEhPRF9HVUFSRDp7YmVnaW46IlxcLlxccypbYS16QS1aX11cXHcqIixy -ZWxldmFuY2U6MH0sRU5EX1NBTUVfQVNfQkVHSU46ZnVuY3Rpb24oZSl7cmV0dXJuIE9iamVjdC5hc3Np -Z24oZSx7Im9uOmJlZ2luIjooZSxuKT0+e24uZGF0YS5fYmVnaW5NYXRjaD1lWzFdfSwib246ZW5kIjoo -ZSxuKT0+e24uZGF0YS5fYmVnaW5NYXRjaCE9PWVbMV0mJm4uaWdub3JlTWF0Y2goKX19KX19KSxOPSJv -ZiBhbmQgZm9yIGluIG5vdCBvciBpZiB0aGVuIi5zcGxpdCgiICIpO2Z1bmN0aW9uIHcoZSxuKXtyZXR1 -cm4gbj8rbjpmdW5jdGlvbihlKXtyZXR1cm4gTi5pbmNsdWRlcyhlLnRvTG93ZXJDYXNlKCkpfShlKT8w -OjF9Y29uc3QgUj10LHk9cix7bm9kZVN0cmVhbTprLG1lcmdlU3RyZWFtczpPfT1pLE09U3ltYm9sKCJu -b21hdGNoIik7cmV0dXJuIGZ1bmN0aW9uKHQpe3ZhciBhPVtdLGk9e30scz17fSxvPVtdLGw9ITAsYz0v -KF4oPFtePl0rPnxcdHwpK3xcbikvZ20sZz0iQ291bGQgbm90IGZpbmQgdGhlIGxhbmd1YWdlICd7fScs -IGRpZCB5b3UgZm9yZ2V0IHRvIGxvYWQvaW5jbHVkZSBhIGxhbmd1YWdlIG1vZHVsZT8iO2NvbnN0IGg9 -e2Rpc2FibGVBdXRvZGV0ZWN0OiEwLG5hbWU6IlBsYWluIHRleHQiLGNvbnRhaW5zOltdfTt2YXIgZj17 -bm9IaWdobGlnaHRSZTovXihuby0/aGlnaGxpZ2h0KSQvaSxsYW5ndWFnZURldGVjdFJlOi9cYmxhbmco -Pzp1YWdlKT8tKFtcdy1dKylcYi9pLGNsYXNzUHJlZml4OiJobGpzLSIsdGFiUmVwbGFjZTpudWxsLHVz -ZUJSOiExLGxhbmd1YWdlczpudWxsLF9fZW1pdHRlcjp1fTtmdW5jdGlvbiBwKGUpe3JldHVybiBmLm5v -SGlnaGxpZ2h0UmUudGVzdChlKX1mdW5jdGlvbiBiKGUsbix0LHIpe3ZhciBhPXtjb2RlOm4sbGFuZ3Vh -Z2U6ZX07UygiYmVmb3JlOmhpZ2hsaWdodCIsYSk7dmFyIGk9YS5yZXN1bHQ/YS5yZXN1bHQ6bShhLmxh -bmd1YWdlLGEuY29kZSx0LHIpO3JldHVybiBpLmNvZGU9YS5jb2RlLFMoImFmdGVyOmhpZ2hsaWdodCIs -aSksaX1mdW5jdGlvbiBtKGUsdCxhLHMpe3ZhciBvPXQ7ZnVuY3Rpb24gYyhlLG4pe3ZhciB0PUUuY2Fz -ZV9pbnNlbnNpdGl2ZT9uWzBdLnRvTG93ZXJDYXNlKCk6blswXTtyZXR1cm4gT2JqZWN0LnByb3RvdHlw -ZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGUua2V5d29yZHMsdCkmJmUua2V5d29yZHNbdF19ZnVuY3Rpb24g -dSgpe251bGwhPXkuc3ViTGFuZ3VhZ2U/ZnVuY3Rpb24oKXtpZigiIiE9PUEpe3ZhciBlPW51bGw7aWYo -InN0cmluZyI9PXR5cGVvZiB5LnN1Ykxhbmd1YWdlKXtpZighaVt5LnN1Ykxhbmd1YWdlXSlyZXR1cm4g -dm9pZCBPLmFkZFRleHQoQSk7ZT1tKHkuc3ViTGFuZ3VhZ2UsQSwhMCxrW3kuc3ViTGFuZ3VhZ2VdKSxr -W3kuc3ViTGFuZ3VhZ2VdPWUudG9wfWVsc2UgZT12KEEseS5zdWJMYW5ndWFnZS5sZW5ndGg/eS5zdWJM -YW5ndWFnZTpudWxsKTt5LnJlbGV2YW5jZT4wJiYoSSs9ZS5yZWxldmFuY2UpLE8uYWRkU3VibGFuZ3Vh -Z2UoZS5lbWl0dGVyLGUubGFuZ3VhZ2UpfX0oKTpmdW5jdGlvbigpe2lmKCF5LmtleXdvcmRzKXJldHVy -biB2b2lkIE8uYWRkVGV4dChBKTtsZXQgZT0wO3kua2V5d29yZFBhdHRlcm5SZS5sYXN0SW5kZXg9MDts -ZXQgbj15LmtleXdvcmRQYXR0ZXJuUmUuZXhlYyhBKSx0PSIiO2Zvcig7bjspe3QrPUEuc3Vic3RyaW5n -KGUsbi5pbmRleCk7Y29uc3Qgcj1jKHksbik7aWYocil7Y29uc3RbZSxhXT1yO08uYWRkVGV4dCh0KSx0 -PSIiLEkrPWEsTy5hZGRLZXl3b3JkKG5bMF0sZSl9ZWxzZSB0Kz1uWzBdO2U9eS5rZXl3b3JkUGF0dGVy -blJlLmxhc3RJbmRleCxuPXkua2V5d29yZFBhdHRlcm5SZS5leGVjKEEpfXQrPUEuc3Vic3RyKGUpLE8u -YWRkVGV4dCh0KX0oKSxBPSIifWZ1bmN0aW9uIGgoZSl7cmV0dXJuIGUuY2xhc3NOYW1lJiZPLm9wZW5O -b2RlKGUuY2xhc3NOYW1lKSx5PU9iamVjdC5jcmVhdGUoZSx7cGFyZW50Ont2YWx1ZTp5fX0pfWZ1bmN0 -aW9uIHAoZSl7cmV0dXJuIDA9PT15Lm1hdGNoZXIucmVnZXhJbmRleD8oQSs9ZVswXSwxKTooTD0hMCww -KX12YXIgYj17fTtmdW5jdGlvbiB4KHQscil7dmFyIGk9ciYmclswXTtpZihBKz10LG51bGw9PWkpcmV0 -dXJuIHUoKSwwO2lmKCJiZWdpbiI9PT1iLnR5cGUmJiJlbmQiPT09ci50eXBlJiZiLmluZGV4PT09ci5p -bmRleCYmIiI9PT1pKXtpZihBKz1vLnNsaWNlKHIuaW5kZXgsci5pbmRleCsxKSwhbCl7Y29uc3Qgbj1F -cnJvcigiMCB3aWR0aCBtYXRjaCByZWdleCIpO3Rocm93IG4ubGFuZ3VhZ2VOYW1lPWUsbi5iYWRSdWxl -PWIucnVsZSxufXJldHVybiAxfWlmKGI9ciwiYmVnaW4iPT09ci50eXBlKXJldHVybiBmdW5jdGlvbihl -KXt2YXIgdD1lWzBdLHI9ZS5ydWxlO2NvbnN0IGE9bmV3IG4ociksaT1bci5fX2JlZm9yZUJlZ2luLHJb -Im9uOmJlZ2luIl1dO2Zvcihjb25zdCBuIG9mIGkpaWYobiYmKG4oZSxhKSxhLmlnbm9yZSkpcmV0dXJu -IHAodCk7cmV0dXJuIHImJnIuZW5kU2FtZUFzQmVnaW4mJihyLmVuZFJlPVJlZ0V4cCh0LnJlcGxhY2Uo -L1stL1xcXiQqKz8uKCl8W1xde31dL2csIlxcJCYiKSwibSIpKSxyLnNraXA/QSs9dDooci5leGNsdWRl -QmVnaW4mJihBKz10KSx1KCksci5yZXR1cm5CZWdpbnx8ci5leGNsdWRlQmVnaW58fChBPXQpKSxoKHIp -LHIucmV0dXJuQmVnaW4/MDp0Lmxlbmd0aH0ocik7aWYoImlsbGVnYWwiPT09ci50eXBlJiYhYSl7Y29u -c3QgZT1FcnJvcignSWxsZWdhbCBsZXhlbWUgIicraSsnIiBmb3IgbW9kZSAiJysoeS5jbGFzc05hbWV8 -fCI8dW5uYW1lZD4iKSsnIicpO3Rocm93IGUubW9kZT15LGV9aWYoImVuZCI9PT1yLnR5cGUpe3ZhciBz -PWZ1bmN0aW9uKGUpe3ZhciB0PWVbMF0scj1vLnN1YnN0cihlLmluZGV4KSxhPWZ1bmN0aW9uIGUodCxy -LGEpe2xldCBpPWZ1bmN0aW9uKGUsbil7dmFyIHQ9ZSYmZS5leGVjKG4pO3JldHVybiB0JiYwPT09dC5p -bmRleH0odC5lbmRSZSxhKTtpZihpKXtpZih0WyJvbjplbmQiXSl7Y29uc3QgZT1uZXcgbih0KTt0WyJv -bjplbmQiXShyLGUpLGUuaWdub3JlJiYoaT0hMSl9aWYoaSl7Zm9yKDt0LmVuZHNQYXJlbnQmJnQucGFy -ZW50Oyl0PXQucGFyZW50O3JldHVybiB0fX1pZih0LmVuZHNXaXRoUGFyZW50KXJldHVybiBlKHQucGFy -ZW50LHIsYSl9KHksZSxyKTtpZighYSlyZXR1cm4gTTt2YXIgaT15O2kuc2tpcD9BKz10OihpLnJldHVy -bkVuZHx8aS5leGNsdWRlRW5kfHwoQSs9dCksdSgpLGkuZXhjbHVkZUVuZCYmKEE9dCkpO2Rve3kuY2xh -c3NOYW1lJiZPLmNsb3NlTm9kZSgpLHkuc2tpcHx8eS5zdWJMYW5ndWFnZXx8KEkrPXkucmVsZXZhbmNl -KSx5PXkucGFyZW50fXdoaWxlKHkhPT1hLnBhcmVudCk7cmV0dXJuIGEuc3RhcnRzJiYoYS5lbmRTYW1l -QXNCZWdpbiYmKGEuc3RhcnRzLmVuZFJlPWEuZW5kUmUpLGgoYS5zdGFydHMpKSxpLnJldHVybkVuZD8w -OnQubGVuZ3RofShyKTtpZihzIT09TSlyZXR1cm4gc31pZigiaWxsZWdhbCI9PT1yLnR5cGUmJiIiPT09 -aSlyZXR1cm4gMTtpZihCPjFlNSYmQj4zKnIuaW5kZXgpdGhyb3cgRXJyb3IoInBvdGVudGlhbCBpbmZp -bml0ZSBsb29wLCB3YXkgbW9yZSBpdGVyYXRpb25zIHRoYW4gbWF0Y2hlcyIpO3JldHVybiBBKz1pLGku -bGVuZ3RofXZhciBFPVQoZSk7aWYoIUUpdGhyb3cgY29uc29sZS5lcnJvcihnLnJlcGxhY2UoInt9Iixl -KSksRXJyb3IoJ1Vua25vd24gbGFuZ3VhZ2U6ICInK2UrJyInKTt2YXIgXz1mdW5jdGlvbihlKXtmdW5j -dGlvbiBuKG4sdCl7cmV0dXJuIFJlZ0V4cChkKG4pLCJtIisoZS5jYXNlX2luc2Vuc2l0aXZlPyJpIjoi -IikrKHQ/ImciOiIiKSl9Y2xhc3MgdHtjb25zdHJ1Y3Rvcigpe3RoaXMubWF0Y2hJbmRleGVzPXt9LHRo -aXMucmVnZXhlcz1bXSx0aGlzLm1hdGNoQXQ9MSx0aGlzLnBvc2l0aW9uPTB9YWRkUnVsZShlLG4pe24u -cG9zaXRpb249dGhpcy5wb3NpdGlvbisrLHRoaXMubWF0Y2hJbmRleGVzW3RoaXMubWF0Y2hBdF09bix0 -aGlzLnJlZ2V4ZXMucHVzaChbbixlXSksdGhpcy5tYXRjaEF0Kz1mdW5jdGlvbihlKXtyZXR1cm4gUmVn -RXhwKGUudG9TdHJpbmcoKSsifCIpLmV4ZWMoIiIpLmxlbmd0aC0xfShlKSsxfWNvbXBpbGUoKXswPT09 -dGhpcy5yZWdleGVzLmxlbmd0aCYmKHRoaXMuZXhlYz0oKT0+bnVsbCk7Y29uc3QgZT10aGlzLnJlZ2V4 -ZXMubWFwKGU9PmVbMV0pO3RoaXMubWF0Y2hlclJlPW4oZnVuY3Rpb24oZSxuPSJ8Iil7Zm9yKHZhciB0 -PS9cWyg/OlteXFxcXV18XFwuKSpcXXxcKFw/P3xcXChbMS05XVswLTldKil8XFwuLyxyPTAsYT0iIixp -PTA7aTxlLmxlbmd0aDtpKyspe3ZhciBzPXIrPTEsbz1kKGVbaV0pO2ZvcihpPjAmJihhKz1uKSxhKz0i -KCI7by5sZW5ndGg+MDspe3ZhciBsPXQuZXhlYyhvKTtpZihudWxsPT1sKXthKz1vO2JyZWFrfWErPW8u -c3Vic3RyaW5nKDAsbC5pbmRleCksbz1vLnN1YnN0cmluZyhsLmluZGV4K2xbMF0ubGVuZ3RoKSwiXFwi -PT09bFswXVswXSYmbFsxXT9hKz0iXFwiKygrbFsxXStzKTooYSs9bFswXSwiKCI9PT1sWzBdJiZyKysp -fWErPSIpIn1yZXR1cm4gYX0oZSksITApLHRoaXMubGFzdEluZGV4PTB9ZXhlYyhlKXt0aGlzLm1hdGNo -ZXJSZS5sYXN0SW5kZXg9dGhpcy5sYXN0SW5kZXg7Y29uc3Qgbj10aGlzLm1hdGNoZXJSZS5leGVjKGUp -O2lmKCFuKXJldHVybiBudWxsO2NvbnN0IHQ9bi5maW5kSW5kZXgoKGUsbik9Pm4+MCYmdm9pZCAwIT09 -ZSkscj10aGlzLm1hdGNoSW5kZXhlc1t0XTtyZXR1cm4gbi5zcGxpY2UoMCx0KSxPYmplY3QuYXNzaWdu -KG4scil9fWNsYXNzIGF7Y29uc3RydWN0b3IoKXt0aGlzLnJ1bGVzPVtdLHRoaXMubXVsdGlSZWdleGVz -PVtdLHRoaXMuY291bnQ9MCx0aGlzLmxhc3RJbmRleD0wLHRoaXMucmVnZXhJbmRleD0wfWdldE1hdGNo -ZXIoZSl7aWYodGhpcy5tdWx0aVJlZ2V4ZXNbZV0pcmV0dXJuIHRoaXMubXVsdGlSZWdleGVzW2VdO2Nv -bnN0IG49bmV3IHQ7cmV0dXJuIHRoaXMucnVsZXMuc2xpY2UoZSkuZm9yRWFjaCgoW2UsdF0pPT5uLmFk -ZFJ1bGUoZSx0KSksbi5jb21waWxlKCksdGhpcy5tdWx0aVJlZ2V4ZXNbZV09bixufWNvbnNpZGVyQWxs -KCl7dGhpcy5yZWdleEluZGV4PTB9YWRkUnVsZShlLG4pe3RoaXMucnVsZXMucHVzaChbZSxuXSksImJl -Z2luIj09PW4udHlwZSYmdGhpcy5jb3VudCsrfWV4ZWMoZSl7Y29uc3Qgbj10aGlzLmdldE1hdGNoZXIo -dGhpcy5yZWdleEluZGV4KTtuLmxhc3RJbmRleD10aGlzLmxhc3RJbmRleDtjb25zdCB0PW4uZXhlYyhl -KTtyZXR1cm4gdCYmKHRoaXMucmVnZXhJbmRleCs9dC5wb3NpdGlvbisxLHRoaXMucmVnZXhJbmRleD09 -PXRoaXMuY291bnQmJih0aGlzLnJlZ2V4SW5kZXg9MCkpLHR9fWZ1bmN0aW9uIGkoZSxuKXtjb25zdCB0 -PWUuaW5wdXRbZS5pbmRleC0xXSxyPWUuaW5wdXRbZS5pbmRleCtlWzBdLmxlbmd0aF07Ii4iIT09dCYm -Ii4iIT09cnx8bi5pZ25vcmVNYXRjaCgpfWlmKGUuY29udGFpbnMmJmUuY29udGFpbnMuaW5jbHVkZXMo -InNlbGYiKSl0aHJvdyBFcnJvcigiRVJSOiBjb250YWlucyBgc2VsZmAgaXMgbm90IHN1cHBvcnRlZCBh -dCB0aGUgdG9wLWxldmVsIG9mIGEgbGFuZ3VhZ2UuICBTZWUgZG9jdW1lbnRhdGlvbi4iKTtyZXR1cm4g -ZnVuY3Rpb24gdChzLG8pe2NvbnN0IGw9cztpZihzLmNvbXBpbGVkKXJldHVybiBsO3MuY29tcGlsZWQ9 -ITAscy5fX2JlZm9yZUJlZ2luPW51bGwscy5rZXl3b3Jkcz1zLmtleXdvcmRzfHxzLmJlZ2luS2V5d29y -ZHM7bGV0IGM9bnVsbDtpZigib2JqZWN0Ij09dHlwZW9mIHMua2V5d29yZHMmJihjPXMua2V5d29yZHMu -JHBhdHRlcm4sZGVsZXRlIHMua2V5d29yZHMuJHBhdHRlcm4pLHMua2V5d29yZHMmJihzLmtleXdvcmRz -PWZ1bmN0aW9uKGUsbil7dmFyIHQ9e307cmV0dXJuInN0cmluZyI9PXR5cGVvZiBlP3IoImtleXdvcmQi -LGUpOk9iamVjdC5rZXlzKGUpLmZvckVhY2goKGZ1bmN0aW9uKG4pe3IobixlW25dKX0pKSx0O2Z1bmN0 -aW9uIHIoZSxyKXtuJiYocj1yLnRvTG93ZXJDYXNlKCkpLHIuc3BsaXQoIiAiKS5mb3JFYWNoKChmdW5j -dGlvbihuKXt2YXIgcj1uLnNwbGl0KCJ8Iik7dFtyWzBdXT1bZSx3KHJbMF0sclsxXSldfSkpfX0ocy5r -ZXl3b3JkcyxlLmNhc2VfaW5zZW5zaXRpdmUpKSxzLmxleGVtZXMmJmMpdGhyb3cgRXJyb3IoIkVSUjog -UHJlZmVyIGBrZXl3b3Jkcy4kcGF0dGVybmAgdG8gYG1vZGUubGV4ZW1lc2AsIEJPVEggYXJlIG5vdCBh -bGxvd2VkLiAoc2VlIG1vZGUgcmVmZXJlbmNlKSAiKTtyZXR1cm4gbC5rZXl3b3JkUGF0dGVyblJlPW4o -cy5sZXhlbWVzfHxjfHwvXHcrLywhMCksbyYmKHMuYmVnaW5LZXl3b3JkcyYmKHMuYmVnaW49IlxcYigi -K3MuYmVnaW5LZXl3b3Jkcy5zcGxpdCgiICIpLmpvaW4oInwiKSsiKSg/PVxcYnxcXHMpIixzLl9fYmVm -b3JlQmVnaW49aSkscy5iZWdpbnx8KHMuYmVnaW49L1xCfFxiLyksbC5iZWdpblJlPW4ocy5iZWdpbiks -cy5lbmRTYW1lQXNCZWdpbiYmKHMuZW5kPXMuYmVnaW4pLHMuZW5kfHxzLmVuZHNXaXRoUGFyZW50fHwo -cy5lbmQ9L1xCfFxiLykscy5lbmQmJihsLmVuZFJlPW4ocy5lbmQpKSxsLnRlcm1pbmF0b3JfZW5kPWQo -cy5lbmQpfHwiIixzLmVuZHNXaXRoUGFyZW50JiZvLnRlcm1pbmF0b3JfZW5kJiYobC50ZXJtaW5hdG9y -X2VuZCs9KHMuZW5kPyJ8IjoiIikrby50ZXJtaW5hdG9yX2VuZCkpLHMuaWxsZWdhbCYmKGwuaWxsZWdh -bFJlPW4ocy5pbGxlZ2FsKSksdm9pZCAwPT09cy5yZWxldmFuY2UmJihzLnJlbGV2YW5jZT0xKSxzLmNv -bnRhaW5zfHwocy5jb250YWlucz1bXSkscy5jb250YWlucz1bXS5jb25jYXQoLi4ucy5jb250YWlucy5t -YXAoKGZ1bmN0aW9uKGUpe3JldHVybiBmdW5jdGlvbihlKXtyZXR1cm4gZS52YXJpYW50cyYmIWUuY2Fj -aGVkX3ZhcmlhbnRzJiYoZS5jYWNoZWRfdmFyaWFudHM9ZS52YXJpYW50cy5tYXAoKGZ1bmN0aW9uKG4p -e3JldHVybiByKGUse3ZhcmlhbnRzOm51bGx9LG4pfSkpKSxlLmNhY2hlZF92YXJpYW50cz9lLmNhY2hl -ZF92YXJpYW50czpmdW5jdGlvbiBlKG4pe3JldHVybiEhbiYmKG4uZW5kc1dpdGhQYXJlbnR8fGUobi5z -dGFydHMpKX0oZSk/cihlLHtzdGFydHM6ZS5zdGFydHM/cihlLnN0YXJ0cyk6bnVsbH0pOk9iamVjdC5p -c0Zyb3plbihlKT9yKGUpOmV9KCJzZWxmIj09PWU/czplKX0pKSkscy5jb250YWlucy5mb3JFYWNoKChm -dW5jdGlvbihlKXt0KGUsbCl9KSkscy5zdGFydHMmJnQocy5zdGFydHMsbyksbC5tYXRjaGVyPWZ1bmN0 -aW9uKGUpe2NvbnN0IG49bmV3IGE7cmV0dXJuIGUuY29udGFpbnMuZm9yRWFjaChlPT5uLmFkZFJ1bGUo -ZS5iZWdpbix7cnVsZTplLHR5cGU6ImJlZ2luIn0pKSxlLnRlcm1pbmF0b3JfZW5kJiZuLmFkZFJ1bGUo -ZS50ZXJtaW5hdG9yX2VuZCx7dHlwZToiZW5kIn0pLGUuaWxsZWdhbCYmbi5hZGRSdWxlKGUuaWxsZWdh -bCx7dHlwZToiaWxsZWdhbCJ9KSxufShsKSxsfShlKX0oRSksTj0iIix5PXN8fF8saz17fSxPPW5ldyBm -Ll9fZW1pdHRlcihmKTshZnVuY3Rpb24oKXtmb3IodmFyIGU9W10sbj15O24hPT1FO249bi5wYXJlbnQp -bi5jbGFzc05hbWUmJmUudW5zaGlmdChuLmNsYXNzTmFtZSk7ZS5mb3JFYWNoKGU9Pk8ub3Blbk5vZGUo -ZSkpfSgpO3ZhciBBPSIiLEk9MCxTPTAsQj0wLEw9ITE7dHJ5e2Zvcih5Lm1hdGNoZXIuY29uc2lkZXJB -bGwoKTs7KXtCKyssTD9MPSExOih5Lm1hdGNoZXIubGFzdEluZGV4PVMseS5tYXRjaGVyLmNvbnNpZGVy -QWxsKCkpO2NvbnN0IGU9eS5tYXRjaGVyLmV4ZWMobyk7aWYoIWUpYnJlYWs7Y29uc3Qgbj14KG8uc3Vi -c3RyaW5nKFMsZS5pbmRleCksZSk7Uz1lLmluZGV4K259cmV0dXJuIHgoby5zdWJzdHIoUykpLE8uY2xv -c2VBbGxOb2RlcygpLE8uZmluYWxpemUoKSxOPU8udG9IVE1MKCkse3JlbGV2YW5jZTpJLHZhbHVlOk4s -bGFuZ3VhZ2U6ZSxpbGxlZ2FsOiExLGVtaXR0ZXI6Tyx0b3A6eX19Y2F0Y2gobil7aWYobi5tZXNzYWdl -JiZuLm1lc3NhZ2UuaW5jbHVkZXMoIklsbGVnYWwiKSlyZXR1cm57aWxsZWdhbDohMCxpbGxlZ2FsQnk6 -e21zZzpuLm1lc3NhZ2UsY29udGV4dDpvLnNsaWNlKFMtMTAwLFMrMTAwKSxtb2RlOm4ubW9kZX0sc29m -YXI6TixyZWxldmFuY2U6MCx2YWx1ZTpSKG8pLGVtaXR0ZXI6T307aWYobClyZXR1cm57aWxsZWdhbDoh -MSxyZWxldmFuY2U6MCx2YWx1ZTpSKG8pLGVtaXR0ZXI6TyxsYW5ndWFnZTplLHRvcDp5LGVycm9yUmFp -c2VkOm59O3Rocm93IG59fWZ1bmN0aW9uIHYoZSxuKXtuPW58fGYubGFuZ3VhZ2VzfHxPYmplY3Qua2V5 -cyhpKTt2YXIgdD1mdW5jdGlvbihlKXtjb25zdCBuPXtyZWxldmFuY2U6MCxlbWl0dGVyOm5ldyBmLl9f -ZW1pdHRlcihmKSx2YWx1ZTpSKGUpLGlsbGVnYWw6ITEsdG9wOmh9O3JldHVybiBuLmVtaXR0ZXIuYWRk -VGV4dChlKSxufShlKSxyPXQ7cmV0dXJuIG4uZmlsdGVyKFQpLmZpbHRlcihJKS5mb3JFYWNoKChmdW5j -dGlvbihuKXt2YXIgYT1tKG4sZSwhMSk7YS5sYW5ndWFnZT1uLGEucmVsZXZhbmNlPnIucmVsZXZhbmNl -JiYocj1hKSxhLnJlbGV2YW5jZT50LnJlbGV2YW5jZSYmKHI9dCx0PWEpfSkpLHIubGFuZ3VhZ2UmJih0 -LnNlY29uZF9iZXN0PXIpLHR9ZnVuY3Rpb24geChlKXtyZXR1cm4gZi50YWJSZXBsYWNlfHxmLnVzZUJS -P2UucmVwbGFjZShjLGU9PiJcbiI9PT1lP2YudXNlQlI/Ijxicj4iOmU6Zi50YWJSZXBsYWNlP2UucmVw -bGFjZSgvXHQvZyxmLnRhYlJlcGxhY2UpOmUpOmV9ZnVuY3Rpb24gRShlKXtsZXQgbj1udWxsO2NvbnN0 -IHQ9ZnVuY3Rpb24oZSl7dmFyIG49ZS5jbGFzc05hbWUrIiAiO24rPWUucGFyZW50Tm9kZT9lLnBhcmVu -dE5vZGUuY2xhc3NOYW1lOiIiO2NvbnN0IHQ9Zi5sYW5ndWFnZURldGVjdFJlLmV4ZWMobik7aWYodCl7 -dmFyIHI9VCh0WzFdKTtyZXR1cm4gcnx8KGNvbnNvbGUud2FybihnLnJlcGxhY2UoInt9Iix0WzFdKSks -Y29uc29sZS53YXJuKCJGYWxsaW5nIGJhY2sgdG8gbm8taGlnaGxpZ2h0IG1vZGUgZm9yIHRoaXMgYmxv -Y2suIixlKSkscj90WzFdOiJuby1oaWdobGlnaHQifXJldHVybiBuLnNwbGl0KC9ccysvKS5maW5kKGU9 -PnAoZSl8fFQoZSkpfShlKTtpZihwKHQpKXJldHVybjtTKCJiZWZvcmU6aGlnaGxpZ2h0QmxvY2siLHti -bG9jazplLGxhbmd1YWdlOnR9KSxmLnVzZUJSPyhuPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIp -KS5pbm5lckhUTUw9ZS5pbm5lckhUTUwucmVwbGFjZSgvXG4vZywiIikucmVwbGFjZSgvPGJyWyAvXSo+ -L2csIlxuIik6bj1lO2NvbnN0IHI9bi50ZXh0Q29udGVudCxhPXQ/Yih0LHIsITApOnYociksaT1rKG4p -O2lmKGkubGVuZ3RoKXtjb25zdCBlPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpO2UuaW5uZXJI -VE1MPWEudmFsdWUsYS52YWx1ZT1PKGksayhlKSxyKX1hLnZhbHVlPXgoYS52YWx1ZSksUygiYWZ0ZXI6 -aGlnaGxpZ2h0QmxvY2siLHtibG9jazplLHJlc3VsdDphfSksZS5pbm5lckhUTUw9YS52YWx1ZSxlLmNs -YXNzTmFtZT1mdW5jdGlvbihlLG4sdCl7dmFyIHI9bj9zW25dOnQsYT1bZS50cmltKCldO3JldHVybiBl -Lm1hdGNoKC9cYmhsanNcYi8pfHxhLnB1c2goImhsanMiKSxlLmluY2x1ZGVzKHIpfHxhLnB1c2gociks -YS5qb2luKCIgIikudHJpbSgpfShlLmNsYXNzTmFtZSx0LGEubGFuZ3VhZ2UpLGUucmVzdWx0PXtsYW5n -dWFnZTphLmxhbmd1YWdlLHJlOmEucmVsZXZhbmNlLHJlbGF2YW5jZTphLnJlbGV2YW5jZX0sYS5zZWNv -bmRfYmVzdCYmKGUuc2Vjb25kX2Jlc3Q9e2xhbmd1YWdlOmEuc2Vjb25kX2Jlc3QubGFuZ3VhZ2UscmU6 -YS5zZWNvbmRfYmVzdC5yZWxldmFuY2UscmVsYXZhbmNlOmEuc2Vjb25kX2Jlc3QucmVsZXZhbmNlfSl9 -Y29uc3QgTj0oKT0+e2lmKCFOLmNhbGxlZCl7Ti5jYWxsZWQ9ITA7dmFyIGU9ZG9jdW1lbnQucXVlcnlT -ZWxlY3RvckFsbCgicHJlIGNvZGUiKTthLmZvckVhY2guY2FsbChlLEUpfX07ZnVuY3Rpb24gVChlKXty -ZXR1cm4gZT0oZXx8IiIpLnRvTG93ZXJDYXNlKCksaVtlXXx8aVtzW2VdXX1mdW5jdGlvbiBBKGUse2xh -bmd1YWdlTmFtZTpufSl7InN0cmluZyI9PXR5cGVvZiBlJiYoZT1bZV0pLGUuZm9yRWFjaChlPT57c1tl -XT1ufSl9ZnVuY3Rpb24gSShlKXt2YXIgbj1UKGUpO3JldHVybiBuJiYhbi5kaXNhYmxlQXV0b2RldGVj -dH1mdW5jdGlvbiBTKGUsbil7dmFyIHQ9ZTtvLmZvckVhY2goKGZ1bmN0aW9uKGUpe2VbdF0mJmVbdF0o -bil9KSl9T2JqZWN0LmFzc2lnbih0LHtoaWdobGlnaHQ6YixoaWdobGlnaHRBdXRvOnYsZml4TWFya3Vw -OngsaGlnaGxpZ2h0QmxvY2s6RSxjb25maWd1cmU6ZnVuY3Rpb24oZSl7Zj15KGYsZSl9LGluaXRIaWdo -bGlnaHRpbmc6Tixpbml0SGlnaGxpZ2h0aW5nT25Mb2FkOmZ1bmN0aW9uKCl7d2luZG93LmFkZEV2ZW50 -TGlzdGVuZXIoIkRPTUNvbnRlbnRMb2FkZWQiLE4sITEpfSxyZWdpc3Rlckxhbmd1YWdlOmZ1bmN0aW9u -KGUsbil7dmFyIHI9bnVsbDt0cnl7cj1uKHQpfWNhdGNoKG4pe2lmKGNvbnNvbGUuZXJyb3IoIkxhbmd1 -YWdlIGRlZmluaXRpb24gZm9yICd7fScgY291bGQgbm90IGJlIHJlZ2lzdGVyZWQuIi5yZXBsYWNlKCJ7 -fSIsZSkpLCFsKXRocm93IG47Y29uc29sZS5lcnJvcihuKSxyPWh9ci5uYW1lfHwoci5uYW1lPWUpLGlb -ZV09cixyLnJhd0RlZmluaXRpb249bi5iaW5kKG51bGwsdCksci5hbGlhc2VzJiZBKHIuYWxpYXNlcyx7 -bGFuZ3VhZ2VOYW1lOmV9KX0sbGlzdExhbmd1YWdlczpmdW5jdGlvbigpe3JldHVybiBPYmplY3Qua2V5 -cyhpKX0sZ2V0TGFuZ3VhZ2U6VCxyZWdpc3RlckFsaWFzZXM6QSxyZXF1aXJlTGFuZ3VhZ2U6ZnVuY3Rp -b24oZSl7dmFyIG49VChlKTtpZihuKXJldHVybiBuO3Rocm93IEVycm9yKCJUaGUgJ3t9JyBsYW5ndWFn -ZSBpcyByZXF1aXJlZCwgYnV0IG5vdCBsb2FkZWQuIi5yZXBsYWNlKCJ7fSIsZSkpfSxhdXRvRGV0ZWN0 -aW9uOkksaW5oZXJpdDp5LGFkZFBsdWdpbjpmdW5jdGlvbihlKXtvLnB1c2goZSl9fSksdC5kZWJ1Z01v -ZGU9ZnVuY3Rpb24oKXtsPSExfSx0LnNhZmVNb2RlPWZ1bmN0aW9uKCl7bD0hMH0sdC52ZXJzaW9uU3Ry -aW5nPSIxMC4xLjAiO2Zvcihjb25zdCBuIGluIF8pIm9iamVjdCI9PXR5cGVvZiBfW25dJiZlKF9bbl0p -O3JldHVybiBPYmplY3QuYXNzaWduKHQsXyksdH0oe30pfSgpOyJvYmplY3QiPT10eXBlb2YgZXhwb3J0 -cyYmInVuZGVmaW5lZCIhPXR5cGVvZiBtb2R1bGUmJihtb2R1bGUuZXhwb3J0cz1obGpzKTtobGpzLnJl -Z2lzdGVyTGFuZ3VhZ2UoInhtbCIsZnVuY3Rpb24oKXsidXNlIHN0cmljdCI7cmV0dXJuIGZ1bmN0aW9u -KGUpe3ZhciBuPXtjbGFzc05hbWU6InN5bWJvbCIsYmVnaW46IiZbYS16XSs7fCYjWzAtOV0rO3wmI3hb -YS1mMC05XSs7In0sYT17YmVnaW46IlxccyIsY29udGFpbnM6W3tjbGFzc05hbWU6Im1ldGEta2V5d29y -ZCIsYmVnaW46IiM/W2Etel9dW2EtejEtOV8tXSsiLGlsbGVnYWw6IlxcbiJ9XX0scz1lLmluaGVyaXQo -YSx7YmVnaW46IlxcKCIsZW5kOiJcXCkifSksdD1lLmluaGVyaXQoZS5BUE9TX1NUUklOR19NT0RFLHtj -bGFzc05hbWU6Im1ldGEtc3RyaW5nIn0pLGk9ZS5pbmhlcml0KGUuUVVPVEVfU1RSSU5HX01PREUse2Ns -YXNzTmFtZToibWV0YS1zdHJpbmcifSksYz17ZW5kc1dpdGhQYXJlbnQ6ITAsaWxsZWdhbDovPC8scmVs -ZXZhbmNlOjAsY29udGFpbnM6W3tjbGFzc05hbWU6ImF0dHIiLGJlZ2luOiJbQS1aYS16MC05XFwuXzot -XSsiLHJlbGV2YW5jZTowfSx7YmVnaW46Lz1ccyovLHJlbGV2YW5jZTowLGNvbnRhaW5zOlt7Y2xhc3NO -YW1lOiJzdHJpbmciLGVuZHNQYXJlbnQ6ITAsdmFyaWFudHM6W3tiZWdpbjovIi8sZW5kOi8iLyxjb250 -YWluczpbbl19LHtiZWdpbjovJy8sZW5kOi8nLyxjb250YWluczpbbl19LHtiZWdpbjovW15ccyInPTw+ -YF0rL31dfV19XX07cmV0dXJue25hbWU6IkhUTUwsIFhNTCIsYWxpYXNlczpbImh0bWwiLCJ4aHRtbCIs -InJzcyIsImF0b20iLCJ4amIiLCJ4c2QiLCJ4c2wiLCJwbGlzdCIsIndzZiIsInN2ZyJdLGNhc2VfaW5z -ZW5zaXRpdmU6ITAsY29udGFpbnM6W3tjbGFzc05hbWU6Im1ldGEiLGJlZ2luOiI8IVthLXpdIixlbmQ6 -Ij4iLHJlbGV2YW5jZToxMCxjb250YWluczpbYSxpLHQscyx7YmVnaW46IlxcWyIsZW5kOiJcXF0iLGNv -bnRhaW5zOlt7Y2xhc3NOYW1lOiJtZXRhIixiZWdpbjoiPCFbYS16XSIsZW5kOiI+Iixjb250YWluczpb -YSxzLGksdF19XX1dfSxlLkNPTU1FTlQoIlx4M2MhLS0iLCItLVx4M2UiLHtyZWxldmFuY2U6MTB9KSx7 -YmVnaW46IjxcXCFcXFtDREFUQVxcWyIsZW5kOiJcXF1cXF0+IixyZWxldmFuY2U6MTB9LG4se2NsYXNz -TmFtZToibWV0YSIsYmVnaW46LzxcP3htbC8sZW5kOi9cPz4vLHJlbGV2YW5jZToxMH0se2NsYXNzTmFt -ZToidGFnIixiZWdpbjoiPHN0eWxlKD89XFxzfD4pIixlbmQ6Ij4iLGtleXdvcmRzOntuYW1lOiJzdHls -ZSJ9LGNvbnRhaW5zOltjXSxzdGFydHM6e2VuZDoiPC9zdHlsZT4iLHJldHVybkVuZDohMCxzdWJMYW5n -dWFnZTpbImNzcyIsInhtbCJdfX0se2NsYXNzTmFtZToidGFnIixiZWdpbjoiPHNjcmlwdCg/PVxcc3w+ -KSIsZW5kOiI+IixrZXl3b3Jkczp7bmFtZToic2NyaXB0In0sY29udGFpbnM6W2NdLHN0YXJ0czp7ZW5k -OiI8XC9zY3JpcHQ+IixyZXR1cm5FbmQ6ITAsc3ViTGFuZ3VhZ2U6WyJqYXZhc2NyaXB0IiwiaGFuZGxl -YmFycyIsInhtbCJdfX0se2NsYXNzTmFtZToidGFnIixiZWdpbjoiPC8/IixlbmQ6Ii8/PiIsY29udGFp -bnM6W3tjbGFzc05hbWU6Im5hbWUiLGJlZ2luOi9bXlwvPjxcc10rLyxyZWxldmFuY2U6MH0sY119XX19 -fSgpKTtobGpzLnJlZ2lzdGVyTGFuZ3VhZ2UoIm1hcmtkb3duIixmdW5jdGlvbigpeyJ1c2Ugc3RyaWN0 -IjtyZXR1cm4gZnVuY3Rpb24obil7Y29uc3QgZT17YmVnaW46IjwiLGVuZDoiPiIsc3ViTGFuZ3VhZ2U6 -InhtbCIscmVsZXZhbmNlOjB9LGE9e2JlZ2luOiJcXFsuKz9cXF1bXFwoXFxbXS4qP1tcXClcXF1dIixy -ZXR1cm5CZWdpbjohMCxjb250YWluczpbe2NsYXNzTmFtZToic3RyaW5nIixiZWdpbjoiXFxbIixlbmQ6 -IlxcXSIsZXhjbHVkZUJlZ2luOiEwLHJldHVybkVuZDohMCxyZWxldmFuY2U6MH0se2NsYXNzTmFtZToi -bGluayIsYmVnaW46IlxcXVxcKCIsZW5kOiJcXCkiLGV4Y2x1ZGVCZWdpbjohMCxleGNsdWRlRW5kOiEw -fSx7Y2xhc3NOYW1lOiJzeW1ib2wiLGJlZ2luOiJcXF1cXFsiLGVuZDoiXFxdIixleGNsdWRlQmVnaW46 -ITAsZXhjbHVkZUVuZDohMH1dLHJlbGV2YW5jZToxMH0saT17Y2xhc3NOYW1lOiJzdHJvbmciLGNvbnRh -aW5zOltdLHZhcmlhbnRzOlt7YmVnaW46L197Mn0vLGVuZDovX3syfS99LHtiZWdpbjovXCp7Mn0vLGVu -ZDovXCp7Mn0vfV19LHM9e2NsYXNzTmFtZToiZW1waGFzaXMiLGNvbnRhaW5zOltdLHZhcmlhbnRzOlt7 -YmVnaW46L1wqKD8hXCopLyxlbmQ6L1wqL30se2JlZ2luOi9fKD8hXykvLGVuZDovXy8scmVsZXZhbmNl -OjB9XX07aS5jb250YWlucy5wdXNoKHMpLHMuY29udGFpbnMucHVzaChpKTt2YXIgYz1bZSxhXTtyZXR1 -cm4gaS5jb250YWlucz1pLmNvbnRhaW5zLmNvbmNhdChjKSxzLmNvbnRhaW5zPXMuY29udGFpbnMuY29u -Y2F0KGMpLHtuYW1lOiJNYXJrZG93biIsYWxpYXNlczpbIm1kIiwibWtkb3duIiwibWtkIl0sY29udGFp -bnM6W3tjbGFzc05hbWU6InNlY3Rpb24iLHZhcmlhbnRzOlt7YmVnaW46Il4jezEsNn0iLGVuZDoiJCIs -Y29udGFpbnM6Yz1jLmNvbmNhdChpLHMpfSx7YmVnaW46Iig/PV4uKz9cXG5bPS1dezIsfSQpIixjb250 -YWluczpbe2JlZ2luOiJeWz0tXSokIn0se2JlZ2luOiJeIixlbmQ6IlxcbiIsY29udGFpbnM6Y31dfV19 -LGUse2NsYXNzTmFtZToiYnVsbGV0IixiZWdpbjoiXlsgXHRdKihbKistXXwoXFxkK1xcLikpKD89XFxz -KykiLGVuZDoiXFxzKyIsZXhjbHVkZUVuZDohMH0saSxzLHtjbGFzc05hbWU6InF1b3RlIixiZWdpbjoi -Xj5cXHMrIixjb250YWluczpjLGVuZDoiJCJ9LHtjbGFzc05hbWU6ImNvZGUiLHZhcmlhbnRzOlt7YmVn -aW46IihgezMsfSkoLnxcXG4pKj9cXDFgKlsgXSoifSx7YmVnaW46Iih+ezMsfSkoLnxcXG4pKj9cXDF+ -KlsgXSoifSx7YmVnaW46ImBgYCIsZW5kOiJgYGArWyBdKiQifSx7YmVnaW46In5+fiIsZW5kOiJ+fn4r -WyBdKiQifSx7YmVnaW46ImAuKz9gIn0se2JlZ2luOiIoPz1eKCB7NH18XFx0KSkiLGNvbnRhaW5zOlt7 -YmVnaW46Il4oIHs0fXxcXHQpIixlbmQ6IihcXG4pJCJ9XSxyZWxldmFuY2U6MH1dfSx7YmVnaW46Il5b -LVxcKl17Myx9IixlbmQ6IiQifSxhLHtiZWdpbjovXlxbW15cbl0rXF06LyxyZXR1cm5CZWdpbjohMCxj -b250YWluczpbe2NsYXNzTmFtZToic3ltYm9sIixiZWdpbjovXFsvLGVuZDovXF0vLGV4Y2x1ZGVCZWdp -bjohMCxleGNsdWRlRW5kOiEwfSx7Y2xhc3NOYW1lOiJsaW5rIixiZWdpbjovOlxzKi8sZW5kOi8kLyxl -eGNsdWRlQmVnaW46ITB9XX1dfX19KCkpO2hsanMucmVnaXN0ZXJMYW5ndWFnZSgiZGFydCIsZnVuY3Rp -b24oKXsidXNlIHN0cmljdCI7cmV0dXJuIGZ1bmN0aW9uKGUpe2NvbnN0IG49e2NsYXNzTmFtZToic3Vi -c3QiLHZhcmlhbnRzOlt7YmVnaW46IlxcJFtBLVphLXowLTlfXSsifV19LHQ9e2NsYXNzTmFtZToic3Vi -c3QiLHZhcmlhbnRzOlt7YmVnaW46IlxcJHsiLGVuZDoifSJ9XSxrZXl3b3JkczoidHJ1ZSBmYWxzZSBu -dWxsIHRoaXMgaXMgbmV3IHN1cGVyIn0sYT17Y2xhc3NOYW1lOiJzdHJpbmciLHZhcmlhbnRzOlt7YmVn -aW46InInJyciLGVuZDoiJycnIn0se2JlZ2luOidyIiIiJyxlbmQ6JyIiIid9LHtiZWdpbjoiciciLGVu -ZDoiJyIsaWxsZWdhbDoiXFxuIn0se2JlZ2luOidyIicsZW5kOiciJyxpbGxlZ2FsOiJcXG4ifSx7YmVn -aW46IicnJyIsZW5kOiInJyciLGNvbnRhaW5zOltlLkJBQ0tTTEFTSF9FU0NBUEUsbix0XX0se2JlZ2lu -OiciIiInLGVuZDonIiIiJyxjb250YWluczpbZS5CQUNLU0xBU0hfRVNDQVBFLG4sdF19LHtiZWdpbjoi -JyIsZW5kOiInIixpbGxlZ2FsOiJcXG4iLGNvbnRhaW5zOltlLkJBQ0tTTEFTSF9FU0NBUEUsbix0XX0s -e2JlZ2luOiciJyxlbmQ6JyInLGlsbGVnYWw6IlxcbiIsY29udGFpbnM6W2UuQkFDS1NMQVNIX0VTQ0FQ -RSxuLHRdfV19O3QuY29udGFpbnM9W2UuQ19OVU1CRVJfTU9ERSxhXTtjb25zdCBpPVsiQ29tcGFyYWJs -ZSIsIkRhdGVUaW1lIiwiRHVyYXRpb24iLCJGdW5jdGlvbiIsIkl0ZXJhYmxlIiwiSXRlcmF0b3IiLCJM -aXN0IiwiTWFwIiwiTWF0Y2giLCJPYmplY3QiLCJQYXR0ZXJuIiwiUmVnRXhwIiwiU2V0IiwiU3RvcHdh -dGNoIiwiU3RyaW5nIiwiU3RyaW5nQnVmZmVyIiwiU3RyaW5nU2luayIsIlN5bWJvbCIsIlR5cGUiLCJV -cmkiLCJib29sIiwiZG91YmxlIiwiaW50IiwibnVtIiwiRWxlbWVudCIsIkVsZW1lbnRMaXN0Il0scj1p -Lm1hcChlPT5gJHtlfT9gKTtyZXR1cm57bmFtZToiRGFydCIsa2V5d29yZHM6e2tleXdvcmQ6ImFic3Ry -YWN0IGFzIGFzc2VydCBhc3luYyBhd2FpdCBicmVhayBjYXNlIGNhdGNoIGNsYXNzIGNvbnN0IGNvbnRp -bnVlIGNvdmFyaWFudCBkZWZhdWx0IGRlZmVycmVkIGRvIGR5bmFtaWMgZWxzZSBlbnVtIGV4cG9ydCBl -eHRlbmRzIGV4dGVuc2lvbiBleHRlcm5hbCBmYWN0b3J5IGZhbHNlIGZpbmFsIGZpbmFsbHkgZm9yIEZ1 -bmN0aW9uIGdldCBoaWRlIGlmIGltcGxlbWVudHMgaW1wb3J0IGluIGluZmVyZmFjZSBpcyBsYXRlIGxp -YnJhcnkgbWl4aW4gbmV3IG51bGwgb24gb3BlcmF0b3IgcGFydCByZXF1aXJlZCByZXRocm93IHJldHVy -biBzZXQgc2hvdyBzdGF0aWMgc3VwZXIgc3dpdGNoIHN5bmMgdGhpcyB0aHJvdyB0cnVlIHRyeSB0eXBl -ZGVmIHZhciB2b2lkIHdoaWxlIHdpdGggeWllbGQiLGJ1aWx0X2luOmkuY29uY2F0KHIpLmNvbmNhdChb -Ik5ldmVyIiwiTnVsbCIsImR5bmFtaWMiLCJwcmludCIsImRvY3VtZW50IiwicXVlcnlTZWxlY3RvciIs -InF1ZXJ5U2VsZWN0b3JBbGwiLCJ3aW5kb3ciXSkuam9pbigiICIpLCRwYXR0ZXJuOi9bQS1aYS16XVtB -LVphLXowLTlfXSpcPz8vfSxjb250YWluczpbYSxlLkNPTU1FTlQoIi9cXCpcXCoiLCJcXCovIix7c3Vi -TGFuZ3VhZ2U6Im1hcmtkb3duIixyZWxldmFuY2U6MH0pLGUuQ09NTUVOVCgiLy8vK1xccyoiLCIkIix7 -Y29udGFpbnM6W3tzdWJMYW5ndWFnZToibWFya2Rvd24iLGJlZ2luOiIuIixlbmQ6IiQiLHJlbGV2YW5j -ZTowfV19KSxlLkNfTElORV9DT01NRU5UX01PREUsZS5DX0JMT0NLX0NPTU1FTlRfTU9ERSx7Y2xhc3NO -YW1lOiJjbGFzcyIsYmVnaW5LZXl3b3JkczoiY2xhc3MgaW50ZXJmYWNlIixlbmQ6InsiLGV4Y2x1ZGVF -bmQ6ITAsY29udGFpbnM6W3tiZWdpbktleXdvcmRzOiJleHRlbmRzIGltcGxlbWVudHMifSxlLlVOREVS -U0NPUkVfVElUTEVfTU9ERV19LGUuQ19OVU1CRVJfTU9ERSx7Y2xhc3NOYW1lOiJtZXRhIixiZWdpbjoi -QFtBLVphLXpdKyJ9LHtiZWdpbjoiPT4ifV19fX0oKSk7 -'''; - -String? _index_html; -// index_html md5 is 'c4127573314e7a91769cf9e0bcb489b1' -String _index_html_base64 = ''' -PGh0bWw+CjxoZWFkPgogICAgPHRpdGxlPk51bGwgU2FmZXR5IFByZXZpZXc8L3RpdGxlPgogICAgPHNj -cmlwdCBzcmM9Int7IGhpZ2hsaWdodEpzUGF0aCB9fSI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0Pnt7IGRh -cnRQYWdlU2NyaXB0IH19PC9zY3JpcHQ+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0 -dHBzOi8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzP2ZhbWlseT1PcGVuK1NhbnM6NDAwLDYwMCZkaXNw -bGF5PXN3YXAiPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJ7eyBoaWdobGlnaHRTdHls -ZVBhdGggfX0iPgogICAgPHN0eWxlPnt7IGRhcnRQYWdlU3R5bGUgfX08L3N0eWxlPgogICAgPGxpbmsg -cmVsPSJzaG9ydGN1dCBpY29uIiBocmVmPSIvZGFydF8xOTIucG5nIj4KPC9oZWFkPgo8Ym9keSBjbGFz -cz0ie3sgbWlncmF0aW9uQXBwbGllZFN0eWxlIH19IHt7IG5lZWRzUmVydW5TdHlsZSB9fSI+CjxkaXYg -Y2xhc3M9InJlcnVubmluZy1wYW5lIj4KICA8aDE+UmVydW5uaW5nLi4uPC9oMT4KPC9kaXY+CjxkaXYg -Y2xhc3M9InBvcHVwLXBhbmUiPgogIDxkaXYgY2xhc3M9ImNsb3NlIGJ1dHRvbiI+WDwvZGl2PgogIDxo -Mj48L2gyPjwhLS0gaGVhZGVyIHBsYWNlaG9sZGVyIGVsZW1lbnQgLS0+CiAgPHA+PC9wPjwhLS0gc3Vi -aGVhZGVyIHBsYWNlaG9sZGVyIGVsZW1lbnQgLS0+CiAgPHByZT48L3ByZT48IS0tIHByZWZvcm1hdGVk -IGNvbnRlbnQgcGxhY2Vob2xkZXIgZWxlbWVudCAtLT4KICA8YSBjbGFzcz0iYnV0dG9uIGJvdHRvbSIg -dGFyZ2V0PSJfYmxhbmsiPkZpbGUgb24gR2l0SHViPC9hPjwhLS0gcG9zdCB0byBnaXRodWIgcGxhY2Vo -b2xkZXIgZWxlbWVudCAtLT4KPC9kaXY+CjxwIGNsYXNzPSJyb290Ij57eyByb290IH19PC9wPgo8aGVh -ZGVyIGNsYXNzPSJlbGV2YXRpb24tejQiPgogICAgPGltZyBzcmM9Int7IGRhcnRMb2dvUGF0aCB9fSIg -YWx0PSJEYXJ0UGFkIExvZ28iIGNsYXNzPSJsb2dvIi8+CiAgICA8aDE+RGFydDwvaDE+CiAgICA8aDIg -Y2xhc3M9ImJlZm9yZS1hcHBseSI+UHJvcG9zZWQgbnVsbCBzYWZldHkgY2hhbmdlczwvaDI+CiAgICA8 -aDIgY2xhc3M9ImFmdGVyLWFwcGx5Ij4mIzEwMDAzOyBOdWxsIHNhZmV0eSBtaWdyYXRpb24gYXBwbGll -ZDwvaDI+CiAgICA8YSB0YXJnZXQ9Il9ibGFuayIKICAgICAgIGhyZWY9Imh0dHBzOi8vZGFydC5kZXYv -Z28vbnVsbC1zYWZldHktbWlncmF0aW9uIj4KICAgICAgPGJ1dHRvbiBjbGFzcz0iYWN0aW9uLWJ1dHRv -biI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWljb25zIj5sYXVuY2g8L2k+CiAgICAgICAgPHNw -YW4+SGVscDwvc3Bhbj4KICAgICAgPC9idXR0b24+CiAgICA8L2E+CiAgICA8YnV0dG9uIGNsYXNzPSJh -Y3Rpb24tYnV0dG9uIGFwcGx5LW1pZ3JhdGlvbiI+CiAgICAgICAgPGkgY2xhc3M9Im1hdGVyaWFsLWlj -b25zIj5lZGl0PC9pPgogICAgICAgIDxzcGFuIGNsYXNzPSJsYWJlbCI+CiAgICAgICAgICBBcHBseSBN -aWdyYXRpb24KICAgICAgICA8L3NwYW4+CiAgICA8L2J1dHRvbj4KICAgIDxidXR0b24gY2xhc3M9ImFj -dGlvbi1idXR0b24gYXBwbHktbWlncmF0aW9uIiBkaXNhYmxlZD4KICAgICAgICA8aSBjbGFzcz0ibWF0 -ZXJpYWwtaWNvbnMiPmVkaXQ8L2k+CiAgICAgICAgPHNwYW4gY2xhc3M9ImxhYmVsIj4KICAgICAgICAg -IEFwcGx5IE1pZ3JhdGlvbgogICAgICAgIDwvc3Bhbj4KICAgIDwvYnV0dG9uPgogICAgPGJ1dHRvbiBj -bGFzcz0iYWN0aW9uLWJ1dHRvbiByZXJ1bi1taWdyYXRpb24gYmVmb3JlLWFwcGx5Ij4KICAgICAgPHNw -YW4gY2xhc3M9Im9wdGlvbmFsIj4KICAgICAgICA8aSBjbGFzcz0ibWF0ZXJpYWwtaWNvbnMiPnJlcGxh -eTwvaT4KICAgICAgICBSZXJ1biBGcm9tIFNvdXJjZXMKICAgICAgPC9zcGFuPgogICAgICA8c3BhbiBj -bGFzcz0icmVxdWlyZWQiPgogICAgICAgIDxpIGNsYXNzPSJtYXRlcmlhbC1pY29ucyI+d2FybmluZzwv -aT4KICAgICAgICBSZXJ1biBXaXRoIENoYW5nZXMKICAgICAgPC9zcGFuPgogICAgPC9idXR0b24+Cjwv -aGVhZGVyPgo8ZGl2IGNsYXNzPSJwYW5lbHMgaG9yaXpvbnRhbCI+CiAgICA8ZGl2IGNsYXNzPSJuYXYt -cGFuZWwiPgogICAgICAgIDxkaXYgY2xhc3M9Im5hdi1pbm5lciI+CiAgICAgICAgICAgIDxkaXYgY2xh -c3M9InBhbmVsLWhlYWRpbmciPlByb2plY3QgRmlsZXM8L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFz -cz0ibmF2LXRyZWUiPjwvZGl2PgogICAgICAgIDwvZGl2PjwhLS0gL25hdi1pbm5lciAtLT4KICAgIDwv -ZGl2PjwhLS0gL25hdiAtLT4KICAgIDxkaXYgY2xhc3M9ImZpbGUiPgogICAgICAgIDxkaXYgY2xhc3M9 -InRpdGxlLWJhciI+CiAgICAgICAgICAgIDxoMyBpZD0idW5pdC1uYW1lIj4mbmJzcDs8L2gzPgogICAg -ICAgICAgICA8c3BhbiBpZD0ibWlncmF0ZS11bml0LXN0YXR1cy1pY29uLWxhYmVsIj5NaWdyYXRlCiAg -ICAgICAgICAgICAgICA8c3BhbgogICAgICAgICAgICAgICAgICAgIGNsYXNzPSJtYXRlcmlhbC1pY29u -cyBzdGF0dXMtaWNvbiBtaWdyYXRpbmciCiAgICAgICAgICAgICAgICAgICAgaWQ9Im1pZ3JhdGUtdW5p -dC1zdGF0dXMtaWNvbiI+Y2hlY2tfYm94PC9zcGFuPgogICAgICAgICAgICA8L3NwYW4+CiAgICAgICAg -PC9kaXY+CiAgICAgICAgPGRpdiBjbGFzcz0iY29udGVudCI+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9 -InJlZ2lvbnMiPgogICAgICAgICAgICAgICAgPCEtLSBUaGUgcmVnaW9ucyBvdmVybGF5IGNvZGUgY29w -eSBvZiB0aGUgY29udGVudCB0byBwcm92aWRlIC0tPgogICAgICAgICAgICAgICAgPCEtLSB0b29sdGlw -cyBmb3IgbW9kaWZpZWQgcmVnaW9ucy4gLS0+CiAgICAgICAgICAgIDwvZGl2PjwhLS0gL3JlZ2lvbnMg -LS0+CiAgICAgICAgICAgIDxkaXYgY2xhc3M9ImNvZGUiPgogICAgICAgICAgICAgICAgPCEtLSBDb21w -aWxhdGlvbiB1bml0IGNvbnRlbnQgaXMgd3JpdHRlbiBoZXJlLiAtLT4KICAgICAgICAgICAgICAgIDxw -IGNsYXNzPSJ3ZWxjb21lIj4KICAgICAgICAgICAgICAgICAgICBTZWxlY3QgYSBzb3VyY2UgZmlsZSBv -biB0aGUgbGVmdCB0byBwcmV2aWV3IHRoZSBwcm9wb3NlZCBlZGl0cy4KICAgICAgICAgICAgICAgIDwv -cD4KICAgICAgICAgICAgPC9kaXY+CiAgICAgICAgPC9kaXY+CiAgICA8L2Rpdj48IS0tIC9jb250ZW50 -IC0tPgogICAgPGRpdiBjbGFzcz0iaW5mby1wYW5lbCI+CiAgICAgICAgPGRpdiBjbGFzcz0iZWRpdC1s -aXN0Ij4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+UHJvcG9zZWQgRWRpdHM8 -L2Rpdj4KICAgICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtY29udGVudCI+PC9kaXY+CiAgICAgICAg -PC9kaXY+PCEtLSAvZWRpdC1saXN0IC0tPgogICAgICAgIDxkaXYgY2xhc3M9ImVkaXQtcGFuZWwiPgog -ICAgICAgICAgICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5FZGl0IERldGFpbHM8L2Rpdj4KICAg -ICAgICAgICAgPGRpdiBjbGFzcz0icGFuZWwtY29udGVudCI+CiAgICAgICAgICAgICAgICA8cCBjbGFz -cz0icGxhY2Vob2xkZXIiPlNlZSBkZXRhaWxzIGFib3V0IGEgcHJvcG9zZWQgZWRpdC48L3A+CiAgICAg -ICAgICAgIDwvZGl2PjwhLS0gL3BhbmVsLWNvbnRlbnQgLS0+CiAgICAgICAgPC9kaXY+PCEtLSAvZWRp -dC1wYW5lbCAtLT4KICAgIDwvZGl2PjwhLS0gL2luZm8tcGFuZWwgLS0+CjwvZGl2PjwhLS0gL3BhbmVs -cyAtLT4KPGZvb3Rlcj4KICA8YnV0dG9uIGNsYXNzPSJyZXBvcnQtcHJvYmxlbSI+U2VuZCBGZWVkYmFj -azwvYnV0dG9uPgogICAgPHNwYW4gY2xhc3M9IndpZGUiPiA8L3NwYW4+CiAgICA8ZGl2IGNsYXNzPSJz -ZGstdmVyc2lvbiI+QmFzZWQgb24gPHNwYW4gaWQ9InNkay12ZXJzaW9uIj57eyBzZGtWZXJzaW9uIH19 -PC9zcGFuPjwvZGl2Pgo8L2Zvb3Rlcj4KPC9ib2R5Pgo8L2h0bWw+Cg== -'''; - -String? _migration_css; -// migration_css md5 is '0e1cb1b9f1ead1ad6ef1027cf3ee93e0' -String _migration_css_base64 = ''' -LyogQ29weXJpZ2h0IChjKSAyMDE5LCB0aGUgRGFydCBwcm9qZWN0IGF1dGhvcnMuIFBsZWFzZSBzZWUg -dGhlIEFVVEhPUlMgZmlsZSAgKi8KLyogZm9yIGRldGFpbHMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuIFVz -ZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgKi8KLyogQlNELXN0eWxlIGxpY2Vu -c2UgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZS4gICAgICAgICAgICAgICAgICAg -Ki8KCi8qCiAqIENvbG9ycyBtZW50aW9uZWQgaW4gdGhpcyBmaWxlLCBlLmcuICIkZGFyay10ZXh0LWNv -bG9yIiBhcmUgZnJvbSBEYXJ0UGFkOgogKiBodHRwczovL2dpdGh1Yi5jb20vZGFydC1sYW5nL2RhcnQt -cGFkL2Jsb2IvbWFzdGVyL2xpYi9zY3NzL2NvbG9ycy5zY3NzCiAqLwoKLyogVGV4dCBzZWxlY3Rpb24g -Ki8KOjpzZWxlY3Rpb24gewogIGJhY2tncm91bmQ6ICM2ZThlYjE7IC8qICRkYXJrLXNlbGVjdGlvbi1j -b2xvciAqLwp9CgovKiBNYXRlcmlhbCBpY29ucyBjb25maWd1cmF0aW9uICovCkBmb250LWZhY2Ugewog -IGZvbnQtZmFtaWx5OiAnTWF0ZXJpYWwgSWNvbnMnOwogIGZvbnQtc3R5bGU6IG5vcm1hbDsKICBmb250 -LXdlaWdodDogNDAwOwogIHNyYzogbG9jYWwoJ01hdGVyaWFsIEljb25zJyksCiAgbG9jYWwoJ01hdGVy -aWFsSWNvbnMtUmVndWxhcicpLAogIHVybCgvTWF0ZXJpYWxJY29uc1JlZ3VsYXIudHRmKSBmb3JtYXQo -J3RydWV0eXBlJyk7Cn0KCi8qCiAqIFJlcXVpcmVkIGZvciBNYXRlcmlhbCBJY29uczoKICogaHR0cHM6 -Ly9nb29nbGUuZ2l0aHViLmlvL21hdGVyaWFsLWRlc2lnbi1pY29ucy8KICovCi5tYXRlcmlhbC1pY29u -cyB7CiAgZm9udC1mYW1pbHk6ICdNYXRlcmlhbCBJY29ucyc7CiAgZm9udC13ZWlnaHQ6IG5vcm1hbDsK -ICBmb250LXN0eWxlOiBub3JtYWw7CiAgZm9udC1zaXplOiAyNHB4OyAgLyogUHJlZmVycmVkIGljb24g -c2l6ZSAqLwogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBsaW5lLWhlaWdodDogMTsKICB0ZXh0LXRy -YW5zZm9ybTogbm9uZTsKICBsZXR0ZXItc3BhY2luZzogbm9ybWFsOwogIHdvcmQtd3JhcDogbm9ybWFs -OwogIHdoaXRlLXNwYWNlOiBub3dyYXA7CiAgZGlyZWN0aW9uOiBsdHI7CgogIC8qIFN1cHBvcnQgZm9y -IGFsbCBXZWJLaXQgYnJvd3NlcnMuICovCiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYW50aWFsaWFz -ZWQ7CiAgLyogU3VwcG9ydCBmb3IgU2FmYXJpIGFuZCBDaHJvbWUuICovCiAgdGV4dC1yZW5kZXJpbmc6 -IG9wdGltaXplTGVnaWJpbGl0eTsKCiAgLyogU3VwcG9ydCBmb3IgRmlyZWZveC4gKi8KICAtbW96LW9z -eC1mb250LXNtb290aGluZzogZ3JheXNjYWxlOwoKICAvKiBTdXBwb3J0IGZvciBJRS4gKi8KICBmb250 -LWZlYXR1cmUtc2V0dGluZ3M6ICdsaWdhJzsKfQoKYm9keSB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzEy -MjAyZjsKICBjb2xvcjogI2NjYzsKICBmb250LWZhbWlseTogIlJvYm90byIsIHNhbnMtc2VyaWY7CiAg -Zm9udC1zaXplOiAxNHB4OwogIGRpc3BsYXk6IGZsZXg7CiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjsK -ICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgdG9wOiAwOwogIHJpZ2h0OiAwOwogIGJvdHRvbTogMDsKICBs -ZWZ0OiAwOwogIG1hcmdpbjogMDsKICBwYWRkaW5nOiAwOwogIG92ZXJmbG93OiBoaWRkZW47Cn0KCi5w -cm9wb3NlZCAuYWZ0ZXItYXBwbHkgewogIGRpc3BsYXk6IG5vbmU7Cn0KCi5hcHBsaWVkIC5iZWZvcmUt -YXBwbHkgewogIGRpc3BsYXk6IG5vbmU7Cn0KCi5hcHBsaWVkIC5hcHBseS1taWdyYXRpb246bm90KFtk -aXNhYmxlZF0pLCAubmVlZHMtcmVydW4gLmFwcGx5LW1pZ3JhdGlvbjpub3QoW2Rpc2FibGVkXSkgewog -IGRpc3BsYXk6IG5vbmU7Cn0KCi5wcm9wb3NlZDpub3QoLm5lZWRzLXJlcnVuKSAuYXBwbHktbWlncmF0 -aW9uW2Rpc2FibGVkXSB7CiAgZGlzcGxheTogbm9uZTsKfQoKaGVhZGVyIHsKICBiYWNrZ3JvdW5kLWNv -bG9yOiAjMWMyODM0OwogIGhlaWdodDogNDhweDsKICBwYWRkaW5nLWxlZnQ6IDI0cHg7CiAgYWxpZ24t -aXRlbXM6IGNlbnRlcjsKICB6LWluZGV4OiA0Owp9CgpoZWFkZXIgaDEsCmhlYWRlciBoMiB7CiAgZGlz -cGxheTogaW5saW5lLWJsb2NrOwogIGZvbnQtZmFtaWx5OiAiR29vZ2xlIFNhbnMiLCJSb2JvdG8iLHNh -bnMtc2VyaWY7CiAgZm9udC13ZWlnaHQ6IDQwMDsKICBtYXJnaW4tcmlnaHQ6IDI0cHg7Cn0KCmgxIHsK -ICBmb250LXNpemU6IDEuNWVtOwp9CgpoZWFkZXIgaDIgewogIGZvbnQtc2l6ZTogMS4yZW07CgogIC8q -IFNoaWZ0IHRleHQgdXAgKi8KICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgdG9wOiAtMnB4Owp9CgpoZWFk -ZXIgLmFjdGlvbi1idXR0b24sIGhlYWRlciBhIHsKICByaWdodDogMHB4OwogIGZsb2F0OiByaWdodDsK -ICBtYXJnaW46IDEwcHg7Cn0KCmhlYWRlciBpbWcubG9nbyB7CiAgaGVpZ2h0OiAyNHB4OwogIHdpZHRo -OiAyNHB4OwogIG1hcmdpbi1yaWdodDogOHB4OwogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICB0b3A6IDRw -eDsKfQoKZm9vdGVyIC5yZXBvcnQtcHJvYmxlbSB7CiAgcmlnaHQ6IDBweDsKICBtYXJnaW46IDRweCA4 -cHg7Cn0KCi5yZXJ1bi1taWdyYXRpb24gLnJlcXVpcmVkIHsKICBkaXNwbGF5OiBub25lOwp9CgoubmVl -ZHMtcmVydW4gLnJlcnVuLW1pZ3JhdGlvbiAucmVxdWlyZWQgewogIGRpc3BsYXk6IGluaXRpYWw7Cn0K -Ci5uZWVkcy1yZXJ1biAucmVydW4tbWlncmF0aW9uIC5vcHRpb25hbCB7CiAgZGlzcGxheTpub25lOwp9 -Cgpmb290ZXIgewogIGNvbG9yOiAjY2NjOwogIGJhY2tncm91bmQtY29sb3I6ICMyNzMyM2E7CiAgZGlz -cGxheTogZmxleDsKICBmbGV4LWRpcmVjdGlvbjogcm93OwogIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAg -cGFkZGluZzogOHB4IDAgOHB4IDI0cHg7Cn0KCmZvb3RlciAud2lkZSB7CiAgZmxleDogMTsKfQoKZm9v -dGVyIC5zZGstdmVyc2lvbiB7CiAgbWFyZ2luLXJpZ2h0OiAzMnB4Owp9CgouaG9yaXpvbnRhbCB7CiAg -ZGlzcGxheTogZmxleDsKfQoKLnBhbmVscyB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzEyMWEyNTsKICBm -bGV4OiAxOwogIG92ZXJmbG93OiBoaWRkZW47Cn0KCi5wYW5lbC1oZWFkaW5nIHsKICBjb2xvcjogIzY3 -Njc2NzsKICBtYXJnaW46IDhweDsKfQoKLm5hdi1saW5rLAoucmVnaW9uIHsKICBjdXJzb3I6IHBvaW50 -ZXI7Cn0KCi5uYXYtcGFuZWwgewogIGJhY2tncm91bmQtY29sb3I6ICMxMjIwMmY7CiAgZmxleDogMSAy -MDBweDsKICBtYXJnaW46IDA7CiAgb3ZlcmZsb3c6IHNjcm9sbDsKICB1c2VyLXNlbGVjdDogbm9uZTsK -fQoKLm5hdi1pbm5lciB7CiAgcGFkZGluZzogMCAwIDdweCA3cHg7Cn0KCi5maXhlZCB7CiAgcG9zaXRp -b246IGZpeGVkOwogIHRvcDogMDsKfQoKLnJvb3QgewogIG1hcmdpbjogMDsKICBkaXNwbGF5OiBub25l -Owp9CgoubmF2LXRyZWUgPiB1bCB7CiAgcGFkZGluZy1sZWZ0OiA2cHg7Cn0KCi5uYXYtdHJlZSAubWF0 -ZXJpYWwtaWNvbnMgewogIGZvbnQtc2l6ZTogMjBweDsKICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgdG9w -OiA1cHg7CiAgbWFyZ2luLXJpZ2h0OiA4cHg7CiAgY29sb3I6ICM2NzY3Njc7IC8qICRzZWNvbmRhcnkt -Y29sb3IgKi8KfQoKLnN0YXR1cy1pY29uLmFscmVhZHktbWlncmF0ZWQgewogIGNvbG9yOiAjMDA3YTI3 -OyAvKiAkbGlnaHQtZ3JlZW4gKi8KfQoKLnN0YXR1cy1pY29uLmRpc2FibGVkIHsKICBjdXJzb3I6IG5v -dC1hbGxvd2VkOwp9Cgouc3RhdHVzLWljb24ub3B0ZWQtb3V0IHsKICBjb2xvcjogIzY3Njc2NzsgLyog -JHNlY29uZGFyeS1jb2xvciAqLwp9Cgouc3RhdHVzLWljb24ub3B0ZWQtb3V0OmhvdmVyIHsKICBjb2xv -cjogI2ZmZmZmZjsKfQoKLnN0YXR1cy1pY29uLm1pZ3JhdGluZyB7CiAgY29sb3I6ICM1MWM2ODY7IC8q -ICRkYXJrLWdyZWVuICovCn0KCi5zdGF0dXMtaWNvbi5taWdyYXRpbmc6aG92ZXIgewogIGNvbG9yOiAj -ZmZmZmZmOwp9CgoubmF2LWlubmVyIHVsIHsKICBwYWRkaW5nLWxlZnQ6IDEycHg7CiAgbWFyZ2luOiAw -Owp9CgoubmF2LWlubmVyIGxpIHsKICBsaXN0LXN0eWxlLXR5cGU6IG5vbmU7CiAgd2hpdGUtc3BhY2U6 -IG5vd3JhcDsKfQoKLm5hdi1pbm5lciBsaTpub3QoLmRpcikgewogIG1hcmdpbi1sZWZ0OiAyMHB4Owog -IG1hcmdpbi1ib3R0b206IDNweDsKfQoKLm5hdi1pbm5lciBsaS5kaXIgLmFycm93IHsKICBjdXJzb3I6 -IHBvaW50ZXI7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogIGZvbnQtc2l6ZTogMTBweDsKICBtYXJn -aW4tcmlnaHQ6IDRweDsKICB0cmFuc2l0aW9uOiB0cmFuc2Zvcm0gMC41cyBlYXNlLW91dDsKfQoKLm5h -di1pbm5lciBsaS5kaXIgLmFycm93LmNvbGxhcHNlZCB7CiAgdHJhbnNmb3JtOiByb3RhdGUoLTkwZGVn -KTsKfQoKLm5hdi1pbm5lciB1bCB7CiAgLyogYSBtYXgtaGVpZ2h0IGlzIGFkZGVkIHRvIGVhY2ggZWxl -bWVudCBhdCBydW50aW1lLiAqLwogIHRyYW5zaXRpb246IG1heC1oZWlnaHQgMC4yNXMgZWFzZS1vdXQ7 -Cn0KCi5uYXYtaW5uZXIgdWwuY29sbGFwc2VkIHsKICBtYXgtaGVpZ2h0OiAwICFpbXBvcnRhbnQ7CiAg -b3ZlcmZsb3c6IGhpZGRlbjsKfQoKLm5hdi1pbm5lciAuc2VsZWN0ZWQtZmlsZSB7CiAgY29sb3I6IHdo -aXRlOwogIGN1cnNvcjogaW5oZXJpdDsKICBmb250LXdlaWdodDogNjAwOwogIHRleHQtZGVjb3JhdGlv -bjogbm9uZTsKfQoKLmVkaXQtY291bnQgewogIGJhY2tncm91bmQtY29sb3I6ICM2NzY3Njc7CiAgYm9y -ZGVyLXJhZGl1czogMTBweDsKICBjb2xvcjogI2ZmZjsKICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7CiAg -Zm9udC1zaXplOiAxMXB4OwogIGZvbnQtd2VpZ2h0OiA2MDA7CiAgbWFyZ2luLWxlZnQ6IDVweDsKICBt -aW4td2lkdGg6IDI1cHg7CiAgcGFkZGluZzogNHB4IDAgMnB4IDA7CiAgdGV4dC1hbGlnbjogY2VudGVy -OwogIGxpbmUtaGVpZ2h0OiAxZW07Cn0KCi5maWxlIHsKICBkaXNwbGF5OiBmbGV4OwogIGZsZXgtZGly -ZWN0aW9uOiBjb2x1bW47CiAgZmxleDogNCAzMDBweDsKICBmb250LWZhbWlseTogIkdvb2dsZSBTYW5z -IiwiUm9ib3RvIixzYW5zLXNlcmlmOwogIGJhY2tncm91bmQ6ICMxMjIwMmY7CiAgbWFyZ2luOiAwIDZw -eDsKfQoKLnRpdGxlLWJhciBoMyB7CiAgZGlzcGxheTogaW5saW5lLWJsb2NrOwogIGZvbnQtd2VpZ2h0 -OiA0MDA7CiAgLyogVGhpcyBhbGlnbnMgdGhlIHRpdGxlIHRleHQgd2l0aCB0aGUgY29udGVudCB0ZXh0 -LCBhY2NvdW50aW5nIGZvciB0aGUgd2lkdGgKICAgKiBvZiB0aGUgbGluZSBudW1iZXJzLgogICAqLwog -IG1hcmdpbjogMC41ZW0gMjRweCAwLjVlbSA2M3B4Owp9CgoudGl0bGUtYmFyICNtaWdyYXRlLXVuaXQt -c3RhdHVzLWljb24tbGFiZWwgewogIGRpc3BsYXk6IG5vbmU7CiAgdXNlci1zZWxlY3Q6IG5vbmU7Cn0K -Ci50aXRsZS1iYXIgI21pZ3JhdGUtdW5pdC1zdGF0dXMtaWNvbi1sYWJlbC52aXNpYmxlIHsKICBkaXNw -bGF5OiBpbmxpbmU7Cn0KCi50aXRsZS1iYXIgI21pZ3JhdGUtdW5pdC1zdGF0dXMtaWNvbiB7CiAgdmVy -dGljYWwtYWxpZ246IHRleHQtYm90dG9tOwp9CgouY29udGVudCB7CiAgZmxleC1ncm93OiAxOwogIGZv -bnQtZmFtaWx5OiAiUm9ib3RvIE1vbm8iLG1vbm9zcGFjZTsKICBvdmVyZmxvdzogc2Nyb2xsOwogIHBv -c2l0aW9uOiByZWxhdGl2ZTsKICB3aGl0ZS1zcGFjZTogcHJlOwp9CgouY29kZSB7CiAgcGFkZGluZzog -MC41ZW07CiAgcG9zaXRpb246IGFic29sdXRlOwogIGxlZnQ6IDA7CiAgdG9wOiAwOwogIG1hcmdpbi1s -ZWZ0OiA1NnB4Owp9CgouY29kZSAud2VsY29tZSB7CiAgZm9udC1mYW1pbHk6ICJHb29nbGUgU2FucyIs -IlJvYm90byIsc2Fucy1zZXJpZjsKICBmb250LXNpemU6IDE4cHg7CiAgbWFyZ2luLXJpZ2h0OiA2MnB4 -OwogIGNvbG9yOiAjNzc3Owp9CgouY29kZSAubmF2LWxpbmsgewogIGNvbG9yOiAjMTZhZGNhOwogIHRl -eHQtZGVjb3JhdGlvbi1saW5lOiBub25lOwp9CgouY29kZSAubmF2LWxpbms6dmlzaXRlZCB7CiAgY29s -b3I6ICMxMzliYjU7IC8qICMxNmFkY2EgZGFya2VuZWQgMTAlICovCiAgdGV4dC1kZWNvcmF0aW9uLWxp -bmU6IG5vbmU7Cn0KCi5jb2RlIC5uYXYtbGluazpob3ZlciB7CiAgdGV4dC1kZWNvcmF0aW9uLWxpbmU6 -IHVuZGVybGluZTsKICBmb250LXdlaWdodDogNjAwOwp9CgoucmVnaW9ucyB7CiAgcGFkZGluZzogMC41 -ZW07CiAgcG9zaXRpb246IGFic29sdXRlOwogIGxlZnQ6IDA7CiAgdG9wOiAwOwp9CgoucmVnaW9ucyB0 -YWJsZSB7CiAgYm9yZGVyLXNwYWNpbmc6IDA7CiAgZm9udC1zaXplOiBpbmhlcml0Owp9CgoucmVnaW9u -cyB0ZCB7CiAgYm9yZGVyOiBub25lOwogIC8qIFRoZSBjb250ZW50IG9mIHRoZSByZWdpb25zIGlzIG5v -dCB2aXNpYmxlOyB0aGUgdXNlciBpbnN0ZWFkIHdpbGwgc2VlIHRoZQogICAqIGhpZ2hsaWdodGVkIGNv -cHkgb2YgdGhlIGNvbnRlbnQuICovCiAgY29sb3I6IHJnYmEoMjU1LCAyNTUsIDI1NSwgMCk7CiAgcGFk -ZGluZzogMDsKICB3aGl0ZS1zcGFjZTogcHJlOwp9CgoucmVnaW9ucyB0ZDplbXB0eTphZnRlciB7CiAg -Y29udGVudDogIlwwMGEwIjsKfQoKLnJlZ2lvbnMgdHIuaGlnaGxpZ2h0IHRkOmxhc3QtY2hpbGQgewog -IGJhY2tncm91bmQtY29sb3I6ICM0NDQ0NDQ7CiAgY29sb3I6IHdoaXRlOwp9CgoucmVnaW9ucyB0ZC5s -aW5lLW5vIHsKICBib3JkZXItcmlnaHQ6IHNvbGlkICMxMjIwMmYgMnB4OwogIGNvbG9yOiAjOTk5OTk5 -OwogIHBhZGRpbmctcmlnaHQ6IDRweDsKICB0ZXh0LWFsaWduOiByaWdodDsKICB2aXNpYmlsaXR5OiB2 -aXNpYmxlOwogIHdpZHRoOiA1MHB4OwogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKfQoKLnJlZ2lvbnMg -dHIuaGlnaGxpZ2h0IHRkLmxpbmUtbm8gewogIGJvcmRlci1yaWdodDogc29saWQgI2NjYyAycHg7Cn0K -Ci5yZWdpb24gewogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBwb3NpdGlvbjogcmVsYXRpdmU7CiAg -dmlzaWJpbGl0eTogdmlzaWJsZTsKICB6LWluZGV4OiAyMDA7Cn0KCi5yZWdpb24uYWRkZWQtcmVnaW9u -IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMTc4YWZkOwogIGNvbG9yOiAjZmZmOwp9CgoucmVnaW9uLnJl -bW92ZWQtcmVnaW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjRkE1NTdkOyAvKiAkZGFyay1waW5rICov -CiAgY29sb3I6ICNmZmY7Cn0KCi5yZWdpb24uaW5mb3JtYXRpdmUtcmVnaW9uIHsKICBiYWNrZ3JvdW5k -LWNvbG9yOiAjMjYzOTUyOwogIGNvbG9yOiAjZmZmOwogIGRpc3BsYXk6IGlubGluZS1ibG9jazsKICBo -ZWlnaHQ6IDE0cHg7CiAgcG9zaXRpb246IHJlbGF0aXZlOwp9CgoudGFyZ2V0IHsKICBiYWNrZ3JvdW5k -LWNvbG9yOiAjNDQ0OwogIHBvc2l0aW9uOiByZWxhdGl2ZTsKICB2aXNpYmlsaXR5OiB2aXNpYmxlOwog -IGZvbnQtd2VpZ2h0OiA2MDA7Cn0KCi5pbmZvLXBhbmVsIHsKICBmbGV4OiAxIDIwMHB4OwogIG1hcmdp -bjogMDsKICBoZWlnaHQ6IDEwMCU7CiAgZGlzcGxheTogZmxleDsKICBmbGV4LWRpcmVjdGlvbjogY29s -dW1uOwp9CgouaW5mby1wYW5lbCAuZWRpdC1wYW5lbCB7CiAgYmFja2dyb3VuZC1jb2xvcjogIzEyMjAy -ZjsKICBvdmVyZmxvdzogYXV0bzsKfQoKLmluZm8tcGFuZWwgLnBhbmVsLWNvbnRlbnQgewogIHBhZGRp -bmc6IDdweDsKfQoKLmluZm8tcGFuZWwgLnBhbmVsLWNvbnRlbnQ+IDpmaXJzdC1jaGlsZCB7CiAgbWFy -Z2luLXRvcDogMDsKfQoKLmluZm8tcGFuZWwgLm5vd3JhcCB7CiAgd2hpdGUtc3BhY2U6IG5vd3JhcDsK -fQoKLmluZm8tcGFuZWwgdWwsCi5pbmZvLXBhbmVsIG9sIHsKICBwYWRkaW5nLWxlZnQ6IDIwcHg7Cn0K -Ci5pbmZvLXBhbmVsIGxpIHsKICBtYXJnaW46IDAgMCA1cHggMDsKfQoKLmluZm8tcGFuZWwgYSB7CiAg -Y29sb3I6ICMxMzliYjU7Cn0KCi5pbmZvLXBhbmVsIGE6aG92ZXIgewogIGNvbG9yOiAjMWVjN2U3OyAv -KiAjMTM5YmI1IGxpZ2h0ZW5lZCAyMCUgKi8KfQoKLmluZm8tcGFuZWwgLmVkaXQtbGlzdCB7CiAgYmFj -a2dyb3VuZC1jb2xvcjogIzEyMjAyZjsKICBvdmVyZmxvdzogYXV0bzsKfQoKLmVkaXQtcGFuZWwgewog -IG1hcmdpbi10b3A6IDZweDsKICBmbGV4OiAxIDEwMHB4Owp9CgouZWRpdC1saXN0IHsKICBmbGV4OiAy -IDEwMHB4Owp9CgouZWRpdC1saXN0IC5lZGl0IHsKICBtYXJnaW46IDNweCAwOwp9CgouZWRpdC1saXN0 -IC5lZGl0LWxpbmsgewogIGN1cnNvcjogcG9pbnRlcjsKfQoKLnBvcHVwLXBhbmUgewogIGRpc3BsYXk6 -IG5vbmU7CiAgcG9zaXRpb246IGZpeGVkOwogIHRvcDogMTUwcHg7CiAgbGVmdDogMTUwcHg7CiAgcmln -aHQ6IDE1MHB4OwogIGJvdHRvbTogMTUwcHg7CiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7CiAgYm9y -ZGVyLXRvcDogMnB4IHNvbGlkIGJsYWNrOwogIGJvcmRlci1yYWRpdXM6IDdweDsKICBib3gtc2hhZG93 -OiAwcHggMHB4IDIwcHggMnB4ICNiNGJmY2IyMjsKICB6LWluZGV4OiA0MDA7CiAgYmFja2dyb3VuZDog -IzJiMzAzNjsKICBwYWRkaW5nOiAyMHB4Owp9CgoucG9wdXAtcGFuZSAuY2xvc2UgewogIHBvc2l0aW9u -OiBhYnNvbHV0ZTsKICByaWdodDogMTBweDsKICB0b3A6IDEwcHg7CiAgY3Vyc29yOiBwb2ludGVyOwog -IHRleHQtc2hhZG93OiAxcHggMXB4IDJweCAjODg4OwogIGJveC1zaGFkb3c6IDFweCAxcHggMnB4ICMx -MTE7Cn0KCi5wb3B1cC1wYW5lIGgyIHsKICBwYWRkaW5nOiAyMXB4OwogIGhlaWdodDogMTAlOwogIG1h -cmdpbjogMHB4OwogIGJveC1zaXppbmc6IGJvcmRlci1ib3g7Cn0KCi5wb3B1cC1wYW5lIHAgewogIGhl -aWdodDogMTAlOwogIGJveC1zaXppbmc6IGJvcmRlci1ib3g7CiAgcGFkZGluZzogMHB4IDIwcHg7Cn0K -Ci5wb3B1cC1wYW5lIHByZSB7CiAgYmFja2dyb3VuZDogIzEyMjAyZjsKICBwYWRkaW5nOiAyMHB4Owog -IGJvdHRvbTogMHB4OwogIG92ZXJmbG93OiBhdXRvIHNjcm9sbDsKICBoZWlnaHQ6IDY1JTsKICBtYXJn -aW46IDBweDsKICBib3gtc2l6aW5nOiBib3JkZXItYm94Owp9CgoucG9wdXAtcGFuZSAuYnV0dG9uLmJv -dHRvbSB7CiAgbWFyZ2luOiAyMHB4IDBweDsKICBkaXNwbGF5OiBibG9jazsKICB0ZXh0LWFsaWduOiBj -ZW50ZXI7Cn0KCi5yZXJ1bm5pbmctcGFuZSB7CiAgZGlzcGxheTogbm9uZTsKfQoKYm9keS5yZXJ1bm5p -bmcgLnJlcnVubmluZy1wYW5lIHsKICBkaXNwbGF5OiBibG9jazsKICBwb3NpdGlvbjogZml4ZWQ7CiAg -dG9wOiAwcHg7CiAgYm90dG9tOiAwcHg7CiAgbGVmdDogMHB4OwogIHJpZ2h0OiAwcHg7CiAgYmFja2dy -b3VuZC1jb2xvcjogIzAwMDAwMEFBOyAvKiB0cmFuc2x1Y2VudCBibGFjayAqLwogIHotaW5kZXg6IDQw -MDsKfQoKLnJlcnVubmluZy1wYW5lIGgxIHsKICBwb3NpdGlvbjogYWJzb2x1dGU7CiAgdG9wOiA1MCU7 -CiAgbGVmdDogNTAlOwogIHRyYW5zZm9ybTogdHJhbnNsYXRlKC01MCUsIC01MCUpOwp9CgouZWRpdC1w -YW5lbCAudHlwZS1kZXNjcmlwdGlvbiB7CiAgLyogRnJvbSBEYXJ0UGFkICRkYXJrLW9yYW5nZSAqLwog -IGNvbG9yOiAjZmY5MTZlOwogIGZvbnQtZmFtaWx5OiBtb25vc3BhY2U7Cn0KCnVsLnRyYWNlIHsKICBm -b250LXNpemU6IDEzcHg7CiAgbGlzdC1zdHlsZS10eXBlOiBub25lOwogIHBhZGRpbmctbGVmdDogMHB4 -Owp9Cgp1bC50cmFjZSBsaSB7CiAgY29sb3I6IHdoaXRlOwp9Cgp1bC50cmFjZSBsaSAuZnVuY3Rpb24g -ewogIC8qIGZyb20gLmhsanMtdmFyaWFibGUgKi8KICBjb2xvcjogIzE2YWRjYTsKICBmb250LWZhbWls -eTogbW9ub3NwYWNlOwogIGZvbnQtd2VpZ2h0OiA2MDA7Cn0KCnVsLnRyYWNlIGxpIHAuZHJhd2VyIHsK -ICBtYXJnaW46IDNweCAwcHg7CiAgcGFkZGluZzogMHB4IDBweCAwcHggMTRweDsKfQoKdWwudHJhY2Ug -bGkgcC5kcmF3ZXIgYnV0dG9uIHsKICBtYXJnaW4tcmlnaHQ6IDNweDsKfQoKLmVsZXZhdGlvbi16NCB7 -CiAgYm94LXNoYWRvdzogMHB4IDJweCA0cHggLTFweCByZ2JhKDAsIDAsIDAsIDAuMiksCiAgICAgIDBw -eCA0cHggNXB4IDBweCByZ2JhKDAsIDAsIDAsIDAuMTQpLAogICAgICAwcHggMXB4IDEwcHggMHB4IHJn -YmEoMCwgMCwgMCwgLjEyKTsKfQoKYSB7CiAgY29sb3I6ICNjY2M7CiAgZmlsbDogI2NjYzsKICB0ZXh0 -LWRlY29yYXRpb246IG5vbmU7Cn0KCmE6aG92ZXIgewogIGNvbG9yOiAjZGJkYmRiOyAvKiAjY2NjIGxp -Z2h0ZW50ZWQgMzAlKi8KICBmaWxsOiAjZmZmOwp9CgouYWRkLWhpbnQtbGluayB7CiAgZGlzcGxheTog -aW5saW5lLWJsb2NrOwogIG1hcmdpbjogM3B4Owp9CgouYWRkLWhpbnQtbGluazpob3ZlciB7CiAgY29s -b3I6ICNmZmY7Cn0KCmhlYWRlciBidXR0b24gewogIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7Cn0K -CmhlYWRlciBhIHsKICBtYXJnaW46IDA7Cn0KCi8qIENhcmVmdWwgaGVyZS4gYGEuYnV0dG9uYCBpcyBy -ZXBldGl0aXZlIGJ1dCByZXF1aXJlZCB0byBnZXQgY29ycmVjdAogKiBzcGVjaWZpY2l0eSAqLwpidXR0 -b24sIC5idXR0b24sIGEuYnV0dG9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDIyLCAxMzgsIDI1 -MywgMC4xNSk7CiAgYm9yZGVyOiBub25lOwogIGJvcmRlci1yYWRpdXM6IDNweDsKICBwYWRkaW5nOiAz -cHggMTBweDsKICBmb250LXdlaWdodDogNTAwOwogIGZvbnQtZm9udDogUm9ib3RvLCBzYW5zLXNlcmlm -OwogIGNvbG9yOiAjZmZmOwp9CgpidXR0b246aG92ZXIsIC5idXR0b246aG92ZXIgewogIGJhY2tncm91 -bmQtY29sb3I6IHJnYmEoMjIsIDEzOCwgMjUzLCAwLjI5KTsKICBjdXJzb3I6IHBvaW50ZXI7Cn0KCmJ1 -dHRvbltkaXNhYmxlZF0gewogIGJhY2tncm91bmQtY29sb3I6IHJnYmEoMjU1LDI1NSwyNTUsLjEyKTsK -ICBjb2xvcjogcmdiYSgyNTUsMjU1LDI1NSwuMzcpOwogIGN1cnNvcjogbm90LWFsbG93ZWQ7Cn0KCi8q -IENoYW5nZSBlZGl0IHBhbmVsIGJ1dHRvbiBjb2xvcnMgKi8KLmVkaXQtcGFuZWwgLmJ1dHRvbiwgLmVk -aXQtcGFuZWwgYnV0dG9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDYzLCAxMDQsIDE0OCwgMC42 -KTsKICBjb2xvcjogd2hpdGU7Cn0KLmVkaXQtcGFuZWwgLmJ1dHRvbjpob3ZlciwgLmVkaXQtcGFuZWwg -YnV0dG9uOmhvdmVyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDEwMSwgMTUzLCAyMDgsIDAuNik7 -CiAgY29sb3I6IHdoaXRlOwp9CgovKgogKiBBZGp1c3RtZW50cyB0byBhbGlnbiBtYXRlcmlhbCBpY29u -cyBpbiB0aGUgdG9vbGJhciBidXR0b25zLgoqLwouYWN0aW9uLWJ1dHRvbiA+IHNwYW4gewogIHBvc2l0 -aW9uOiByZWxhdGl2ZTsKICB0b3A6IC0zcHg7Cn0KCi5hY3Rpb24tYnV0dG9uIC5tYXRlcmlhbC1pY29u -cyB7CiAgdG9wOiA0cHg7Cn0KCi8qIERvbid0IHNoaWZ0IHRoZSBpY29uIHdoZW4gaXQncyBhIGRpcmVj -dCBjaGlsZCBvZiB0aGUgYnV0dG9uICovCi5hY3Rpb24tYnV0dG9uID4gLm1hdGVyaWFsLWljb25zIHsK -ICB0b3A6IDFweDsKfQoKLyogU2hpZnQgdGhlIHRleHQgdG8gY2VudGVyIHdpdGggdGhlIGljb24uICov -Ci5hY3Rpb24tYnV0dG9uID4gc3Bhbi5sYWJlbCB7CiAgcG9zaXRpb246cmVsYXRpdmU7CiAgdG9wOiAt -NHB4Owp9CgouYWN0aW9uLWJ1dHRvbiAubWF0ZXJpYWwtaWNvbnMgewogIGZvbnQtc2l6ZTogMjBweDsK -ICBwb3NpdGlvbjogcmVsYXRpdmU7Cn0KCi5wbGFjZWhvbGRlciB7CiAgY29sb3I6ICM3Nzc7CiAgdGV4 -dC1hbGlnbjogY2VudGVyOwogIG1hcmdpbi10b3A6IDNlbSAhaW1wb3J0YW50Owp9CgovKioKICogSExK -UyBPdmVycmlkZXMKICovCi5obGpzIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiAjMTIyMDJmOyAvKiAkZGFy -ay1jb2RlLWJhY2tncm91bmQtY29sb3IgKi8KICBjb2xvcjogI2MwYzJjNTsgLyogJGRhcmstZWRpdG9y -LXRleHQgKi8KICBkaXNwbGF5OiBibG9jazsKICBvdmVyZmxvdy14OiBhdXRvOwogIHBhZGRpbmc6IDAu -NWVtOwogIC8qKgogICAqIFRoaXMgYWxsb3dzIHRoZSBwZXItbGluZSBoaWdobGlnaHRzIHRvIHNob3cu -CiAgICovCiAgYmFja2dyb3VuZDogbm9uZTsKfQoKLmhsanMta2V5d29yZCwKLmhsanMtc2VsZWN0b3It -dGFnLAouaGxqcy1kZWxldGlvbiB7CiAgY29sb3I6ICM1MWM2ODY7IC8qIGNtLWtleXdvcmQgKi8KfQoK -LmhsanMtbnVtYmVyIHsKICBjb2xvcjogIzYyNzk3ODsgLyogY20tbnVtYmVyICovCn0KCi5obGpzLWNv -bW1lbnQgewogIGNvbG9yOiAjOTE5OGI0OyAvKiBjbS1jb21tZW50ICovCn0KCi5obGpzLWxpdGVyYWwg -ewogIGNvbG9yOiAjZWU4NjY2OyAvKiBjbS1hdG9tICovCn0KCi5obGpzLXN0cmluZyB7CiAgY29sb3I6 -ICNlNTUwNzQ7IC8qIGNtLXN0cmluZyAqLwp9CgouaGxqcy12YXJpYWJsZSB7CiAgY29sb3I6ICMxNmFk -Y2E7IC8qIGNtLXZhcmlhYmxlICovCn0KCi5obGpzLWxpbmsgewogIGNvbG9yOiAjZTU1MDc0OyAvKiBj -bS1zdHJpbmcgKi8KfQouaGxqcy1zZWN0aW9uLAouaGxqcy10eXBlLAouaGxqcy1idWlsdF9pbiwKLmhs -anMtdGl0bGUgewogIGNvbG9yOiAjZWU4NjY2OyAvKiBjbS12YXJpYWJsZS0yICovCn0KCi5obGpzLWFk -ZGl0aW9uIHsKICBjb2xvcjogIzI2Mzk1MjsgLyogJGRhcmstc2VsZWN0aW9uLWNvbG9yICovCn0KCi5o -bGpzLW1ldGEgewogIGNvbG9yOiAjNjI3OTc4Owp9Cg== -'''; - -String? _migration_js; -// migration_dart md5 is '1f11f086ebc0013cdd15679b588bd03e' -String _migration_js_base64 = ''' -KGZ1bmN0aW9uIGRhcnRQcm9ncmFtKCl7ZnVuY3Rpb24gY29weVByb3BlcnRpZXMoYSxiKXt2YXIgcz1P -YmplY3Qua2V5cyhhKQpmb3IodmFyIHI9MDtyPHMubGVuZ3RoO3IrKyl7dmFyIHE9c1tyXQpiW3FdPWFb -cV19fWZ1bmN0aW9uIG1peGluUHJvcGVydGllc0hhcmQoYSxiKXt2YXIgcz1PYmplY3Qua2V5cyhhKQpm -b3IodmFyIHI9MDtyPHMubGVuZ3RoO3IrKyl7dmFyIHE9c1tyXQppZighYi5oYXNPd25Qcm9wZXJ0eShx -KSliW3FdPWFbcV19fWZ1bmN0aW9uIG1peGluUHJvcGVydGllc0Vhc3koYSxiKXtPYmplY3QuYXNzaWdu -KGIsYSl9dmFyIHo9ZnVuY3Rpb24oKXt2YXIgcz1mdW5jdGlvbigpe30Kcy5wcm90b3R5cGU9e3A6e319 -CnZhciByPW5ldyBzKCkKaWYoIShyLl9fcHJvdG9fXyYmci5fX3Byb3RvX18ucD09PXMucHJvdG90eXBl -LnApKXJldHVybiBmYWxzZQp0cnl7aWYodHlwZW9mIG5hdmlnYXRvciE9InVuZGVmaW5lZCImJnR5cGVv -ZiBuYXZpZ2F0b3IudXNlckFnZW50PT0ic3RyaW5nIiYmbmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9m -KCJDaHJvbWUvIik+PTApcmV0dXJuIHRydWUKaWYodHlwZW9mIHZlcnNpb249PSJmdW5jdGlvbiImJnZl -cnNpb24ubGVuZ3RoPT0wKXt2YXIgcT12ZXJzaW9uKCkKaWYoL15cZCtcLlxkK1wuXGQrXC5cZCskLy50 -ZXN0KHEpKXJldHVybiB0cnVlfX1jYXRjaChwKXt9cmV0dXJuIGZhbHNlfSgpCmZ1bmN0aW9uIGluaGVy -aXQoYSxiKXthLnByb3RvdHlwZS5jb25zdHJ1Y3Rvcj1hCmEucHJvdG90eXBlWyIkaSIrYS5uYW1lXT1h -CmlmKGIhPW51bGwpe2lmKHope2EucHJvdG90eXBlLl9fcHJvdG9fXz1iLnByb3RvdHlwZQpyZXR1cm59 -dmFyIHM9T2JqZWN0LmNyZWF0ZShiLnByb3RvdHlwZSkKY29weVByb3BlcnRpZXMoYS5wcm90b3R5cGUs -cykKYS5wcm90b3R5cGU9c319ZnVuY3Rpb24gaW5oZXJpdE1hbnkoYSxiKXtmb3IodmFyIHM9MDtzPGIu -bGVuZ3RoO3MrKylpbmhlcml0KGJbc10sYSl9ZnVuY3Rpb24gbWl4aW5FYXN5KGEsYil7bWl4aW5Qcm9w -ZXJ0aWVzRWFzeShiLnByb3RvdHlwZSxhLnByb3RvdHlwZSkKYS5wcm90b3R5cGUuY29uc3RydWN0b3I9 -YX1mdW5jdGlvbiBtaXhpbkhhcmQoYSxiKXttaXhpblByb3BlcnRpZXNIYXJkKGIucHJvdG90eXBlLGEu -cHJvdG90eXBlKQphLnByb3RvdHlwZS5jb25zdHJ1Y3Rvcj1hfWZ1bmN0aW9uIGxhenlPbGQoYSxiLGMs -ZCl7dmFyIHM9YQphW2JdPXMKYVtjXT1mdW5jdGlvbigpe2FbY109ZnVuY3Rpb24oKXtBLmFnKGIpfQp2 -YXIgcgp2YXIgcT1kCnRyeXtpZihhW2JdPT09cyl7cj1hW2JdPXEKcj1hW2JdPWQoKX1lbHNlIHI9YVti -XX1maW5hbGx5e2lmKHI9PT1xKWFbYl09bnVsbAphW2NdPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXNbYl19 -fXJldHVybiByfX1mdW5jdGlvbiBsYXp5KGEsYixjLGQpe3ZhciBzPWEKYVtiXT1zCmFbY109ZnVuY3Rp -b24oKXtpZihhW2JdPT09cylhW2JdPWQoKQphW2NdPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXNbYl19CnJl -dHVybiBhW2JdfX1mdW5jdGlvbiBsYXp5RmluYWwoYSxiLGMsZCl7dmFyIHM9YQphW2JdPXMKYVtjXT1m -dW5jdGlvbigpe2lmKGFbYl09PT1zKXt2YXIgcj1kKCkKaWYoYVtiXSE9PXMpQS5wUihiKQphW2JdPXJ9 -dmFyIHE9YVtiXQphW2NdPWZ1bmN0aW9uKCl7cmV0dXJuIHF9CnJldHVybiBxfX1mdW5jdGlvbiBtYWtl -Q29uc3RMaXN0KGEpe2EuaW1tdXRhYmxlJGxpc3Q9QXJyYXkKYS5maXhlZCRsZW5ndGg9QXJyYXkKcmV0 -dXJuIGF9ZnVuY3Rpb24gY29udmVydFRvRmFzdE9iamVjdChhKXtmdW5jdGlvbiB0KCl7fXQucHJvdG90 -eXBlPWEKbmV3IHQoKQpyZXR1cm4gYX1mdW5jdGlvbiBjb252ZXJ0QWxsVG9GYXN0T2JqZWN0KGEpe2Zv -cih2YXIgcz0wO3M8YS5sZW5ndGg7KytzKWNvbnZlcnRUb0Zhc3RPYmplY3QoYVtzXSl9dmFyIHk9MApm -dW5jdGlvbiBpbnN0YW5jZVRlYXJPZmZHZXR0ZXIoYSxiKXt2YXIgcz1udWxsCnJldHVybiBhP2Z1bmN0 -aW9uKGMpe2lmKHM9PT1udWxsKXM9QS5VMihiKQpyZXR1cm4gbmV3IHMoYyx0aGlzKX06ZnVuY3Rpb24o -KXtpZihzPT09bnVsbClzPUEuVTIoYikKcmV0dXJuIG5ldyBzKHRoaXMsbnVsbCl9fWZ1bmN0aW9uIHN0 -YXRpY1RlYXJPZmZHZXR0ZXIoYSl7dmFyIHM9bnVsbApyZXR1cm4gZnVuY3Rpb24oKXtpZihzPT09bnVs -bClzPUEuVTIoYSkucHJvdG90eXBlCnJldHVybiBzfX12YXIgeD0wCmZ1bmN0aW9uIHRlYXJPZmZQYXJh -bWV0ZXJzKGEsYixjLGQsZSxmLGcsaCxpLGope2lmKHR5cGVvZiBoPT0ibnVtYmVyIiloKz14CnJldHVy -bntjbzphLGlTOmIsaUk6YyxyQzpkLGRWOmUsY3M6ZixmczpnLGZUOmgsYUk6aXx8MCxuREE6an19ZnVu -Y3Rpb24gaW5zdGFsbFN0YXRpY1RlYXJPZmYoYSxiLGMsZCxlLGYsZyxoKXt2YXIgcz10ZWFyT2ZmUGFy -YW1ldGVycyhhLHRydWUsZmFsc2UsYyxkLGUsZixnLGgsZmFsc2UpCnZhciByPXN0YXRpY1RlYXJPZmZH -ZXR0ZXIocykKYVtiXT1yfWZ1bmN0aW9uIGluc3RhbGxJbnN0YW5jZVRlYXJPZmYoYSxiLGMsZCxlLGYs -ZyxoLGksail7Yz0hIWMKdmFyIHM9dGVhck9mZlBhcmFtZXRlcnMoYSxmYWxzZSxjLGQsZSxmLGcsaCxp -LCEhaikKdmFyIHI9aW5zdGFuY2VUZWFyT2ZmR2V0dGVyKGMscykKYVtiXT1yfWZ1bmN0aW9uIHNldE9y -VXBkYXRlSW50ZXJjZXB0b3JzQnlUYWcoYSl7dmFyIHM9di5pbnRlcmNlcHRvcnNCeVRhZwppZighcyl7 -di5pbnRlcmNlcHRvcnNCeVRhZz1hCnJldHVybn1jb3B5UHJvcGVydGllcyhhLHMpfWZ1bmN0aW9uIHNl -dE9yVXBkYXRlTGVhZlRhZ3MoYSl7dmFyIHM9di5sZWFmVGFncwppZighcyl7di5sZWFmVGFncz1hCnJl -dHVybn1jb3B5UHJvcGVydGllcyhhLHMpfWZ1bmN0aW9uIHVwZGF0ZVR5cGVzKGEpe3ZhciBzPXYudHlw -ZXMKdmFyIHI9cy5sZW5ndGgKcy5wdXNoLmFwcGx5KHMsYSkKcmV0dXJuIHJ9ZnVuY3Rpb24gdXBkYXRl -SG9sZGVyKGEsYil7Y29weVByb3BlcnRpZXMoYixhKQpyZXR1cm4gYX12YXIgaHVua0hlbHBlcnM9ZnVu -Y3Rpb24oKXt2YXIgcz1mdW5jdGlvbihhLGIsYyxkLGUpe3JldHVybiBmdW5jdGlvbihmLGcsaCxpKXty -ZXR1cm4gaW5zdGFsbEluc3RhbmNlVGVhck9mZihmLGcsYSxiLGMsZCxbaF0saSxlLGZhbHNlKX19LHI9 -ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIGZ1bmN0aW9uKGUsZixnLGgpe3JldHVybiBpbnN0YWxsU3Rh -dGljVGVhck9mZihlLGYsYSxiLGMsW2ddLGgsZCl9fQpyZXR1cm57aW5oZXJpdDppbmhlcml0LGluaGVy -aXRNYW55OmluaGVyaXRNYW55LG1peGluOm1peGluRWFzeSxtaXhpbkhhcmQ6bWl4aW5IYXJkLGluc3Rh -bGxTdGF0aWNUZWFyT2ZmOmluc3RhbGxTdGF0aWNUZWFyT2ZmLGluc3RhbGxJbnN0YW5jZVRlYXJPZmY6 -aW5zdGFsbEluc3RhbmNlVGVhck9mZixfaW5zdGFuY2VfMHU6cygwLDAsbnVsbCxbIiQwIl0sMCksX2lu -c3RhbmNlXzF1OnMoMCwxLG51bGwsWyIkMSJdLDApLF9pbnN0YW5jZV8ydTpzKDAsMixudWxsLFsiJDIi -XSwwKSxfaW5zdGFuY2VfMGk6cygxLDAsbnVsbCxbIiQwIl0sMCksX2luc3RhbmNlXzFpOnMoMSwxLG51 -bGwsWyIkMSJdLDApLF9pbnN0YW5jZV8yaTpzKDEsMixudWxsLFsiJDIiXSwwKSxfc3RhdGljXzA6cigw -LG51bGwsWyIkMCJdLDApLF9zdGF0aWNfMTpyKDEsbnVsbCxbIiQxIl0sMCksX3N0YXRpY18yOnIoMixu -dWxsLFsiJDIiXSwwKSxtYWtlQ29uc3RMaXN0Om1ha2VDb25zdExpc3QsbGF6eTpsYXp5LGxhenlGaW5h -bDpsYXp5RmluYWwsbGF6eU9sZDpsYXp5T2xkLHVwZGF0ZUhvbGRlcjp1cGRhdGVIb2xkZXIsY29udmVy -dFRvRmFzdE9iamVjdDpjb252ZXJ0VG9GYXN0T2JqZWN0LHVwZGF0ZVR5cGVzOnVwZGF0ZVR5cGVzLHNl -dE9yVXBkYXRlSW50ZXJjZXB0b3JzQnlUYWc6c2V0T3JVcGRhdGVJbnRlcmNlcHRvcnNCeVRhZyxzZXRP -clVwZGF0ZUxlYWZUYWdzOnNldE9yVXBkYXRlTGVhZlRhZ3N9fSgpCmZ1bmN0aW9uIGluaXRpYWxpemVE -ZWZlcnJlZEh1bmsoYSl7eD12LnR5cGVzLmxlbmd0aAphKGh1bmtIZWxwZXJzLHYsdywkKX12YXIgQT17 -Rks6ZnVuY3Rpb24gRksoKXt9LApHSihhLGIsYyl7aWYoYi5DKCJiUTwwPiIpLmIoYSkpcmV0dXJuIG5l -dyBBLm9sKGEsYi5DKCJAPDA+IikuS3EoYykuQygib2w8MSwyPiIpKQpyZXR1cm4gbmV3IEEuWnkoYSxi -LkMoIkA8MD4iKS5LcShjKS5DKCJaeTwxLDI+IikpfSwKRyhhKXtyZXR1cm4gbmV3IEEuYygiRmllbGQg -JyIrYSsiJyBoYXMgYmVlbiBhc3NpZ25lZCBkdXJpbmcgaW5pdGlhbGl6YXRpb24uIil9LApsYShhKXty -ZXR1cm4gbmV3IEEuYygiRmllbGQgJyIrYSsiJyBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQuIil9LApS -SShhKXtyZXR1cm4gbmV3IEEuYygiRmllbGQgJyIrYSsiJyBoYXMgYWxyZWFkeSBiZWVuIGluaXRpYWxp -emVkLiIpfSwKb28oYSl7dmFyIHMscj1hXjQ4CmlmKHI8PTkpcmV0dXJuIHIKcz1hfDMyCmlmKDk3PD1z -JiZzPD0xMDIpcmV0dXJuIHMtODcKcmV0dXJuLTF9LAp5YyhhLGIpe2E9YStiJjUzNjg3MDkxMQphPWEr -KChhJjUyNDI4Nyk8PDEwKSY1MzY4NzA5MTEKcmV0dXJuIGFeYT4+PjZ9LApxTChhKXthPWErKChhJjY3 -MTA4ODYzKTw8MykmNTM2ODcwOTExCmFePWE+Pj4xMQpyZXR1cm4gYSsoKGEmMTYzODMpPDwxNSkmNTM2 -ODcwOTExfSwKY2IoYSxiLGMpe3JldHVybiBhfSwKcUMoYSxiLGMsZCl7QS5rMShiLCJzdGFydCIpCmlm -KGMhPW51bGwpe0EuazEoYywiZW5kIikKaWYoYj5jKUEudihBLlRFKGIsMCxjLCJzdGFydCIsbnVsbCkp -fXJldHVybiBuZXcgQS5uSChhLGIsYyxkLkMoIm5IPDA+IikpfSwKSzEoYSxiLGMsZCl7aWYodC5VLmIo -YSkpcmV0dXJuIG5ldyBBLnh5KGEsYixjLkMoIkA8MD4iKS5LcShkKS5DKCJ4eTwxLDI+IikpCnJldHVy -biBuZXcgQS5pMShhLGIsYy5DKCJAPDA+IikuS3EoZCkuQygiaTE8MSwyPiIpKX0sCmJLKGEsYixjKXt2 -YXIgcz0iY291bnQiCmlmKHQuVS5iKGEpKXtBLk1SKGIscyx0LlMpCkEuazEoYixzKQpyZXR1cm4gbmV3 -IEEuZDUoYSxiLGMuQygiZDU8MD4iKSl9QS5NUihiLHMsdC5TKQpBLmsxKGIscykKcmV0dXJuIG5ldyBB -LkFNKGEsYixjLkMoIkFNPDA+IikpfSwKV3AoKXtyZXR1cm4gbmV3IEEubGooIk5vIGVsZW1lbnQiKX0s -CkFtKCl7cmV0dXJuIG5ldyBBLmxqKCJUb28gbWFueSBlbGVtZW50cyIpfSwKYXIoKXtyZXR1cm4gbmV3 -IEEubGooIlRvbyBmZXcgZWxlbWVudHMiKX0sCkJSOmZ1bmN0aW9uIEJSKCl7fSwKQ2Y6ZnVuY3Rpb24g -Q2YoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKWnk6ZnVuY3Rpb24gWnkoYSxiKXt0aGlzLmE9YQp0 -aGlzLiR0aT1ifSwKb2w6ZnVuY3Rpb24gb2woYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKVXE6ZnVu -Y3Rpb24gVXEoKXt9LApqVjpmdW5jdGlvbiBqVihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApjOmZ1 -bmN0aW9uIGMoYSl7dGhpcy5hPWF9LApxajpmdW5jdGlvbiBxaihhKXt0aGlzLmE9YX0sCkhiOmZ1bmN0 -aW9uIEhiKCl7fSwKYlE6ZnVuY3Rpb24gYlEoKXt9LAphTDpmdW5jdGlvbiBhTCgpe30sCm5IOmZ1bmN0 -aW9uIG5IKGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy4kdGk9ZH0sCmE3OmZ1 -bmN0aW9uIGE3KGEsYixjKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz0wCl8uZD1udWxsCl8uJHRp -PWN9LAppMTpmdW5jdGlvbiBpMShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCnh5 -OmZ1bmN0aW9uIHh5KGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKTUg6ZnVuY3Rp -b24gTUgoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPW51bGwKXy5iPWEKXy5jPWIKXy4kdGk9Y30sCmxKOmZ1 -bmN0aW9uIGxKKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKVTU6ZnVuY3Rpb24g -VTUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuJHRpPWN9LApTTzpmdW5jdGlvbiBTTyhhLGIs -Yyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy4kdGk9Y30sCkFNOmZ1bmN0aW9uIEFNKGEsYixjKXt0aGlz -LmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKZDU6ZnVuY3Rpb24gZDUoYSxiLGMpe3RoaXMuYT1hCnRo -aXMuYj1iCnRoaXMuJHRpPWN9LApVMTpmdW5jdGlvbiBVMShhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIK -dGhpcy4kdGk9Y30sCk1COmZ1bmN0aW9uIE1CKGEpe3RoaXMuJHRpPWF9LApGdTpmdW5jdGlvbiBGdShh -KXt0aGlzLiR0aT1hfSwKdTY6ZnVuY3Rpb24gdTYoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKSkI6 -ZnVuY3Rpb24gSkIoYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKU1U6ZnVuY3Rpb24gU1UoKXt9LApS -ZTpmdW5jdGlvbiBSZSgpe30sCncyOmZ1bmN0aW9uIHcyKCl7fSwKd3Y6ZnVuY3Rpb24gd3YoYSl7dGhp -cy5hPWF9LApRQzpmdW5jdGlvbiBRQygpe30sCmRjKCl7dGhyb3cgQS5iKEEuTDQoIkNhbm5vdCBtb2Rp -ZnkgdW5tb2RpZmlhYmxlIE1hcCIpKX0sCk5RKGEpe3ZhciBzPXYubWFuZ2xlZEdsb2JhbE5hbWVzW2Fd -CmlmKHMhPW51bGwpcmV0dXJuIHMKcmV0dXJuIm1pbmlmaWVkOiIrYX0sCkdwKGEsYil7dmFyIHMKaWYo -YiE9bnVsbCl7cz1iLngKaWYocyE9bnVsbClyZXR1cm4gc31yZXR1cm4gdC5hVS5iKGEpfSwKZChhKXt2 -YXIgcwppZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKaWYodHlwZW9mIGE9PSJudW1iZXIiKXtp -ZihhIT09MClyZXR1cm4iIithfWVsc2UgaWYoITA9PT1hKXJldHVybiJ0cnVlIgplbHNlIGlmKCExPT09 -YSlyZXR1cm4iZmFsc2UiCmVsc2UgaWYoYT09bnVsbClyZXR1cm4ibnVsbCIKcz1KLllTKGEpCnJldHVy -biBzfSwKZVEoYSl7dmFyIHMscj0kLnh1CmlmKHI9PW51bGwpcj0kLnh1PVN5bWJvbCgiaWRlbnRpdHlI -YXNoQ29kZSIpCnM9YVtyXQppZihzPT1udWxsKXtzPU1hdGgucmFuZG9tKCkqMHgzZmZmZmZmZnwwCmFb -cl09c31yZXR1cm4gc30sCkhwKGEsYil7dmFyIHMscixxLHAsbyxuPW51bGwsbT0vXlxzKlsrLV0/KCgw -eFthLWYwLTldKyl8KFxkKyl8KFthLXowLTldKykpXHMqJC9pLmV4ZWMoYSkKaWYobT09bnVsbClyZXR1 -cm4gbgppZigzPj1tLmxlbmd0aClyZXR1cm4gQS5PSChtLDMpCnM9bVszXQppZihiPT1udWxsKXtpZihz -IT1udWxsKXJldHVybiBwYXJzZUludChhLDEwKQppZihtWzJdIT1udWxsKXJldHVybiBwYXJzZUludChh -LDE2KQpyZXR1cm4gbn1pZihiPDJ8fGI+MzYpdGhyb3cgQS5iKEEuVEUoYiwyLDM2LCJyYWRpeCIsbikp -CmlmKGI9PT0xMCYmcyE9bnVsbClyZXR1cm4gcGFyc2VJbnQoYSwxMCkKaWYoYjwxMHx8cz09bnVsbCl7 -cj1iPD0xMD80NytiOjg2K2IKcT1tWzFdCmZvcihwPXEubGVuZ3RoLG89MDtvPHA7KytvKWlmKChCLnhC -LlcocSxvKXwzMik+cilyZXR1cm4gbn1yZXR1cm4gcGFyc2VJbnQoYSxiKX0sCmxoKGEpe3JldHVybiBB -Lkg1KGEpfSwKSDUoYSl7dmFyIHMscixxLHAKaWYoYSBpbnN0YW5jZW9mIEEuTWgpcmV0dXJuIEEuZG0o -QS56SyhhKSxudWxsKQpzPUouaWEoYSkKaWYocz09PUIuT2t8fHM9PT1CLlVifHx0LmJJLmIoYSkpe3I9 -Qi5PNChhKQppZihyIT09Ik9iamVjdCImJnIhPT0iIilyZXR1cm4gcgpxPWEuY29uc3RydWN0b3IKaWYo -dHlwZW9mIHE9PSJmdW5jdGlvbiIpe3A9cS5uYW1lCmlmKHR5cGVvZiBwPT0ic3RyaW5nIiYmcCE9PSJP -YmplY3QiJiZwIT09IiIpcmV0dXJuIHB9fXJldHVybiBBLmRtKEEueksoYSksbnVsbCl9LApNMCgpe2lm -KCEhc2VsZi5sb2NhdGlvbilyZXR1cm4gc2VsZi5sb2NhdGlvbi5ocmVmCnJldHVybiBudWxsfSwKZnco -YSxiLGMpe3ZhciBzLHIscSxwCmlmKGM8PTUwMCYmYj09PTAmJmM9PT1hLmxlbmd0aClyZXR1cm4gU3Ry -aW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLGEpCmZvcihzPWIscj0iIjtzPGM7cz1xKXtxPXMrNTAw -CnA9cTxjP3E6YwpyKz1TdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsYS5zdWJhcnJheShzLHAp -KX1yZXR1cm4gcn0sCkx3KGEpe3ZhciBzCmlmKDA8PWEpe2lmKGE8PTY1NTM1KXJldHVybiBTdHJpbmcu -ZnJvbUNoYXJDb2RlKGEpCmlmKGE8PTExMTQxMTEpe3M9YS02NTUzNgpyZXR1cm4gU3RyaW5nLmZyb21D -aGFyQ29kZSgoQi5qbi53RyhzLDEwKXw1NTI5Nik+Pj4wLHMmMTAyM3w1NjMyMCl9fXRocm93IEEuYihB -LlRFKGEsMCwxMTE0MTExLG51bGwsbnVsbCkpfSwKbzIoYSl7aWYoYS5kYXRlPT09dm9pZCAwKWEuZGF0 -ZT1uZXcgRGF0ZShhLmEpCnJldHVybiBhLmRhdGV9LAp0SihhKXt2YXIgcz1BLm8yKGEpLmdldEZ1bGxZ -ZWFyKCkrMApyZXR1cm4gc30sCk5TKGEpe3ZhciBzPUEubzIoYSkuZ2V0TW9udGgoKSsxCnJldHVybiBz -fSwKakEoYSl7dmFyIHM9QS5vMihhKS5nZXREYXRlKCkrMApyZXR1cm4gc30sCklYKGEpe3ZhciBzPUEu -bzIoYSkuZ2V0SG91cnMoKSswCnJldHVybiBzfSwKY2goYSl7dmFyIHM9QS5vMihhKS5nZXRNaW51dGVz -KCkrMApyZXR1cm4gc30sCkpkKGEpe3ZhciBzPUEubzIoYSkuZ2V0U2Vjb25kcygpKzAKcmV0dXJuIHN9 -LApvMShhKXt2YXIgcz1BLm8yKGEpLmdldE1pbGxpc2Vjb25kcygpKzAKcmV0dXJuIHN9LAp6byhhLGIs -Yyl7dmFyIHMscixxPXt9CnEuYT0wCnM9W10Kcj1bXQpxLmE9Yi5sZW5ndGgKQi5ObS5GVihzLGIpCnEu -Yj0iIgppZihjIT1udWxsJiZjLmEhPT0wKWMuSygwLG5ldyBBLkNqKHEscixzKSkKcmV0dXJuIEouSnko -YSxuZXcgQS5MSShCLlRlLDAscyxyLDApKX0sCkVrKGEsYixjKXt2YXIgcyxyLHE9Yz09bnVsbHx8Yy5h -PT09MAppZihxKXtzPWIubGVuZ3RoCmlmKHM9PT0wKXtpZighIWEuJDApcmV0dXJuIGEuJDAoKX1lbHNl -IGlmKHM9PT0xKXtpZighIWEuJDEpcmV0dXJuIGEuJDEoYlswXSl9ZWxzZSBpZihzPT09Mil7aWYoISFh -LiQyKXJldHVybiBhLiQyKGJbMF0sYlsxXSl9ZWxzZSBpZihzPT09Myl7aWYoISFhLiQzKXJldHVybiBh -LiQzKGJbMF0sYlsxXSxiWzJdKX1lbHNlIGlmKHM9PT00KXtpZighIWEuJDQpcmV0dXJuIGEuJDQoYlsw -XSxiWzFdLGJbMl0sYlszXSl9ZWxzZSBpZihzPT09NSlpZighIWEuJDUpcmV0dXJuIGEuJDUoYlswXSxi -WzFdLGJbMl0sYlszXSxiWzRdKQpyPWFbIiIrIiQiK3NdCmlmKHIhPW51bGwpcmV0dXJuIHIuYXBwbHko -YSxiKX1yZXR1cm4gQS5aVChhLGIsYyl9LApaVChhLGIsYyl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGos -aSxoLGcsZj1iLmxlbmd0aCxlPWEuJFIKaWYoZjxlKXJldHVybiBBLnpvKGEsYixjKQpzPWEuJEQKcj1z -PT1udWxsCnE9IXI/cygpOm51bGwKcD1KLmlhKGEpCm89cC4kQwppZih0eXBlb2Ygbz09InN0cmluZyIp -bz1wW29dCmlmKHIpe2lmKGMhPW51bGwmJmMuYSE9PTApcmV0dXJuIEEuem8oYSxiLGMpCmlmKGY9PT1l -KXJldHVybiBvLmFwcGx5KGEsYikKcmV0dXJuIEEuem8oYSxiLGMpfWlmKEFycmF5LmlzQXJyYXkocSkp -e2lmKGMhPW51bGwmJmMuYSE9PTApcmV0dXJuIEEuem8oYSxiLGMpCm49ZStxLmxlbmd0aAppZihmPm4p -cmV0dXJuIEEuem8oYSxiLG51bGwpCmlmKGY8bil7bT1xLnNsaWNlKGYtZSkKbD1BLlkxKGIsITAsdC56 -KQpCLk5tLkZWKGwsbSl9ZWxzZSBsPWIKcmV0dXJuIG8uYXBwbHkoYSxsKX1lbHNle2lmKGY+ZSlyZXR1 -cm4gQS56byhhLGIsYykKbD1BLlkxKGIsITAsdC56KQprPU9iamVjdC5rZXlzKHEpCmlmKGM9PW51bGwp -Zm9yKHI9ay5sZW5ndGgsaj0wO2o8ay5sZW5ndGg7ay5sZW5ndGg9PT1yfHwoMCxBLmxrKShrKSwrK2op -e2k9cVtBLm4oa1tqXSldCmlmKEIuTnY9PT1pKXJldHVybiBBLnpvKGEsbCxjKQpCLk5tLmkobCxpKX1l -bHNle2ZvcihyPWsubGVuZ3RoLGg9MCxqPTA7ajxrLmxlbmd0aDtrLmxlbmd0aD09PXJ8fCgwLEEubGsp -KGspLCsrail7Zz1BLm4oa1tqXSkKaWYoYy54NChnKSl7KytoCkIuTm0uaShsLGMucSgwLGcpKX1lbHNl -e2k9cVtnXQppZihCLk52PT09aSlyZXR1cm4gQS56byhhLGwsYykKQi5ObS5pKGwsaSl9fWlmKGghPT1j -LmEpcmV0dXJuIEEuem8oYSxsLGMpfXJldHVybiBvLmFwcGx5KGEsbCl9fSwKcFkoYSl7dGhyb3cgQS5i -KEEudEwoYSkpfSwKT0goYSxiKXtpZihhPT1udWxsKUouSG0oYSkKdGhyb3cgQS5iKEEuaihhLGIpKX0s -CmooYSxiKXt2YXIgcyxyPSJpbmRleCIKaWYoIUEub2soYikpcmV0dXJuIG5ldyBBLnUoITAsYixyLG51 -bGwpCnM9QS5JWihKLkhtKGEpKQppZihiPDB8fGI+PXMpcmV0dXJuIEEueEYoYixzLGEscikKcmV0dXJu -IEEuTzcoYixyKX0sCmF1KGEsYixjKXtpZihhPmMpcmV0dXJuIEEuVEUoYSwwLGMsInN0YXJ0IixudWxs -KQppZihiIT1udWxsKWlmKGI8YXx8Yj5jKXJldHVybiBBLlRFKGIsYSxjLCJlbmQiLG51bGwpCnJldHVy -biBuZXcgQS51KCEwLGIsImVuZCIsbnVsbCl9LAp0TChhKXtyZXR1cm4gbmV3IEEudSghMCxhLG51bGws -bnVsbCl9LApiKGEpe3ZhciBzLHIKaWYoYT09bnVsbClhPW5ldyBBLkYoKQpzPW5ldyBFcnJvcigpCnMu -ZGFydEV4Y2VwdGlvbj1hCnI9QS5oCmlmKCJkZWZpbmVQcm9wZXJ0eSIgaW4gT2JqZWN0KXtPYmplY3Qu -ZGVmaW5lUHJvcGVydHkocywibWVzc2FnZSIse2dldDpyfSkKcy5uYW1lPSIifWVsc2Ugcy50b1N0cmlu -Zz1yCnJldHVybiBzfSwKaCgpe3JldHVybiBKLllTKHRoaXMuZGFydEV4Y2VwdGlvbil9LAp2KGEpe3Ro -cm93IEEuYihhKX0sCmxrKGEpe3Rocm93IEEuYihBLmE0KGEpKX0sCmNNKGEpe3ZhciBzLHIscSxwLG8s -bgphPUEuZUEoYS5yZXBsYWNlKFN0cmluZyh7fSksIiRyZWNlaXZlciQiKSkKcz1hLm1hdGNoKC9cXFwk -W2EtekEtWl0rXFxcJC9nKQppZihzPT1udWxsKXM9QS5RSShbXSx0LnMpCnI9cy5pbmRleE9mKCJcXCRh -cmd1bWVudHNcXCQiKQpxPXMuaW5kZXhPZigiXFwkYXJndW1lbnRzRXhwclxcJCIpCnA9cy5pbmRleE9m -KCJcXCRleHByXFwkIikKbz1zLmluZGV4T2YoIlxcJG1ldGhvZFxcJCIpCm49cy5pbmRleE9mKCJcXCRy -ZWNlaXZlclxcJCIpCnJldHVybiBuZXcgQS5mOShhLnJlcGxhY2UobmV3IFJlZ0V4cCgiXFxcXFxcJGFy -Z3VtZW50c1xcXFxcXCQiLCJnIiksIigoPzp4fFteeF0pKikiKS5yZXBsYWNlKG5ldyBSZWdFeHAoIlxc -XFxcXCRhcmd1bWVudHNFeHByXFxcXFxcJCIsImciKSwiKCg/Onh8W154XSkqKSIpLnJlcGxhY2UobmV3 -IFJlZ0V4cCgiXFxcXFxcJGV4cHJcXFxcXFwkIiwiZyIpLCIoKD86eHxbXnhdKSopIikucmVwbGFjZShu -ZXcgUmVnRXhwKCJcXFxcXFwkbWV0aG9kXFxcXFxcJCIsImciKSwiKCg/Onh8W154XSkqKSIpLnJlcGxh -Y2UobmV3IFJlZ0V4cCgiXFxcXFxcJHJlY2VpdmVyXFxcXFxcJCIsImciKSwiKCg/Onh8W154XSkqKSIp -LHIscSxwLG8sbil9LApTNyhhKXtyZXR1cm4gZnVuY3Rpb24oJGV4cHIkKXt2YXIgJGFyZ3VtZW50c0V4 -cHIkPSIkYXJndW1lbnRzJCIKdHJ5eyRleHByJC4kbWV0aG9kJCgkYXJndW1lbnRzRXhwciQpfWNhdGNo -KHMpe3JldHVybiBzLm1lc3NhZ2V9fShhKX0sCk1qKGEpe3JldHVybiBmdW5jdGlvbigkZXhwciQpe3Ry -eXskZXhwciQuJG1ldGhvZCR9Y2F0Y2gocyl7cmV0dXJuIHMubWVzc2FnZX19KGEpfSwKVDMoYSxiKXt2 -YXIgcz1iPT1udWxsLHI9cz9udWxsOmIubWV0aG9kCnJldHVybiBuZXcgQS5heihhLHIscz9udWxsOmIu -cmVjZWl2ZXIpfSwKUnUoYSl7dmFyIHMKaWYoYT09bnVsbClyZXR1cm4gbmV3IEEudGUoYSkKaWYoYSBp -bnN0YW5jZW9mIEEuYnEpe3M9YS5hCnJldHVybiBBLnRXKGEscz09bnVsbD90LksuYShzKTpzKX1pZih0 -eXBlb2YgYSE9PSJvYmplY3QiKXJldHVybiBhCmlmKCJkYXJ0RXhjZXB0aW9uIiBpbiBhKXJldHVybiBB -LnRXKGEsYS5kYXJ0RXhjZXB0aW9uKQpyZXR1cm4gQS50bChhKX0sCnRXKGEsYil7aWYodC51LmIoYikp -aWYoYi4kdGhyb3duSnNFcnJvcj09bnVsbCliLiR0aHJvd25Kc0Vycm9yPWEKcmV0dXJuIGJ9LAp0bChh -KXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGU9bnVsbAppZighKCJtZXNzYWdlIiBpbiBh -KSlyZXR1cm4gYQpzPWEubWVzc2FnZQppZigibnVtYmVyIiBpbiBhJiZ0eXBlb2YgYS5udW1iZXI9PSJu -dW1iZXIiKXtyPWEubnVtYmVyCnE9ciY2NTUzNQppZigoQi5qbi53RyhyLDE2KSY4MTkxKT09PTEwKXN3 -aXRjaChxKXtjYXNlIDQzODpyZXR1cm4gQS50VyhhLEEuVDMoQS5kKHMpKyIgKEVycm9yICIrcSsiKSIs -ZSkpCmNhc2UgNDQ1OmNhc2UgNTAwNzpwPUEuZChzKQpyZXR1cm4gQS50VyhhLG5ldyBBLlcwKHArIiAo -RXJyb3IgIitxKyIpIixlKSl9fWlmKGEgaW5zdGFuY2VvZiBUeXBlRXJyb3Ipe289JC5TbigpCm49JC5s -cSgpCm09JC5OOSgpCmw9JC5pSSgpCms9JC5VTigpCmo9JC5aaCgpCmk9JC5yTigpCiQuYzMoKQpoPSQu -SEsoKQpnPSQucjEoKQpmPW8ucVMocykKaWYoZiE9bnVsbClyZXR1cm4gQS50VyhhLEEuVDMoQS5uKHMp -LGYpKQplbHNle2Y9bi5xUyhzKQppZihmIT1udWxsKXtmLm1ldGhvZD0iY2FsbCIKcmV0dXJuIEEudFco -YSxBLlQzKEEubihzKSxmKSl9ZWxzZXtmPW0ucVMocykKaWYoZj09bnVsbCl7Zj1sLnFTKHMpCmlmKGY9 -PW51bGwpe2Y9ay5xUyhzKQppZihmPT1udWxsKXtmPWoucVMocykKaWYoZj09bnVsbCl7Zj1pLnFTKHMp -CmlmKGY9PW51bGwpe2Y9bC5xUyhzKQppZihmPT1udWxsKXtmPWgucVMocykKaWYoZj09bnVsbCl7Zj1n -LnFTKHMpCnA9ZiE9bnVsbH1lbHNlIHA9ITB9ZWxzZSBwPSEwfWVsc2UgcD0hMH1lbHNlIHA9ITB9ZWxz -ZSBwPSEwfWVsc2UgcD0hMH1lbHNlIHA9ITAKaWYocCl7QS5uKHMpCnJldHVybiBBLnRXKGEsbmV3IEEu -VzAocyxmPT1udWxsP2U6Zi5tZXRob2QpKX19fXJldHVybiBBLnRXKGEsbmV3IEEudlYodHlwZW9mIHM9 -PSJzdHJpbmciP3M6IiIpKX1pZihhIGluc3RhbmNlb2YgUmFuZ2VFcnJvcil7aWYodHlwZW9mIHM9PSJz -dHJpbmciJiZzLmluZGV4T2YoImNhbGwgc3RhY2siKSE9PS0xKXJldHVybiBuZXcgQS5LWSgpCnM9ZnVu -Y3Rpb24oYil7dHJ5e3JldHVybiBTdHJpbmcoYil9Y2F0Y2goZCl7fXJldHVybiBudWxsfShhKQpyZXR1 -cm4gQS50VyhhLG5ldyBBLnUoITEsZSxlLHR5cGVvZiBzPT0ic3RyaW5nIj9zLnJlcGxhY2UoL15SYW5n -ZUVycm9yOlxzKi8sIiIpOnMpKX1pZih0eXBlb2YgSW50ZXJuYWxFcnJvcj09ImZ1bmN0aW9uIiYmYSBp -bnN0YW5jZW9mIEludGVybmFsRXJyb3IpaWYodHlwZW9mIHM9PSJzdHJpbmciJiZzPT09InRvbyBtdWNo -IHJlY3Vyc2lvbiIpcmV0dXJuIG5ldyBBLktZKCkKcmV0dXJuIGF9LAp0cyhhKXt2YXIgcwppZihhIGlu -c3RhbmNlb2YgQS5icSlyZXR1cm4gYS5iCmlmKGE9PW51bGwpcmV0dXJuIG5ldyBBLlhPKGEpCnM9YS4k -Y2FjaGVkVHJhY2UKaWYocyE9bnVsbClyZXR1cm4gcwpyZXR1cm4gYS4kY2FjaGVkVHJhY2U9bmV3IEEu -WE8oYSl9LApDVShhKXtpZihhPT1udWxsfHx0eXBlb2YgYSE9Im9iamVjdCIpcmV0dXJuIEouVTMoYSkK -ZWxzZSByZXR1cm4gQS5lUShhKX0sCkI3KGEsYil7dmFyIHMscixxLHA9YS5sZW5ndGgKZm9yKHM9MDtz -PHA7cz1xKXtyPXMrMQpxPXIrMQpiLlk1KDAsYVtzXSxhW3JdKX1yZXR1cm4gYn0sCmZ0KGEsYixjLGQs -ZSxmKXt0LlkuYShhKQpzd2l0Y2goQS5JWihiKSl7Y2FzZSAwOnJldHVybiBhLiQwKCkKY2FzZSAxOnJl -dHVybiBhLiQxKGMpCmNhc2UgMjpyZXR1cm4gYS4kMihjLGQpCmNhc2UgMzpyZXR1cm4gYS4kMyhjLGQs -ZSkKY2FzZSA0OnJldHVybiBhLiQ0KGMsZCxlLGYpfXRocm93IEEuYihuZXcgQS5DRCgiVW5zdXBwb3J0 -ZWQgbnVtYmVyIG9mIGFyZ3VtZW50cyBmb3Igd3JhcHBlZCBjbG9zdXJlIikpfSwKdFIoYSxiKXt2YXIg -cwppZihhPT1udWxsKXJldHVybiBudWxsCnM9YS4kaWRlbnRpdHkKaWYoISFzKXJldHVybiBzCnM9ZnVu -Y3Rpb24oYyxkLGUpe3JldHVybiBmdW5jdGlvbihmLGcsaCxpKXtyZXR1cm4gZShjLGQsZixnLGgsaSl9 -fShhLGIsQS5mdCkKYS4kaWRlbnRpdHk9cwpyZXR1cm4gc30sCmlBKGEyKXt2YXIgcyxyLHEscCxvLG4s -bSxsLGssaixpPWEyLmNvLGg9YTIuaVMsZz1hMi5pSSxmPWEyLm5EQSxlPWEyLmFJLGQ9YTIuZnMsYz1h -Mi5jcyxiPWRbMF0sYT1jWzBdLGEwPWlbYl0sYTE9YTIuZlQKYTEudG9TdHJpbmcKcz1oP09iamVjdC5j -cmVhdGUobmV3IEEuengoKS5jb25zdHJ1Y3Rvci5wcm90b3R5cGUpOk9iamVjdC5jcmVhdGUobmV3IEEu -clQobnVsbCxudWxsKS5jb25zdHJ1Y3Rvci5wcm90b3R5cGUpCnMuJGluaXRpYWxpemU9cy5jb25zdHJ1 -Y3RvcgppZihoKXI9ZnVuY3Rpb24gc3RhdGljX3RlYXJfb2ZmKCl7dGhpcy4kaW5pdGlhbGl6ZSgpfQpl -bHNlIHI9ZnVuY3Rpb24gdGVhcl9vZmYoYTMsYTQpe3RoaXMuJGluaXRpYWxpemUoYTMsYTQpfQpzLmNv -bnN0cnVjdG9yPXIKci5wcm90b3R5cGU9cwpzLiRfbmFtZT1iCnMuJF90YXJnZXQ9YTAKcT0haAppZihx -KXA9QS5ieChiLGEwLGcsZikKZWxzZXtzLiRzdGF0aWNfbmFtZT1iCnA9YTB9cy4kUz1BLmltKGExLGgs -ZykKc1thXT1wCmZvcihvPXAsbj0xO248ZC5sZW5ndGg7KytuKXttPWRbbl0KaWYodHlwZW9mIG09PSJz -dHJpbmciKXtsPWlbbV0Kaz1tCm09bH1lbHNlIGs9IiIKaj1jW25dCmlmKGohPW51bGwpe2lmKHEpbT1B -LmJ4KGssbSxnLGYpCnNbal09bX1pZihuPT09ZSlvPW19cy4kQz1vCnMuJFI9YTIuckMKcy4kRD1hMi5k -VgpyZXR1cm4gcn0sCmltKGEsYixjKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYodHlw -ZW9mIGE9PSJzdHJpbmciKXtpZihiKXRocm93IEEuYigiQ2Fubm90IGNvbXB1dGUgc2lnbmF0dXJlIGZv -ciBzdGF0aWMgdGVhcm9mZi4iKQpyZXR1cm4gZnVuY3Rpb24oZCxlKXtyZXR1cm4gZnVuY3Rpb24oKXty -ZXR1cm4gZSh0aGlzLGQpfX0oYSxBLlRuKX10aHJvdyBBLmIoIkVycm9yIGluIGZ1bmN0aW9uVHlwZSBv -ZiB0ZWFyb2ZmIil9LAp2cShhLGIsYyxkKXt2YXIgcz1BLnlTCnN3aXRjaChiPy0xOmEpe2Nhc2UgMDpy -ZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZih0aGlzKVtlXSgpfX0o -YyxzKQpjYXNlIDE6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcpe3JldHVybiBm -KHRoaXMpW2VdKGcpfX0oYyxzKQpjYXNlIDI6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0 -aW9uKGcsaCl7cmV0dXJuIGYodGhpcylbZV0oZyxoKX19KGMscykKY2FzZSAzOnJldHVybiBmdW5jdGlv -bihlLGYpe3JldHVybiBmdW5jdGlvbihnLGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGkpfX0oYyxz -KQpjYXNlIDQ6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0dXJuIGZ1bmN0aW9uKGcsaCxpLGope3JldHVy -biBmKHRoaXMpW2VdKGcsaCxpLGopfX0oYyxzKQpjYXNlIDU6cmV0dXJuIGZ1bmN0aW9uKGUsZil7cmV0 -dXJuIGZ1bmN0aW9uKGcsaCxpLGosayl7cmV0dXJuIGYodGhpcylbZV0oZyxoLGksaixrKX19KGMscykK -ZGVmYXVsdDpyZXR1cm4gZnVuY3Rpb24oZSxmKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZS5hcHBs -eShmKHRoaXMpLGFyZ3VtZW50cyl9fShkLHMpfX0sCmJ4KGEsYixjLGQpe3ZhciBzLHIKaWYoYylyZXR1 -cm4gQS5IZihhLGIsZCkKcz1iLmxlbmd0aApyPUEudnEocyxkLGEsYikKcmV0dXJuIHJ9LApaNChhLGIs -YyxkKXt2YXIgcz1BLnlTLHI9QS5BTwpzd2l0Y2goYj8tMTphKXtjYXNlIDA6dGhyb3cgQS5iKG5ldyBB -LkVxKCJJbnRlcmNlcHRlZCBmdW5jdGlvbiB3aXRoIG5vIGFyZ3VtZW50cy4iKSkKY2FzZSAxOnJldHVy -biBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKCl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlz -KSl9fShjLHIscykKY2FzZSAyOnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgp -e3JldHVybiBmKHRoaXMpW2VdKGcodGhpcyksaCl9fShjLHIscykKY2FzZSAzOnJldHVybiBmdW5jdGlv -bihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgsaSl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGkp -fX0oYyxyLHMpCmNhc2UgNDpyZXR1cm4gZnVuY3Rpb24oZSxmLGcpe3JldHVybiBmdW5jdGlvbihoLGks -ail7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGksail9fShjLHIscykKY2FzZSA1OnJldHVybiBm -dW5jdGlvbihlLGYsZyl7cmV0dXJuIGZ1bmN0aW9uKGgsaSxqLGspe3JldHVybiBmKHRoaXMpW2VdKGco -dGhpcyksaCxpLGosayl9fShjLHIscykKY2FzZSA2OnJldHVybiBmdW5jdGlvbihlLGYsZyl7cmV0dXJu -IGZ1bmN0aW9uKGgsaSxqLGssbCl7cmV0dXJuIGYodGhpcylbZV0oZyh0aGlzKSxoLGksaixrLGwpfX0o -YyxyLHMpCmRlZmF1bHQ6cmV0dXJuIGZ1bmN0aW9uKGUsZixnKXtyZXR1cm4gZnVuY3Rpb24oKXt2YXIg -cT1bZyh0aGlzKV0KQXJyYXkucHJvdG90eXBlLnB1c2guYXBwbHkocSxhcmd1bWVudHMpCnJldHVybiBl -LmFwcGx5KGYodGhpcykscSl9fShkLHIscyl9fSwKSGYoYSxiLGMpe3ZhciBzLHIKaWYoJC5BbD09bnVs -bCkkLkFsPUEubTkoImludGVyY2VwdG9yIikKaWYoJC5pMD09bnVsbCkkLmkwPUEubTkoInJlY2VpdmVy -IikKcz1iLmxlbmd0aApyPUEuWjQocyxjLGEsYikKcmV0dXJuIHJ9LApVMihhKXtyZXR1cm4gQS5pQShh -KX0sClRuKGEsYil7cmV0dXJuIEEuY0Uodi50eXBlVW5pdmVyc2UsQS56SyhhLmEpLGIpfSwKeVMoYSl7 -cmV0dXJuIGEuYX0sCkFPKGEpe3JldHVybiBhLmJ9LAptOShhKXt2YXIgcyxyLHEscD1uZXcgQS5yVCgi -cmVjZWl2ZXIiLCJpbnRlcmNlcHRvciIpLG89Si5FcChPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhw -KSx0LlgpCmZvcihzPW8ubGVuZ3RoLHI9MDtyPHM7KytyKXtxPW9bcl0KaWYocFtxXT09PWEpcmV0dXJu -IHF9dGhyb3cgQS5iKEEueFkoIkZpZWxkIG5hbWUgIithKyIgbm90IGZvdW5kLiIsbnVsbCkpfSwKb1Qo -YSl7aWYoYT09bnVsbClBLmZPKCJib29sZWFuIGV4cHJlc3Npb24gbXVzdCBub3QgYmUgbnVsbCIpCnJl -dHVybiBhfSwKZk8oYSl7dGhyb3cgQS5iKG5ldyBBLmtZKGEpKX0sCmFnKGEpe3Rocm93IEEuYihuZXcg -QS5wKGEpKX0sCllnKGEpe3JldHVybiB2LmdldElzb2xhdGVUYWcoYSl9LAppdyhhLGIsYyl7T2JqZWN0 -LmRlZmluZVByb3BlcnR5KGEsYix7dmFsdWU6YyxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUs -Y29uZmlndXJhYmxlOnRydWV9KX0sCnczKGEpe3ZhciBzLHIscSxwLG8sbj1BLm4oJC5ORi4kMShhKSks -bT0kLm53W25dCmlmKG0hPW51bGwpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShhLHYuZGlzcGF0Y2hQcm9w -ZXJ0eU5hbWUse3ZhbHVlOm0sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJs -ZTp0cnVlfSkKcmV0dXJuIG0uaX1zPSQudnZbbl0KaWYocyE9bnVsbClyZXR1cm4gcwpyPXYuaW50ZXJj -ZXB0b3JzQnlUYWdbbl0KaWYocj09bnVsbCl7cT1BLmsoJC5UWC4kMihhLG4pKQppZihxIT1udWxsKXtt -PSQubndbcV0KaWYobSE9bnVsbCl7T2JqZWN0LmRlZmluZVByb3BlcnR5KGEsdi5kaXNwYXRjaFByb3Bl -cnR5TmFtZSx7dmFsdWU6bSxlbnVtZXJhYmxlOmZhbHNlLHdyaXRhYmxlOnRydWUsY29uZmlndXJhYmxl -OnRydWV9KQpyZXR1cm4gbS5pfXM9JC52dltxXQppZihzIT1udWxsKXJldHVybiBzCnI9di5pbnRlcmNl -cHRvcnNCeVRhZ1txXQpuPXF9fWlmKHI9PW51bGwpcmV0dXJuIG51bGwKcz1yLnByb3RvdHlwZQpwPW5b -MF0KaWYocD09PSIhIil7bT1BLlZhKHMpCiQubndbbl09bQpPYmplY3QuZGVmaW5lUHJvcGVydHkoYSx2 -LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTptLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1 -ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBtLml9aWYocD09PSJ+Iil7JC52dltuXT1zCnJldHVy -biBzfWlmKHA9PT0iLSIpe289QS5WYShzKQpPYmplY3QuZGVmaW5lUHJvcGVydHkoT2JqZWN0LmdldFBy -b3RvdHlwZU9mKGEpLHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm8sZW51bWVyYWJsZTpmYWxz -ZSx3cml0YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIG8uaX1pZihwPT09IisiKXJl -dHVybiBBLkxjKGEscykKaWYocD09PSIqIil0aHJvdyBBLmIoQS5TWShuKSkKaWYodi5sZWFmVGFnc1tu -XT09PXRydWUpe289QS5WYShzKQpPYmplY3QuZGVmaW5lUHJvcGVydHkoT2JqZWN0LmdldFByb3RvdHlw -ZU9mKGEpLHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm8sZW51bWVyYWJsZTpmYWxzZSx3cml0 -YWJsZTp0cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcmV0dXJuIG8uaX1lbHNlIHJldHVybiBBLkxjKGEs -cyl9LApMYyhhLGIpe3ZhciBzPU9iamVjdC5nZXRQcm90b3R5cGVPZihhKQpPYmplY3QuZGVmaW5lUHJv -cGVydHkocyx2LmRpc3BhdGNoUHJvcGVydHlOYW1lLHt2YWx1ZTpKLlF1KGIscyxudWxsLG51bGwpLGVu -dW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBifSwK -VmEoYSl7cmV0dXJuIEouUXUoYSwhMSxudWxsLCEhYS4kaVhqKX0sClZGKGEsYixjKXt2YXIgcz1iLnBy -b3RvdHlwZQppZih2LmxlYWZUYWdzW2FdPT09dHJ1ZSlyZXR1cm4gQS5WYShzKQplbHNlIHJldHVybiBK -LlF1KHMsYyxudWxsLG51bGwpfSwKWEQoKXtpZighMD09PSQuQnYpcmV0dXJuCiQuQnY9ITAKQS5aMSgp -fSwKWjEoKXt2YXIgcyxyLHEscCxvLG4sbSxsCiQubnc9T2JqZWN0LmNyZWF0ZShudWxsKQokLnZ2PU9i -amVjdC5jcmVhdGUobnVsbCkKQS5rTygpCnM9di5pbnRlcmNlcHRvcnNCeVRhZwpyPU9iamVjdC5nZXRP -d25Qcm9wZXJ0eU5hbWVzKHMpCmlmKHR5cGVvZiB3aW5kb3chPSJ1bmRlZmluZWQiKXt3aW5kb3cKcT1m -dW5jdGlvbigpe30KZm9yKHA9MDtwPHIubGVuZ3RoOysrcCl7bz1yW3BdCm49JC54Ny4kMShvKQppZihu -IT1udWxsKXttPUEuVkYobyxzW29dLG4pCmlmKG0hPW51bGwpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShu -LHYuZGlzcGF0Y2hQcm9wZXJ0eU5hbWUse3ZhbHVlOm0sZW51bWVyYWJsZTpmYWxzZSx3cml0YWJsZTp0 -cnVlLGNvbmZpZ3VyYWJsZTp0cnVlfSkKcS5wcm90b3R5cGU9bn19fX1mb3IocD0wO3A8ci5sZW5ndGg7 -KytwKXtvPXJbcF0KaWYoL15bQS1aYS16X10vLnRlc3Qobykpe2w9c1tvXQpzWyIhIitvXT1sCnNbIn4i -K29dPWwKc1siLSIrb109bApzWyIrIitvXT1sCnNbIioiK29dPWx9fX0sCmtPKCl7dmFyIHMscixxLHAs -byxuLG09Qi5ZcSgpCm09QS51ZChCLktVLEEudWQoQi5mUSxBLnVkKEIuaTcsQS51ZChCLmk3LEEudWQo -Qi54aSxBLnVkKEIuZGssQS51ZChCLndiKEIuTzQpLG0pKSkpKSkpCmlmKHR5cGVvZiBkYXJ0TmF0aXZl -RGlzcGF0Y2hIb29rc1RyYW5zZm9ybWVyIT0idW5kZWZpbmVkIil7cz1kYXJ0TmF0aXZlRGlzcGF0Y2hI -b29rc1RyYW5zZm9ybWVyCmlmKHR5cGVvZiBzPT0iZnVuY3Rpb24iKXM9W3NdCmlmKHMuY29uc3RydWN0 -b3I9PUFycmF5KWZvcihyPTA7cjxzLmxlbmd0aDsrK3Ipe3E9c1tyXQppZih0eXBlb2YgcT09ImZ1bmN0 -aW9uIiltPXEobSl8fG19fXA9bS5nZXRUYWcKbz1tLmdldFVua25vd25UYWcKbj1tLnByb3RvdHlwZUZv -clRhZwokLk5GPW5ldyBBLmRDKHApCiQuVFg9bmV3IEEud04obykKJC54Nz1uZXcgQS5WWChuKX0sCnVk -KGEsYil7cmV0dXJuIGEoYil8fGJ9LAp2NChhLGIsYyxkLGUsZil7dmFyIHM9Yj8ibSI6IiIscj1jPyIi -OiJpIixxPWQ/InUiOiIiLHA9ZT8icyI6IiIsbz1mPyJnIjoiIixuPWZ1bmN0aW9uKGcsaCl7dHJ5e3Jl -dHVybiBuZXcgUmVnRXhwKGcsaCl9Y2F0Y2gobSl7cmV0dXJuIG19fShhLHMrcitxK3ArbykKaWYobiBp -bnN0YW5jZW9mIFJlZ0V4cClyZXR1cm4gbgp0aHJvdyBBLmIoQS5ycigiSWxsZWdhbCBSZWdFeHAgcGF0 -dGVybiAoIitTdHJpbmcobikrIikiLGEsbnVsbCkpfSwKU1EoYSxiLGMpe3ZhciBzCmlmKHR5cGVvZiBi -PT0ic3RyaW5nIilyZXR1cm4gYS5pbmRleE9mKGIsYyk+PTAKZWxzZSBpZihiIGluc3RhbmNlb2YgQS5W -Uil7cz1CLnhCLnluKGEsYykKcmV0dXJuIGIuYi50ZXN0KHMpfWVsc2V7cz1KLkZMKGIsQi54Qi55bihh -LGMpKQpyZXR1cm4hcy5nbDAocyl9fSwKQTQoYSl7aWYoYS5pbmRleE9mKCIkIiwwKT49MClyZXR1cm4g -YS5yZXBsYWNlKC9cJC9nLCIkJCQkIikKcmV0dXJuIGF9LAplQShhKXtpZigvW1tcXXt9KCkqKz8uXFxe -JHxdLy50ZXN0KGEpKXJldHVybiBhLnJlcGxhY2UoL1tbXF17fSgpKis/LlxcXiR8XS9nLCJcXCQmIikK -cmV0dXJuIGF9LAp5cyhhLGIsYyl7dmFyIHM9QS5uTShhLGIsYykKcmV0dXJuIHN9LApuTShhLGIsYyl7 -dmFyIHMscixxLHAKaWYoYj09PSIiKXtpZihhPT09IiIpcmV0dXJuIGMKcz1hLmxlbmd0aApyPSIiK2MK -Zm9yKHE9MDtxPHM7KytxKXI9cithW3FdK2MKcmV0dXJuIHIuY2hhckNvZGVBdCgwKT09MD9yOnJ9cD1h -LmluZGV4T2YoYiwwKQppZihwPDApcmV0dXJuIGEKaWYoYS5sZW5ndGg8NTAwfHxjLmluZGV4T2YoIiQi -LDApPj0wKXJldHVybiBhLnNwbGl0KGIpLmpvaW4oYykKcmV0dXJuIGEucmVwbGFjZShuZXcgUmVnRXhw -KEEuZUEoYiksImciKSxBLkE0KGMpKX0sClBEOmZ1bmN0aW9uIFBEKGEsYil7dGhpcy5hPWEKdGhpcy4k -dGk9Yn0sCldVOmZ1bmN0aW9uIFdVKCl7fSwKTFA6ZnVuY3Rpb24gTFAoYSxiLGMsZCl7dmFyIF89dGhp -cwpfLmE9YQpfLmI9YgpfLmM9YwpfLiR0aT1kfSwKWFI6ZnVuY3Rpb24gWFIoYSxiKXt0aGlzLmE9YQp0 -aGlzLiR0aT1ifSwKTEk6ZnVuY3Rpb24gTEkoYSxiLGMsZCxlKXt2YXIgXz10aGlzCl8uYT1hCl8uYz1i -Cl8uZD1jCl8uZT1kCl8uZj1lfSwKQ2o6ZnVuY3Rpb24gQ2ooYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1i -CnRoaXMuYz1jfSwKZjk6ZnVuY3Rpb24gZjkoYSxiLGMsZCxlLGYpe3ZhciBfPXRoaXMKXy5hPWEKXy5i -PWIKXy5jPWMKXy5kPWQKXy5lPWUKXy5mPWZ9LApXMDpmdW5jdGlvbiBXMChhLGIpe3RoaXMuYT1hCnRo -aXMuYj1ifSwKYXo6ZnVuY3Rpb24gYXooYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwK -dlY6ZnVuY3Rpb24gdlYoYSl7dGhpcy5hPWF9LAp0ZTpmdW5jdGlvbiB0ZShhKXt0aGlzLmE9YX0sCmJx -OmZ1bmN0aW9uIGJxKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApYTzpmdW5jdGlvbiBYTyhhKXt0aGlz -LmE9YQp0aGlzLmI9bnVsbH0sClRwOmZ1bmN0aW9uIFRwKCl7fSwKQXk6ZnVuY3Rpb24gQXkoKXt9LApF -MTpmdW5jdGlvbiBFMSgpe30sCmxjOmZ1bmN0aW9uIGxjKCl7fSwKeng6ZnVuY3Rpb24gengoKXt9LApy -VDpmdW5jdGlvbiByVChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKRXE6ZnVuY3Rpb24gRXEoYSl7dGhp -cy5hPWF9LAprWTpmdW5jdGlvbiBrWShhKXt0aGlzLmE9YX0sCmtyOmZ1bmN0aW9uIGtyKCl7fSwKTjU6 -ZnVuY3Rpb24gTjUoYSl7dmFyIF89dGhpcwpfLmE9MApfLmY9Xy5lPV8uZD1fLmM9Xy5iPW51bGwKXy5y -PTAKXy4kdGk9YX0sCmV3OmZ1bmN0aW9uIGV3KGEpe3RoaXMuYT1hfSwKdmg6ZnVuY3Rpb24gdmgoYSxi -KXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uZD1fLmM9bnVsbH0sCmk1OmZ1bmN0aW9uIGk1KGEsYil7 -dGhpcy5hPWEKdGhpcy4kdGk9Yn0sCk42OmZ1bmN0aW9uIE42KGEsYixjKXt2YXIgXz10aGlzCl8uYT1h -Cl8uYj1iCl8uZD1fLmM9bnVsbApfLiR0aT1jfSwKZEM6ZnVuY3Rpb24gZEMoYSl7dGhpcy5hPWF9LAp3 -TjpmdW5jdGlvbiB3TihhKXt0aGlzLmE9YX0sClZYOmZ1bmN0aW9uIFZYKGEpe3RoaXMuYT1hfSwKVlI6 -ZnVuY3Rpb24gVlIoYSxiKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uZD1fLmM9bnVsbH0sCkVLOmZ1 -bmN0aW9uIEVLKGEpe3RoaXMuYj1hfSwKS1c6ZnVuY3Rpb24gS1coYSxiLGMpe3RoaXMuYT1hCnRoaXMu -Yj1iCnRoaXMuYz1jfSwKUGI6ZnVuY3Rpb24gUGIoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIK -Xy5jPWMKXy5kPW51bGx9LAp0UTpmdW5jdGlvbiB0UShhLGIpe3RoaXMuYT1hCnRoaXMuYz1ifSwKdW46 -ZnVuY3Rpb24gdW4oYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKU2Q6ZnVuY3Rpb24g -U2QoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPW51bGx9LApYRihhKXtyZXR1 -cm4gYX0sCm9kKGEsYixjKXtpZihhPj4+MCE9PWF8fGE+PWMpdGhyb3cgQS5iKEEuaihiLGEpKX0sCnJN -KGEsYixjKXt2YXIgcwppZighKGE+Pj4wIT09YSkpcz1iPj4+MCE9PWJ8fGE+Ynx8Yj5jCmVsc2Ugcz0h -MAppZihzKXRocm93IEEuYihBLmF1KGEsYixjKSkKcmV0dXJuIGJ9LAplSDpmdW5jdGlvbiBlSCgpe30s -ClhIOmZ1bmN0aW9uIFhIKCl7fSwKRGc6ZnVuY3Rpb24gRGcoKXt9LApQZzpmdW5jdGlvbiBQZygpe30s -CnhqOmZ1bmN0aW9uIHhqKCl7fSwKZEU6ZnVuY3Rpb24gZEUoKXt9LApaQTpmdW5jdGlvbiBaQSgpe30s -CmRUOmZ1bmN0aW9uIGRUKCl7fSwKUHE6ZnVuY3Rpb24gUHEoKXt9LAplRTpmdW5jdGlvbiBlRSgpe30s -ClY2OmZ1bmN0aW9uIFY2KCl7fSwKUkc6ZnVuY3Rpb24gUkcoKXt9LApWUDpmdW5jdGlvbiBWUCgpe30s -CldCOmZ1bmN0aW9uIFdCKCl7fSwKWkc6ZnVuY3Rpb24gWkcoKXt9LApjeihhLGIpe3ZhciBzPWIuYwpy -ZXR1cm4gcz09bnVsbD9iLmM9QS5CKGEsYi55LCEwKTpzfSwKeFooYSxiKXt2YXIgcz1iLmMKcmV0dXJu -IHM9PW51bGw/Yi5jPUEuSihhLCJiOCIsW2IueV0pOnN9LApRMShhKXt2YXIgcz1hLngKaWYocz09PTZ8 -fHM9PT03fHxzPT09OClyZXR1cm4gQS5RMShhLnkpCnJldHVybiBzPT09MTJ8fHM9PT0xM30sCm1EKGEp -e3JldHVybiBhLmF0fSwKTjAoYSl7cmV0dXJuIEEuRSh2LnR5cGVVbml2ZXJzZSxhLCExKX0sClBMKGEs -YixhMCxhMSl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQsYz1iLngKc3dpdGNoKGMp -e2Nhc2UgNTpjYXNlIDE6Y2FzZSAyOmNhc2UgMzpjYXNlIDQ6cmV0dXJuIGIKY2FzZSA2OnM9Yi55CnI9 -QS5QTChhLHMsYTAsYTEpCmlmKHI9PT1zKXJldHVybiBiCnJldHVybiBBLkMoYSxyLCEwKQpjYXNlIDc6 -cz1iLnkKcj1BLlBMKGEscyxhMCxhMSkKaWYocj09PXMpcmV0dXJuIGIKcmV0dXJuIEEuQihhLHIsITAp -CmNhc2UgODpzPWIueQpyPUEuUEwoYSxzLGEwLGExKQppZihyPT09cylyZXR1cm4gYgpyZXR1cm4gQS5m -KGEsciwhMCkKY2FzZSA5OnE9Yi56CnA9QS5iWihhLHEsYTAsYTEpCmlmKHA9PT1xKXJldHVybiBiCnJl -dHVybiBBLkooYSxiLnkscCkKY2FzZSAxMDpvPWIueQpuPUEuUEwoYSxvLGEwLGExKQptPWIuegpsPUEu -YlooYSxtLGEwLGExKQppZihuPT09byYmbD09PW0pcmV0dXJuIGIKcmV0dXJuIEEuYShhLG4sbCkKY2Fz -ZSAxMjprPWIueQpqPUEuUEwoYSxrLGEwLGExKQppPWIuegpoPUEucVQoYSxpLGEwLGExKQppZihqPT09 -ayYmaD09PWkpcmV0dXJuIGIKcmV0dXJuIEEuTmYoYSxqLGgpCmNhc2UgMTM6Zz1iLnoKYTErPWcubGVu -Z3RoCmY9QS5iWihhLGcsYTAsYTEpCm89Yi55Cm49QS5QTChhLG8sYTAsYTEpCmlmKGY9PT1nJiZuPT09 -bylyZXR1cm4gYgpyZXR1cm4gQS5EKGEsbixmLCEwKQpjYXNlIDE0OmU9Yi55CmlmKGU8YTEpcmV0dXJu -IGIKZD1hMFtlLWExXQppZihkPT1udWxsKXJldHVybiBiCnJldHVybiBkCmRlZmF1bHQ6dGhyb3cgQS5i -KEEuaFYoIkF0dGVtcHRlZCB0byBzdWJzdGl0dXRlIHVuZXhwZWN0ZWQgUlRJIGtpbmQgIitjKSl9fSwK -YlooYSxiLGMsZCl7dmFyIHMscixxLHAsbz1iLmxlbmd0aCxuPUEudlUobykKZm9yKHM9ITEscj0wO3I8 -bzsrK3Ipe3E9YltyXQpwPUEuUEwoYSxxLGMsZCkKaWYocCE9PXEpcz0hMApuW3JdPXB9cmV0dXJuIHM/ -bjpifSwKdk8oYSxiLGMsZCl7dmFyIHMscixxLHAsbyxuLG09Yi5sZW5ndGgsbD1BLnZVKG0pCmZvcihz -PSExLHI9MDtyPG07cis9Myl7cT1iW3JdCnA9YltyKzFdCm89YltyKzJdCm49QS5QTChhLG8sYyxkKQpp -ZihuIT09bylzPSEwCmwuc3BsaWNlKHIsMyxxLHAsbil9cmV0dXJuIHM/bDpifSwKcVQoYSxiLGMsZCl7 -dmFyIHMscj1iLmEscT1BLmJaKGEscixjLGQpLHA9Yi5iLG89QS5iWihhLHAsYyxkKSxuPWIuYyxtPUEu -dk8oYSxuLGMsZCkKaWYocT09PXImJm89PT1wJiZtPT09bilyZXR1cm4gYgpzPW5ldyBBLkVUKCkKcy5h -PXEKcy5iPW8Kcy5jPW0KcmV0dXJuIHN9LApRSShhLGIpe2Fbdi5hcnJheVJ0aV09YgpyZXR1cm4gYX0s -CkpTKGEpe3ZhciBzLHI9YS4kUwppZihyIT1udWxsKXtpZih0eXBlb2Ygcj09Im51bWJlciIpcmV0dXJu -IEEuQnAocikKcz1hLiRTKCkKcmV0dXJuIHN9cmV0dXJuIG51bGx9LApVZShhLGIpe3ZhciBzCmlmKEEu -UTEoYikpaWYoYSBpbnN0YW5jZW9mIEEuVHApe3M9QS5KUyhhKQppZihzIT1udWxsKXJldHVybiBzfXJl -dHVybiBBLnpLKGEpfSwKeksoYSl7dmFyIHMKaWYoYSBpbnN0YW5jZW9mIEEuTWgpe3M9YS4kdGkKcmV0 -dXJuIHMhPW51bGw/czpBLlZVKGEpfWlmKEFycmF5LmlzQXJyYXkoYSkpcmV0dXJuIEEudDYoYSkKcmV0 -dXJuIEEuVlUoSi5pYShhKSl9LAp0NihhKXt2YXIgcz1hW3YuYXJyYXlSdGldLHI9dC5iCmlmKHM9PW51 -bGwpcmV0dXJuIHIKaWYocy5jb25zdHJ1Y3RvciE9PXIuY29uc3RydWN0b3IpcmV0dXJuIHIKcmV0dXJu -IHN9LApMaChhKXt2YXIgcz1hLiR0aQpyZXR1cm4gcyE9bnVsbD9zOkEuVlUoYSl9LApWVShhKXt2YXIg -cz1hLmNvbnN0cnVjdG9yLHI9cy4kY2NhY2hlCmlmKHIhPW51bGwpcmV0dXJuIHIKcmV0dXJuIEEucjko -YSxzKX0sCnI5KGEsYil7dmFyIHM9YSBpbnN0YW5jZW9mIEEuVHA/YS5fX3Byb3RvX18uX19wcm90b19f -LmNvbnN0cnVjdG9yOmIscj1BLmFpKHYudHlwZVVuaXZlcnNlLHMubmFtZSkKYi4kY2NhY2hlPXIKcmV0 -dXJuIHJ9LApCcChhKXt2YXIgcyxyPXYudHlwZXMscT1yW2FdCmlmKHR5cGVvZiBxPT0ic3RyaW5nIil7 -cz1BLkUodi50eXBlVW5pdmVyc2UscSwhMSkKclthXT1zCnJldHVybiBzfXJldHVybiBxfSwKS3goYSl7 -dmFyIHMscixxLHA9YS53CmlmKHAhPW51bGwpcmV0dXJuIHAKcz1hLmF0CnI9cy5yZXBsYWNlKC9cKi9n -LCIiKQppZihyPT09cylyZXR1cm4gYS53PW5ldyBBLmxZKGEpCnE9QS5FKHYudHlwZVVuaXZlcnNlLHIs -ITApCnA9cS53CnJldHVybiBhLnc9cD09bnVsbD9xLnc9bmV3IEEubFkocSk6cH0sCnhxKGEpe3JldHVy -biBBLkt4KEEuRSh2LnR5cGVVbml2ZXJzZSxhLCExKSl9LApKSihhKXt2YXIgcyxyLHEscCxvPXRoaXMK -aWYobz09PXQuSylyZXR1cm4gQS5SRShvLGEsQS5rZSkKaWYoIUEuQTgobykpaWYoIShvPT09dC5fKSlz -PSExCmVsc2Ugcz0hMAplbHNlIHM9ITAKaWYocylyZXR1cm4gQS5SRShvLGEsQS5JdykKcz1vLngKcj1z -PT09Nj9vLnk6bwppZihyPT09dC5TKXE9QS5vawplbHNlIGlmKHI9PT10LmdSfHxyPT09dC5kaSlxPUEu -S0gKZWxzZSBpZihyPT09dC5OKXE9QS5NTQplbHNlIHE9cj09PXQueT9BLnJROm51bGwKaWYocSE9bnVs -bClyZXR1cm4gQS5SRShvLGEscSkKaWYoci54PT09OSl7cD1yLnkKaWYoci56LmV2ZXJ5KEEuY2MpKXtv -LnI9IiRpIitwCmlmKHA9PT0iek0iKXJldHVybiBBLlJFKG8sYSxBLnlNKQpyZXR1cm4gQS5SRShvLGEs -QS50NCl9fWVsc2UgaWYocz09PTcpcmV0dXJuIEEuUkUobyxhLEEuQVEpCnJldHVybiBBLlJFKG8sYSxB -LllPKX0sClJFKGEsYixjKXthLmI9YwpyZXR1cm4gYS5iKGIpfSwKQXUoYSl7dmFyIHMscj10aGlzLHE9 -QS5PegppZighQS5BOChyKSlpZighKHI9PT10Ll8pKXM9ITEKZWxzZSBzPSEwCmVsc2Ugcz0hMAppZihz -KXE9QS5obgplbHNlIGlmKHI9PT10LkspcT1BLlRpCmVsc2V7cz1BLmxSKHIpCmlmKHMpcT1BLmw0fXIu -YT1xCnJldHVybiByLmEoYSl9LApRaihhKXt2YXIgcyxyPWEueAppZighQS5BOChhKSlpZighKGE9PT10 -Ll8pKWlmKCEoYT09PXQuYXcpKWlmKHIhPT03KWlmKCEocj09PTYmJkEuUWooYS55KSkpcz1yPT09OCYm -QS5RaihhLnkpfHxhPT09dC5QfHxhPT09dC5UCmVsc2Ugcz0hMAplbHNlIHM9ITAKZWxzZSBzPSEwCmVs -c2Ugcz0hMAplbHNlIHM9ITAKcmV0dXJuIHN9LApZTyhhKXt2YXIgcz10aGlzCmlmKGE9PW51bGwpcmV0 -dXJuIEEuUWoocykKcmV0dXJuIEEuV2Uodi50eXBlVW5pdmVyc2UsQS5VZShhLHMpLG51bGwscyxudWxs -KX0sCkFRKGEpe2lmKGE9PW51bGwpcmV0dXJuITAKcmV0dXJuIHRoaXMueS5iKGEpfSwKdDQoYSl7dmFy -IHMscj10aGlzCmlmKGE9PW51bGwpcmV0dXJuIEEuUWoocikKcz1yLnIKaWYoYSBpbnN0YW5jZW9mIEEu -TWgpcmV0dXJuISFhW3NdCnJldHVybiEhSi5pYShhKVtzXX0sCnlNKGEpe3ZhciBzLHI9dGhpcwppZihh -PT1udWxsKXJldHVybiBBLlFqKHIpCmlmKHR5cGVvZiBhIT0ib2JqZWN0IilyZXR1cm4hMQppZihBcnJh -eS5pc0FycmF5KGEpKXJldHVybiEwCnM9ci5yCmlmKGEgaW5zdGFuY2VvZiBBLk1oKXJldHVybiEhYVtz -XQpyZXR1cm4hIUouaWEoYSlbc119LApPeihhKXt2YXIgcyxyPXRoaXMKaWYoYT09bnVsbCl7cz1BLmxS -KHIpCmlmKHMpcmV0dXJuIGF9ZWxzZSBpZihyLmIoYSkpcmV0dXJuIGEKQS5tNChhLHIpfSwKbDQoYSl7 -dmFyIHM9dGhpcwppZihhPT1udWxsKXJldHVybiBhCmVsc2UgaWYocy5iKGEpKXJldHVybiBhCkEubTQo -YSxzKX0sCm00KGEsYil7dGhyb3cgQS5iKEEuWmMoQS5XSyhhLEEuVWUoYSxiKSxBLmRtKGIsbnVsbCkp -KSl9LApEaChhLGIsYyxkKXt2YXIgcz1udWxsCmlmKEEuV2Uodi50eXBlVW5pdmVyc2UsYSxzLGIscykp -cmV0dXJuIGEKdGhyb3cgQS5iKEEuWmMoIlRoZSB0eXBlIGFyZ3VtZW50ICciK0EuZG0oYSxzKSsiJyBp -cyBub3QgYSBzdWJ0eXBlIG9mIHRoZSB0eXBlIHZhcmlhYmxlIGJvdW5kICciK0EuZG0oYixzKSsiJyBv -ZiB0eXBlIHZhcmlhYmxlICciK2MrIicgaW4gJyIrZCsiJy4iKSl9LApXSyhhLGIsYyl7dmFyIHM9QS5o -bChhKQpyZXR1cm4gcysiOiB0eXBlICciK0EuZG0oYj09bnVsbD9BLnpLKGEpOmIsbnVsbCkrIicgaXMg -bm90IGEgc3VidHlwZSBvZiB0eXBlICciK2MrIicifSwKWmMoYSl7cmV0dXJuIG5ldyBBLmlNKCJUeXBl -RXJyb3I6ICIrYSl9LApxKGEsYil7cmV0dXJuIG5ldyBBLmlNKCJUeXBlRXJyb3I6ICIrQS5XSyhhLG51 -bGwsYikpfSwKa2UoYSl7cmV0dXJuIGEhPW51bGx9LApUaShhKXtpZihhIT1udWxsKXJldHVybiBhCnRo -cm93IEEuYihBLnEoYSwiT2JqZWN0IikpfSwKSXcoYSl7cmV0dXJuITB9LApobihhKXtyZXR1cm4gYX0s -CnJRKGEpe3JldHVybiEwPT09YXx8ITE9PT1hfSwKcDgoYSl7aWYoITA9PT1hKXJldHVybiEwCmlmKCEx -PT09YSlyZXR1cm4hMQp0aHJvdyBBLmIoQS5xKGEsImJvb2wiKSl9LAptTChhKXtpZighMD09PWEpcmV0 -dXJuITAKaWYoITE9PT1hKXJldHVybiExCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgQS5iKEEucShh -LCJib29sIikpfSwKTTQoYSl7aWYoITA9PT1hKXJldHVybiEwCmlmKCExPT09YSlyZXR1cm4hMQppZihh -PT1udWxsKXJldHVybiBhCnRocm93IEEuYihBLnEoYSwiYm9vbD8iKSl9LApyVihhKXtpZih0eXBlb2Yg -YT09Im51bWJlciIpcmV0dXJuIGEKdGhyb3cgQS5iKEEucShhLCJkb3VibGUiKSl9LAp0RihhKXtpZih0 -eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBBLmIoQS5x -KGEsImRvdWJsZSIpKX0sClFrKGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIilyZXR1cm4gYQppZihhPT1u -dWxsKXJldHVybiBhCnRocm93IEEuYihBLnEoYSwiZG91YmxlPyIpKX0sCm9rKGEpe3JldHVybiB0eXBl -b2YgYT09Im51bWJlciImJk1hdGguZmxvb3IoYSk9PT1hfSwKSVooYSl7aWYodHlwZW9mIGE9PSJudW1i -ZXIiJiZNYXRoLmZsb29yKGEpPT09YSlyZXR1cm4gYQp0aHJvdyBBLmIoQS5xKGEsImludCIpKX0sCnVQ -KGEpe2lmKHR5cGVvZiBhPT0ibnVtYmVyIiYmTWF0aC5mbG9vcihhKT09PWEpcmV0dXJuIGEKaWYoYT09 -bnVsbClyZXR1cm4gYQp0aHJvdyBBLmIoQS5xKGEsImludCIpKX0sClVjKGEpe2lmKHR5cGVvZiBhPT0i -bnVtYmVyIiYmTWF0aC5mbG9vcihhKT09PWEpcmV0dXJuIGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJv -dyBBLmIoQS5xKGEsImludD8iKSl9LApLSChhKXtyZXR1cm4gdHlwZW9mIGE9PSJudW1iZXIifSwKejUo -YSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCnRocm93IEEuYihBLnEoYSwibnVtIikpfSwK -VzEoYSl7aWYodHlwZW9mIGE9PSJudW1iZXIiKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhy -b3cgQS5iKEEucShhLCJudW0iKSl9LApjVShhKXtpZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEK -aWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBBLmIoQS5xKGEsIm51bT8iKSl9LApNTShhKXtyZXR1cm4g -dHlwZW9mIGE9PSJzdHJpbmcifSwKbihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIGEKdGhy -b3cgQS5iKEEucShhLCJTdHJpbmciKSl9LApoTihhKXtpZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJu -IGEKaWYoYT09bnVsbClyZXR1cm4gYQp0aHJvdyBBLmIoQS5xKGEsIlN0cmluZyIpKX0sCmsoYSl7aWYo -dHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBhCmlmKGE9PW51bGwpcmV0dXJuIGEKdGhyb3cgQS5iKEEu -cShhLCJTdHJpbmc/IikpfSwKaW8oYSxiKXt2YXIgcyxyLHEKZm9yKHM9IiIscj0iIixxPTA7cTxhLmxl -bmd0aDsrK3Escj0iLCAiKXMrPXIrQS5kbShhW3FdLGIpCnJldHVybiBzfSwKd1QoYSxiKXt2YXIgcyxy -LHEscCxvLG4sbT1hLnksbD1hLnoKaWYoIiI9PT1tKXJldHVybiIoIitBLmlvKGwsYikrIikiCnM9bC5s -ZW5ndGgKcj1tLnNwbGl0KCIsIikKcT1yLmxlbmd0aC1zCmZvcihwPSIoIixvPSIiLG49MDtuPHM7Kytu -LG89IiwgIil7cCs9bwppZihxPT09MClwKz0ieyIKcCs9QS5kbShsW25dLGIpCmlmKHE+PTApcCs9IiAi -K3JbcV07KytxfXJldHVybiBwKyJ9KSJ9LApiSShhNCxhNSxhNil7dmFyIHMscixxLHAsbyxuLG0sbCxr -LGosaSxoLGcsZixlLGQsYyxiLGEsYTAsYTEsYTIsYTM9IiwgIgppZihhNiE9bnVsbCl7cz1hNi5sZW5n -dGgKaWYoYTU9PW51bGwpe2E1PUEuUUkoW10sdC5zKQpyPW51bGx9ZWxzZSByPWE1Lmxlbmd0aApxPWE1 -Lmxlbmd0aApmb3IocD1zO3A+MDstLXApQi5ObS5pKGE1LCJUIisocStwKSkKZm9yKG89dC5YLG49dC5f -LG09IjwiLGw9IiIscD0wO3A8czsrK3AsbD1hMyl7az1hNS5sZW5ndGgKaj1rLTEtcAppZighKGo+PTAp -KXJldHVybiBBLk9IKGE1LGopCm09Qi54Qi5oKG0rbCxhNVtqXSkKaT1hNltwXQpoPWkueAppZighKGg9 -PT0yfHxoPT09M3x8aD09PTR8fGg9PT01fHxpPT09bykpaWYoIShpPT09bikpaz0hMQplbHNlIGs9ITAK -ZWxzZSBrPSEwCmlmKCFrKW0rPSIgZXh0ZW5kcyAiK0EuZG0oaSxhNSl9bSs9Ij4ifWVsc2V7bT0iIgpy -PW51bGx9bz1hNC55Cmc9YTQuegpmPWcuYQplPWYubGVuZ3RoCmQ9Zy5iCmM9ZC5sZW5ndGgKYj1nLmMK -YT1iLmxlbmd0aAphMD1BLmRtKG8sYTUpCmZvcihhMT0iIixhMj0iIixwPTA7cDxlOysrcCxhMj1hMylh -MSs9YTIrQS5kbShmW3BdLGE1KQppZihjPjApe2ExKz1hMisiWyIKZm9yKGEyPSIiLHA9MDtwPGM7Kytw -LGEyPWEzKWExKz1hMitBLmRtKGRbcF0sYTUpCmExKz0iXSJ9aWYoYT4wKXthMSs9YTIrInsiCmZvcihh -Mj0iIixwPTA7cDxhO3ArPTMsYTI9YTMpe2ExKz1hMgppZihiW3ArMV0pYTErPSJyZXF1aXJlZCAiCmEx -Kz1BLmRtKGJbcCsyXSxhNSkrIiAiK2JbcF19YTErPSJ9In1pZihyIT1udWxsKXthNS50b1N0cmluZwph -NS5sZW5ndGg9cn1yZXR1cm4gbSsiKCIrYTErIikgPT4gIithMH0sCmRtKGEsYil7dmFyIHMscixxLHAs -byxuLG0sbD1hLngKaWYobD09PTUpcmV0dXJuImVyYXNlZCIKaWYobD09PTIpcmV0dXJuImR5bmFtaWMi -CmlmKGw9PT0zKXJldHVybiJ2b2lkIgppZihsPT09MSlyZXR1cm4iTmV2ZXIiCmlmKGw9PT00KXJldHVy -biJhbnkiCmlmKGw9PT02KXtzPUEuZG0oYS55LGIpCnJldHVybiBzfWlmKGw9PT03KXtyPWEueQpzPUEu -ZG0ocixiKQpxPXIueApyZXR1cm4ocT09PTEyfHxxPT09MTM/IigiK3MrIikiOnMpKyI/In1pZihsPT09 -OClyZXR1cm4iRnV0dXJlT3I8IitBLmRtKGEueSxiKSsiPiIKaWYobD09PTkpe3A9QS5vMyhhLnkpCm89 -YS56CnJldHVybiBvLmxlbmd0aD4wP3ArKCI8IitBLmlvKG8sYikrIj4iKTpwfWlmKGw9PT0xMSlyZXR1 -cm4gQS53VChhLGIpCmlmKGw9PT0xMilyZXR1cm4gQS5iSShhLGIsbnVsbCkKaWYobD09PTEzKXJldHVy -biBBLmJJKGEueSxiLGEueikKaWYobD09PTE0KXtuPWEueQptPWIubGVuZ3RoCm49bS0xLW4KaWYoIShu -Pj0wJiZuPG0pKXJldHVybiBBLk9IKGIsbikKcmV0dXJuIGJbbl19cmV0dXJuIj8ifSwKbzMoYSl7dmFy -IHM9di5tYW5nbGVkR2xvYmFsTmFtZXNbYV0KaWYocyE9bnVsbClyZXR1cm4gcwpyZXR1cm4ibWluaWZp -ZWQ6IithfSwKUW8oYSxiKXt2YXIgcz1hLnRSW2JdCmZvcig7dHlwZW9mIHM9PSJzdHJpbmciOylzPWEu -dFJbc10KcmV0dXJuIHN9LAphaShhLGIpe3ZhciBzLHIscSxwLG8sbj1hLmVULG09bltiXQppZihtPT1u -dWxsKXJldHVybiBBLkUoYSxiLCExKQplbHNlIGlmKHR5cGVvZiBtPT0ibnVtYmVyIil7cz1tCnI9QS5t -KGEsNSwiIyIpCnE9QS52VShzKQpmb3IocD0wO3A8czsrK3ApcVtwXT1yCm89QS5KKGEsYixxKQpuW2Jd -PW8KcmV0dXJuIG99ZWxzZSByZXR1cm4gbX0sCnhiKGEsYil7cmV0dXJuIEEuSXgoYS50UixiKX0sCkZG -KGEsYil7cmV0dXJuIEEuSXgoYS5lVCxiKX0sCkUoYSxiLGMpe3ZhciBzLHI9YS5lQyxxPXIuZ2V0KGIp -CmlmKHEhPW51bGwpcmV0dXJuIHEKcz1BLmkoQS5vKGEsbnVsbCxiLGMpKQpyLnNldChiLHMpCnJldHVy -biBzfSwKY0UoYSxiLGMpe3ZhciBzLHIscT1iLlEKaWYocT09bnVsbClxPWIuUT1uZXcgTWFwKCkKcz1x -LmdldChjKQppZihzIT1udWxsKXJldHVybiBzCnI9QS5pKEEubyhhLGIsYywhMCkpCnEuc2V0KGMscikK -cmV0dXJuIHJ9LAp2NShhLGIsYyl7dmFyIHMscixxLHA9Yi5hcwppZihwPT1udWxsKXA9Yi5hcz1uZXcg -TWFwKCkKcz1jLmF0CnI9cC5nZXQocykKaWYociE9bnVsbClyZXR1cm4gcgpxPUEuYShhLGIsYy54PT09 -MTA/Yy56OltjXSkKcC5zZXQocyxxKQpyZXR1cm4gcX0sCkJEKGEsYil7Yi5hPUEuQXUKYi5iPUEuSkoK -cmV0dXJuIGJ9LAptKGEsYixjKXt2YXIgcyxyLHE9YS5lQy5nZXQoYykKaWYocSE9bnVsbClyZXR1cm4g -cQpzPW5ldyBBLkpjKG51bGwsbnVsbCkKcy54PWIKcy5hdD1jCnI9QS5CRChhLHMpCmEuZUMuc2V0KGMs -cikKcmV0dXJuIHJ9LApDKGEsYixjKXt2YXIgcyxyPWIuYXQrIioiLHE9YS5lQy5nZXQocikKaWYocSE9 -bnVsbClyZXR1cm4gcQpzPUEuWjcoYSxiLHIsYykKYS5lQy5zZXQocixzKQpyZXR1cm4gc30sClo3KGEs -YixjLGQpe3ZhciBzLHIscQppZihkKXtzPWIueAppZighQS5BOChiKSlyPWI9PT10LlB8fGI9PT10LlR8 -fHM9PT03fHxzPT09NgplbHNlIHI9ITAKaWYocilyZXR1cm4gYn1xPW5ldyBBLkpjKG51bGwsbnVsbCkK -cS54PTYKcS55PWIKcS5hdD1jCnJldHVybiBBLkJEKGEscSl9LApCKGEsYixjKXt2YXIgcyxyPWIuYXQr -Ij8iLHE9YS5lQy5nZXQocikKaWYocSE9bnVsbClyZXR1cm4gcQpzPUEubGwoYSxiLHIsYykKYS5lQy5z -ZXQocixzKQpyZXR1cm4gc30sCmxsKGEsYixjLGQpe3ZhciBzLHIscSxwCmlmKGQpe3M9Yi54CmlmKCFB -LkE4KGIpKWlmKCEoYj09PXQuUHx8Yj09PXQuVCkpaWYocyE9PTcpcj1zPT09OCYmQS5sUihiLnkpCmVs -c2Ugcj0hMAplbHNlIHI9ITAKZWxzZSByPSEwCmlmKHIpcmV0dXJuIGIKZWxzZSBpZihzPT09MXx8Yj09 -PXQuYXcpcmV0dXJuIHQuUAplbHNlIGlmKHM9PT02KXtxPWIueQppZihxLng9PT04JiZBLmxSKHEueSkp -cmV0dXJuIHEKZWxzZSByZXR1cm4gQS5jeihhLGIpfX1wPW5ldyBBLkpjKG51bGwsbnVsbCkKcC54PTcK -cC55PWIKcC5hdD1jCnJldHVybiBBLkJEKGEscCl9LApmKGEsYixjKXt2YXIgcyxyPWIuYXQrIi8iLHE9 -YS5lQy5nZXQocikKaWYocSE9bnVsbClyZXR1cm4gcQpzPUEuZVYoYSxiLHIsYykKYS5lQy5zZXQocixz -KQpyZXR1cm4gc30sCmVWKGEsYixjLGQpe3ZhciBzLHIscQppZihkKXtzPWIueAppZighQS5BOChiKSlp -ZighKGI9PT10Ll8pKXI9ITEKZWxzZSByPSEwCmVsc2Ugcj0hMAppZihyfHxiPT09dC5LKXJldHVybiBi -CmVsc2UgaWYocz09PTEpcmV0dXJuIEEuSihhLCJiOCIsW2JdKQplbHNlIGlmKGI9PT10LlB8fGI9PT10 -LlQpcmV0dXJuIHQuYkd9cT1uZXcgQS5KYyhudWxsLG51bGwpCnEueD04CnEueT1iCnEuYXQ9YwpyZXR1 -cm4gQS5CRChhLHEpfSwKSChhLGIpe3ZhciBzLHIscT0iIitiKyJeIixwPWEuZUMuZ2V0KHEpCmlmKHAh -PW51bGwpcmV0dXJuIHAKcz1uZXcgQS5KYyhudWxsLG51bGwpCnMueD0xNApzLnk9YgpzLmF0PXEKcj1B -LkJEKGEscykKYS5lQy5zZXQocSxyKQpyZXR1cm4gcn0sClV4KGEpe3ZhciBzLHIscSxwPWEubGVuZ3Ro -CmZvcihzPSIiLHI9IiIscT0wO3E8cDsrK3Escj0iLCIpcys9cithW3FdLmF0CnJldHVybiBzfSwKUzQo -YSl7dmFyIHMscixxLHAsbyxuPWEubGVuZ3RoCmZvcihzPSIiLHI9IiIscT0wO3E8bjtxKz0zLHI9Iiwi -KXtwPWFbcV0Kbz1hW3ErMV0/IiEiOiI6IgpzKz1yK3ArbythW3ErMl0uYXR9cmV0dXJuIHN9LApKKGEs -YixjKXt2YXIgcyxyLHEscD1iCmlmKGMubGVuZ3RoPjApcCs9IjwiK0EuVXgoYykrIj4iCnM9YS5lQy5n -ZXQocCkKaWYocyE9bnVsbClyZXR1cm4gcwpyPW5ldyBBLkpjKG51bGwsbnVsbCkKci54PTkKci55PWIK -ci56PWMKaWYoYy5sZW5ndGg+MClyLmM9Y1swXQpyLmF0PXAKcT1BLkJEKGEscikKYS5lQy5zZXQocCxx -KQpyZXR1cm4gcX0sCmEoYSxiLGMpe3ZhciBzLHIscSxwLG8sbgppZihiLng9PT0xMCl7cz1iLnkKcj1i -LnouY29uY2F0KGMpfWVsc2V7cj1jCnM9Yn1xPXMuYXQrKCI7PCIrQS5VeChyKSsiPiIpCnA9YS5lQy5n -ZXQocSkKaWYocCE9bnVsbClyZXR1cm4gcApvPW5ldyBBLkpjKG51bGwsbnVsbCkKby54PTEwCm8ueT1z -Cm8uej1yCm8uYXQ9cQpuPUEuQkQoYSxvKQphLmVDLnNldChxLG4pCnJldHVybiBufSwKb1AoYSxiLGMp -e3ZhciBzLHIscT0iKyIrKGIrIigiK0EuVXgoYykrIikiKSxwPWEuZUMuZ2V0KHEpCmlmKHAhPW51bGwp -cmV0dXJuIHAKcz1uZXcgQS5KYyhudWxsLG51bGwpCnMueD0xMQpzLnk9YgpzLno9YwpzLmF0PXEKcj1B -LkJEKGEscykKYS5lQy5zZXQocSxyKQpyZXR1cm4gcn0sCk5mKGEsYixjKXt2YXIgcyxyLHEscCxvLG49 -Yi5hdCxtPWMuYSxsPW0ubGVuZ3RoLGs9Yy5iLGo9ay5sZW5ndGgsaT1jLmMsaD1pLmxlbmd0aCxnPSIo -IitBLlV4KG0pCmlmKGo+MCl7cz1sPjA/IiwiOiIiCmcrPXMrIlsiK0EuVXgoaykrIl0ifWlmKGg+MCl7 -cz1sPjA/IiwiOiIiCmcrPXMrInsiK0EuUzQoaSkrIn0ifXI9bisoZysiKSIpCnE9YS5lQy5nZXQocikK -aWYocSE9bnVsbClyZXR1cm4gcQpwPW5ldyBBLkpjKG51bGwsbnVsbCkKcC54PTEyCnAueT1iCnAuej1j -CnAuYXQ9cgpvPUEuQkQoYSxwKQphLmVDLnNldChyLG8pCnJldHVybiBvfSwKRChhLGIsYyxkKXt2YXIg -cyxyPWIuYXQrKCI8IitBLlV4KGMpKyI+IikscT1hLmVDLmdldChyKQppZihxIT1udWxsKXJldHVybiBx -CnM9QS5odyhhLGIsYyxyLGQpCmEuZUMuc2V0KHIscykKcmV0dXJuIHN9LApodyhhLGIsYyxkLGUpe3Zh -ciBzLHIscSxwLG8sbixtLGwKaWYoZSl7cz1jLmxlbmd0aApyPUEudlUocykKZm9yKHE9MCxwPTA7cDxz -OysrcCl7bz1jW3BdCmlmKG8ueD09PTEpe3JbcF09bzsrK3F9fWlmKHE+MCl7bj1BLlBMKGEsYixyLDAp -Cm09QS5iWihhLGMsciwwKQpyZXR1cm4gQS5EKGEsbixtLGMhPT1tKX19bD1uZXcgQS5KYyhudWxsLG51 -bGwpCmwueD0xMwpsLnk9YgpsLno9YwpsLmF0PWQKcmV0dXJuIEEuQkQoYSxsKX0sCm8oYSxiLGMsZCl7 -cmV0dXJue3U6YSxlOmIscjpjLHM6W10scDowLG46ZH19LAppKGEpe3ZhciBzLHIscSxwLG8sbixtLGws -ayxqPWEucixpPWEucwpmb3Iocz1qLmxlbmd0aCxyPTA7cjxzOyl7cT1qLmNoYXJDb2RlQXQocikKaWYo -cT49NDgmJnE8PTU3KXI9QS5BKHIrMSxxLGosaSkKZWxzZSBpZigoKChxfDMyKT4+PjApLTk3JjY1NTM1 -KTwyNnx8cT09PTk1fHxxPT09MzZ8fHE9PT0xMjQpcj1BLnQoYSxyLGosaSwhMSkKZWxzZSBpZihxPT09 -NDYpcj1BLnQoYSxyLGosaSwhMCkKZWxzZXsrK3IKc3dpdGNoKHEpe2Nhc2UgNDQ6YnJlYWsKY2FzZSA1 -ODppLnB1c2goITEpCmJyZWFrCmNhc2UgMzM6aS5wdXNoKCEwKQpicmVhawpjYXNlIDU5OmkucHVzaChB -LksoYS51LGEuZSxpLnBvcCgpKSkKYnJlYWsKY2FzZSA5NDppLnB1c2goQS5IKGEudSxpLnBvcCgpKSkK -YnJlYWsKY2FzZSAzNTppLnB1c2goQS5tKGEudSw1LCIjIikpCmJyZWFrCmNhc2UgNjQ6aS5wdXNoKEEu -bShhLnUsMiwiQCIpKQpicmVhawpjYXNlIDEyNjppLnB1c2goQS5tKGEudSwzLCJ+IikpCmJyZWFrCmNh -c2UgNjA6aS5wdXNoKGEucCkKYS5wPWkubGVuZ3RoCmJyZWFrCmNhc2UgNjI6cD1hLnUKbz1pLnNwbGlj -ZShhLnApCkEucihhLnUsYS5lLG8pCmEucD1pLnBvcCgpCm49aS5wb3AoKQppZih0eXBlb2Ygbj09InN0 -cmluZyIpaS5wdXNoKEEuSihwLG4sbykpCmVsc2V7bT1BLksocCxhLmUsbikKc3dpdGNoKG0ueCl7Y2Fz -ZSAxMjppLnB1c2goQS5EKHAsbSxvLGEubikpCmJyZWFrCmRlZmF1bHQ6aS5wdXNoKEEuYShwLG0sbykp -CmJyZWFrfX1icmVhawpjYXNlIDM4OkEuSShhLGkpCmJyZWFrCmNhc2UgNDI6cD1hLnUKaS5wdXNoKEEu -QyhwLEEuSyhwLGEuZSxpLnBvcCgpKSxhLm4pKQpicmVhawpjYXNlIDYzOnA9YS51CmkucHVzaChBLkIo -cCxBLksocCxhLmUsaS5wb3AoKSksYS5uKSkKYnJlYWsKY2FzZSA0NzpwPWEudQppLnB1c2goQS5mKHAs -QS5LKHAsYS5lLGkucG9wKCkpLGEubikpCmJyZWFrCmNhc2UgNDA6aS5wdXNoKC0zKQppLnB1c2goYS5w -KQphLnA9aS5sZW5ndGgKYnJlYWsKY2FzZSA0MTpBLk0oYSxpKQpicmVhawpjYXNlIDkxOmkucHVzaChh -LnApCmEucD1pLmxlbmd0aApicmVhawpjYXNlIDkzOm89aS5zcGxpY2UoYS5wKQpBLnIoYS51LGEuZSxv -KQphLnA9aS5wb3AoKQppLnB1c2gobykKaS5wdXNoKC0xKQpicmVhawpjYXNlIDEyMzppLnB1c2goYS5w -KQphLnA9aS5sZW5ndGgKYnJlYWsKY2FzZSAxMjU6bz1pLnNwbGljZShhLnApCkEueShhLnUsYS5lLG8p -CmEucD1pLnBvcCgpCmkucHVzaChvKQppLnB1c2goLTIpCmJyZWFrCmNhc2UgNDM6bD1qLmluZGV4T2Yo -IigiLHIpCmkucHVzaChqLnN1YnN0cmluZyhyLGwpKQppLnB1c2goLTQpCmkucHVzaChhLnApCmEucD1p -Lmxlbmd0aApyPWwrMQpicmVhawpkZWZhdWx0OnRocm93IkJhZCBjaGFyYWN0ZXIgIitxfX19az1pLnBv -cCgpCnJldHVybiBBLksoYS51LGEuZSxrKX0sCkEoYSxiLGMsZCl7dmFyIHMscixxPWItNDgKZm9yKHM9 -Yy5sZW5ndGg7YTxzOysrYSl7cj1jLmNoYXJDb2RlQXQoYSkKaWYoIShyPj00OCYmcjw9NTcpKWJyZWFr -CnE9cSoxMCsoci00OCl9ZC5wdXNoKHEpCnJldHVybiBhfSwKdChhLGIsYyxkLGUpe3ZhciBzLHIscSxw -LG8sbixtPWIrMQpmb3Iocz1jLmxlbmd0aDttPHM7KyttKXtyPWMuY2hhckNvZGVBdChtKQppZihyPT09 -NDYpe2lmKGUpYnJlYWsKZT0hMH1lbHNle2lmKCEoKCgocnwzMik+Pj4wKS05NyY2NTUzNSk8MjZ8fHI9 -PT05NXx8cj09PTM2fHxyPT09MTI0KSlxPXI+PTQ4JiZyPD01NwplbHNlIHE9ITAKaWYoIXEpYnJlYWt9 -fXA9Yy5zdWJzdHJpbmcoYixtKQppZihlKXtzPWEudQpvPWEuZQppZihvLng9PT0xMClvPW8ueQpuPUEu -UW8ocyxvLnkpW3BdCmlmKG49PW51bGwpQS52KCdObyAiJytwKyciIGluICInK0EubUQobykrJyInKQpk -LnB1c2goQS5jRShzLG8sbikpfWVsc2UgZC5wdXNoKHApCnJldHVybiBtfSwKTShhLGIpe3ZhciBzLHIs -cSxwLG8sbj1udWxsLG09YS51LGw9Yi5wb3AoKQppZih0eXBlb2YgbD09Im51bWJlciIpc3dpdGNoKGwp -e2Nhc2UtMTpzPWIucG9wKCkKcj1uCmJyZWFrCmNhc2UtMjpyPWIucG9wKCkKcz1uCmJyZWFrCmRlZmF1 -bHQ6Yi5wdXNoKGwpCnI9bgpzPXIKYnJlYWt9ZWxzZXtiLnB1c2gobCkKcj1uCnM9cn1xPUEub1UoYSxi -KQpsPWIucG9wKCkKc3dpdGNoKGwpe2Nhc2UtMzpsPWIucG9wKCkKaWYocz09bnVsbClzPW0uc0VBCmlm -KHI9PW51bGwpcj1tLnNFQQpwPUEuSyhtLGEuZSxsKQpvPW5ldyBBLkVUKCkKby5hPXEKby5iPXMKby5j -PXIKYi5wdXNoKEEuTmYobSxwLG8pKQpyZXR1cm4KY2FzZS00OmIucHVzaChBLm9QKG0sYi5wb3AoKSxx -KSkKcmV0dXJuCmRlZmF1bHQ6dGhyb3cgQS5iKEEuaFYoIlVuZXhwZWN0ZWQgc3RhdGUgdW5kZXIgYCgp -YDogIitBLmQobCkpKX19LApJKGEsYil7dmFyIHM9Yi5wb3AoKQppZigwPT09cyl7Yi5wdXNoKEEubShh -LnUsMSwiMCYiKSkKcmV0dXJufWlmKDE9PT1zKXtiLnB1c2goQS5tKGEudSw0LCIxJiIpKQpyZXR1cm59 -dGhyb3cgQS5iKEEuaFYoIlVuZXhwZWN0ZWQgZXh0ZW5kZWQgb3BlcmF0aW9uICIrQS5kKHMpKSl9LApv -VShhLGIpe3ZhciBzPWIuc3BsaWNlKGEucCkKQS5yKGEudSxhLmUscykKYS5wPWIucG9wKCkKcmV0dXJu -IHN9LApLKGEsYixjKXtpZih0eXBlb2YgYz09InN0cmluZyIpcmV0dXJuIEEuSihhLGMsYS5zRUEpCmVs -c2UgaWYodHlwZW9mIGM9PSJudW1iZXIiKXtiLnRvU3RyaW5nCnJldHVybiBBLlRWKGEsYixjKX1lbHNl -IHJldHVybiBjfSwKcihhLGIsYyl7dmFyIHMscj1jLmxlbmd0aApmb3Iocz0wO3M8cjsrK3MpY1tzXT1B -LksoYSxiLGNbc10pfSwKeShhLGIsYyl7dmFyIHMscj1jLmxlbmd0aApmb3Iocz0yO3M8cjtzKz0zKWNb -c109QS5LKGEsYixjW3NdKX0sClRWKGEsYixjKXt2YXIgcyxyLHE9Yi54CmlmKHE9PT0xMCl7aWYoYz09 -PTApcmV0dXJuIGIueQpzPWIuegpyPXMubGVuZ3RoCmlmKGM8PXIpcmV0dXJuIHNbYy0xXQpjLT1yCmI9 -Yi55CnE9Yi54fWVsc2UgaWYoYz09PTApcmV0dXJuIGIKaWYocSE9PTkpdGhyb3cgQS5iKEEuaFYoIklu -ZGV4ZWQgYmFzZSBtdXN0IGJlIGFuIGludGVyZmFjZSB0eXBlIikpCnM9Yi56CmlmKGM8PXMubGVuZ3Ro -KXJldHVybiBzW2MtMV0KdGhyb3cgQS5iKEEuaFYoIkJhZCBpbmRleCAiK2MrIiBmb3IgIitiWyJbIl0o -MCkpKX0sCldlKGEsYixjLGQsZSl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGoKaWYoYj09PWQpcmV0dXJu -ITAKaWYoIUEuQTgoZCkpaWYoIShkPT09dC5fKSlzPSExCmVsc2Ugcz0hMAplbHNlIHM9ITAKaWYocyly -ZXR1cm4hMApyPWIueAppZihyPT09NClyZXR1cm4hMAppZihBLkE4KGIpKXJldHVybiExCmlmKGIueCE9 -PTEpcz0hMQplbHNlIHM9ITAKaWYocylyZXR1cm4hMApxPXI9PT0xNAppZihxKWlmKEEuV2UoYSxjW2Iu -eV0sYyxkLGUpKXJldHVybiEwCnA9ZC54CnM9Yj09PXQuUHx8Yj09PXQuVAppZihzKXtpZihwPT09OCly -ZXR1cm4gQS5XZShhLGIsYyxkLnksZSkKcmV0dXJuIGQ9PT10LlB8fGQ9PT10LlR8fHA9PT03fHxwPT09 -Nn1pZihkPT09dC5LKXtpZihyPT09OClyZXR1cm4gQS5XZShhLGIueSxjLGQsZSkKaWYocj09PTYpcmV0 -dXJuIEEuV2UoYSxiLnksYyxkLGUpCnJldHVybiByIT09N31pZihyPT09NilyZXR1cm4gQS5XZShhLGIu -eSxjLGQsZSkKaWYocD09PTYpe3M9QS5jeihhLGQpCnJldHVybiBBLldlKGEsYixjLHMsZSl9aWYocj09 -PTgpe2lmKCFBLldlKGEsYi55LGMsZCxlKSlyZXR1cm4hMQpyZXR1cm4gQS5XZShhLEEueFooYSxiKSxj -LGQsZSl9aWYocj09PTcpe3M9QS5XZShhLHQuUCxjLGQsZSkKcmV0dXJuIHMmJkEuV2UoYSxiLnksYyxk -LGUpfWlmKHA9PT04KXtpZihBLldlKGEsYixjLGQueSxlKSlyZXR1cm4hMApyZXR1cm4gQS5XZShhLGIs -YyxBLnhaKGEsZCksZSl9aWYocD09PTcpe3M9QS5XZShhLGIsYyx0LlAsZSkKcmV0dXJuIHN8fEEuV2Uo -YSxiLGMsZC55LGUpfWlmKHEpcmV0dXJuITEKcz1yIT09MTIKaWYoKCFzfHxyPT09MTMpJiZkPT09dC5Z -KXJldHVybiEwCmlmKHA9PT0xMyl7aWYoYj09PXQuRSlyZXR1cm4hMAppZihyIT09MTMpcmV0dXJuITEK -bz1iLnoKbj1kLnoKbT1vLmxlbmd0aAppZihtIT09bi5sZW5ndGgpcmV0dXJuITEKYz1jPT1udWxsP286 -by5jb25jYXQoYykKZT1lPT1udWxsP246bi5jb25jYXQoZSkKZm9yKGw9MDtsPG07KytsKXtrPW9bbF0K -aj1uW2xdCmlmKCFBLldlKGEsayxjLGosZSl8fCFBLldlKGEsaixlLGssYykpcmV0dXJuITF9cmV0dXJu -IEEuYk8oYSxiLnksYyxkLnksZSl9aWYocD09PTEyKXtpZihiPT09dC5FKXJldHVybiEwCmlmKHMpcmV0 -dXJuITEKcmV0dXJuIEEuYk8oYSxiLGMsZCxlKX1pZihyPT09OSl7aWYocCE9PTkpcmV0dXJuITEKcmV0 -dXJuIEEucEcoYSxiLGMsZCxlKX1zPXI9PT0xMQppZihzJiZkPT09dC5nVClyZXR1cm4hMAppZihzJiZw -PT09MTEpcmV0dXJuIEEuYjYoYSxiLGMsZCxlKQpyZXR1cm4hMX0sCmJPKGEzLGE0LGE1LGE2LGE3KXt2 -YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxhMCxhMSxhMgppZighQS5XZShh -MyxhNC55LGE1LGE2LnksYTcpKXJldHVybiExCnM9YTQuegpyPWE2LnoKcT1zLmEKcD1yLmEKbz1xLmxl -bmd0aApuPXAubGVuZ3RoCmlmKG8+bilyZXR1cm4hMQptPW4tbwpsPXMuYgprPXIuYgpqPWwubGVuZ3Ro -Cmk9ay5sZW5ndGgKaWYobytqPG4raSlyZXR1cm4hMQpmb3IoaD0wO2g8bzsrK2gpe2c9cVtoXQppZigh -QS5XZShhMyxwW2hdLGE3LGcsYTUpKXJldHVybiExfWZvcihoPTA7aDxtOysraCl7Zz1sW2hdCmlmKCFB -LldlKGEzLHBbbytoXSxhNyxnLGE1KSlyZXR1cm4hMX1mb3IoaD0wO2g8aTsrK2gpe2c9bFttK2hdCmlm -KCFBLldlKGEzLGtbaF0sYTcsZyxhNSkpcmV0dXJuITF9Zj1zLmMKZT1yLmMKZD1mLmxlbmd0aApjPWUu -bGVuZ3RoCmZvcihiPTAsYT0wO2E8YzthKz0zKXthMD1lW2FdCmZvcig7ITA7KXtpZihiPj1kKXJldHVy -biExCmExPWZbYl0KYis9MwppZihhMDxhMSlyZXR1cm4hMQphMj1mW2ItMl0KaWYoYTE8YTApe2lmKGEy -KXJldHVybiExCmNvbnRpbnVlfWc9ZVthKzFdCmlmKGEyJiYhZylyZXR1cm4hMQpnPWZbYi0xXQppZigh -QS5XZShhMyxlW2ErMl0sYTcsZyxhNSkpcmV0dXJuITEKYnJlYWt9fWZvcig7YjxkOyl7aWYoZltiKzFd -KXJldHVybiExCmIrPTN9cmV0dXJuITB9LApwRyhhLGIsYyxkLGUpe3ZhciBzLHIscSxwLG8sbixtLGw9 -Yi55LGs9ZC55CmZvcig7bCE9PWs7KXtzPWEudFJbbF0KaWYocz09bnVsbClyZXR1cm4hMQppZih0eXBl -b2Ygcz09InN0cmluZyIpe2w9cwpjb250aW51ZX1yPXNba10KaWYocj09bnVsbClyZXR1cm4hMQpxPXIu -bGVuZ3RoCnA9cT4wP25ldyBBcnJheShxKTp2LnR5cGVVbml2ZXJzZS5zRUEKZm9yKG89MDtvPHE7Kytv -KXBbb109QS5jRShhLGIscltvXSkKcmV0dXJuIEEuU1coYSxwLG51bGwsYyxkLnosZSl9bj1iLnoKbT1k -LnoKcmV0dXJuIEEuU1coYSxuLG51bGwsYyxtLGUpfSwKU1coYSxiLGMsZCxlLGYpe3ZhciBzLHIscSxw -PWIubGVuZ3RoCmZvcihzPTA7czxwOysrcyl7cj1iW3NdCnE9ZVtzXQppZighQS5XZShhLHIsZCxxLGYp -KXJldHVybiExfXJldHVybiEwfSwKYjYoYSxiLGMsZCxlKXt2YXIgcyxyPWIueixxPWQueixwPXIubGVu -Z3RoCmlmKHAhPT1xLmxlbmd0aClyZXR1cm4hMQppZihiLnkhPT1kLnkpcmV0dXJuITEKZm9yKHM9MDtz -PHA7KytzKWlmKCFBLldlKGEscltzXSxjLHFbc10sZSkpcmV0dXJuITEKcmV0dXJuITB9LApsUihhKXt2 -YXIgcyxyPWEueAppZighKGE9PT10LlB8fGE9PT10LlQpKWlmKCFBLkE4KGEpKWlmKHIhPT03KWlmKCEo -cj09PTYmJkEubFIoYS55KSkpcz1yPT09OCYmQS5sUihhLnkpCmVsc2Ugcz0hMAplbHNlIHM9ITAKZWxz -ZSBzPSEwCmVsc2Ugcz0hMApyZXR1cm4gc30sCmNjKGEpe3ZhciBzCmlmKCFBLkE4KGEpKWlmKCEoYT09 -PXQuXykpcz0hMQplbHNlIHM9ITAKZWxzZSBzPSEwCnJldHVybiBzfSwKQTgoYSl7dmFyIHM9YS54CnJl -dHVybiBzPT09Mnx8cz09PTN8fHM9PT00fHxzPT09NXx8YT09PXQuWH0sCkl4KGEsYil7dmFyIHMscixx -PU9iamVjdC5rZXlzKGIpLHA9cS5sZW5ndGgKZm9yKHM9MDtzPHA7KytzKXtyPXFbc10KYVtyXT1iW3Jd -fX0sCnZVKGEpe3JldHVybiBhPjA/bmV3IEFycmF5KGEpOnYudHlwZVVuaXZlcnNlLnNFQX0sCkpjOmZ1 -bmN0aW9uIEpjKGEsYil7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLnc9Xy5yPV8uYz1udWxsCl8ueD0w -Cl8uYXQ9Xy5hcz1fLlE9Xy56PV8ueT1udWxsfSwKRVQ6ZnVuY3Rpb24gRVQoKXt0aGlzLmM9dGhpcy5i -PXRoaXMuYT1udWxsfSwKbFk6ZnVuY3Rpb24gbFkoYSl7dGhpcy5hPWF9LAprUzpmdW5jdGlvbiBrUygp -e30sCmlNOmZ1bmN0aW9uIGlNKGEpe3RoaXMuYT1hfSwKeGcoKXt2YXIgcyxyLHE9e30KaWYoc2VsZi5z -Y2hlZHVsZUltbWVkaWF0ZSE9bnVsbClyZXR1cm4gQS5FWCgpCmlmKHNlbGYuTXV0YXRpb25PYnNlcnZl -ciE9bnVsbCYmc2VsZi5kb2N1bWVudCE9bnVsbCl7cz1zZWxmLmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQo -ImRpdiIpCnI9c2VsZi5kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzcGFuIikKcS5hPW51bGwKbmV3IHNl -bGYuTXV0YXRpb25PYnNlcnZlcihBLnRSKG5ldyBBLnRoKHEpLDEpKS5vYnNlcnZlKHMse2NoaWxkTGlz -dDp0cnVlfSkKcmV0dXJuIG5ldyBBLmhhKHEscyxyKX1lbHNlIGlmKHNlbGYuc2V0SW1tZWRpYXRlIT1u -dWxsKXJldHVybiBBLnl0KCkKcmV0dXJuIEEucVcoKX0sClpWKGEpe3NlbGYuc2NoZWR1bGVJbW1lZGlh -dGUoQS50UihuZXcgQS5Wcyh0Lk0uYShhKSksMCkpfSwKb0EoYSl7c2VsZi5zZXRJbW1lZGlhdGUoQS50 -UihuZXcgQS5GdCh0Lk0uYShhKSksMCkpfSwKQnooYSl7dC5NLmEoYSkKQS5RTigwLGEpfSwKUU4oYSxi -KXt2YXIgcz1uZXcgQS5XMygpCnMuQ1koYSxiKQpyZXR1cm4gc30sCkZYKGEpe3JldHVybiBuZXcgQS5p -aChuZXcgQS52cygkLlgzLGEuQygidnM8MD4iKSksYS5DKCJpaDwwPiIpKX0sCkRJKGEsYil7YS4kMigw -LG51bGwpCmIuYj0hMApyZXR1cm4gYi5hfSwKalEoYSxiKXtBLkplKGEsYil9LAp5QyhhLGIpe2IuYU0o -MCxhKX0sCmYzKGEsYil7Yi53MChBLlJ1KGEpLEEudHMoYSkpfSwKSmUoYSxiKXt2YXIgcyxyLHE9bmV3 -IEEuV00oYikscD1uZXcgQS5TWChiKQppZihhIGluc3RhbmNlb2YgQS52cylhLlFkKHEscCx0LnopCmVs -c2V7cz10LnoKaWYodC5pLmIoYSkpYS5TcShxLHAscykKZWxzZXtyPW5ldyBBLnZzKCQuWDMsdC5jKQpy -LmE9OApyLmM9YQpyLlFkKHEscCxzKX19fSwKbHooYSl7dmFyIHM9ZnVuY3Rpb24oYixjKXtyZXR1cm4g -ZnVuY3Rpb24oZCxlKXt3aGlsZSh0cnVlKXRyeXtiKGQsZSkKYnJlYWt9Y2F0Y2gocil7ZT1yCmQ9Y319 -fShhLDEpCnJldHVybiAkLlgzLkxqKG5ldyBBLkdzKHMpLHQuSCx0LlMsdC56KX0sCkdRKGEpe3JldHVy -biBuZXcgQS5GeShhLDEpfSwKVGgoKXtyZXR1cm4gQi53UX0sClltKGEpe3JldHVybiBuZXcgQS5GeShh -LDMpfSwKbDAoYSxiKXtyZXR1cm4gbmV3IEEucTQoYSxiLkMoInE0PDA+IikpfSwKVGwoYSxiKXt2YXIg -cz1BLmNiKGEsImVycm9yIix0LkspCnJldHVybiBuZXcgQS5DdyhzLGI9PW51bGw/QS52MChhKTpiKX0s -CnYwKGEpe3ZhciBzCmlmKHQudS5iKGEpKXtzPWEuZ0lJKCkKaWYocyE9bnVsbClyZXR1cm4gc31yZXR1 -cm4gQi5wZH0sCkE5KGEsYil7dmFyIHMscixxCmZvcihzPXQuYztyPWEuYSwociY0KSE9PTA7KWE9cy5h -KGEuYykKaWYoKHImMjQpIT09MCl7cT1iLmFoKCkKYi51ZyhhKQpBLkhaKGIscSl9ZWxzZXtxPXQuZS5h -KGIuYykKYi5hPWIuYSYxfDQKYi5jPWEKYS5qUShxKX19LApIWihhLGEwKXt2YXIgcyxyLHEscCxvLG4s -bSxsLGssaixpLGgsZyxmLGUsZCxjPXt9LGI9Yy5hPWEKZm9yKHM9dC5uLHI9dC5lLHE9dC5pOyEwOyl7 -cD17fQpvPWIuYQpuPShvJjE2KT09PTAKbT0hbgppZihhMD09bnVsbCl7aWYobSYmKG8mMSk9PT0wKXts -PXMuYShiLmMpCkEuU2kobC5hLGwuYil9cmV0dXJufXAuYT1hMAprPWEwLmEKZm9yKGI9YTA7ayE9bnVs -bDtiPWssaz1qKXtiLmE9bnVsbApBLkhaKGMuYSxiKQpwLmE9awpqPWsuYX1vPWMuYQppPW8uYwpwLmI9 -bQpwLmM9aQppZihuKXtoPWIuYwpoPShoJjEpIT09MHx8KGgmMTUpPT09OH1lbHNlIGg9ITAKaWYoaCl7 -Zz1iLmIuYgppZihtKXtvPW8uYj09PWcKbz0hKG98fG8pfWVsc2Ugbz0hMQppZihvKXtzLmEoaSkKQS5T -aShpLmEsaS5iKQpyZXR1cm59Zj0kLlgzCmlmKGYhPT1nKSQuWDM9ZwplbHNlIGY9bnVsbApiPWIuYwpp -ZigoYiYxNSk9PT04KW5ldyBBLlJUKHAsYyxtKS4kMCgpCmVsc2UgaWYobil7aWYoKGImMSkhPT0wKW5l -dyBBLnJxKHAsaSkuJDAoKX1lbHNlIGlmKChiJjIpIT09MCluZXcgQS5SVyhjLHApLiQwKCkKaWYoZiE9 -bnVsbCkkLlgzPWYKYj1wLmMKaWYocS5iKGIpKXtvPXAuYS4kdGkKbz1vLkMoImI4PDI+IikuYihiKXx8 -IW8uelsxXS5iKGIpfWVsc2Ugbz0hMQppZihvKXtxLmEoYikKZT1wLmEuYgppZigoYi5hJjI0KSE9PTAp -e2Q9ci5hKGUuYykKZS5jPW51bGwKYTA9ZS5OOChkKQplLmE9Yi5hJjMwfGUuYSYxCmUuYz1iLmMKYy5h -PWIKY29udGludWV9ZWxzZSBBLkE5KGIsZSkKcmV0dXJufX1lPXAuYS5iCmQ9ci5hKGUuYykKZS5jPW51 -bGwKYTA9ZS5OOChkKQpiPXAuYgpvPXAuYwppZighYil7ZS4kdGkuYy5hKG8pCmUuYT04CmUuYz1vfWVs -c2V7cy5hKG8pCmUuYT1lLmEmMXwxNgplLmM9b31jLmE9ZQpiPWV9fSwKVkgoYSxiKXt2YXIgcwppZih0 -Lm0uYihhKSlyZXR1cm4gYi5MaihhLHQueix0LkssdC5sKQpzPXQudgppZihzLmIoYSkpcmV0dXJuIHMu -YShhKQp0aHJvdyBBLmIoQS5MMyhhLCJvbkVycm9yIix1LmMpKX0sCnB1KCl7dmFyIHMscgpmb3Iocz0k -LlM2O3MhPW51bGw7cz0kLlM2KXskLm1nPW51bGwKcj1zLmIKJC5TNj1yCmlmKHI9PW51bGwpJC5rOD1u -dWxsCnMuYS4kMCgpfX0sCmVOKCl7JC5VRD0hMAp0cnl7QS5wdSgpfWZpbmFsbHl7JC5tZz1udWxsCiQu -VUQ9ITEKaWYoJC5TNiE9bnVsbCkkLnV0KCkuJDEoQS5VSSgpKX19LAplVyhhKXt2YXIgcz1uZXcgQS5P -TShhKSxyPSQuazgKaWYocj09bnVsbCl7JC5TNj0kLms4PXMKaWYoISQuVUQpJC51dCgpLiQxKEEuVUko -KSl9ZWxzZSAkLms4PXIuYj1zfSwKclIoYSl7dmFyIHMscixxLHA9JC5TNgppZihwPT1udWxsKXtBLmVX -KGEpCiQubWc9JC5rOApyZXR1cm59cz1uZXcgQS5PTShhKQpyPSQubWcKaWYocj09bnVsbCl7cy5iPXAK -JC5TNj0kLm1nPXN9ZWxzZXtxPXIuYgpzLmI9cQokLm1nPXIuYj1zCmlmKHE9PW51bGwpJC5rOD1zfX0s -CnJiKGEpe3ZhciBzLHI9bnVsbCxxPSQuWDMKaWYoQi5OVT09PXEpe0EuVGsocixyLEIuTlUsYSkKcmV0 -dXJufXM9ITEKaWYocyl7QS5UayhyLHIscSx0Lk0uYShhKSkKcmV0dXJufUEuVGsocixyLHEsdC5NLmEo -cS5HWShhKSkpfSwKUXcoYSxiKXtBLmNiKGEsInN0cmVhbSIsdC5LKQpyZXR1cm4gbmV3IEEueEkoYi5D -KCJ4STwwPiIpKX0sClNpKGEsYil7QS5yUihuZXcgQS5FdihhLGIpKX0sClQ4KGEsYixjLGQsZSl7dmFy -IHMscj0kLlgzCmlmKHI9PT1jKXJldHVybiBkLiQwKCkKJC5YMz1jCnM9cgp0cnl7cj1kLiQwKCkKcmV0 -dXJuIHJ9ZmluYWxseXskLlgzPXN9fSwKeXYoYSxiLGMsZCxlLGYsZyl7dmFyIHMscj0kLlgzCmlmKHI9 -PT1jKXJldHVybiBkLiQxKGUpCiQuWDM9YwpzPXIKdHJ5e3I9ZC4kMShlKQpyZXR1cm4gcn1maW5hbGx5 -eyQuWDM9c319LApReChhLGIsYyxkLGUsZixnLGgsaSl7dmFyIHMscj0kLlgzCmlmKHI9PT1jKXJldHVy -biBkLiQyKGUsZikKJC5YMz1jCnM9cgp0cnl7cj1kLiQyKGUsZikKcmV0dXJuIHJ9ZmluYWxseXskLlgz -PXN9fSwKVGsoYSxiLGMsZCl7dC5NLmEoZCkKaWYoQi5OVSE9PWMpZD1jLkdZKGQpCkEuZVcoZCl9LAp0 -aDpmdW5jdGlvbiB0aChhKXt0aGlzLmE9YX0sCmhhOmZ1bmN0aW9uIGhhKGEsYixjKXt0aGlzLmE9YQp0 -aGlzLmI9Ygp0aGlzLmM9Y30sClZzOmZ1bmN0aW9uIFZzKGEpe3RoaXMuYT1hfSwKRnQ6ZnVuY3Rpb24g -RnQoYSl7dGhpcy5hPWF9LApXMzpmdW5jdGlvbiBXMygpe30sCnlIOmZ1bmN0aW9uIHlIKGEsYil7dGhp -cy5hPWEKdGhpcy5iPWJ9LAppaDpmdW5jdGlvbiBpaChhLGIpe3RoaXMuYT1hCnRoaXMuYj0hMQp0aGlz -LiR0aT1ifSwKV006ZnVuY3Rpb24gV00oYSl7dGhpcy5hPWF9LApTWDpmdW5jdGlvbiBTWChhKXt0aGlz -LmE9YX0sCkdzOmZ1bmN0aW9uIEdzKGEpe3RoaXMuYT1hfSwKRnk6ZnVuY3Rpb24gRnkoYSxiKXt0aGlz -LmE9YQp0aGlzLmI9Yn0sCkdWOmZ1bmN0aW9uIEdWKGEsYil7dmFyIF89dGhpcwpfLmE9YQpfLmQ9Xy5j -PV8uYj1udWxsCl8uJHRpPWJ9LApxNDpmdW5jdGlvbiBxNChhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9 -LApDdzpmdW5jdGlvbiBDdyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKUGY6ZnVuY3Rpb24gUGYoKXt9 -LApaZjpmdW5jdGlvbiBaZihhLGIpe3RoaXMuYT1hCnRoaXMuJHRpPWJ9LApGZTpmdW5jdGlvbiBGZShh -LGIsYyxkLGUpe3ZhciBfPXRoaXMKXy5hPW51bGwKXy5iPWEKXy5jPWIKXy5kPWMKXy5lPWQKXy4kdGk9 -ZX0sCnZzOmZ1bmN0aW9uIHZzKGEsYil7dmFyIF89dGhpcwpfLmE9MApfLmI9YQpfLmM9bnVsbApfLiR0 -aT1ifSwKZGE6ZnVuY3Rpb24gZGEoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCm9ROmZ1bmN0aW9uIG9R -KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApwVjpmdW5jdGlvbiBwVihhKXt0aGlzLmE9YX0sClU3OmZ1 -bmN0aW9uIFU3KGEpe3RoaXMuYT1hfSwKdnI6ZnVuY3Rpb24gdnIoYSxiLGMpe3RoaXMuYT1hCnRoaXMu -Yj1iCnRoaXMuYz1jfSwKcnQ6ZnVuY3Rpb24gcnQoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCktGOmZ1 -bmN0aW9uIEtGKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApaTDpmdW5jdGlvbiBaTChhLGIsYyl7dGhp -cy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApSVDpmdW5jdGlvbiBSVChhLGIsYyl7dGhpcy5hPWEKdGhp -cy5iPWIKdGhpcy5jPWN9LApqWjpmdW5jdGlvbiBqWihhKXt0aGlzLmE9YX0sCnJxOmZ1bmN0aW9uIHJx -KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApSVzpmdW5jdGlvbiBSVyhhLGIpe3RoaXMuYT1hCnRoaXMu -Yj1ifSwKT006ZnVuY3Rpb24gT00oYSl7dGhpcy5hPWEKdGhpcy5iPW51bGx9LApxaDpmdW5jdGlvbiBx -aCgpe30sCkI1OmZ1bmN0aW9uIEI1KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LAp1TzpmdW5jdGlvbiB1 -TyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKTU86ZnVuY3Rpb24gTU8oKXt9LAprVDpmdW5jdGlvbiBr -VCgpe30sCnhJOmZ1bmN0aW9uIHhJKGEpe3RoaXMuJHRpPWF9LAptMDpmdW5jdGlvbiBtMCgpe30sCkV2 -OmZ1bmN0aW9uIEV2KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApKaTpmdW5jdGlvbiBKaSgpe30sClZw -OmZ1bmN0aW9uIFZwKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApPUjpmdW5jdGlvbiBPUihhLGIsYyl7 -dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LApMNShhLGIsYyxkKXt2YXIgcwppZihiPT1udWxsKXtp -ZihhPT1udWxsKXJldHVybiBuZXcgQS5ONShjLkMoIkA8MD4iKS5LcShkKS5DKCJONTwxLDI+IikpCnM9 -QS5sUygpfWVsc2V7aWYoYT09bnVsbClhPUEuVE4oKQpzPUEubFMoKX1yZXR1cm4gQS5FeChzLGEsYixj -LGQpfSwKRUYoYSxiLGMpe3JldHVybiBiLkMoIkA8MD4iKS5LcShjKS5DKCJGbzwxLDI+IikuYShBLkI3 -KGEsbmV3IEEuTjUoYi5DKCJAPDA+IikuS3EoYykuQygiTjU8MSwyPiIpKSkpfSwKRmwoYSxiKXtyZXR1 -cm4gbmV3IEEuTjUoYS5DKCJAPDA+IikuS3EoYikuQygiTjU8MSwyPiIpKX0sCkV4KGEsYixjLGQsZSl7 -dmFyIHM9YyE9bnVsbD9jOm5ldyBBLnY2KGQpCnJldHVybiBuZXcgQS54ZChhLGIscyxkLkMoIkA8MD4i -KS5LcShlKS5DKCJ4ZDwxLDI+IikpfSwKTHMoYSl7cmV0dXJuIG5ldyBBLkQwKGEuQygiRDA8MD4iKSl9 -LApUMigpe3ZhciBzPU9iamVjdC5jcmVhdGUobnVsbCkKc1siPG5vbi1pZGVudGlmaWVyLWtleT4iXT1z -CmRlbGV0ZSBzWyI8bm9uLWlkZW50aWZpZXIta2V5PiJdCnJldHVybiBzfSwKcmooYSxiLGMpe3ZhciBz -PW5ldyBBLmxtKGEsYixjLkMoImxtPDA+IikpCnMuYz1hLmUKcmV0dXJuIHN9LApPdShhLGIpe3JldHVy -biBKLlJNKGEsYil9LApUOShhKXtyZXR1cm4gSi5VMyhhKX0sCkVQKGEsYixjKXt2YXIgcyxyCmlmKEEu -aEIoYSkpe2lmKGI9PT0iKCImJmM9PT0iKSIpcmV0dXJuIiguLi4pIgpyZXR1cm4gYisiLi4uIitjfXM9 -QS5RSShbXSx0LnMpCkIuTm0uaSgkLnosYSkKdHJ5e0EuVnIoYSxzKX1maW5hbGx5e2lmKDA+PSQuei5s -ZW5ndGgpcmV0dXJuIEEuT0goJC56LC0xKQokLnoucG9wKCl9cj1BLmwoYix0LmsuYShzKSwiLCAiKStj -CnJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpyfSwKeChhLGIsYyl7dmFyIHMscgppZihBLmhCKGEp -KXJldHVybiBiKyIuLi4iK2MKcz1uZXcgQS5SbihiKQpCLk5tLmkoJC56LGEpCnRyeXtyPXMKci5hPUEu -bChyLmEsYSwiLCAiKX1maW5hbGx5e2lmKDA+PSQuei5sZW5ndGgpcmV0dXJuIEEuT0goJC56LC0xKQok -LnoucG9wKCl9cy5hKz1jCnI9cy5hCnJldHVybiByLmNoYXJDb2RlQXQoMCk9PTA/cjpyfSwKaEIoYSl7 -dmFyIHMscgpmb3Iocz0kLnoubGVuZ3RoLHI9MDtyPHM7KytyKWlmKGE9PT0kLnpbcl0pcmV0dXJuITAK -cmV0dXJuITF9LApWcihhLGIpe3ZhciBzLHIscSxwLG8sbixtLGw9YS5nTShhKSxrPTAsaj0wCndoaWxl -KCEwKXtpZighKGs8ODB8fGo8MykpYnJlYWsKaWYoIWwuRygpKXJldHVybgpzPUEuZChsLmdsKCkpCkIu -Tm0uaShiLHMpCmsrPXMubGVuZ3RoKzI7KytqfWlmKCFsLkcoKSl7aWYoajw9NSlyZXR1cm4KaWYoMD49 -Yi5sZW5ndGgpcmV0dXJuIEEuT0goYiwtMSkKcj1iLnBvcCgpCmlmKDA+PWIubGVuZ3RoKXJldHVybiBB -Lk9IKGIsLTEpCnE9Yi5wb3AoKX1lbHNle3A9bC5nbCgpOysragppZighbC5HKCkpe2lmKGo8PTQpe0Iu -Tm0uaShiLEEuZChwKSkKcmV0dXJufXI9QS5kKHApCmlmKDA+PWIubGVuZ3RoKXJldHVybiBBLk9IKGIs -LTEpCnE9Yi5wb3AoKQprKz1yLmxlbmd0aCsyfWVsc2V7bz1sLmdsKCk7KytqCmZvcig7bC5HKCk7cD1v -LG89bil7bj1sLmdsKCk7KytqCmlmKGo+MTAwKXt3aGlsZSghMCl7aWYoIShrPjc1JiZqPjMpKWJyZWFr -CmlmKDA+PWIubGVuZ3RoKXJldHVybiBBLk9IKGIsLTEpCmstPWIucG9wKCkubGVuZ3RoKzI7LS1qfUIu -Tm0uaShiLCIuLi4iKQpyZXR1cm59fXE9QS5kKHApCnI9QS5kKG8pCmsrPXIubGVuZ3RoK3EubGVuZ3Ro -KzR9fWlmKGo+Yi5sZW5ndGgrMil7ays9NQptPSIuLi4ifWVsc2UgbT1udWxsCndoaWxlKCEwKXtpZigh -KGs+ODAmJmIubGVuZ3RoPjMpKWJyZWFrCmlmKDA+PWIubGVuZ3RoKXJldHVybiBBLk9IKGIsLTEpCmst -PWIucG9wKCkubGVuZ3RoKzIKaWYobT09bnVsbCl7ays9NQptPSIuLi4ifX1pZihtIT1udWxsKUIuTm0u -aShiLG0pCkIuTm0uaShiLHEpCkIuTm0uaShiLHIpfSwKdE0oYSxiKXt2YXIgcyxyLHE9QS5McyhiKQpm -b3Iocz1hLmxlbmd0aCxyPTA7cjxhLmxlbmd0aDthLmxlbmd0aD09PXN8fCgwLEEubGspKGEpLCsrcilx -LmkoMCxiLmEoYVtyXSkpCnJldHVybiBxfSwKbk8oYSl7dmFyIHMscj17fQppZihBLmhCKGEpKXJldHVy -biJ7Li4ufSIKcz1uZXcgQS5SbigiIikKdHJ5e0IuTm0uaSgkLnosYSkKcy5hKz0ieyIKci5hPSEwCmEu -SygwLG5ldyBBLnJhKHIscykpCnMuYSs9In0ifWZpbmFsbHl7aWYoMD49JC56Lmxlbmd0aClyZXR1cm4g -QS5PSCgkLnosLTEpCiQuei5wb3AoKX1yPXMuYQpyZXR1cm4gci5jaGFyQ29kZUF0KDApPT0wP3I6cn0s -CnhkOmZ1bmN0aW9uIHhkKGEsYixjLGQpe3ZhciBfPXRoaXMKXy53PWEKXy54PWIKXy55PWMKXy5hPTAK -Xy5mPV8uZT1fLmQ9Xy5jPV8uYj1udWxsCl8ucj0wCl8uJHRpPWR9LAp2NjpmdW5jdGlvbiB2NihhKXt0 -aGlzLmE9YX0sCkQwOmZ1bmN0aW9uIEQwKGEpe3ZhciBfPXRoaXMKXy5hPTAKXy5mPV8uZT1fLmQ9Xy5j -PV8uYj1udWxsCl8ucj0wCl8uJHRpPWF9LApibjpmdW5jdGlvbiBibihhKXt0aGlzLmE9YQp0aGlzLmM9 -dGhpcy5iPW51bGx9LApsbTpmdW5jdGlvbiBsbShhLGIsYyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9Ygpf -LmQ9Xy5jPW51bGwKXy4kdGk9Y30sCm1XOmZ1bmN0aW9uIG1XKCl7fSwKdXk6ZnVuY3Rpb24gdXkoKXt9 -LApsRDpmdW5jdGlvbiBsRCgpe30sCmlsOmZ1bmN0aW9uIGlsKCl7fSwKcmE6ZnVuY3Rpb24gcmEoYSxi -KXt0aGlzLmE9YQp0aGlzLmI9Yn0sCllrOmZ1bmN0aW9uIFlrKCl7fSwKeVE6ZnVuY3Rpb24geVEoYSl7 -dGhpcy5hPWF9LApLUDpmdW5jdGlvbiBLUCgpe30sClBuOmZ1bmN0aW9uIFBuKCl7fSwKR2o6ZnVuY3Rp -b24gR2ooYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKbGY6ZnVuY3Rpb24gbGYoKXt9LApWajpmdW5j -dGlvbiBWaigpe30sClh2OmZ1bmN0aW9uIFh2KCl7fSwKblk6ZnVuY3Rpb24gblkoKXt9LApXWTpmdW5j -dGlvbiBXWSgpe30sClJVOmZ1bmN0aW9uIFJVKCl7fSwKRlA6ZnVuY3Rpb24gRlAoKXt9LApCUyhhLGIp -e3ZhciBzLHIscSxwPW51bGwKdHJ5e3A9SlNPTi5wYXJzZShhKX1jYXRjaChyKXtzPUEuUnUocikKcT1B -LnJyKFN0cmluZyhzKSxudWxsLG51bGwpCnRocm93IEEuYihxKX1xPUEuUWUocCkKcmV0dXJuIHF9LApR -ZShhKXt2YXIgcwppZihhPT1udWxsKXJldHVybiBudWxsCmlmKHR5cGVvZiBhIT0ib2JqZWN0IilyZXR1 -cm4gYQppZihPYmplY3QuZ2V0UHJvdG90eXBlT2YoYSkhPT1BcnJheS5wcm90b3R5cGUpcmV0dXJuIG5l -dyBBLnV3KGEsT2JqZWN0LmNyZWF0ZShudWxsKSkKZm9yKHM9MDtzPGEubGVuZ3RoOysrcylhW3NdPUEu -UWUoYVtzXSkKcmV0dXJuIGF9LApreShhLGIsYyxkKXt2YXIgcyxyCmlmKGIgaW5zdGFuY2VvZiBVaW50 -OEFycmF5KXtzPWIKZD1zLmxlbmd0aAppZihkLWM8MTUpcmV0dXJuIG51bGwKcj1BLlJQKGEscyxjLGQp -CmlmKHIhPW51bGwmJmEpaWYoci5pbmRleE9mKCJcdWZmZmQiKT49MClyZXR1cm4gbnVsbApyZXR1cm4g -cn1yZXR1cm4gbnVsbH0sClJQKGEsYixjLGQpe3ZhciBzPWE/JC5IRygpOiQucmYoKQppZihzPT1udWxs -KXJldHVybiBudWxsCmlmKDA9PT1jJiZkPT09Yi5sZW5ndGgpcmV0dXJuIEEuUmIocyxiKQpyZXR1cm4g -QS5SYihzLGIuc3ViYXJyYXkoYyxBLmpCKGMsZCxiLmxlbmd0aCkpKX0sClJiKGEsYil7dmFyIHMscgp0 -cnl7cz1hLmRlY29kZShiKQpyZXR1cm4gc31jYXRjaChyKXt9cmV0dXJuIG51bGx9LAp4TShhLGIsYyxk -LGUsZil7aWYoQi5qbi56WShmLDQpIT09MCl0aHJvdyBBLmIoQS5ycigiSW52YWxpZCBiYXNlNjQgcGFk -ZGluZywgcGFkZGVkIGxlbmd0aCBtdXN0IGJlIG11bHRpcGxlIG9mIGZvdXIsIGlzICIrZixhLGMpKQpp -ZihkK2UhPT1mKXRocm93IEEuYihBLnJyKCJJbnZhbGlkIGJhc2U2NCBwYWRkaW5nLCAnPScgbm90IGF0 -IHRoZSBlbmQiLGEsYikpCmlmKGU+Mil0aHJvdyBBLmIoQS5ycigiSW52YWxpZCBiYXNlNjQgcGFkZGlu -ZywgbW9yZSB0aGFuIHR3byAnPScgY2hhcmFjdGVycyIsYSxiKSl9LApHeShhLGIsYyl7cmV0dXJuIG5l -dyBBLlVkKGEsYil9LApOQyhhKXtyZXR1cm4gYS5MdCgpfSwKVWcoYSxiKXtyZXR1cm4gbmV3IEEudHUo -YSxbXSxBLkN5KCkpfSwKdVgoYSxiLGMpe3ZhciBzLHI9bmV3IEEuUm4oIiIpLHE9QS5VZyhyLGIpCnEu -aVUoYSkKcz1yLmEKcmV0dXJuIHMuY2hhckNvZGVBdCgwKT09MD9zOnN9LApqNChhKXtzd2l0Y2goYSl7 -Y2FzZSA2NTpyZXR1cm4iTWlzc2luZyBleHRlbnNpb24gYnl0ZSIKY2FzZSA2NzpyZXR1cm4iVW5leHBl -Y3RlZCBleHRlbnNpb24gYnl0ZSIKY2FzZSA2OTpyZXR1cm4iSW52YWxpZCBVVEYtOCBieXRlIgpjYXNl -IDcxOnJldHVybiJPdmVybG9uZyBlbmNvZGluZyIKY2FzZSA3MzpyZXR1cm4iT3V0IG9mIHVuaWNvZGUg -cmFuZ2UiCmNhc2UgNzU6cmV0dXJuIkVuY29kZWQgc3Vycm9nYXRlIgpjYXNlIDc3OnJldHVybiJVbmZp -bmlzaGVkIFVURi04IG9jdGV0IHNlcXVlbmNlIgpkZWZhdWx0OnJldHVybiIifX0sCmp5KGEsYixjKXt2 -YXIgcyxyLHEscD1jLWIsbz1uZXcgVWludDhBcnJheShwKQpmb3Iocz1KLlU2KGEpLHI9MDtyPHA7Kyty -KXtxPXMucShhLGIrcikKaWYoKHEmNDI5NDk2NzA0MCk+Pj4wIT09MClxPTI1NQppZighKHI8cCkpcmV0 -dXJuIEEuT0gobyxyKQpvW3JdPXF9cmV0dXJuIG99LAp1dzpmdW5jdGlvbiB1dyhhLGIpe3RoaXMuYT1h -CnRoaXMuYj1iCnRoaXMuYz1udWxsfSwKaTg6ZnVuY3Rpb24gaTgoYSl7dGhpcy5hPWF9LAp4cjpmdW5j -dGlvbiB4cigpe30sCk56OmZ1bmN0aW9uIE56KCl7fSwKQ1Y6ZnVuY3Rpb24gQ1YoKXt9LApVODpmdW5j -dGlvbiBVOCgpe30sClVrOmZ1bmN0aW9uIFVrKCl7fSwKd0k6ZnVuY3Rpb24gd0koKXt9LApaaTpmdW5j -dGlvbiBaaSgpe30sClVkOmZ1bmN0aW9uIFVkKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApLODpmdW5j -dGlvbiBLOChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKYnk6ZnVuY3Rpb24gYnkoKXt9LApvajpmdW5j -dGlvbiBvaihhKXt0aGlzLmI9YX0sCk14OmZ1bmN0aW9uIE14KGEpe3RoaXMuYT1hfSwKU2g6ZnVuY3Rp -b24gU2goKXt9LAp0aTpmdW5jdGlvbiB0aShhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKdHU6ZnVuY3Rp -b24gdHUoYSxiLGMpe3RoaXMuYz1hCnRoaXMuYT1iCnRoaXMuYj1jfSwKdTU6ZnVuY3Rpb24gdTUoKXt9 -LApFMzpmdW5jdGlvbiBFMygpe30sClJ3OmZ1bmN0aW9uIFJ3KGEpe3RoaXMuYj0wCnRoaXMuYz1hfSwK -R1k6ZnVuY3Rpb24gR1koYSl7dGhpcy5hPWF9LApiejpmdW5jdGlvbiBieihhKXt0aGlzLmE9YQp0aGlz -LmI9MTYKdGhpcy5jPTB9LApRQShhLGIpe3ZhciBzPUEuSHAoYSxiKQppZihzIT1udWxsKXJldHVybiBz -CnRocm93IEEuYihBLnJyKGEsbnVsbCxudWxsKSl9LApvcyhhKXtpZihhIGluc3RhbmNlb2YgQS5UcCly -ZXR1cm4gYVsiWyJdKDApCnJldHVybiJJbnN0YW5jZSBvZiAnIitBLmxoKGEpKyInIn0sCk8xKGEsYil7 -YT1BLmIoYSkKaWYoYT09bnVsbClhPXQuSy5hKGEpCmEuc3RhY2s9YlsiWyJdKDApCnRocm93IGEKdGhy -b3cgQS5iKCJ1bnJlYWNoYWJsZSIpfSwKTzgoYSxiLGMsZCl7dmFyIHMscj1jP0ouS2goYSxkKTpKLlFp -KGEsZCkKaWYoYSE9PTAmJmIhPW51bGwpZm9yKHM9MDtzPHIubGVuZ3RoOysrcylyW3NdPWIKcmV0dXJu -IHJ9LApQVyhhLGIsYyl7dmFyIHMscj1BLlFJKFtdLGMuQygiamQ8MD4iKSkKZm9yKHM9Si5JVChhKTtz -LkcoKTspQi5ObS5pKHIsYy5hKHMuZ2woKSkpCmlmKGIpcmV0dXJuIHIKcmV0dXJuIEouRXAocixjKX0s -ClkxKGEsYixjKXt2YXIgcwppZihiKXJldHVybiBBLmV2KGEsYykKcz1KLkVwKEEuZXYoYSxjKSxjKQpy -ZXR1cm4gc30sCmV2KGEsYil7dmFyIHMscgppZihBcnJheS5pc0FycmF5KGEpKXJldHVybiBBLlFJKGEu -c2xpY2UoMCksYi5DKCJqZDwwPiIpKQpzPUEuUUkoW10sYi5DKCJqZDwwPiIpKQpmb3Iocj1KLklUKGEp -O3IuRygpOylCLk5tLmkocyxyLmdsKCkpCnJldHVybiBzfSwKQUYoYSxiKXtyZXR1cm4gSi56QyhBLlBX -KGEsITEsYikpfSwKSE0oYSxiLGMpe3ZhciBzPUEuZncoYSxiLEEuakIoYixjLGEubGVuZ3RoKSkKcmV0 -dXJuIHN9LApPbyhhKXtyZXR1cm4gQS5MdyhhKX0sCm51KGEpe3JldHVybiBuZXcgQS5WUihhLEEudjQo -YSwhMSwhMCwhMSwhMSwhMSkpfSwKbChhLGIsYyl7dmFyIHM9Si5JVChiKQppZighcy5HKCkpcmV0dXJu -IGEKaWYoYy5sZW5ndGg9PT0wKXtkbyBhKz1BLmQocy5nbCgpKQp3aGlsZShzLkcoKSl9ZWxzZXthKz1B -LmQocy5nbCgpKQpmb3IoO3MuRygpOylhPWErYytBLmQocy5nbCgpKX1yZXR1cm4gYX0sClpSKGEsYixj -LGQsZSl7cmV0dXJuIG5ldyBBLm1wKGEsYixjLGQsZSl9LAp1bygpe3ZhciBzPUEuTTAoKQppZihzIT1u -dWxsKXJldHVybiBBLmhLKHMpCnRocm93IEEuYihBLkw0KCInVXJpLmJhc2UnIGlzIG5vdCBzdXBwb3J0 -ZWQiKSl9LAplUChhLGIsYyxkKXt2YXIgcyxyLHEscCxvLG4sbT0iMDEyMzQ1Njc4OUFCQ0RFRiIKaWYo -Yz09PUIueE0pe3M9JC56NCgpLmIKcz1zLnRlc3QoYil9ZWxzZSBzPSExCmlmKHMpcmV0dXJuIGIKQS5M -aChjKS5DKCJVay5TIikuYShiKQpyPWMuZ1pFKCkuV0ooYikKZm9yKHM9ci5sZW5ndGgscT0wLHA9IiI7 -cTxzOysrcSl7bz1yW3FdCmlmKG88MTI4KXtuPW8+Pj40CmlmKCEobjw4KSlyZXR1cm4gQS5PSChhLG4p -Cm49KGFbbl0mMTw8KG8mMTUpKSE9PTB9ZWxzZSBuPSExCmlmKG4pcCs9QS5MdyhvKQplbHNlIHA9ZCYm -bz09PTMyP3ArIisiOnArIiUiK21bbz4+PjQmMTVdK21bbyYxNV19cmV0dXJuIHAuY2hhckNvZGVBdCgw -KT09MD9wOnB9LApHcShhKXt2YXIgcz1NYXRoLmFicyhhKSxyPWE8MD8iLSI6IiIKaWYocz49MTAwMCly -ZXR1cm4iIithCmlmKHM+PTEwMClyZXR1cm4gcisiMCIrcwppZihzPj0xMClyZXR1cm4gcisiMDAiK3MK -cmV0dXJuIHIrIjAwMCIrc30sClZ4KGEpe2lmKGE+PTEwMClyZXR1cm4iIithCmlmKGE+PTEwKXJldHVy -biIwIithCnJldHVybiIwMCIrYX0sCmgwKGEpe2lmKGE+PTEwKXJldHVybiIiK2EKcmV0dXJuIjAiK2F9 -LApobChhKXtpZih0eXBlb2YgYT09Im51bWJlciJ8fEEuclEoYSl8fGE9PW51bGwpcmV0dXJuIEouWVMo -YSkKaWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBKU09OLnN0cmluZ2lmeShhKQpyZXR1cm4gQS5v -cyhhKX0sCmhWKGEpe3JldHVybiBuZXcgQS5DNihhKX0sCnhZKGEsYil7cmV0dXJuIG5ldyBBLnUoITEs -bnVsbCxiLGEpfSwKTDMoYSxiLGMpe3JldHVybiBuZXcgQS51KCEwLGEsYixjKX0sCk1SKGEsYixjKXty -ZXR1cm4gYX0sCk83KGEsYil7cmV0dXJuIG5ldyBBLmJKKG51bGwsbnVsbCwhMCxhLGIsIlZhbHVlIG5v -dCBpbiByYW5nZSIpfSwKVEUoYSxiLGMsZCxlKXtyZXR1cm4gbmV3IEEuYkooYixjLCEwLGEsZCwiSW52 -YWxpZCB2YWx1ZSIpfSwKd0EoYSxiLGMsZCl7aWYoYTxifHxhPmMpdGhyb3cgQS5iKEEuVEUoYSxiLGMs -ZCxudWxsKSkKcmV0dXJuIGF9LApqQihhLGIsYyl7aWYoMD5hfHxhPmMpdGhyb3cgQS5iKEEuVEUoYSww -LGMsInN0YXJ0IixudWxsKSkKaWYoYiE9bnVsbCl7aWYoYT5ifHxiPmMpdGhyb3cgQS5iKEEuVEUoYixh -LGMsImVuZCIsbnVsbCkpCnJldHVybiBifXJldHVybiBjfSwKazEoYSxiKXtpZihhPDApdGhyb3cgQS5i -KEEuVEUoYSwwLG51bGwsYixudWxsKSkKcmV0dXJuIGF9LAp4RihhLGIsYyxkKXtyZXR1cm4gbmV3IEEu -ZVkoYiwhMCxhLGQsIkluZGV4IG91dCBvZiByYW5nZSIpfSwKTDQoYSl7cmV0dXJuIG5ldyBBLnViKGEp -fSwKU1koYSl7cmV0dXJuIG5ldyBBLmRzKGEpfSwKUFYoYSl7cmV0dXJuIG5ldyBBLmxqKGEpfSwKYTQo -YSl7cmV0dXJuIG5ldyBBLlVWKGEpfSwKcnIoYSxiLGMpe3JldHVybiBuZXcgQS5hRShhLGIsYyl9LApm -NShhLGIsYyxkKXt2YXIgcyxyPUIuQ0QuZ20oYSkKYj1CLkNELmdtKGIpCmM9Qi5DRC5nbShjKQpkPUIu -Q0QuZ20oZCkKcz0kLnQ4KCkKcmV0dXJuIEEucUwoQS55YyhBLnljKEEueWMoQS55YyhzLHIpLGIpLGMp -LGQpKX0sCmhLKGE1KXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxhMCxh -MSxhMixhMz1udWxsLGE0PWE1Lmxlbmd0aAppZihhND49NSl7cz0oKEIueEIuVyhhNSw0KV41OCkqM3xC -LnhCLlcoYTUsMCleMTAwfEIueEIuVyhhNSwxKV45N3xCLnhCLlcoYTUsMileMTE2fEIueEIuVyhhNSwz -KV45Nyk+Pj4wCmlmKHM9PT0wKXJldHVybiBBLktEKGE0PGE0P0IueEIuTmooYTUsMCxhNCk6YTUsNSxh -MykuZ2xSKCkKZWxzZSBpZihzPT09MzIpcmV0dXJuIEEuS0QoQi54Qi5OaihhNSw1LGE0KSwwLGEzKS5n -bFIoKX1yPUEuTzgoOCwwLCExLHQuUykKQi5ObS5ZNShyLDAsMCkKQi5ObS5ZNShyLDEsLTEpCkIuTm0u -WTUociwyLC0xKQpCLk5tLlk1KHIsNywtMSkKQi5ObS5ZNShyLDMsMCkKQi5ObS5ZNShyLDQsMCkKQi5O -bS5ZNShyLDUsYTQpCkIuTm0uWTUociw2LGE0KQppZihBLlVCKGE1LDAsYTQsMCxyKT49MTQpQi5ObS5Z -NShyLDcsYTQpCnE9clsxXQppZihxPj0wKWlmKEEuVUIoYTUsMCxxLDIwLHIpPT09MjApcls3XT1xCnA9 -clsyXSsxCm89clszXQpuPXJbNF0KbT1yWzVdCmw9cls2XQppZihsPG0pbT1sCmlmKG48cCluPW0KZWxz -ZSBpZihuPD1xKW49cSsxCmlmKG88cClvPW4Kaz1yWzddPDAKaWYoaylpZihwPnErMyl7aj1hMwprPSEx -fWVsc2V7aT1vPjAKaWYoaSYmbysxPT09bil7aj1hMwprPSExfWVsc2V7aWYoIUIueEIuUWkoYTUsIlxc -IixuKSlpZihwPjApaD1CLnhCLlFpKGE1LCJcXCIscC0xKXx8Qi54Qi5RaShhNSwiXFwiLHAtMikKZWxz -ZSBoPSExCmVsc2UgaD0hMAppZihoKXtqPWEzCms9ITF9ZWxzZXtpZighKG08YTQmJm09PT1uKzImJkIu -eEIuUWkoYTUsIi4uIixuKSkpaD1tPm4rMiYmQi54Qi5RaShhNSwiLy4uIixtLTMpCmVsc2UgaD0hMApp -ZihoKXtqPWEzCms9ITF9ZWxzZXtpZihxPT09NClpZihCLnhCLlFpKGE1LCJmaWxlIiwwKSl7aWYocDw9 -MCl7aWYoIUIueEIuUWkoYTUsIi8iLG4pKXtnPSJmaWxlOi8vLyIKcz0zfWVsc2V7Zz0iZmlsZTovLyIK -cz0yfWE1PWcrQi54Qi5OaihhNSxuLGE0KQpxLT0wCmk9cy0wCm0rPWkKbCs9aQphND1hNS5sZW5ndGgK -cD03Cm89NwpuPTd9ZWxzZSBpZihuPT09bSl7KytsCmY9bSsxCmE1PUIueEIuaTcoYTUsbixtLCIvIik7 -KythNAptPWZ9aj0iZmlsZSJ9ZWxzZSBpZihCLnhCLlFpKGE1LCJodHRwIiwwKSl7aWYoaSYmbyszPT09 -biYmQi54Qi5RaShhNSwiODAiLG8rMSkpe2wtPTMKZT1uLTMKbS09MwphNT1CLnhCLmk3KGE1LG8sbiwi -IikKYTQtPTMKbj1lfWo9Imh0dHAifWVsc2Ugaj1hMwplbHNlIGlmKHE9PT01JiZCLnhCLlFpKGE1LCJo -dHRwcyIsMCkpe2lmKGkmJm8rND09PW4mJkIueEIuUWkoYTUsIjQ0MyIsbysxKSl7bC09NAplPW4tNApt -LT00CmE1PUIueEIuaTcoYTUsbyxuLCIiKQphNC09MwpuPWV9aj0iaHR0cHMifWVsc2Ugaj1hMwprPSEw -fX19fWVsc2Ugaj1hMwppZihrKXtpZihhNDxhNS5sZW5ndGgpe2E1PUIueEIuTmooYTUsMCxhNCkKcS09 -MApwLT0wCm8tPTAKbi09MAptLT0wCmwtPTB9cmV0dXJuIG5ldyBBLlVmKGE1LHEscCxvLG4sbSxsLGop -fWlmKGo9PW51bGwpaWYocT4wKWo9QS5QaShhNSwwLHEpCmVsc2V7aWYocT09PTApQS5SMyhhNSwwLCJJ -bnZhbGlkIGVtcHR5IHNjaGVtZSIpCmo9IiJ9aWYocD4wKXtkPXErMwpjPWQ8cD9BLnpSKGE1LGQscC0x -KToiIgpiPUEuT2UoYTUscCxvLCExKQppPW8rMQppZihpPG4pe2E9QS5IcChCLnhCLk5qKGE1LGksbiks -YTMpCmEwPUEud0IoYT09bnVsbD9BLnYoQS5ycigiSW52YWxpZCBwb3J0IixhNSxpKSk6YSxqKX1lbHNl -IGEwPWEzfWVsc2V7YTA9YTMKYj1hMApjPSIifWExPUEua2EoYTUsbixtLGEzLGosYiE9bnVsbCkKYTI9 -bTxsP0EubGUoYTUsbSsxLGwsYTMpOmEzCnJldHVybiBBLkNnKGosYyxiLGEwLGExLGEyLGw8YTQ/QS50 -RyhhNSxsKzEsYTQpOmEzKX0sCk10KGEpe0EubihhKQpyZXR1cm4gQS5rdShhLDAsYS5sZW5ndGgsQi54 -TSwhMSl9LApXWChhKXt2YXIgcz10Lk4KcmV0dXJuIEIuTm0uTjAoQS5RSShhLnNwbGl0KCImIiksdC5z -KSxBLkZsKHMscyksbmV3IEEubjEoQi54TSksdC5JKX0sCkhoKGEsYixjKXt2YXIgcyxyLHEscCxvLG4s -bT0iSVB2NCBhZGRyZXNzIHNob3VsZCBjb250YWluIGV4YWN0bHkgNCBwYXJ0cyIsbD0iZWFjaCBwYXJ0 -IG11c3QgYmUgaW4gdGhlIHJhbmdlIDAuLjI1NSIsaz1uZXcgQS5jUyhhKSxqPW5ldyBVaW50OEFycmF5 -KDQpCmZvcihzPWIscj1zLHE9MDtzPGM7KytzKXtwPUIueEIuTyhhLHMpCmlmKHAhPT00Nil7aWYoKHBe -NDgpPjkpay4kMigiaW52YWxpZCBjaGFyYWN0ZXIiLHMpfWVsc2V7aWYocT09PTMpay4kMihtLHMpCm89 -QS5RQShCLnhCLk5qKGEscixzKSxudWxsKQppZihvPjI1NSlrLiQyKGwscikKbj1xKzEKaWYoIShxPDQp -KXJldHVybiBBLk9IKGoscSkKaltxXT1vCnI9cysxCnE9bn19aWYocSE9PTMpay4kMihtLGMpCm89QS5R -QShCLnhCLk5qKGEscixjKSxudWxsKQppZihvPjI1NSlrLiQyKGwscikKaWYoIShxPDQpKXJldHVybiBB -Lk9IKGoscSkKaltxXT1vCnJldHVybiBqfSwKZWcoYSxhMCxhMSl7dmFyIHMscixxLHAsbyxuLG0sbCxr -LGosaSxoLGcsZixlLGQ9bnVsbCxjPW5ldyBBLlZDKGEpLGI9bmV3IEEuSlQoYyxhKQppZihhLmxlbmd0 -aDwyKWMuJDIoImFkZHJlc3MgaXMgdG9vIHNob3J0IixkKQpzPUEuUUkoW10sdC50KQpmb3Iocj1hMCxx -PXIscD0hMSxvPSExO3I8YTE7KytyKXtuPUIueEIuTyhhLHIpCmlmKG49PT01OCl7aWYocj09PWEwKXsr -K3IKaWYoQi54Qi5PKGEscikhPT01OCljLiQyKCJpbnZhbGlkIHN0YXJ0IGNvbG9uLiIscikKcT1yfWlm -KHI9PT1xKXtpZihwKWMuJDIoIm9ubHkgb25lIHdpbGRjYXJkIGA6OmAgaXMgYWxsb3dlZCIscikKQi5O -bS5pKHMsLTEpCnA9ITB9ZWxzZSBCLk5tLmkocyxiLiQyKHEscikpCnE9cisxfWVsc2UgaWYobj09PTQ2 -KW89ITB9aWYocy5sZW5ndGg9PT0wKWMuJDIoInRvbyBmZXcgcGFydHMiLGQpCm09cT09PWExCmw9Qi5O -bS5ncloocykKaWYobSYmbCE9PS0xKWMuJDIoImV4cGVjdGVkIGEgcGFydCBhZnRlciBsYXN0IGA6YCIs -YTEpCmlmKCFtKWlmKCFvKUIuTm0uaShzLGIuJDIocSxhMSkpCmVsc2V7az1BLkhoKGEscSxhMSkKQi5O -bS5pKHMsKGtbMF08PDh8a1sxXSk+Pj4wKQpCLk5tLmkocywoa1syXTw8OHxrWzNdKT4+PjApfWlmKHAp -e2lmKHMubGVuZ3RoPjcpYy4kMigiYW4gYWRkcmVzcyB3aXRoIGEgd2lsZGNhcmQgbXVzdCBoYXZlIGxl -c3MgdGhhbiA3IHBhcnRzIixkKX1lbHNlIGlmKHMubGVuZ3RoIT09OCljLiQyKCJhbiBhZGRyZXNzIHdp -dGhvdXQgYSB3aWxkY2FyZCBtdXN0IGNvbnRhaW4gZXhhY3RseSA4IHBhcnRzIixkKQpqPW5ldyBVaW50 -OEFycmF5KDE2KQpmb3IobD1zLmxlbmd0aCxpPTktbCxyPTAsaD0wO3I8bDsrK3Ipe2c9c1tyXQppZihn -PT09LTEpZm9yKGY9MDtmPGk7KytmKXtpZighKGg+PTAmJmg8MTYpKXJldHVybiBBLk9IKGosaCkKalto -XT0wCmU9aCsxCmlmKCEoZTwxNikpcmV0dXJuIEEuT0goaixlKQpqW2VdPTAKaCs9Mn1lbHNle2U9Qi5q -bi53RyhnLDgpCmlmKCEoaD49MCYmaDwxNikpcmV0dXJuIEEuT0goaixoKQpqW2hdPWUKZT1oKzEKaWYo -IShlPDE2KSlyZXR1cm4gQS5PSChqLGUpCmpbZV09ZyYyNTUKaCs9Mn19cmV0dXJuIGp9LApDZyhhLGIs -YyxkLGUsZixnKXtyZXR1cm4gbmV3IEEuRG4oYSxiLGMsZCxlLGYsZyl9LApLTChhLGIsYyxkLGUsZixn -KXt2YXIgcyxyLHEscCxvLG4KZj1mPT1udWxsPyIiOkEuUGkoZiwwLGYubGVuZ3RoKQpnPUEuelIoZyww -LGc9PW51bGw/MDpnLmxlbmd0aCkKYT1BLk9lKGEsMCxhPT1udWxsPzA6YS5sZW5ndGgsITEpCnM9QS5s -ZShudWxsLDAsMCxlKQpyPUEudEcobnVsbCwwLDApCmQ9QS53QihkLGYpCnE9Zj09PSJmaWxlIgppZihh -PT1udWxsKXA9Zy5sZW5ndGghPT0wfHxkIT1udWxsfHxxCmVsc2UgcD0hMQppZihwKWE9IiIKcD1hPT1u -dWxsCm89IXAKYj1BLmthKGIsMCxiPT1udWxsPzA6Yi5sZW5ndGgsYyxmLG8pCm49Zi5sZW5ndGg9PT0w -CmlmKG4mJnAmJiFCLnhCLm5DKGIsIi8iKSliPUEud0YoYiwhbnx8bykKZWxzZSBiPUEueGUoYikKcmV0 -dXJuIEEuQ2coZixnLHAmJkIueEIubkMoYiwiLy8iKT8iIjphLGQsYixzLHIpfSwKd0soYSl7aWYoYT09 -PSJodHRwIilyZXR1cm4gODAKaWYoYT09PSJodHRwcyIpcmV0dXJuIDQ0MwpyZXR1cm4gMH0sClIzKGEs -YixjKXt0aHJvdyBBLmIoQS5ycihjLGEsYikpfSwKWGQoYSxiLGMsZCl7dmFyIHMscixxLHAsbyxuLG0s -bCxrLGosaSxoPW51bGwsZz1iLmxlbmd0aAppZihnIT09MCl7cT0wCndoaWxlKCEwKXtpZighKHE8Zykp -e3M9IiIKcj0wCmJyZWFrfWlmKEIueEIuVyhiLHEpPT09NjQpe3M9Qi54Qi5OaihiLDAscSkKcj1xKzEK -YnJlYWt9KytxfWlmKHI8ZyYmQi54Qi5XKGIscik9PT05MSl7Zm9yKHA9cixvPS0xO3A8ZzsrK3Ape249 -Qi54Qi5XKGIscCkKaWYobj09PTM3JiZvPDApe209Qi54Qi5RaShiLCIyNSIscCsxKT9wKzI6cApvPXAK -cD1tfWVsc2UgaWYobj09PTkzKWJyZWFrfWlmKHA9PT1nKXRocm93IEEuYihBLnJyKCJJbnZhbGlkIElQ -djYgaG9zdCBlbnRyeS4iLGIscikpCmw9bzwwP3A6bwpBLmVnKGIscisxLGwpOysrcAppZihwIT09ZyYm -Qi54Qi5XKGIscCkhPT01OCl0aHJvdyBBLmIoQS5ycigiSW52YWxpZCBlbmQgb2YgYXV0aG9yaXR5Iixi -LHApKX1lbHNlIHA9cgp3aGlsZSghMCl7aWYoIShwPGcpKXtrPWgKYnJlYWt9aWYoQi54Qi5XKGIscCk9 -PT01OCl7aj1CLnhCLnluKGIscCsxKQprPWoubGVuZ3RoIT09MD9BLlFBKGosaCk6aApicmVha30rK3B9 -aT1CLnhCLk5qKGIscixwKX1lbHNle2s9aAppPWsKcz0iIn1yZXR1cm4gQS5LTChpLGgsQS5RSShjLnNw -bGl0KCIvIiksdC5zKSxrLGQsYSxzKX0sCmtFKGEsYil7dmFyIHMscixxLHAsbwpmb3Iocz1hLmxlbmd0 -aCxyPTA7cjxzOysrcil7cT1hW3JdCnA9Si5VNihxKQpvPXAuZ2socSkKaWYoMD5vKUEudihBLlRFKDAs -MCxwLmdrKHEpLG51bGwsbnVsbCkpCmlmKEEuU1EocSwiLyIsMCkpe3M9QS5MNCgiSWxsZWdhbCBwYXRo -IGNoYXJhY3RlciAiK0EuZChxKSkKdGhyb3cgQS5iKHMpfX19LApITihhLGIsYyl7dmFyIHMscixxLHAs -bwpmb3Iocz1BLnFDKGEsYyxudWxsLEEudDYoYSkuYykscj1zLiR0aSxzPW5ldyBBLmE3KHMscy5nayhz -KSxyLkMoImE3PGFMLkU+IikpLHI9ci5DKCJhTC5FIik7cy5HKCk7KXtxPXMuZAppZihxPT1udWxsKXE9 -ci5hKHEpCnA9QS5udSgnWyIqLzo8Pj9cXFxcfF0nKQpvPXEubGVuZ3RoCmlmKEEuU1EocSxwLDApKXtz -PUEuTDQoIklsbGVnYWwgY2hhcmFjdGVyIGluIHBhdGg6ICIrcSkKdGhyb3cgQS5iKHMpfX19LApyZyhh -LGIpe3ZhciBzCmlmKCEoNjU8PWEmJmE8PTkwKSlzPTk3PD1hJiZhPD0xMjIKZWxzZSBzPSEwCmlmKHMp -cmV0dXJuCnM9QS5MNCgiSWxsZWdhbCBkcml2ZSBsZXR0ZXIgIitBLk9vKGEpKQp0aHJvdyBBLmIocyl9 -LAp3QihhLGIpe2lmKGEhPW51bGwmJmE9PT1BLndLKGIpKXJldHVybiBudWxsCnJldHVybiBhfSwKT2Uo -YSxiLGMsZCl7dmFyIHMscixxLHAsbyxuCmlmKGE9PW51bGwpcmV0dXJuIG51bGwKaWYoYj09PWMpcmV0 -dXJuIiIKaWYoQi54Qi5PKGEsYik9PT05MSl7cz1jLTEKaWYoQi54Qi5PKGEscykhPT05MylBLlIzKGEs -YiwiTWlzc2luZyBlbmQgYF1gIHRvIG1hdGNoIGBbYCBpbiBob3N0IikKcj1iKzEKcT1BLnRvKGEscixz -KQppZihxPHMpe3A9cSsxCm89QS5PQShhLEIueEIuUWkoYSwiMjUiLHApP3ErMzpwLHMsIiUyNSIpfWVs -c2Ugbz0iIgpBLmVnKGEscixxKQpyZXR1cm4gQi54Qi5OaihhLGIscSkudG9Mb3dlckNhc2UoKStvKyJd -In1mb3Iobj1iO248YzsrK24paWYoQi54Qi5PKGEsbik9PT01OCl7cT1CLnhCLlhVKGEsIiUiLGIpCnE9 -cT49YiYmcTxjP3E6YwppZihxPGMpe3A9cSsxCm89QS5PQShhLEIueEIuUWkoYSwiMjUiLHApP3ErMzpw -LGMsIiUyNSIpfWVsc2Ugbz0iIgpBLmVnKGEsYixxKQpyZXR1cm4iWyIrQi54Qi5OaihhLGIscSkrbysi -XSJ9cmV0dXJuIEEuT0woYSxiLGMpfSwKdG8oYSxiLGMpe3ZhciBzPUIueEIuWFUoYSwiJSIsYikKcmV0 -dXJuIHM+PWImJnM8Yz9zOmN9LApPQShhLGIsYyxkKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpPWQh -PT0iIj9uZXcgQS5SbihkKTpudWxsCmZvcihzPWIscj1zLHE9ITA7czxjOyl7cD1CLnhCLk8oYSxzKQpp -ZihwPT09Mzcpe289QS5ydihhLHMsITApCm49bz09bnVsbAppZihuJiZxKXtzKz0zCmNvbnRpbnVlfWlm -KGk9PW51bGwpaT1uZXcgQS5SbigiIikKbT1pLmErPUIueEIuTmooYSxyLHMpCmlmKG4pbz1CLnhCLk5q -KGEscyxzKzMpCmVsc2UgaWYobz09PSIlIilBLlIzKGEscywiWm9uZUlEIHNob3VsZCBub3QgY29udGFp -biAlIGFueW1vcmUiKQppLmE9bStvCnMrPTMKcj1zCnE9ITB9ZWxzZXtpZihwPDEyNyl7bj1wPj4+NApp -ZighKG48OCkpcmV0dXJuIEEuT0goQi5GMyxuKQpuPShCLkYzW25dJjE8PChwJjE1KSkhPT0wfWVsc2Ug -bj0hMQppZihuKXtpZihxJiY2NTw9cCYmOTA+PXApe2lmKGk9PW51bGwpaT1uZXcgQS5SbigiIikKaWYo -cjxzKXtpLmErPUIueEIuTmooYSxyLHMpCnI9c31xPSExfSsrc31lbHNle2lmKChwJjY0NTEyKT09PTU1 -Mjk2JiZzKzE8Yyl7bD1CLnhCLk8oYSxzKzEpCmlmKChsJjY0NTEyKT09PTU2MzIwKXtwPShwJjEwMjMp -PDwxMHxsJjEwMjN8NjU1MzYKaz0yfWVsc2Ugaz0xfWVsc2Ugaz0xCmo9Qi54Qi5OaihhLHIscykKaWYo -aT09bnVsbCl7aT1uZXcgQS5SbigiIikKbj1pfWVsc2Ugbj1pCm4uYSs9agpuLmErPUEuelgocCkKcys9 -awpyPXN9fX1pZihpPT1udWxsKXJldHVybiBCLnhCLk5qKGEsYixjKQppZihyPGMpaS5hKz1CLnhCLk5q -KGEscixjKQpuPWkuYQpyZXR1cm4gbi5jaGFyQ29kZUF0KDApPT0wP246bn0sCk9MKGEsYixjKXt2YXIg -cyxyLHEscCxvLG4sbSxsLGssaixpCmZvcihzPWIscj1zLHE9bnVsbCxwPSEwO3M8Yzspe289Qi54Qi5P -KGEscykKaWYobz09PTM3KXtuPUEucnYoYSxzLCEwKQptPW49PW51bGwKaWYobSYmcCl7cys9Mwpjb250 -aW51ZX1pZihxPT1udWxsKXE9bmV3IEEuUm4oIiIpCmw9Qi54Qi5OaihhLHIscykKaz1xLmErPSFwP2wu -dG9Mb3dlckNhc2UoKTpsCmlmKG0pe249Qi54Qi5OaihhLHMscyszKQpqPTN9ZWxzZSBpZihuPT09IiUi -KXtuPSIlMjUiCmo9MX1lbHNlIGo9MwpxLmE9aytuCnMrPWoKcj1zCnA9ITB9ZWxzZXtpZihvPDEyNyl7 -bT1vPj4+NAppZighKG08OCkpcmV0dXJuIEEuT0goQi5lYSxtKQptPShCLmVhW21dJjE8PChvJjE1KSkh -PT0wfWVsc2UgbT0hMQppZihtKXtpZihwJiY2NTw9byYmOTA+PW8pe2lmKHE9PW51bGwpcT1uZXcgQS5S -bigiIikKaWYocjxzKXtxLmErPUIueEIuTmooYSxyLHMpCnI9c31wPSExfSsrc31lbHNle2lmKG88PTkz -KXttPW8+Pj40CmlmKCEobTw4KSlyZXR1cm4gQS5PSChCLmFrLG0pCm09KEIuYWtbbV0mMTw8KG8mMTUp -KSE9PTB9ZWxzZSBtPSExCmlmKG0pQS5SMyhhLHMsIkludmFsaWQgY2hhcmFjdGVyIikKZWxzZXtpZigo -byY2NDUxMik9PT01NTI5NiYmcysxPGMpe2k9Qi54Qi5PKGEscysxKQppZigoaSY2NDUxMik9PT01NjMy -MCl7bz0obyYxMDIzKTw8MTB8aSYxMDIzfDY1NTM2Cmo9Mn1lbHNlIGo9MX1lbHNlIGo9MQpsPUIueEIu -TmooYSxyLHMpCmlmKCFwKWw9bC50b0xvd2VyQ2FzZSgpCmlmKHE9PW51bGwpe3E9bmV3IEEuUm4oIiIp -Cm09cX1lbHNlIG09cQptLmErPWwKbS5hKz1BLnpYKG8pCnMrPWoKcj1zfX19fWlmKHE9PW51bGwpcmV0 -dXJuIEIueEIuTmooYSxiLGMpCmlmKHI8Yyl7bD1CLnhCLk5qKGEscixjKQpxLmErPSFwP2wudG9Mb3dl -ckNhc2UoKTpsfW09cS5hCnJldHVybiBtLmNoYXJDb2RlQXQoMCk9PTA/bTptfSwKUGkoYSxiLGMpe3Zh -ciBzLHIscSxwCmlmKGI9PT1jKXJldHVybiIiCmlmKCFBLkV0KEIueEIuVyhhLGIpKSlBLlIzKGEsYiwi -U2NoZW1lIG5vdCBzdGFydGluZyB3aXRoIGFscGhhYmV0aWMgY2hhcmFjdGVyIikKZm9yKHM9YixyPSEx -O3M8YzsrK3Mpe3E9Qi54Qi5XKGEscykKaWYocTwxMjgpe3A9cT4+PjQKaWYoIShwPDgpKXJldHVybiBB -Lk9IKEIubUsscCkKcD0oQi5tS1twXSYxPDwocSYxNSkpIT09MH1lbHNlIHA9ITEKaWYoIXApQS5SMyhh -LHMsIklsbGVnYWwgc2NoZW1lIGNoYXJhY3RlciIpCmlmKDY1PD1xJiZxPD05MClyPSEwfWE9Qi54Qi5O -aihhLGIsYykKcmV0dXJuIEEuWWEocj9hLnRvTG93ZXJDYXNlKCk6YSl9LApZYShhKXtpZihhPT09Imh0 -dHAiKXJldHVybiJodHRwIgppZihhPT09ImZpbGUiKXJldHVybiJmaWxlIgppZihhPT09Imh0dHBzIily -ZXR1cm4iaHR0cHMiCmlmKGE9PT0icGFja2FnZSIpcmV0dXJuInBhY2thZ2UiCnJldHVybiBhfSwKelIo -YSxiLGMpe2lmKGE9PW51bGwpcmV0dXJuIiIKcmV0dXJuIEEuUEkoYSxiLGMsQi50bywhMSwhMSl9LApr -YShhLGIsYyxkLGUsZil7dmFyIHMscixxPWU9PT0iZmlsZSIscD1xfHxmCmlmKGE9PW51bGwpe2lmKGQ9 -PW51bGwpcmV0dXJuIHE/Ii8iOiIiCnM9QS50NihkKQpyPW5ldyBBLmxKKGQscy5DKCJxVSgxKSIpLmEo -bmV3IEEuUlooKSkscy5DKCJsSjwxLHFVPiIpKS5IKDAsIi8iKX1lbHNlIGlmKGQhPW51bGwpdGhyb3cg -QS5iKEEueFkoIkJvdGggcGF0aCBhbmQgcGF0aFNlZ21lbnRzIHNwZWNpZmllZCIsbnVsbCkpCmVsc2Ug -cj1BLlBJKGEsYixjLEIuV2QsITAsITApCmlmKHIubGVuZ3RoPT09MCl7aWYocSlyZXR1cm4iLyJ9ZWxz -ZSBpZihwJiYhQi54Qi5uQyhyLCIvIikpcj0iLyIrcgpyZXR1cm4gQS5KcihyLGUsZil9LApKcihhLGIs -Yyl7dmFyIHM9Yi5sZW5ndGg9PT0wCmlmKHMmJiFjJiYhQi54Qi5uQyhhLCIvIikmJiFCLnhCLm5DKGEs -IlxcIikpcmV0dXJuIEEud0YoYSwhc3x8YykKcmV0dXJuIEEueGUoYSl9LApsZShhLGIsYyxkKXt2YXIg -cyxyPXt9CmlmKGEhPW51bGwpe2lmKGQhPW51bGwpdGhyb3cgQS5iKEEueFkoIkJvdGggcXVlcnkgYW5k -IHF1ZXJ5UGFyYW1ldGVycyBzcGVjaWZpZWQiLG51bGwpKQpyZXR1cm4gQS5QSShhLGIsYyxCLlZDLCEw -LCExKX1pZihkPT1udWxsKXJldHVybiBudWxsCnM9bmV3IEEuUm4oIiIpCnIuYT0iIgpkLksoMCxuZXcg -QS55NShuZXcgQS5NRShyLHMpKSkKcj1zLmEKcmV0dXJuIHIuY2hhckNvZGVBdCgwKT09MD9yOnJ9LAp0 -RyhhLGIsYyl7aWYoYT09bnVsbClyZXR1cm4gbnVsbApyZXR1cm4gQS5QSShhLGIsYyxCLlZDLCEwLCEx -KX0sCnJ2KGEsYixjKXt2YXIgcyxyLHEscCxvLG49YisyCmlmKG4+PWEubGVuZ3RoKXJldHVybiIlIgpz -PUIueEIuTyhhLGIrMSkKcj1CLnhCLk8oYSxuKQpxPUEub28ocykKcD1BLm9vKHIpCmlmKHE8MHx8cDww -KXJldHVybiIlIgpvPXEqMTYrcAppZihvPDEyNyl7bj1CLmpuLndHKG8sNCkKaWYoIShuPDgpKXJldHVy -biBBLk9IKEIuRjMsbikKbj0oQi5GM1tuXSYxPDwobyYxNSkpIT09MH1lbHNlIG49ITEKaWYobilyZXR1 -cm4gQS5MdyhjJiY2NTw9byYmOTA+PW8/KG98MzIpPj4+MDpvKQppZihzPj05N3x8cj49OTcpcmV0dXJu -IEIueEIuTmooYSxiLGIrMykudG9VcHBlckNhc2UoKQpyZXR1cm4gbnVsbH0sCnpYKGEpe3ZhciBzLHIs -cSxwLG8sbixtLGwsaz0iMDEyMzQ1Njc4OUFCQ0RFRiIKaWYoYTwxMjgpe3M9bmV3IFVpbnQ4QXJyYXko -MykKc1swXT0zNwpzWzFdPUIueEIuVyhrLGE+Pj40KQpzWzJdPUIueEIuVyhrLGEmMTUpfWVsc2V7aWYo -YT4yMDQ3KWlmKGE+NjU1MzUpe3I9MjQwCnE9NH1lbHNle3I9MjI0CnE9M31lbHNle3I9MTkyCnE9Mn1w -PTMqcQpzPW5ldyBVaW50OEFycmF5KHApCmZvcihvPTA7LS1xLHE+PTA7cj0xMjgpe249Qi5qbi5iZihh -LDYqcSkmNjN8cgppZighKG88cCkpcmV0dXJuIEEuT0gocyxvKQpzW29dPTM3Cm09bysxCmw9Qi54Qi5X -KGssbj4+PjQpCmlmKCEobTxwKSlyZXR1cm4gQS5PSChzLG0pCnNbbV09bApsPW8rMgptPUIueEIuVyhr -LG4mMTUpCmlmKCEobDxwKSlyZXR1cm4gQS5PSChzLGwpCnNbbF09bQpvKz0zfX1yZXR1cm4gQS5ITShz -LDAsbnVsbCl9LApQSShhLGIsYyxkLGUsZil7dmFyIHM9QS5VbChhLGIsYyxkLGUsZikKcmV0dXJuIHM9 -PW51bGw/Qi54Qi5OaihhLGIsYyk6c30sClVsKGEsYixjLGQsZSxmKXt2YXIgcyxyLHEscCxvLG4sbSxs -LGssaixpPW51bGwKZm9yKHM9IWUscj1iLHE9cixwPWk7cjxjOyl7bz1CLnhCLk8oYSxyKQppZihvPDEy -Nyl7bj1vPj4+NAppZighKG48OCkpcmV0dXJuIEEuT0goZCxuKQpuPShkW25dJjE8PChvJjE1KSkhPT0w -fWVsc2Ugbj0hMQppZihuKSsrcgplbHNle2lmKG89PT0zNyl7bT1BLnJ2KGEsciwhMSkKaWYobT09bnVs -bCl7cis9Mwpjb250aW51ZX1pZigiJSI9PT1tKXttPSIlMjUiCmw9MX1lbHNlIGw9M31lbHNlIGlmKG89 -PT05MiYmZil7bT0iLyIKbD0xfWVsc2V7aWYocylpZihvPD05Myl7bj1vPj4+NAppZighKG48OCkpcmV0 -dXJuIEEuT0goQi5hayxuKQpuPShCLmFrW25dJjE8PChvJjE1KSkhPT0wfWVsc2Ugbj0hMQplbHNlIG49 -ITEKaWYobil7QS5SMyhhLHIsIkludmFsaWQgY2hhcmFjdGVyIikKbD1pCm09bH1lbHNle2lmKChvJjY0 -NTEyKT09PTU1Mjk2KXtuPXIrMQppZihuPGMpe2s9Qi54Qi5PKGEsbikKaWYoKGsmNjQ1MTIpPT09NTYz -MjApe289KG8mMTAyMyk8PDEwfGsmMTAyM3w2NTUzNgpsPTJ9ZWxzZSBsPTF9ZWxzZSBsPTF9ZWxzZSBs -PTEKbT1BLnpYKG8pfX1pZihwPT1udWxsKXtwPW5ldyBBLlJuKCIiKQpuPXB9ZWxzZSBuPXAKaj1uLmEr -PUIueEIuTmooYSxxLHIpCm4uYT1qK0EuZChtKQppZih0eXBlb2YgbCE9PSJudW1iZXIiKXJldHVybiBB -LnBZKGwpCnIrPWwKcT1yfX1pZihwPT1udWxsKXJldHVybiBpCmlmKHE8YylwLmErPUIueEIuTmooYSxx -LGMpCnM9cC5hCnJldHVybiBzLmNoYXJDb2RlQXQoMCk9PTA/czpzfSwKeUIoYSl7aWYoQi54Qi5uQyhh -LCIuIikpcmV0dXJuITAKcmV0dXJuIEIueEIuT1koYSwiLy4iKSE9PS0xfSwKeGUoYSl7dmFyIHMscixx -LHAsbyxuLG0KaWYoIUEueUIoYSkpcmV0dXJuIGEKcz1BLlFJKFtdLHQucykKZm9yKHI9YS5zcGxpdCgi -LyIpLHE9ci5sZW5ndGgscD0hMSxvPTA7bzxxOysrbyl7bj1yW29dCmlmKEouUk0obiwiLi4iKSl7bT1z -Lmxlbmd0aAppZihtIT09MCl7aWYoMD49bSlyZXR1cm4gQS5PSChzLC0xKQpzLnBvcCgpCmlmKHMubGVu -Z3RoPT09MClCLk5tLmkocywiIil9cD0hMH1lbHNlIGlmKCIuIj09PW4pcD0hMAplbHNle0IuTm0uaShz -LG4pCnA9ITF9fWlmKHApQi5ObS5pKHMsIiIpCnJldHVybiBCLk5tLkgocywiLyIpfSwKd0YoYSxiKXt2 -YXIgcyxyLHEscCxvLG4KaWYoIUEueUIoYSkpcmV0dXJuIWI/QS5DMShhKTphCnM9QS5RSShbXSx0LnMp -CmZvcihyPWEuc3BsaXQoIi8iKSxxPXIubGVuZ3RoLHA9ITEsbz0wO288cTsrK28pe249cltvXQppZigi -Li4iPT09bilpZihzLmxlbmd0aCE9PTAmJkIuTm0uZ3JaKHMpIT09Ii4uIil7aWYoMD49cy5sZW5ndGgp -cmV0dXJuIEEuT0gocywtMSkKcy5wb3AoKQpwPSEwfWVsc2V7Qi5ObS5pKHMsIi4uIikKcD0hMX1lbHNl -IGlmKCIuIj09PW4pcD0hMAplbHNle0IuTm0uaShzLG4pCnA9ITF9fXI9cy5sZW5ndGgKaWYociE9PTAp -aWYocj09PTEpe2lmKDA+PXIpcmV0dXJuIEEuT0gocywwKQpyPXNbMF0ubGVuZ3RoPT09MH1lbHNlIHI9 -ITEKZWxzZSByPSEwCmlmKHIpcmV0dXJuIi4vIgppZihwfHxCLk5tLmdyWihzKT09PSIuLiIpQi5ObS5p -KHMsIiIpCmlmKCFiKXtpZigwPj1zLmxlbmd0aClyZXR1cm4gQS5PSChzLDApCkIuTm0uWTUocywwLEEu -QzEoc1swXSkpfXJldHVybiBCLk5tLkgocywiLyIpfSwKQzEoYSl7dmFyIHMscixxLHA9YS5sZW5ndGgK -aWYocD49MiYmQS5FdChCLnhCLlcoYSwwKSkpZm9yKHM9MTtzPHA7KytzKXtyPUIueEIuVyhhLHMpCmlm -KHI9PT01OClyZXR1cm4gQi54Qi5OaihhLDAscykrIiUzQSIrQi54Qi55bihhLHMrMSkKaWYocjw9MTI3 -KXtxPXI+Pj40CmlmKCEocTw4KSlyZXR1cm4gQS5PSChCLm1LLHEpCnE9KEIubUtbcV0mMTw8KHImMTUp -KT09PTB9ZWxzZSBxPSEwCmlmKHEpYnJlYWt9cmV0dXJuIGF9LAp1aihhLGIpe2lmKGEuaEIoInBhY2th -Z2UiKSYmYS5jPT1udWxsKXJldHVybiBBLmZGKGIsMCxiLmxlbmd0aCkKcmV0dXJuLTF9LAptbihhKXt2 -YXIgcyxyLHEscD1hLmdGaigpLG89cC5sZW5ndGgKaWYobz4wJiZKLkhtKHBbMF0pPT09MiYmSi5hNihw -WzBdLDEpPT09NTgpe2lmKDA+PW8pcmV0dXJuIEEuT0gocCwwKQpBLnJnKEouYTYocFswXSwwKSwhMSkK -QS5ITihwLCExLDEpCnM9ITB9ZWxzZXtBLkhOKHAsITEsMCkKcz0hMX1yPWEuZ3RUKCkmJiFzPyIiKyJc -XCI6IiIKaWYoYS5nY2ooKSl7cT1hLmdKZihhKQppZihxLmxlbmd0aCE9PTApcj1yKyJcXCIrcSsiXFwi -fXI9QS5sKHIscCwiXFwiKQpvPXMmJm89PT0xP3IrIlxcIjpyCnJldHVybiBvLmNoYXJDb2RlQXQoMCk9 -PTA/bzpvfSwKSWgoYSxiKXt2YXIgcyxyLHEKZm9yKHM9MCxyPTA7cjwyOysrcil7cT1CLnhCLlcoYSxi -K3IpCmlmKDQ4PD1xJiZxPD01NylzPXMqMTYrcS00OAplbHNle3F8PTMyCmlmKDk3PD1xJiZxPD0xMDIp -cz1zKjE2K3EtODcKZWxzZSB0aHJvdyBBLmIoQS54WSgiSW52YWxpZCBVUkwgZW5jb2RpbmciLG51bGwp -KX19cmV0dXJuIHN9LAprdShhLGIsYyxkLGUpe3ZhciBzLHIscSxwLG89Ygp3aGlsZSghMCl7aWYoIShv -PGMpKXtzPSEwCmJyZWFrfXI9Qi54Qi5XKGEsbykKaWYocjw9MTI3KWlmKHIhPT0zNylxPWUmJnI9PT00 -MwplbHNlIHE9ITAKZWxzZSBxPSEwCmlmKHEpe3M9ITEKYnJlYWt9KytvfWlmKHMpe2lmKEIueE0hPT1k -KXE9ITEKZWxzZSBxPSEwCmlmKHEpcmV0dXJuIEIueEIuTmooYSxiLGMpCmVsc2UgcD1uZXcgQS5xaihC -LnhCLk5qKGEsYixjKSl9ZWxzZXtwPUEuUUkoW10sdC50KQpmb3IocT1hLmxlbmd0aCxvPWI7bzxjOysr -byl7cj1CLnhCLlcoYSxvKQppZihyPjEyNyl0aHJvdyBBLmIoQS54WSgiSWxsZWdhbCBwZXJjZW50IGVu -Y29kaW5nIGluIFVSSSIsbnVsbCkpCmlmKHI9PT0zNyl7aWYobyszPnEpdGhyb3cgQS5iKEEueFkoIlRy -dW5jYXRlZCBVUkkiLG51bGwpKQpCLk5tLmkocCxBLkloKGEsbysxKSkKbys9Mn1lbHNlIGlmKGUmJnI9 -PT00MylCLk5tLmkocCwzMikKZWxzZSBCLk5tLmkocCxyKX19dC5MLmEocCkKcmV0dXJuIEIub0UuV0oo -cCl9LApFdChhKXt2YXIgcz1hfDMyCnJldHVybiA5Nzw9cyYmczw9MTIyfSwKS0QoYSxiLGMpe3ZhciBz -LHIscSxwLG8sbixtLGwsaz0iSW52YWxpZCBNSU1FIHR5cGUiLGo9QS5RSShbYi0xXSx0LnQpCmZvcihz -PWEubGVuZ3RoLHI9YixxPS0xLHA9bnVsbDtyPHM7KytyKXtwPUIueEIuVyhhLHIpCmlmKHA9PT00NHx8 -cD09PTU5KWJyZWFrCmlmKHA9PT00Nyl7aWYocTwwKXtxPXIKY29udGludWV9dGhyb3cgQS5iKEEucnIo -ayxhLHIpKX19aWYocTwwJiZyPmIpdGhyb3cgQS5iKEEucnIoayxhLHIpKQpmb3IoO3AhPT00NDspe0Iu -Tm0uaShqLHIpOysrcgpmb3Iobz0tMTtyPHM7KytyKXtwPUIueEIuVyhhLHIpCmlmKHA9PT02MSl7aWYo -bzwwKW89cn1lbHNlIGlmKHA9PT01OXx8cD09PTQ0KWJyZWFrfWlmKG8+PTApQi5ObS5pKGosbykKZWxz -ZXtuPUIuTm0uZ3JaKGopCmlmKHAhPT00NHx8ciE9PW4rN3x8IUIueEIuUWkoYSwiYmFzZTY0IixuKzEp -KXRocm93IEEuYihBLnJyKCJFeHBlY3RpbmcgJz0nIixhLHIpKQpicmVha319Qi5ObS5pKGoscikKbT1y -KzEKaWYoKGoubGVuZ3RoJjEpPT09MSlhPUIuaDkueXIoYSxtLHMpCmVsc2V7bD1BLlVsKGEsbSxzLEIu -VkMsITAsITEpCmlmKGwhPW51bGwpYT1CLnhCLmk3KGEsbSxzLGwpfXJldHVybiBuZXcgQS5QRShhLGos -Yyl9LAp1eCgpe3ZhciBzLHIscSxwLG8sbixtPSIwMTIzNDU2Nzg5QUJDREVGR0hJSktMTU5PUFFSU1RV -VldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ei0uX34hJCYnKCkqKyw7PSIsbD0iLiIsaz0iOiIs -aj0iLyIsaT0iXFwiLGg9Ij8iLGc9IiMiLGY9Ii9cXCIsZT1BLlFJKG5ldyBBcnJheSgyMiksdC5nTikK -Zm9yKHM9MDtzPDIyOysrcyllW3NdPW5ldyBVaW50OEFycmF5KDk2KQpyPW5ldyBBLnlJKGUpCnE9bmV3 -IEEuYzYoKQpwPW5ldyBBLnFkKCkKbz10LmdjLmEoci4kMigwLDIyNSkpCnEuJDMobyxtLDEpCnEuJDMo -byxsLDE0KQpxLiQzKG8saywzNCkKcS4kMyhvLGosMykKcS4kMyhvLGksMjI3KQpxLiQzKG8saCwxNzIp -CnEuJDMobyxnLDIwNSkKbj1yLiQyKDE0LDIyNSkKcS4kMyhuLG0sMSkKcS4kMyhuLGwsMTUpCnEuJDMo -bixrLDM0KQpxLiQzKG4sZiwyMzQpCnEuJDMobixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIuJDIoMTUs -MjI1KQpxLiQzKG4sbSwxKQpxLiQzKG4sIiUiLDIyNSkKcS4kMyhuLGssMzQpCnEuJDMobixqLDkpCnEu -JDMobixpLDIzMykKcS4kMyhuLGgsMTcyKQpxLiQzKG4sZywyMDUpCm49ci4kMigxLDIyNSkKcS4kMyhu -LG0sMSkKcS4kMyhuLGssMzQpCnEuJDMobixqLDEwKQpxLiQzKG4saSwyMzQpCnEuJDMobixoLDE3MikK -cS4kMyhuLGcsMjA1KQpuPXIuJDIoMiwyMzUpCnEuJDMobixtLDEzOSkKcS4kMyhuLGosMTMxKQpxLiQz -KG4saSwxMzEpCnEuJDMobixsLDE0NikKcS4kMyhuLGgsMTcyKQpxLiQzKG4sZywyMDUpCm49ci4kMigz -LDIzNSkKcS4kMyhuLG0sMTEpCnEuJDMobixqLDY4KQpxLiQzKG4saSw2OCkKcS4kMyhuLGwsMTgpCnEu -JDMobixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIuJDIoNCwyMjkpCnEuJDMobixtLDUpCnAuJDMobiwi -QVoiLDIyOSkKcS4kMyhuLGssMTAyKQpxLiQzKG4sIkAiLDY4KQpxLiQzKG4sIlsiLDIzMikKcS4kMyhu -LGosMTM4KQpxLiQzKG4saSwxMzgpCnEuJDMobixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIuJDIoNSwy -MjkpCnEuJDMobixtLDUpCnAuJDMobiwiQVoiLDIyOSkKcS4kMyhuLGssMTAyKQpxLiQzKG4sIkAiLDY4 -KQpxLiQzKG4saiwxMzgpCnEuJDMobixpLDEzOCkKcS4kMyhuLGgsMTcyKQpxLiQzKG4sZywyMDUpCm49 -ci4kMig2LDIzMSkKcC4kMyhuLCIxOSIsNykKcS4kMyhuLCJAIiw2OCkKcS4kMyhuLGosMTM4KQpxLiQz -KG4saSwxMzgpCnEuJDMobixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIuJDIoNywyMzEpCnAuJDMobiwi -MDkiLDcpCnEuJDMobiwiQCIsNjgpCnEuJDMobixqLDEzOCkKcS4kMyhuLGksMTM4KQpxLiQzKG4saCwx -NzIpCnEuJDMobixnLDIwNSkKcS4kMyhyLiQyKDgsOCksIl0iLDUpCm49ci4kMig5LDIzNSkKcS4kMyhu -LG0sMTEpCnEuJDMobixsLDE2KQpxLiQzKG4sZiwyMzQpCnEuJDMobixoLDE3MikKcS4kMyhuLGcsMjA1 -KQpuPXIuJDIoMTYsMjM1KQpxLiQzKG4sbSwxMSkKcS4kMyhuLGwsMTcpCnEuJDMobixmLDIzNCkKcS4k -MyhuLGgsMTcyKQpxLiQzKG4sZywyMDUpCm49ci4kMigxNywyMzUpCnEuJDMobixtLDExKQpxLiQzKG4s -aiw5KQpxLiQzKG4saSwyMzMpCnEuJDMobixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIuJDIoMTAsMjM1 -KQpxLiQzKG4sbSwxMSkKcS4kMyhuLGwsMTgpCnEuJDMobixqLDEwKQpxLiQzKG4saSwyMzQpCnEuJDMo -bixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIuJDIoMTgsMjM1KQpxLiQzKG4sbSwxMSkKcS4kMyhuLGws -MTkpCnEuJDMobixmLDIzNCkKcS4kMyhuLGgsMTcyKQpxLiQzKG4sZywyMDUpCm49ci4kMigxOSwyMzUp -CnEuJDMobixtLDExKQpxLiQzKG4sZiwyMzQpCnEuJDMobixoLDE3MikKcS4kMyhuLGcsMjA1KQpuPXIu -JDIoMTEsMjM1KQpxLiQzKG4sbSwxMSkKcS4kMyhuLGosMTApCnEuJDMobixpLDIzNCkKcS4kMyhuLGgs -MTcyKQpxLiQzKG4sZywyMDUpCm49ci4kMigxMiwyMzYpCnEuJDMobixtLDEyKQpxLiQzKG4saCwxMikK -cS4kMyhuLGcsMjA1KQpuPXIuJDIoMTMsMjM3KQpxLiQzKG4sbSwxMykKcS4kMyhuLGgsMTMpCnAuJDMo -ci4kMigyMCwyNDUpLCJheiIsMjEpCm49ci4kMigyMSwyNDUpCnAuJDMobiwiYXoiLDIxKQpwLiQzKG4s -IjA5IiwyMSkKcS4kMyhuLCIrLS4iLDIxKQpyZXR1cm4gZX0sClVCKGEsYixjLGQsZSl7dmFyIHMscixx -LHAsbz0kLnZaKCkKZm9yKHM9YjtzPGM7KytzKXtpZighKGQ+PTAmJmQ8by5sZW5ndGgpKXJldHVybiBB -Lk9IKG8sZCkKcj1vW2RdCnE9Qi54Qi5XKGEscyleOTYKcD1yW3E+OTU/MzE6cV0KZD1wJjMxCkIuTm0u -WTUoZSxwPj4+NSxzKX1yZXR1cm4gZH0sClJ4KGEpe2lmKGEuYj09PTcmJkIueEIubkMoYS5hLCJwYWNr -YWdlIikmJmEuYzw9MClyZXR1cm4gQS5mRihhLmEsYS5lLGEuZikKcmV0dXJuLTF9LApmRihhLGIsYyl7 -dmFyIHMscixxCmZvcihzPWIscj0wO3M8YzsrK3Mpe3E9Qi54Qi5PKGEscykKaWYocT09PTQ3KXJldHVy -biByIT09MD9zOi0xCmlmKHE9PT0zN3x8cT09PTU4KXJldHVybi0xCnJ8PXFeNDZ9cmV0dXJuLTF9LApi -VShhLGIsYyl7dmFyIHMscixxLHAsbyxuLG0KZm9yKHM9YS5sZW5ndGgscj0wLHE9MDtxPHM7KytxKXtw -PUIueEIuVyhhLHEpCm89Qi54Qi5XKGIsYytxKQpuPXBebwppZihuIT09MCl7aWYobj09PTMyKXttPW98 -bgppZig5Nzw9bSYmbTw9MTIyKXtyPTMyCmNvbnRpbnVlfX1yZXR1cm4tMX19cmV0dXJuIHJ9LApXRjpm -dW5jdGlvbiBXRihhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKaVA6ZnVuY3Rpb24gaVAoYSxiKXt0aGlz -LmE9YQp0aGlzLmI9Yn0sCmNrOmZ1bmN0aW9uIGNrKCl7fSwKWFM6ZnVuY3Rpb24gWFMoKXt9LApDNjpm -dW5jdGlvbiBDNihhKXt0aGlzLmE9YX0sCkV6OmZ1bmN0aW9uIEV6KCl7fSwKRjpmdW5jdGlvbiBGKCl7 -fSwKdTpmdW5jdGlvbiB1KGEsYixjLGQpe3ZhciBfPXRoaXMKXy5hPWEKXy5iPWIKXy5jPWMKXy5kPWR9 -LApiSjpmdW5jdGlvbiBiSihhLGIsYyxkLGUsZil7dmFyIF89dGhpcwpfLmU9YQpfLmY9YgpfLmE9Ywpf -LmI9ZApfLmM9ZQpfLmQ9Zn0sCmVZOmZ1bmN0aW9uIGVZKGEsYixjLGQsZSl7dmFyIF89dGhpcwpfLmY9 -YQpfLmE9YgpfLmI9YwpfLmM9ZApfLmQ9ZX0sCm1wOmZ1bmN0aW9uIG1wKGEsYixjLGQsZSl7dmFyIF89 -dGhpcwpfLmE9YQpfLmI9YgpfLmM9YwpfLmQ9ZApfLmU9ZX0sCnViOmZ1bmN0aW9uIHViKGEpe3RoaXMu -YT1hfSwKZHM6ZnVuY3Rpb24gZHMoYSl7dGhpcy5hPWF9LApsajpmdW5jdGlvbiBsaihhKXt0aGlzLmE9 -YX0sClVWOmZ1bmN0aW9uIFVWKGEpe3RoaXMuYT1hfSwKazU6ZnVuY3Rpb24gazUoKXt9LApLWTpmdW5j -dGlvbiBLWSgpe30sCnA6ZnVuY3Rpb24gcChhKXt0aGlzLmE9YX0sCkNEOmZ1bmN0aW9uIENEKGEpe3Ro -aXMuYT1hfSwKYUU6ZnVuY3Rpb24gYUUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwK -Y1g6ZnVuY3Rpb24gY1goKXt9LApBbjpmdW5jdGlvbiBBbigpe30sCk4zOmZ1bmN0aW9uIE4zKGEsYixj -KXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLiR0aT1jfSwKYzg6ZnVuY3Rpb24gYzgoKXt9LApNaDpmdW5j -dGlvbiBNaCgpe30sClpkOmZ1bmN0aW9uIFpkKCl7fSwKUm46ZnVuY3Rpb24gUm4oYSl7dGhpcy5hPWF9 -LApuMTpmdW5jdGlvbiBuMShhKXt0aGlzLmE9YX0sCmNTOmZ1bmN0aW9uIGNTKGEpe3RoaXMuYT1hfSwK -VkM6ZnVuY3Rpb24gVkMoYSl7dGhpcy5hPWF9LApKVDpmdW5jdGlvbiBKVChhLGIpe3RoaXMuYT1hCnRo -aXMuYj1ifSwKRG46ZnVuY3Rpb24gRG4oYSxiLGMsZCxlLGYsZyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9 -YgpfLmM9YwpfLmQ9ZApfLmU9ZQpfLmY9ZgpfLnI9ZwpfLno9Xy55PV8ueD1fLnc9JH0sClJaOmZ1bmN0 -aW9uIFJaKCl7fSwKTUU6ZnVuY3Rpb24gTUUoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCnk1OmZ1bmN0 -aW9uIHk1KGEpe3RoaXMuYT1hfSwKUEU6ZnVuY3Rpb24gUEUoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1i -CnRoaXMuYz1jfSwKeUk6ZnVuY3Rpb24geUkoYSl7dGhpcy5hPWF9LApjNjpmdW5jdGlvbiBjNigpe30s -CnFkOmZ1bmN0aW9uIHFkKCl7fSwKVWY6ZnVuY3Rpb24gVWYoYSxiLGMsZCxlLGYsZyxoKXt2YXIgXz10 -aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kCl8uZT1lCl8uZj1mCl8ucj1nCl8udz1oCl8ueD1udWxs -fSwKcWU6ZnVuY3Rpb24gcWUoYSxiLGMsZCxlLGYsZyl7dmFyIF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9 -YwpfLmQ9ZApfLmU9ZQpfLmY9ZgpfLnI9ZwpfLno9Xy55PV8ueD1fLnc9JH0sCngzKCl7cmV0dXJuIHdp -bmRvd30sClpyKCl7cmV0dXJuIGRvY3VtZW50fSwKSjYoYSl7dmFyIHM9ZG9jdW1lbnQuY3JlYXRlRWxl -bWVudCgiYSIpCmlmKGEhPW51bGwpQi54bi5zTFUocyxhKQpyZXR1cm4gc30sCkxqKGEpe3JldHVybiBD -U1MuZXNjYXBlKGEpfSwKVTkoYSxiLGMpe3ZhciBzLHI9ZG9jdW1lbnQuYm9keQpyLnRvU3RyaW5nCnM9 -dC5hYwpzPW5ldyBBLlU1KG5ldyBBLmU3KEIuUlkucjYocixhLGIsYykpLHMuQygiYTIobEQuRSkiKS5h -KG5ldyBBLkN2KCkpLHMuQygiVTU8bEQuRT4iKSkKcmV0dXJuIHQuaC5hKHMuZ3I4KHMpKX0sCnJTKGEp -e3ZhciBzLHI9ImVsZW1lbnQgdGFnIHVuYXZhaWxhYmxlIgp0cnl7cj1hLnRhZ05hbWV9Y2F0Y2gocyl7 -fXJldHVybiByfSwKUjYoYSxiKXt2YXIgcyxyLHE9YS5jbGFzc0xpc3QKZm9yKHM9Yi5sZW5ndGgscj0w -O3I8Yi5sZW5ndGg7Yi5sZW5ndGg9PT1zfHwoMCxBLmxrKShiKSwrK3IpcS5hZGQoYltyXSl9LApKRShh -LGIsYyxkLGUpe3ZhciBzPUEuYUYobmV3IEEudk4oYyksdC5CKQppZihzIT1udWxsJiYhMClKLmRaKGEs -YixzLCExKQpyZXR1cm4gbmV3IEEueEMoYSxiLHMsITEsZS5DKCJ4QzwwPiIpKX0sClR3KGEpe3ZhciBz -PUEuSjYobnVsbCkscj10LkYuYSh3aW5kb3cubG9jYXRpb24pCnM9bmV3IEEuSlEobmV3IEEubWsocyxy -KSkKcy5DWShhKQpyZXR1cm4gc30sCnFEKGEsYixjLGQpe3QuaC5hKGEpCkEubihiKQpBLm4oYykKdC5j -ci5hKGQpCnJldHVybiEwfSwKblooYSxiLGMsZCl7dmFyIHMscixxCnQuaC5hKGEpCkEubihiKQpBLm4o -YykKcz10LmNyLmEoZCkuYQpyPXMuYQpCLnhuLnNMVShyLGMpCnE9ci5ob3N0bmFtZQpzPXMuYgppZigh -KHE9PXMuaG9zdG5hbWUmJnIucG9ydD09PXMucG9ydCYmci5wcm90b2NvbD09PXMucHJvdG9jb2wpKWlm -KHE9PT0iIilpZihyLnBvcnQ9PT0iIil7cz1yLnByb3RvY29sCnM9cz09PSI6Inx8cz09PSIifWVsc2Ug -cz0hMQplbHNlIHM9ITEKZWxzZSBzPSEwCnJldHVybiBzfSwKQmwoKXt2YXIgcz10Lk4scj1BLnRNKEIu -UXgscykscT1BLlFJKFsiVEVNUExBVEUiXSx0LnMpLHA9dC5kRy5hKG5ldyBBLklBKCkpCnM9bmV3IEEu -Y3QocixBLkxzKHMpLEEuTHMocyksQS5McyhzKSxudWxsKQpzLkNZKG51bGwsbmV3IEEubEooQi5ReCxw -LHQuZHYpLHEsbnVsbCkKcmV0dXJuIHN9LApxYyhhKXt2YXIgcwppZihhPT1udWxsKXJldHVybiBudWxs -CmlmKCJwb3N0TWVzc2FnZSIgaW4gYSl7cz1BLlAxKGEpCnJldHVybiBzfWVsc2UgcmV0dXJuIHQuY2gu -YShhKX0sClAxKGEpe2lmKGE9PT13aW5kb3cpcmV0dXJuIHQuY2kuYShhKQplbHNlIHJldHVybiBuZXcg -QS5kVygpfSwKYUYoYSxiKXt2YXIgcz0kLlgzCmlmKHM9PT1CLk5VKXJldHVybiBhCnJldHVybiBzLlB5 -KGEsYil9LApxRTpmdW5jdGlvbiBxRSgpe30sCkdoOmZ1bmN0aW9uIEdoKCl7fSwKZlk6ZnVuY3Rpb24g -ZlkoKXt9LApyWjpmdW5jdGlvbiByWigpe30sCkF6OmZ1bmN0aW9uIEF6KCl7fSwKUVA6ZnVuY3Rpb24g -UVAoKXt9LApueDpmdW5jdGlvbiBueCgpe30sCm9KOmZ1bmN0aW9uIG9KKCl7fSwKaWQ6ZnVuY3Rpb24g -aWQoKXt9LApRRjpmdW5jdGlvbiBRRigpe30sCk5oOmZ1bmN0aW9uIE5oKCl7fSwKYWU6ZnVuY3Rpb24g -YWUoKXt9LApJQjpmdW5jdGlvbiBJQigpe30sCm43OmZ1bmN0aW9uIG43KCl7fSwKd3o6ZnVuY3Rpb24g -d3ooYSxiKXt0aGlzLmE9YQp0aGlzLiR0aT1ifSwKY3Y6ZnVuY3Rpb24gY3YoKXt9LApDdjpmdW5jdGlv -biBDdigpe30sCmVhOmZ1bmN0aW9uIGVhKCl7fSwKUFo6ZnVuY3Rpb24gUFooKXt9LApoSDpmdW5jdGlv -biBoSCgpe30sCmg0OmZ1bmN0aW9uIGg0KCl7fSwKYnI6ZnVuY3Rpb24gYnIoKXt9LApWYjpmdW5jdGlv -biBWYigpe30sCmZKOmZ1bmN0aW9uIGZKKCl7fSwKd2E6ZnVuY3Rpb24gd2EoKXt9LApTZzpmdW5jdGlv -biBTZygpe30sCnU4OmZ1bmN0aW9uIHU4KCl7fSwKQWo6ZnVuY3Rpb24gQWooKXt9LAplNzpmdW5jdGlv -biBlNyhhKXt0aGlzLmE9YX0sCktWOmZ1bmN0aW9uIEtWKCl7fSwKQkg6ZnVuY3Rpb24gQkgoKXt9LApT -TjpmdW5jdGlvbiBTTigpe30sCndWOmZ1bmN0aW9uIHdWKCl7fSwKbHA6ZnVuY3Rpb24gbHAoKXt9LApU -YjpmdW5jdGlvbiBUYigpe30sCkl2OmZ1bmN0aW9uIEl2KCl7fSwKV1A6ZnVuY3Rpb24gV1AoKXt9LAp5 -WTpmdW5jdGlvbiB5WSgpe30sCnc2OmZ1bmN0aW9uIHc2KCl7fSwKSzU6ZnVuY3Rpb24gSzUoKXt9LApD -bTpmdW5jdGlvbiBDbSgpe30sCkNROmZ1bmN0aW9uIENRKCl7fSwKdzQ6ZnVuY3Rpb24gdzQoKXt9LApy -aDpmdW5jdGlvbiByaCgpe30sCmNmOmZ1bmN0aW9uIGNmKCl7fSwKaTc6ZnVuY3Rpb24gaTcoYSl7dGhp -cy5hPWF9LApTeTpmdW5jdGlvbiBTeShhKXt0aGlzLmE9YX0sCktTOmZ1bmN0aW9uIEtTKGEsYil7dGhp -cy5hPWEKdGhpcy5iPWJ9LApBMzpmdW5jdGlvbiBBMyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKSTQ6 -ZnVuY3Rpb24gSTQoYSl7dGhpcy5hPWF9LApGazpmdW5jdGlvbiBGayhhLGIpe3RoaXMuYT1hCnRoaXMu -JHRpPWJ9LApSTzpmdW5jdGlvbiBSTyhhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1j -Cl8uJHRpPWR9LApDcTpmdW5jdGlvbiBDcShhLGIsYyxkKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8u -Yz1jCl8uJHRpPWR9LAp4QzpmdW5jdGlvbiB4QyhhLGIsYyxkLGUpe3ZhciBfPXRoaXMKXy5iPWEKXy5j -PWIKXy5kPWMKXy5lPWQKXy4kdGk9ZX0sCnZOOmZ1bmN0aW9uIHZOKGEpe3RoaXMuYT1hfSwKSlE6ZnVu -Y3Rpb24gSlEoYSl7dGhpcy5hPWF9LApHbTpmdW5jdGlvbiBHbSgpe30sCnZEOmZ1bmN0aW9uIHZEKGEp -e3RoaXMuYT1hfSwKVXY6ZnVuY3Rpb24gVXYoYSl7dGhpcy5hPWF9LApFZzpmdW5jdGlvbiBFZyhhLGIs -Yyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhpcy5jPWN9LAptNjpmdW5jdGlvbiBtNigpe30sCkVvOmZ1bmN0 -aW9uIEVvKCl7fSwKV2s6ZnVuY3Rpb24gV2soKXt9LApjdDpmdW5jdGlvbiBjdChhLGIsYyxkLGUpe3Zh -ciBfPXRoaXMKXy5lPWEKXy5hPWIKXy5iPWMKXy5jPWQKXy5kPWV9LApJQTpmdW5jdGlvbiBJQSgpe30s -Ck93OmZ1bmN0aW9uIE93KCl7fSwKVzk6ZnVuY3Rpb24gVzkoYSxiLGMpe3ZhciBfPXRoaXMKXy5hPWEK -Xy5iPWIKXy5jPS0xCl8uZD1udWxsCl8uJHRpPWN9LApkVzpmdW5jdGlvbiBkVygpe30sCm1rOmZ1bmN0 -aW9uIG1rKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApLbzpmdW5jdGlvbiBLbyhhKXt0aGlzLmE9YQp0 -aGlzLmI9MH0sCmZtOmZ1bmN0aW9uIGZtKGEpe3RoaXMuYT1hfSwKWTg6ZnVuY3Rpb24gWTgoKXt9LApu -cTpmdW5jdGlvbiBucSgpe30sCkFyOmZ1bmN0aW9uIEFyKCl7fSwKdEQ6ZnVuY3Rpb24gdEQoKXt9LAp1 -ZjpmdW5jdGlvbiB1Zigpe30sCmlKOmZ1bmN0aW9uIGlKKCl7fSwKRTI6ZnVuY3Rpb24gRTIoYSxiKXt0 -aGlzLmE9YQp0aGlzLmI9Yn0sCmpnOmZ1bmN0aW9uIGpnKGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApC -ZjpmdW5jdGlvbiBCZihhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKQXM6ZnVuY3Rpb24gQXMoKXt9LApH -RTpmdW5jdGlvbiBHRShhKXt0aGlzLmE9YX0sCk43OmZ1bmN0aW9uIE43KGEsYil7dGhpcy5hPWEKdGhp -cy5iPWJ9LAp1UTpmdW5jdGlvbiB1USgpe30sCmhGOmZ1bmN0aW9uIGhGKCl7fSwKUjQoYSxiLGMsZCl7 -dmFyIHMscixxCkEucDgoYikKdC5qLmEoZCkKaWYoYil7cz1bY10KQi5ObS5GVihzLGQpCmQ9c31yPXQu -egpxPUEuUFcoSi5NMShkLEEudzAoKSxyKSwhMCxyKQp0LlkuYShhKQpyZXR1cm4gQS53WShBLkVrKGEs -cSxudWxsKSl9LApEbShhLGIsYyl7dmFyIHMKdHJ5e2lmKE9iamVjdC5pc0V4dGVuc2libGUoYSkmJiFP -YmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoYSxiKSl7T2JqZWN0LmRlZmluZVByb3Bl -cnR5KGEsYix7dmFsdWU6Y30pCnJldHVybiEwfX1jYXRjaChzKXt9cmV0dXJuITF9LApPbShhLGIpe2lm -KE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChhLGIpKXJldHVybiBhW2JdCnJldHVy -biBudWxsfSwKd1koYSl7aWYoYT09bnVsbHx8dHlwZW9mIGE9PSJzdHJpbmcifHx0eXBlb2YgYT09Im51 -bWJlciJ8fEEuclEoYSkpcmV0dXJuIGEKaWYoYSBpbnN0YW5jZW9mIEEuRTQpcmV0dXJuIGEuYQppZihB -LlI5KGEpKXJldHVybiBhCmlmKHQuYWsuYihhKSlyZXR1cm4gYQppZihhIGluc3RhbmNlb2YgQS5pUCly -ZXR1cm4gQS5vMihhKQppZih0LlkuYihhKSlyZXR1cm4gQS5oRShhLCIkZGFydF9qc0Z1bmN0aW9uIixu -ZXcgQS5EVigpKQpyZXR1cm4gQS5oRShhLCJfJGRhcnRfanNPYmplY3QiLG5ldyBBLlBDKCQua0koKSkp -fSwKaEUoYSxiLGMpe3ZhciBzPUEuT20oYSxiKQppZihzPT1udWxsKXtzPWMuJDEoYSkKQS5EbShhLGIs -cyl9cmV0dXJuIHN9LApkVShhKXt2YXIgcyxyCmlmKGE9PW51bGx8fHR5cGVvZiBhPT0ic3RyaW5nInx8 -dHlwZW9mIGE9PSJudW1iZXIifHx0eXBlb2YgYT09ImJvb2xlYW4iKXJldHVybiBhCmVsc2UgaWYoYSBp -bnN0YW5jZW9mIE9iamVjdCYmQS5SOShhKSlyZXR1cm4gYQplbHNlIGlmKGEgaW5zdGFuY2VvZiBPYmpl -Y3QmJnQuYWsuYihhKSlyZXR1cm4gYQplbHNlIGlmKGEgaW5zdGFuY2VvZiBEYXRlKXtzPUEuSVooYS5n -ZXRUaW1lKCkpCmlmKE1hdGguYWJzKHMpPD04NjRlMTMpcj0hMQplbHNlIHI9ITAKaWYocilBLnYoQS54 -WSgiRGF0ZVRpbWUgaXMgb3V0c2lkZSB2YWxpZCByYW5nZTogIitzLG51bGwpKQpBLmNiKCExLCJpc1V0 -YyIsdC55KQpyZXR1cm4gbmV3IEEuaVAocywhMSl9ZWxzZSBpZihhLmNvbnN0cnVjdG9yPT09JC5rSSgp -KXJldHVybiBhLm8KZWxzZSByZXR1cm4gQS5ORChhKX0sCk5EKGEpe2lmKHR5cGVvZiBhPT0iZnVuY3Rp -b24iKXJldHVybiBBLmlRKGEsJC53KCksbmV3IEEuUVMoKSkKaWYoYSBpbnN0YW5jZW9mIEFycmF5KXJl -dHVybiBBLmlRKGEsJC5SOCgpLG5ldyBBLm5wKCkpCnJldHVybiBBLmlRKGEsJC5SOCgpLG5ldyBBLlV0 -KCkpfSwKaVEoYSxiLGMpe3ZhciBzPUEuT20oYSxiKQppZihzPT1udWxsfHwhKGEgaW5zdGFuY2VvZiBP -YmplY3QpKXtzPWMuJDEoYSkKQS5EbShhLGIscyl9cmV0dXJuIHN9LApEVjpmdW5jdGlvbiBEVigpe30s -ClBDOmZ1bmN0aW9uIFBDKGEpe3RoaXMuYT1hfSwKUVM6ZnVuY3Rpb24gUVMoKXt9LApucDpmdW5jdGlv -biBucCgpe30sClV0OmZ1bmN0aW9uIFV0KCl7fSwKRTQ6ZnVuY3Rpb24gRTQoYSl7dGhpcy5hPWF9LApy -NzpmdW5jdGlvbiByNyhhKXt0aGlzLmE9YX0sClR6OmZ1bmN0aW9uIFR6KGEsYil7dGhpcy5hPWEKdGhp -cy4kdGk9Yn0sCnZnOmZ1bmN0aW9uIHZnKCl7fSwKbmQ6ZnVuY3Rpb24gbmQoKXt9LApLZTpmdW5jdGlv -biBLZShhKXt0aGlzLmE9YX0sCmhpOmZ1bmN0aW9uIGhpKCl7fSwKamYoYSl7dmFyIHMscixxLHAKaWYo -YT09bnVsbClzPW51bGwKZWxzZXtzPUEuUUkoW10sdC5mQSkKZm9yKHI9Si5JVChhKTtyLkcoKTspe3E9 -ci5nbCgpCnA9Si5VNihxKQpzLnB1c2gobmV3IEEuU2UoQS5rKHAucShxLCJkZXNjcmlwdGlvbiIpKSxB -LmsocC5xKHEsImhyZWYiKSkpKX19cmV0dXJuIHN9LApOZChhKXt2YXIgcyxyCmlmKGE9PW51bGwpcz1u -dWxsCmVsc2V7cz1BLlFJKFtdLHQuaGgpCmZvcihyPUouSVQoYSk7ci5HKCk7KXMucHVzaChBLkpqKHIu -Z2woKSkpfXJldHVybiBzfSwKSmooYSl7dmFyIHM9Si5VNihhKSxyPUEuayhzLnEoYSwiZGVzY3JpcHRp -b24iKSkscT1BLlFJKFtdLHQuYUopCmZvcihzPUouSVQodC5XLmEocy5xKGEsImVudHJpZXMiKSkpO3Mu -RygpOylxLnB1c2goQS5SaihzLmdsKCkpKQpyZXR1cm4gbmV3IEEueUQocixxKX0sClJqKGEpe3ZhciBz -LHI9Si5VNihhKSxxPUEuayhyLnEoYSwiZGVzY3JpcHRpb24iKSkscD1BLmsoci5xKGEsImZ1bmN0aW9u -IikpLG89ci5xKGEsImxpbmsiKQppZihvPT1udWxsKW89bnVsbAplbHNle3M9Si5VNihvKQpvPW5ldyBB -Lk1sKEEuayhzLnEobywiaHJlZiIpKSxBLlVjKHMucShvLCJsaW5lIikpLEEuayhzLnEobywicGF0aCIp -KSl9cj10LmJNLmEoci5xKGEsImhpbnRBY3Rpb25zIikpCnI9cj09bnVsbD9udWxsOkouTTEocixuZXcg -QS5hTigpLHQuSikuYnIoMCkKcmV0dXJuIG5ldyBBLndiKHEscCxvLHI9PW51bGw/Qi5kbjpyKX0sCmQy -OmZ1bmN0aW9uIGQyKGEsYixjLGQsZSxmKXt2YXIgXz10aGlzCl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1k -Cl8uZT1lCl8uZj1mfSwKU2U6ZnVuY3Rpb24gU2UoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCk1sOmZ1 -bmN0aW9uIE1sKGEsYixjKXt0aGlzLmE9YQp0aGlzLmI9Ygp0aGlzLmM9Y30sCnlEOmZ1bmN0aW9uIHlE -KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LAp3YjpmdW5jdGlvbiB3YihhLGIsYyxkKXt2YXIgXz10aGlz -Cl8uYT1hCl8uYj1iCl8uYz1jCl8uZD1kfSwKYU46ZnVuY3Rpb24gYU4oKXt9LApiMDpmdW5jdGlvbiBi -MCgpe30sCndSKCl7cmV0dXJuIG5ldyBBLnFwKCIiLCIiLCIiLEIuRHgpfSwKWWYoYSl7dmFyIHMscixx -LHAsbyxuLG0sbCxrPUEuayhhLnEoMCwicmVnaW9ucyIpKSxqPUEuayhhLnEoMCwibmF2aWdhdGlvbkNv -bnRlbnQiKSksaT1BLmsoYS5xKDAsInNvdXJjZUNvZGUiKSksaD1BLkZsKHQuTix0LmY0KQpmb3Iocz10 -LkcuYShhLnEoMCwiZWRpdHMiKSkscz1zLmdQdShzKSxzPXMuZ00ocykscj10LmsscT10LmdpO3MuRygp -Oyl7cD1zLmdsKCkKbz1wLmEKbj1BLlFJKFtdLHEpCmZvcihwPUouSVQoci5hKHAuYikpO3AuRygpOyl7 -bT1wLmdsKCkKbD1KLlU2KG0pCm4ucHVzaChuZXcgQS5qOChBLlVjKGwucShtLCJsaW5lIikpLEEuayhs -LnEobSwiZXhwbGFuYXRpb24iKSksQS5VYyhsLnEobSwib2Zmc2V0IikpKSl9aC5ZNSgwLG8sbil9cmV0 -dXJuIG5ldyBBLnFwKGssaixpLGgpfSwKajg6ZnVuY3Rpb24gajgoYSxiLGMpe3RoaXMuYT1hCnRoaXMu -Yj1iCnRoaXMuYz1jfSwKcXA6ZnVuY3Rpb24gcXAoYSxiLGMsZCl7dmFyIF89dGhpcwpfLmE9YQpfLmI9 -YgpfLmM9YwpfLmQ9ZH0sCm1ROmZ1bmN0aW9uIG1RKCl7fSwKSXEoKXtCLkJaLkIoZG9jdW1lbnQsIkRP -TUNvbnRlbnRMb2FkZWQiLG5ldyBBLmUoKSkKQi5vbC5CKHdpbmRvdywicG9wc3RhdGUiLG5ldyBBLkwo -KSl9LApreihhLGIpe3ZhciBzLHIscT10LmguYShhLnBhcmVudE5vZGUpLnF1ZXJ5U2VsZWN0b3IoIjpz -Y29wZSA+IHVsIikscD1xLnN0eWxlLG89Qi5DRC56UShxLm9mZnNldEhlaWdodCkKcC5tYXhIZWlnaHQ9 -IiIrbyoyKyJweCIKcD1uZXcgQS5MWihxLGEpCm89Si5xRihhKQpzPW8uJHRpCnI9cy5DKCJ+KDEpPyIp -LmEobmV3IEEuV3gocSxwLG5ldyBBLnAxKHEsYSkpKQp0LlouYShudWxsKQpBLkpFKG8uYSxvLmIsciwh -MSxzLmMpCmlmKGIpcC4kMCgpfSwKeVgoYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxm -PSJxdWVyeVNlbGVjdG9yQWxsIixlPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYSkKZS50b1N0cmluZwpz -PXQuaApBLkRoKHMscywiVCIsZikKcj10LlIKcT1uZXcgQS53eihlLnF1ZXJ5U2VsZWN0b3JBbGwoIi5u -YXYtbGluayIpLHIpCmZvcihwPXIuQygiYTc8bEQuRT4iKSxvPW5ldyBBLmE3KHEscS5nayhxKSxwKSxu -PXQuWixtPXIuQygibEQuRSIpO28uRygpOyl7bD1vLmQKbD1KLnFGKGw9PW51bGw/bS5hKGwpOmwpCms9 -bC4kdGkKaj1rLkMoIn4oMSk/IikuYShuZXcgQS5IbyhiKSkKbi5hKG51bGwpCkEuSkUobC5hLGwuYixq -LCExLGsuYyl9QS5EaChzLHMsIlQiLGYpCmk9bmV3IEEud3ooZS5xdWVyeVNlbGVjdG9yQWxsKCIucmVn -aW9uIikscikKaWYoIWkuZ2wwKGkpKXtvPWUucXVlcnlTZWxlY3RvcigidGFibGVbZGF0YS1wYXRoXSIp -Cm8udG9TdHJpbmcKaD1vLmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBBLlN5KG5ldyBBLmk3KG8pKS5P -VSgicGF0aCIpKQpmb3Iobz1uZXcgQS5hNyhpLGkuZ2soaSkscCk7by5HKCk7KXtsPW8uZAppZihsPT1u -dWxsKWw9bS5hKGwpCms9Si5xRihsKQpqPWsuJHRpCmw9ai5DKCJ+KDEpPyIpLmEobmV3IEEuSUMobCxo -KSkKbi5hKG51bGwpCkEuSkUoay5hLGsuYixsLCExLGouYyl9fUEuRGgocyxzLCJUIixmKQpnPW5ldyBB -Lnd6KGUucXVlcnlTZWxlY3RvckFsbCgiLmFkZC1oaW50LWxpbmsiKSxyKQpmb3IoZT1uZXcgQS5hNyhn -LGcuZ2soZykscCk7ZS5HKCk7KXtzPWUuZApzPUoucUYocz09bnVsbD9tLmEocyk6cykKcj1zLiR0aQpy -LkMoIn4oMSk/IikuYShBLmlTKCkpCm4uYShudWxsKQpBLkpFKHMuYSxzLmIsQS5pUygpLCExLHIuYyl9 -fSwKUTYoYSxiLGMpe3ZhciBzPW5ldyBYTUxIdHRwUmVxdWVzdCgpCkIuRHQuZW8ocywiR0VUIixBLlE0 -KGEsYiksITApCnMuc2V0UmVxdWVzdEhlYWRlcigiQ29udGVudC1UeXBlIiwiYXBwbGljYXRpb24vanNv -bjsgY2hhcnNldD1VVEYtOCIpCnJldHVybiBBLkxVKHMsbnVsbCxjKX0sCnR5KGEsYil7dmFyIHM9bmV3 -IFhNTEh0dHBSZXF1ZXN0KCkscj10Lk4KQi5EdC5lbyhzLCJQT1NUIixBLlE0KGEsQS5GbChyLHIpKSwh -MCkKcy5zZXRSZXF1ZXN0SGVhZGVyKCJDb250ZW50LVR5cGUiLCJhcHBsaWNhdGlvbi9qc29uOyBjaGFy -c2V0PVVURi04IikKcmV0dXJuIEEuTFUocyxiLHQuRyl9LApMVShhLGIsYyl7cmV0dXJuIEEuVGcoYSxi -LGMsYy5DKCIwPyIpKX0sClRnKGEsYixjLGQpe3ZhciBzPTAscj1BLkZYKGQpLHEscD0yLG8sbixtLGws -ayxqLGksaCxnLGYKdmFyICRhc3luYyRMVT1BLmx6KGZ1bmN0aW9uKGUsYTApe2lmKGU9PT0xKXtvPWEw -CnM9cH13aGlsZSh0cnVlKXN3aXRjaChzKXtjYXNlIDA6aT1uZXcgQS5aZihuZXcgQS52cygkLlgzLHQu -YW8pLHQuYmopCmg9dC5neApnPWguYShuZXcgQS5mQyhpLGEpKQp0LlouYShudWxsKQpsPXQucApBLkpF -KGEsImxvYWQiLGcsITEsbCkKQS5KRShhLCJlcnJvciIsaC5hKGkuZ1lKKCkpLCExLGwpCmEuc2VuZChi -PT1udWxsP251bGw6Qi5DdC5PQihiLG51bGwpKQpwPTQKcz03CnJldHVybiBBLmpRKGkuYSwkYXN5bmMk -TFUpCmNhc2UgNzpwPTIKcz02CmJyZWFrCmNhc2UgNDpwPTMKZj1vCm49QS50cyhmKQpoPWEucmVhZHlT -dGF0ZQppZihoPT09NCYmYS5zdGF0dXM9PT0wKXRocm93IEEuYihBLlRHKCJFcnJvciByZWFjaGluZyBt -aWdyYXRpb24gcHJldmlldyBzZXJ2ZXIiLCJUaGlzIHVzdWFsbHkgaGFwcGVucyBiZWNhdXNlIHRoZSBt -aWdyYXRpb24gcHJldmlldyBzZXJ2ZXIgaGFzIGV4aXRlZC4gIEZvclxuZXhhbXBsZSBpdCBtYXkgaGF2 -ZSBiZWVuIGFib3J0ZWQgd2l0aCBDdHJsLUMsIG9yIHlvdSBtYXkgaGF2ZSBjb21wbGV0ZWQgdGhpc1xu -bWlncmF0aW9uLCBvciBhbiBleGNlcHRpb24gbWF5IGhhdmUgb2NjdXJyZWQuICBQbGVhc2UgY2hlY2sg -dGhlIGNvbnNvbGUgd2hlcmVcbnlvdSBpbnZva2VkIGBkYXJ0IG1pZ3JhdGVgIHRvIHZlcmlmeSB0aGF0 -IHRoZSBwcmV2aWV3IHNlcnZlciBpcyBzdGlsbCBydW5uaW5nLlxuIikpCmVsc2V7bT1uZXcgQS5sSihB -LlFJKFsicmVhZHlTdGF0ZT0iK2gsInJlc3BvbnNlVGV4dD0iK0IuQ3QuT0IoYS5yZXNwb25zZVRleHQs -bnVsbCksInJlc3BvbnNlVHlwZT0iK0IuQ3QuT0IoYS5yZXNwb25zZVR5cGUsbnVsbCksInJlc3BvbnNl -VXJsPSIrQi5DdC5PQihhLnJlc3BvbnNlVVJMLG51bGwpLCJzdGF0dXM9IitBLmQoYS5zdGF0dXMpLCJz -dGF0dXNUZXh0PSIrQi5DdC5PQihhLnN0YXR1c1RleHQsbnVsbCldLHQucyksdC5kRy5hKG5ldyBBLlRt -KCkpLHQuZHYpLkgoMCwiLCAiKQp0aHJvdyBBLmIoQS5UbCgiRXJyb3IgcmVhY2hpbmcgbWlncmF0aW9u -IHByZXZpZXcgc2VydmVyOiAiK0EuZChtKSxuKSl9cz02CmJyZWFrCmNhc2UgMzpzPTIKYnJlYWsKY2Fz -ZSA2OmlmKGEuc3RhdHVzPT09NDAxKXRocm93IEEuYihBLlRHKCJVbmF1dGhvcml6ZWQgcmVzcG9uc2Ug -ZnJvbSBtaWdyYXRpb24gcHJldmlldyBzZXJ2ZXIiLCJUaGUgbWlncmF0aW9uIHByZXZpZXcgc2VydmVy -IGhhcyBkZXRlY3RlZCBhIG1pc21hdGNoIGJldHdlZW4gdGhlIGF1dGhUb2tlbiBpblxueW91ciBVUkwg -YW5kIHRoZSB0b2tlbiB0aGF0IHdhcyBnZW5lcmF0ZWQgYXQgdGhlIHRpbWUgdGhhdCBgZGFydCBtaWdy -YXRlYCB3YXNcbnJ1bi4gIEhhdmUgeW91IHJlc3RhcnRlZCB0aGUgbWlncmF0aW9uIHNlcnZlciByZWNl -bnRseT8gIElmIHNvLCB5b3UnbGwgbmVlZCB0b1xuY2hlY2sgaXRzIG91dHB1dCBmb3IgYSBmcmVzaCBV -UkwsIGFuZCB1c2UgdGhhdCBVUkwgdG8gcGVyZm9ybSB5b3VyIG1pZ3JhdGlvbi5cbiIpKQpoPWEucmVz -cG9uc2VUZXh0CmgudG9TdHJpbmcKaj1CLkN0LnBXKDAsaCxudWxsKQppZihhLnN0YXR1cz09PTIwMCl7 -cT1jLkMoIjA/IikuYShqKQpzPTEKYnJlYWt9ZWxzZXtqLnRvU3RyaW5nCnRocm93IEEuYihqKX1jYXNl -IDE6cmV0dXJuIEEueUMocSxyKQpjYXNlIDI6cmV0dXJuIEEuZjMobyxyKX19KQpyZXR1cm4gQS5ESSgk -YXN5bmMkTFUscil9LAphSyhhKXt2YXIgcz1BLmhLKGEpLmdoWSgpLnEoMCwibGluZSIpCnJldHVybiBz -PT1udWxsP251bGw6QS5IcChzLG51bGwpfSwKRzYoYSl7dmFyIHM9QS5oSyhhKS5naFkoKS5xKDAsIm9m -ZnNldCIpCnJldHVybiBzPT1udWxsP251bGw6QS5IcChzLG51bGwpfSwKaTYoYSl7cmV0dXJuIEEublco -dC5WLmEoYSkpfSwKblcoYSl7dmFyIHM9MCxyPUEuRlgodC56KSxxPTEscCxvLG4sbSxsLGssaixpLGgK -dmFyICRhc3luYyRpNj1BLmx6KGZ1bmN0aW9uKGIsYyl7aWYoYj09PTEpe3A9YwpzPXF9d2hpbGUodHJ1 -ZSlzd2l0Y2gocyl7Y2FzZSAwOmk9dC5oLmEoQS5xYyhhLmN1cnJlbnRUYXJnZXQpKS5nZXRBdHRyaWJ1 -dGUoImhyZWYiKQppLnRvU3RyaW5nCm89aQphLnByZXZlbnREZWZhdWx0KCkKcT0zCmk9ZG9jdW1lbnQK -bj1CLkNELnpRKGkucXVlcnlTZWxlY3RvcigiLmNvbnRlbnQiKS5zY3JvbGxUb3ApCnM9NgpyZXR1cm4g -QS5qUShBLnR5KG8sbnVsbCksJGFzeW5jJGk2KQpjYXNlIDY6az10LkYuYSh3aW5kb3cubG9jYXRpb24p -LnBhdGhuYW1lCmsudG9TdHJpbmcKcz03CnJldHVybiBBLmpRKEEuRzcoayxudWxsLG51bGwsITEsbnVs -bCksJGFzeW5jJGk2KQpjYXNlIDc6aS5ib2R5LmNsYXNzTGlzdC5hZGQoIm5lZWRzLXJlcnVuIikKaT1p -LnF1ZXJ5U2VsZWN0b3IoIi5jb250ZW50IikKaS50b1N0cmluZwppLnNjcm9sbFRvcD1CLmpuLnpRKG4p -CnE9MQpzPTUKYnJlYWsKY2FzZSAzOnE9MgpoPXAKbT1BLlJ1KGgpCmw9QS50cyhoKQpBLkMyKCJjb3Vs -ZG4ndCBhZGQvcmVtb3ZlIGhpbnQiLG0sbCkKcz01CmJyZWFrCmNhc2UgMjpzPTEKYnJlYWsKY2FzZSA1 -OnJldHVybiBBLnlDKG51bGwscikKY2FzZSAxOnJldHVybiBBLmYzKHAscil9fSkKcmV0dXJuIEEuREko -JGFzeW5jJGk2LHIpfSwKQzIoYSxiLGMpe3ZhciBzLHIscSxwLG8sbixtPSJleGNlcHRpb24iLGw9InN0 -YWNrVHJhY2UiCmlmKHQuaDYuYihiKSYmSi5STShiLnEoMCwic3VjY2VzcyIpLCExKSYmYi54NChtKSYm -Yi54NChsKSl7cz1KLlU2KGIpCnI9QS5rKHMucShiLG0pKQpjPXMucShiLGwpCnE9bnVsbH1lbHNlIGlm -KGIgaW5zdGFuY2VvZiBBLlFXKXtyPWIuYQpxPWIuYn1lbHNle3I9Si5ZUyhiKQpxPW51bGx9aWYocT09 -bnVsbClxPWMKcz1kb2N1bWVudApwPXMucXVlcnlTZWxlY3RvcigiLnBvcHVwLXBhbmUiKQpwLnF1ZXJ5 -U2VsZWN0b3IoImgyIikuaW5uZXJUZXh0PWEKbz1wLnF1ZXJ5U2VsZWN0b3IoInAiKQpvLnRvU3RyaW5n -CnIudG9TdHJpbmcKby5pbm5lclRleHQ9cgpvPXAucXVlcnlTZWxlY3RvcigicHJlIikKby50b1N0cmlu -ZwpvLmlubmVyVGV4dD1KLllTKHEpCm49dC5icS5hKHAucXVlcnlTZWxlY3RvcigiYS5ib3R0b20iKSkK -Qi54bi5zTFUobixBLlhkKCJodHRwcyIsImdpdGh1Yi5jb20iLCJkYXJ0LWxhbmcvc2RrL2lzc3Vlcy9u -ZXciLEEuRUYoWyJ0aXRsZSIsIkN1c3RvbWVyLXJlcG9ydGVkIGlzc3VlIHdpdGggbnVsbCBzYWZldHkg -bWlncmF0aW9uIHRvb2w6ICIrYSwibGFiZWxzIix1LmQsImJvZHkiLGErIlxuXG5FcnJvcjogIityKyJc -blxuUGxlYXNlIGZpbGwgaW4gdGhlIGZvbGxvd2luZzpcblxuKipOYW1lIG9mIHBhY2thZ2UgYmVpbmcg -bWlncmF0ZWQgKGlmIHB1YmxpYykqKjpcbioqV2hhdCBJIHdhcyBkb2luZyB3aGVuIHRoaXMgaXNzdWUg -b2NjdXJyZWQqKjpcbioqSXMgaXQgcG9zc2libGUgdG8gd29yayBhcm91bmQgdGhpcyBpc3N1ZSoqOlxu -KipIYXMgdGhpcyBpc3N1ZSBoYXBwZW5lZCBiZWZvcmUsIGFuZCBpZiBzbywgaG93IG9mdGVuKio6XG4q -KkRhcnQgU0RLIHZlcnNpb24qKjogIitBLmQocy5nZXRFbGVtZW50QnlJZCgic2RrLXZlcnNpb24iKS50 -ZXh0Q29udGVudCkrIlxuKipBZGRpdGlvbmFsIGRldGFpbHMqKjpcblxuVGhhbmtzIGZvciBmaWxpbmch -XG5cblN0YWNrdHJhY2U6IF9hdXRvIHBvcHVsYXRlZCBieSBtaWdyYXRpb24gcHJldmlldyB0b29sLl9c -blxuYGBgXG4iK0EuZChjKSsiXG5gYGBcbiJdLHQuTix0LnopKS5nbkQoKSkKcz1uLnN0eWxlCnMuZGlz -cGxheT0iaW5pdGlhbCIKcz1wLnN0eWxlCnMuZGlzcGxheT0iaW5pdGlhbCIKcz1BLmQoYikKd2luZG93 -CmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS5lcnJvcihhKyI6ICIr -cykKd2luZG93CnM9QS5kKGMpCmlmKHR5cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29u -c29sZS5lcnJvcihzKX0sCnQyKGEsYil7dmFyIHMscixxLHAsbz10LmguYShBLnFjKGEuY3VycmVudFRh -cmdldCkpCmEucHJldmVudERlZmF1bHQoKQpzPW8uZ2V0QXR0cmlidXRlKCJocmVmIikKcy50b1N0cmlu -ZwpyPUEuVXMocykKcT1BLkc2KHMpCnA9QS5hSyhzKQppZihxIT1udWxsKUEuYWYocixxLHAsYixuZXcg -QS5uVChyLHEscCkpCmVsc2UgQS5hZihyLG51bGwsbnVsbCxiLG5ldyBBLk5ZKHIpKX0sCkswKGEpe3Zh -ciBzLHIscSxwPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5wb3B1cC1wYW5lIikKcC5xdWVyeVNlbGVj -dG9yKCJoMiIpLmlubmVyVGV4dD0iRmFpbGVkIHRvIHJlcnVuIGZyb20gc291cmNlcyIKcC5xdWVyeVNl -bGVjdG9yKCJwIikuaW5uZXJUZXh0PSJTb3VyY2VzIGNvbnRhaW4gc3RhdGljIGFuYWx5c2lzIGVycm9y -czoiCnM9cC5xdWVyeVNlbGVjdG9yKCJwcmUiKQpzLnRvU3RyaW5nCnI9Si5FbChhLHQuZikKcT1BLkxo -KHIpCnMuaW5uZXJUZXh0PW5ldyBBLmxKKHIscS5DKCJxVShsRC5FKSIpLmEobmV3IEEudWUoKSkscS5D -KCJsSjxsRC5FLHFVPiIpKS5IKDAsIlxuIikKcT1wLnF1ZXJ5U2VsZWN0b3IoImEuYm90dG9tIikuc3R5 -bGUKcS5kaXNwbGF5PSJub25lIgpzPXAuc3R5bGUKcy5kaXNwbGF5PSJpbml0aWFsIn0sCkdnKCl7dmFy -IHM9ZG9jdW1lbnQscj10LmgKQS5EaChyLHIsIlQiLCJxdWVyeVNlbGVjdG9yQWxsIikKcz1uZXcgQS53 -eihzLnF1ZXJ5U2VsZWN0b3JBbGwoIi5jb2RlIiksdC5SKQpzLksocyxuZXcgQS5HSCgpKX0sCmhYKGEs -YixjKXtyZXR1cm4gQS5ZdyhhLGIsYyl9LApZdyhhLGIsYyl7dmFyIHM9MCxyPUEuRlgodC56KSxxPTEs -cCxvLG4sbSxsLGssaixpLGgsZwp2YXIgJGFzeW5jJGhYPUEubHooZnVuY3Rpb24oZCxlKXtpZihkPT09 -MSl7cD1lCnM9cX13aGlsZSh0cnVlKXN3aXRjaChzKXtjYXNlIDA6cT0zCms9dC5OCnM9NgpyZXR1cm4g -QS5qUShBLlE2KGEsQS5FRihbInJlZ2lvbiIsInJlZ2lvbiIsIm9mZnNldCIsQS5kKGIpXSxrLGspLHQu -RyksJGFzeW5jJGhYKQpjYXNlIDY6bz1lCms9bwpqPUouVTYoaykKaT10LmdxCm49bmV3IEEuZDIoQS5q -ZihpLmEoai5xKGssImVkaXRzIikpKSxBLmsoai5xKGssImV4cGxhbmF0aW9uIikpLEEuVWMoai5xKGss -ImxpbmUiKSksQS5rKGoucShrLCJkaXNwbGF5UGF0aCIpKSxBLmsoai5xKGssInVyaVBhdGgiKSksQS5O -ZChpLmEoai5xKGssInRyYWNlcyIpKSkpCkEuVDEobikKQS5GcihhLGIsYykKQS55WCgiLmVkaXQtcGFu -ZWwgLnBhbmVsLWNvbnRlbnQiLCExKQpxPTEKcz01CmJyZWFrCmNhc2UgMzpxPTIKZz1wCm09QS5SdShn -KQpsPUEudHMoZykKQS5DMigiY291bGRuJ3QgbG9hZCBlZGl0IGRldGFpbHMiLG0sbCkKcz01CmJyZWFr -CmNhc2UgMjpzPTEKYnJlYWsKY2FzZSA1OnJldHVybiBBLnlDKG51bGwscikKY2FzZSAxOnJldHVybiBB -LmYzKHAscil9fSkKcmV0dXJuIEEuREkoJGFzeW5jJGhYLHIpfSwKRzcoYSxiLGMsZCxlKXtyZXR1cm4g -QS5mcihhLGIsYyxkLGUpfSwKZnIoYSxiLGMsZCxlKXt2YXIgcz0wLHI9QS5GWCh0LkgpLHEscD0yLG8s -bixtLGwsayxqLGksaAp2YXIgJGFzeW5jJEc3PUEubHooZnVuY3Rpb24oZixnKXtpZihmPT09MSl7bz1n -CnM9cH13aGlsZSh0cnVlKXN3aXRjaChzKXtjYXNlIDA6aWYoIUIueEIuVGMoYSwiLmRhcnQiKSl7QS5C -RShhLEEud1IoKSxkKQpBLkJYKGEsbnVsbCkKaWYoZSE9bnVsbCllLiQwKCkKcz0xCmJyZWFrfXA9NApq -PXQuTgpzPTcKcmV0dXJuIEEualEoQS5RNihhLEEuRUYoWyJpbmxpbmUiLCJ0cnVlIl0saixqKSx0Lkcp -LCRhc3luYyRHNykKY2FzZSA3Om49ZwpBLkJFKGEsQS5ZZihuKSxkKQpBLmZHKGIsYykKbT1BLlVzKGEp -CkEuQlgobSxiKQppZihlIT1udWxsKWUuJDAoKQpwPTIKcz02CmJyZWFrCmNhc2UgNDpwPTMKaD1vCmw9 -QS5SdShoKQprPUEudHMoaCkKQS5DMigiY291bGRuJ3QgbG9hZCBkYXJ0IGZpbGUgIithLGwsaykKcz02 -CmJyZWFrCmNhc2UgMzpzPTIKYnJlYWsKY2FzZSA2OmNhc2UgMTpyZXR1cm4gQS55QyhxLHIpCmNhc2Ug -MjpyZXR1cm4gQS5mMyhvLHIpfX0pCnJldHVybiBBLkRJKCRhc3luYyRHNyxyKX0sCkdlKCl7dmFyIHM9 -MCxyPUEuRlgodC56KSxxPTEscCxvLG4sbSxsLGssaixpLGgKdmFyICRhc3luYyRHZT1BLmx6KGZ1bmN0 -aW9uKGEsYil7aWYoYT09PTEpe3A9YgpzPXF9d2hpbGUodHJ1ZSlzd2l0Y2gocyl7Y2FzZSAwOmk9Ii9f -cHJldmlldy9uYXZpZ2F0aW9uVHJlZS5qc29uIgpxPTMKcz02CnJldHVybiBBLmpRKEEuUTYoaSxCLkNN -LHQuVyksJGFzeW5jJEdlKQpjYXNlIDY6az1iCmsudG9TdHJpbmcKbz1rCms9ZG9jdW1lbnQucXVlcnlT -ZWxlY3RvcigiLm5hdi10cmVlIikKay50b1N0cmluZwpuPWsKSi5kcihuLCIiKQprPUEubUsobykKJC5J -Uj1rCkEudFgobixrLCEwKQpxPTEKcz01CmJyZWFrCmNhc2UgMzpxPTIKaD1wCm09QS5SdShoKQpsPUEu -dHMoaCkKQS5DMigiY291bGRuJ3QgbG9hZCBuYXZpZ2F0aW9uIHRyZWUiLG0sbCkKcz01CmJyZWFrCmNh -c2UgMjpzPTEKYnJlYWsKY2FzZSA1OnJldHVybiBBLnlDKG51bGwscikKY2FzZSAxOnJldHVybiBBLmYz -KHAscil9fSkKcmV0dXJuIEEuREkoJGFzeW5jJEdlLHIpfSwKcU8oYSl7dmFyIHMscixxPWEuZ2V0Qm91 -bmRpbmdDbGllbnRSZWN0KCkscD1CLkNELnpRKCQuZmkoKS5vZmZzZXRIZWlnaHQpLG89d2luZG93Lmlu -bmVySGVpZ2h0Cm8udG9TdHJpbmcKcz1CLkNELnpRKCQuRFcoKS5vZmZzZXRIZWlnaHQpCnI9cS5ib3R0 -b20Kci50b1N0cmluZwppZihyPm8tKHMrMTQpKUouZGgoYSkKZWxzZXtvPXEudG9wCm8udG9TdHJpbmcK -aWYobzxwKzE0KUouZGgoYSl9fSwKZkcoYSxiKXt2YXIgcyxyLHEscCxvCmlmKGEhPW51bGwpe3M9ZG9j -dW1lbnQKcj1zLmdldEVsZW1lbnRCeUlkKCJvIitBLmQoYSkpCnE9cy5xdWVyeVNlbGVjdG9yKCIubGlu -ZS0iK0EuZChiKSkKaWYociE9bnVsbCl7QS5xTyhyKQpKLmRSKHIpLmkoMCwidGFyZ2V0Iil9ZWxzZSBp -ZihxIT1udWxsKXtzPXEucGFyZW50RWxlbWVudApzLnRvU3RyaW5nCkEucU8ocyl9aWYocSE9bnVsbClK -LmRSKHQuaC5hKHEucGFyZW50Tm9kZSkpLmkoMCwiaGlnaGxpZ2h0Iil9ZWxzZXtzPWRvY3VtZW50CnA9 -dC5oCkEuRGgocCxwLCJUIiwicXVlcnlTZWxlY3RvckFsbCIpCnM9cy5xdWVyeVNlbGVjdG9yQWxsKCIu -bGluZS1ubyIpCm89bmV3IEEud3oocyx0LlIpCmlmKG8uZ2sobyk9PT0wKXJldHVybgpBLnFPKHAuYShC -LnQ1Lmd0SChzKSkpfX0sCmFmKGEsYixjLGQsZSl7dmFyIHMscixxLHA9dC5GLG89QS5HNihwLmEod2lu -ZG93LmxvY2F0aW9uKS5ocmVmKSxuPUEuYUsocC5hKHdpbmRvdy5sb2NhdGlvbikuaHJlZikKaWYobyE9 -bnVsbCl7cz1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgibyIrQS5kKG8pKQppZihzIT1udWxsKUouZFIo -cykuUigwLCJ0YXJnZXQiKX1pZihuIT1udWxsKXtyPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIi5saW5l -LSIrQS5kKG4pKQppZihyIT1udWxsKXtxPXIucGFyZW50RWxlbWVudApxLnRvU3RyaW5nCkouZFIocSku -UigwLCJoaWdobGlnaHQiKX19aWYoYT09cC5hKHdpbmRvdy5sb2NhdGlvbikucGF0aG5hbWUpe0EuZkco -YixjKQplLiQwKCl9ZWxzZXthLnRvU3RyaW5nCkEuRzcoYSxiLGMsZCxlKX19LApRNChhLGIpe3ZhciBz -PUEuaEsoYSkscj1zLmdoWSgpLHE9QS5MNShudWxsLG51bGwsdC5OLHQuZGspCnEuRlYoMCxyKQpxLkZW -KDAsYikKcS5ZNSgwLCJhdXRoVG9rZW4iLCQuVUUoKSkKcmV0dXJuIHMubm0oMCxxKS5nbkQoKX0sClQx -KGEpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGk9JC5oTCgpCmkudG9TdHJpbmcKSi5kcihpLCIiKQpp -ZihhPT1udWxsKXtzPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInAiKQpCLkx0LnNhNChzLCJTZWUgZGV0 -YWlscyBhYm91dCBhIHByb3Bvc2VkIGVkaXQuIikKQi5MdC5zUChzLEEuUUkoWyJwbGFjZWhvbGRlciJd -LHQucykpCmkuYXBwZW5kQ2hpbGQocykKQi5MdC5GRihzKQpyZXR1cm59cj1hLmQKci50b1N0cmluZwpx -PSQublUoKQpwPXEuemYocikKbz1hLmIKbj1kb2N1bWVudAptPW4ucXVlcnlTZWxlY3RvcigiLnJvb3Qi -KS50ZXh0Q29udGVudAptLnRvU3RyaW5nCmw9cS5IUChyLEIueEIuYlMobSkpCms9YS5jCmo9bi5jcmVh -dGVFbGVtZW50KCJwIikKaS5hcHBlbmRDaGlsZChqKQpqLmFwcGVuZENoaWxkKG4uY3JlYXRlVGV4dE5v -ZGUoQS5kKG8pKyIgYXQgIikpCnI9YS5lCnIudG9TdHJpbmcKcT10Lk4KcT1BLko2KEEuUTQocixBLkVG -KFsibGluZSIsSi5ZUyhrKV0scSxxKSkpCnEuYXBwZW5kQ2hpbGQobi5jcmVhdGVUZXh0Tm9kZShsKyI6 -IitBLmQoaykrIi4iKSkKai5hcHBlbmRDaGlsZChxKQpKLmRoKGopCkEuQ0MoYSxpLHApCkEuRnooYSxp -KX0sCkxIKGExLGEyLGEzKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZCxjLGIsYSxh -MD0kLnlQKCkKYTAudG9TdHJpbmcKSi5kcihhMCwiIikKcz1hMi5nayhhMikKaWYoczwyKSQuRFooKS5p -bm5lclRleHQ9IlByb3Bvc2VkIEVkaXRzIgplbHNle3I9YTIuZ1B1KGEyKS5OMCgwLDAsbmV3IEEuRUUo -KSx0LlMpCiQuRFooKS5pbm5lclRleHQ9QS5kKHIpKyIgUHJvcG9zZWQgRWRpdHMifWlmKHM9PT0wKXtx -PWRvY3VtZW50CnA9cS5jcmVhdGVFbGVtZW50KCJwIikKYTAuYXBwZW5kQ2hpbGQocCkKcC5hcHBlbmRD -aGlsZChxLmNyZWF0ZVRleHROb2RlKCJObyBwcm9wb3NlZCBlZGl0cyIpKX1lbHNlIGZvcihhMD1hMi5n -UHUoYTIpLGEwPWEwLmdNKGEwKSxxPXQuRixvPXQuTixuPXQuUSxtPW4uQygifigxKT8iKSxsPXQuWixu -PW4uYzthMC5HKCk7KXtrPWEwLmdsKCkKaj1kb2N1bWVudApwPWouY3JlYXRlRWxlbWVudCgicCIpCmk9 -JC55UCgpCmkuYXBwZW5kQ2hpbGQocCkKcC5hcHBlbmRDaGlsZChqLmNyZWF0ZVRleHROb2RlKEEuZChr -LmEpKyI6IikpCmg9ai5jcmVhdGVFbGVtZW50KCJ1bCIpCmkuYXBwZW5kQ2hpbGQoaCkKZm9yKGs9Si5J -VChrLmIpO2suRygpOyl7aT1rLmdsKCkKZz1qLmNyZWF0ZUVsZW1lbnQoImxpIikKaC5hcHBlbmRDaGls -ZChnKQpKLmRSKGcpLmkoMCwiZWRpdCIpCmY9ai5jcmVhdGVFbGVtZW50KCJhIikKZy5hcHBlbmRDaGls -ZChmKQpmLmNsYXNzTGlzdC5hZGQoImVkaXQtbGluayIpCmU9aS5jCmQ9QS5kKGUpCmYuc2V0QXR0cmli -dXRlKCJkYXRhLSIrbmV3IEEuU3kobmV3IEEuaTcoZikpLk9VKCJvZmZzZXQiKSxkKQpjPWkuYQpiPUEu -ZChjKQpmLnNldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBBLlN5KG5ldyBBLmk3KGYpKS5PVSgibGluZSIp -LGIpCmYuYXBwZW5kQ2hpbGQoai5jcmVhdGVUZXh0Tm9kZSgibGluZSAiK2IpKQphPXEuYSh3aW5kb3cu -bG9jYXRpb24pLnBhdGhuYW1lCmEudG9TdHJpbmcKZi5zZXRBdHRyaWJ1dGUoImhyZWYiLEEuUTQoYSxB -LkVGKFsibGluZSIsYiwib2Zmc2V0IixkXSxvLG8pKSkKZD1tLmEobmV3IEEuUE4oZSxjLGExKSkKbC5h -KG51bGwpCkEuSkUoZiwiY2xpY2siLGQsITEsbikKZy5hcHBlbmRDaGlsZChqLmNyZWF0ZVRleHROb2Rl -KCI6ICIrQS5kKGkuYikpKX19aWYoYTMpQS5UMShudWxsKX0sCkZyKGEsYixjKXt2YXIgcyxyLHE9QS5o -SyhCLkV4LmdEcih0LkYuYSh3aW5kb3cubG9jYXRpb24pKStBLmQoYSkpLHA9QS5GbCh0Lk4sdC5kaykK -aWYoYiE9bnVsbClwLlk1KDAsIm9mZnNldCIsQS5kKGIpKQppZihjIT1udWxsKXAuWTUoMCwibGluZSIs -QS5kKGMpKQpwLlk1KDAsImF1dGhUb2tlbiIsJC5VRSgpKQpxPXEubm0oMCxwKQpwPXdpbmRvdy5oaXN0 -b3J5CnM9dC56CnI9cS5nbkQoKQpwLnB1c2hTdGF0ZShuZXcgQS5CZihbXSxbXSkuUHYoQS5GbChzLHMp -KSwiIixyKX0sCkVuKGEpe3ZhciBzLHI9ZG9jdW1lbnQucXVlcnlTZWxlY3RvcigiLnJvb3QiKS50ZXh0 -Q29udGVudApyLnRvU3RyaW5nCnM9cisiLyIKaWYoQi54Qi5uQyhhLHMpKXJldHVybiBCLnhCLnluKGEs -cy5sZW5ndGgpCmVsc2UgcmV0dXJuIGF9LApPdChhKXtzd2l0Y2goYS5yKXtjYXNlIEIuY3c6YnJlYWsK -Y2FzZSBCLldEOmEucj1CLlhqCmJyZWFrCmNhc2UgQi5YajphLnI9Qi5XRApicmVhawpjYXNlIEIuZGM6 -dGhyb3cgQS5iKEEuUFYoIkZpbGUgIitBLmQoYS5jKSsiIHNob3VsZCBub3QgaGF2ZSBpbmRldGVybWlu -YXRlIG1pZ3JhdGlvbiBzdGF0dXMiKSkKZGVmYXVsdDpicmVha319LAp0YShhLGIpe3ZhciBzLHI9ImNo -ZWNrX2JveCIscT0idGl0bGUiLHA9Im9wdGVkLW91dCIsbz0ibWlncmF0aW5nIgpzd2l0Y2goYil7Y2Fz -ZSBCLmN3OmEuaW5uZXJUZXh0PXIKcz1KLllFKGEpCnMuZ1AoYSkuaSgwLCJhbHJlYWR5LW1pZ3JhdGVk -IikKcy5nUChhKS5pKDAsImRpc2FibGVkIikKYS5zZXRBdHRyaWJ1dGUocSwiQWxyZWFkeSBtaWdyYXRl -ZCIpCmJyZWFrCmNhc2UgQi5XRDphLmlubmVyVGV4dD1yCnM9Si5ZRShhKQpzLmdQKGEpLlIoMCxwKQpz -LmdQKGEpLmkoMCxvKQphLnNldEF0dHJpYnV0ZShxLCJNaWdyYXRpbmcgdG8gbnVsbCBzYWZldHkiKQpi -cmVhawpjYXNlIEIuWGo6YS5pbm5lclRleHQ9ImNoZWNrX2JveF9vdXRsaW5lX2JsYW5rIgpzPUouWUUo -YSkKcy5nUChhKS5SKDAsbykKcy5nUChhKS5pKDAscCkKYS5zZXRBdHRyaWJ1dGUocSwiT3B0aW5nIG91 -dCBvZiBudWxsIHNhZmV0eSIpCmJyZWFrCmRlZmF1bHQ6YS5pbm5lclRleHQ9ImluZGV0ZXJtaW5hdGVf -Y2hlY2tfYm94IgpzPUouWUUoYSkKcy5nUChhKS5SKDAsbykKcy5nUChhKS5pKDAscCkKYS5zZXRBdHRy -aWJ1dGUocSwiTWl4ZWQgc3RhdHVzZXMgb2YgJ21pZ3JhdGluZycgYW5kICdvcHRpbmcgb3V0JyIpCmJy -ZWFrfX0sCnhuKGEsYil7dmFyIHMscj0iZGlzYWJsZWQiLHE9Yi5nTCgpCkEudGEoYSxxKQppZihiLmM9 -PT0kLkQ5KCkuaW5uZXJUZXh0KXtpZihiIGluc3RhbmNlb2YgQS5jRCl7cz1iLncKcy50b1N0cmluZwpz -PSFzfWVsc2Ugcz0hMQppZihzKXthLnRvU3RyaW5nCkouZFIoYSkuaSgwLHIpfWVsc2V7YS50b1N0cmlu -ZwpKLmRSKGEpLlIoMCxyKX1BLnRhKCQuYzAoKSxxKX19LApCWChhLGIpe3ZhciBzLHIscSxwPXt9CnAu -YT1hCmE9QS5FbihhKQpwLmE9YQpzPSQuRDkoKQpzLnRvU3RyaW5nCkouZHIocyxhKQpzPWRvY3VtZW50 -CnI9dC5oCkEuRGgocixyLCJUIiwicXVlcnlTZWxlY3RvckFsbCIpCnM9bmV3IEEud3oocy5xdWVyeVNl -bGVjdG9yQWxsKCIubmF2LXBhbmVsIC5uYXYtbGluayIpLHQuUikKcy5LKHMsbmV3IEEuVlMocCkpCnM9 -JC5JUgpxPXM9PW51bGw/bnVsbDpBLnl3KHMscC5hKQppZihxPT1udWxsKXtwPSQuYk4oKQpwLnRvU3Ry -aW5nCkouZFIocCkuUigwLCJ2aXNpYmxlIil9ZWxzZXtwPSQuYk4oKQpwLnRvU3RyaW5nCkouZFIocCku -aSgwLCJ2aXNpYmxlIikKQS50YSgkLmMwKCkscS5nTCgpKX19LApBUihhLGIpe3ZhciBzLHIscT1iLmIK -cT09PSQmJkEueWsoInBhcmVudCIpCnM9dC5oCnI9cy5hKHMuYShhLnBhcmVudE5vZGUpLnBhcmVudE5v -ZGUpCkEueG4oci5xdWVyeVNlbGVjdG9yKCI6c2NvcGUgPiAuc3RhdHVzLWljb24iKSxxKQpBLkFSKHIs -cSl9LApiTChhLGIpe3ZhciBzLHIscSxwLG8sbixtLGw9IjpzY29wZSA+IC5zdGF0dXMtaWNvbiIKZm9y -KHM9Yi5kLHI9cy5sZW5ndGgscT10LmgscD0wO3A8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxBLmxr -KShzKSwrK3Ape289c1twXQphLnRvU3RyaW5nCm49by5jCm4udG9TdHJpbmcKbT1hLnF1ZXJ5U2VsZWN0 -b3IoJ1tkYXRhLW5hbWUqPSInK0EuTGoobikrJyJdJykKaWYobyBpbnN0YW5jZW9mIEEudnQpe0EuYkwo -bSxvKQpBLnhuKG0ucXVlcnlTZWxlY3RvcihsKSxiKX1lbHNlIEEueG4ocS5hKG0ucGFyZW50Tm9kZSku -cXVlcnlTZWxlY3RvcihsKSxvKX19LApCRShhLGIsYyl7dmFyIHM9Ii5yZWdpb25zIixyPWRvY3VtZW50 -LHE9ci5xdWVyeVNlbGVjdG9yKHMpCnEudG9TdHJpbmcKcj1yLnF1ZXJ5U2VsZWN0b3IoIi5jb2RlIikK -ci50b1N0cmluZwpKLnRIKHEsYi5hLCQuS0coKSkKSi50SChyLGIuYiwkLktHKCkpCkEuTEgoYSxiLmQs -YykKaWYoYi5jLmxlbmd0aDwyZTUpQS5HZygpCkEueVgoIi5jb2RlIiwhMCkKQS55WChzLCEwKX0sCnRY -KGEwLGExLGEyKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGgsZyxmLGUsZD0ibWF0ZXJpYWwtaWNv -bnMiLGM9InN0YXR1cy1pY29uIixiPWRvY3VtZW50LGE9Yi5jcmVhdGVFbGVtZW50KCJ1bCIpCmEwLmFw -cGVuZENoaWxkKGEpCmZvcihzPWExLmxlbmd0aCxyPXQuTixxPXQuWixwPTA7cDxhMS5sZW5ndGg7YTEu -bGVuZ3RoPT09c3x8KDAsQS5saykoYTEpLCsrcCl7bz1hMVtwXQpuPWIuY3JlYXRlRWxlbWVudCgibGki -KQphLmFwcGVuZENoaWxkKG4pCmlmKG8gaW5zdGFuY2VvZiBBLnZ0KXttPUouWUUobikKbS5nUChuKS5p -KDAsImRpciIpCmw9by5jCmwudG9TdHJpbmcKbi5zZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgQS5TeShu -ZXcgQS5pNyhuKSkuT1UoIm5hbWUiKSxsKQprPWIuY3JlYXRlRWxlbWVudCgic3BhbiIpCm4uYXBwZW5k -Q2hpbGQoaykKbD1KLllFKGspCmwuZ1AoaykuaSgwLCJhcnJvdyIpCmwuc2E0KGssIlx1MjViYyIpCmo9 -Yi5jcmVhdGVFbGVtZW50KCJzcGFuIikKSi5kUihqKS5pKDAsZCkKai5pbm5lclRleHQ9ImZvbGRlcl9v -cGVuIgpuLmFwcGVuZENoaWxkKGopCmw9by5hCmwudG9TdHJpbmcKbi5hcHBlbmRDaGlsZChiLmNyZWF0 -ZVRleHROb2RlKGwpKQpsPW8uZApsLnRvU3RyaW5nCkEudFgobixsLCEwKQppPWIuY3JlYXRlRWxlbWVu -dCgic3BhbiIpCmw9Si5ZRShpKQpsLmdQKGkpLmkoMCxkKQppLmlubmVyVGV4dD0iaW5kZXRlcm1pbmF0 -ZV9jaGVja19ib3giCmwuZ1AoaSkuaSgwLGMpCkEueG4oaSxvKQpsPWwuZ1ZsKGkpCmg9bC4kdGkKZz1o -LkMoIn4oMSk/IikuYShuZXcgQS5URChvLG4saSkpCnEuYShudWxsKQpBLkpFKGwuYSxsLmIsZywhMSxo -LmMpCm0ubUsobixpLGopCkEua3ooayxvLmdMKCk9PT1CLmN3KX1lbHNlIGlmKG8gaW5zdGFuY2VvZiBB -LmNEKXtpPWIuY3JlYXRlRWxlbWVudCgic3BhbiIpCm09Si5ZRShpKQptLmdQKGkpLmkoMCxkKQppLmlu -bmVyVGV4dD0iIgptLmdQKGkpLmkoMCxjKQpsPW8udwpsLnRvU3RyaW5nCmlmKCFsKW0uZ1AoaSkuaSgw -LCJkaXNhYmxlZCIpCkEueG4oaSxvKQppZihsKXttPW0uZ1ZsKGkpCmw9bS4kdGkKaD1sLkMoIn4oMSk/ -IikuYShuZXcgQS5JZihvLGksbikpCnEuYShudWxsKQpBLkpFKG0uYSxtLmIsaCwhMSxsLmMpfW4uYXBw -ZW5kQ2hpbGQoaSkKbT1iLmNyZWF0ZUVsZW1lbnQoInNwYW4iKQpKLmRSKG0pLmkoMCxkKQptLmlubmVy -VGV4dD0iaW5zZXJ0X2RyaXZlX2ZpbGUiCm4uYXBwZW5kQ2hpbGQobSkKZj1iLmNyZWF0ZUVsZW1lbnQo -ImEiKQpuLmFwcGVuZENoaWxkKGYpCm09Si5ZRShmKQptLmdQKGYpLmkoMCwibmF2LWxpbmsiKQpsPW8u -YwpsLnRvU3RyaW5nCmYuc2V0QXR0cmlidXRlKCJkYXRhLSIrbmV3IEEuU3kobmV3IEEuaTcoZikpLk9V -KCJuYW1lIiksbCkKbD1vLmQKbC50b1N0cmluZwpmLnNldEF0dHJpYnV0ZSgiaHJlZiIsQS5RNChsLEEu -RmwocixyKSkpCmw9by5hCmwudG9TdHJpbmcKZi5hcHBlbmRDaGlsZChiLmNyZWF0ZVRleHROb2RlKGwp -KQptPW0uZ1ZsKGYpCmw9bS4kdGkKaD1sLkMoIn4oMSk/IikuYShuZXcgQS50QigpKQpxLmEobnVsbCkK -QS5KRShtLmEsbS5iLGgsITEsbC5jKQpsPW8uZQpsLnRvU3RyaW5nCmlmKGw+MCl7ZT1iLmNyZWF0ZUVs -ZW1lbnQoInNwYW4iKQpuLmFwcGVuZENoaWxkKGUpCkouZFIoZSkuaSgwLCJlZGl0LWNvdW50IikKaWYo -bD09PTEpbT0icHJvcG9zZWQgZWRpdCIKZWxzZSBtPSJwcm9wb3NlZCBlZGl0cyIKZS5zZXRBdHRyaWJ1 -dGUoInRpdGxlIiwiIitsKyIgIittKQplLmFwcGVuZENoaWxkKGIuY3JlYXRlVGV4dE5vZGUoQi5qblsi -WyJdKGwpKSl9fX19LAp1eihhLGIsYyl7dmFyIHM9ZG9jdW1lbnQscj1zLmNyZWF0ZUVsZW1lbnQoImJ1 -dHRvbiIpLHE9dC5RLHA9cS5DKCJ+KDEpPyIpLmEobmV3IEEubTIoYSxjKSkKdC5aLmEobnVsbCkKQS5K -RShyLCJjbGljayIscCwhMSxxLmMpCnE9QS5PWChhLmEpCnEudG9TdHJpbmcKci5hcHBlbmRDaGlsZChz -LmNyZWF0ZVRleHROb2RlKHEpKQpiLmFwcGVuZENoaWxkKHIpfSwKRnooYSxiKXt2YXIgcyxyLHEscCxv -LG4sbSxsLGssaixpLGg9YS5hCmlmKGg9PW51bGwpcmV0dXJuCmIudG9TdHJpbmcKcz1kb2N1bWVudApy -PXMuY3JlYXRlRWxlbWVudCgicCIpCnE9Yi5hcHBlbmRDaGlsZChyKQpyPXMuY3JlYXRlRWxlbWVudCgi -c3BhbiIpCnA9dC5zCkouTXUocixBLlFJKFsidHlwZS1kZXNjcmlwdGlvbiJdLHApKQpyLmFwcGVuZENo -aWxkKHMuY3JlYXRlVGV4dE5vZGUoIkFjdGlvbnMiKSkKcS5hcHBlbmRDaGlsZChyKQpxLmFwcGVuZENo -aWxkKHMuY3JlYXRlVGV4dE5vZGUoIjoiKSkKbz1zLmNyZWF0ZUVsZW1lbnQoInAiKQpiLmFwcGVuZENo -aWxkKG8pCmZvcihyPWgubGVuZ3RoLG49dC5PLG09MDttPGgubGVuZ3RoO2gubGVuZ3RoPT09cnx8KDAs -QS5saykoaCksKyttKXtsPWhbbV0Kaz1zLmNyZWF0ZUVsZW1lbnQoImEiKQpvLmFwcGVuZENoaWxkKGsp -Cmo9bC5hCmoudG9TdHJpbmcKay5hcHBlbmRDaGlsZChzLmNyZWF0ZVRleHROb2RlKGopKQpqPWwuYgpq -LnRvU3RyaW5nCmsuc2V0QXR0cmlidXRlKCJocmVmIixqKQpqPW4uYShBLlFJKFsiYWRkLWhpbnQtbGlu -ayIsImJlZm9yZS1hcHBseSIsImJ1dHRvbiJdLHApKQppPUouZFIoaykKaS5WMSgwKQppLkZWKDAsail9 -fSwKQ0MoYTQsYTUsYTYpe3ZhciBzLHIscSxwLG8sbixtLGwsayxqLGksaCxnLGYsZSxkLGMsYixhLGEw -LGExLGEyLGEzCmZvcihzPWE0LmYscj1zLmxlbmd0aCxxPXQucyxwPXQuTyxvPTA7bzxzLmxlbmd0aDtz -Lmxlbmd0aD09PXJ8fCgwLEEubGspKHMpLCsrbyl7bj1zW29dCmE1LnRvU3RyaW5nCm09ZG9jdW1lbnQK -bD1tLmNyZWF0ZUVsZW1lbnQoInAiKQprPXAuYShBLlFJKFsidHJhY2UiXSxxKSkKaj1KLmRSKGwpCmou -VjEoMCkKai5GVigwLGspCmk9YTUuYXBwZW5kQ2hpbGQobCkKbD1tLmNyZWF0ZUVsZW1lbnQoInNwYW4i -KQprPXAuYShBLlFJKFsidHlwZS1kZXNjcmlwdGlvbiJdLHEpKQpqPUouZFIobCkKai5WMSgwKQpqLkZW -KDAsaykKaz1uLmEKay50b1N0cmluZwpsLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUoaykpCmku -YXBwZW5kQ2hpbGQobCkKaS5hcHBlbmRDaGlsZChtLmNyZWF0ZVRleHROb2RlKCI6IikpCmw9bS5jcmVh -dGVFbGVtZW50KCJ1bCIpCms9cC5hKEEuUUkoWyJ0cmFjZSJdLHEpKQpqPUouZFIobCkKai5WMSgwKQpq -LkZWKDAsaykKaD1pLmFwcGVuZENoaWxkKGwpCmZvcihsPW4uYixrPWwubGVuZ3RoLGc9MDtnPGwubGVu -Z3RoO2wubGVuZ3RoPT09a3x8KDAsQS5saykobCksKytnKXtmPWxbZ10KZT1tLmNyZWF0ZUVsZW1lbnQo -ImxpIikKaC5hcHBlbmRDaGlsZChlKQpkPW0uY3JlYXRlRWxlbWVudCgic3BhbiIpCmM9cC5hKEEuUUko -WyJmdW5jdGlvbiJdLHEpKQpqPUouZFIoZCkKai5WMSgwKQpqLkZWKDAsYykKYz1mLmIKQS5XaihkLGM9 -PW51bGw/InVua25vd24iOmMpCmUuYXBwZW5kQ2hpbGQoZCkKYj1mLmMKaWYoYiE9bnVsbCl7ZS5hcHBl -bmRDaGlsZChtLmNyZWF0ZVRleHROb2RlKCIgKCIpKQphPWIuYgphMD1tLmNyZWF0ZUVsZW1lbnQoImEi -KQphMC5hcHBlbmRDaGlsZChtLmNyZWF0ZVRleHROb2RlKEEuZChiLmMpKyI6IitBLmQoYSkpKQpkPWIu -YQpkLnRvU3RyaW5nCmEwLnNldEF0dHJpYnV0ZSgiaHJlZiIsZCkKYTAuY2xhc3NMaXN0LmFkZCgibmF2 -LWxpbmsiKQplLmFwcGVuZENoaWxkKGEwKQplLmFwcGVuZENoaWxkKG0uY3JlYXRlVGV4dE5vZGUoIiki -KSl9ZS5hcHBlbmRDaGlsZChtLmNyZWF0ZVRleHROb2RlKCI6ICIpKQpkPWYuYQpBLldqKGUsZD09bnVs -bD8idW5rbm93biI6ZCkKZD1mLmQKaWYoZC5sZW5ndGghPT0wKXtjPW0uY3JlYXRlRWxlbWVudCgicCIp -CmExPXAuYShBLlFJKFsiZHJhd2VyIiwiYmVmb3JlLWFwcGx5Il0scSkpCmo9Si5kUihjKQpqLlYxKDAp -CmouRlYoMCxhMSkKYTI9ZS5hcHBlbmRDaGlsZChjKQpmb3IoYz1kLmxlbmd0aCxhMz0wO2EzPGQubGVu -Z3RoO2QubGVuZ3RoPT09Y3x8KDAsQS5saykoZCksKythMylBLnV6KGRbYTNdLGEyLGIpfX19fSwKVXMo -YSl7cmV0dXJuIEIueEIudGcoYSwiPyIpP0IueEIuTmooYSwwLEIueEIuT1koYSwiPyIpKTphfSwKVEco -YSxiKXtyZXR1cm4gbmV3IEEuUVcoYSxiKX0sCnl3KGEsYil7dmFyIHMscixxLHAsbwpmb3Iocz1hLmxl -bmd0aCxyPTA7cjxhLmxlbmd0aDthLmxlbmd0aD09PXN8fCgwLEEubGspKGEpLCsrcil7cT1hW3JdCmlm -KHEgaW5zdGFuY2VvZiBBLnZ0KXtwPXEuZApwLnRvU3RyaW5nCm89QS55dyhwLGIpCmlmKG8hPW51bGwp -cmV0dXJuIG99ZWxzZSBpZihxLmM9PT1iKXJldHVybiBxfXJldHVybiBudWxsfSwKV2ooYSxiKXt2YXIg -cyxyLHEscD1BLlFJKGIuc3BsaXQoIi4iKSx0LnMpLG89Qi5ObS5ndEgocCksbj1kb2N1bWVudAphLmFw -cGVuZENoaWxkKG4uY3JlYXRlVGV4dE5vZGUobykpCmZvcihvPUEucUMocCwxLG51bGwsdC5OKSxzPW8u -JHRpLG89bmV3IEEuYTcobyxvLmdrKG8pLHMuQygiYTc8YUwuRT4iKSkscj1KLllFKGEpLHM9cy5DKCJh -TC5FIik7by5HKCk7KXtxPW8uZAppZihxPT1udWxsKXE9cy5hKHEpCnIubnooYSwiYmVmb3JlZW5kIiwi -JiM4MjAzOy4iLG51bGwsbnVsbCkKYS5hcHBlbmRDaGlsZChuLmNyZWF0ZVRleHROb2RlKHEpKX19LApl -OmZ1bmN0aW9uIGUoKXt9LApWVzpmdW5jdGlvbiBWVyhhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhp -cy5jPWN9LApvWjpmdW5jdGlvbiBvWigpe30sCmpyOmZ1bmN0aW9uIGpyKCl7fSwKcWw6ZnVuY3Rpb24g -cWwoKXt9LAp5ODpmdW5jdGlvbiB5OCgpe30sCkhpOmZ1bmN0aW9uIEhpKCl7fSwKQlQ6ZnVuY3Rpb24g -QlQoKXt9LApQWTpmdW5jdGlvbiBQWSgpe30sCkw6ZnVuY3Rpb24gTCgpe30sCkxaOmZ1bmN0aW9uIExa -KGEsYil7dGhpcy5hPWEKdGhpcy5iPWJ9LApwMTpmdW5jdGlvbiBwMShhLGIpe3RoaXMuYT1hCnRoaXMu -Yj1ifSwKV3g6ZnVuY3Rpb24gV3goYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKSG86 -ZnVuY3Rpb24gSG8oYSl7dGhpcy5hPWF9LApJQzpmdW5jdGlvbiBJQyhhLGIpe3RoaXMuYT1hCnRoaXMu -Yj1ifSwKZkM6ZnVuY3Rpb24gZkMoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sClRtOmZ1bmN0aW9uIFRt -KCl7fSwKblQ6ZnVuY3Rpb24gblQoYSxiLGMpe3RoaXMuYT1hCnRoaXMuYj1iCnRoaXMuYz1jfSwKTlk6 -ZnVuY3Rpb24gTlkoYSl7dGhpcy5hPWF9LAp1ZTpmdW5jdGlvbiB1ZSgpe30sCkdIOmZ1bmN0aW9uIEdI -KCl7fSwKRUU6ZnVuY3Rpb24gRUUoKXt9LApQTjpmdW5jdGlvbiBQTihhLGIsYyl7dGhpcy5hPWEKdGhp -cy5iPWIKdGhpcy5jPWN9LApRTDpmdW5jdGlvbiBRTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKVlM6 -ZnVuY3Rpb24gVlMoYSl7dGhpcy5hPWF9LApURDpmdW5jdGlvbiBURChhLGIsYyl7dGhpcy5hPWEKdGhp -cy5iPWIKdGhpcy5jPWN9LApJZjpmdW5jdGlvbiBJZihhLGIsYyl7dGhpcy5hPWEKdGhpcy5iPWIKdGhp -cy5jPWN9LAp0QjpmdW5jdGlvbiB0Qigpe30sCm0yOmZ1bmN0aW9uIG0yKGEsYil7dGhpcy5hPWEKdGhp -cy5iPWJ9LApRVzpmdW5jdGlvbiBRVyhhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwKWEE6ZnVuY3Rpb24g -WEEoKXt9LApacyhhKXt2YXIgcyxyLHE9Si5VNihhKQppZihBLnAyKEEuayhxLnEoYSwidHlwZSIpKSk9 -PT1CLlkyKXtzPUEuayhxLnEoYSwibmFtZSIpKQpyPUEuayhxLnEoYSwicGF0aCIpKQpxPXEucShhLCJz -dWJ0cmVlIikKcT1uZXcgQS52dChxPT1udWxsP251bGw6QS5tSyh0LlcuYShxKSkscyxyKQpxLkxWKCkK -cmV0dXJuIHF9ZWxzZXtzPUEuayhxLnEoYSwibmFtZSIpKQpyPUEuayhxLnEoYSwicGF0aCIpKQpyZXR1 -cm4gbmV3IEEuY0QoQS5rKHEucShhLCJocmVmIikpLEEuVWMocS5xKGEsImVkaXRDb3VudCIpKSxBLk00 -KHEucShhLCJ3YXNFeHBsaWNpdGx5T3B0ZWRPdXQiKSksQS52QihBLlVjKHEucShhLCJtaWdyYXRpb25T -dGF0dXMiKSkpLEEuTTQocS5xKGEsIm1pZ3JhdGlvblN0YXR1c0NhbkJlQ2hhbmdlZCIpKSxzLHIpfX0s -Cm1LKGEpe3ZhciBzLHI9QS5RSShbXSx0LmZoKQpmb3Iocz1KLklUKGEpO3MuRygpOylyLnB1c2goQS5a -cyhzLmdsKCkpKQpyZXR1cm4gcn0sClZEKGEpe3ZhciBzLHIscT1BLlFJKFtdLHQuZCkKZm9yKHM9YS5s -ZW5ndGgscj0wO3I8YS5sZW5ndGg7YS5sZW5ndGg9PT1zfHwoMCxBLmxrKShhKSwrK3IpcS5wdXNoKGFb -cl0uTHQoKSkKcmV0dXJuIHF9LAp2QihhKXtpZihhPT1udWxsKXJldHVybiBudWxsCmlmKGE+Pj4wIT09 -YXx8YT49NClyZXR1cm4gQS5PSChCLldHLGEpCnJldHVybiBCLldHW2FdfSwKcDIoYSl7c3dpdGNoKGEp -e2Nhc2UiZGlyZWN0b3J5IjpyZXR1cm4gQi5ZMgpjYXNlImZpbGUiOnJldHVybiBCLnJmCmRlZmF1bHQ6 -dGhyb3cgQS5iKEEuUFYoIlVucmVjb2duaXplZCBuYXZpZ2F0aW9uIHRyZWUgbm9kZSB0eXBlOiAiK0Eu -ZChhKSkpfX0sCnZ0OmZ1bmN0aW9uIHZ0KGEsYixjKXt2YXIgXz10aGlzCl8uZD1hCl8uYT1iCl8uYj0k -Cl8uYz1jfSwKY0Q6ZnVuY3Rpb24gY0QoYSxiLGMsZCxlLGYsZyl7dmFyIF89dGhpcwpfLmQ9YQpfLmU9 -YgpfLmY9YwpfLnI9ZApfLnc9ZQpfLmE9ZgpfLmI9JApfLmM9Z30sCkQ4OmZ1bmN0aW9uIEQ4KCl7fSwK -Tzk6ZnVuY3Rpb24gTzkoYSxiKXt0aGlzLmE9YQp0aGlzLmI9Yn0sCkdiOmZ1bmN0aW9uIEdiKGEsYil7 -dGhpcy5hPWEKdGhpcy5iPWJ9LApueihhKXt2YXIgcz1BLlVjKGEucSgwLCJub2RlSWQiKSkKcmV0dXJu -IG5ldyBBLkxMKEIuTm0uSHQoQi5NbSxuZXcgQS5NRChhKSkscyl9LApPWChhKXtzd2l0Y2goYS5hKXtj -YXNlIDA6cmV0dXJuIkFkZCAvKj8qLyBoaW50IgpjYXNlIDE6cmV0dXJuIkFkZCAvKiEqLyBoaW50Igpj -YXNlIDQ6cmV0dXJuIlJlbW92ZSAvKj8qLyBoaW50IgpjYXNlIDU6cmV0dXJuIlJlbW92ZSAvKiEqLyBo -aW50IgpjYXNlIDI6cmV0dXJuIkNoYW5nZSB0byAvKj8qLyBoaW50IgpjYXNlIDM6cmV0dXJuIkNoYW5n -ZSB0byAvKiEqLyBoaW50In19LApMTDpmdW5jdGlvbiBMTChhLGIpe3RoaXMuYT1hCnRoaXMuYj1ifSwK -TUQ6ZnVuY3Rpb24gTUQoYSl7dGhpcy5hPWF9LApINzpmdW5jdGlvbiBINyhhLGIpe3RoaXMuYT1hCnRo -aXMuYj1ifSwKWUYoYSxiKXt2YXIgcyxyLHEscCxvLG4sbSxsCmZvcihzPWIubGVuZ3RoLHI9MTtyPHM7 -KytyKXtpZihiW3JdPT1udWxsfHxiW3ItMV0hPW51bGwpY29udGludWUKZm9yKDtzPj0xO3M9cSl7cT1z -LTEKaWYoYltxXSE9bnVsbClicmVha31wPW5ldyBBLlJuKCIiKQpvPSIiKyhhKyIoIikKcC5hPW8Kbj1B -LnQ2KGIpCm09bi5DKCJuSDwxPiIpCmw9bmV3IEEubkgoYiwwLHMsbSkKbC5IZChiLDAscyxuLmMpCm09 -bytuZXcgQS5sSihsLG0uQygicVUoYUwuRSkiKS5hKG5ldyBBLk5vKCkpLG0uQygibEo8YUwuRSxxVT4i -KSkuSCgwLCIsICIpCnAuYT1tCnAuYT1tKygiKTogcGFydCAiKyhyLTEpKyIgd2FzIG51bGwsIGJ1dCBw -YXJ0ICIrcisiIHdhcyBub3QuIikKdGhyb3cgQS5iKEEueFkocFsiWyJdKDApLG51bGwpKX19LApsSTpm -dW5jdGlvbiBsSShhKXt0aGlzLmE9YX0sCnE3OmZ1bmN0aW9uIHE3KCl7fSwKTm86ZnVuY3Rpb24gTm8o -KXt9LApmdjpmdW5jdGlvbiBmdigpe30sCkNMKGEsYil7dmFyIHMscixxLHAsbyxuPWIueFooYSkKYi5o -SyhhKQppZihuIT1udWxsKWE9Qi54Qi55bihhLG4ubGVuZ3RoKQpzPXQucwpyPUEuUUkoW10scykKcT1B -LlFJKFtdLHMpCnM9YS5sZW5ndGgKaWYocyE9PTAmJmIucjQoQi54Qi5XKGEsMCkpKXtpZigwPj1zKXJl -dHVybiBBLk9IKGEsMCkKQi5ObS5pKHEsYVswXSkKcD0xfWVsc2V7Qi5ObS5pKHEsIiIpCnA9MH1mb3Io -bz1wO288czsrK28paWYoYi5yNChCLnhCLlcoYSxvKSkpe0IuTm0uaShyLEIueEIuTmooYSxwLG8pKQpC -Lk5tLmkocSxhW29dKQpwPW8rMX1pZihwPHMpe0IuTm0uaShyLEIueEIueW4oYSxwKSkKQi5ObS5pKHEs -IiIpfXJldHVybiBuZXcgQS5XRChiLG4scixxKX0sCldEOmZ1bmN0aW9uIFdEKGEsYixjLGQpe3ZhciBf -PXRoaXMKXy5hPWEKXy5iPWIKXy5kPWMKXy5lPWR9LApJNyhhKXtyZXR1cm4gbmV3IEEuZHYoYSl9LApk -djpmdW5jdGlvbiBkdihhKXt0aGlzLmE9YX0sClJoKCl7dmFyIHMscj1udWxsCmlmKEEudW8oKS5nRmko -KSE9PSJmaWxlIilyZXR1cm4gJC5FYigpCnM9QS51bygpCmlmKCFCLnhCLlRjKHMuZ0lpKHMpLCIvIikp -cmV0dXJuICQuRWIoKQppZihBLktMKHIsImEvYiIscixyLHIscixyKS50NCgpPT09ImFcXGIiKXJldHVy -biAkLktrKCkKcmV0dXJuICQuYkQoKX0sCnpMOmZ1bmN0aW9uIHpMKCl7fSwKT0Y6ZnVuY3Rpb24gT0Yo -YSxiLGMpe3RoaXMuZD1hCnRoaXMuZT1iCnRoaXMuZj1jfSwKcnU6ZnVuY3Rpb24gcnUoYSxiLGMsZCl7 -dmFyIF89dGhpcwpfLmQ9YQpfLmU9YgpfLmY9YwpfLnI9ZH0sCklWOmZ1bmN0aW9uIElWKGEsYixjLGQp -e3ZhciBfPXRoaXMKXy5kPWEKXy5lPWIKXy5mPWMKXy5yPWR9LApSOShhKXtyZXR1cm4gdC53LmIoYSl8 -fHQuQi5iKGEpfHx0LmR6LmIoYSl8fHQueC5iKGEpfHx0LkEuYihhKXx8dC5nNC5iKGEpfHx0LmcyLmIo -YSl9LAp5ayhhKXtyZXR1cm4gQS52KEEubGEoYSkpfSwKYnQoYSl7cmV0dXJuIEEudihBLlJJKGEpKX0s -CnBSKGEpe3JldHVybiBBLnYoQS5HKGEpKX0sCmFiKCl7dmFyIHMscixxLHAsbz1udWxsCnRyeXtvPUEu -dW8oKX1jYXRjaChzKXtpZih0Lmc4LmIoQS5SdShzKSkpe3I9JC5GZgppZihyIT1udWxsKXJldHVybiBy -CnRocm93IHN9ZWxzZSB0aHJvdyBzfWlmKEouUk0obywkLkk2KSl7cj0kLkZmCnIudG9TdHJpbmcKcmV0 -dXJuIHJ9JC5JNj1vCmlmKCQuSGsoKT09JC5FYigpKXI9JC5GZj1vLlpJKCIuIilbIlsiXSgwKQplbHNl -e3E9by50NCgpCnA9cS5sZW5ndGgtMQpyPSQuRmY9cD09PTA/cTpCLnhCLk5qKHEsMCxwKX1yZXR1cm4g -cn0sCk9TKGEpe3ZhciBzCmlmKCEoYT49NjUmJmE8PTkwKSlzPWE+PTk3JiZhPD0xMjIKZWxzZSBzPSEw -CnJldHVybiBzfSwKWXUoYSxiKXt2YXIgcz1hLmxlbmd0aCxyPWIrMgppZihzPHIpcmV0dXJuITEKaWYo -IUEuT1MoQi54Qi5PKGEsYikpKXJldHVybiExCmlmKEIueEIuTyhhLGIrMSkhPT01OClyZXR1cm4hMQpp -ZihzPT09cilyZXR1cm4hMApyZXR1cm4gQi54Qi5PKGEscik9PT00N319LEo9ewpRdShhLGIsYyxkKXty -ZXR1cm57aTphLHA6YixlOmMseDpkfX0sCmtzKGEpe3ZhciBzLHIscSxwLG8sbj1hW3YuZGlzcGF0Y2hQ -cm9wZXJ0eU5hbWVdCmlmKG49PW51bGwpaWYoJC5Cdj09bnVsbCl7QS5YRCgpCm49YVt2LmRpc3BhdGNo -UHJvcGVydHlOYW1lXX1pZihuIT1udWxsKXtzPW4ucAppZighMT09PXMpcmV0dXJuIG4uaQppZighMD09 -PXMpcmV0dXJuIGEKcj1PYmplY3QuZ2V0UHJvdG90eXBlT2YoYSkKaWYocz09PXIpcmV0dXJuIG4uaQpp -ZihuLmU9PT1yKXRocm93IEEuYihBLlNZKCJSZXR1cm4gaW50ZXJjZXB0b3IgZm9yICIrQS5kKHMoYSxu -KSkpKX1xPWEuY29uc3RydWN0b3IKaWYocT09bnVsbClwPW51bGwKZWxzZXtvPSQuem0KaWYobz09bnVs -bClvPSQuem09di5nZXRJc29sYXRlVGFnKCJfJGRhcnRfanMiKQpwPXFbb119aWYocCE9bnVsbClyZXR1 -cm4gcApwPUEudzMoYSkKaWYocCE9bnVsbClyZXR1cm4gcAppZih0eXBlb2YgYT09ImZ1bmN0aW9uIily -ZXR1cm4gQi5ERwpzPU9iamVjdC5nZXRQcm90b3R5cGVPZihhKQppZihzPT1udWxsKXJldHVybiBCLlpR -CmlmKHM9PT1PYmplY3QucHJvdG90eXBlKXJldHVybiBCLlpRCmlmKHR5cGVvZiBxPT0iZnVuY3Rpb24i -KXtvPSQuem0KaWYobz09bnVsbClvPSQuem09di5nZXRJc29sYXRlVGFnKCJfJGRhcnRfanMiKQpPYmpl -Y3QuZGVmaW5lUHJvcGVydHkocSxvLHt2YWx1ZTpCLnZCLGVudW1lcmFibGU6ZmFsc2Usd3JpdGFibGU6 -dHJ1ZSxjb25maWd1cmFibGU6dHJ1ZX0pCnJldHVybiBCLnZCfXJldHVybiBCLnZCfSwKUWkoYSxiKXtp -ZihhPDB8fGE+NDI5NDk2NzI5NSl0aHJvdyBBLmIoQS5URShhLDAsNDI5NDk2NzI5NSwibGVuZ3RoIixu -dWxsKSkKcmV0dXJuIEoucHkobmV3IEFycmF5KGEpLGIpfSwKS2goYSxiKXtpZihhPDApdGhyb3cgQS5i -KEEueFkoIkxlbmd0aCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIGludGVnZXI6ICIrYSxudWxsKSkKcmV0 -dXJuIEEuUUkobmV3IEFycmF5KGEpLGIuQygiamQ8MD4iKSl9LApweShhLGIpe3JldHVybiBKLkVwKEEu -UUkoYSxiLkMoImpkPDA+IikpLGIpfSwKRXAoYSxiKXthLmZpeGVkJGxlbmd0aD1BcnJheQpyZXR1cm4g -YX0sCnpDKGEpe2EuZml4ZWQkbGVuZ3RoPUFycmF5CmEuaW1tdXRhYmxlJGxpc3Q9QXJyYXkKcmV0dXJu -IGF9LApHYShhKXtpZihhPDI1Nilzd2l0Y2goYSl7Y2FzZSA5OmNhc2UgMTA6Y2FzZSAxMTpjYXNlIDEy -OmNhc2UgMTM6Y2FzZSAzMjpjYXNlIDEzMzpjYXNlIDE2MDpyZXR1cm4hMApkZWZhdWx0OnJldHVybiEx -fXN3aXRjaChhKXtjYXNlIDU3NjA6Y2FzZSA4MTkyOmNhc2UgODE5MzpjYXNlIDgxOTQ6Y2FzZSA4MTk1 -OmNhc2UgODE5NjpjYXNlIDgxOTc6Y2FzZSA4MTk4OmNhc2UgODE5OTpjYXNlIDgyMDA6Y2FzZSA4MjAx -OmNhc2UgODIwMjpjYXNlIDgyMzI6Y2FzZSA4MjMzOmNhc2UgODIzOTpjYXNlIDgyODc6Y2FzZSAxMjI4 -ODpjYXNlIDY1Mjc5OnJldHVybiEwCmRlZmF1bHQ6cmV0dXJuITF9fSwKbW0oYSxiKXt2YXIgcyxyCmZv -cihzPWEubGVuZ3RoO2I8czspe3I9Qi54Qi5XKGEsYikKaWYociE9PTMyJiZyIT09MTMmJiFKLkdhKHIp -KWJyZWFrOysrYn1yZXR1cm4gYn0sCmMxKGEsYil7dmFyIHMscgpmb3IoO2I+MDtiPXMpe3M9Yi0xCnI9 -Qi54Qi5PKGEscykKaWYociE9PTMyJiZyIT09MTMmJiFKLkdhKHIpKWJyZWFrfXJldHVybiBifSwKVTYo -YSl7aWYodHlwZW9mIGE9PSJzdHJpbmciKXJldHVybiBKLkRyLnByb3RvdHlwZQppZihhPT1udWxsKXJl -dHVybiBhCmlmKGEuY29uc3RydWN0b3I9PUFycmF5KXJldHVybiBKLmpkLnByb3RvdHlwZQppZih0eXBl -b2YgYSE9Im9iamVjdCIpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBKLmM1LnByb3RvdHlw -ZQpyZXR1cm4gYX1pZihhIGluc3RhbmNlb2YgQS5NaClyZXR1cm4gYQpyZXR1cm4gSi5rcyhhKX0sCllF -KGEpe2lmKGE9PW51bGwpcmV0dXJuIGEKaWYodHlwZW9mIGEhPSJvYmplY3QiKXtpZih0eXBlb2YgYT09 -ImZ1bmN0aW9uIilyZXR1cm4gSi5jNS5wcm90b3R5cGUKcmV0dXJuIGF9aWYoYSBpbnN0YW5jZW9mIEEu -TWgpcmV0dXJuIGEKcmV0dXJuIEoua3MoYSl9LAppYShhKXtpZih0eXBlb2YgYT09Im51bWJlciIpe2lm -KE1hdGguZmxvb3IoYSk9PWEpcmV0dXJuIEouTDcucHJvdG90eXBlCnJldHVybiBKLmtELnByb3RvdHlw -ZX1pZih0eXBlb2YgYT09InN0cmluZyIpcmV0dXJuIEouRHIucHJvdG90eXBlCmlmKGE9PW51bGwpcmV0 -dXJuIEoud2UucHJvdG90eXBlCmlmKHR5cGVvZiBhPT0iYm9vbGVhbiIpcmV0dXJuIEoueUUucHJvdG90 -eXBlCmlmKGEuY29uc3RydWN0b3I9PUFycmF5KXJldHVybiBKLmpkLnByb3RvdHlwZQppZih0eXBlb2Yg -YSE9Im9iamVjdCIpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24iKXJldHVybiBKLmM1LnByb3RvdHlwZQpy -ZXR1cm4gYX1pZihhIGluc3RhbmNlb2YgQS5NaClyZXR1cm4gYQpyZXR1cm4gSi5rcyhhKX0sCnJZKGEp -e2lmKHR5cGVvZiBhPT0ic3RyaW5nIilyZXR1cm4gSi5Eci5wcm90b3R5cGUKaWYoYT09bnVsbClyZXR1 -cm4gYQppZighKGEgaW5zdGFuY2VvZiBBLk1oKSlyZXR1cm4gSi5rZC5wcm90b3R5cGUKcmV0dXJuIGF9 -LAp3MShhKXtpZihhPT1udWxsKXJldHVybiBhCmlmKGEuY29uc3RydWN0b3I9PUFycmF5KXJldHVybiBK -LmpkLnByb3RvdHlwZQppZih0eXBlb2YgYSE9Im9iamVjdCIpe2lmKHR5cGVvZiBhPT0iZnVuY3Rpb24i -KXJldHVybiBKLmM1LnByb3RvdHlwZQpyZXR1cm4gYX1pZihhIGluc3RhbmNlb2YgQS5NaClyZXR1cm4g -YQpyZXR1cm4gSi5rcyhhKX0sCkE1KGEsYil7cmV0dXJuIEoudzEoYSkuZVIoYSxiKX0sCkVoKGEsYixj -KXtyZXR1cm4gSi5ZRShhKS5tSyhhLGIsYyl9LApFbChhLGIpe3JldHVybiBKLncxKGEpLmRyKGEsYil9 -LApGNyhhKXtyZXR1cm4gSi5VNihhKS5nb3IoYSl9LApGTChhLGIpe3JldHVybiBKLnJZKGEpLmRkKGEs -Yil9LApHQShhLGIpe3JldHVybiBKLncxKGEpLkEoYSxiKX0sCkhtKGEpe3JldHVybiBKLlU2KGEpLmdr -KGEpfSwKSVQoYSl7cmV0dXJuIEoudzEoYSkuZ00oYSl9LApKeShhLGIpe3JldHVybiBKLmlhKGEpLmU3 -KGEsYil9LApMdChhKXtyZXR1cm4gSi5ZRShhKS56QihhKX0sCk0xKGEsYixjKXtyZXR1cm4gSi53MShh -KS5FMihhLGIsYyl9LApNdShhLGIpe3JldHVybiBKLllFKGEpLnNQKGEsYil9LApSTShhLGIpe2lmKGE9 -PW51bGwpcmV0dXJuIGI9PW51bGwKaWYodHlwZW9mIGEhPSJvYmplY3QiKXJldHVybiBiIT1udWxsJiZh -PT09YgpyZXR1cm4gSi5pYShhKS5ETihhLGIpfSwKUlgoYSl7cmV0dXJuIEoudzEoYSkuYnIoYSl9LApU -MChhKXtyZXR1cm4gSi5yWShhKS5iUyhhKX0sClUzKGEpe3JldHVybiBKLmlhKGEpLmdtKGEpfSwKWVMo -YSl7cmV0dXJuIEouaWEoYSlbIlsiXShhKX0sCmE2KGEsYil7cmV0dXJuIEouclkoYSkuTyhhLGIpfSwK -YlQoYSl7cmV0dXJuIEouWUUoYSkuRDQoYSl9LApjSChhKXtyZXR1cm4gSi5yWShhKS5oYyhhKX0sCmRS -KGEpe3JldHVybiBKLllFKGEpLmdQKGEpfSwKZFooYSxiLGMsZCl7cmV0dXJuIEouWUUoYSkuT24oYSxi -LGMsZCl9LApkaChhKXtyZXR1cm4gSi5ZRShhKS5GRihhKX0sCmRyKGEsYil7cmV0dXJuIEouWUUoYSku -c2E0KGEsYil9LAppZyhhKXtyZXR1cm4gSi5ZRShhKS5nUWcoYSl9LApxRihhKXtyZXR1cm4gSi5ZRShh -KS5nVmwoYSl9LAp0SChhLGIsYyl7cmV0dXJuIEouWUUoYSkucGsoYSxiLGMpfSwKdTkoYSxiLGMpe3Jl -dHVybiBKLncxKGEpLlk1KGEsYixjKX0sCnVVKGEpe3JldHVybiBKLlU2KGEpLmdsMChhKX0sCndmKGEs -Yil7cmV0dXJuIEouWUUoYSkuc1JOKGEsYil9LAp4OShhLGIpe2lmKHR5cGVvZiBiPT09Im51bWJlciIp -aWYoYS5jb25zdHJ1Y3Rvcj09QXJyYXl8fHR5cGVvZiBhPT0ic3RyaW5nInx8QS5HcChhLGFbdi5kaXNw -YXRjaFByb3BlcnR5TmFtZV0pKWlmKGI+Pj4wPT09YiYmYjxhLmxlbmd0aClyZXR1cm4gYVtiXQpyZXR1 -cm4gSi5VNihhKS5xKGEsYil9LAp6bChhLGIpe3JldHVybiBKLlU2KGEpLnRnKGEsYil9LApHdjpmdW5j -dGlvbiBHdigpe30sCnlFOmZ1bmN0aW9uIHlFKCl7fSwKd2U6ZnVuY3Rpb24gd2UoKXt9LApNRjpmdW5j -dGlvbiBNRigpe30sCnUwOmZ1bmN0aW9uIHUwKCl7fSwKaUM6ZnVuY3Rpb24gaUMoKXt9LAprZDpmdW5j -dGlvbiBrZCgpe30sCmM1OmZ1bmN0aW9uIGM1KCl7fSwKamQ6ZnVuY3Rpb24gamQoYSl7dGhpcy4kdGk9 -YX0sClBvOmZ1bmN0aW9uIFBvKGEpe3RoaXMuJHRpPWF9LAptMTpmdW5jdGlvbiBtMShhLGIsYyl7dmFy -IF89dGhpcwpfLmE9YQpfLmI9YgpfLmM9MApfLmQ9bnVsbApfLiR0aT1jfSwKcUk6ZnVuY3Rpb24gcUko -KXt9LApMNzpmdW5jdGlvbiBMNygpe30sCmtEOmZ1bmN0aW9uIGtEKCl7fSwKRHI6ZnVuY3Rpb24gRHIo -KXt9fSxCPXt9CnZhciB3PVtBLEosQl0KdmFyICQ9e30KQS5GSy5wcm90b3R5cGU9e30KSi5Hdi5wcm90 -b3R5cGU9ewpETihhLGIpe3JldHVybiBhPT09Yn0sCmdtKGEpe3JldHVybiBBLmVRKGEpfSwKIlsiKGEp -e3JldHVybiJJbnN0YW5jZSBvZiAnIitBLmxoKGEpKyInIn0sCmU3KGEsYil7dC5vLmEoYikKdGhyb3cg -QS5iKG5ldyBBLm1wKGEsYi5nV2EoKSxiLmduZCgpLGIuZ1ZtKCksbnVsbCkpfX0KSi55RS5wcm90b3R5 -cGU9ewoiWyIoYSl7cmV0dXJuIFN0cmluZyhhKX0sCmdtKGEpe3JldHVybiBhPzUxOTAxODoyMTgxNTl9 -LAokaWEyOjF9Ckoud2UucHJvdG90eXBlPXsKRE4oYSxiKXtyZXR1cm4gbnVsbD09Yn0sCiJbIihhKXty -ZXR1cm4ibnVsbCJ9LApnbShhKXtyZXR1cm4gMH0sCiRpYzg6MX0KSi5NRi5wcm90b3R5cGU9e30KSi51 -MC5wcm90b3R5cGU9ewpnbShhKXtyZXR1cm4gMH0sCiJbIihhKXtyZXR1cm4gU3RyaW5nKGEpfSwKJGl2 -bToxfQpKLmlDLnByb3RvdHlwZT17fQpKLmtkLnByb3RvdHlwZT17fQpKLmM1LnByb3RvdHlwZT17CiJb -IihhKXt2YXIgcz1hWyQudygpXQppZihzPT1udWxsKXJldHVybiB0aGlzLnUoYSkKcmV0dXJuIkphdmFT -Y3JpcHQgZnVuY3Rpb24gZm9yICIrQS5kKEouWVMocykpfSwKJGlFSDoxfQpKLmpkLnByb3RvdHlwZT17 -CmRyKGEsYil7cmV0dXJuIG5ldyBBLmpWKGEsQS50NihhKS5DKCJAPDE+IikuS3EoYikuQygialY8MSwy -PiIpKX0sCmkoYSxiKXtBLnQ2KGEpLmMuYShiKQppZighIWEuZml4ZWQkbGVuZ3RoKUEudihBLkw0KCJh -ZGQiKSkKYS5wdXNoKGIpfSwKVzQoYSxiKXt2YXIgcwppZighIWEuZml4ZWQkbGVuZ3RoKUEudihBLkw0 -KCJyZW1vdmVBdCIpKQpzPWEubGVuZ3RoCmlmKGI+PXMpdGhyb3cgQS5iKEEuTzcoYixudWxsKSkKcmV0 -dXJuIGEuc3BsaWNlKGIsMSlbMF19LApVRyhhLGIsYyl7dmFyIHMscgpBLnQ2KGEpLkMoImNYPDE+Iiku -YShjKQppZighIWEuZml4ZWQkbGVuZ3RoKUEudihBLkw0KCJpbnNlcnRBbGwiKSkKQS53QShiLDAsYS5s -ZW5ndGgsImluZGV4IikKaWYoIXQuVS5iKGMpKWM9Si5SWChjKQpzPUouSG0oYykKYS5sZW5ndGg9YS5s -ZW5ndGgrcwpyPWIrcwp0aGlzLllXKGEscixhLmxlbmd0aCxhLGIpCnRoaXMudmcoYSxiLHIsYyl9LApG -VihhLGIpe3ZhciBzCkEudDYoYSkuQygiY1g8MT4iKS5hKGIpCmlmKCEhYS5maXhlZCRsZW5ndGgpQS52 -KEEuTDQoImFkZEFsbCIpKQppZihBcnJheS5pc0FycmF5KGIpKXt0aGlzLktoKGEsYikKcmV0dXJufWZv -cihzPUouSVQoYik7cy5HKCk7KWEucHVzaChzLmdsKCkpfSwKS2goYSxiKXt2YXIgcyxyCnQuYi5hKGIp -CnM9Yi5sZW5ndGgKaWYocz09PTApcmV0dXJuCmlmKGE9PT1iKXRocm93IEEuYihBLmE0KGEpKQpmb3Io -cj0wO3I8czsrK3IpYS5wdXNoKGJbcl0pfSwKVjEoYSl7aWYoISFhLmZpeGVkJGxlbmd0aClBLnYoQS5M -NCgiY2xlYXIiKSkKYS5sZW5ndGg9MH0sCkUyKGEsYixjKXt2YXIgcz1BLnQ2KGEpCnJldHVybiBuZXcg -QS5sSihhLHMuS3EoYykuQygiMSgyKSIpLmEoYikscy5DKCJAPDE+IikuS3EoYykuQygibEo8MSwyPiIp -KX0sCkgoYSxiKXt2YXIgcyxyPUEuTzgoYS5sZW5ndGgsIiIsITEsdC5OKQpmb3Iocz0wO3M8YS5sZW5n -dGg7KytzKXRoaXMuWTUocixzLEEuZChhW3NdKSkKcmV0dXJuIHIuam9pbihiKX0sCmVSKGEsYil7cmV0 -dXJuIEEucUMoYSxiLG51bGwsQS50NihhKS5jKX0sCk4wKGEsYixjLGQpe3ZhciBzLHIscQpkLmEoYikK -QS50NihhKS5LcShkKS5DKCIxKDEsMikiKS5hKGMpCnM9YS5sZW5ndGgKZm9yKHI9YixxPTA7cTxzOysr -cSl7cj1jLiQyKHIsYVtxXSkKaWYoYS5sZW5ndGghPT1zKXRocm93IEEuYihBLmE0KGEpKX1yZXR1cm4g -cn0sCkh0KGEsYil7dmFyIHMscixxLHAsbyxuPUEudDYoYSkKbi5DKCJhMigxKSIpLmEoYikKcz1hLmxl -bmd0aApmb3Iocj1udWxsLHE9ITEscD0wO3A8czsrK3Ape289YVtwXQppZihBLm9UKGIuJDEobykpKXtp -ZihxKXRocm93IEEuYihBLkFtKCkpCnI9bwpxPSEwfWlmKHMhPT1hLmxlbmd0aCl0aHJvdyBBLmIoQS5h -NChhKSl9aWYocSlyZXR1cm4gcj09bnVsbD9uLmMuYShyKTpyCnRocm93IEEuYihBLldwKCkpfSwKQShh -LGIpe2lmKCEoYj49MCYmYjxhLmxlbmd0aCkpcmV0dXJuIEEuT0goYSxiKQpyZXR1cm4gYVtiXX0sCmd0 -SChhKXtpZihhLmxlbmd0aD4wKXJldHVybiBhWzBdCnRocm93IEEuYihBLldwKCkpfSwKZ3JaKGEpe3Zh -ciBzPWEubGVuZ3RoCmlmKHM+MClyZXR1cm4gYVtzLTFdCnRocm93IEEuYihBLldwKCkpfSwKWVcoYSxi -LGMsZCxlKXt2YXIgcyxyLHEscCxvCkEudDYoYSkuQygiY1g8MT4iKS5hKGQpCmlmKCEhYS5pbW11dGFi -bGUkbGlzdClBLnYoQS5MNCgic2V0UmFuZ2UiKSkKQS5qQihiLGMsYS5sZW5ndGgpCnM9Yy1iCmlmKHM9 -PT0wKXJldHVybgpBLmsxKGUsInNraXBDb3VudCIpCmlmKHQuai5iKGQpKXtyPWQKcT1lfWVsc2V7cj1K -LkE1KGQsZSkudHQoMCwhMSkKcT0wfXA9Si5VNihyKQppZihxK3M+cC5nayhyKSl0aHJvdyBBLmIoQS5h -cigpKQppZihxPGIpZm9yKG89cy0xO28+PTA7LS1vKWFbYitvXT1wLnEocixxK28pCmVsc2UgZm9yKG89 -MDtvPHM7KytvKWFbYitvXT1wLnEocixxK28pfSwKdmcoYSxiLGMsZCl7cmV0dXJuIHRoaXMuWVcoYSxi -LGMsZCwwKX0sClZyKGEsYil7dmFyIHMscgpBLnQ2KGEpLkMoImEyKDEpIikuYShiKQpzPWEubGVuZ3Ro -CmZvcihyPTA7cjxzOysrcil7aWYoQS5vVChiLiQxKGFbcl0pKSlyZXR1cm4hMAppZihhLmxlbmd0aCE9 -PXMpdGhyb3cgQS5iKEEuYTQoYSkpfXJldHVybiExfSwKdGcoYSxiKXt2YXIgcwpmb3Iocz0wO3M8YS5s -ZW5ndGg7KytzKWlmKEouUk0oYVtzXSxiKSlyZXR1cm4hMApyZXR1cm4hMX0sCmdsMChhKXtyZXR1cm4g -YS5sZW5ndGg9PT0wfSwKZ29yKGEpe3JldHVybiBhLmxlbmd0aCE9PTB9LAoiWyIoYSl7cmV0dXJuIEEu -eChhLCJbIiwiXSIpfSwKdHQoYSxiKXt2YXIgcz1BLlFJKGEuc2xpY2UoMCksQS50NihhKSkKcmV0dXJu -IHN9LApicihhKXtyZXR1cm4gdGhpcy50dChhLCEwKX0sCmdNKGEpe3JldHVybiBuZXcgSi5tMShhLGEu -bGVuZ3RoLEEudDYoYSkuQygibTE8MT4iKSl9LApnbShhKXtyZXR1cm4gQS5lUShhKX0sCmdrKGEpe3Jl -dHVybiBhLmxlbmd0aH0sCnEoYSxiKXtBLklaKGIpCmlmKCEoYj49MCYmYjxhLmxlbmd0aCkpdGhyb3cg -QS5iKEEuaihhLGIpKQpyZXR1cm4gYVtiXX0sClk1KGEsYixjKXtBLnQ2KGEpLmMuYShjKQppZighIWEu -aW1tdXRhYmxlJGxpc3QpQS52KEEuTDQoImluZGV4ZWQgc2V0IikpCmlmKCEoYj49MCYmYjxhLmxlbmd0 -aCkpdGhyb3cgQS5iKEEuaihhLGIpKQphW2JdPWN9LAokaWJROjEsCiRpY1g6MSwKJGl6TToxfQpKLlBv -LnByb3RvdHlwZT17fQpKLm0xLnByb3RvdHlwZT17CmdsKCl7dmFyIHM9dGhpcy5kCnJldHVybiBzPT1u -dWxsP3RoaXMuJHRpLmMuYShzKTpzfSwKRygpe3ZhciBzLHI9dGhpcyxxPXIuYSxwPXEubGVuZ3RoCmlm -KHIuYiE9PXApdGhyb3cgQS5iKEEubGsocSkpCnM9ci5jCmlmKHM+PXApe3Iuc0YobnVsbCkKcmV0dXJu -ITF9ci5zRihxW3NdKTsrK3IuYwpyZXR1cm4hMH0sCnNGKGEpe3RoaXMuZD10aGlzLiR0aS5DKCIxPyIp -LmEoYSl9LAokaUFuOjF9CkoucUkucHJvdG90eXBlPXsKelEoYSl7aWYoYT4wKXtpZihhIT09MS8wKXJl -dHVybiBNYXRoLnJvdW5kKGEpfWVsc2UgaWYoYT4tMS8wKXJldHVybiAwLU1hdGgucm91bmQoMC1hKQp0 -aHJvdyBBLmIoQS5MNCgiIithKyIucm91bmQoKSIpKX0sCiJbIihhKXtpZihhPT09MCYmMS9hPDApcmV0 -dXJuIi0wLjAiCmVsc2UgcmV0dXJuIiIrYX0sCmdtKGEpe3ZhciBzLHIscSxwLG89YXwwCmlmKGE9PT1v -KXJldHVybiBvJjUzNjg3MDkxMQpzPU1hdGguYWJzKGEpCnI9TWF0aC5sb2cocykvMC42OTMxNDcxODA1 -NTk5NDUzfDAKcT1NYXRoLnBvdygyLHIpCnA9czwxP3MvcTpxL3MKcmV0dXJuKChwKjkwMDcxOTkyNTQ3 -NDA5OTJ8MCkrKHAqMzU0MjI0MzE4MTE3NjUyMXwwKSkqNTk5MTk3K3IqMTI1OSY1MzY4NzA5MTF9LAp6 -WShhLGIpe3ZhciBzPWElYgppZihzPT09MClyZXR1cm4gMAppZihzPjApcmV0dXJuIHMKcmV0dXJuIHMr -Yn0sCkJVKGEsYil7cmV0dXJuKGF8MCk9PT1hP2EvYnwwOnRoaXMuREooYSxiKX0sCkRKKGEsYil7dmFy -IHM9YS9iCmlmKHM+PS0yMTQ3NDgzNjQ4JiZzPD0yMTQ3NDgzNjQ3KXJldHVybiBzfDAKaWYocz4wKXtp -ZihzIT09MS8wKXJldHVybiBNYXRoLmZsb29yKHMpfWVsc2UgaWYocz4tMS8wKXJldHVybiBNYXRoLmNl -aWwocykKdGhyb3cgQS5iKEEuTDQoIlJlc3VsdCBvZiB0cnVuY2F0aW5nIGRpdmlzaW9uIGlzICIrQS5k -KHMpKyI6ICIrQS5kKGEpKyIgfi8gIitiKSl9LAp3RyhhLGIpe3ZhciBzCmlmKGE+MClzPXRoaXMuVWgo -YSxiKQplbHNle3M9Yj4zMT8zMTpiCnM9YT4+cz4+PjB9cmV0dXJuIHN9LApiZihhLGIpe2lmKDA+Yil0 -aHJvdyBBLmIoQS50TChiKSkKcmV0dXJuIHRoaXMuVWgoYSxiKX0sClVoKGEsYil7cmV0dXJuIGI+MzE/ -MDphPj4+Yn0sCiRpQ1A6MSwKJGlaWjoxfQpKLkw3LnByb3RvdHlwZT17JGlLTjoxfQpKLmtELnByb3Rv -dHlwZT17fQpKLkRyLnByb3RvdHlwZT17Ck8oYSxiKXtpZihiPDApdGhyb3cgQS5iKEEuaihhLGIpKQpp -ZihiPj1hLmxlbmd0aClBLnYoQS5qKGEsYikpCnJldHVybiBhLmNoYXJDb2RlQXQoYil9LApXKGEsYil7 -aWYoYj49YS5sZW5ndGgpdGhyb3cgQS5iKEEuaihhLGIpKQpyZXR1cm4gYS5jaGFyQ29kZUF0KGIpfSwK -ZGQoYSxiKXtyZXR1cm4gbmV3IEEudW4oYixhLDApfSwKaChhLGIpe3JldHVybiBhK2J9LApUYyhhLGIp -e3ZhciBzPWIubGVuZ3RoLHI9YS5sZW5ndGgKaWYocz5yKXJldHVybiExCnJldHVybiBiPT09dGhpcy55 -bihhLHItcyl9LAppNyhhLGIsYyxkKXt2YXIgcz1BLmpCKGIsYyxhLmxlbmd0aCkKcmV0dXJuIGEuc3Vi -c3RyaW5nKDAsYikrZCthLnN1YnN0cmluZyhzKX0sClFpKGEsYixjKXt2YXIgcwppZihjPDB8fGM+YS5s -ZW5ndGgpdGhyb3cgQS5iKEEuVEUoYywwLGEubGVuZ3RoLG51bGwsbnVsbCkpCnM9YytiLmxlbmd0aApp -ZihzPmEubGVuZ3RoKXJldHVybiExCnJldHVybiBiPT09YS5zdWJzdHJpbmcoYyxzKX0sCm5DKGEsYil7 -cmV0dXJuIHRoaXMuUWkoYSxiLDApfSwKTmooYSxiLGMpe3JldHVybiBhLnN1YnN0cmluZyhiLEEuakIo -YixjLGEubGVuZ3RoKSl9LAp5bihhLGIpe3JldHVybiB0aGlzLk5qKGEsYixudWxsKX0sCmhjKGEpe3Jl -dHVybiBhLnRvTG93ZXJDYXNlKCl9LApiUyhhKXt2YXIgcyxyLHEscD1hLnRyaW0oKSxvPXAubGVuZ3Ro -CmlmKG89PT0wKXJldHVybiBwCmlmKHRoaXMuVyhwLDApPT09MTMzKXtzPUoubW0ocCwxKQppZihzPT09 -bylyZXR1cm4iIn1lbHNlIHM9MApyPW8tMQpxPXRoaXMuTyhwLHIpPT09MTMzP0ouYzEocCxyKTpvCmlm -KHM9PT0wJiZxPT09bylyZXR1cm4gcApyZXR1cm4gcC5zdWJzdHJpbmcocyxxKX0sCkl4KGEsYil7dmFy -IHMscgppZigwPj1iKXJldHVybiIiCmlmKGI9PT0xfHxhLmxlbmd0aD09PTApcmV0dXJuIGEKaWYoYiE9 -PWI+Pj4wKXRocm93IEEuYihCLkVxKQpmb3Iocz1hLHI9IiI7ITA7KXtpZigoYiYxKT09PTEpcj1zK3IK -Yj1iPj4+MQppZihiPT09MClicmVhawpzKz1zfXJldHVybiByfSwKWFUoYSxiLGMpe3ZhciBzCmlmKGM8 -MHx8Yz5hLmxlbmd0aCl0aHJvdyBBLmIoQS5URShjLDAsYS5sZW5ndGgsbnVsbCxudWxsKSkKcz1hLmlu -ZGV4T2YoYixjKQpyZXR1cm4gc30sCk9ZKGEsYil7cmV0dXJuIHRoaXMuWFUoYSxiLDApfSwKUGsoYSxi -LGMpe3ZhciBzLHIKaWYoYz09bnVsbCljPWEubGVuZ3RoCmVsc2UgaWYoYzwwfHxjPmEubGVuZ3RoKXRo -cm93IEEuYihBLlRFKGMsMCxhLmxlbmd0aCxudWxsLG51bGwpKQpzPWIubGVuZ3RoCnI9YS5sZW5ndGgK -aWYoYytzPnIpYz1yLXMKcmV0dXJuIGEubGFzdEluZGV4T2YoYixjKX0sCmNuKGEsYil7cmV0dXJuIHRo -aXMuUGsoYSxiLG51bGwpfSwKSXMoYSxiLGMpe3ZhciBzPWEubGVuZ3RoCmlmKGM+cyl0aHJvdyBBLmIo -QS5URShjLDAscyxudWxsLG51bGwpKQpyZXR1cm4gQS5TUShhLGIsYyl9LAp0ZyhhLGIpe3JldHVybiB0 -aGlzLklzKGEsYiwwKX0sCiJbIihhKXtyZXR1cm4gYX0sCmdtKGEpe3ZhciBzLHIscQpmb3Iocz1hLmxl -bmd0aCxyPTAscT0wO3E8czsrK3Epe3I9cithLmNoYXJDb2RlQXQocSkmNTM2ODcwOTExCnI9cisoKHIm -NTI0Mjg3KTw8MTApJjUzNjg3MDkxMQpyXj1yPj42fXI9cisoKHImNjcxMDg4NjMpPDwzKSY1MzY4NzA5 -MTEKcl49cj4+MTEKcmV0dXJuIHIrKChyJjE2MzgzKTw8MTUpJjUzNjg3MDkxMX0sCmdrKGEpe3JldHVy -biBhLmxlbmd0aH0sCnEoYSxiKXtBLklaKGIpCmlmKGI+PWEubGVuZ3RoKXRocm93IEEuYihBLmooYSxi -KSkKcmV0dXJuIGFbYl19LAokaXZYOjEsCiRpcVU6MX0KQS5CUi5wcm90b3R5cGU9ewpnTShhKXt2YXIg -cz1BLkxoKHRoaXMpCnJldHVybiBuZXcgQS5DZihKLklUKHRoaXMuZ09OKCkpLHMuQygiQDwxPiIpLktx -KHMuelsxXSkuQygiQ2Y8MSwyPiIpKX0sCmdrKGEpe3JldHVybiBKLkhtKHRoaXMuZ09OKCkpfSwKZ2ww -KGEpe3JldHVybiBKLnVVKHRoaXMuZ09OKCkpfSwKZ29yKGEpe3JldHVybiBKLkY3KHRoaXMuZ09OKCkp -fSwKZVIoYSxiKXt2YXIgcz1BLkxoKHRoaXMpCnJldHVybiBBLkdKKEouQTUodGhpcy5nT04oKSxiKSxz -LmMscy56WzFdKX0sCkEoYSxiKXtyZXR1cm4gQS5MaCh0aGlzKS56WzFdLmEoSi5HQSh0aGlzLmdPTigp -LGIpKX0sCiJbIihhKXtyZXR1cm4gSi5ZUyh0aGlzLmdPTigpKX19CkEuQ2YucHJvdG90eXBlPXsKRygp -e3JldHVybiB0aGlzLmEuRygpfSwKZ2woKXtyZXR1cm4gdGhpcy4kdGkuelsxXS5hKHRoaXMuYS5nbCgp -KX0sCiRpQW46MX0KQS5aeS5wcm90b3R5cGU9ewpnT04oKXtyZXR1cm4gdGhpcy5hfX0KQS5vbC5wcm90 -b3R5cGU9eyRpYlE6MX0KQS5VcS5wcm90b3R5cGU9ewpxKGEsYil7cmV0dXJuIHRoaXMuJHRpLnpbMV0u -YShKLng5KHRoaXMuYSxBLklaKGIpKSl9LApZNShhLGIsYyl7dmFyIHM9dGhpcy4kdGkKSi51OSh0aGlz -LmEsYixzLmMuYShzLnpbMV0uYShjKSkpfSwKJGliUToxLAokaXpNOjF9CkEualYucHJvdG90eXBlPXsK -ZHIoYSxiKXtyZXR1cm4gbmV3IEEualYodGhpcy5hLHRoaXMuJHRpLkMoIkA8MT4iKS5LcShiKS5DKCJq -VjwxLDI+IikpfSwKZ09OKCl7cmV0dXJuIHRoaXMuYX19CkEuYy5wcm90b3R5cGU9ewoiWyIoYSl7cmV0 -dXJuIkxhdGVJbml0aWFsaXphdGlvbkVycm9yOiAiK3RoaXMuYX19CkEucWoucHJvdG90eXBlPXsKZ2so -YSl7cmV0dXJuIHRoaXMuYS5sZW5ndGh9LApxKGEsYil7cmV0dXJuIEIueEIuTyh0aGlzLmEsQS5JWihi -KSl9fQpBLkhiLnByb3RvdHlwZT17fQpBLmJRLnByb3RvdHlwZT17fQpBLmFMLnByb3RvdHlwZT17CmdN -KGEpe3ZhciBzPXRoaXMKcmV0dXJuIG5ldyBBLmE3KHMscy5nayhzKSxBLkxoKHMpLkMoImE3PGFMLkU+ -IikpfSwKZ2wwKGEpe3JldHVybiB0aGlzLmdrKHRoaXMpPT09MH0sCkgoYSxiKXt2YXIgcyxyLHEscD10 -aGlzLG89cC5nayhwKQppZihiLmxlbmd0aCE9PTApe2lmKG89PT0wKXJldHVybiIiCnM9QS5kKHAuQSgw -LDApKQppZihvIT09cC5nayhwKSl0aHJvdyBBLmIoQS5hNChwKSkKZm9yKHI9cyxxPTE7cTxvOysrcSl7 -cj1yK2IrQS5kKHAuQSgwLHEpKQppZihvIT09cC5nayhwKSl0aHJvdyBBLmIoQS5hNChwKSl9cmV0dXJu -IHIuY2hhckNvZGVBdCgwKT09MD9yOnJ9ZWxzZXtmb3IocT0wLHI9IiI7cTxvOysrcSl7cis9QS5kKHAu -QSgwLHEpKQppZihvIT09cC5nayhwKSl0aHJvdyBBLmIoQS5hNChwKSl9cmV0dXJuIHIuY2hhckNvZGVB -dCgwKT09MD9yOnJ9fSwKZXYoYSxiKXtyZXR1cm4gdGhpcy5HRygwLEEuTGgodGhpcykuQygiYTIoYUwu -RSkiKS5hKGIpKX0sCkUyKGEsYixjKXt2YXIgcz1BLkxoKHRoaXMpCnJldHVybiBuZXcgQS5sSih0aGlz -LHMuS3EoYykuQygiMShhTC5FKSIpLmEoYikscy5DKCJAPGFMLkU+IikuS3EoYykuQygibEo8MSwyPiIp -KX0sCk4wKGEsYixjLGQpe3ZhciBzLHIscSxwPXRoaXMKZC5hKGIpCkEuTGgocCkuS3EoZCkuQygiMSgx -LGFMLkUpIikuYShjKQpzPXAuZ2socCkKZm9yKHI9YixxPTA7cTxzOysrcSl7cj1jLiQyKHIscC5BKDAs -cSkpCmlmKHMhPT1wLmdrKHApKXRocm93IEEuYihBLmE0KHApKX1yZXR1cm4gcn0sCmVSKGEsYil7cmV0 -dXJuIEEucUModGhpcyxiLG51bGwsQS5MaCh0aGlzKS5DKCJhTC5FIikpfSwKdHQoYSxiKXtyZXR1cm4g -QS5ZMSh0aGlzLCEwLEEuTGgodGhpcykuQygiYUwuRSIpKX0sCmJyKGEpe3JldHVybiB0aGlzLnR0KGEs -ITApfX0KQS5uSC5wcm90b3R5cGU9ewpIZChhLGIsYyxkKXt2YXIgcyxyPXRoaXMuYgpBLmsxKHIsInN0 -YXJ0IikKcz10aGlzLmMKaWYocyE9bnVsbCl7QS5rMShzLCJlbmQiKQppZihyPnMpdGhyb3cgQS5iKEEu -VEUociwwLHMsInN0YXJ0IixudWxsKSl9fSwKZ1VEKCl7dmFyIHM9Si5IbSh0aGlzLmEpLHI9dGhpcy5j -CmlmKHI9PW51bGx8fHI+cylyZXR1cm4gcwpyZXR1cm4gcn0sCmdBcygpe3ZhciBzPUouSG0odGhpcy5h -KSxyPXRoaXMuYgppZihyPnMpcmV0dXJuIHMKcmV0dXJuIHJ9LApnayhhKXt2YXIgcyxyPUouSG0odGhp -cy5hKSxxPXRoaXMuYgppZihxPj1yKXJldHVybiAwCnM9dGhpcy5jCmlmKHM9PW51bGx8fHM+PXIpcmV0 -dXJuIHItcQppZih0eXBlb2YgcyE9PSJudW1iZXIiKXJldHVybiBzLkhOKCkKcmV0dXJuIHMtcX0sCkEo -YSxiKXt2YXIgcz10aGlzLHI9cy5nQXMoKStiCmlmKGI8MHx8cj49cy5nVUQoKSl0aHJvdyBBLmIoQS54 -RihiLHMuZ2socykscywiaW5kZXgiKSkKcmV0dXJuIEouR0Eocy5hLHIpfSwKZVIoYSxiKXt2YXIgcyxy -LHE9dGhpcwpBLmsxKGIsImNvdW50IikKcz1xLmIrYgpyPXEuYwppZihyIT1udWxsJiZzPj1yKXJldHVy -biBuZXcgQS5NQihxLiR0aS5DKCJNQjwxPiIpKQpyZXR1cm4gQS5xQyhxLmEscyxyLHEuJHRpLmMpfSwK -dHQoYSxiKXt2YXIgcyxyLHEscD10aGlzLG89cC5iLG49cC5hLG09Si5VNihuKSxsPW0uZ2sobiksaz1w -LmMKaWYoayE9bnVsbCYmazxsKWw9awpzPWwtbwppZihzPD0wKXtuPUouUWkoMCxwLiR0aS5jKQpyZXR1 -cm4gbn1yPUEuTzgocyxtLkEobixvKSwhMSxwLiR0aS5jKQpmb3IocT0xO3E8czsrK3Epe0IuTm0uWTUo -cixxLG0uQShuLG8rcSkpCmlmKG0uZ2sobik8bCl0aHJvdyBBLmIoQS5hNChwKSl9cmV0dXJuIHJ9fQpB -LmE3LnByb3RvdHlwZT17CmdsKCl7dmFyIHM9dGhpcy5kCnJldHVybiBzPT1udWxsP3RoaXMuJHRpLmMu -YShzKTpzfSwKRygpe3ZhciBzLHI9dGhpcyxxPXIuYSxwPUouVTYocSksbz1wLmdrKHEpCmlmKHIuYiE9 -PW8pdGhyb3cgQS5iKEEuYTQocSkpCnM9ci5jCmlmKHM+PW8pe3Iuc0kobnVsbCkKcmV0dXJuITF9ci5z -SShwLkEocSxzKSk7KytyLmMKcmV0dXJuITB9LApzSShhKXt0aGlzLmQ9dGhpcy4kdGkuQygiMT8iKS5h -KGEpfSwKJGlBbjoxfQpBLmkxLnByb3RvdHlwZT17CmdNKGEpe3ZhciBzPUEuTGgodGhpcykKcmV0dXJu -IG5ldyBBLk1IKEouSVQodGhpcy5hKSx0aGlzLmIscy5DKCJAPDE+IikuS3Eocy56WzFdKS5DKCJNSDwx -LDI+IikpfSwKZ2soYSl7cmV0dXJuIEouSG0odGhpcy5hKX0sCmdsMChhKXtyZXR1cm4gSi51VSh0aGlz -LmEpfSwKQShhLGIpe3JldHVybiB0aGlzLmIuJDEoSi5HQSh0aGlzLmEsYikpfX0KQS54eS5wcm90b3R5 -cGU9eyRpYlE6MX0KQS5NSC5wcm90b3R5cGU9ewpHKCl7dmFyIHM9dGhpcyxyPXMuYgppZihyLkcoKSl7 -cy5zSShzLmMuJDEoci5nbCgpKSkKcmV0dXJuITB9cy5zSShudWxsKQpyZXR1cm4hMX0sCmdsKCl7dmFy -IHM9dGhpcy5hCnJldHVybiBzPT1udWxsP3RoaXMuJHRpLnpbMV0uYShzKTpzfSwKc0koYSl7dGhpcy5h -PXRoaXMuJHRpLkMoIjI/IikuYShhKX19CkEubEoucHJvdG90eXBlPXsKZ2soYSl7cmV0dXJuIEouSG0o -dGhpcy5hKX0sCkEoYSxiKXtyZXR1cm4gdGhpcy5iLiQxKEouR0EodGhpcy5hLGIpKX19CkEuVTUucHJv -dG90eXBlPXsKZ00oYSl7cmV0dXJuIG5ldyBBLlNPKEouSVQodGhpcy5hKSx0aGlzLmIsdGhpcy4kdGku -QygiU088MT4iKSl9fQpBLlNPLnByb3RvdHlwZT17CkcoKXt2YXIgcyxyCmZvcihzPXRoaXMuYSxyPXRo -aXMuYjtzLkcoKTspaWYoQS5vVChyLiQxKHMuZ2woKSkpKXJldHVybiEwCnJldHVybiExfSwKZ2woKXty -ZXR1cm4gdGhpcy5hLmdsKCl9fQpBLkFNLnByb3RvdHlwZT17CmVSKGEsYil7QS5NUihiLCJjb3VudCIs -dC5TKQpBLmsxKGIsImNvdW50IikKcmV0dXJuIG5ldyBBLkFNKHRoaXMuYSx0aGlzLmIrYixBLkxoKHRo -aXMpLkMoIkFNPDE+IikpfSwKZ00oYSl7cmV0dXJuIG5ldyBBLlUxKEouSVQodGhpcy5hKSx0aGlzLmIs -QS5MaCh0aGlzKS5DKCJVMTwxPiIpKX19CkEuZDUucHJvdG90eXBlPXsKZ2soYSl7dmFyIHM9Si5IbSh0 -aGlzLmEpLXRoaXMuYgppZihzPj0wKXJldHVybiBzCnJldHVybiAwfSwKZVIoYSxiKXtBLk1SKGIsImNv -dW50Iix0LlMpCkEuazEoYiwiY291bnQiKQpyZXR1cm4gbmV3IEEuZDUodGhpcy5hLHRoaXMuYitiLHRo -aXMuJHRpKX0sCiRpYlE6MX0KQS5VMS5wcm90b3R5cGU9ewpHKCl7dmFyIHMscgpmb3Iocz10aGlzLmEs -cj0wO3I8dGhpcy5iOysrcilzLkcoKQp0aGlzLmI9MApyZXR1cm4gcy5HKCl9LApnbCgpe3JldHVybiB0 -aGlzLmEuZ2woKX19CkEuTUIucHJvdG90eXBlPXsKZ00oYSl7cmV0dXJuIEIuR3d9LApnbDAoYSl7cmV0 -dXJuITB9LApnayhhKXtyZXR1cm4gMH0sCkEoYSxiKXt0aHJvdyBBLmIoQS5URShiLDAsMCwiaW5kZXgi -LG51bGwpKX0sCmVSKGEsYil7QS5rMShiLCJjb3VudCIpCnJldHVybiB0aGlzfX0KQS5GdS5wcm90b3R5 -cGU9ewpHKCl7cmV0dXJuITF9LApnbCgpe3Rocm93IEEuYihBLldwKCkpfSwKJGlBbjoxfQpBLnU2LnBy -b3RvdHlwZT17CmdNKGEpe3JldHVybiBuZXcgQS5KQihKLklUKHRoaXMuYSksdGhpcy4kdGkuQygiSkI8 -MT4iKSl9fQpBLkpCLnByb3RvdHlwZT17CkcoKXt2YXIgcyxyCmZvcihzPXRoaXMuYSxyPXRoaXMuJHRp -LmM7cy5HKCk7KWlmKHIuYihzLmdsKCkpKXJldHVybiEwCnJldHVybiExfSwKZ2woKXtyZXR1cm4gdGhp -cy4kdGkuYy5hKHRoaXMuYS5nbCgpKX0sCiRpQW46MX0KQS5TVS5wcm90b3R5cGU9e30KQS5SZS5wcm90 -b3R5cGU9ewpZNShhLGIsYyl7QS5MaCh0aGlzKS5DKCJSZS5FIikuYShjKQp0aHJvdyBBLmIoQS5MNCgi -Q2Fubm90IG1vZGlmeSBhbiB1bm1vZGlmaWFibGUgbGlzdCIpKX19CkEudzIucHJvdG90eXBlPXt9CkEu -d3YucHJvdG90eXBlPXsKZ20oYSl7dmFyIHM9dGhpcy5faGFzaENvZGUKaWYocyE9bnVsbClyZXR1cm4g -cwpzPTY2NDU5NypKLlUzKHRoaXMuYSkmNTM2ODcwOTExCnRoaXMuX2hhc2hDb2RlPXMKcmV0dXJuIHN9 -LAoiWyIoYSl7cmV0dXJuJ1N5bWJvbCgiJytBLmQodGhpcy5hKSsnIiknfSwKRE4oYSxiKXtpZihiPT1u -dWxsKXJldHVybiExCnJldHVybiBiIGluc3RhbmNlb2YgQS53diYmdGhpcy5hPT1iLmF9LAokaUdEOjF9 -CkEuUUMucHJvdG90eXBlPXt9CkEuUEQucHJvdG90eXBlPXt9CkEuV1UucHJvdG90eXBlPXsKZ2wwKGEp -e3JldHVybiB0aGlzLmdrKHRoaXMpPT09MH0sCiJbIihhKXtyZXR1cm4gQS5uTyh0aGlzKX0sClk1KGEs -YixjKXt2YXIgcz1BLkxoKHRoaXMpCnMuYy5hKGIpCnMuelsxXS5hKGMpCkEuZGMoKX0sCmdQdShhKXty -ZXR1cm4gdGhpcy5xNCgwLEEuTGgodGhpcykuQygiTjM8MSwyPiIpKX0sCnE0KGEsYil7dmFyIHM9dGhp -cwpyZXR1cm4gQS5sMChmdW5jdGlvbigpe3ZhciByPWEKdmFyIHE9MCxwPTEsbyxuLG0sbCxrLGoKcmV0 -dXJuIGZ1bmN0aW9uICRhc3luYyRnUHUoYyxkKXtpZihjPT09MSl7bz1kCnE9cH13aGlsZSh0cnVlKXN3 -aXRjaChxKXtjYXNlIDA6bj1zLmd2YygpLG49bi5nTShuKSxtPUEuTGgocyksbD1tLnpbMV0sbT1tLkMo -IkA8MT4iKS5LcShsKS5DKCJOMzwxLDI+IikKY2FzZSAyOmlmKCFuLkcoKSl7cT0zCmJyZWFrfWs9bi5n -bCgpCmo9cy5xKDAsaykKcT00CnJldHVybiBuZXcgQS5OMyhrLGo9PW51bGw/bC5hKGopOmosbSkKY2Fz -ZSA0OnE9MgpicmVhawpjYXNlIDM6cmV0dXJuIEEuVGgoKQpjYXNlIDE6cmV0dXJuIEEuWW0obyl9fX0s -Yil9LAokaVowOjF9CkEuTFAucHJvdG90eXBlPXsKZ2soYSl7cmV0dXJuIHRoaXMuYX0sCng0KGEpe2lm -KHR5cGVvZiBhIT0ic3RyaW5nIilyZXR1cm4hMQppZigiX19wcm90b19fIj09PWEpcmV0dXJuITEKcmV0 -dXJuIHRoaXMuYi5oYXNPd25Qcm9wZXJ0eShhKX0sCnEoYSxiKXtpZighdGhpcy54NChiKSlyZXR1cm4g -bnVsbApyZXR1cm4gdGhpcy5iW0EubihiKV19LApLKGEsYil7dmFyIHMscixxLHAsbyxuPXRoaXMuJHRp -Cm4uQygifigxLDIpIikuYShiKQpzPXRoaXMuYwpmb3Iocj1zLmxlbmd0aCxxPXRoaXMuYixuPW4uelsx -XSxwPTA7cDxyOysrcCl7bz1BLm4oc1twXSkKYi4kMihvLG4uYShxW29dKSl9fSwKZ3ZjKCl7cmV0dXJu -IG5ldyBBLlhSKHRoaXMsdGhpcy4kdGkuQygiWFI8MT4iKSl9fQpBLlhSLnByb3RvdHlwZT17CmdNKGEp -e3ZhciBzPXRoaXMuYS5jCnJldHVybiBuZXcgSi5tMShzLHMubGVuZ3RoLEEudDYocykuQygibTE8MT4i -KSl9LApnayhhKXtyZXR1cm4gdGhpcy5hLmMubGVuZ3RofX0KQS5MSS5wcm90b3R5cGU9ewpnV2EoKXt2 -YXIgcz10aGlzLmEKcmV0dXJuIHN9LApnbmQoKXt2YXIgcyxyLHEscCxvPXRoaXMKaWYoby5jPT09MSly -ZXR1cm4gQi5oVQpzPW8uZApyPXMubGVuZ3RoLW8uZS5sZW5ndGgtby5mCmlmKHI9PT0wKXJldHVybiBC -LmhVCnE9W10KZm9yKHA9MDtwPHI7KytwKXtpZighKHA8cy5sZW5ndGgpKXJldHVybiBBLk9IKHMscCkK -cS5wdXNoKHNbcF0pfXJldHVybiBKLnpDKHEpfSwKZ1ZtKCl7dmFyIHMscixxLHAsbyxuLG0sbCxrPXRo -aXMKaWYoay5jIT09MClyZXR1cm4gQi5XTwpzPWsuZQpyPXMubGVuZ3RoCnE9ay5kCnA9cS5sZW5ndGgt -ci1rLmYKaWYocj09PTApcmV0dXJuIEIuV08Kbz1uZXcgQS5ONSh0LmVvKQpmb3Iobj0wO248cjsrK24p -e2lmKCEobjxzLmxlbmd0aCkpcmV0dXJuIEEuT0gocyxuKQptPXNbbl0KbD1wK24KaWYoIShsPj0wJiZs -PHEubGVuZ3RoKSlyZXR1cm4gQS5PSChxLGwpCm8uWTUoMCxuZXcgQS53dihtKSxxW2xdKX1yZXR1cm4g -bmV3IEEuUEQobyx0LmdGKX0sCiRpdlE6MX0KQS5Dai5wcm90b3R5cGU9ewokMihhLGIpe3ZhciBzCkEu -bihhKQpzPXRoaXMuYQpzLmI9cy5iKyIkIithCkIuTm0uaSh0aGlzLmIsYSkKQi5ObS5pKHRoaXMuYyxi -KTsrK3MuYX0sCiRTOjEyfQpBLmY5LnByb3RvdHlwZT17CnFTKGEpe3ZhciBzLHIscT10aGlzLHA9bmV3 -IFJlZ0V4cChxLmEpLmV4ZWMoYSkKaWYocD09bnVsbClyZXR1cm4gbnVsbApzPU9iamVjdC5jcmVhdGUo -bnVsbCkKcj1xLmIKaWYociE9PS0xKXMuYXJndW1lbnRzPXBbcisxXQpyPXEuYwppZihyIT09LTEpcy5h -cmd1bWVudHNFeHByPXBbcisxXQpyPXEuZAppZihyIT09LTEpcy5leHByPXBbcisxXQpyPXEuZQppZihy -IT09LTEpcy5tZXRob2Q9cFtyKzFdCnI9cS5mCmlmKHIhPT0tMSlzLnJlY2VpdmVyPXBbcisxXQpyZXR1 -cm4gc319CkEuVzAucHJvdG90eXBlPXsKIlsiKGEpe3ZhciBzPXRoaXMuYgppZihzPT1udWxsKXJldHVy -biJOb1N1Y2hNZXRob2RFcnJvcjogIit0aGlzLmEKcmV0dXJuIk5vU3VjaE1ldGhvZEVycm9yOiBtZXRo -b2Qgbm90IGZvdW5kOiAnIitzKyInIG9uIG51bGwifX0KQS5hei5wcm90b3R5cGU9ewoiWyIoYSl7dmFy -IHMscj10aGlzLHE9Ik5vU3VjaE1ldGhvZEVycm9yOiBtZXRob2Qgbm90IGZvdW5kOiAnIixwPXIuYgpp -ZihwPT1udWxsKXJldHVybiJOb1N1Y2hNZXRob2RFcnJvcjogIityLmEKcz1yLmMKaWYocz09bnVsbCly -ZXR1cm4gcStwKyInICgiK3IuYSsiKSIKcmV0dXJuIHErcCsiJyBvbiAnIitzKyInICgiK3IuYSsiKSJ9 -fQpBLnZWLnByb3RvdHlwZT17CiJbIihhKXt2YXIgcz10aGlzLmEKcmV0dXJuIHMubGVuZ3RoPT09MD8i -RXJyb3IiOiJFcnJvcjogIitzfX0KQS50ZS5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIlRocm93IG9m -IG51bGwgKCciKyh0aGlzLmE9PT1udWxsPyJudWxsIjoidW5kZWZpbmVkIikrIicgZnJvbSBKYXZhU2Ny -aXB0KSJ9LAokaVJ6OjF9CkEuYnEucHJvdG90eXBlPXt9CkEuWE8ucHJvdG90eXBlPXsKIlsiKGEpe3Zh -ciBzLHI9dGhpcy5iCmlmKHIhPW51bGwpcmV0dXJuIHIKcj10aGlzLmEKcz1yIT09bnVsbCYmdHlwZW9m -IHI9PT0ib2JqZWN0Ij9yLnN0YWNrOm51bGwKcmV0dXJuIHRoaXMuYj1zPT1udWxsPyIiOnN9LAokaUd6 -OjF9CkEuVHAucHJvdG90eXBlPXsKIlsiKGEpe3ZhciBzPXRoaXMuY29uc3RydWN0b3Iscj1zPT1udWxs -P251bGw6cy5uYW1lCnJldHVybiJDbG9zdXJlICciK0EuTlEocj09bnVsbD8idW5rbm93biI6cikrIici -fSwKJGlFSDoxLApnS3UoKXtyZXR1cm4gdGhpc30sCiRDOiIkMSIsCiRSOjEsCiREOm51bGx9CkEuQXku -cHJvdG90eXBlPXskQzoiJDAiLCRSOjB9CkEuRTEucHJvdG90eXBlPXskQzoiJDIiLCRSOjJ9CkEubGMu -cHJvdG90eXBlPXt9CkEuengucHJvdG90eXBlPXsKIlsiKGEpe3ZhciBzPXRoaXMuJHN0YXRpY19uYW1l -CmlmKHM9PW51bGwpcmV0dXJuIkNsb3N1cmUgb2YgdW5rbm93biBzdGF0aWMgbWV0aG9kIgpyZXR1cm4i -Q2xvc3VyZSAnIitBLk5RKHMpKyInIn19CkEuclQucHJvdG90eXBlPXsKRE4oYSxiKXtpZihiPT1udWxs -KXJldHVybiExCmlmKHRoaXM9PT1iKXJldHVybiEwCmlmKCEoYiBpbnN0YW5jZW9mIEEuclQpKXJldHVy -biExCnJldHVybiB0aGlzLiRfdGFyZ2V0PT09Yi4kX3RhcmdldCYmdGhpcy5hPT09Yi5hfSwKZ20oYSl7 -cmV0dXJuKEEuQ1UodGhpcy5hKV5BLmVRKHRoaXMuJF90YXJnZXQpKT4+PjB9LAoiWyIoYSl7cmV0dXJu -IkNsb3N1cmUgJyIrdGhpcy4kX25hbWUrIicgb2YgIisoIkluc3RhbmNlIG9mICciK0EubGgodGhpcy5h -KSsiJyIpfX0KQS5FcS5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIlJ1bnRpbWVFcnJvcjogIit0aGlz -LmF9fQpBLmtZLnByb3RvdHlwZT17CiJbIihhKXtyZXR1cm4iQXNzZXJ0aW9uIGZhaWxlZDogIitBLmhs -KHRoaXMuYSl9fQpBLmtyLnByb3RvdHlwZT17fQpBLk41LnByb3RvdHlwZT17CmdrKGEpe3JldHVybiB0 -aGlzLmF9LApnbDAoYSl7cmV0dXJuIHRoaXMuYT09PTB9LApndmMoKXtyZXR1cm4gbmV3IEEuaTUodGhp -cyxBLkxoKHRoaXMpLkMoImk1PDE+IikpfSwKeDQoYSl7dmFyIHM9dGhpcy5iCmlmKHM9PW51bGwpcmV0 -dXJuITEKcmV0dXJuIHNbYV0hPW51bGx9LApDWChhKXt2YXIgcz10aGlzLmQKaWYocz09bnVsbClyZXR1 -cm4hMQpyZXR1cm4gdGhpcy5GaChzW3RoaXMueGkoYSldLGEpPj0wfSwKRlYoYSxiKXtBLkxoKHRoaXMp -LkMoIlowPDEsMj4iKS5hKGIpLksoMCxuZXcgQS5ldyh0aGlzKSl9LApxKGEsYil7dmFyIHMscixxLHAs -bz1udWxsCmlmKHR5cGVvZiBiPT0ic3RyaW5nIil7cz10aGlzLmIKaWYocz09bnVsbClyZXR1cm4gbwpy -PXNbYl0KcT1yPT1udWxsP286ci5iCnJldHVybiBxfWVsc2UgaWYodHlwZW9mIGI9PSJudW1iZXIiJiYo -YiYweDNmZmZmZmZmKT09PWIpe3A9dGhpcy5jCmlmKHA9PW51bGwpcmV0dXJuIG8Kcj1wW2JdCnE9cj09 -bnVsbD9vOnIuYgpyZXR1cm4gcX1lbHNlIHJldHVybiB0aGlzLmFhKGIpfSwKYWEoYSl7dmFyIHMscixx -PXRoaXMuZAppZihxPT1udWxsKXJldHVybiBudWxsCnM9cVt0aGlzLnhpKGEpXQpyPXRoaXMuRmgocyxh -KQppZihyPDApcmV0dXJuIG51bGwKcmV0dXJuIHNbcl0uYn0sClk1KGEsYixjKXt2YXIgcyxyLHE9dGhp -cyxwPUEuTGgocSkKcC5jLmEoYikKcC56WzFdLmEoYykKaWYodHlwZW9mIGI9PSJzdHJpbmciKXtzPXEu -YgpxLkVIKHM9PW51bGw/cS5iPXEueksoKTpzLGIsYyl9ZWxzZSBpZih0eXBlb2YgYj09Im51bWJlciIm -JihiJjB4M2ZmZmZmZmYpPT09Yil7cj1xLmMKcS5FSChyPT1udWxsP3EuYz1xLnpLKCk6cixiLGMpfWVs -c2UgcS54dyhiLGMpfSwKeHcoYSxiKXt2YXIgcyxyLHEscCxvPXRoaXMsbj1BLkxoKG8pCm4uYy5hKGEp -Cm4uelsxXS5hKGIpCnM9by5kCmlmKHM9PW51bGwpcz1vLmQ9by56SygpCnI9by54aShhKQpxPXNbcl0K -aWYocT09bnVsbClzW3JdPVtvLkhuKGEsYildCmVsc2V7cD1vLkZoKHEsYSkKaWYocD49MClxW3BdLmI9 -YgplbHNlIHEucHVzaChvLkhuKGEsYikpfX0sCksoYSxiKXt2YXIgcyxyLHE9dGhpcwpBLkxoKHEpLkMo -In4oMSwyKSIpLmEoYikKcz1xLmUKcj1xLnIKZm9yKDtzIT1udWxsOyl7Yi4kMihzLmEscy5iKQppZihy -IT09cS5yKXRocm93IEEuYihBLmE0KHEpKQpzPXMuY319LApFSChhLGIsYyl7dmFyIHMscj1BLkxoKHRo -aXMpCnIuYy5hKGIpCnIuelsxXS5hKGMpCnM9YVtiXQppZihzPT1udWxsKWFbYl09dGhpcy5IbihiLGMp -CmVsc2Ugcy5iPWN9LAprcygpe3RoaXMucj10aGlzLnIrMSYxMDczNzQxODIzfSwKSG4oYSxiKXt2YXIg -cz10aGlzLHI9QS5MaChzKSxxPW5ldyBBLnZoKHIuYy5hKGEpLHIuelsxXS5hKGIpKQppZihzLmU9PW51 -bGwpcy5lPXMuZj1xCmVsc2V7cj1zLmYKci50b1N0cmluZwpxLmQ9cgpzLmY9ci5jPXF9KytzLmEKcy5r -cygpCnJldHVybiBxfSwKeGkoYSl7cmV0dXJuIEouVTMoYSkmMHgzZmZmZmZmZn0sCkZoKGEsYil7dmFy -IHMscgppZihhPT1udWxsKXJldHVybi0xCnM9YS5sZW5ndGgKZm9yKHI9MDtyPHM7KytyKWlmKEouUk0o -YVtyXS5hLGIpKXJldHVybiByCnJldHVybi0xfSwKIlsiKGEpe3JldHVybiBBLm5PKHRoaXMpfSwKekso -KXt2YXIgcz1PYmplY3QuY3JlYXRlKG51bGwpCnNbIjxub24taWRlbnRpZmllci1rZXk+Il09cwpkZWxl -dGUgc1siPG5vbi1pZGVudGlmaWVyLWtleT4iXQpyZXR1cm4gc30sCiRpRm86MX0KQS5ldy5wcm90b3R5 -cGU9ewokMihhLGIpe3ZhciBzPXRoaXMuYSxyPUEuTGgocykKcy5ZNSgwLHIuYy5hKGEpLHIuelsxXS5h -KGIpKX0sCiRTKCl7cmV0dXJuIEEuTGgodGhpcy5hKS5DKCJ+KDEsMikiKX19CkEudmgucHJvdG90eXBl -PXt9CkEuaTUucHJvdG90eXBlPXsKZ2soYSl7cmV0dXJuIHRoaXMuYS5hfSwKZ2wwKGEpe3JldHVybiB0 -aGlzLmEuYT09PTB9LApnTShhKXt2YXIgcz10aGlzLmEscj1uZXcgQS5ONihzLHMucix0aGlzLiR0aS5D -KCJONjwxPiIpKQpyLmM9cy5lCnJldHVybiByfSwKdGcoYSxiKXtyZXR1cm4gdGhpcy5hLng0KGIpfX0K -QS5ONi5wcm90b3R5cGU9ewpnbCgpe3JldHVybiB0aGlzLmR9LApHKCl7dmFyIHMscj10aGlzLHE9ci5h -CmlmKHIuYiE9PXEucil0aHJvdyBBLmIoQS5hNChxKSkKcz1yLmMKaWYocz09bnVsbCl7ci5zcVkobnVs -bCkKcmV0dXJuITF9ZWxzZXtyLnNxWShzLmEpCnIuYz1zLmMKcmV0dXJuITB9fSwKc3FZKGEpe3RoaXMu -ZD10aGlzLiR0aS5DKCIxPyIpLmEoYSl9LAokaUFuOjF9CkEuZEMucHJvdG90eXBlPXsKJDEoYSl7cmV0 -dXJuIHRoaXMuYShhKX0sCiRTOjN9CkEud04ucHJvdG90eXBlPXsKJDIoYSxiKXtyZXR1cm4gdGhpcy5h -KGEsYil9LAokUzo0Mn0KQS5WWC5wcm90b3R5cGU9ewokMShhKXtyZXR1cm4gdGhpcy5hKEEubihhKSl9 -LAokUzo0MX0KQS5WUi5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIlJlZ0V4cC8iK3RoaXMuYSsiLyIr -dGhpcy5iLmZsYWdzfSwKZ0hjKCl7dmFyIHM9dGhpcyxyPXMuYwppZihyIT1udWxsKXJldHVybiByCnI9 -cy5iCnJldHVybiBzLmM9QS52NChzLmEsci5tdWx0aWxpbmUsIXIuaWdub3JlQ2FzZSxyLnVuaWNvZGUs -ci5kb3RBbGwsITApfSwKZGQoYSxiKXtyZXR1cm4gbmV3IEEuS1codGhpcyxiLDApfSwKVVooYSxiKXt2 -YXIgcyxyPXRoaXMuZ0hjKCkKaWYocj09bnVsbClyPXQuSy5hKHIpCnIubGFzdEluZGV4PWIKcz1yLmV4 -ZWMoYSkKaWYocz09bnVsbClyZXR1cm4gbnVsbApyZXR1cm4gbmV3IEEuRUsocyl9LAokaXZYOjEsCiRp -d0w6MX0KQS5FSy5wcm90b3R5cGU9ewpxKGEsYil7dmFyIHMKQS5JWihiKQpzPXRoaXMuYgppZighKGI8 -cy5sZW5ndGgpKXJldHVybiBBLk9IKHMsYikKcmV0dXJuIHNbYl19LAokaU9kOjEsCiRpaWI6MX0KQS5L -Vy5wcm90b3R5cGU9ewpnTShhKXtyZXR1cm4gbmV3IEEuUGIodGhpcy5hLHRoaXMuYix0aGlzLmMpfX0K -QS5QYi5wcm90b3R5cGU9ewpnbCgpe3ZhciBzPXRoaXMuZApyZXR1cm4gcz09bnVsbD90LmN6LmEocyk6 -c30sCkcoKXt2YXIgcyxyLHEscCxvLG4sbT10aGlzLGw9bS5iCmlmKGw9PW51bGwpcmV0dXJuITEKcz1t -LmMKcj1sLmxlbmd0aAppZihzPD1yKXtxPW0uYQpwPXEuVVoobCxzKQppZihwIT1udWxsKXttLmQ9cApz -PXAuYgpvPXMuaW5kZXgKbj1vK3NbMF0ubGVuZ3RoCmlmKG89PT1uKXtpZihxLmIudW5pY29kZSl7cz1t -LmMKcT1zKzEKaWYocTxyKXtzPUIueEIuTyhsLHMpCmlmKHM+PTU1Mjk2JiZzPD01NjMxOSl7cz1CLnhC -Lk8obCxxKQpzPXM+PTU2MzIwJiZzPD01NzM0M31lbHNlIHM9ITF9ZWxzZSBzPSExfWVsc2Ugcz0hMQpu -PShzP24rMTpuKSsxfW0uYz1uCnJldHVybiEwfX1tLmI9bS5kPW51bGwKcmV0dXJuITF9LAokaUFuOjF9 -CkEudFEucHJvdG90eXBlPXsKcShhLGIpe0EuSVooYikKaWYoYiE9PTApQS52KEEuTzcoYixudWxsKSkK -cmV0dXJuIHRoaXMuY30sCiRpT2Q6MX0KQS51bi5wcm90b3R5cGU9ewpnTShhKXtyZXR1cm4gbmV3IEEu -U2QodGhpcy5hLHRoaXMuYix0aGlzLmMpfX0KQS5TZC5wcm90b3R5cGU9ewpHKCl7dmFyIHMscixxPXRo -aXMscD1xLmMsbz1xLmIsbj1vLmxlbmd0aCxtPXEuYSxsPW0ubGVuZ3RoCmlmKHArbj5sKXtxLmQ9bnVs -bApyZXR1cm4hMX1zPW0uaW5kZXhPZihvLHApCmlmKHM8MCl7cS5jPWwrMQpxLmQ9bnVsbApyZXR1cm4h -MX1yPXMrbgpxLmQ9bmV3IEEudFEocyxvKQpxLmM9cj09PXEuYz9yKzE6cgpyZXR1cm4hMH0sCmdsKCl7 -dmFyIHM9dGhpcy5kCnMudG9TdHJpbmcKcmV0dXJuIHN9LAokaUFuOjF9CkEuZUgucHJvdG90eXBlPXsk -aWVIOjEsJGlBUzoxfQpBLlhILnByb3RvdHlwZT17CmdrKGEpe3JldHVybiBhLmxlbmd0aH0sCiRpWGo6 -MX0KQS5EZy5wcm90b3R5cGU9ewpxKGEsYil7QS5JWihiKQpBLm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJu -IGFbYl19LApZNShhLGIsYyl7QS5yVihjKQpBLm9kKGIsYSxhLmxlbmd0aCkKYVtiXT1jfSwKJGliUTox -LAokaWNYOjEsCiRpek06MX0KQS5QZy5wcm90b3R5cGU9ewpZNShhLGIsYyl7QS5JWihjKQpBLm9kKGIs -YSxhLmxlbmd0aCkKYVtiXT1jfSwKJGliUToxLAokaWNYOjEsCiRpek06MX0KQS54ai5wcm90b3R5cGU9 -ewpxKGEsYil7QS5JWihiKQpBLm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFbYl19fQpBLmRFLnByb3Rv -dHlwZT17CnEoYSxiKXtBLklaKGIpCkEub2QoYixhLGEubGVuZ3RoKQpyZXR1cm4gYVtiXX19CkEuWkEu -cHJvdG90eXBlPXsKcShhLGIpe0EuSVooYikKQS5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0K -QS5kVC5wcm90b3R5cGU9ewpxKGEsYil7QS5JWihiKQpBLm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJuIGFb -Yl19fQpBLlBxLnByb3RvdHlwZT17CnEoYSxiKXtBLklaKGIpCkEub2QoYixhLGEubGVuZ3RoKQpyZXR1 -cm4gYVtiXX19CkEuZUUucHJvdG90eXBlPXsKZ2soYSl7cmV0dXJuIGEubGVuZ3RofSwKcShhLGIpe0Eu -SVooYikKQS5vZChiLGEsYS5sZW5ndGgpCnJldHVybiBhW2JdfX0KQS5WNi5wcm90b3R5cGU9ewpnayhh -KXtyZXR1cm4gYS5sZW5ndGh9LApxKGEsYil7QS5JWihiKQpBLm9kKGIsYSxhLmxlbmd0aCkKcmV0dXJu -IGFbYl19LAokaW42OjF9CkEuUkcucHJvdG90eXBlPXt9CkEuVlAucHJvdG90eXBlPXt9CkEuV0IucHJv -dG90eXBlPXt9CkEuWkcucHJvdG90eXBlPXt9CkEuSmMucHJvdG90eXBlPXsKQyhhKXtyZXR1cm4gQS5j -RSh2LnR5cGVVbml2ZXJzZSx0aGlzLGEpfSwKS3EoYSl7cmV0dXJuIEEudjUodi50eXBlVW5pdmVyc2Us -dGhpcyxhKX19CkEuRVQucHJvdG90eXBlPXt9CkEubFkucHJvdG90eXBlPXsKIlsiKGEpe3JldHVybiBB -LmRtKHRoaXMuYSxudWxsKX19CkEua1MucHJvdG90eXBlPXsKIlsiKGEpe3JldHVybiB0aGlzLmF9fQpB -LmlNLnByb3RvdHlwZT17JGlFejoxfQpBLnRoLnByb3RvdHlwZT17CiQxKGEpe3ZhciBzPXRoaXMuYSxy -PXMuYQpzLmE9bnVsbApyLiQwKCl9LAokUzo3fQpBLmhhLnByb3RvdHlwZT17CiQxKGEpe3ZhciBzLHIK -dGhpcy5hLmE9dC5NLmEoYSkKcz10aGlzLmIKcj10aGlzLmMKcy5maXJzdENoaWxkP3MucmVtb3ZlQ2hp -bGQocik6cy5hcHBlbmRDaGlsZChyKX0sCiRTOjIyfQpBLlZzLnByb3RvdHlwZT17CiQwKCl7dGhpcy5h -LiQwKCl9LAokUzo4fQpBLkZ0LnByb3RvdHlwZT17CiQwKCl7dGhpcy5hLiQwKCl9LAokUzo4fQpBLlcz -LnByb3RvdHlwZT17CkNZKGEsYil7aWYoc2VsZi5zZXRUaW1lb3V0IT1udWxsKXNlbGYuc2V0VGltZW91 -dChBLnRSKG5ldyBBLnlIKHRoaXMsYiksMCksYSkKZWxzZSB0aHJvdyBBLmIoQS5MNCgiYHNldFRpbWVv -dXQoKWAgbm90IGZvdW5kLiIpKX19CkEueUgucHJvdG90eXBlPXsKJDAoKXt0aGlzLmIuJDAoKX0sCiRT -OjB9CkEuaWgucHJvdG90eXBlPXsKYU0oYSxiKXt2YXIgcyxyPXRoaXMscT1yLiR0aQpxLkMoIjEvPyIp -LmEoYikKaWYoYj09bnVsbClxLmMuYShiKQppZighci5iKXIuYS5YZihiKQplbHNle3M9ci5hCmlmKHEu -QygiYjg8MT4iKS5iKGIpKXMuY1UoYikKZWxzZSBzLlgyKHEuYy5hKGIpKX19LAp3MChhLGIpe3ZhciBz -PXRoaXMuYQppZih0aGlzLmIpcy5aTChhLGIpCmVsc2Ugcy5OayhhLGIpfX0KQS5XTS5wcm90b3R5cGU9 -ewokMShhKXtyZXR1cm4gdGhpcy5hLiQyKDAsYSl9LAokUzo1NX0KQS5TWC5wcm90b3R5cGU9ewokMihh -LGIpe3RoaXMuYS4kMigxLG5ldyBBLmJxKGEsdC5sLmEoYikpKX0sCiRTOjU3fQpBLkdzLnByb3RvdHlw -ZT17CiQyKGEsYil7dGhpcy5hKEEuSVooYSksYil9LAokUzoyNX0KQS5GeS5wcm90b3R5cGU9ewoiWyIo -YSl7cmV0dXJuIkl0ZXJhdGlvbk1hcmtlcigiK3RoaXMuYisiLCAiK0EuZCh0aGlzLmEpKyIpIn19CkEu -R1YucHJvdG90eXBlPXsKZ2woKXt2YXIgcyxyPXRoaXMuYwppZihyPT1udWxsKXtzPXRoaXMuYgpyZXR1 -cm4gcz09bnVsbD90aGlzLiR0aS5jLmEocyk6c31yZXR1cm4gci5nbCgpfSwKRygpe3ZhciBzLHIscSxw -LG8sbixtPXRoaXMKZm9yKHM9bS4kdGkuQygiQW48MT4iKTshMDspe3I9bS5jCmlmKHIhPW51bGwpaWYo -ci5HKCkpcmV0dXJuITAKZWxzZSBtLnNYOShudWxsKQpxPWZ1bmN0aW9uKGEsYixjKXt2YXIgbCxrPWIK -d2hpbGUodHJ1ZSl0cnl7cmV0dXJuIGEoayxsKX1jYXRjaChqKXtsPWoKaz1jfX0obS5hLDAsMSkKaWYo -cSBpbnN0YW5jZW9mIEEuRnkpe3A9cS5iCmlmKHA9PT0yKXtvPW0uZAppZihvPT1udWxsfHxvLmxlbmd0 -aD09PTApe20uc0VDKG51bGwpCnJldHVybiExfWlmKDA+PW8ubGVuZ3RoKXJldHVybiBBLk9IKG8sLTEp -Cm0uYT1vLnBvcCgpCmNvbnRpbnVlfWVsc2V7cj1xLmEKaWYocD09PTMpdGhyb3cgcgplbHNle249cy5h -KEouSVQocikpCmlmKG4gaW5zdGFuY2VvZiBBLkdWKXtyPW0uZAppZihyPT1udWxsKXI9bS5kPVtdCkIu -Tm0uaShyLG0uYSkKbS5hPW4uYQpjb250aW51ZX1lbHNle20uc1g5KG4pCmNvbnRpbnVlfX19fWVsc2V7 -bS5zRUMocSkKcmV0dXJuITB9fXJldHVybiExfSwKc0VDKGEpe3RoaXMuYj10aGlzLiR0aS5DKCIxPyIp -LmEoYSl9LApzWDkoYSl7dGhpcy5jPXRoaXMuJHRpLkMoIkFuPDE+PyIpLmEoYSl9LAokaUFuOjF9CkEu -cTQucHJvdG90eXBlPXsKZ00oYSl7cmV0dXJuIG5ldyBBLkdWKHRoaXMuYSgpLHRoaXMuJHRpLkMoIkdW -PDE+IikpfX0KQS5Ddy5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIEEuZCh0aGlzLmEpfSwKJGlYUzox -LApnSUkoKXtyZXR1cm4gdGhpcy5ifX0KQS5QZi5wcm90b3R5cGU9ewp3MChhLGIpe3ZhciBzCkEuY2Io -YSwiZXJyb3IiLHQuSykKcz10aGlzLmEKaWYoKHMuYSYzMCkhPT0wKXRocm93IEEuYihBLlBWKCJGdXR1 -cmUgYWxyZWFkeSBjb21wbGV0ZWQiKSkKaWYoYj09bnVsbCliPUEudjAoYSkKcy5OayhhLGIpfSwKcG0o -YSl7cmV0dXJuIHRoaXMudzAoYSxudWxsKX19CkEuWmYucHJvdG90eXBlPXsKYU0oYSxiKXt2YXIgcyxy -PXRoaXMuJHRpCnIuQygiMS8/IikuYShiKQpzPXRoaXMuYQppZigocy5hJjMwKSE9PTApdGhyb3cgQS5i -KEEuUFYoIkZ1dHVyZSBhbHJlYWR5IGNvbXBsZXRlZCIpKQpzLlhmKHIuQygiMS8iKS5hKGIpKX19CkEu -RmUucHJvdG90eXBlPXsKSFIoYSl7aWYoKHRoaXMuYyYxNSkhPT02KXJldHVybiEwCnJldHVybiB0aGlz -LmIuYi5idih0LmFsLmEodGhpcy5kKSxhLmEsdC55LHQuSyl9LApLdyhhKXt2YXIgcyxyPXRoaXMscT1y -LmUscD1udWxsLG89dC56LG49dC5LLG09YS5hLGw9ci5iLmIKaWYodC5tLmIocSkpcD1sLnJwKHEsbSxh -LmIsbyxuLHQubCkKZWxzZSBwPWwuYnYodC52LmEocSksbSxvLG4pCnRyeXtvPXIuJHRpLkMoIjIvIiku -YShwKQpyZXR1cm4gb31jYXRjaChzKXtpZih0LmVLLmIoQS5SdShzKSkpe2lmKChyLmMmMSkhPT0wKXRo -cm93IEEuYihBLnhZKCJUaGUgZXJyb3IgaGFuZGxlciBvZiBGdXR1cmUudGhlbiBtdXN0IHJldHVybiBh -IHZhbHVlIG9mIHRoZSByZXR1cm5lZCBmdXR1cmUncyB0eXBlIiwib25FcnJvciIpKQp0aHJvdyBBLmIo -QS54WSgiVGhlIGVycm9yIGhhbmRsZXIgb2YgRnV0dXJlLmNhdGNoRXJyb3IgbXVzdCByZXR1cm4gYSB2 -YWx1ZSBvZiB0aGUgZnV0dXJlJ3MgdHlwZSIsIm9uRXJyb3IiKSl9ZWxzZSB0aHJvdyBzfX19CkEudnMu -cHJvdG90eXBlPXsKU3EoYSxiLGMpe3ZhciBzLHIscSxwPXRoaXMuJHRpCnAuS3EoYykuQygiMS8oMiki -KS5hKGEpCnM9JC5YMwppZihzPT09Qi5OVSl7aWYoYiE9bnVsbCYmIXQubS5iKGIpJiYhdC52LmIoYikp -dGhyb3cgQS5iKEEuTDMoYiwib25FcnJvciIsdS5jKSl9ZWxzZXtjLkMoIkA8MC8+IikuS3EocC5jKS5D -KCIxKDIpIikuYShhKQppZihiIT1udWxsKWI9QS5WSChiLHMpfXI9bmV3IEEudnMocyxjLkMoInZzPDA+ -IikpCnE9Yj09bnVsbD8xOjMKdGhpcy54ZihuZXcgQS5GZShyLHEsYSxiLHAuQygiQDwxPiIpLktxKGMp -LkMoIkZlPDEsMj4iKSkpCnJldHVybiByfSwKVzcoYSxiKXtyZXR1cm4gdGhpcy5TcShhLG51bGwsYil9 -LApRZChhLGIsYyl7dmFyIHMscj10aGlzLiR0aQpyLktxKGMpLkMoIjEvKDIpIikuYShhKQpzPW5ldyBB -LnZzKCQuWDMsYy5DKCJ2czwwPiIpKQp0aGlzLnhmKG5ldyBBLkZlKHMsMyxhLGIsci5DKCJAPDE+Iiku -S3EoYykuQygiRmU8MSwyPiIpKSkKcmV0dXJuIHN9LApQOShhKXt0aGlzLmE9dGhpcy5hJjF8MTYKdGhp -cy5jPWF9LAp1ZyhhKXt0aGlzLmE9YS5hJjMwfHRoaXMuYSYxCnRoaXMuYz1hLmN9LAp4ZihhKXt2YXIg -cyxyPXRoaXMscT1yLmEKaWYocTw9Myl7YS5hPXQuZS5hKHIuYykKci5jPWF9ZWxzZXtpZigocSY0KSE9 -PTApe3M9dC5jLmEoci5jKQppZigocy5hJjI0KT09PTApe3MueGYoYSkKcmV0dXJufXIudWcocyl9QS5U -ayhudWxsLG51bGwsci5iLHQuTS5hKG5ldyBBLmRhKHIsYSkpKX19LApqUShhKXt2YXIgcyxyLHEscCxv -LG4sbT10aGlzLGw9e30KbC5hPWEKaWYoYT09bnVsbClyZXR1cm4Kcz1tLmEKaWYoczw9Myl7cj10LmUu -YShtLmMpCm0uYz1hCmlmKHIhPW51bGwpe3E9YS5hCmZvcihwPWE7cSE9bnVsbDtwPXEscT1vKW89cS5h -CnAuYT1yfX1lbHNle2lmKChzJjQpIT09MCl7bj10LmMuYShtLmMpCmlmKChuLmEmMjQpPT09MCl7bi5q -UShhKQpyZXR1cm59bS51ZyhuKX1sLmE9bS5OOChhKQpBLlRrKG51bGwsbnVsbCxtLmIsdC5NLmEobmV3 -IEEub1EobCxtKSkpfX0sCmFoKCl7dmFyIHM9dC5lLmEodGhpcy5jKQp0aGlzLmM9bnVsbApyZXR1cm4g -dGhpcy5OOChzKX0sCk44KGEpe3ZhciBzLHIscQpmb3Iocz1hLHI9bnVsbDtzIT1udWxsO3I9cyxzPXEp -e3E9cy5hCnMuYT1yfXJldHVybiByfSwKZWMoYSl7dmFyIHMscixxLHA9dGhpcwpwLmFePTIKdHJ5e2Eu -U3EobmV3IEEucFYocCksbmV3IEEuVTcocCksdC5QKX1jYXRjaChxKXtzPUEuUnUocSkKcj1BLnRzKHEp -CkEucmIobmV3IEEudnIocCxzLHIpKX19LApYMihhKXt2YXIgcyxyPXRoaXMKci4kdGkuYy5hKGEpCnM9 -ci5haCgpCnIuYT04CnIuYz1hCkEuSFoocixzKX0sClpMKGEsYil7dmFyIHMKdC5sLmEoYikKcz10aGlz -LmFoKCkKdGhpcy5QOShBLlRsKGEsYikpCkEuSFoodGhpcyxzKX0sClhmKGEpe3ZhciBzPXRoaXMuJHRp -CnMuQygiMS8iKS5hKGEpCmlmKHMuQygiYjg8MT4iKS5iKGEpKXt0aGlzLmNVKGEpCnJldHVybn10aGlz -LndVKHMuYy5hKGEpKX0sCndVKGEpe3ZhciBzPXRoaXMKcy4kdGkuYy5hKGEpCnMuYV49MgpBLlRrKG51 -bGwsbnVsbCxzLmIsdC5NLmEobmV3IEEucnQocyxhKSkpfSwKY1UoYSl7dmFyIHM9dGhpcyxyPXMuJHRp -CnIuQygiYjg8MT4iKS5hKGEpCmlmKHIuYihhKSl7aWYoKGEuYSYxNikhPT0wKXtzLmFePTIKQS5Uayhu -dWxsLG51bGwscy5iLHQuTS5hKG5ldyBBLktGKHMsYSkpKX1lbHNlIEEuQTkoYSxzKQpyZXR1cm59cy5l -YyhhKX0sCk5rKGEsYil7dGhpcy5hXj0yCkEuVGsobnVsbCxudWxsLHRoaXMuYix0Lk0uYShuZXcgQS5a -TCh0aGlzLGEsYikpKX0sCiRpYjg6MX0KQS5kYS5wcm90b3R5cGU9ewokMCgpe0EuSFoodGhpcy5hLHRo -aXMuYil9LAokUzowfQpBLm9RLnByb3RvdHlwZT17CiQwKCl7QS5IWih0aGlzLmIsdGhpcy5hLmEpfSwK -JFM6MH0KQS5wVi5wcm90b3R5cGU9ewokMShhKXt2YXIgcyxyLHEscD10aGlzLmEKcC5hXj0yCnRyeXtw -LlgyKHAuJHRpLmMuYShhKSl9Y2F0Y2gocSl7cz1BLlJ1KHEpCnI9QS50cyhxKQpwLlpMKHMscil9fSwK -JFM6N30KQS5VNy5wcm90b3R5cGU9ewokMihhLGIpe3RoaXMuYS5aTCh0LksuYShhKSx0LmwuYShiKSl9 -LAokUzoyOX0KQS52ci5wcm90b3R5cGU9ewokMCgpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRT -OjB9CkEucnQucHJvdG90eXBlPXsKJDAoKXt0aGlzLmEuWDIodGhpcy5iKX0sCiRTOjB9CkEuS0YucHJv -dG90eXBlPXsKJDAoKXtBLkE5KHRoaXMuYix0aGlzLmEpfSwKJFM6MH0KQS5aTC5wcm90b3R5cGU9ewok -MCgpe3RoaXMuYS5aTCh0aGlzLmIsdGhpcy5jKX0sCiRTOjB9CkEuUlQucHJvdG90eXBlPXsKJDAoKXt2 -YXIgcyxyLHEscCxvLG4sbT10aGlzLGw9bnVsbAp0cnl7cT1tLmEuYQpsPXEuYi5iLnp6KHQuZk8uYShx -LmQpLHQueil9Y2F0Y2gocCl7cz1BLlJ1KHApCnI9QS50cyhwKQpxPW0uYyYmdC5uLmEobS5iLmEuYyku -YT09PXMKbz1tLmEKaWYocSlvLmM9dC5uLmEobS5iLmEuYykKZWxzZSBvLmM9QS5UbChzLHIpCm8uYj0h -MApyZXR1cm59aWYobCBpbnN0YW5jZW9mIEEudnMmJihsLmEmMjQpIT09MCl7aWYoKGwuYSYxNikhPT0w -KXtxPW0uYQpxLmM9dC5uLmEobC5jKQpxLmI9ITB9cmV0dXJufWlmKHQuaS5iKGwpKXtuPW0uYi5hCnE9 -bS5hCnEuYz1sLlc3KG5ldyBBLmpaKG4pLHQueikKcS5iPSExfX0sCiRTOjB9CkEualoucHJvdG90eXBl -PXsKJDEoYSl7cmV0dXJuIHRoaXMuYX0sCiRTOjMwfQpBLnJxLnByb3RvdHlwZT17CiQwKCl7dmFyIHMs -cixxLHAsbyxuLG0sbAp0cnl7cT10aGlzLmEKcD1xLmEKbz1wLiR0aQpuPW8uYwptPW4uYSh0aGlzLmIp -CnEuYz1wLmIuYi5idihvLkMoIjIvKDEpIikuYShwLmQpLG0sby5DKCIyLyIpLG4pfWNhdGNoKGwpe3M9 -QS5SdShsKQpyPUEudHMobCkKcT10aGlzLmEKcS5jPUEuVGwocyxyKQpxLmI9ITB9fSwKJFM6MH0KQS5S -Vy5wcm90b3R5cGU9ewokMCgpe3ZhciBzLHIscSxwLG8sbixtPXRoaXMKdHJ5e3M9dC5uLmEobS5hLmEu -YykKcD1tLmIKaWYocC5hLkhSKHMpJiZwLmEuZSE9bnVsbCl7cC5jPXAuYS5LdyhzKQpwLmI9ITF9fWNh -dGNoKG8pe3I9QS5SdShvKQpxPUEudHMobykKcD10Lm4uYShtLmEuYS5jKQpuPW0uYgppZihwLmE9PT1y -KW4uYz1wCmVsc2Ugbi5jPUEuVGwocixxKQpuLmI9ITB9fSwKJFM6MH0KQS5PTS5wcm90b3R5cGU9e30K -QS5xaC5wcm90b3R5cGU9ewpnayhhKXt2YXIgcyxyLHE9dGhpcyxwPXt9LG89bmV3IEEudnMoJC5YMyx0 -LmZKKQpwLmE9MApzPUEuTGgocSkKcj1zLkMoIn4oMSk/IikuYShuZXcgQS5CNShwLHEpKQp0LlouYShu -ZXcgQS51TyhwLG8pKQpBLkpFKHEuYSxxLmIsciwhMSxzLmMpCnJldHVybiBvfX0KQS5CNS5wcm90b3R5 -cGU9ewokMShhKXtBLkxoKHRoaXMuYikuYy5hKGEpOysrdGhpcy5hLmF9LAokUygpe3JldHVybiBBLkxo -KHRoaXMuYikuQygifigxKSIpfX0KQS51Ty5wcm90b3R5cGU9ewokMCgpe3ZhciBzPXRoaXMuYixyPXMu -JHRpLHE9ci5DKCIxLyIpLmEodGhpcy5hLmEpLHA9cy5haCgpCnIuYy5hKHEpCnMuYT04CnMuYz1xCkEu -SFoocyxwKX0sCiRTOjB9CkEuTU8ucHJvdG90eXBlPXt9CkEua1QucHJvdG90eXBlPXt9CkEueEkucHJv -dG90eXBlPXt9CkEubTAucHJvdG90eXBlPXskaVFtOjF9CkEuRXYucHJvdG90eXBlPXsKJDAoKXt2YXIg -cz10aGlzLmEscj10aGlzLmIKQS5jYihzLCJlcnJvciIsdC5LKQpBLmNiKHIsInN0YWNrVHJhY2UiLHQu -bCkKQS5PMShzLHIpfSwKJFM6MH0KQS5KaS5wcm90b3R5cGU9ewpiSChhKXt2YXIgcyxyLHEKdC5NLmEo -YSkKdHJ5e2lmKEIuTlU9PT0kLlgzKXthLiQwKCkKcmV0dXJufUEuVDgobnVsbCxudWxsLHRoaXMsYSx0 -LkgpfWNhdGNoKHEpe3M9QS5SdShxKQpyPUEudHMocSkKQS5TaSh0LksuYShzKSx0LmwuYShyKSl9fSwK -RGwoYSxiLGMpe3ZhciBzLHIscQpjLkMoIn4oMCkiKS5hKGEpCmMuYShiKQp0cnl7aWYoQi5OVT09PSQu -WDMpe2EuJDEoYikKcmV0dXJufUEueXYobnVsbCxudWxsLHRoaXMsYSxiLHQuSCxjKX1jYXRjaChxKXtz -PUEuUnUocSkKcj1BLnRzKHEpCkEuU2kodC5LLmEocyksdC5sLmEocikpfX0sCkdZKGEpe3JldHVybiBu -ZXcgQS5WcCh0aGlzLHQuTS5hKGEpKX0sClB5KGEsYil7cmV0dXJuIG5ldyBBLk9SKHRoaXMsYi5DKCJ+ -KDApIikuYShhKSxiKX0sCnEoYSxiKXtyZXR1cm4gbnVsbH0sCnp6KGEsYil7Yi5DKCIwKCkiKS5hKGEp -CmlmKCQuWDM9PT1CLk5VKXJldHVybiBhLiQwKCkKcmV0dXJuIEEuVDgobnVsbCxudWxsLHRoaXMsYSxi -KX0sCmJ2KGEsYixjLGQpe2MuQygiQDwwPiIpLktxKGQpLkMoIjEoMikiKS5hKGEpCmQuYShiKQppZigk -LlgzPT09Qi5OVSlyZXR1cm4gYS4kMShiKQpyZXR1cm4gQS55dihudWxsLG51bGwsdGhpcyxhLGIsYyxk -KX0sCnJwKGEsYixjLGQsZSxmKXtkLkMoIkA8MD4iKS5LcShlKS5LcShmKS5DKCIxKDIsMykiKS5hKGEp -CmUuYShiKQpmLmEoYykKaWYoJC5YMz09PUIuTlUpcmV0dXJuIGEuJDIoYixjKQpyZXR1cm4gQS5ReChu -dWxsLG51bGwsdGhpcyxhLGIsYyxkLGUsZil9LApMaihhLGIsYyxkKXtyZXR1cm4gYi5DKCJAPDA+Iiku -S3EoYykuS3EoZCkuQygiMSgyLDMpIikuYShhKX19CkEuVnAucHJvdG90eXBlPXsKJDAoKXtyZXR1cm4g -dGhpcy5hLmJIKHRoaXMuYil9LAokUzowfQpBLk9SLnByb3RvdHlwZT17CiQxKGEpe3ZhciBzPXRoaXMu -YwpyZXR1cm4gdGhpcy5hLkRsKHRoaXMuYixzLmEoYSkscyl9LAokUygpe3JldHVybiB0aGlzLmMuQygi -figwKSIpfX0KQS54ZC5wcm90b3R5cGU9ewpxKGEsYil7aWYoIUEub1QodGhpcy55LiQxKGIpKSlyZXR1 -cm4gbnVsbApyZXR1cm4gdGhpcy5GUShiKX0sClk1KGEsYixjKXt2YXIgcz10aGlzLiR0aQp0aGlzLkox -KHMuYy5hKGIpLHMuelsxXS5hKGMpKX0sCng0KGEpe2lmKCFBLm9UKHRoaXMueS4kMShhKSkpcmV0dXJu -ITEKcmV0dXJuIHRoaXMuUEEoYSl9LAp4aShhKXtyZXR1cm4gdGhpcy54LiQxKHRoaXMuJHRpLmMuYShh -KSkmMTA3Mzc0MTgyM30sCkZoKGEsYil7dmFyIHMscixxLHAKaWYoYT09bnVsbClyZXR1cm4tMQpzPWEu -bGVuZ3RoCmZvcihyPXRoaXMuJHRpLmMscT10aGlzLncscD0wO3A8czsrK3ApaWYoQS5vVChxLiQyKHIu -YShhW3BdLmEpLHIuYShiKSkpKXJldHVybiBwCnJldHVybi0xfX0KQS52Ni5wcm90b3R5cGU9ewokMShh -KXtyZXR1cm4gdGhpcy5hLmIoYSl9LAokUzozNX0KQS5EMC5wcm90b3R5cGU9ewpnTShhKXt2YXIgcz10 -aGlzLHI9bmV3IEEubG0ocyxzLnIsQS5MaChzKS5DKCJsbTwxPiIpKQpyLmM9cy5lCnJldHVybiByfSwK -Z2soYSl7cmV0dXJuIHRoaXMuYX0sCmdsMChhKXtyZXR1cm4gdGhpcy5hPT09MH0sCmdvcihhKXtyZXR1 -cm4gdGhpcy5hIT09MH0sCnRnKGEsYil7dmFyIHMscgppZihiIT09Il9fcHJvdG9fXyIpe3M9dGhpcy5i -CmlmKHM9PW51bGwpcmV0dXJuITEKcmV0dXJuIHQuZy5hKHNbYl0pIT1udWxsfWVsc2V7cj10aGlzLlBS -KGIpCnJldHVybiByfX0sClBSKGEpe3ZhciBzPXRoaXMuZAppZihzPT1udWxsKXJldHVybiExCnJldHVy -biB0aGlzLnQoc1t0aGlzLlkoYSldLGEpPj0wfSwKaShhLGIpe3ZhciBzLHIscT10aGlzCkEuTGgocSku -Yy5hKGIpCmlmKHR5cGVvZiBiPT0ic3RyaW5nIiYmYiE9PSJfX3Byb3RvX18iKXtzPXEuYgpyZXR1cm4g -cS5iUShzPT1udWxsP3EuYj1BLlQyKCk6cyxiKX1lbHNlIGlmKHR5cGVvZiBiPT0ibnVtYmVyIiYmKGIm -MTA3Mzc0MTgyMyk9PT1iKXtyPXEuYwpyZXR1cm4gcS5iUShyPT1udWxsP3EuYz1BLlQyKCk6cixiKX1l -bHNlIHJldHVybiBxLkI3KGIpfSwKQjcoYSl7dmFyIHMscixxLHA9dGhpcwpBLkxoKHApLmMuYShhKQpz -PXAuZAppZihzPT1udWxsKXM9cC5kPUEuVDIoKQpyPXAuWShhKQpxPXNbcl0KaWYocT09bnVsbClzW3Jd -PVtwLnlvKGEpXQplbHNle2lmKHAudChxLGEpPj0wKXJldHVybiExCnEucHVzaChwLnlvKGEpKX1yZXR1 -cm4hMH0sClIoYSxiKXt2YXIgcz10aGlzCmlmKHR5cGVvZiBiPT0ic3RyaW5nIiYmYiE9PSJfX3Byb3Rv -X18iKXJldHVybiBzLkoocy5iLGIpCmVsc2UgaWYodHlwZW9mIGI9PSJudW1iZXIiJiYoYiYxMDczNzQx -ODIzKT09PWIpcmV0dXJuIHMuSihzLmMsYikKZWxzZSByZXR1cm4gcy5xZyhiKX0sCnFnKGEpe3ZhciBz -LHIscSxwLG89dGhpcyxuPW8uZAppZihuPT1udWxsKXJldHVybiExCnM9by5ZKGEpCnI9bltzXQpxPW8u -dChyLGEpCmlmKHE8MClyZXR1cm4hMQpwPXIuc3BsaWNlKHEsMSlbMF0KaWYoMD09PXIubGVuZ3RoKWRl -bGV0ZSBuW3NdCm8uVChwKQpyZXR1cm4hMH0sCmJRKGEsYil7QS5MaCh0aGlzKS5jLmEoYikKaWYodC5n -LmEoYVtiXSkhPW51bGwpcmV0dXJuITEKYVtiXT10aGlzLnlvKGIpCnJldHVybiEwfSwKSihhLGIpe3Zh -ciBzCmlmKGE9PW51bGwpcmV0dXJuITEKcz10LmcuYShhW2JdKQppZihzPT1udWxsKXJldHVybiExCnRo -aXMuVChzKQpkZWxldGUgYVtiXQpyZXR1cm4hMH0sClMoKXt0aGlzLnI9dGhpcy5yKzEmMTA3Mzc0MTgy -M30sCnlvKGEpe3ZhciBzLHI9dGhpcyxxPW5ldyBBLmJuKEEuTGgocikuYy5hKGEpKQppZihyLmU9PW51 -bGwpci5lPXIuZj1xCmVsc2V7cz1yLmYKcy50b1N0cmluZwpxLmM9cwpyLmY9cy5iPXF9KytyLmEKci5T -KCkKcmV0dXJuIHF9LApUKGEpe3ZhciBzPXRoaXMscj1hLmMscT1hLmIKaWYocj09bnVsbClzLmU9cQpl -bHNlIHIuYj1xCmlmKHE9PW51bGwpcy5mPXIKZWxzZSBxLmM9cjstLXMuYQpzLlMoKX0sClkoYSl7cmV0 -dXJuIEouVTMoYSkmMTA3Mzc0MTgyM30sCnQoYSxiKXt2YXIgcyxyCmlmKGE9PW51bGwpcmV0dXJuLTEK -cz1hLmxlbmd0aApmb3Iocj0wO3I8czsrK3IpaWYoSi5STShhW3JdLmEsYikpcmV0dXJuIHIKcmV0dXJu -LTF9fQpBLmJuLnByb3RvdHlwZT17fQpBLmxtLnByb3RvdHlwZT17CmdsKCl7dmFyIHM9dGhpcy5kCnJl -dHVybiBzPT1udWxsP3RoaXMuJHRpLmMuYShzKTpzfSwKRygpe3ZhciBzPXRoaXMscj1zLmMscT1zLmEK -aWYocy5iIT09cS5yKXRocm93IEEuYihBLmE0KHEpKQplbHNlIGlmKHI9PW51bGwpe3Muc2oobnVsbCkK -cmV0dXJuITF9ZWxzZXtzLnNqKHMuJHRpLkMoIjE/IikuYShyLmEpKQpzLmM9ci5iCnJldHVybiEwfX0s -CnNqKGEpe3RoaXMuZD10aGlzLiR0aS5DKCIxPyIpLmEoYSl9LAokaUFuOjF9CkEubVcucHJvdG90eXBl -PXt9CkEudXkucHJvdG90eXBlPXskaWJROjEsJGljWDoxLCRpek06MX0KQS5sRC5wcm90b3R5cGU9ewpn -TShhKXtyZXR1cm4gbmV3IEEuYTcoYSx0aGlzLmdrKGEpLEEueksoYSkuQygiYTc8bEQuRT4iKSl9LApB -KGEsYil7cmV0dXJuIHRoaXMucShhLGIpfSwKSyhhLGIpe3ZhciBzLHIKQS56SyhhKS5DKCJ+KGxELkUp -IikuYShiKQpzPXRoaXMuZ2soYSkKZm9yKHI9MDtyPHM7KytyKXtiLiQxKHRoaXMucShhLHIpKQppZihz -IT09dGhpcy5nayhhKSl0aHJvdyBBLmIoQS5hNChhKSl9fSwKZ2wwKGEpe3JldHVybiB0aGlzLmdrKGEp -PT09MH0sCmdvcihhKXtyZXR1cm4hdGhpcy5nbDAoYSl9LApFMihhLGIsYyl7dmFyIHM9QS56SyhhKQpy -ZXR1cm4gbmV3IEEubEooYSxzLktxKGMpLkMoIjEobEQuRSkiKS5hKGIpLHMuQygiQDxsRC5FPiIpLktx -KGMpLkMoImxKPDEsMj4iKSl9LAplUihhLGIpe3JldHVybiBBLnFDKGEsYixudWxsLEEueksoYSkuQygi -bEQuRSIpKX0sCmRyKGEsYil7cmV0dXJuIG5ldyBBLmpWKGEsQS56SyhhKS5DKCJAPGxELkU+IikuS3Eo -YikuQygialY8MSwyPiIpKX0sCmR1KGEsYixjLGQpe3ZhciBzCkEueksoYSkuQygibEQuRT8iKS5hKGQp -CkEuakIoYixjLHRoaXMuZ2soYSkpCmZvcihzPWI7czxjOysrcyl0aGlzLlk1KGEscyxkKX0sCiJbIihh -KXtyZXR1cm4gQS54KGEsIlsiLCJdIil9fQpBLmlsLnByb3RvdHlwZT17fQpBLnJhLnByb3RvdHlwZT17 -CiQyKGEsYil7dmFyIHMscj10aGlzLmEKaWYoIXIuYSl0aGlzLmIuYSs9IiwgIgpyLmE9ITEKcj10aGlz -LmIKcz1yLmErPUEuZChhKQpyLmE9cysiOiAiCnIuYSs9QS5kKGIpfSwKJFM6MTB9CkEuWWsucHJvdG90 -eXBlPXsKSyhhLGIpe3ZhciBzLHIscSxwPUEuTGgodGhpcykKcC5DKCJ+KFlrLkssWWsuVikiKS5hKGIp -CmZvcihzPUouSVQodGhpcy5ndmMoKSkscD1wLkMoIllrLlYiKTtzLkcoKTspe3I9cy5nbCgpCnE9dGhp -cy5xKDAscikKYi4kMihyLHE9PW51bGw/cC5hKHEpOnEpfX0sCmdQdShhKXtyZXR1cm4gSi5NMSh0aGlz -Lmd2YygpLG5ldyBBLnlRKHRoaXMpLEEuTGgodGhpcykuQygiTjM8WWsuSyxZay5WPiIpKX0sCng0KGEp -e3JldHVybiBKLnpsKHRoaXMuZ3ZjKCksYSl9LApnayhhKXtyZXR1cm4gSi5IbSh0aGlzLmd2YygpKX0s -CmdsMChhKXtyZXR1cm4gSi51VSh0aGlzLmd2YygpKX0sCiJbIihhKXtyZXR1cm4gQS5uTyh0aGlzKX0s -CiRpWjA6MX0KQS55US5wcm90b3R5cGU9ewokMShhKXt2YXIgcz10aGlzLmEscj1BLkxoKHMpCnIuQygi -WWsuSyIpLmEoYSkKcz1zLnEoMCxhKQppZihzPT1udWxsKXM9ci5DKCJZay5WIikuYShzKQpyZXR1cm4g -bmV3IEEuTjMoYSxzLHIuQygiQDxZay5LPiIpLktxKHIuQygiWWsuViIpKS5DKCJOMzwxLDI+IikpfSwK -JFMoKXtyZXR1cm4gQS5MaCh0aGlzLmEpLkMoIk4zPFlrLkssWWsuVj4oWWsuSykiKX19CkEuS1AucHJv -dG90eXBlPXsKWTUoYSxiLGMpe3ZhciBzPUEuTGgodGhpcykKcy5jLmEoYikKcy56WzFdLmEoYykKdGhy -b3cgQS5iKEEuTDQoIkNhbm5vdCBtb2RpZnkgdW5tb2RpZmlhYmxlIG1hcCIpKX19CkEuUG4ucHJvdG90 -eXBlPXsKcShhLGIpe3JldHVybiB0aGlzLmEucSgwLGIpfSwKWTUoYSxiLGMpe3ZhciBzPUEuTGgodGhp -cykKdGhpcy5hLlk1KDAscy5jLmEoYikscy56WzFdLmEoYykpfSwKeDQoYSl7cmV0dXJuIHRoaXMuYS54 -NChhKX0sCksoYSxiKXt0aGlzLmEuSygwLEEuTGgodGhpcykuQygifigxLDIpIikuYShiKSl9LApnbDAo -YSl7dmFyIHM9dGhpcy5hCnJldHVybiBzLmdsMChzKX0sCmdrKGEpe3ZhciBzPXRoaXMuYQpyZXR1cm4g -cy5nayhzKX0sCiJbIihhKXtyZXR1cm4gdGhpcy5hWyJbIl0oMCl9LApnUHUoYSl7dmFyIHM9dGhpcy5h -CnJldHVybiBzLmdQdShzKX0sCiRpWjA6MX0KQS5Hai5wcm90b3R5cGU9e30KQS5sZi5wcm90b3R5cGU9 -ewpnbDAoYSl7cmV0dXJuIHRoaXMuZ2sodGhpcyk9PT0wfSwKZ29yKGEpe3JldHVybiB0aGlzLmdrKHRo -aXMpIT09MH0sCkZWKGEsYil7dmFyIHMKZm9yKHM9Si5JVChBLkxoKHRoaXMpLkMoImNYPGxmLkU+Iiku -YShiKSk7cy5HKCk7KXRoaXMuaSgwLHMuZ2woKSl9LAoiWyIoYSl7cmV0dXJuIEEueCh0aGlzLCJ7Iiwi -fSIpfSwKSChhLGIpe3ZhciBzLHIscSxwPXRoaXMuZ00odGhpcykKaWYoIXAuRygpKXJldHVybiIiCmlm -KGI9PT0iIil7cz1wLiR0aS5jCnI9IiIKZG97cT1wLmQKcis9QS5kKHE9PW51bGw/cy5hKHEpOnEpfXdo -aWxlKHAuRygpKQpzPXJ9ZWxzZXtzPXAuZApzPSIiK0EuZChzPT1udWxsP3AuJHRpLmMuYShzKTpzKQpm -b3Iocj1wLiR0aS5jO3AuRygpOyl7cT1wLmQKcz1zK2IrQS5kKHE9PW51bGw/ci5hKHEpOnEpfX1yZXR1 -cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c30sCmVSKGEsYil7cmV0dXJuIEEuYksodGhpcyxiLEEuTGgo -dGhpcykuQygibGYuRSIpKX0sCkEoYSxiKXt2YXIgcyxyLHEscCxvPSJpbmRleCIKQS5jYihiLG8sdC5T -KQpBLmsxKGIsbykKZm9yKHM9dGhpcy5nTSh0aGlzKSxyPXMuJHRpLmMscT0wO3MuRygpOyl7cD1zLmQK -aWYocD09bnVsbClwPXIuYShwKQppZihiPT09cSlyZXR1cm4gcDsrK3F9dGhyb3cgQS5iKEEueEYoYixx -LHRoaXMsbykpfX0KQS5Wai5wcm90b3R5cGU9eyRpYlE6MSwkaWNYOjEsJGlPbDoxfQpBLlh2LnByb3Rv -dHlwZT17JGliUToxLCRpY1g6MSwkaU9sOjF9CkEublkucHJvdG90eXBlPXt9CkEuV1kucHJvdG90eXBl -PXt9CkEuUlUucHJvdG90eXBlPXt9CkEuRlAucHJvdG90eXBlPXt9CkEudXcucHJvdG90eXBlPXsKcShh -LGIpe3ZhciBzLHI9dGhpcy5iCmlmKHI9PW51bGwpcmV0dXJuIHRoaXMuYy5xKDAsYikKZWxzZSBpZih0 -eXBlb2YgYiE9InN0cmluZyIpcmV0dXJuIG51bGwKZWxzZXtzPXJbYl0KcmV0dXJuIHR5cGVvZiBzPT0i -dW5kZWZpbmVkIj90aGlzLmZiKGIpOnN9fSwKZ2soYSl7cmV0dXJuIHRoaXMuYj09bnVsbD90aGlzLmMu -YTp0aGlzLkNmKCkubGVuZ3RofSwKZ2wwKGEpe3JldHVybiB0aGlzLmdrKHRoaXMpPT09MH0sCmd2Yygp -e2lmKHRoaXMuYj09bnVsbCl7dmFyIHM9dGhpcy5jCnJldHVybiBuZXcgQS5pNShzLEEuTGgocykuQygi -aTU8MT4iKSl9cmV0dXJuIG5ldyBBLmk4KHRoaXMpfSwKWTUoYSxiLGMpe3ZhciBzLHIscT10aGlzCmlm -KHEuYj09bnVsbClxLmMuWTUoMCxiLGMpCmVsc2UgaWYocS54NChiKSl7cz1xLmIKc1tiXT1jCnI9cS5h -CmlmKHI9PW51bGw/cyE9bnVsbDpyIT09cylyW2JdPW51bGx9ZWxzZSBxLlhLKCkuWTUoMCxiLGMpfSwK -eDQoYSl7aWYodGhpcy5iPT1udWxsKXJldHVybiB0aGlzLmMueDQoYSkKcmV0dXJuIE9iamVjdC5wcm90 -b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0aGlzLmEsYSl9LApLKGEsYil7dmFyIHMscixxLHAsbz10 -aGlzCnQuY0EuYShiKQppZihvLmI9PW51bGwpcmV0dXJuIG8uYy5LKDAsYikKcz1vLkNmKCkKZm9yKHI9 -MDtyPHMubGVuZ3RoOysrcil7cT1zW3JdCnA9by5iW3FdCmlmKHR5cGVvZiBwPT0idW5kZWZpbmVkIil7 -cD1BLlFlKG8uYVtxXSkKby5iW3FdPXB9Yi4kMihxLHApCmlmKHMhPT1vLmMpdGhyb3cgQS5iKEEuYTQo -bykpfX0sCkNmKCl7dmFyIHM9dC5iTS5hKHRoaXMuYykKaWYocz09bnVsbClzPXRoaXMuYz1BLlFJKE9i -amVjdC5rZXlzKHRoaXMuYSksdC5zKQpyZXR1cm4gc30sClhLKCl7dmFyIHMscixxLHAsbyxuPXRoaXMK -aWYobi5iPT1udWxsKXJldHVybiBuLmMKcz1BLkZsKHQuTix0LnopCnI9bi5DZigpCmZvcihxPTA7cD1y -Lmxlbmd0aCxxPHA7KytxKXtvPXJbcV0Kcy5ZNSgwLG8sbi5xKDAsbykpfWlmKHA9PT0wKUIuTm0uaShy -LCIiKQplbHNlIEIuTm0uVjEocikKbi5hPW4uYj1udWxsCnJldHVybiBuLmM9c30sCmZiKGEpe3ZhciBz -CmlmKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodGhpcy5hLGEpKXJldHVybiBu -dWxsCnM9QS5RZSh0aGlzLmFbYV0pCnJldHVybiB0aGlzLmJbYV09c319CkEuaTgucHJvdG90eXBlPXsK -Z2soYSl7dmFyIHM9dGhpcy5hCnJldHVybiBzLmdrKHMpfSwKQShhLGIpe3ZhciBzPXRoaXMuYQppZihz -LmI9PW51bGwpcz1zLmd2YygpLkEoMCxiKQplbHNle3M9cy5DZigpCmlmKCEoYj49MCYmYjxzLmxlbmd0 -aCkpcmV0dXJuIEEuT0gocyxiKQpzPXNbYl19cmV0dXJuIHN9LApnTShhKXt2YXIgcz10aGlzLmEKaWYo -cy5iPT1udWxsKXtzPXMuZ3ZjKCkKcz1zLmdNKHMpfWVsc2V7cz1zLkNmKCkKcz1uZXcgSi5tMShzLHMu -bGVuZ3RoLEEudDYocykuQygibTE8MT4iKSl9cmV0dXJuIHN9LAp0ZyhhLGIpe3JldHVybiB0aGlzLmEu -eDQoYil9fQpBLnhyLnByb3RvdHlwZT17CiQwKCl7dmFyIHMscgp0cnl7cz1uZXcgVGV4dERlY29kZXIo -InV0Zi04Iix7ZmF0YWw6dHJ1ZX0pCnJldHVybiBzfWNhdGNoKHIpe31yZXR1cm4gbnVsbH0sCiRTOjEx -fQpBLk56LnByb3RvdHlwZT17CiQwKCl7dmFyIHMscgp0cnl7cz1uZXcgVGV4dERlY29kZXIoInV0Zi04 -Iix7ZmF0YWw6ZmFsc2V9KQpyZXR1cm4gc31jYXRjaChyKXt9cmV0dXJuIG51bGx9LAokUzoxMX0KQS5D -Vi5wcm90b3R5cGU9ewp5cihhMSxhMixhMyl7dmFyIHMscixxLHAsbyxuLG0sbCxrLGosaSxoLGcsZixl -LGQsYyxiLGEsYTA9IkludmFsaWQgYmFzZTY0IGVuY29kaW5nIGxlbmd0aCAiCmEzPUEuakIoYTIsYTMs -YTEubGVuZ3RoKQpzPSQuVjcoKQpmb3Iocj1zLmxlbmd0aCxxPWEyLHA9cSxvPW51bGwsbj0tMSxtPS0x -LGw9MDtxPGEzO3E9ayl7az1xKzEKaj1CLnhCLlcoYTEscSkKaWYoaj09PTM3KXtpPWsrMgppZihpPD1h -Myl7aD1BLm9vKEIueEIuVyhhMSxrKSkKZz1BLm9vKEIueEIuVyhhMSxrKzEpKQpmPWgqMTYrZy0oZyYy -NTYpCmlmKGY9PT0zNylmPS0xCms9aX1lbHNlIGY9LTF9ZWxzZSBmPWoKaWYoMDw9ZiYmZjw9MTI3KXtp -ZighKGY+PTAmJmY8cikpcmV0dXJuIEEuT0gocyxmKQplPXNbZl0KaWYoZT49MCl7Zj1CLnhCLk8oIkFC -Q0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5 -Ky8iLGUpCmlmKGY9PT1qKWNvbnRpbnVlCmo9Zn1lbHNle2lmKGU9PT0tMSl7aWYobjwwKXtkPW89PW51 -bGw/bnVsbDpvLmEubGVuZ3RoCmlmKGQ9PW51bGwpZD0wCm49ZCsocS1wKQptPXF9KytsCmlmKGo9PT02 -MSljb250aW51ZX1qPWZ9aWYoZSE9PS0yKXtpZihvPT1udWxsKXtvPW5ldyBBLlJuKCIiKQpkPW99ZWxz -ZSBkPW8KYz1kLmErPUIueEIuTmooYTEscCxxKQpkLmE9YytBLkx3KGopCnA9awpjb250aW51ZX19dGhy -b3cgQS5iKEEucnIoIkludmFsaWQgYmFzZTY0IGRhdGEiLGExLHEpKX1pZihvIT1udWxsKXtyPW8uYSs9 -Qi54Qi5OaihhMSxwLGEzKQpkPXIubGVuZ3RoCmlmKG4+PTApQS54TShhMSxtLGEzLG4sbCxkKQplbHNl -e2I9Qi5qbi56WShkLTEsNCkrMQppZihiPT09MSl0aHJvdyBBLmIoQS5ycihhMCxhMSxhMykpCmZvcig7 -Yjw0Oyl7cis9Ij0iCm8uYT1yOysrYn19cj1vLmEKcmV0dXJuIEIueEIuaTcoYTEsYTIsYTMsci5jaGFy -Q29kZUF0KDApPT0wP3I6cil9YT1hMy1hMgppZihuPj0wKUEueE0oYTEsbSxhMyxuLGwsYSkKZWxzZXti -PUIuam4uelkoYSw0KQppZihiPT09MSl0aHJvdyBBLmIoQS5ycihhMCxhMSxhMykpCmlmKGI+MSlhMT1C -LnhCLmk3KGExLGEzLGEzLGI9PT0yPyI9PSI6Ij0iKX1yZXR1cm4gYTF9fQpBLlU4LnByb3RvdHlwZT17 -fQpBLlVrLnByb3RvdHlwZT17fQpBLndJLnByb3RvdHlwZT17fQpBLlppLnByb3RvdHlwZT17fQpBLlVk -LnByb3RvdHlwZT17CiJbIihhKXt2YXIgcz1BLmhsKHRoaXMuYSkKcmV0dXJuKHRoaXMuYiE9bnVsbD8i -Q29udmVydGluZyBvYmplY3QgdG8gYW4gZW5jb2RhYmxlIG9iamVjdCBmYWlsZWQ6IjoiQ29udmVydGlu -ZyBvYmplY3QgZGlkIG5vdCByZXR1cm4gYW4gZW5jb2RhYmxlIG9iamVjdDoiKSsiICIrc319CkEuSzgu -cHJvdG90eXBlPXsKIlsiKGEpe3JldHVybiJDeWNsaWMgZXJyb3IgaW4gSlNPTiBzdHJpbmdpZnkifX0K -QS5ieS5wcm90b3R5cGU9ewpwVyhhLGIsYyl7dmFyIHMKdC5mVi5hKGMpCnM9QS5CUyhiLHRoaXMuZ0hl -KCkuYSkKcmV0dXJuIHN9LApPQihhLGIpe3ZhciBzCnQuZEEuYShiKQpzPUEudVgoYSx0aGlzLmdaRSgp -LmIsbnVsbCkKcmV0dXJuIHN9LApnWkUoKXtyZXR1cm4gQi5uWH0sCmdIZSgpe3JldHVybiBCLkEzfX0K -QS5vai5wcm90b3R5cGU9e30KQS5NeC5wcm90b3R5cGU9e30KQS5TaC5wcm90b3R5cGU9ewpSVChhKXt2 -YXIgcyxyLHEscCxvLG4sbT1hLmxlbmd0aApmb3Iocz10aGlzLmMscj0wLHE9MDtxPG07KytxKXtwPUIu -eEIuVyhhLHEpCmlmKHA+OTIpe2lmKHA+PTU1Mjk2KXtvPXAmNjQ1MTIKaWYobz09PTU1Mjk2KXtuPXEr -MQpuPSEobjxtJiYoQi54Qi5XKGEsbikmNjQ1MTIpPT09NTYzMjApfWVsc2Ugbj0hMQppZighbilpZihv -PT09NTYzMjApe289cS0xCm89IShvPj0wJiYoQi54Qi5PKGEsbykmNjQ1MTIpPT09NTUyOTYpfWVsc2Ug -bz0hMQplbHNlIG89ITAKaWYobyl7aWYocT5yKXMuYSs9Qi54Qi5OaihhLHIscSkKcj1xKzEKbz1zLmEr -PUEuTHcoOTIpCm8rPUEuTHcoMTE3KQpzLmE9bwpvKz1BLkx3KDEwMCkKcy5hPW8Kbj1wPj4+OCYxNQpv -Kz1BLkx3KG48MTA/NDgrbjo4NytuKQpzLmE9bwpuPXA+Pj40JjE1Cm8rPUEuTHcobjwxMD80OCtuOjg3 -K24pCnMuYT1vCm49cCYxNQpzLmE9bytBLkx3KG48MTA/NDgrbjo4NytuKX19Y29udGludWV9aWYocDwz -Mil7aWYocT5yKXMuYSs9Qi54Qi5OaihhLHIscSkKcj1xKzEKbz1zLmErPUEuTHcoOTIpCnN3aXRjaChw -KXtjYXNlIDg6cy5hPW8rQS5Mdyg5OCkKYnJlYWsKY2FzZSA5OnMuYT1vK0EuTHcoMTE2KQpicmVhawpj -YXNlIDEwOnMuYT1vK0EuTHcoMTEwKQpicmVhawpjYXNlIDEyOnMuYT1vK0EuTHcoMTAyKQpicmVhawpj -YXNlIDEzOnMuYT1vK0EuTHcoMTE0KQpicmVhawpkZWZhdWx0Om8rPUEuTHcoMTE3KQpzLmE9bwpvKz1B -Lkx3KDQ4KQpzLmE9bwpvKz1BLkx3KDQ4KQpzLmE9bwpuPXA+Pj40JjE1Cm8rPUEuTHcobjwxMD80OCtu -Ojg3K24pCnMuYT1vCm49cCYxNQpzLmE9bytBLkx3KG48MTA/NDgrbjo4NytuKQpicmVha319ZWxzZSBp -ZihwPT09MzR8fHA9PT05Mil7aWYocT5yKXMuYSs9Qi54Qi5OaihhLHIscSkKcj1xKzEKbz1zLmErPUEu -THcoOTIpCnMuYT1vK0EuTHcocCl9fWlmKHI9PT0wKXMuYSs9YQplbHNlIGlmKHI8bSlzLmErPUIueEIu -TmooYSxyLG0pfSwKSm4oYSl7dmFyIHMscixxLHAKZm9yKHM9dGhpcy5hLHI9cy5sZW5ndGgscT0wO3E8 -cjsrK3Epe3A9c1txXQppZihhPT1udWxsP3A9PW51bGw6YT09PXApdGhyb3cgQS5iKG5ldyBBLks4KGEs -bnVsbCkpfUIuTm0uaShzLGEpfSwKaVUoYSl7dmFyIHMscixxLHAsbz10aGlzCmlmKG8udE0oYSkpcmV0 -dXJuCm8uSm4oYSkKdHJ5e3M9by5iLiQxKGEpCmlmKCFvLnRNKHMpKXtxPUEuR3koYSxudWxsLG8uZ1ZL -KCkpCnRocm93IEEuYihxKX1xPW8uYQppZigwPj1xLmxlbmd0aClyZXR1cm4gQS5PSChxLC0xKQpxLnBv -cCgpfWNhdGNoKHApe3I9QS5SdShwKQpxPUEuR3koYSxyLG8uZ1ZLKCkpCnRocm93IEEuYihxKX19LAp0 -TShhKXt2YXIgcyxyLHE9dGhpcwppZih0eXBlb2YgYT09Im51bWJlciIpe2lmKCFpc0Zpbml0ZShhKSly -ZXR1cm4hMQpxLmMuYSs9Qi5DRFsiWyJdKGEpCnJldHVybiEwfWVsc2UgaWYoYT09PSEwKXtxLmMuYSs9 -InRydWUiCnJldHVybiEwfWVsc2UgaWYoYT09PSExKXtxLmMuYSs9ImZhbHNlIgpyZXR1cm4hMH1lbHNl -IGlmKGE9PW51bGwpe3EuYy5hKz0ibnVsbCIKcmV0dXJuITB9ZWxzZSBpZih0eXBlb2YgYT09InN0cmlu -ZyIpe3M9cS5jCnMuYSs9JyInCnEuUlQoYSkKcy5hKz0nIicKcmV0dXJuITB9ZWxzZSBpZih0LmouYihh -KSl7cS5KbihhKQpxLmxLKGEpCnM9cS5hCmlmKDA+PXMubGVuZ3RoKXJldHVybiBBLk9IKHMsLTEpCnMu -cG9wKCkKcmV0dXJuITB9ZWxzZSBpZih0LmYuYihhKSl7cS5KbihhKQpyPXEuancoYSkKcz1xLmEKaWYo -MD49cy5sZW5ndGgpcmV0dXJuIEEuT0gocywtMSkKcy5wb3AoKQpyZXR1cm4gcn1lbHNlIHJldHVybiEx -fSwKbEsoYSl7dmFyIHMscixxPXRoaXMuYwpxLmErPSJbIgpzPUouVTYoYSkKaWYocy5nb3IoYSkpe3Ro -aXMuaVUocy5xKGEsMCkpCmZvcihyPTE7cjxzLmdrKGEpOysrcil7cS5hKz0iLCIKdGhpcy5pVShzLnEo -YSxyKSl9fXEuYSs9Il0ifSwKancoYSl7dmFyIHMscixxLHAsbyxuLG09dGhpcyxsPXt9CmlmKGEuZ2ww -KGEpKXttLmMuYSs9Int9IgpyZXR1cm4hMH1zPWEuZ2soYSkqMgpyPUEuTzgocyxudWxsLCExLHQuWCkK -cT1sLmE9MApsLmI9ITAKYS5LKDAsbmV3IEEudGkobCxyKSkKaWYoIWwuYilyZXR1cm4hMQpwPW0uYwpw -LmErPSJ7Igpmb3Iobz0nIic7cTxzO3ErPTIsbz0nLCInKXtwLmErPW8KbS5SVChBLm4ocltxXSkpCnAu -YSs9JyI6JwpuPXErMQppZighKG48cykpcmV0dXJuIEEuT0gocixuKQptLmlVKHJbbl0pfXAuYSs9In0i -CnJldHVybiEwfX0KQS50aS5wcm90b3R5cGU9ewokMihhLGIpe3ZhciBzLHIKaWYodHlwZW9mIGEhPSJz -dHJpbmciKXRoaXMuYS5iPSExCnM9dGhpcy5iCnI9dGhpcy5hCkIuTm0uWTUocyxyLmErKyxhKQpCLk5t -Llk1KHMsci5hKyssYil9LAokUzoxMH0KQS50dS5wcm90b3R5cGU9ewpnVksoKXt2YXIgcz10aGlzLmMu -YQpyZXR1cm4gcy5jaGFyQ29kZUF0KDApPT0wP3M6c319CkEudTUucHJvdG90eXBlPXsKZ1pFKCl7cmV0 -dXJuIEIuUWt9fQpBLkUzLnByb3RvdHlwZT17CldKKGEpe3ZhciBzLHIscSxwPUEuakIoMCxudWxsLGEu -bGVuZ3RoKSxvPXAtMAppZihvPT09MClyZXR1cm4gbmV3IFVpbnQ4QXJyYXkoMCkKcz1vKjMKcj1uZXcg -VWludDhBcnJheShzKQpxPW5ldyBBLlJ3KHIpCmlmKHEuR3goYSwwLHApIT09cCl7Qi54Qi5PKGEscC0x -KQpxLlJPKCl9cmV0dXJuIG5ldyBVaW50OEFycmF5KHIuc3ViYXJyYXkoMCxBLnJNKDAscS5iLHMpKSl9 -fQpBLlJ3LnByb3RvdHlwZT17ClJPKCl7dmFyIHM9dGhpcyxyPXMuYyxxPXMuYixwPXMuYj1xKzEsbz1y -Lmxlbmd0aAppZighKHE8bykpcmV0dXJuIEEuT0gocixxKQpyW3FdPTIzOQpxPXMuYj1wKzEKaWYoIShw -PG8pKXJldHVybiBBLk9IKHIscCkKcltwXT0xOTEKcy5iPXErMQppZighKHE8bykpcmV0dXJuIEEuT0go -cixxKQpyW3FdPTE4OX0sCk82KGEsYil7dmFyIHMscixxLHAsbyxuPXRoaXMKaWYoKGImNjQ1MTIpPT09 -NTYzMjApe3M9NjU1MzYrKChhJjEwMjMpPDwxMCl8YiYxMDIzCnI9bi5jCnE9bi5iCnA9bi5iPXErMQpv -PXIubGVuZ3RoCmlmKCEocTxvKSlyZXR1cm4gQS5PSChyLHEpCnJbcV09cz4+PjE4fDI0MApxPW4uYj1w -KzEKaWYoIShwPG8pKXJldHVybiBBLk9IKHIscCkKcltwXT1zPj4+MTImNjN8MTI4CnA9bi5iPXErMQpp -ZighKHE8bykpcmV0dXJuIEEuT0gocixxKQpyW3FdPXM+Pj42JjYzfDEyOApuLmI9cCsxCmlmKCEocDxv -KSlyZXR1cm4gQS5PSChyLHApCnJbcF09cyY2M3wxMjgKcmV0dXJuITB9ZWxzZXtuLlJPKCkKcmV0dXJu -ITF9fSwKR3goYSxiLGMpe3ZhciBzLHIscSxwLG8sbixtLGw9dGhpcwppZihiIT09YyYmKEIueEIuTyhh -LGMtMSkmNjQ1MTIpPT09NTUyOTYpLS1jCmZvcihzPWwuYyxyPXMubGVuZ3RoLHE9YjtxPGM7KytxKXtw -PUIueEIuVyhhLHEpCmlmKHA8PTEyNyl7bz1sLmIKaWYobz49cilicmVhawpsLmI9bysxCnNbb109cH1l -bHNle289cCY2NDUxMgppZihvPT09NTUyOTYpe2lmKGwuYis0PnIpYnJlYWsKbj1xKzEKaWYobC5PNihw -LEIueEIuVyhhLG4pKSlxPW59ZWxzZSBpZihvPT09NTYzMjApe2lmKGwuYiszPnIpYnJlYWsKbC5STygp -fWVsc2UgaWYocDw9MjA0Nyl7bz1sLmIKbT1vKzEKaWYobT49cilicmVhawpsLmI9bQppZighKG88cikp -cmV0dXJuIEEuT0gocyxvKQpzW29dPXA+Pj42fDE5MgpsLmI9bSsxCnNbbV09cCY2M3wxMjh9ZWxzZXtv -PWwuYgppZihvKzI+PXIpYnJlYWsKbT1sLmI9bysxCmlmKCEobzxyKSlyZXR1cm4gQS5PSChzLG8pCnNb -b109cD4+PjEyfDIyNApvPWwuYj1tKzEKaWYoIShtPHIpKXJldHVybiBBLk9IKHMsbSkKc1ttXT1wPj4+ -NiY2M3wxMjgKbC5iPW8rMQppZighKG88cikpcmV0dXJuIEEuT0gocyxvKQpzW29dPXAmNjN8MTI4fX19 -cmV0dXJuIHF9fQpBLkdZLnByb3RvdHlwZT17CldKKGEpe3ZhciBzLHIKdC5MLmEoYSkKcz10aGlzLmEK -cj1BLmt5KHMsYSwwLG51bGwpCmlmKHIhPW51bGwpcmV0dXJuIHIKcmV0dXJuIG5ldyBBLmJ6KHMpLk5l -KGEsMCxudWxsLCEwKX19CkEuYnoucHJvdG90eXBlPXsKTmUoYSxiLGMsZCl7dmFyIHMscixxLHAsbyxu -PXRoaXMKdC5MLmEoYSkKcz1BLmpCKGIsYyxKLkhtKGEpKQppZihiPT09cylyZXR1cm4iIgpyPUEuanko -YSxiLHMpCnE9bi5oTyhyLDAscy1iLCEwKQpwPW4uYgppZigocCYxKSE9PTApe289QS5qNChwKQpuLmI9 -MAp0aHJvdyBBLmIoQS5ycihvLGEsYituLmMpKX1yZXR1cm4gcX0sCmhPKGEsYixjLGQpe3ZhciBzLHIs -cT10aGlzCmlmKGMtYj4xMDAwKXtzPUIuam4uQlUoYitjLDIpCnI9cS5oTyhhLGIscywhMSkKaWYoKHEu -YiYxKSE9PTApcmV0dXJuIHIKcmV0dXJuIHIrcS5oTyhhLHMsYyxkKX1yZXR1cm4gcS5FaChhLGIsYyxk -KX0sCkVoKGEsYixjLGQpe3ZhciBzLHIscSxwLG8sbixtLGwsaz10aGlzLGo9NjU1MzMsaT1rLmIsaD1r -LmMsZz1uZXcgQS5SbigiIiksZj1iKzEsZT1hLmxlbmd0aAppZighKGI+PTAmJmI8ZSkpcmV0dXJuIEEu -T0goYSxiKQpzPWFbYl0KJGxhYmVsMCQwOmZvcihyPWsuYTshMDspe2Zvcig7ITA7Zj1vKXtxPUIueEIu -VygiQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB -QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB -QUFBQUFBQUFBQUFGRkZGRkZGRkZGRkZGRkZGR0dHR0dHR0dHR0dHR0dHR0hISEhISEhISEhISEhISEhI -SEhISEhISEhISElISEhKRUVCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJLQ0NDQ0NDQ0NDQ0ND -RENMT05OTk1FRUVFRUVFRUVFRSIscykmMzEKaD1pPD0zMj9zJjYxNjk0Pj4+cToocyY2M3xoPDw2KT4+ -PjAKaT1CLnhCLlcoIiBceDAwMDpYRUNDQ0NDTjpsRGIgXHgwMDA6WEVDQ0NDQ052bERiIFx4MDAwOlhF -Q0NDQ0NOOmxEYiBBQUFBQVx4MDBceDAwXHgwMFx4MDBceDAwQUFBQUEwMDAwMEFBQUFBOjo6OjpBQUFB -QUdHMDAwQUFBQUEwMEtLS0FBQUFBRzo6OjpBQUFBQTpJSUlJQUFBQUEwMDBceDgwMEFBQUFBXHgwMFx4 -MDBceDAwXHgwMCBBQUFBQSIsaStxKQppZihpPT09MCl7Zy5hKz1BLkx3KGgpCmlmKGY9PT1jKWJyZWFr -ICRsYWJlbDAkMApicmVha31lbHNlIGlmKChpJjEpIT09MCl7aWYocilzd2l0Y2goaSl7Y2FzZSA2OTpj -YXNlIDY3OmcuYSs9QS5MdyhqKQpicmVhawpjYXNlIDY1OmcuYSs9QS5MdyhqKTstLWYKYnJlYWsKZGVm -YXVsdDpwPWcuYSs9QS5MdyhqKQpnLmE9cCtBLkx3KGopCmJyZWFrfWVsc2V7ay5iPWkKay5jPWYtMQpy -ZXR1cm4iIn1pPTB9aWYoZj09PWMpYnJlYWsgJGxhYmVsMCQwCm89ZisxCmlmKCEoZj49MCYmZjxlKSly -ZXR1cm4gQS5PSChhLGYpCnM9YVtmXX1vPWYrMQppZighKGY+PTAmJmY8ZSkpcmV0dXJuIEEuT0goYSxm -KQpzPWFbZl0KaWYoczwxMjgpe3doaWxlKCEwKXtpZighKG88Yykpe249YwpicmVha31tPW8rMQppZigh -KG8+PTAmJm88ZSkpcmV0dXJuIEEuT0goYSxvKQpzPWFbb10KaWYocz49MTI4KXtuPW0tMQpvPW0KYnJl -YWt9bz1tfWlmKG4tZjwyMClmb3IobD1mO2w8bjsrK2wpe2lmKCEobDxlKSlyZXR1cm4gQS5PSChhLGwp -CmcuYSs9QS5MdyhhW2xdKX1lbHNlIGcuYSs9QS5ITShhLGYsbikKaWYobj09PWMpYnJlYWsgJGxhYmVs -MCQwCmY9b31lbHNlIGY9b31pZihkJiZpPjMyKWlmKHIpZy5hKz1BLkx3KGopCmVsc2V7ay5iPTc3Cmsu -Yz1jCnJldHVybiIifWsuYj1pCmsuYz1oCmU9Zy5hCnJldHVybiBlLmNoYXJDb2RlQXQoMCk9PTA/ZTpl -fX0KQS5XRi5wcm90b3R5cGU9ewokMihhLGIpe3ZhciBzLHIscQp0LmZvLmEoYSkKcz10aGlzLmIKcj10 -aGlzLmEKcT1zLmErPXIuYQpxKz1hLmEKcy5hPXEKcy5hPXErIjogIgpzLmErPUEuaGwoYikKci5hPSIs -ICJ9LAokUzo0NX0KQS5pUC5wcm90b3R5cGU9ewpETihhLGIpe2lmKGI9PW51bGwpcmV0dXJuITEKcmV0 -dXJuIGIgaW5zdGFuY2VvZiBBLmlQJiZ0aGlzLmE9PT1iLmEmJiEwfSwKZ20oYSl7dmFyIHM9dGhpcy5h -CnJldHVybihzXkIuam4ud0cocywzMCkpJjEwNzM3NDE4MjN9LAoiWyIoYSl7dmFyIHM9dGhpcyxyPUEu -R3EoQS50SihzKSkscT1BLmgwKEEuTlMocykpLHA9QS5oMChBLmpBKHMpKSxvPUEuaDAoQS5JWChzKSks -bj1BLmgwKEEuY2gocykpLG09QS5oMChBLkpkKHMpKSxsPUEuVngoQS5vMShzKSkKcmV0dXJuIHIrIi0i -K3ErIi0iK3ArIiAiK28rIjoiK24rIjoiK20rIi4iK2x9fQpBLmNrLnByb3RvdHlwZT17CiJbIihhKXty -ZXR1cm4gdGhpcy5uKCl9fQpBLlhTLnByb3RvdHlwZT17CmdJSSgpe3JldHVybiBBLnRzKHRoaXMuJHRo -cm93bkpzRXJyb3IpfX0KQS5DNi5wcm90b3R5cGU9ewoiWyIoYSl7dmFyIHM9dGhpcy5hCmlmKHMhPW51 -bGwpcmV0dXJuIkFzc2VydGlvbiBmYWlsZWQ6ICIrQS5obChzKQpyZXR1cm4iQXNzZXJ0aW9uIGZhaWxl -ZCJ9fQpBLkV6LnByb3RvdHlwZT17fQpBLkYucHJvdG90eXBlPXsKIlsiKGEpe3JldHVybiJUaHJvdyBv -ZiBudWxsLiJ9LAokaUV6OjF9CkEudS5wcm90b3R5cGU9ewpnWigpe3JldHVybiJJbnZhbGlkIGFyZ3Vt -ZW50IisoIXRoaXMuYT8iKHMpIjoiIil9LApnTigpe3JldHVybiIifSwKIlsiKGEpe3ZhciBzPXRoaXMs -cj1zLmMscT1yPT1udWxsPyIiOiIgKCIrcisiKSIscD1zLmQsbz1wPT1udWxsPyIiOiI6ICIrQS5kKHAp -LG49cy5nWigpK3ErbwppZighcy5hKXJldHVybiBuCnJldHVybiBuK3MuZ04oKSsiOiAiK0EuaGwocy5n -RSgpKX0sCmdFKCl7cmV0dXJuIHRoaXMuYn19CkEuYkoucHJvdG90eXBlPXsKZ0UoKXtyZXR1cm4gQS5j -VSh0aGlzLmIpfSwKZ1ooKXtyZXR1cm4iUmFuZ2VFcnJvciJ9LApnTigpe3ZhciBzLHI9dGhpcy5lLHE9 -dGhpcy5mCmlmKHI9PW51bGwpcz1xIT1udWxsPyI6IE5vdCBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gIitB -LmQocSk6IiIKZWxzZSBpZihxPT1udWxsKXM9IjogTm90IGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAi -K0EuZChyKQplbHNlIGlmKHE+cilzPSI6IE5vdCBpbiBpbmNsdXNpdmUgcmFuZ2UgIitBLmQocikrIi4u -IitBLmQocSkKZWxzZSBzPXE8cj8iOiBWYWxpZCB2YWx1ZSByYW5nZSBpcyBlbXB0eSI6IjogT25seSB2 -YWxpZCB2YWx1ZSBpcyAiK0EuZChyKQpyZXR1cm4gc319CkEuZVkucHJvdG90eXBlPXsKZ0UoKXtyZXR1 -cm4gQS5JWih0aGlzLmIpfSwKZ1ooKXtyZXR1cm4iUmFuZ2VFcnJvciJ9LApnTigpe2lmKEEuSVoodGhp -cy5iKTwwKXJldHVybiI6IGluZGV4IG11c3Qgbm90IGJlIG5lZ2F0aXZlIgp2YXIgcz10aGlzLmYKaWYo -cz09PTApcmV0dXJuIjogbm8gaW5kaWNlcyBhcmUgdmFsaWQiCnJldHVybiI6IGluZGV4IHNob3VsZCBi -ZSBsZXNzIHRoYW4gIitzfSwKZ2soYSl7cmV0dXJuIHRoaXMuZn19CkEubXAucHJvdG90eXBlPXsKIlsi -KGEpe3ZhciBzLHIscSxwLG8sbixtLGwsaz10aGlzLGo9e30saT1uZXcgQS5SbigiIikKai5hPSIiCnM9 -ay5jCmZvcihyPXMubGVuZ3RoLHE9MCxwPSIiLG89IiI7cTxyOysrcSxvPSIsICIpe249c1txXQppLmE9 -cCtvCnA9aS5hKz1BLmhsKG4pCmouYT0iLCAifWsuZC5LKDAsbmV3IEEuV0YoaixpKSkKbT1BLmhsKGsu -YSkKbD1pWyJbIl0oMCkKcmV0dXJuIk5vU3VjaE1ldGhvZEVycm9yOiBtZXRob2Qgbm90IGZvdW5kOiAn -IitrLmIuYSsiJ1xuUmVjZWl2ZXI6ICIrbSsiXG5Bcmd1bWVudHM6IFsiK2wrIl0ifX0KQS51Yi5wcm90 -b3R5cGU9ewoiWyIoYSl7cmV0dXJuIlVuc3VwcG9ydGVkIG9wZXJhdGlvbjogIit0aGlzLmF9fQpBLmRz -LnByb3RvdHlwZT17CiJbIihhKXtyZXR1cm4iVW5pbXBsZW1lbnRlZEVycm9yOiAiK3RoaXMuYX19CkEu -bGoucHJvdG90eXBlPXsKIlsiKGEpe3JldHVybiJCYWQgc3RhdGU6ICIrdGhpcy5hfX0KQS5VVi5wcm90 -b3R5cGU9ewoiWyIoYSl7dmFyIHM9dGhpcy5hCmlmKHM9PW51bGwpcmV0dXJuIkNvbmN1cnJlbnQgbW9k -aWZpY2F0aW9uIGR1cmluZyBpdGVyYXRpb24uIgpyZXR1cm4iQ29uY3VycmVudCBtb2RpZmljYXRpb24g -ZHVyaW5nIGl0ZXJhdGlvbjogIitBLmhsKHMpKyIuIn19CkEuazUucHJvdG90eXBlPXsKIlsiKGEpe3Jl -dHVybiJPdXQgb2YgTWVtb3J5In0sCmdJSSgpe3JldHVybiBudWxsfSwKJGlYUzoxfQpBLktZLnByb3Rv -dHlwZT17CiJbIihhKXtyZXR1cm4iU3RhY2sgT3ZlcmZsb3cifSwKZ0lJKCl7cmV0dXJuIG51bGx9LAok -aVhTOjF9CkEucC5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIlJlYWRpbmcgc3RhdGljIHZhcmlhYmxl -ICciK3RoaXMuYSsiJyBkdXJpbmcgaXRzIGluaXRpYWxpemF0aW9uIn19CkEuQ0QucHJvdG90eXBlPXsK -IlsiKGEpe3JldHVybiJFeGNlcHRpb246ICIrdGhpcy5hfSwKJGlSejoxfQpBLmFFLnByb3RvdHlwZT17 -CiJbIihhKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssaixpLGg9dGhpcy5hLGc9IiIhPT1oPyJGb3JtYXRF -eGNlcHRpb246ICIraDoiRm9ybWF0RXhjZXB0aW9uIixmPXRoaXMuYyxlPXRoaXMuYgppZih0eXBlb2Yg -ZT09InN0cmluZyIpe2lmKGYhPW51bGwpcz1mPDB8fGY+ZS5sZW5ndGgKZWxzZSBzPSExCmlmKHMpZj1u -dWxsCmlmKGY9PW51bGwpe2lmKGUubGVuZ3RoPjc4KWU9Qi54Qi5OaihlLDAsNzUpKyIuLi4iCnJldHVy -biBnKyJcbiIrZX1mb3Iocj0xLHE9MCxwPSExLG89MDtvPGY7KytvKXtuPUIueEIuVyhlLG8pCmlmKG49 -PT0xMCl7aWYocSE9PW98fCFwKSsrcgpxPW8rMQpwPSExfWVsc2UgaWYobj09PTEzKXsrK3IKcT1vKzEK -cD0hMH19Zz1yPjE/ZysoIiAoYXQgbGluZSAiK3IrIiwgY2hhcmFjdGVyICIrKGYtcSsxKSsiKVxuIik6 -ZysoIiAoYXQgY2hhcmFjdGVyICIrKGYrMSkrIilcbiIpCm09ZS5sZW5ndGgKZm9yKG89ZjtvPG07Kytv -KXtuPUIueEIuTyhlLG8pCmlmKG49PT0xMHx8bj09PTEzKXttPW8KYnJlYWt9fWlmKG0tcT43OClpZihm -LXE8NzUpe2w9cSs3NQprPXEKaj0iIgppPSIuLi4ifWVsc2V7aWYobS1mPDc1KXtrPW0tNzUKbD1tCmk9 -IiJ9ZWxzZXtrPWYtMzYKbD1mKzM2Cmk9Ii4uLiJ9aj0iLi4uIn1lbHNle2w9bQprPXEKaj0iIgppPSIi -fXJldHVybiBnK2orQi54Qi5OaihlLGssbCkraSsiXG4iK0IueEIuSXgoIiAiLGYtaytqLmxlbmd0aCkr -Il5cbiJ9ZWxzZSByZXR1cm4gZiE9bnVsbD9nKygiIChhdCBvZmZzZXQgIitBLmQoZikrIikiKTpnfSwK -JGlSejoxfQpBLmNYLnByb3RvdHlwZT17CmRyKGEsYil7cmV0dXJuIEEuR0oodGhpcyxBLkxoKHRoaXMp -LkMoImNYLkUiKSxiKX0sCkUyKGEsYixjKXt2YXIgcz1BLkxoKHRoaXMpCnJldHVybiBBLksxKHRoaXMs -cy5LcShjKS5DKCIxKGNYLkUpIikuYShiKSxzLkMoImNYLkUiKSxjKX0sCmV2KGEsYil7dmFyIHM9QS5M -aCh0aGlzKQpyZXR1cm4gbmV3IEEuVTUodGhpcyxzLkMoImEyKGNYLkUpIikuYShiKSxzLkMoIlU1PGNY -LkU+IikpfSwKTjAoYSxiLGMsZCl7dmFyIHMscgpkLmEoYikKQS5MaCh0aGlzKS5LcShkKS5DKCIxKDEs -Y1guRSkiKS5hKGMpCmZvcihzPXRoaXMuZ00odGhpcykscj1iO3MuRygpOylyPWMuJDIocixzLmdsKCkp -CnJldHVybiByfSwKdHQoYSxiKXtyZXR1cm4gQS5ZMSh0aGlzLGIsQS5MaCh0aGlzKS5DKCJjWC5FIikp -fSwKYnIoYSl7cmV0dXJuIHRoaXMudHQoYSwhMCl9LApnayhhKXt2YXIgcyxyPXRoaXMuZ00odGhpcykK -Zm9yKHM9MDtyLkcoKTspKytzCnJldHVybiBzfSwKZ2wwKGEpe3JldHVybiF0aGlzLmdNKHRoaXMpLkco -KX0sCmdvcihhKXtyZXR1cm4hdGhpcy5nbDAodGhpcyl9LAplUihhLGIpe3JldHVybiBBLmJLKHRoaXMs -YixBLkxoKHRoaXMpLkMoImNYLkUiKSl9LApncjgoYSl7dmFyIHMscj10aGlzLmdNKHRoaXMpCmlmKCFy -LkcoKSl0aHJvdyBBLmIoQS5XcCgpKQpzPXIuZ2woKQppZihyLkcoKSl0aHJvdyBBLmIoQS5BbSgpKQpy -ZXR1cm4gc30sCkEoYSxiKXt2YXIgcyxyLHEKQS5rMShiLCJpbmRleCIpCmZvcihzPXRoaXMuZ00odGhp -cykscj0wO3MuRygpOyl7cT1zLmdsKCkKaWYoYj09PXIpcmV0dXJuIHE7KytyfXRocm93IEEuYihBLnhG -KGIscix0aGlzLCJpbmRleCIpKX0sCiJbIihhKXtyZXR1cm4gQS5FUCh0aGlzLCIoIiwiKSIpfX0KQS5B -bi5wcm90b3R5cGU9e30KQS5OMy5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIk1hcEVudHJ5KCIrQS5k -KHRoaXMuYSkrIjogIitBLmQodGhpcy5iKSsiKSJ9fQpBLmM4LnByb3RvdHlwZT17CmdtKGEpe3JldHVy -biBBLk1oLnByb3RvdHlwZS5nbS5jYWxsKHRoaXMsdGhpcyl9LAoiWyIoYSl7cmV0dXJuIm51bGwifX0K -QS5NaC5wcm90b3R5cGU9eyRpTWg6MSwKRE4oYSxiKXtyZXR1cm4gdGhpcz09PWJ9LApnbShhKXtyZXR1 -cm4gQS5lUSh0aGlzKX0sCiJbIihhKXtyZXR1cm4iSW5zdGFuY2Ugb2YgJyIrQS5saCh0aGlzKSsiJyJ9 -LAplNyhhLGIpe3Quby5hKGIpCnRocm93IEEuYihBLlpSKHRoaXMsYi5nV2EoKSxiLmduZCgpLGIuZ1Zt -KCksbnVsbCkpfSwKdG9TdHJpbmcoKXtyZXR1cm4gdGhpc1siWyJdKHRoaXMpfX0KQS5aZC5wcm90b3R5 -cGU9ewoiWyIoYSl7cmV0dXJuIiJ9LAokaUd6OjF9CkEuUm4ucHJvdG90eXBlPXsKZ2soYSl7cmV0dXJu -IHRoaXMuYS5sZW5ndGh9LAoiWyIoYSl7dmFyIHM9dGhpcy5hCnJldHVybiBzLmNoYXJDb2RlQXQoMCk9 -PTA/czpzfSwKJGlCTDoxfQpBLm4xLnByb3RvdHlwZT17CiQyKGEsYil7dmFyIHMscixxLHAKdC5JLmEo -YSkKQS5uKGIpCnM9Qi54Qi5PWShiLCI9IikKaWYocz09PS0xKXtpZihiIT09IiIpYS5ZNSgwLEEua3Uo -YiwwLGIubGVuZ3RoLHRoaXMuYSwhMCksIiIpfWVsc2UgaWYocyE9PTApe3I9Qi54Qi5OaihiLDAscykK -cT1CLnhCLnluKGIscysxKQpwPXRoaXMuYQphLlk1KDAsQS5rdShyLDAsci5sZW5ndGgscCwhMCksQS5r -dShxLDAscS5sZW5ndGgscCwhMCkpfXJldHVybiBhfSwKJFM6NTJ9CkEuY1MucHJvdG90eXBlPXsKJDIo -YSxiKXt0aHJvdyBBLmIoQS5ycigiSWxsZWdhbCBJUHY0IGFkZHJlc3MsICIrYSx0aGlzLmEsYikpfSwK -JFM6MTl9CkEuVkMucHJvdG90eXBlPXsKJDIoYSxiKXt0aHJvdyBBLmIoQS5ycigiSWxsZWdhbCBJUHY2 -IGFkZHJlc3MsICIrYSx0aGlzLmEsYikpfSwKJFM6MjB9CkEuSlQucHJvdG90eXBlPXsKJDIoYSxiKXt2 -YXIgcwppZihiLWE+NCl0aGlzLmEuJDIoImFuIElQdjYgcGFydCBjYW4gb25seSBjb250YWluIGEgbWF4 -aW11bSBvZiA0IGhleCBkaWdpdHMiLGEpCnM9QS5RQShCLnhCLk5qKHRoaXMuYixhLGIpLDE2KQppZihz -PDB8fHM+NjU1MzUpdGhpcy5hLiQyKCJlYWNoIHBhcnQgbXVzdCBiZSBpbiB0aGUgcmFuZ2Ugb2YgYDB4 -MC4uMHhGRkZGYCIsYSkKcmV0dXJuIHN9LAokUzoyMX0KQS5Ebi5wcm90b3R5cGU9ewpnbkQoKXt2YXIg -cyxyLHEscCxvPXRoaXMsbj1vLncKaWYobj09PSQpe3M9by5hCnI9cy5sZW5ndGghPT0wPyIiK3MrIjoi -OiIiCnE9by5jCnA9cT09bnVsbAppZighcHx8cz09PSJmaWxlIil7cz1yKyIvLyIKcj1vLmIKaWYoci5s -ZW5ndGghPT0wKXM9cytyKyJAIgppZighcClzKz1xCnI9by5kCmlmKHIhPW51bGwpcz1zKyI6IitBLmQo -cil9ZWxzZSBzPXIKcys9by5lCnI9by5mCmlmKHIhPW51bGwpcz1zKyI/IityCnI9by5yCmlmKHIhPW51 -bGwpcz1zKyIjIityCm4hPT0kJiZBLnBSKCJfdGV4dCIpCm49by53PXMuY2hhckNvZGVBdCgwKT09MD9z -OnN9cmV0dXJuIG59LApnRmooKXt2YXIgcyxyLHE9dGhpcyxwPXEueAppZihwPT09JCl7cz1xLmUKaWYo -cy5sZW5ndGghPT0wJiZCLnhCLlcocywwKT09PTQ3KXM9Qi54Qi55bihzLDEpCnI9cy5sZW5ndGg9PT0w -P0IueEQ6QS5BRihuZXcgQS5sSihBLlFJKHMuc3BsaXQoIi8iKSx0LnMpLHQuZE8uYShBLlBIKCkpLHQu -ZG8pLHQuTikKcS54IT09JCYmQS5wUigicGF0aFNlZ21lbnRzIikKcS5za0QocikKcD1yfXJldHVybiBw -fSwKZ20oYSl7dmFyIHMscj10aGlzLHE9ci55CmlmKHE9PT0kKXtzPUIueEIuZ20oci5nbkQoKSkKci55 -IT09JCYmQS5wUigiaGFzaENvZGUiKQpyLnk9cwpxPXN9cmV0dXJuIHF9LApnaFkoKXt2YXIgcyxyLHE9 -dGhpcyxwPXEuegppZihwPT09JCl7cz1xLmYKcj1uZXcgQS5HaihBLldYKHM9PW51bGw/IiI6cyksdC5k -dykKcS56IT09JCYmQS5wUigicXVlcnlQYXJhbWV0ZXJzIikKcS5zd2cocikKcD1yfXJldHVybiBwfSwK -Z2t1KCl7cmV0dXJuIHRoaXMuYn0sCmdKZihhKXt2YXIgcz10aGlzLmMKaWYocz09bnVsbClyZXR1cm4i -IgppZihCLnhCLm5DKHMsIlsiKSlyZXR1cm4gQi54Qi5OaihzLDEscy5sZW5ndGgtMSkKcmV0dXJuIHN9 -LApndHAoYSl7dmFyIHM9dGhpcy5kCnJldHVybiBzPT1udWxsP0Eud0sodGhpcy5hKTpzfSwKZ3RQKCl7 -dmFyIHM9dGhpcy5mCnJldHVybiBzPT1udWxsPyIiOnN9LApnS2EoKXt2YXIgcz10aGlzLnIKcmV0dXJu -IHM9PW51bGw/IiI6c30sCmhCKGEpe3ZhciBzPXRoaXMuYQppZihhLmxlbmd0aCE9PXMubGVuZ3RoKXJl -dHVybiExCnJldHVybiBBLmJVKGEscywwKT49MH0sCm5tKGEsYil7dmFyIHMscixxLHAsbyxuLG0sbCxr -LGo9dGhpcwp0LmM5LmEoYikKcz1qLmEKcj1zPT09ImZpbGUiCnE9ai5iCnA9ai5kCm89ai5jCmlmKCEo -byE9bnVsbCkpbz1xLmxlbmd0aCE9PTB8fHAhPW51bGx8fHI/IiI6bnVsbApuPWouZQppZighciltPW8h -PW51bGwmJm4ubGVuZ3RoIT09MAplbHNlIG09ITAKaWYobSYmIUIueEIubkMobiwiLyIpKW49Ii8iK24K -bD1uCms9QS5sZShudWxsLDAsMCxiKQpyZXR1cm4gQS5DZyhzLHEsbyxwLGwsayxqLnIpfSwKSmgoYSxi -KXt2YXIgcyxyLHEscCxvLG4KZm9yKHM9MCxyPTA7Qi54Qi5RaShiLCIuLi8iLHIpOyl7cis9MzsrK3N9 -cT1CLnhCLmNuKGEsIi8iKQp3aGlsZSghMCl7aWYoIShxPjAmJnM+MCkpYnJlYWsKcD1CLnhCLlBrKGEs -Ii8iLHEtMSkKaWYocDwwKWJyZWFrCm89cS1wCm49byE9PTIKaWYoIW58fG89PT0zKWlmKEIueEIuTyhh -LHArMSk9PT00NiluPSFufHxCLnhCLk8oYSxwKzIpPT09NDYKZWxzZSBuPSExCmVsc2Ugbj0hMQppZihu -KWJyZWFrOy0tcwpxPXB9cmV0dXJuIEIueEIuaTcoYSxxKzEsbnVsbCxCLnhCLnluKGIsci0zKnMpKX0s -ClpJKGEpe3JldHVybiB0aGlzLm1TKEEuaEsoYSkpfSwKbVMoYSl7dmFyIHMscixxLHAsbyxuLG0sbCxr -LGosaT10aGlzLGg9bnVsbAppZihhLmdGaSgpLmxlbmd0aCE9PTApe3M9YS5nRmkoKQppZihhLmdjaigp -KXtyPWEuZ2t1KCkKcT1hLmdKZihhKQpwPWEuZ3hBKCk/YS5ndHAoYSk6aH1lbHNle3A9aApxPXAKcj0i -In1vPUEueGUoYS5nSWkoYSkpCm49YS5nUUQoKT9hLmd0UCgpOmh9ZWxzZXtzPWkuYQppZihhLmdjaigp -KXtyPWEuZ2t1KCkKcT1hLmdKZihhKQpwPUEud0IoYS5neEEoKT9hLmd0cChhKTpoLHMpCm89QS54ZShh -LmdJaShhKSkKbj1hLmdRRCgpP2EuZ3RQKCk6aH1lbHNle3I9aS5iCnE9aS5jCnA9aS5kCm89aS5lCmlm -KGEuZ0lpKGEpPT09IiIpbj1hLmdRRCgpP2EuZ3RQKCk6aS5mCmVsc2V7bT1BLnVqKGksbykKaWYobT4w -KXtsPUIueEIuTmoobywwLG0pCm89YS5ndFQoKT9sK0EueGUoYS5nSWkoYSkpOmwrQS54ZShpLkpoKEIu -eEIueW4obyxsLmxlbmd0aCksYS5nSWkoYSkpKX1lbHNlIGlmKGEuZ3RUKCkpbz1BLnhlKGEuZ0lpKGEp -KQplbHNlIGlmKG8ubGVuZ3RoPT09MClpZihxPT1udWxsKW89cy5sZW5ndGg9PT0wP2EuZ0lpKGEpOkEu -eGUoYS5nSWkoYSkpCmVsc2Ugbz1BLnhlKCIvIithLmdJaShhKSkKZWxzZXtrPWkuSmgobyxhLmdJaShh -KSkKaj1zLmxlbmd0aD09PTAKaWYoIWp8fHEhPW51bGx8fEIueEIubkMobywiLyIpKW89QS54ZShrKQpl -bHNlIG89QS53RihrLCFqfHxxIT1udWxsKX1uPWEuZ1FEKCk/YS5ndFAoKTpofX19cmV0dXJuIEEuQ2co -cyxyLHEscCxvLG4sYS5nWjgoKT9hLmdLYSgpOmgpfSwKZ2NqKCl7cmV0dXJuIHRoaXMuYyE9bnVsbH0s -Cmd4QSgpe3JldHVybiB0aGlzLmQhPW51bGx9LApnUUQoKXtyZXR1cm4gdGhpcy5mIT1udWxsfSwKZ1o4 -KCl7cmV0dXJuIHRoaXMuciE9bnVsbH0sCmd0VCgpe3JldHVybiBCLnhCLm5DKHRoaXMuZSwiLyIpfSwK -dDQoKXt2YXIgcyxyPXRoaXMscT1yLmEKaWYocSE9PSIiJiZxIT09ImZpbGUiKXRocm93IEEuYihBLkw0 -KCJDYW5ub3QgZXh0cmFjdCBhIGZpbGUgcGF0aCBmcm9tIGEgIitxKyIgVVJJIikpCnE9ci5mCmlmKChx -PT1udWxsPyIiOnEpIT09IiIpdGhyb3cgQS5iKEEuTDQodS5pKSkKcT1yLnIKaWYoKHE9PW51bGw/IiI6 -cSkhPT0iIil0aHJvdyBBLmIoQS5MNCh1LmwpKQpxPSQud1EoKQppZihBLm9UKHEpKXE9QS5tbihyKQpl -bHNle2lmKHIuYyE9bnVsbCYmci5nSmYocikhPT0iIilBLnYoQS5MNCh1LmopKQpzPXIuZ0ZqKCkKQS5r -RShzLCExKQpxPUEubChCLnhCLm5DKHIuZSwiLyIpPyIiKyIvIjoiIixzLCIvIikKcT1xLmNoYXJDb2Rl -QXQoMCk9PTA/cTpxfXJldHVybiBxfSwKIlsiKGEpe3JldHVybiB0aGlzLmduRCgpfSwKRE4oYSxiKXt2 -YXIgcyxyLHE9dGhpcwppZihiPT1udWxsKXJldHVybiExCmlmKHE9PT1iKXJldHVybiEwCmlmKHQuZEQu -YihiKSlpZihxLmE9PT1iLmdGaSgpKWlmKHEuYyE9bnVsbD09PWIuZ2NqKCkpaWYocS5iPT09Yi5na3Uo -KSlpZihxLmdKZihxKT09PWIuZ0pmKGIpKWlmKHEuZ3RwKHEpPT09Yi5ndHAoYikpaWYocS5lPT09Yi5n -SWkoYikpe3M9cS5mCnI9cz09bnVsbAppZighcj09PWIuZ1FEKCkpe2lmKHIpcz0iIgppZihzPT09Yi5n -dFAoKSl7cz1xLnIKcj1zPT1udWxsCmlmKCFyPT09Yi5nWjgoKSl7aWYocilzPSIiCnM9cz09PWIuZ0th -KCl9ZWxzZSBzPSExfWVsc2Ugcz0hMX1lbHNlIHM9ITF9ZWxzZSBzPSExCmVsc2Ugcz0hMQplbHNlIHM9 -ITEKZWxzZSBzPSExCmVsc2Ugcz0hMQplbHNlIHM9ITEKZWxzZSBzPSExCnJldHVybiBzfSwKc2tEKGEp -e3RoaXMueD10LmEuYShhKX0sCnN3ZyhhKXt0aGlzLno9dC5JLmEoYSl9LAokaWlEOjEsCmdGaSgpe3Jl -dHVybiB0aGlzLmF9LApnSWkoYSl7cmV0dXJuIHRoaXMuZX19CkEuUloucHJvdG90eXBlPXsKJDEoYSl7 -cmV0dXJuIEEuZVAoQi5aSixBLm4oYSksQi54TSwhMSl9LAokUzoyfQpBLk1FLnByb3RvdHlwZT17CiQy -KGEsYil7dmFyIHM9dGhpcy5iLHI9dGhpcy5hCnMuYSs9ci5hCnIuYT0iJiIKcj1zLmErPUEuZVAoQi5G -MyxhLEIueE0sITApCmlmKGIhPW51bGwmJmIubGVuZ3RoIT09MCl7cy5hPXIrIj0iCnMuYSs9QS5lUChC -LkYzLGIsQi54TSwhMCl9fSwKJFM6MjN9CkEueTUucHJvdG90eXBlPXsKJDIoYSxiKXt2YXIgcyxyCkEu -bihhKQppZihiPT1udWxsfHx0eXBlb2YgYj09InN0cmluZyIpdGhpcy5hLiQyKGEsQS5rKGIpKQplbHNl -IGZvcihzPUouSVQodC5rLmEoYikpLHI9dGhpcy5hO3MuRygpOylyLiQyKGEsQS5uKHMuZ2woKSkpfSwK -JFM6MTJ9CkEuUEUucHJvdG90eXBlPXsKZ2xSKCl7dmFyIHMscixxLHAsbz10aGlzLG49bnVsbCxtPW8u -YwppZihtPT1udWxsKXttPW8uYgppZigwPj1tLmxlbmd0aClyZXR1cm4gQS5PSChtLDApCnM9by5hCm09 -bVswXSsxCnI9Qi54Qi5YVShzLCI/IixtKQpxPXMubGVuZ3RoCmlmKHI+PTApe3A9QS5QSShzLHIrMSxx -LEIuVkMsITEsITEpCnE9cn1lbHNlIHA9bgptPW8uYz1uZXcgQS5xZSgiZGF0YSIsIiIsbixuLEEuUEko -cyxtLHEsQi5XZCwhMSwhMSkscCxuKX1yZXR1cm4gbX0sCiJbIihhKXt2YXIgcyxyPXRoaXMuYgppZigw -Pj1yLmxlbmd0aClyZXR1cm4gQS5PSChyLDApCnM9dGhpcy5hCnJldHVybiByWzBdPT09LTE/ImRhdGE6 -IitzOnN9fQpBLnlJLnByb3RvdHlwZT17CiQyKGEsYil7dmFyIHM9dGhpcy5hCmlmKCEoYTxzLmxlbmd0 -aCkpcmV0dXJuIEEuT0gocyxhKQpzPXNbYV0KQi5OQS5kdShzLDAsOTYsYikKcmV0dXJuIHN9LAokUzoy -NH0KQS5jNi5wcm90b3R5cGU9ewokMyhhLGIsYyl7dmFyIHMscixxCmZvcihzPWIubGVuZ3RoLHI9MDty -PHM7KytyKXtxPUIueEIuVyhiLHIpXjk2CmlmKCEocTw5NikpcmV0dXJuIEEuT0goYSxxKQphW3FdPWN9 -fSwKJFM6MTN9CkEucWQucHJvdG90eXBlPXsKJDMoYSxiLGMpe3ZhciBzLHIscQpmb3Iocz1CLnhCLlco -YiwwKSxyPUIueEIuVyhiLDEpO3M8PXI7KytzKXtxPShzXjk2KT4+PjAKaWYoIShxPDk2KSlyZXR1cm4g -QS5PSChhLHEpCmFbcV09Y319LAokUzoxM30KQS5VZi5wcm90b3R5cGU9ewpnY2ooKXtyZXR1cm4gdGhp -cy5jPjB9LApneEEoKXtyZXR1cm4gdGhpcy5jPjAmJnRoaXMuZCsxPHRoaXMuZX0sCmdRRCgpe3JldHVy -biB0aGlzLmY8dGhpcy5yfSwKZ1o4KCl7cmV0dXJuIHRoaXMucjx0aGlzLmEubGVuZ3RofSwKZ3RUKCl7 -cmV0dXJuIEIueEIuUWkodGhpcy5hLCIvIix0aGlzLmUpfSwKZ0ZpKCl7dmFyIHM9dGhpcy53CnJldHVy -biBzPT1udWxsP3RoaXMudz10aGlzLlUyKCk6c30sClUyKCl7dmFyIHMscj10aGlzLHE9ci5iCmlmKHE8 -PTApcmV0dXJuIiIKcz1xPT09NAppZihzJiZCLnhCLm5DKHIuYSwiaHR0cCIpKXJldHVybiJodHRwIgpp -ZihxPT09NSYmQi54Qi5uQyhyLmEsImh0dHBzIikpcmV0dXJuImh0dHBzIgppZihzJiZCLnhCLm5DKHIu -YSwiZmlsZSIpKXJldHVybiJmaWxlIgppZihxPT09NyYmQi54Qi5uQyhyLmEsInBhY2thZ2UiKSlyZXR1 -cm4icGFja2FnZSIKcmV0dXJuIEIueEIuTmooci5hLDAscSl9LApna3UoKXt2YXIgcz10aGlzLmMscj10 -aGlzLmIrMwpyZXR1cm4gcz5yP0IueEIuTmoodGhpcy5hLHIscy0xKToiIn0sCmdKZihhKXt2YXIgcz10 -aGlzLmMKcmV0dXJuIHM+MD9CLnhCLk5qKHRoaXMuYSxzLHRoaXMuZCk6IiJ9LApndHAoYSl7dmFyIHMs -cj10aGlzCmlmKHIuZ3hBKCkpcmV0dXJuIEEuUUEoQi54Qi5OaihyLmEsci5kKzEsci5lKSxudWxsKQpz -PXIuYgppZihzPT09NCYmQi54Qi5uQyhyLmEsImh0dHAiKSlyZXR1cm4gODAKaWYocz09PTUmJkIueEIu -bkMoci5hLCJodHRwcyIpKXJldHVybiA0NDMKcmV0dXJuIDB9LApnSWkoYSl7cmV0dXJuIEIueEIuTmoo -dGhpcy5hLHRoaXMuZSx0aGlzLmYpfSwKZ3RQKCl7dmFyIHM9dGhpcy5mLHI9dGhpcy5yCnJldHVybiBz -PHI/Qi54Qi5Oaih0aGlzLmEscysxLHIpOiIifSwKZ0thKCl7dmFyIHM9dGhpcy5yLHI9dGhpcy5hCnJl -dHVybiBzPHIubGVuZ3RoP0IueEIueW4ocixzKzEpOiIifSwKZ0ZqKCl7dmFyIHMscixxPXRoaXMuZSxw -PXRoaXMuZixvPXRoaXMuYQppZihCLnhCLlFpKG8sIi8iLHEpKSsrcQppZihxPT09cClyZXR1cm4gQi54 -RApzPUEuUUkoW10sdC5zKQpmb3Iocj1xO3I8cDsrK3IpaWYoQi54Qi5PKG8scik9PT00Nyl7Qi5ObS5p -KHMsQi54Qi5OaihvLHEscikpCnE9cisxfUIuTm0uaShzLEIueEIuTmoobyxxLHApKQpyZXR1cm4gQS5B -RihzLHQuTil9LApnaFkoKXtpZih0aGlzLmY+PXRoaXMucilyZXR1cm4gQi5DTQpyZXR1cm4gbmV3IEEu -R2ooQS5XWCh0aGlzLmd0UCgpKSx0LmR3KX0sCmtYKGEpe3ZhciBzPXRoaXMuZCsxCnJldHVybiBzK2Eu -bGVuZ3RoPT09dGhpcy5lJiZCLnhCLlFpKHRoaXMuYSxhLHMpfSwKTjkoKXt2YXIgcz10aGlzLHI9cy5y -LHE9cy5hCmlmKHI+PXEubGVuZ3RoKXJldHVybiBzCnJldHVybiBuZXcgQS5VZihCLnhCLk5qKHEsMCxy -KSxzLmIscy5jLHMuZCxzLmUscy5mLHIscy53KX0sCm5tKGEsYil7dmFyIHMscixxLHAsbyxuLG0sbCxr -LGosaT10aGlzLGg9bnVsbAp0LmM5LmEoYikKcz1pLmdGaSgpCnI9cz09PSJmaWxlIgpxPWkuYwpwPXE+ -MD9CLnhCLk5qKGkuYSxpLmIrMyxxKToiIgpvPWkuZ3hBKCk/aS5ndHAoaSk6aApxPWkuYwppZihxPjAp -bj1CLnhCLk5qKGkuYSxxLGkuZCkKZWxzZSBuPXAubGVuZ3RoIT09MHx8byE9bnVsbHx8cj8iIjpoCnE9 -aS5hCm09Qi54Qi5OaihxLGkuZSxpLmYpCmlmKCFyKWw9biE9bnVsbCYmbS5sZW5ndGghPT0wCmVsc2Ug -bD0hMAppZihsJiYhQi54Qi5uQyhtLCIvIikpbT0iLyIrbQprPUEubGUoaCwwLDAsYikKbD1pLnIKaj1s -PHEubGVuZ3RoP0IueEIueW4ocSxsKzEpOmgKcmV0dXJuIEEuQ2cocyxwLG4sbyxtLGssail9LApaSShh -KXtyZXR1cm4gdGhpcy5tUyhBLmhLKGEpKX0sCm1TKGEpe2lmKGEgaW5zdGFuY2VvZiBBLlVmKXJldHVy -biB0aGlzLnUxKHRoaXMsYSkKcmV0dXJuIHRoaXMuUmUoKS5tUyhhKX0sCnUxKGEsYil7dmFyIHMscixx -LHAsbyxuLG0sbCxrLGosaSxoLGcsZixlLGQsYz1iLmIKaWYoYz4wKXJldHVybiBiCnM9Yi5jCmlmKHM+ -MCl7cj1hLmIKaWYocjw9MClyZXR1cm4gYgpxPXI9PT00CmlmKHEmJkIueEIubkMoYS5hLCJmaWxlIikp -cD1iLmUhPT1iLmYKZWxzZSBpZihxJiZCLnhCLm5DKGEuYSwiaHR0cCIpKXA9IWIua1goIjgwIikKZWxz -ZSBwPSEocj09PTUmJkIueEIubkMoYS5hLCJodHRwcyIpKXx8IWIua1goIjQ0MyIpCmlmKHApe289cisx -CnJldHVybiBuZXcgQS5VZihCLnhCLk5qKGEuYSwwLG8pK0IueEIueW4oYi5hLGMrMSkscixzK28sYi5k -K28sYi5lK28sYi5mK28sYi5yK28sYS53KX1lbHNlIHJldHVybiB0aGlzLlJlKCkubVMoYil9bj1iLmUK -Yz1iLmYKaWYobj09PWMpe3M9Yi5yCmlmKGM8cyl7cj1hLmYKbz1yLWMKcmV0dXJuIG5ldyBBLlVmKEIu -eEIuTmooYS5hLDAscikrQi54Qi55bihiLmEsYyksYS5iLGEuYyxhLmQsYS5lLGMrbyxzK28sYS53KX1j -PWIuYQppZihzPGMubGVuZ3RoKXtyPWEucgpyZXR1cm4gbmV3IEEuVWYoQi54Qi5OaihhLmEsMCxyKStC -LnhCLnluKGMscyksYS5iLGEuYyxhLmQsYS5lLGEuZixzKyhyLXMpLGEudyl9cmV0dXJuIGEuTjkoKX1z -PWIuYQppZihCLnhCLlFpKHMsIi8iLG4pKXttPWEuZQpsPUEuUngodGhpcykKaz1sPjA/bDptCm89ay1u -CnJldHVybiBuZXcgQS5VZihCLnhCLk5qKGEuYSwwLGspK0IueEIueW4ocyxuKSxhLmIsYS5jLGEuZCxt -LGMrbyxiLnIrbyxhLncpfWo9YS5lCmk9YS5mCmlmKGo9PT1pJiZhLmM+MCl7Zm9yKDtCLnhCLlFpKHMs -Ii4uLyIsbik7KW4rPTMKbz1qLW4rMQpyZXR1cm4gbmV3IEEuVWYoQi54Qi5OaihhLmEsMCxqKSsiLyIr -Qi54Qi55bihzLG4pLGEuYixhLmMsYS5kLGosYytvLGIucitvLGEudyl9aD1hLmEKbD1BLlJ4KHRoaXMp -CmlmKGw+PTApZz1sCmVsc2UgZm9yKGc9ajtCLnhCLlFpKGgsIi4uLyIsZyk7KWcrPTMKZj0wCndoaWxl -KCEwKXtlPW4rMwppZighKGU8PWMmJkIueEIuUWkocywiLi4vIixuKSkpYnJlYWs7KytmCm49ZX1mb3Io -ZD0iIjtpPmc7KXstLWkKaWYoQi54Qi5PKGgsaSk9PT00Nyl7aWYoZj09PTApe2Q9Ii8iCmJyZWFrfS0t -ZgpkPSIvIn19aWYoaT09PWcmJmEuYjw9MCYmIUIueEIuUWkoaCwiLyIsaikpe24tPWYqMwpkPSIifW89 -aS1uK2QubGVuZ3RoCnJldHVybiBuZXcgQS5VZihCLnhCLk5qKGgsMCxpKStkK0IueEIueW4ocyxuKSxh -LmIsYS5jLGEuZCxqLGMrbyxiLnIrbyxhLncpfSwKdDQoKXt2YXIgcyxyLHE9dGhpcyxwPXEuYgppZihw -Pj0wKXtzPSEocD09PTQmJkIueEIubkMocS5hLCJmaWxlIikpCnA9c31lbHNlIHA9ITEKaWYocCl0aHJv -dyBBLmIoQS5MNCgiQ2Fubm90IGV4dHJhY3QgYSBmaWxlIHBhdGggZnJvbSBhICIrcS5nRmkoKSsiIFVS -SSIpKQpwPXEuZgpzPXEuYQppZihwPHMubGVuZ3RoKXtpZihwPHEucil0aHJvdyBBLmIoQS5MNCh1Lmkp -KQp0aHJvdyBBLmIoQS5MNCh1LmwpKX1yPSQud1EoKQppZihBLm9UKHIpKXA9QS5tbihxKQplbHNle2lm -KHEuYzxxLmQpQS52KEEuTDQodS5qKSkKcD1CLnhCLk5qKHMscS5lLHApfXJldHVybiBwfSwKZ20oYSl7 -dmFyIHM9dGhpcy54CnJldHVybiBzPT1udWxsP3RoaXMueD1CLnhCLmdtKHRoaXMuYSk6c30sCkROKGEs -Yil7aWYoYj09bnVsbClyZXR1cm4hMQppZih0aGlzPT09YilyZXR1cm4hMApyZXR1cm4gdC5kRC5iKGIp -JiZ0aGlzLmE9PT1iWyJbIl0oMCl9LApSZSgpe3ZhciBzPXRoaXMscj1udWxsLHE9cy5nRmkoKSxwPXMu -Z2t1KCksbz1zLmM+MD9zLmdKZihzKTpyLG49cy5neEEoKT9zLmd0cChzKTpyLG09cy5hLGw9cy5mLGs9 -Qi54Qi5OaihtLHMuZSxsKSxqPXMucgpsPWw8aj9zLmd0UCgpOnIKcmV0dXJuIEEuQ2cocSxwLG8sbixr -LGwsajxtLmxlbmd0aD9zLmdLYSgpOnIpfSwKIlsiKGEpe3JldHVybiB0aGlzLmF9LAokaWlEOjF9CkEu -cWUucHJvdG90eXBlPXt9CkEucUUucHJvdG90eXBlPXt9CkEuR2gucHJvdG90eXBlPXsKc0xVKGEsYil7 -YS5ocmVmPWJ9LAoiWyIoYSl7cmV0dXJuIFN0cmluZyhhKX0sCiRpR2g6MX0KQS5mWS5wcm90b3R5cGU9 -ewoiWyIoYSl7cmV0dXJuIFN0cmluZyhhKX19CkEucloucHJvdG90eXBlPXskaXJaOjF9CkEuQXoucHJv -dG90eXBlPXskaUF6OjF9CkEuUVAucHJvdG90eXBlPXskaVFQOjF9CkEubngucHJvdG90eXBlPXsKZ2so -YSl7cmV0dXJuIGEubGVuZ3RofX0KQS5vSi5wcm90b3R5cGU9ewpnayhhKXtyZXR1cm4gYS5sZW5ndGh9 -fQpBLmlkLnByb3RvdHlwZT17fQpBLlFGLnByb3RvdHlwZT17fQpBLk5oLnByb3RvdHlwZT17CiJbIihh -KXtyZXR1cm4gU3RyaW5nKGEpfX0KQS5hZS5wcm90b3R5cGU9ewpEYyhhLGIpe3JldHVybiBhLmNyZWF0 -ZUhUTUxEb2N1bWVudChiKX19CkEuSUIucHJvdG90eXBlPXsKIlsiKGEpe3ZhciBzLHIscSxwPWEubGVm -dApwLnRvU3RyaW5nCnM9YS50b3AKcy50b1N0cmluZwpyPWEud2lkdGgKci50b1N0cmluZwpxPWEuaGVp -Z2h0CnEudG9TdHJpbmcKcmV0dXJuIlJlY3RhbmdsZSAoIitBLmQocCkrIiwgIitBLmQocykrIikgIitB -LmQocikrIiB4ICIrQS5kKHEpfSwKRE4oYSxiKXt2YXIgcyxyCmlmKGI9PW51bGwpcmV0dXJuITEKaWYo -dC5xLmIoYikpe3M9YS5sZWZ0CnMudG9TdHJpbmcKcj1iLmxlZnQKci50b1N0cmluZwppZihzPT09cil7 -cz1hLnRvcApzLnRvU3RyaW5nCnI9Yi50b3AKci50b1N0cmluZwppZihzPT09cil7cz1hLndpZHRoCnMu -dG9TdHJpbmcKcj1iLndpZHRoCnIudG9TdHJpbmcKaWYocz09PXIpe3M9YS5oZWlnaHQKcy50b1N0cmlu -ZwpyPWIuaGVpZ2h0CnIudG9TdHJpbmcKcj1zPT09cgpzPXJ9ZWxzZSBzPSExfWVsc2Ugcz0hMX1lbHNl -IHM9ITF9ZWxzZSBzPSExCnJldHVybiBzfSwKZ20oYSl7dmFyIHMscixxLHA9YS5sZWZ0CnAudG9TdHJp -bmcKcz1hLnRvcApzLnRvU3RyaW5nCnI9YS53aWR0aApyLnRvU3RyaW5nCnE9YS5oZWlnaHQKcS50b1N0 -cmluZwpyZXR1cm4gQS5mNShwLHMscixxKX0sCiRpdG46MX0KQS5uNy5wcm90b3R5cGU9ewpnayhhKXty -ZXR1cm4gYS5sZW5ndGh9fQpBLnd6LnByb3RvdHlwZT17CmdrKGEpe3JldHVybiB0aGlzLmEubGVuZ3Ro -fSwKcShhLGIpe3ZhciBzCkEuSVooYikKcz10aGlzLmEKaWYoIShiPj0wJiZiPHMubGVuZ3RoKSlyZXR1 -cm4gQS5PSChzLGIpCnJldHVybiB0aGlzLiR0aS5jLmEoc1tiXSl9LApZNShhLGIsYyl7dGhpcy4kdGku -Yy5hKGMpCnRocm93IEEuYihBLkw0KCJDYW5ub3QgbW9kaWZ5IGxpc3QiKSl9fQpBLmN2LnByb3RvdHlw -ZT17CmdRZyhhKXtyZXR1cm4gbmV3IEEuaTcoYSl9LApnUChhKXtyZXR1cm4gbmV3IEEuSTQoYSl9LApz -UChhLGIpe3ZhciBzCnQuTy5hKGIpCnM9dGhpcy5nUChhKQpzLlYxKDApCnMuRlYoMCxiKX0sCiJbIihh -KXtyZXR1cm4gYS5sb2NhbE5hbWV9LApGRihhKXt2YXIgcz0hIWEuc2Nyb2xsSW50b1ZpZXdJZk5lZWRl -ZAppZihzKWEuc2Nyb2xsSW50b1ZpZXdJZk5lZWRlZCgpCmVsc2UgYS5zY3JvbGxJbnRvVmlldygpfSwK -bnooYSxiLGMsZCxlKXt2YXIgcyxyPXRoaXMucjYoYSxjLGQsZSkKc3dpdGNoKGIudG9Mb3dlckNhc2Uo -KSl7Y2FzZSJiZWZvcmViZWdpbiI6cz1hLnBhcmVudE5vZGUKcy50b1N0cmluZwpKLkVoKHMscixhKQpi -cmVhawpjYXNlImFmdGVyYmVnaW4iOnM9YS5jaGlsZE5vZGVzCnRoaXMubUsoYSxyLHMubGVuZ3RoPjA/ -c1swXTpudWxsKQpicmVhawpjYXNlImJlZm9yZWVuZCI6YS5hcHBlbmRDaGlsZChyKQpicmVhawpjYXNl -ImFmdGVyZW5kIjpzPWEucGFyZW50Tm9kZQpzLnRvU3RyaW5nCkouRWgocyxyLGEubmV4dFNpYmxpbmcp -CmJyZWFrCmRlZmF1bHQ6QS52KEEueFkoIkludmFsaWQgcG9zaXRpb24gIitiLG51bGwpKX19LApyNihh -LGIsYyxkKXt2YXIgcyxyLHEscAppZihjPT1udWxsKXtpZihkPT1udWxsKXtzPSQubHQKaWYocz09bnVs -bCl7cz1BLlFJKFtdLHQuRCkKcj1uZXcgQS52RChzKQpCLk5tLmkocyxBLlR3KG51bGwpKQpCLk5tLmko -cyxBLkJsKCkpCiQubHQ9cgpkPXJ9ZWxzZSBkPXN9cz0kLkVVCmlmKHM9PW51bGwpe3M9bmV3IEEuS28o -ZCkKJC5FVT1zCmM9c31lbHNle3MuYT1kCmM9c319ZWxzZSBpZihkIT1udWxsKXRocm93IEEuYihBLnhZ -KCJ2YWxpZGF0b3IgY2FuIG9ubHkgYmUgcGFzc2VkIGlmIHRyZWVTYW5pdGl6ZXIgaXMgbnVsbCIsbnVs -bCkpCmlmKCQueG89PW51bGwpe3M9ZG9jdW1lbnQKcj1zLmltcGxlbWVudGF0aW9uCnIudG9TdHJpbmcK -cj1CLm1ILkRjKHIsIiIpCiQueG89cgokLkJPPXIuY3JlYXRlUmFuZ2UoKQpyPSQueG8uY3JlYXRlRWxl -bWVudCgiYmFzZSIpCnQuY1IuYShyKQpzPXMuYmFzZVVSSQpzLnRvU3RyaW5nCnIuaHJlZj1zCiQueG8u -aGVhZC5hcHBlbmRDaGlsZChyKX1zPSQueG8KaWYocy5ib2R5PT1udWxsKXtyPXMuY3JlYXRlRWxlbWVu -dCgiYm9keSIpCkIuQlouc0dTKHMsdC5yLmEocikpfXM9JC54bwppZih0LnIuYihhKSl7cz1zLmJvZHkK -cy50b1N0cmluZwpxPXN9ZWxzZXtzLnRvU3RyaW5nCnE9cy5jcmVhdGVFbGVtZW50KGEudGFnTmFtZSkK -JC54by5ib2R5LmFwcGVuZENoaWxkKHEpfWlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdp -bmRvdy5SYW5nZS5wcm90b3R5cGUmJiFCLk5tLnRnKEIuU3EsYS50YWdOYW1lKSl7JC5CTy5zZWxlY3RO -b2RlQ29udGVudHMocSkKcz0kLkJPCnMudG9TdHJpbmcKcD1zLmNyZWF0ZUNvbnRleHR1YWxGcmFnbWVu -dChiPT1udWxsPyJudWxsIjpiKX1lbHNle0oud2YocSxiKQpwPSQueG8uY3JlYXRlRG9jdW1lbnRGcmFn -bWVudCgpCmZvcig7cz1xLmZpcnN0Q2hpbGQscyE9bnVsbDspcC5hcHBlbmRDaGlsZChzKX1pZihxIT09 -JC54by5ib2R5KUouTHQocSkKYy5QbihwKQpkb2N1bWVudC5hZG9wdE5vZGUocCkKcmV0dXJuIHB9LApB -SChhLGIsYyl7cmV0dXJuIHRoaXMucjYoYSxiLGMsbnVsbCl9LApwayhhLGIsYyl7dGhpcy5zYTQoYSxu -dWxsKQphLmFwcGVuZENoaWxkKHRoaXMucjYoYSxiLG51bGwsYykpfSwKc1JOKGEsYil7YS5pbm5lckhU -TUw9Yn0sCmdWbChhKXtyZXR1cm4gbmV3IEEuQ3EoYSwiY2xpY2siLCExLHQuUSl9LAokaWN2OjF9CkEu -Q3YucHJvdG90eXBlPXsKJDEoYSl7cmV0dXJuIHQuaC5iKHQuQS5hKGEpKX0sCiRTOjI2fQpBLmVhLnBy -b3RvdHlwZT17JGllYToxfQpBLlBaLnByb3RvdHlwZT17Ck9uKGEsYixjLGQpe3QuYncuYShjKQppZihj -IT1udWxsKXRoaXMudihhLGIsYyxkKX0sCkIoYSxiLGMpe3JldHVybiB0aGlzLk9uKGEsYixjLG51bGwp -fSwKdihhLGIsYyxkKXtyZXR1cm4gYS5hZGRFdmVudExpc3RlbmVyKGIsQS50Uih0LmJ3LmEoYyksMSks -ZCl9LAokaVBaOjF9CkEuaEgucHJvdG90eXBlPXskaWhIOjF9CkEuaDQucHJvdG90eXBlPXsKZ2soYSl7 -cmV0dXJuIGEubGVuZ3RofX0KQS5ici5wcm90b3R5cGU9ewpnayhhKXtyZXR1cm4gYS5sZW5ndGh9fQpB -LlZiLnByb3RvdHlwZT17CnNHUyhhLGIpe2EuYm9keT1ifX0KQS5mSi5wcm90b3R5cGU9ewplbyhhLGIs -YyxkKXtyZXR1cm4gYS5vcGVuKGIsYywhMCl9LAokaWZKOjF9CkEud2EucHJvdG90eXBlPXt9CkEuU2cu -cHJvdG90eXBlPXskaVNnOjF9CkEudTgucHJvdG90eXBlPXsKZ0RyKGEpe2lmKCJvcmlnaW4iIGluIGEp -cmV0dXJuIGEub3JpZ2luCnJldHVybiBhLnByb3RvY29sKyIvLyIrYS5ob3N0fSwKIlsiKGEpe3JldHVy -biBTdHJpbmcoYSl9LAokaXU4OjF9CkEuQWoucHJvdG90eXBlPXskaUFqOjF9CkEuZTcucHJvdG90eXBl -PXsKZ3I4KGEpe3ZhciBzPXRoaXMuYSxyPXMuY2hpbGROb2Rlcy5sZW5ndGgKaWYocj09PTApdGhyb3cg -QS5iKEEuUFYoIk5vIGVsZW1lbnRzIikpCmlmKHI+MSl0aHJvdyBBLmIoQS5QVigiTW9yZSB0aGFuIG9u -ZSBlbGVtZW50IikpCnM9cy5maXJzdENoaWxkCnMudG9TdHJpbmcKcmV0dXJuIHN9LApGVihhLGIpe3Zh -ciBzLHIscSxwLG8KdC5laC5hKGIpCmlmKGIgaW5zdGFuY2VvZiBBLmU3KXtzPWIuYQpyPXRoaXMuYQpp -ZihzIT09cilmb3IocT1zLmNoaWxkTm9kZXMubGVuZ3RoLHA9MDtwPHE7KytwKXtvPXMuZmlyc3RDaGls -ZApvLnRvU3RyaW5nCnIuYXBwZW5kQ2hpbGQobyl9cmV0dXJufWZvcihzPWIuZ00oYikscj10aGlzLmE7 -cy5HKCk7KXIuYXBwZW5kQ2hpbGQocy5nbCgpKX0sClk1KGEsYixjKXt2YXIgcyxyCnQuQS5hKGMpCnM9 -dGhpcy5hCnI9cy5jaGlsZE5vZGVzCmlmKCEoYj49MCYmYjxyLmxlbmd0aCkpcmV0dXJuIEEuT0gocixi -KQpzLnJlcGxhY2VDaGlsZChjLHJbYl0pfSwKZ00oYSl7dmFyIHM9dGhpcy5hLmNoaWxkTm9kZXMKcmV0 -dXJuIG5ldyBBLlc5KHMscy5sZW5ndGgsQS56SyhzKS5DKCJXOTxHbS5FPiIpKX0sCmdrKGEpe3JldHVy -biB0aGlzLmEuY2hpbGROb2Rlcy5sZW5ndGh9LApxKGEsYil7dmFyIHMKQS5JWihiKQpzPXRoaXMuYS5j -aGlsZE5vZGVzCmlmKCEoYj49MCYmYjxzLmxlbmd0aCkpcmV0dXJuIEEuT0gocyxiKQpyZXR1cm4gc1ti -XX19CkEuS1YucHJvdG90eXBlPXsKekIoYSl7dmFyIHM9YS5wYXJlbnROb2RlCmlmKHMhPW51bGwpcy5y -ZW1vdmVDaGlsZChhKX0sCkQ0KGEpe3ZhciBzCmZvcig7cz1hLmZpcnN0Q2hpbGQscyE9bnVsbDspYS5y -ZW1vdmVDaGlsZChzKX0sCiJbIihhKXt2YXIgcz1hLm5vZGVWYWx1ZQpyZXR1cm4gcz09bnVsbD90aGlz -LlUoYSk6c30sCnNhNChhLGIpe2EudGV4dENvbnRlbnQ9Yn0sCm1LKGEsYixjKXtyZXR1cm4gYS5pbnNl -cnRCZWZvcmUoYixjKX0sCiRpS1Y6MX0KQS5CSC5wcm90b3R5cGU9ewpnayhhKXtyZXR1cm4gYS5sZW5n -dGh9LApxKGEsYil7dmFyIHMKQS5JWihiKQpzPWEubGVuZ3RoCmlmKGI+Pj4wIT09Ynx8Yj49cyl0aHJv -dyBBLmIoQS54RihiLHMsYSxudWxsKSkKcmV0dXJuIGFbYl19LApZNShhLGIsYyl7dC5BLmEoYykKdGhy -b3cgQS5iKEEuTDQoIkNhbm5vdCBhc3NpZ24gZWxlbWVudCBvZiBpbW11dGFibGUgTGlzdC4iKSl9LApn -dEgoYSl7aWYoYS5sZW5ndGg+MClyZXR1cm4gYVswXQp0aHJvdyBBLmIoQS5QVigiTm8gZWxlbWVudHMi -KSl9LApBKGEsYil7aWYoIShiPj0wJiZiPGEubGVuZ3RoKSlyZXR1cm4gQS5PSChhLGIpCnJldHVybiBh -W2JdfSwKJGliUToxLAokaVhqOjEsCiRpY1g6MSwKJGl6TToxfQpBLlNOLnByb3RvdHlwZT17fQpBLndW -LnByb3RvdHlwZT17JGl3VjoxfQpBLmxwLnByb3RvdHlwZT17CmdrKGEpe3JldHVybiBhLmxlbmd0aH19 -CkEuVGIucHJvdG90eXBlPXsKcjYoYSxiLGMsZCl7dmFyIHMscgppZigiY3JlYXRlQ29udGV4dHVhbEZy -YWdtZW50IiBpbiB3aW5kb3cuUmFuZ2UucHJvdG90eXBlKXJldHVybiB0aGlzLkRXKGEsYixjLGQpCnM9 -QS5VOSgiPHRhYmxlPiIrQS5kKGIpKyI8L3RhYmxlPiIsYyxkKQpyPWRvY3VtZW50LmNyZWF0ZURvY3Vt -ZW50RnJhZ21lbnQoKQpuZXcgQS5lNyhyKS5GVigwLG5ldyBBLmU3KHMpKQpyZXR1cm4gcn19CkEuSXYu -cHJvdG90eXBlPXsKcjYoYSxiLGMsZCl7dmFyIHMscgppZigiY3JlYXRlQ29udGV4dHVhbEZyYWdtZW50 -IiBpbiB3aW5kb3cuUmFuZ2UucHJvdG90eXBlKXJldHVybiB0aGlzLkRXKGEsYixjLGQpCnM9ZG9jdW1l -bnQKcj1zLmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKQpzPW5ldyBBLmU3KEIuSWUucjYocy5jcmVhdGVF -bGVtZW50KCJ0YWJsZSIpLGIsYyxkKSkKcz1uZXcgQS5lNyhzLmdyOChzKSkKbmV3IEEuZTcocikuRlYo -MCxuZXcgQS5lNyhzLmdyOChzKSkpCnJldHVybiByfX0KQS5XUC5wcm90b3R5cGU9ewpyNihhLGIsYyxk -KXt2YXIgcyxyCmlmKCJjcmVhdGVDb250ZXh0dWFsRnJhZ21lbnQiIGluIHdpbmRvdy5SYW5nZS5wcm90 -b3R5cGUpcmV0dXJuIHRoaXMuRFcoYSxiLGMsZCkKcz1kb2N1bWVudApyPXMuY3JlYXRlRG9jdW1lbnRG -cmFnbWVudCgpCnM9bmV3IEEuZTcoQi5JZS5yNihzLmNyZWF0ZUVsZW1lbnQoInRhYmxlIiksYixjLGQp -KQpuZXcgQS5lNyhyKS5GVigwLG5ldyBBLmU3KHMuZ3I4KHMpKSkKcmV0dXJuIHJ9fQpBLnlZLnByb3Rv -dHlwZT17CnBrKGEsYixjKXt2YXIgcyxyCnRoaXMuc2E0KGEsbnVsbCkKcz1hLmNvbnRlbnQKcy50b1N0 -cmluZwpKLmJUKHMpCnI9dGhpcy5yNihhLGIsbnVsbCxjKQphLmNvbnRlbnQuYXBwZW5kQ2hpbGQocil9 -LAokaXlZOjF9CkEudzYucHJvdG90eXBlPXt9CkEuSzUucHJvdG90eXBlPXsKUG8oYSxiLGMpe3ZhciBz -PUEuUDEoYS5vcGVuKGIsYykpCnJldHVybiBzfSwKZ21XKGEpe3JldHVybiB0LkYuYShhLmxvY2F0aW9u -KX0sCnVzKGEsYil7cmV0dXJuIGEuY29uZmlybShiKX0sCiRpSzU6MSwKJGlkQToxfQpBLkNtLnByb3Rv -dHlwZT17JGlDbToxfQpBLkNRLnByb3RvdHlwZT17JGlDUToxfQpBLnc0LnByb3RvdHlwZT17CiJbIihh -KXt2YXIgcyxyLHEscD1hLmxlZnQKcC50b1N0cmluZwpzPWEudG9wCnMudG9TdHJpbmcKcj1hLndpZHRo -CnIudG9TdHJpbmcKcT1hLmhlaWdodApxLnRvU3RyaW5nCnJldHVybiJSZWN0YW5nbGUgKCIrQS5kKHAp -KyIsICIrQS5kKHMpKyIpICIrQS5kKHIpKyIgeCAiK0EuZChxKX0sCkROKGEsYil7dmFyIHMscgppZihi -PT1udWxsKXJldHVybiExCmlmKHQucS5iKGIpKXtzPWEubGVmdApzLnRvU3RyaW5nCnI9Yi5sZWZ0CnIu -dG9TdHJpbmcKaWYocz09PXIpe3M9YS50b3AKcy50b1N0cmluZwpyPWIudG9wCnIudG9TdHJpbmcKaWYo -cz09PXIpe3M9YS53aWR0aApzLnRvU3RyaW5nCnI9Yi53aWR0aApyLnRvU3RyaW5nCmlmKHM9PT1yKXtz -PWEuaGVpZ2h0CnMudG9TdHJpbmcKcj1iLmhlaWdodApyLnRvU3RyaW5nCnI9cz09PXIKcz1yfWVsc2Ug -cz0hMX1lbHNlIHM9ITF9ZWxzZSBzPSExfWVsc2Ugcz0hMQpyZXR1cm4gc30sCmdtKGEpe3ZhciBzLHIs -cSxwPWEubGVmdApwLnRvU3RyaW5nCnM9YS50b3AKcy50b1N0cmluZwpyPWEud2lkdGgKci50b1N0cmlu -ZwpxPWEuaGVpZ2h0CnEudG9TdHJpbmcKcmV0dXJuIEEuZjUocCxzLHIscSl9fQpBLnJoLnByb3RvdHlw -ZT17CmdrKGEpe3JldHVybiBhLmxlbmd0aH0sCnEoYSxiKXt2YXIgcwpBLklaKGIpCnM9YS5sZW5ndGgK -aWYoYj4+PjAhPT1ifHxiPj1zKXRocm93IEEuYihBLnhGKGIscyxhLG51bGwpKQpyZXR1cm4gYVtiXX0s -Clk1KGEsYixjKXt0LkEuYShjKQp0aHJvdyBBLmIoQS5MNCgiQ2Fubm90IGFzc2lnbiBlbGVtZW50IG9m -IGltbXV0YWJsZSBMaXN0LiIpKX0sCkEoYSxiKXtpZighKGI+PTAmJmI8YS5sZW5ndGgpKXJldHVybiBB -Lk9IKGEsYikKcmV0dXJuIGFbYl19LAokaWJROjEsCiRpWGo6MSwKJGljWDoxLAokaXpNOjF9CkEuY2Yu -cHJvdG90eXBlPXsKSyhhLGIpe3ZhciBzLHIscSxwLG8sbgp0LmVBLmEoYikKZm9yKHM9dGhpcy5ndmMo -KSxyPXMubGVuZ3RoLHE9dGhpcy5hLHA9MDtwPHMubGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAsQS5sayko -cyksKytwKXtvPXNbcF0Kbj1xLmdldEF0dHJpYnV0ZShvKQpiLiQyKG8sbj09bnVsbD9BLm4obik6bil9 -fSwKZ3ZjKCl7dmFyIHMscixxLHAsbyxuLG09dGhpcy5hLmF0dHJpYnV0ZXMKbS50b1N0cmluZwpzPUEu -UUkoW10sdC5zKQpmb3Iocj1tLmxlbmd0aCxxPXQuaDkscD0wO3A8cjsrK3Ape2lmKCEocDxtLmxlbmd0 -aCkpcmV0dXJuIEEuT0gobSxwKQpvPXEuYShtW3BdKQppZihvLm5hbWVzcGFjZVVSST09bnVsbCl7bj1v -Lm5hbWUKbi50b1N0cmluZwpCLk5tLmkocyxuKX19cmV0dXJuIHN9LApnbDAoYSl7cmV0dXJuIHRoaXMu -Z3ZjKCkubGVuZ3RoPT09MH19CkEuaTcucHJvdG90eXBlPXsKeDQoYSl7dmFyIHM9dGhpcy5hLmhhc0F0 -dHJpYnV0ZShhKQpyZXR1cm4gc30sCnEoYSxiKXtyZXR1cm4gdGhpcy5hLmdldEF0dHJpYnV0ZShBLm4o -YikpfSwKWTUoYSxiLGMpe3RoaXMuYS5zZXRBdHRyaWJ1dGUoYixjKX0sCmdrKGEpe3JldHVybiB0aGlz -Lmd2YygpLmxlbmd0aH19CkEuU3kucHJvdG90eXBlPXsKeDQoYSl7dmFyIHM9dGhpcy5hLmEuaGFzQXR0 -cmlidXRlKCJkYXRhLSIrdGhpcy5PVShhKSkKcmV0dXJuIHN9LApxKGEsYil7cmV0dXJuIHRoaXMuYS5h -LmdldEF0dHJpYnV0ZSgiZGF0YS0iK3RoaXMuT1UoQS5uKGIpKSl9LApZNShhLGIsYyl7dGhpcy5hLmEu -c2V0QXR0cmlidXRlKCJkYXRhLSIrdGhpcy5PVShiKSxjKX0sCksoYSxiKXt0aGlzLmEuSygwLG5ldyBB -LktTKHRoaXMsdC5lQS5hKGIpKSl9LApndmMoKXt2YXIgcz1BLlFJKFtdLHQucykKdGhpcy5hLksoMCxu -ZXcgQS5BMyh0aGlzLHMpKQpyZXR1cm4gc30sCmdrKGEpe3JldHVybiB0aGlzLmd2YygpLmxlbmd0aH0s -CmdsMChhKXtyZXR1cm4gdGhpcy5ndmMoKS5sZW5ndGg9PT0wfSwKeHEoYSl7dmFyIHMscixxPUEuUUko -YS5zcGxpdCgiLSIpLHQucykKZm9yKHM9MTtzPHEubGVuZ3RoOysrcyl7cj1xW3NdCmlmKHIubGVuZ3Ro -PjApQi5ObS5ZNShxLHMsclswXS50b1VwcGVyQ2FzZSgpK0IueEIueW4ociwxKSl9cmV0dXJuIEIuTm0u -SChxLCIiKX0sCk9VKGEpe3ZhciBzLHIscSxwLG8KZm9yKHM9YS5sZW5ndGgscj0wLHE9IiI7cjxzOysr -cil7cD1hW3JdCm89cC50b0xvd2VyQ2FzZSgpCnE9KHAhPT1vJiZyPjA/cSsiLSI6cSkrb31yZXR1cm4g -cS5jaGFyQ29kZUF0KDApPT0wP3E6cX19CkEuS1MucHJvdG90eXBlPXsKJDIoYSxiKXtpZihCLnhCLm5D -KGEsImRhdGEtIikpdGhpcy5iLiQyKHRoaXMuYS54cShCLnhCLnluKGEsNSkpLGIpfSwKJFM6MTR9CkEu -QTMucHJvdG90eXBlPXsKJDIoYSxiKXtpZihCLnhCLm5DKGEsImRhdGEtIikpQi5ObS5pKHRoaXMuYix0 -aGlzLmEueHEoQi54Qi55bihhLDUpKSl9LAokUzoxNH0KQS5JNC5wcm90b3R5cGU9ewpEKCl7dmFyIHMs -cixxLHAsbz1BLkxzKHQuTikKZm9yKHM9dGhpcy5hLmNsYXNzTmFtZS5zcGxpdCgiICIpLHI9cy5sZW5n -dGgscT0wO3E8cjsrK3Epe3A9Si5UMChzW3FdKQppZihwLmxlbmd0aCE9PTApby5pKDAscCl9cmV0dXJu -IG99LApYKGEpe3RoaXMuYS5jbGFzc05hbWU9dC5DLmEoYSkuSCgwLCIgIil9LApnayhhKXtyZXR1cm4g -dGhpcy5hLmNsYXNzTGlzdC5sZW5ndGh9LApnbDAoYSl7cmV0dXJuIHRoaXMuYS5jbGFzc0xpc3QubGVu -Z3RoPT09MH0sCmdvcihhKXtyZXR1cm4gdGhpcy5hLmNsYXNzTGlzdC5sZW5ndGghPT0wfSwKVjEoYSl7 -dGhpcy5hLmNsYXNzTmFtZT0iIn0sCnRnKGEsYil7dmFyIHM9dGhpcy5hLmNsYXNzTGlzdC5jb250YWlu -cyhiKQpyZXR1cm4gc30sCmkoYSxiKXt2YXIgcyxyCkEubihiKQpzPXRoaXMuYS5jbGFzc0xpc3QKcj1z -LmNvbnRhaW5zKGIpCnMuYWRkKGIpCnJldHVybiFyfSwKUihhLGIpe3ZhciBzLHIscQppZih0eXBlb2Yg -Yj09InN0cmluZyIpe3M9dGhpcy5hLmNsYXNzTGlzdApyPXMuY29udGFpbnMoYikKcy5yZW1vdmUoYikK -cT1yfWVsc2UgcT0hMQpyZXR1cm4gcX0sCkZWKGEsYil7QS5SNih0aGlzLmEsdC5PLmEoYikpfX0KQS5G -ay5wcm90b3R5cGU9e30KQS5STy5wcm90b3R5cGU9e30KQS5DcS5wcm90b3R5cGU9e30KQS54Qy5wcm90 -b3R5cGU9e30KQS52Ti5wcm90b3R5cGU9ewokMShhKXtyZXR1cm4gdGhpcy5hLiQxKHQuQi5hKGEpKX0s -CiRTOjI4fQpBLkpRLnByb3RvdHlwZT17CkNZKGEpe3ZhciBzCmlmKCQub3IuYT09PTApe2ZvcihzPTA7 -czwyNjI7KytzKSQub3IuWTUoMCxCLmNtW3NdLEEucFMoKSkKZm9yKHM9MDtzPDEyOysrcykkLm9yLlk1 -KDAsQi5CSVtzXSxBLlY0KCkpfX0sCmkwKGEpe3JldHVybiAkLkFOKCkudGcoMCxBLnJTKGEpKX0sCkVi -KGEsYixjKXt2YXIgcz0kLm9yLnEoMCxBLnJTKGEpKyI6OiIrYikKaWYocz09bnVsbClzPSQub3IucSgw -LCIqOjoiK2IpCmlmKHM9PW51bGwpcmV0dXJuITEKcmV0dXJuIEEucDgocy4kNChhLGIsYyx0aGlzKSl9 -LAokaWtGOjF9CkEuR20ucHJvdG90eXBlPXsKZ00oYSl7cmV0dXJuIG5ldyBBLlc5KGEsdGhpcy5nayhh -KSxBLnpLKGEpLkMoIlc5PEdtLkU+IikpfX0KQS52RC5wcm90b3R5cGU9ewppMChhKXtyZXR1cm4gQi5O -bS5Wcih0aGlzLmEsbmV3IEEuVXYoYSkpfSwKRWIoYSxiLGMpe3JldHVybiBCLk5tLlZyKHRoaXMuYSxu -ZXcgQS5FZyhhLGIsYykpfSwKJGlrRjoxfQpBLlV2LnByb3RvdHlwZT17CiQxKGEpe3JldHVybiB0LmY2 -LmEoYSkuaTAodGhpcy5hKX0sCiRTOjE1fQpBLkVnLnByb3RvdHlwZT17CiQxKGEpe3JldHVybiB0LmY2 -LmEoYSkuRWIodGhpcy5hLHRoaXMuYix0aGlzLmMpfSwKJFM6MTV9CkEubTYucHJvdG90eXBlPXsKQ1ko -YSxiLGMsZCl7dmFyIHMscixxCnRoaXMuYS5GVigwLGMpCnM9Yi5ldigwLG5ldyBBLkVvKCkpCnI9Yi5l -digwLG5ldyBBLldrKCkpCnRoaXMuYi5GVigwLHMpCnE9dGhpcy5jCnEuRlYoMCxCLnhEKQpxLkZWKDAs -cil9LAppMChhKXtyZXR1cm4gdGhpcy5hLnRnKDAsQS5yUyhhKSl9LApFYihhLGIsYyl7dmFyIHMscj10 -aGlzLHE9QS5yUyhhKSxwPXIuYyxvPXErIjo6IitiCmlmKHAudGcoMCxvKSlyZXR1cm4gci5kLkR0KGMp -CmVsc2V7cz0iKjo6IitiCmlmKHAudGcoMCxzKSlyZXR1cm4gci5kLkR0KGMpCmVsc2V7cD1yLmIKaWYo -cC50ZygwLG8pKXJldHVybiEwCmVsc2UgaWYocC50ZygwLHMpKXJldHVybiEwCmVsc2UgaWYocC50Zygw -LHErIjo6KiIpKXJldHVybiEwCmVsc2UgaWYocC50ZygwLCIqOjoqIikpcmV0dXJuITB9fXJldHVybiEx -fSwKJGlrRjoxfQpBLkVvLnByb3RvdHlwZT17CiQxKGEpe3JldHVybiFCLk5tLnRnKEIuQkksQS5uKGEp -KX0sCiRTOjR9CkEuV2sucHJvdG90eXBlPXsKJDEoYSl7cmV0dXJuIEIuTm0udGcoQi5CSSxBLm4oYSkp -fSwKJFM6NH0KQS5jdC5wcm90b3R5cGU9ewpFYihhLGIsYyl7aWYodGhpcy5qRihhLGIsYykpcmV0dXJu -ITAKaWYoYj09PSJ0ZW1wbGF0ZSImJmM9PT0iIilyZXR1cm4hMAppZihhLmdldEF0dHJpYnV0ZSgidGVt -cGxhdGUiKT09PSIiKXJldHVybiB0aGlzLmUudGcoMCxiKQpyZXR1cm4hMX19CkEuSUEucHJvdG90eXBl -PXsKJDEoYSl7cmV0dXJuIlRFTVBMQVRFOjoiK0EubihhKX0sCiRTOjJ9CkEuT3cucHJvdG90eXBlPXsK -aTAoYSl7dmFyIHMKaWYodC5hTy5iKGEpKXJldHVybiExCnM9dC5nNy5iKGEpCmlmKHMmJkEuclMoYSk9 -PT0iZm9yZWlnbk9iamVjdCIpcmV0dXJuITEKaWYocylyZXR1cm4hMApyZXR1cm4hMX0sCkViKGEsYixj -KXtpZihiPT09ImlzInx8Qi54Qi5uQyhiLCJvbiIpKXJldHVybiExCnJldHVybiB0aGlzLmkwKGEpfSwK -JGlrRjoxfQpBLlc5LnByb3RvdHlwZT17CkcoKXt2YXIgcz10aGlzLHI9cy5jKzEscT1zLmIKaWYocjxx -KXtzLnNwKEoueDkocy5hLHIpKQpzLmM9cgpyZXR1cm4hMH1zLnNwKG51bGwpCnMuYz1xCnJldHVybiEx -fSwKZ2woKXt2YXIgcz10aGlzLmQKcmV0dXJuIHM9PW51bGw/dGhpcy4kdGkuYy5hKHMpOnN9LApzcChh -KXt0aGlzLmQ9dGhpcy4kdGkuQygiMT8iKS5hKGEpfSwKJGlBbjoxfQpBLmRXLnByb3RvdHlwZT17JGlQ -WjoxLCRpZEE6MX0KQS5tay5wcm90b3R5cGU9eyRpeTA6MX0KQS5Lby5wcm90b3R5cGU9ewpQbihhKXt2 -YXIgcyxyPW5ldyBBLmZtKHRoaXMpCmRve3M9dGhpcy5iCnIuJDIoYSxudWxsKX13aGlsZShzIT09dGhp -cy5iKX0sCkVQKGEsYil7Kyt0aGlzLmIKaWYoYj09bnVsbHx8YiE9PWEucGFyZW50Tm9kZSlKLkx0KGEp -CmVsc2UgYi5yZW1vdmVDaGlsZChhKX0sCkk0KGEsYil7dmFyIHMscixxLHAsbyxuPSEwLG09bnVsbCxs -PW51bGwKdHJ5e209Si5pZyhhKQpsPW0uYS5nZXRBdHRyaWJ1dGUoImlzIikKdC5oLmEoYSkKcz1mdW5j -dGlvbihjKXtpZighKGMuYXR0cmlidXRlcyBpbnN0YW5jZW9mIE5hbWVkTm9kZU1hcCkpcmV0dXJuIHRy -dWUKaWYoYy5pZD09Imxhc3RDaGlsZCJ8fGMubmFtZT09Imxhc3RDaGlsZCJ8fGMuaWQ9PSJwcmV2aW91 -c1NpYmxpbmcifHxjLm5hbWU9PSJwcmV2aW91c1NpYmxpbmcifHxjLmlkPT0iY2hpbGRyZW4ifHxjLm5h -bWU9PSJjaGlsZHJlbiIpcmV0dXJuIHRydWUKdmFyIGs9Yy5jaGlsZE5vZGVzCmlmKGMubGFzdENoaWxk -JiZjLmxhc3RDaGlsZCE9PWtbay5sZW5ndGgtMV0pcmV0dXJuIHRydWUKaWYoYy5jaGlsZHJlbilpZigh -KGMuY2hpbGRyZW4gaW5zdGFuY2VvZiBIVE1MQ29sbGVjdGlvbnx8Yy5jaGlsZHJlbiBpbnN0YW5jZW9m -IE5vZGVMaXN0KSlyZXR1cm4gdHJ1ZQp2YXIgaj0wCmlmKGMuY2hpbGRyZW4paj1jLmNoaWxkcmVuLmxl -bmd0aApmb3IodmFyIGk9MDtpPGo7aSsrKXt2YXIgaD1jLmNoaWxkcmVuW2ldCmlmKGguaWQ9PSJhdHRy -aWJ1dGVzInx8aC5uYW1lPT0iYXR0cmlidXRlcyJ8fGguaWQ9PSJsYXN0Q2hpbGQifHxoLm5hbWU9PSJs -YXN0Q2hpbGQifHxoLmlkPT0icHJldmlvdXNTaWJsaW5nInx8aC5uYW1lPT0icHJldmlvdXNTaWJsaW5n -Inx8aC5pZD09ImNoaWxkcmVuInx8aC5uYW1lPT0iY2hpbGRyZW4iKXJldHVybiB0cnVlfXJldHVybiBm -YWxzZX0oYSkKbj1BLm9UKHMpPyEwOiEoYS5hdHRyaWJ1dGVzIGluc3RhbmNlb2YgTmFtZWROb2RlTWFw -KX1jYXRjaChwKXt9cj0iZWxlbWVudCB1bnByaW50YWJsZSIKdHJ5e3I9Si5ZUyhhKX1jYXRjaChwKXt9 -dHJ5e3E9QS5yUyhhKQp0aGlzLmtSKGEsYixuLHIscSx0LmYuYShtKSxBLmsobCkpfWNhdGNoKHApe2lm -KEEuUnUocCkgaW5zdGFuY2VvZiBBLnUpdGhyb3cgcAplbHNle3RoaXMuRVAoYSxiKQp3aW5kb3cKbz1B -LmQocikKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25zb2xlLndhcm4oIlJl -bW92aW5nIGNvcnJ1cHRlZCBlbGVtZW50ICIrbyl9fX0sCmtSKGEsYixjLGQsZSxmLGcpe3ZhciBzLHIs -cSxwLG8sbixtLGw9dGhpcwppZihjKXtsLkVQKGEsYikKd2luZG93CmlmKHR5cGVvZiBjb25zb2xlIT0i -dW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJuKCJSZW1vdmluZyBlbGVtZW50IGR1ZSB0byBjb3Jy -dXB0ZWQgYXR0cmlidXRlcyBvbiA8IitkKyI+IikKcmV0dXJufWlmKCFsLmEuaTAoYSkpe2wuRVAoYSxi -KQp3aW5kb3cKcz1BLmQoYikKaWYodHlwZW9mIGNvbnNvbGUhPSJ1bmRlZmluZWQiKXdpbmRvdy5jb25z -b2xlLndhcm4oIlJlbW92aW5nIGRpc2FsbG93ZWQgZWxlbWVudCA8IitlKyI+IGZyb20gIitzKQpyZXR1 -cm59aWYoZyE9bnVsbClpZighbC5hLkViKGEsImlzIixnKSl7bC5FUChhLGIpCndpbmRvdwppZih0eXBl -b2YgY29uc29sZSE9InVuZGVmaW5lZCIpd2luZG93LmNvbnNvbGUud2FybigiUmVtb3ZpbmcgZGlzYWxs -b3dlZCB0eXBlIGV4dGVuc2lvbiA8IitlKycgaXM9IicrZysnIj4nKQpyZXR1cm59cz1mLmd2YygpCnI9 -QS5RSShzLnNsaWNlKDApLEEudDYocykpCmZvcihxPWYuZ3ZjKCkubGVuZ3RoLTEscz1mLmEscD0iUmVt -b3ZpbmcgZGlzYWxsb3dlZCBhdHRyaWJ1dGUgPCIrZSsiICI7cT49MDstLXEpe2lmKCEocTxyLmxlbmd0 -aCkpcmV0dXJuIEEuT0gocixxKQpvPXJbcV0Kbj1sLmEKbT1KLmNIKG8pCkEubihvKQppZighbi5FYihh -LG0sQS5uKHMuZ2V0QXR0cmlidXRlKG8pKSkpe3dpbmRvdwpuPXMuZ2V0QXR0cmlidXRlKG8pCmlmKHR5 -cGVvZiBjb25zb2xlIT0idW5kZWZpbmVkIil3aW5kb3cuY29uc29sZS53YXJuKHArbysnPSInK0EuZChu -KSsnIj4nKQpzLnJlbW92ZUF0dHJpYnV0ZShvKX19aWYodC5hVy5iKGEpKXtzPWEuY29udGVudApzLnRv -U3RyaW5nCmwuUG4ocyl9fSwKJGlvbjoxfQpBLmZtLnByb3RvdHlwZT17CiQyKGEsYil7dmFyIHMscixx -LHAsbyxuLG09dGhpcy5hCnN3aXRjaChhLm5vZGVUeXBlKXtjYXNlIDE6bS5JNChhLGIpCmJyZWFrCmNh -c2UgODpjYXNlIDExOmNhc2UgMzpjYXNlIDQ6YnJlYWsKZGVmYXVsdDptLkVQKGEsYil9cz1hLmxhc3RD -aGlsZApmb3IocT10LkE7cyE9bnVsbDspe3I9bnVsbAp0cnl7cj1zLnByZXZpb3VzU2libGluZwppZihy -IT1udWxsKXtwPXIubmV4dFNpYmxpbmcKbz1zCm89cD09bnVsbD9vIT1udWxsOnAhPT1vCnA9b31lbHNl -IHA9ITEKaWYocCl7cD1BLlBWKCJDb3JydXB0IEhUTUwiKQp0aHJvdyBBLmIocCl9fWNhdGNoKG4pe3A9 -cS5hKHMpOysrbS5iCm89cC5wYXJlbnROb2RlCmlmKGEhPT1vKXtpZihvIT1udWxsKW8ucmVtb3ZlQ2hp -bGQocCl9ZWxzZSBhLnJlbW92ZUNoaWxkKHApCnM9bnVsbApyPWEubGFzdENoaWxkfWlmKHMhPW51bGwp -dGhpcy4kMihzLGEpCnM9cn19LAokUzozMX0KQS5ZOC5wcm90b3R5cGU9e30KQS5ucS5wcm90b3R5cGU9 -e30KQS5Bci5wcm90b3R5cGU9e30KQS50RC5wcm90b3R5cGU9e30KQS51Zi5wcm90b3R5cGU9e30KQS5p -Si5wcm90b3R5cGU9ewpWSChhKXt2YXIgcyxyPXRoaXMuYSxxPXIubGVuZ3RoCmZvcihzPTA7czxxOysr -cylpZihyW3NdPT09YSlyZXR1cm4gcwpCLk5tLmkocixhKQpCLk5tLmkodGhpcy5iLG51bGwpCnJldHVy -biBxfSwKUHYoYSl7dmFyIHMscixxLHA9dGhpcyxvPXt9CmlmKGE9PW51bGwpcmV0dXJuIGEKaWYoQS5y -UShhKSlyZXR1cm4gYQppZih0eXBlb2YgYT09Im51bWJlciIpcmV0dXJuIGEKaWYodHlwZW9mIGE9PSJz -dHJpbmciKXJldHVybiBhCmlmKGEgaW5zdGFuY2VvZiBBLmlQKXJldHVybiBuZXcgRGF0ZShhLmEpCmlm -KHQuZnYuYihhKSl0aHJvdyBBLmIoQS5TWSgic3RydWN0dXJlZCBjbG9uZSBvZiBSZWdFeHAiKSkKaWYo -dC5jOC5iKGEpKXJldHVybiBhCmlmKHQudy5iKGEpKXJldHVybiBhCmlmKHQueC5iKGEpKXJldHVybiBh -CmlmKCF0LmRFLmIoYSkpcz0hMQplbHNlIHM9ITAKaWYocylyZXR1cm4gYQppZih0LmYuYihhKSl7cj1w -LlZIKGEpCnM9cC5iCmlmKCEocjxzLmxlbmd0aCkpcmV0dXJuIEEuT0gocyxyKQpxPW8uYT1zW3JdCmlm -KHEhPW51bGwpcmV0dXJuIHEKcT17fQpvLmE9cQpCLk5tLlk1KHMscixxKQphLksoMCxuZXcgQS5FMihv -LHApKQpyZXR1cm4gby5hfWlmKHQuai5iKGEpKXtyPXAuVkgoYSkKbz1wLmIKaWYoIShyPG8ubGVuZ3Ro -KSlyZXR1cm4gQS5PSChvLHIpCnE9b1tyXQppZihxIT1udWxsKXJldHVybiBxCnJldHVybiBwLmVrKGEs -cil9aWYodC5lSC5iKGEpKXtyPXAuVkgoYSkKcz1wLmIKaWYoIShyPHMubGVuZ3RoKSlyZXR1cm4gQS5P -SChzLHIpCnE9by5iPXNbcl0KaWYocSE9bnVsbClyZXR1cm4gcQpxPXt9Cm8uYj1xCkIuTm0uWTUocyxy -LHEpCnAuaW0oYSxuZXcgQS5qZyhvLHApKQpyZXR1cm4gby5ifXRocm93IEEuYihBLlNZKCJzdHJ1Y3R1 -cmVkIGNsb25lIG9mIG90aGVyIHR5cGUiKSl9LAplayhhLGIpe3ZhciBzLHI9Si5VNihhKSxxPXIuZ2so -YSkscD1uZXcgQXJyYXkocSkKQi5ObS5ZNSh0aGlzLmIsYixwKQpmb3Iocz0wO3M8cTsrK3MpQi5ObS5Z -NShwLHMsdGhpcy5QdihyLnEoYSxzKSkpCnJldHVybiBwfX0KQS5FMi5wcm90b3R5cGU9ewokMihhLGIp -e3RoaXMuYS5hW2FdPXRoaXMuYi5QdihiKX0sCiRTOjMyfQpBLmpnLnByb3RvdHlwZT17CiQyKGEsYil7 -dGhpcy5hLmJbYV09dGhpcy5iLlB2KGIpfSwKJFM6MzN9CkEuQmYucHJvdG90eXBlPXsKaW0oYSxiKXt2 -YXIgcyxyLHEscAp0LmI4LmEoYikKZm9yKHM9T2JqZWN0LmtleXMoYSkscj1zLmxlbmd0aCxxPTA7cTxy -OysrcSl7cD1zW3FdCmIuJDIocCxhW3BdKX19fQpBLkFzLnByb3RvdHlwZT17ClYoYSl7dmFyIHMKQS5u -KGEpCnM9JC5oRygpLmIKaWYocy50ZXN0KGEpKXJldHVybiBhCnRocm93IEEuYihBLkwzKGEsInZhbHVl -IiwiTm90IGEgdmFsaWQgY2xhc3MgdG9rZW4iKSl9LAoiWyIoYSl7cmV0dXJuIHRoaXMuRCgpLkgoMCwi -ICIpfSwKZ00oYSl7dmFyIHM9dGhpcy5EKCkKcmV0dXJuIEEucmoocyxzLnIsQS5MaChzKS5jKX0sCmds -MChhKXtyZXR1cm4gdGhpcy5EKCkuYT09PTB9LApnb3IoYSl7cmV0dXJuIHRoaXMuRCgpLmEhPT0wfSwK -Z2soYSl7cmV0dXJuIHRoaXMuRCgpLmF9LAp0ZyhhLGIpe3RoaXMuVihiKQpyZXR1cm4gdGhpcy5EKCku -dGcoMCxiKX0sCmkoYSxiKXt2YXIgcwpBLm4oYikKdGhpcy5WKGIpCnM9dGhpcy5PUyhuZXcgQS5HRShi -KSkKcmV0dXJuIEEucDgocz09bnVsbD8hMTpzKX0sClIoYSxiKXt2YXIgcyxyCmlmKHR5cGVvZiBiIT0i -c3RyaW5nIilyZXR1cm4hMQp0aGlzLlYoYikKcz10aGlzLkQoKQpyPXMuUigwLGIpCnRoaXMuWChzKQpy -ZXR1cm4gcn0sCkZWKGEsYil7dGhpcy5PUyhuZXcgQS5ONyh0aGlzLHQuTy5hKGIpKSl9LAplUihhLGIp -e3ZhciBzPXRoaXMuRCgpCnJldHVybiBBLmJLKHMsYixBLkxoKHMpLkMoImxmLkUiKSl9LApBKGEsYil7 -cmV0dXJuIHRoaXMuRCgpLkEoMCxiKX0sClYxKGEpe3RoaXMuT1MobmV3IEEudVEoKSl9LApPUyhhKXt2 -YXIgcyxyCnQuYlUuYShhKQpzPXRoaXMuRCgpCnI9YS4kMShzKQp0aGlzLlgocykKcmV0dXJuIHJ9fQpB -LkdFLnByb3RvdHlwZT17CiQxKGEpe3JldHVybiB0LkMuYShhKS5pKDAsdGhpcy5hKX0sCiRTOjM0fQpB -Lk43LnByb3RvdHlwZT17CiQxKGEpe3ZhciBzPXRoaXMuYixyPUEudDYocykKcmV0dXJuIHQuQy5hKGEp -LkZWKDAsbmV3IEEubEoocyxyLkMoInFVKDEpIikuYSh0aGlzLmEuZ3VNKCkpLHIuQygibEo8MSxxVT4i -KSkpfSwKJFM6MTZ9CkEudVEucHJvdG90eXBlPXsKJDEoYSl7dC5DLmEoYSkKaWYoYS5hPjApe2EuYj1h -LmM9YS5kPWEuZT1hLmY9bnVsbAphLmE9MAphLlMoKX1yZXR1cm4gbnVsbH0sCiRTOjE2fQpBLmhGLnBy -b3RvdHlwZT17JGloRjoxfQpBLkRWLnByb3RvdHlwZT17CiQxKGEpe3ZhciBzCnQuWS5hKGEpCnM9ZnVu -Y3Rpb24oYixjLGQpe3JldHVybiBmdW5jdGlvbigpe3JldHVybiBiKGMsZCx0aGlzLEFycmF5LnByb3Rv -dHlwZS5zbGljZS5hcHBseShhcmd1bWVudHMpKX19KEEuUjQsYSwhMSkKQS5EbShzLCQudygpLGEpCnJl -dHVybiBzfSwKJFM6M30KQS5QQy5wcm90b3R5cGU9ewokMShhKXtyZXR1cm4gbmV3IHRoaXMuYShhKX0s -CiRTOjN9CkEuUVMucHJvdG90eXBlPXsKJDEoYSl7cmV0dXJuIG5ldyBBLnI3KGE9PW51bGw/dC5LLmEo -YSk6YSl9LAokUzozNn0KQS5ucC5wcm90b3R5cGU9ewokMShhKXt2YXIgcz1hPT1udWxsP3QuSy5hKGEp -OmEKcmV0dXJuIG5ldyBBLlR6KHMsdC5hbSl9LAokUzo0OH0KQS5VdC5wcm90b3R5cGU9ewokMShhKXty -ZXR1cm4gbmV3IEEuRTQoYT09bnVsbD90LksuYShhKTphKX0sCiRTOjM4fQpBLkU0LnByb3RvdHlwZT17 -CnEoYSxiKXtpZih0eXBlb2YgYiE9InN0cmluZyImJnR5cGVvZiBiIT0ibnVtYmVyIil0aHJvdyBBLmIo -QS54WSgicHJvcGVydHkgaXMgbm90IGEgU3RyaW5nIG9yIG51bSIsbnVsbCkpCnJldHVybiBBLmRVKHRo -aXMuYVtiXSl9LApZNShhLGIsYyl7aWYodHlwZW9mIGIhPSJzdHJpbmciJiZ0eXBlb2YgYiE9Im51bWJl -ciIpdGhyb3cgQS5iKEEueFkoInByb3BlcnR5IGlzIG5vdCBhIFN0cmluZyBvciBudW0iLG51bGwpKQp0 -aGlzLmFbYl09QS53WShjKX0sCkROKGEsYil7aWYoYj09bnVsbClyZXR1cm4hMQpyZXR1cm4gYiBpbnN0 -YW5jZW9mIEEuRTQmJnRoaXMuYT09PWIuYX0sCiJbIihhKXt2YXIgcyxyCnRyeXtzPVN0cmluZyh0aGlz -LmEpCnJldHVybiBzfWNhdGNoKHIpe3M9dGhpcy54YigwKQpyZXR1cm4gc319LApWNyhhLGIpe3ZhciBz -LHI9dGhpcy5hCmlmKGI9PW51bGwpcz1udWxsCmVsc2V7cz1BLnQ2KGIpCnM9QS5QVyhuZXcgQS5sSihi -LHMuQygiQCgxKSIpLmEoQS5pRygpKSxzLkMoImxKPDEsQD4iKSksITAsdC56KX1yZXR1cm4gQS5kVShy -W2FdLmFwcGx5KHIscykpfSwKZ20oYSl7cmV0dXJuIDB9fQpBLnI3LnByb3RvdHlwZT17fQpBLlR6LnBy -b3RvdHlwZT17CmNQKGEpe3ZhciBzPXRoaXMscj1hPDB8fGE+PXMuZ2socykKaWYocil0aHJvdyBBLmIo -QS5URShhLDAscy5nayhzKSxudWxsLG51bGwpKX0sCnEoYSxiKXtpZihBLm9rKGIpKXRoaXMuY1AoYikK -cmV0dXJuIHRoaXMuJHRpLmMuYSh0aGlzLlVyKDAsYikpfSwKWTUoYSxiLGMpe3RoaXMuY1AoYikKdGhp -cy5iaCgwLGIsYyl9LApnayhhKXt2YXIgcz10aGlzLmEubGVuZ3RoCmlmKHR5cGVvZiBzPT09Im51bWJl -ciImJnM+Pj4wPT09cylyZXR1cm4gcwp0aHJvdyBBLmIoQS5QVigiQmFkIEpzQXJyYXkgbGVuZ3RoIikp -fSwKJGliUToxLAokaWNYOjEsCiRpek06MX0KQS52Zy5wcm90b3R5cGU9ewpZNShhLGIsYyl7cmV0dXJu -IHRoaXMuZTQoMCxiLGMpfX0KQS5uZC5wcm90b3R5cGU9eyRpbmQ6MX0KQS5LZS5wcm90b3R5cGU9ewpE -KCl7dmFyIHMscixxLHAsbz10aGlzLmEuZ2V0QXR0cmlidXRlKCJjbGFzcyIpLG49QS5Mcyh0Lk4pCmlm -KG89PW51bGwpcmV0dXJuIG4KZm9yKHM9by5zcGxpdCgiICIpLHI9cy5sZW5ndGgscT0wO3E8cjsrK3Ep -e3A9Si5UMChzW3FdKQppZihwLmxlbmd0aCE9PTApbi5pKDAscCl9cmV0dXJuIG59LApYKGEpe3RoaXMu -YS5zZXRBdHRyaWJ1dGUoImNsYXNzIixhLkgoMCwiICIpKX19CkEuaGkucHJvdG90eXBlPXsKZ1AoYSl7 -cmV0dXJuIG5ldyBBLktlKGEpfSwKcjYoYSxiLGMsZCl7dmFyIHMscixxLHAsbwppZihkPT1udWxsKXtz -PUEuUUkoW10sdC5EKQpkPW5ldyBBLnZEKHMpCkIuTm0uaShzLEEuVHcobnVsbCkpCkIuTm0uaShzLEEu -QmwoKSkKQi5ObS5pKHMsbmV3IEEuT3coKSl9Yz1uZXcgQS5LbyhkKQpzPWRvY3VtZW50CnI9cy5ib2R5 -CnIudG9TdHJpbmcKcT1CLlJZLkFIKHIsJzxzdmcgdmVyc2lvbj0iMS4xIj4nK0EuZChiKSsiPC9zdmc+ -IixjKQpwPXMuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpCnM9bmV3IEEuZTcocSkKbz1zLmdyOChzKQpm -b3IoO3M9by5maXJzdENoaWxkLHMhPW51bGw7KXAuYXBwZW5kQ2hpbGQocykKcmV0dXJuIHB9LApueihh -LGIsYyxkLGUpe3Rocm93IEEuYihBLkw0KCJDYW5ub3QgaW52b2tlIGluc2VydEFkamFjZW50SHRtbCBv -biBTVkcuIikpfSwKZ1ZsKGEpe3JldHVybiBuZXcgQS5DcShhLCJjbGljayIsITEsdC5RKX0sCiRpaGk6 -MX0KQS5kMi5wcm90b3R5cGU9ewpMdCgpe3ZhciBzLHIscSxwLG89dGhpcyxuPXQuTixtPXQuWCxsPUEu -RmwobixtKSxrPW8uYQppZihrIT1udWxsKXtzPUEuUUkoW10sdC5kKQpmb3Iocj1rLmxlbmd0aCxxPTA7 -cTxrLmxlbmd0aDtrLmxlbmd0aD09PXJ8fCgwLEEubGspKGspLCsrcSl7cD1rW3FdCnMucHVzaChBLkVG -KFsiZGVzY3JpcHRpb24iLHAuYSwiaHJlZiIscC5iXSxuLG0pKX1sLlk1KDAsImVkaXRzIixzKX1sLlk1 -KDAsImV4cGxhbmF0aW9uIixvLmIpCmwuWTUoMCwibGluZSIsby5jKQpsLlk1KDAsImRpc3BsYXlQYXRo -IixvLmQpCmwuWTUoMCwidXJpUGF0aCIsby5lKQpuPW8uZgppZihuIT1udWxsKXttPUEuUUkoW10sdC5k -KQpmb3Ioaz1uLmxlbmd0aCxxPTA7cTxuLmxlbmd0aDtuLmxlbmd0aD09PWt8fCgwLEEubGspKG4pLCsr -cSltLnB1c2gobltxXS5MdCgpKQpsLlk1KDAsInRyYWNlcyIsbSl9cmV0dXJuIGx9fQpBLlNlLnByb3Rv -dHlwZT17Ckx0KCl7cmV0dXJuIEEuRUYoWyJkZXNjcmlwdGlvbiIsdGhpcy5hLCJocmVmIix0aGlzLmJd -LHQuTix0LlgpfX0KQS5NbC5wcm90b3R5cGU9ewpMdCgpe3JldHVybiBBLkVGKFsiaHJlZiIsdGhpcy5h -LCJsaW5lIix0aGlzLmIsInBhdGgiLHRoaXMuY10sdC5OLHQuWCl9fQpBLnlELnByb3RvdHlwZT17Ckx0 -KCl7dmFyIHMscixxLHA9QS5RSShbXSx0LmQpCmZvcihzPXRoaXMuYixyPXMubGVuZ3RoLHE9MDtxPHMu -bGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAsQS5saykocyksKytxKXAucHVzaChzW3FdLkx0KCkpCnJldHVy -biBBLkVGKFsiZGVzY3JpcHRpb24iLHRoaXMuYSwiZW50cmllcyIscF0sdC5OLHQuWCl9fQpBLndiLnBy -b3RvdHlwZT17Ckx0KCl7dmFyIHMscixxLHA9dGhpcyxvPUEuRmwodC5OLHQuWCkKby5ZNSgwLCJkZXNj -cmlwdGlvbiIscC5hKQpzPXAuYgppZihzIT1udWxsKW8uWTUoMCwiZnVuY3Rpb24iLHMpCnM9cC5jCmlm -KHMhPW51bGwpby5ZNSgwLCJsaW5rIixzLkx0KCkpCnM9cC5kCmlmKHMubGVuZ3RoIT09MCl7cj1BLnQ2 -KHMpCnE9ci5DKCJsSjwxLFowPHFVLE1oPz4+IikKby5ZNSgwLCJoaW50QWN0aW9ucyIsQS5ZMShuZXcg -QS5sSihzLHIuQygiWjA8cVUsTWg/PigxKSIpLmEobmV3IEEuYjAoKSkscSksITAscS5DKCJhTC5FIikp -KX1yZXR1cm4gb319CkEuYU4ucHJvdG90eXBlPXsKJDEoYSl7cmV0dXJuIEEubnoodC5HLmEoYSkpfSwK -JFM6Mzl9CkEuYjAucHJvdG90eXBlPXsKJDEoYSl7cmV0dXJuIHQuSi5hKGEpLkx0KCl9LAokUzo0MH0K -QS5qOC5wcm90b3R5cGU9ewpMdCgpe3JldHVybiBBLkVGKFsibGluZSIsdGhpcy5hLCJleHBsYW5hdGlv -biIsdGhpcy5iLCJvZmZzZXQiLHRoaXMuY10sdC5OLHQuWCl9fQpBLnFwLnByb3RvdHlwZT17Ckx0KCl7 -dmFyIHMscixxLHAsbyxuLG0sbD10aGlzLGs9dC5OLGo9QS5GbChrLHQuYVMpCmZvcihzPWwuZCxzPXMu -Z1B1KHMpLHM9cy5nTShzKSxyPXQuWCxxPXQuZDtzLkcoKTspe3A9cy5nbCgpCm89cC5hCm49QS5RSShb -XSxxKQpmb3IocD1KLklUKHAuYik7cC5HKCk7KXttPXAuZ2woKQpuLnB1c2goQS5FRihbImxpbmUiLG0u -YSwiZXhwbGFuYXRpb24iLG0uYiwib2Zmc2V0IixtLmNdLGsscikpfWouWTUoMCxvLG4pfXJldHVybiBB -LkVGKFsicmVnaW9ucyIsbC5hLCJuYXZpZ2F0aW9uQ29udGVudCIsbC5iLCJzb3VyY2VDb2RlIixsLmMs -ImVkaXRzIixqXSxrLHIpfX0KQS5tUS5wcm90b3R5cGU9e30KQS5lLnByb3RvdHlwZT17CiQxKGEpe3Zh -ciBzLHIscSxwLG8sbixtCnQuQi5hKGEpCnM9dC5GCnI9cy5hKHdpbmRvdy5sb2NhdGlvbikucGF0aG5h -bWUKcT1BLkc2KHMuYSh3aW5kb3cubG9jYXRpb24pLmhyZWYpCnA9QS5hSyhzLmEod2luZG93LmxvY2F0 -aW9uKS5ocmVmKQpBLkdlKCkKaWYociE9PSIvIil7cz1kb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIucm9v -dCIpLnRleHRDb250ZW50CnMudG9TdHJpbmcKcz1yIT09Qi54Qi5iUyhzKX1lbHNlIHM9ITEKaWYocyl7 -ci50b1N0cmluZwpBLkc3KHIscSxwLCEwLG5ldyBBLlZXKHIscSxwKSl9cz1kb2N1bWVudApvPXMucXVl -cnlTZWxlY3RvcigiLmFwcGx5LW1pZ3JhdGlvbiIpCm8udG9TdHJpbmcKbz1KLnFGKG8pCm49by4kdGkK -bT1uLkMoIn4oMSk/IikuYShuZXcgQS5vWigpKQp0LlouYShudWxsKQpBLkpFKG8uYSxvLmIsbSwhMSxu -LmMpCm49cy5xdWVyeVNlbGVjdG9yKCIucmVydW4tbWlncmF0aW9uIikKbi50b1N0cmluZwpuPUoucUYo -bikKbT1uLiR0aQpBLkpFKG4uYSxuLmIsbS5DKCJ+KDEpPyIpLmEobmV3IEEueTgoKSksITEsbS5jKQpt -PXMucXVlcnlTZWxlY3RvcigiLnJlcG9ydC1wcm9ibGVtIikKbS50b1N0cmluZwptPUoucUYobSkKbj1t -LiR0aQpBLkpFKG0uYSxtLmIsbi5DKCJ+KDEpPyIpLmEobmV3IEEuSGkoKSksITEsbi5jKQpzPXMucXVl -cnlTZWxlY3RvcigiLnBvcHVwLXBhbmUgLmNsb3NlIikKcy50b1N0cmluZwpzPUoucUYocykKbj1zLiR0 -aQpBLkpFKHMuYSxzLmIsbi5DKCJ+KDEpPyIpLmEobmV3IEEuQlQoKSksITEsbi5jKQpuPSQuYzAoKQpu -LnRvU3RyaW5nCm49Si5xRihuKQpzPW4uJHRpCkEuSkUobi5hLG4uYixzLkMoIn4oMSk/IikuYShuZXcg -QS5QWSgpKSwhMSxzLmMpfSwKJFM6MTd9CkEuVlcucHJvdG90eXBlPXsKJDAoKXtBLkZyKHRoaXMuYSx0 -aGlzLmIsdGhpcy5jKX0sCiRTOjB9CkEub1oucHJvdG90eXBlPXsKJDEoYSl7dmFyIHMscixxLHAsbwp0 -LlYuYShhKQppZihCLm9sLnVzKHdpbmRvdywiVGhpcyB3aWxsIGFwcGx5IHRoZSBjaGFuZ2VzIHlvdSd2 -ZSBwcmV2aWV3ZWQgdG8geW91ciB3b3JraW5nIGRpcmVjdG9yeS4gSXQgaXMgcmVjb21tZW5kZWQgeW91 -IGNvbW1pdCBhbnkgY2hhbmdlcyB5b3UgbWFkZSBiZWZvcmUgZG9pbmcgdGhpcy4iKSl7cz1BLlFJKFtd -LHQuZCkKZm9yKHI9JC5JUixxPXIubGVuZ3RoLHA9MDtwPHIubGVuZ3RoO3IubGVuZ3RoPT09cXx8KDAs -QS5saykociksKytwKXMucHVzaChyW3BdLkx0KCkpCnM9QS50eSgiL2FwcGx5LW1pZ3JhdGlvbiIsQS5F -RihbIm5hdmlnYXRpb25UcmVlIixzXSx0Lk4sdC5hUykpLlc3KG5ldyBBLmpyKCksdC5QKQpvPW5ldyBB -LnFsKCkKdC5iNy5hKG51bGwpCnI9cy4kdGkKcT0kLlgzCmlmKHEhPT1CLk5VKW89QS5WSChvLHEpCnMu -eGYobmV3IEEuRmUobmV3IEEudnMocSxyKSwyLG51bGwsbyxyLkMoIkA8MT4iKS5LcShyLmMpLkMoIkZl -PDEsMj4iKSkpfX0sCiRTOjF9CkEuanIucHJvdG90eXBlPXsKJDEoYSl7dmFyIHMKdC5mbi5hKGEpCnM9 -ZG9jdW1lbnQuYm9keQpzLmNsYXNzTGlzdC5yZW1vdmUoInByb3Bvc2VkIikKcy5jbGFzc0xpc3QuYWRk -KCJhcHBsaWVkIil9LAokUzo0M30KQS5xbC5wcm90b3R5cGU9ewokMihhLGIpe0EuQzIoIkNvdWxkbid0 -IGFwcGx5IG1pZ3JhdGlvbiIsdC5LLmEoYSksYil9LAokUzo0NH0KQS55OC5wcm90b3R5cGU9ewokMShh -KXtyZXR1cm4gdGhpcy54bih0LlYuYShhKSl9LAp4bihhKXt2YXIgcz0wLHI9QS5GWCh0LkgpLHE9MSxw -LG89W10sbixtLGwsayxqLGkKdmFyICRhc3luYyQkMT1BLmx6KGZ1bmN0aW9uKGIsYyl7aWYoYj09PTEp -e3A9YwpzPXF9d2hpbGUodHJ1ZSlzd2l0Y2gocyl7Y2FzZSAwOnE9Mwpkb2N1bWVudC5ib2R5LmNsYXNz -TGlzdC5hZGQoInJlcnVubmluZyIpCnM9NgpyZXR1cm4gQS5qUShBLnR5KCIvcmVydW4tbWlncmF0aW9u -IixudWxsKSwkYXN5bmMkJDEpCmNhc2UgNjpuPWMKaz1uCmsudG9TdHJpbmcKaWYoQS5wOChKLng5KGss -InN1Y2Nlc3MiKSkpdC5GLmEod2luZG93LmxvY2F0aW9uKS5yZWxvYWQoKQplbHNlIEEuSzAodC5ldy5h -KEoueDkobiwiZXJyb3JzIikpKQpvLnB1c2goNSkKcz00CmJyZWFrCmNhc2UgMzpxPTIKaT1wCm09QS5S -dShpKQpsPUEudHMoaSkKQS5DMigiRmFpbGVkIHRvIHJlcnVuIG1pZ3JhdGlvbiIsbSxsKQpvLnB1c2go -NSkKcz00CmJyZWFrCmNhc2UgMjpvPVsxXQpjYXNlIDQ6cT0xCmRvY3VtZW50LmJvZHkuY2xhc3NMaXN0 -LnJlbW92ZSgicmVydW5uaW5nIikKcz1vLnBvcCgpCmJyZWFrCmNhc2UgNTpyZXR1cm4gQS55QyhudWxs -LHIpCmNhc2UgMTpyZXR1cm4gQS5mMyhwLHIpfX0pCnJldHVybiBBLkRJKCRhc3luYyQkMSxyKX0sCiRT -OjE4fQpBLkhpLnByb3RvdHlwZT17CiQxKGEpe3QuVi5hKGEpCkIub2wuUG8od2luZG93LEEuWGQoImh0 -dHBzIiwiZ2l0aHViLmNvbSIsImRhcnQtbGFuZy9zZGsvaXNzdWVzL25ldyIsQS5FRihbInRpdGxlIiwi -Q3VzdG9tZXItcmVwb3J0ZWQgaXNzdWUgd2l0aCBudWxsIHNhZmV0eSBtaWdyYXRpb24gdG9vbCIsImxh -YmVscyIsdS5kLCJib2R5IiwiIyMjIyBTdGVwcyB0byByZXByb2R1Y2VcblxuIyMjIyBXaGF0IGRpZCB5 -b3UgZXhwZWN0IHRvIGhhcHBlbj9cblxuIyMjIyBXaGF0IGFjdHVhbGx5IGhhcHBlbmVkP1xuXG5fU2Ny -ZWVuc2hvdHMgYXJlIGFwcHJlY2lhdGVkX1xuXG4qKkRhcnQgU0RLIHZlcnNpb24qKjogIitBLmQoZG9j -dW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNkay12ZXJzaW9uIikudGV4dENvbnRlbnQpKyJcblxuVGhhbmtz -IGZvciBmaWxpbmchXG4iXSx0Lk4sdC56KSkuZ25EKCksInJlcG9ydC1wcm9ibGVtIil9LAokUzoxfQpB -LkJULnByb3RvdHlwZT17CiQxKGEpe3ZhciBzCnQuVi5hKGEpCnM9ZG9jdW1lbnQucXVlcnlTZWxlY3Rv -cigiLnBvcHVwLXBhbmUiKS5zdHlsZQpzLmRpc3BsYXk9Im5vbmUiCnJldHVybiJub25lIn0sCiRTOjF9 -CkEuUFkucHJvdG90eXBlPXsKJDEoYSl7dmFyIHMscixxLHAsbwp0LlYuYShhKQpzPSQuRDkoKS5pbm5l -clRleHQKcj10LmguYShkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcubmF2LXBhbmVsIFtkYXRhLW5hbWUq -PSInK0EuTGoocykrJyJdJykucGFyZW50Tm9kZSkKcT1yLnF1ZXJ5U2VsZWN0b3IoIi5zdGF0dXMtaWNv -biIpCnA9JC5JUgpwLnRvU3RyaW5nCm89QS55dyhwLHMpCmlmKG8gaW5zdGFuY2VvZiBBLmNEKXtwPW8u -dwpwLnRvU3RyaW5nfWVsc2UgcD0hMQppZihwKXtBLk90KG8pCkEueG4ocSxvKQpBLkFSKHIsbyl9fSwK -JFM6MX0KQS5MLnByb3RvdHlwZT17CiQxKGEpe3ZhciBzLHIscSxwCnQuQi5hKGEpCnM9dC5GCnI9cy5h -KHdpbmRvdy5sb2NhdGlvbikucGF0aG5hbWUKci50b1N0cmluZwpxPUEuRzYocy5hKHdpbmRvdy5sb2Nh -dGlvbikuaHJlZikKcD1BLmFLKHMuYSh3aW5kb3cubG9jYXRpb24pLmhyZWYpCmlmKHIubGVuZ3RoPjEp -QS5HNyhyLHEscCwhMSxudWxsKQplbHNle0EuQkUocixBLndSKCksITApCkEuQlgoIiZuYnNwOyIsbnVs -bCl9fSwKJFM6MTd9CkEuTFoucHJvdG90eXBlPXsKJDAoKXt2YXIgcz0iY29sbGFwc2VkIgpKLmRSKHRo -aXMuYSkuaSgwLHMpCkouZFIodGhpcy5iKS5pKDAscyl9LAokUzowfQpBLnAxLnByb3RvdHlwZT17CiQw -KCl7dmFyIHM9ImNvbGxhcHNlZCIKSi5kUih0aGlzLmEpLlIoMCxzKQpKLmRSKHRoaXMuYikuUigwLHMp -fSwKJFM6MH0KQS5XeC5wcm90b3R5cGU9ewokMShhKXt0LlYuYShhKQppZighSi5kUih0aGlzLmEpLnRn -KDAsImNvbGxhcHNlZCIpKXRoaXMuYi4kMCgpCmVsc2UgdGhpcy5jLiQwKCl9LAokUzoxfQpBLkhvLnBy -b3RvdHlwZT17CiQxKGEpe3JldHVybiBBLnQyKHQuVi5hKGEpLHRoaXMuYSl9LAokUzoxfQpBLklDLnBy -b3RvdHlwZT17CiQxKGEpe3ZhciBzLHIscSxwCnQuVi5hKGEpCnM9dGhpcy5hCnI9cy5nZXRBdHRyaWJ1 -dGUoImRhdGEtIituZXcgQS5TeShuZXcgQS5pNyhzKSkuT1UoIm9mZnNldCIpKQpyLnRvU3RyaW5nCnE9 -QS5RQShyLG51bGwpCnM9cy5nZXRBdHRyaWJ1dGUoImRhdGEtIituZXcgQS5TeShuZXcgQS5pNyhzKSku -T1UoImxpbmUiKSkKcy50b1N0cmluZwpwPUEuUUEocyxudWxsKQpzPXRoaXMuYgpzLnRvU3RyaW5nCkEu -aFgocyxxLHApfSwKJFM6MX0KQS5mQy5wcm90b3R5cGU9ewokMShhKXt0LnAuYShhKQp0aGlzLmEuYU0o -MCx0aGlzLmIpfSwKJFM6NDZ9CkEuVG0ucHJvdG90eXBlPXsKJDEoYSl7QS5uKGEpCnJldHVybiBhLmxl -bmd0aD40MD9CLnhCLk5qKGEsMCw0MCkrIi4uLiI6YX0sCiRTOjJ9CkEublQucHJvdG90eXBlPXsKJDAo -KXtBLkZyKHRoaXMuYSx0aGlzLmIsdGhpcy5jKX0sCiRTOjB9CkEuTlkucHJvdG90eXBlPXsKJDAoKXtB -LkZyKHRoaXMuYSxudWxsLG51bGwpfSwKJFM6MH0KQS51ZS5wcm90b3R5cGU9ewokMShhKXt0LmYuYShh -KQpyZXR1cm4gQS5kKGEucSgwLCJzZXZlcml0eSIpKSsiIC0gIitBLmQoYS5xKDAsIm1lc3NhZ2UiKSkr -IiBhdCAiK0EuZChhLnEoMCwibG9jYXRpb24iKSkrIiAtICgiK0EuZChhLnEoMCwiY29kZSIpKSsiKSJ9 -LAokUzo0N30KQS5HSC5wcm90b3R5cGU9ewokMShhKXt0LmguYShhKQokLnpCKCkKdC5lcy5hKCQub3co -KS5xKDAsImhsanMiKSkuVjcoImhpZ2hsaWdodEJsb2NrIixbYV0pfSwKJFM6OX0KQS5FRS5wcm90b3R5 -cGU9ewokMihhLGIpe3JldHVybiBBLklaKGEpK0ouSG0odC5lZS5hKGIpLmIpfSwKJFM6NDl9CkEuUE4u -cHJvdG90eXBlPXsKJDEoYSl7dmFyIHMscgp0LlYuYShhKS5wcmV2ZW50RGVmYXVsdCgpCnM9dGhpcy5h -CnI9dGhpcy5iCkEuYWYodC5GLmEod2luZG93LmxvY2F0aW9uKS5wYXRobmFtZSxzLHIsITAsbmV3IEEu -UUwocyxyKSkKQS5oWCh0aGlzLmMscyxyKX0sCiRTOjF9CkEuUUwucHJvdG90eXBlPXsKJDAoKXtBLkZy -KHQuRi5hKHdpbmRvdy5sb2NhdGlvbikucGF0aG5hbWUsdGhpcy5hLHRoaXMuYil9LAokUzowfQpBLlZT -LnByb3RvdHlwZT17CiQxKGEpe3ZhciBzLHI9InNlbGVjdGVkLWZpbGUiCnQuaC5hKGEpCnM9Si5ZRShh -KQppZihhLmdldEF0dHJpYnV0ZSgiZGF0YS0iK25ldyBBLlN5KG5ldyBBLmk3KGEpKS5PVSgibmFtZSIp -KT09PXRoaXMuYS5hKXMuZ1AoYSkuaSgwLHIpCmVsc2Ugcy5nUChhKS5SKDAscil9LAokUzo5fQpBLlRE -LnByb3RvdHlwZT17CiQxKGEpe3ZhciBzLHIKdC5WLmEoYSkKcz10aGlzLmEKc3dpdGNoKHMuZ0woKS5h -KXtjYXNlIDA6YnJlYWsKY2FzZSAyOnMubkcoKQpicmVhawpjYXNlIDM6cy5jMigpCmJyZWFrCmNhc2Ug -MTpzLmMyKCkKYnJlYWt9cj10aGlzLmIKQS5iTChyLHMpCkEueG4odGhpcy5jLHMpCkEuQVIocixzKX0s -CiRTOjF9CkEuSWYucHJvdG90eXBlPXsKJDEoYSl7dmFyIHMKdC5WLmEoYSkKcz10aGlzLmEKQS5PdChz -KQpBLnhuKHRoaXMuYixzKQpBLkFSKHRoaXMuYyxzKX0sCiRTOjF9CkEudEIucHJvdG90eXBlPXsKJDEo -YSl7cmV0dXJuIEEudDIodC5WLmEoYSksITApfSwKJFM6MX0KQS5tMi5wcm90b3R5cGU9ewokMShhKXty -ZXR1cm4gdGhpcy5SSSh0LlYuYShhKSl9LApSSShhKXt2YXIgcz0wLHI9QS5GWCh0LkgpLHE9MSxwLG89 -dGhpcyxuLG0sbCxrLGosaSxoLGcsZgp2YXIgJGFzeW5jJCQxPUEubHooZnVuY3Rpb24oYixjKXtpZihi -PT09MSl7cD1jCnM9cX13aGlsZSh0cnVlKXN3aXRjaChzKXtjYXNlIDA6cT0zCmo9ZG9jdW1lbnQKbj1C -LkNELnpRKGoucXVlcnlTZWxlY3RvcigiLmNvbnRlbnQiKS5zY3JvbGxUb3ApCmk9dC5OCnM9NgpyZXR1 -cm4gQS5qUShBLnR5KEEuUTQoIi9hcHBseS1oaW50IixBLkZsKGksaSkpLG8uYS5MdCgpKSwkYXN5bmMk -JDEpCmNhc2UgNjppPW8uYgpoPWkuYQpoLnRvU3RyaW5nCm09QS5VcyhoKQpzPTcKcmV0dXJuIEEualEo -QS5HNyhtLG51bGwsaS5iLCExLG51bGwpLCRhc3luYyQkMSkKY2FzZSA3OmouYm9keS5jbGFzc0xpc3Qu -YWRkKCJuZWVkcy1yZXJ1biIpCmo9ai5xdWVyeVNlbGVjdG9yKCIuY29udGVudCIpCmoudG9TdHJpbmcK -ai5zY3JvbGxUb3A9Qi5qbi56UShuKQpxPTEKcz01CmJyZWFrCmNhc2UgMzpxPTIKZj1wCmw9QS5SdShm -KQprPUEudHMoZikKQS5DMigiY291bGRuJ3QgYXBwbHkgaGludCIsbCxrKQpzPTUKYnJlYWsKY2FzZSAy -OnM9MQpicmVhawpjYXNlIDU6cmV0dXJuIEEueUMobnVsbCxyKQpjYXNlIDE6cmV0dXJuIEEuZjMocCxy -KX19KQpyZXR1cm4gQS5ESSgkYXN5bmMkJDEscil9LAokUzoxOH0KQS5RVy5wcm90b3R5cGU9ewoiWyIo -YSl7cmV0dXJuIHRoaXMuYSsiOlxuIit0aGlzLmJ9LAokaVJ6OjF9CkEuWEEucHJvdG90eXBlPXsKRWIo -YSxiLGMpe3JldHVybiEwfSwKaTAoYSl7cmV0dXJuITB9LAokaWtGOjF9CkEudnQucHJvdG90eXBlPXsK -Z0woKXt2YXIgcyxyLHEscCxvLG4sbSxsPXRoaXMuZAppZihsLmxlbmd0aD09PTApcmV0dXJuIEIuY3cK -cz1CLk5tLmd0SChsKS5nTCgpCmZvcihyPWwubGVuZ3RoLHE9ITAscD0hMCxvPTA7bzxsLmxlbmd0aDts -Lmxlbmd0aD09PXJ8fCgwLEEubGspKGwpLCsrbyl7bj1sW29dLmdMKCkKaWYobiE9cylzPW51bGwKbT1u -IT09Qi5jdwppZihtJiZuIT09Qi5XRClxPSExCmlmKG0mJm4hPT1CLlhqKXA9ITF9aWYocyE9bnVsbCly -ZXR1cm4gcwppZihxKXJldHVybiBCLldECmlmKHApcmV0dXJuIEIuWGoKcmV0dXJuIEIuZGN9LApMVigp -e3ZhciBzLHIscSxwPXRoaXMuZAppZihwIT1udWxsKWZvcihzPXAubGVuZ3RoLHI9MDtyPHM7KytyKXtx -PXBbcl0KcS5iIT09JCYmQS5idCgicGFyZW50IikKcS5iPXRoaXN9fSwKYzIoKXt2YXIgcyxyLHEscCxv -CmZvcihzPXRoaXMuZCxyPXMubGVuZ3RoLHE9MDtxPHMubGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAsQS5s -aykocyksKytxKXtwPXNbcV0KaWYocCBpbnN0YW5jZW9mIEEudnQpcC5jMigpCmVsc2V7aWYocCBpbnN0 -YW5jZW9mIEEuY0Qpe289cC53Cm8udG9TdHJpbmcKbz1vJiZwLnI9PT1CLlhqfWVsc2Ugbz0hMQppZihv -KXAucj1CLldEfX19LApuRygpe3ZhciBzLHIscSxwLG8KZm9yKHM9dGhpcy5kLHI9cy5sZW5ndGgscT0w -O3E8cy5sZW5ndGg7cy5sZW5ndGg9PT1yfHwoMCxBLmxrKShzKSwrK3Epe3A9c1txXQppZihwIGluc3Rh -bmNlb2YgQS52dClwLm5HKCkKZWxzZXtpZihwIGluc3RhbmNlb2YgQS5jRCl7bz1wLncKby50b1N0cmlu -ZwpvPW8mJnAucj09PUIuV0R9ZWxzZSBvPSExCmlmKG8pcC5yPUIuWGp9fX0sCkx0KCl7dmFyIHMscj1B -LkZsKHQuTix0LlgpCnIuWTUoMCwidHlwZSIsImRpcmVjdG9yeSIpCnIuWTUoMCwibmFtZSIsdGhpcy5h -KQpzPXRoaXMuZApzLnRvU3RyaW5nCnIuWTUoMCwic3VidHJlZSIsQS5WRChzKSkKcz10aGlzLmMKaWYo -cyE9bnVsbClyLlk1KDAsInBhdGgiLHMpCnJldHVybiByfX0KQS5jRC5wcm90b3R5cGU9ewpMdCgpe3Zh -ciBzLHI9dGhpcyxxPUEuRmwodC5OLHQuWCkKcS5ZNSgwLCJ0eXBlIiwiZmlsZSIpCnEuWTUoMCwibmFt -ZSIsci5hKQpzPXIuYwppZihzIT1udWxsKXEuWTUoMCwicGF0aCIscykKcz1yLmQKaWYocyE9bnVsbClx -Llk1KDAsImhyZWYiLHMpCnM9ci5lCmlmKHMhPW51bGwpcS5ZNSgwLCJlZGl0Q291bnQiLHMpCnM9ci5m -CmlmKHMhPW51bGwpcS5ZNSgwLCJ3YXNFeHBsaWNpdGx5T3B0ZWRPdXQiLHMpCnM9ci5yCmlmKHMhPW51 -bGwpcS5ZNSgwLCJtaWdyYXRpb25TdGF0dXMiLHMuYSkKcz1yLncKaWYocyE9bnVsbClxLlk1KDAsIm1p -Z3JhdGlvblN0YXR1c0NhbkJlQ2hhbmdlZCIscykKcmV0dXJuIHF9LApnTCgpe3JldHVybiB0aGlzLnJ9 -fQpBLkQ4LnByb3RvdHlwZT17fQpBLk85LnByb3RvdHlwZT17Cm4oKXtyZXR1cm4iTmF2aWdhdGlvblRy -ZWVOb2RlVHlwZS4iK3RoaXMuYn19CkEuR2IucHJvdG90eXBlPXsKbigpe3JldHVybiJVbml0TWlncmF0 -aW9uU3RhdHVzLiIrdGhpcy5ifX0KQS5MTC5wcm90b3R5cGU9ewpMdCgpe3JldHVybiBBLkVGKFsibm9k -ZUlkIix0aGlzLmIsImtpbmQiLHRoaXMuYS5hXSx0Lk4sdC5YKX19CkEuTUQucHJvdG90eXBlPXsKJDEo -YSl7cmV0dXJuIHQuZ3AuYShhKS5hPT09dGhpcy5hLnEoMCwia2luZCIpfSwKJFM6NTB9CkEuSDcucHJv -dG90eXBlPXsKbigpe3JldHVybiJIaW50QWN0aW9uS2luZC4iK3RoaXMuYn19CkEubEkucHJvdG90eXBl -PXsKV08oYSxiKXt2YXIgcyxyLHE9dC5kNApBLllGKCJhYnNvbHV0ZSIsQS5RSShbYixudWxsLG51bGws -bnVsbCxudWxsLG51bGwsbnVsbCxudWxsLG51bGwsbnVsbCxudWxsLG51bGwsbnVsbCxudWxsLG51bGxd -LHEpKQpzPXRoaXMuYQpzPXMuWXIoYik+MCYmIXMuaEsoYikKaWYocylyZXR1cm4gYgpzPUEuYWIoKQpy -PUEuUUkoW3MsYixudWxsLG51bGwsbnVsbCxudWxsLG51bGwsbnVsbCxudWxsLG51bGwsbnVsbCxudWxs -LG51bGwsbnVsbCxudWxsLG51bGxdLHEpCkEuWUYoImpvaW4iLHIpCnJldHVybiB0aGlzLklQKG5ldyBB -LnU2KHIsdC5lSikpfSwKemYoYSl7dmFyIHMscixxPUEuQ0woYSx0aGlzLmEpCnEuSVYoKQpzPXEuZApy -PXMubGVuZ3RoCmlmKHI9PT0wKXtzPXEuYgpyZXR1cm4gcz09bnVsbD8iLiI6c31pZihyPT09MSl7cz1x -LmIKcmV0dXJuIHM9PW51bGw/Ii4iOnN9aWYoMD49cilyZXR1cm4gQS5PSChzLC0xKQpzLnBvcCgpCnM9 -cS5lCmlmKDA+PXMubGVuZ3RoKXJldHVybiBBLk9IKHMsLTEpCnMucG9wKCkKcS5JVigpCnJldHVybiBx -WyJbIl0oMCl9LApJUChhKXt2YXIgcyxyLHEscCxvLG4sbSxsLGssagp0Lk8uYShhKQpmb3Iocz1hLiR0 -aSxyPXMuQygiYTIoY1guRSkiKS5hKG5ldyBBLnE3KCkpLHE9YS5nTShhKSxzPW5ldyBBLlNPKHEscixz -LkMoIlNPPGNYLkU+IikpLHI9dGhpcy5hLHA9ITEsbz0hMSxuPSIiO3MuRygpOyl7bT1xLmdsKCkKaWYo -ci5oSyhtKSYmbyl7bD1BLkNMKG0scikKaz1uLmNoYXJDb2RlQXQoMCk9PTA/bjpuCm49Qi54Qi5Oaihr -LDAsci5TcChrLCEwKSkKbC5iPW4KaWYoci5kcyhuKSlCLk5tLlk1KGwuZSwwLHIuZ21JKCkpCm49IiIr -bFsiWyJdKDApfWVsc2UgaWYoci5ZcihtKT4wKXtvPSFyLmhLKG0pCm49IiIrbX1lbHNle2o9bS5sZW5n -dGgKaWYoaiE9PTApe2lmKDA+PWopcmV0dXJuIEEuT0gobSwwKQpqPXIuVWQobVswXSl9ZWxzZSBqPSEx -CmlmKCFqKWlmKHApbis9ci5nbUkoKQpuKz1tfXA9ci5kcyhtKX1yZXR1cm4gbi5jaGFyQ29kZUF0KDAp -PT0wP246bn0sCm81KGEpe3ZhciBzCmlmKCF0aGlzLnkzKGEpKXJldHVybiBhCnM9QS5DTChhLHRoaXMu -YSkKcy5yUigpCnJldHVybiBzWyJbIl0oMCl9LAp5MyhhKXt2YXIgcyxyLHEscCxvLG4sbSxsLGs9dGhp -cy5hLGo9ay5ZcihhKQppZihqIT09MCl7aWYoaz09PSQuS2soKSlmb3Iocz0wO3M8ajsrK3MpaWYoQi54 -Qi5XKGEscyk9PT00NylyZXR1cm4hMApyPWoKcT00N31lbHNle3I9MApxPW51bGx9Zm9yKHA9bmV3IEEu -cWooYSkuYSxvPXAubGVuZ3RoLHM9cixuPW51bGw7czxvOysrcyxuPXEscT1tKXttPUIueEIuTyhwLHMp -CmlmKGsucjQobSkpe2lmKGs9PT0kLktrKCkmJm09PT00NylyZXR1cm4hMAppZihxIT1udWxsJiZrLnI0 -KHEpKXJldHVybiEwCmlmKHE9PT00NilsPW49PW51bGx8fG49PT00Nnx8ay5yNChuKQplbHNlIGw9ITEK -aWYobClyZXR1cm4hMH19aWYocT09bnVsbClyZXR1cm4hMAppZihrLnI0KHEpKXJldHVybiEwCmlmKHE9 -PT00NilrPW49PW51bGx8fGsucjQobil8fG49PT00NgplbHNlIGs9ITEKaWYoaylyZXR1cm4hMApyZXR1 -cm4hMX0sCkhQKGEsYil7dmFyIHMscixxLHAsbyxuLG0sbD10aGlzLGs9J1VuYWJsZSB0byBmaW5kIGEg -cGF0aCB0byAiJwpiPWwuV08oMCxiKQpzPWwuYQppZihzLllyKGIpPD0wJiZzLllyKGEpPjApcmV0dXJu -IGwubzUoYSkKaWYocy5ZcihhKTw9MHx8cy5oSyhhKSlhPWwuV08oMCxhKQppZihzLllyKGEpPD0wJiZz -LllyKGIpPjApdGhyb3cgQS5iKEEuSTcoaythKyciIGZyb20gIicrYisnIi4nKSkKcj1BLkNMKGIscykK -ci5yUigpCnE9QS5DTChhLHMpCnEuclIoKQpwPXIuZApvPXAubGVuZ3RoCmlmKG8hPT0wKXtpZigwPj1v -KXJldHVybiBBLk9IKHAsMCkKcD1KLlJNKHBbMF0sIi4iKX1lbHNlIHA9ITEKaWYocClyZXR1cm4gcVsi -WyJdKDApCnA9ci5iCm89cS5iCmlmKHAhPW8pcD1wPT1udWxsfHxvPT1udWxsfHwhcy5OYyhwLG8pCmVs -c2UgcD0hMQppZihwKXJldHVybiBxWyJbIl0oMCkKd2hpbGUoITApe3A9ci5kCm89cC5sZW5ndGgKaWYo -byE9PTApe249cS5kCm09bi5sZW5ndGgKaWYobSE9PTApe2lmKDA+PW8pcmV0dXJuIEEuT0gocCwwKQpw -PXBbMF0KaWYoMD49bSlyZXR1cm4gQS5PSChuLDApCm49cy5OYyhwLG5bMF0pCnA9bn1lbHNlIHA9ITF9 -ZWxzZSBwPSExCmlmKCFwKWJyZWFrCkIuTm0uVzQoci5kLDApCkIuTm0uVzQoci5lLDEpCkIuTm0uVzQo -cS5kLDApCkIuTm0uVzQocS5lLDEpfXA9ci5kCm89cC5sZW5ndGgKaWYobyE9PTApe2lmKDA+PW8pcmV0 -dXJuIEEuT0gocCwwKQpwPUouUk0ocFswXSwiLi4iKX1lbHNlIHA9ITEKaWYocCl0aHJvdyBBLmIoQS5J -NyhrK2ErJyIgZnJvbSAiJytiKyciLicpKQpwPXQuTgpCLk5tLlVHKHEuZCwwLEEuTzgoci5kLmxlbmd0 -aCwiLi4iLCExLHApKQpCLk5tLlk1KHEuZSwwLCIiKQpCLk5tLlVHKHEuZSwxLEEuTzgoci5kLmxlbmd0 -aCxzLmdtSSgpLCExLHApKQpzPXEuZApwPXMubGVuZ3RoCmlmKHA9PT0wKXJldHVybiIuIgppZihwPjEm -JkouUk0oQi5ObS5ncloocyksIi4iKSl7cz1xLmQKaWYoMD49cy5sZW5ndGgpcmV0dXJuIEEuT0gocywt -MSkKcy5wb3AoKQpzPXEuZQppZigwPj1zLmxlbmd0aClyZXR1cm4gQS5PSChzLC0xKQpzLnBvcCgpCmlm -KDA+PXMubGVuZ3RoKXJldHVybiBBLk9IKHMsLTEpCnMucG9wKCkKQi5ObS5pKHMsIiIpfXEuYj0iIgpx -LklWKCkKcmV0dXJuIHFbIlsiXSgwKX19CkEucTcucHJvdG90eXBlPXsKJDEoYSl7cmV0dXJuIEEubihh -KSE9PSIifSwKJFM6NH0KQS5Oby5wcm90b3R5cGU9ewokMShhKXtBLmsoYSkKcmV0dXJuIGE9PW51bGw/ -Im51bGwiOiciJythKyciJ30sCiRTOjUxfQpBLmZ2LnByb3RvdHlwZT17CnhaKGEpe3ZhciBzLHI9dGhp -cy5ZcihhKQppZihyPjApcmV0dXJuIEIueEIuTmooYSwwLHIpCmlmKHRoaXMuaEsoYSkpe2lmKDA+PWEu -bGVuZ3RoKXJldHVybiBBLk9IKGEsMCkKcz1hWzBdfWVsc2Ugcz1udWxsCnJldHVybiBzfSwKTmMoYSxi -KXtyZXR1cm4gYT09PWJ9fQpBLldELnByb3RvdHlwZT17CklWKCl7dmFyIHMscixxPXRoaXMKd2hpbGUo -ITApe3M9cS5kCmlmKCEocy5sZW5ndGghPT0wJiZKLlJNKEIuTm0uZ3JaKHMpLCIiKSkpYnJlYWsKcz1x -LmQKaWYoMD49cy5sZW5ndGgpcmV0dXJuIEEuT0gocywtMSkKcy5wb3AoKQpzPXEuZQppZigwPj1zLmxl -bmd0aClyZXR1cm4gQS5PSChzLC0xKQpzLnBvcCgpfXM9cS5lCnI9cy5sZW5ndGgKaWYociE9PTApQi5O -bS5ZNShzLHItMSwiIil9LApyUigpe3ZhciBzLHIscSxwLG8sbixtPXRoaXMsbD1BLlFJKFtdLHQucykK -Zm9yKHM9bS5kLHI9cy5sZW5ndGgscT0wLHA9MDtwPHMubGVuZ3RoO3MubGVuZ3RoPT09cnx8KDAsQS5s -aykocyksKytwKXtvPXNbcF0Kbj1KLmlhKG8pCmlmKCEobi5ETihvLCIuIil8fG4uRE4obywiIikpKWlm -KG4uRE4obywiLi4iKSl7bj1sLmxlbmd0aAppZihuIT09MCl7aWYoMD49bilyZXR1cm4gQS5PSChsLC0x -KQpsLnBvcCgpfWVsc2UgKytxfWVsc2UgQi5ObS5pKGwsbyl9aWYobS5iPT1udWxsKUIuTm0uVUcobCww -LEEuTzgocSwiLi4iLCExLHQuTikpCmlmKGwubGVuZ3RoPT09MCYmbS5iPT1udWxsKUIuTm0uaShsLCIu -IikKbS5zbkoobCkKcz1tLmEKbS5zUGgoQS5POChsLmxlbmd0aCsxLHMuZ21JKCksITAsdC5OKSkKcj1t -LmIKaWYocj09bnVsbHx8bC5sZW5ndGg9PT0wfHwhcy5kcyhyKSlCLk5tLlk1KG0uZSwwLCIiKQpyPW0u -YgppZihyIT1udWxsJiZzPT09JC5LaygpKXtyLnRvU3RyaW5nCm0uYj1BLnlzKHIsIi8iLCJcXCIpfW0u -SVYoKX0sCiJbIihhKXt2YXIgcyxyLHEscD10aGlzLG89cC5iCm89byE9bnVsbD8iIitvOiIiCmZvcihz -PTA7czxwLmQubGVuZ3RoOysrcyxvPXEpe3I9cC5lCmlmKCEoczxyLmxlbmd0aCkpcmV0dXJuIEEuT0go -cixzKQpyPUEuZChyW3NdKQpxPXAuZAppZighKHM8cS5sZW5ndGgpKXJldHVybiBBLk9IKHEscykKcT1v -K3IrQS5kKHFbc10pfW8rPUEuZChCLk5tLmdyWihwLmUpKQpyZXR1cm4gby5jaGFyQ29kZUF0KDApPT0w -P286b30sCnNuSihhKXt0aGlzLmQ9dC5hLmEoYSl9LApzUGgoYSl7dGhpcy5lPXQuYS5hKGEpfX0KQS5k -di5wcm90b3R5cGU9ewoiWyIoYSl7cmV0dXJuIlBhdGhFeGNlcHRpb246ICIrdGhpcy5hfSwKJGlSejox -fQpBLnpMLnByb3RvdHlwZT17CiJbIihhKXtyZXR1cm4gdGhpcy5nb2ModGhpcyl9fQpBLk9GLnByb3Rv -dHlwZT17ClVkKGEpe3JldHVybiBCLnhCLnRnKGEsIi8iKX0sCnI0KGEpe3JldHVybiBhPT09NDd9LApk -cyhhKXt2YXIgcz1hLmxlbmd0aApyZXR1cm4gcyE9PTAmJkIueEIuTyhhLHMtMSkhPT00N30sClNwKGEs -Yil7aWYoYS5sZW5ndGghPT0wJiZCLnhCLlcoYSwwKT09PTQ3KXJldHVybiAxCnJldHVybiAwfSwKWXIo -YSl7cmV0dXJuIHRoaXMuU3AoYSwhMSl9LApoSyhhKXtyZXR1cm4hMX0sCmdvYygpe3JldHVybiJwb3Np -eCJ9LApnbUkoKXtyZXR1cm4iLyJ9fQpBLnJ1LnByb3RvdHlwZT17ClVkKGEpe3JldHVybiBCLnhCLnRn -KGEsIi8iKX0sCnI0KGEpe3JldHVybiBhPT09NDd9LApkcyhhKXt2YXIgcz1hLmxlbmd0aAppZihzPT09 -MClyZXR1cm4hMQppZihCLnhCLk8oYSxzLTEpIT09NDcpcmV0dXJuITAKcmV0dXJuIEIueEIuVGMoYSwi -Oi8vIikmJnRoaXMuWXIoYSk9PT1zfSwKU3AoYSxiKXt2YXIgcyxyLHEscCxvPWEubGVuZ3RoCmlmKG89 -PT0wKXJldHVybiAwCmlmKEIueEIuVyhhLDApPT09NDcpcmV0dXJuIDEKZm9yKHM9MDtzPG87KytzKXty -PUIueEIuVyhhLHMpCmlmKHI9PT00NylyZXR1cm4gMAppZihyPT09NTgpe2lmKHM9PT0wKXJldHVybiAw -CnE9Qi54Qi5YVShhLCIvIixCLnhCLlFpKGEsIi8vIixzKzEpP3MrMzpzKQppZihxPD0wKXJldHVybiBv -CmlmKCFifHxvPHErMylyZXR1cm4gcQppZighQi54Qi5uQyhhLCJmaWxlOi8vIikpcmV0dXJuIHEKaWYo -IUEuWXUoYSxxKzEpKXJldHVybiBxCnA9cSszCnJldHVybiBvPT09cD9wOnErNH19cmV0dXJuIDB9LApZ -cihhKXtyZXR1cm4gdGhpcy5TcChhLCExKX0sCmhLKGEpe3JldHVybiBhLmxlbmd0aCE9PTAmJkIueEIu -VyhhLDApPT09NDd9LApnb2MoKXtyZXR1cm4idXJsIn0sCmdtSSgpe3JldHVybiIvIn19CkEuSVYucHJv -dG90eXBlPXsKVWQoYSl7cmV0dXJuIEIueEIudGcoYSwiLyIpfSwKcjQoYSl7cmV0dXJuIGE9PT00N3x8 -YT09PTkyfSwKZHMoYSl7dmFyIHM9YS5sZW5ndGgKaWYocz09PTApcmV0dXJuITEKcz1CLnhCLk8oYSxz -LTEpCnJldHVybiEocz09PTQ3fHxzPT09OTIpfSwKU3AoYSxiKXt2YXIgcyxyLHE9YS5sZW5ndGgKaWYo -cT09PTApcmV0dXJuIDAKcz1CLnhCLlcoYSwwKQppZihzPT09NDcpcmV0dXJuIDEKaWYocz09PTkyKXtp -ZihxPDJ8fEIueEIuVyhhLDEpIT09OTIpcmV0dXJuIDEKcj1CLnhCLlhVKGEsIlxcIiwyKQppZihyPjAp -e3I9Qi54Qi5YVShhLCJcXCIscisxKQppZihyPjApcmV0dXJuIHJ9cmV0dXJuIHF9aWYocTwzKXJldHVy -biAwCmlmKCFBLk9TKHMpKXJldHVybiAwCmlmKEIueEIuVyhhLDEpIT09NTgpcmV0dXJuIDAKcT1CLnhC -LlcoYSwyKQppZighKHE9PT00N3x8cT09PTkyKSlyZXR1cm4gMApyZXR1cm4gM30sCllyKGEpe3JldHVy -biB0aGlzLlNwKGEsITEpfSwKaEsoYSl7cmV0dXJuIHRoaXMuWXIoYSk9PT0xfSwKT3QoYSxiKXt2YXIg -cwppZihhPT09YilyZXR1cm4hMAppZihhPT09NDcpcmV0dXJuIGI9PT05MgppZihhPT09OTIpcmV0dXJu -IGI9PT00NwppZigoYV5iKSE9PTMyKXJldHVybiExCnM9YXwzMgpyZXR1cm4gcz49OTcmJnM8PTEyMn0s -Ck5jKGEsYil7dmFyIHMscgppZihhPT09YilyZXR1cm4hMApzPWEubGVuZ3RoCmlmKHMhPT1iLmxlbmd0 -aClyZXR1cm4hMQpmb3Iocj0wO3I8czsrK3IpaWYoIXRoaXMuT3QoQi54Qi5XKGEsciksQi54Qi5XKGIs -cikpKXJldHVybiExCnJldHVybiEwfSwKZ29jKCl7cmV0dXJuIndpbmRvd3MifSwKZ21JKCl7cmV0dXJu -IlxcIn19OyhmdW5jdGlvbiBhbGlhc2VzKCl7dmFyIHM9Si5Hdi5wcm90b3R5cGUKcy5VPXNbIlsiXQpz -PUoudTAucHJvdG90eXBlCnMudT1zWyJbIl0Kcz1BLk41LnByb3RvdHlwZQpzLlBBPXMuQ1gKcy5GUT1z -LmFhCnMuSjE9cy54dwpzPUEuY1gucHJvdG90eXBlCnMuR0c9cy5ldgpzPUEuTWgucHJvdG90eXBlCnMu -eGI9c1siWyJdCnM9QS5jdi5wcm90b3R5cGUKcy5EVz1zLnI2CnM9QS5tNi5wcm90b3R5cGUKcy5qRj1z -LkViCnM9QS5FNC5wcm90b3R5cGUKcy5Vcj1zLnEKcy5lND1zLlk1CnM9QS52Zy5wcm90b3R5cGUKcy5i -aD1zLlk1fSkoKTsoZnVuY3Rpb24gaW5zdGFsbFRlYXJPZmZzKCl7dmFyIHM9aHVua0hlbHBlcnMuX3N0 -YXRpY18xLHI9aHVua0hlbHBlcnMuX3N0YXRpY18wLHE9aHVua0hlbHBlcnMuaW5zdGFsbEluc3RhbmNl -VGVhck9mZixwPWh1bmtIZWxwZXJzLl9zdGF0aWNfMixvPWh1bmtIZWxwZXJzLmluc3RhbGxTdGF0aWNU -ZWFyT2ZmLG49aHVua0hlbHBlcnMuX2luc3RhbmNlXzF1CnMoQSwiRVgiLCJaViIsNSkKcyhBLCJ5dCIs -Im9BIiw1KQpzKEEsInFXIiwiQnoiLDUpCnIoQSwiVUkiLCJlTiIsMCkKcShBLlBmLnByb3RvdHlwZSwi -Z1lKIiwwLDEsbnVsbCxbIiQyIiwiJDEiXSxbIncwIiwicG0iXSwyNywwLDApCnAoQSwibFMiLCJPdSIs -NTMpCnMoQSwiVE4iLCJUOSIsNTQpCnMoQSwiQ3kiLCJOQyIsMykKcyhBLCJQSCIsIk10IiwyKQpvKEEs -InBTIiw0LG51bGwsWyIkNCJdLFsicUQiXSw2LDApCm8oQSwiVjQiLDQsbnVsbCxbIiQ0Il0sWyJuWiJd -LDYsMCkKbihBLkFzLnByb3RvdHlwZSwiZ3VNIiwiViIsMikKcyhBLCJpRyIsIndZIiw1NikKcyhBLCJ3 -MCIsImRVIiwzNykKcyhBLCJpUyIsImk2IiwxKX0pKCk7KGZ1bmN0aW9uIGluaGVyaXRhbmNlKCl7dmFy -IHM9aHVua0hlbHBlcnMubWl4aW4scj1odW5rSGVscGVycy5taXhpbkhhcmQscT1odW5rSGVscGVycy5p -bmhlcml0LHA9aHVua0hlbHBlcnMuaW5oZXJpdE1hbnkKcShBLk1oLG51bGwpCnAoQS5NaCxbQS5GSyxK -Lkd2LEoubTEsQS5jWCxBLkNmLEEuWFMsQS5uWSxBLkhiLEEuYTcsQS5BbixBLkZ1LEEuSkIsQS5TVSxB -LlJlLEEud3YsQS5QbixBLldVLEEuTEksQS5UcCxBLmY5LEEudGUsQS5icSxBLlhPLEEua3IsQS5ZayxB -LnZoLEEuTjYsQS5WUixBLkVLLEEuUGIsQS50USxBLlNkLEEuSmMsQS5FVCxBLmxZLEEuVzMsQS5paCxB -LkZ5LEEuR1YsQS5DdyxBLlBmLEEuRmUsQS52cyxBLk9NLEEucWgsQS5NTyxBLmtULEEueEksQS5tMCxB -LkZQLEEuYm4sQS5sbSxBLmxELEEuS1AsQS5sZixBLldZLEEuVWssQS5TaCxBLlJ3LEEuYnosQS5pUCxB -LmNrLEEuazUsQS5LWSxBLkNELEEuYUUsQS5OMyxBLmM4LEEuWmQsQS5SbixBLkRuLEEuUEUsQS5VZixB -LmlkLEEuRmssQS5KUSxBLkdtLEEudkQsQS5tNixBLk93LEEuVzksQS5kVyxBLm1rLEEuS28sQS5pSixB -LkU0LEEuZDIsQS5TZSxBLk1sLEEueUQsQS53YixBLmo4LEEucXAsQS5tUSxBLlhBLEEuRDgsQS5MTCxB -LmxJLEEuekwsQS5XRCxBLmR2XSkKcChKLkd2LFtKLnlFLEoud2UsSi5NRixKLmpkLEoucUksSi5EcixB -LmVIXSkKcChKLk1GLFtKLnUwLEEuUFosQS5BeixBLlk4LEEuTmgsQS5hZSxBLklCLEEubjcsQS5lYSxB -LmJyLEEuU2csQS51OCxBLm5xLEEudEQsQS5oRl0pCnAoSi51MCxbSi5pQyxKLmtkLEouYzVdKQpxKEou -UG8sSi5qZCkKcChKLnFJLFtKLkw3LEoua0RdKQpwKEEuY1gsW0EuQlIsQS5iUSxBLmkxLEEuVTUsQS5B -TSxBLnU2LEEuWFIsQS5tVyxBLnVuXSkKcChBLkJSLFtBLlp5LEEuUUNdKQpxKEEub2wsQS5aeSkKcShB -LlVxLEEuUUMpCnEoQS5qVixBLlVxKQpwKEEuWFMsW0EuYyxBLkV6LEEuYXosQS52VixBLkVxLEEuQzYs -QS5rUyxBLlVkLEEuRixBLnUsQS5tcCxBLnViLEEuZHMsQS5saixBLlVWLEEucCxBLlFXXSkKcShBLnV5 -LEEublkpCnAoQS51eSxbQS53MixBLnd6LEEuZTddKQpxKEEucWosQS53MikKcChBLmJRLFtBLmFMLEEu -TUIsQS5pNV0pCnAoQS5hTCxbQS5uSCxBLmxKLEEuaThdKQpxKEEueHksQS5pMSkKcChBLkFuLFtBLk1I -LEEuU08sQS5VMV0pCnEoQS5kNSxBLkFNKQpxKEEuUlUsQS5QbikKcShBLkdqLEEuUlUpCnEoQS5QRCxB -LkdqKQpxKEEuTFAsQS5XVSkKcChBLlRwLFtBLkUxLEEuQXksQS5sYyxBLmRDLEEuVlgsQS50aCxBLmhh -LEEuV00sQS5wVixBLmpaLEEuQjUsQS5PUixBLnY2LEEueVEsQS5SWixBLmM2LEEucWQsQS5DdixBLnZO -LEEuVXYsQS5FZyxBLkVvLEEuV2ssQS5JQSxBLkdFLEEuTjcsQS51USxBLkRWLEEuUEMsQS5RUyxBLm5w -LEEuVXQsQS5hTixBLmIwLEEuZSxBLm9aLEEuanIsQS55OCxBLkhpLEEuQlQsQS5QWSxBLkwsQS5XeCxB -LkhvLEEuSUMsQS5mQyxBLlRtLEEudWUsQS5HSCxBLlBOLEEuVlMsQS5URCxBLklmLEEudEIsQS5tMixB -Lk1ELEEucTcsQS5Ob10pCnAoQS5FMSxbQS5DaixBLmV3LEEud04sQS5TWCxBLkdzLEEuVTcsQS5yYSxB -LnRpLEEuV0YsQS5uMSxBLmNTLEEuVkMsQS5KVCxBLk1FLEEueTUsQS55SSxBLktTLEEuQTMsQS5mbSxB -LkUyLEEuamcsQS5xbCxBLkVFXSkKcShBLlcwLEEuRXopCnAoQS5sYyxbQS56eCxBLnJUXSkKcShBLmtZ -LEEuQzYpCnEoQS5pbCxBLllrKQpwKEEuaWwsW0EuTjUsQS51dyxBLmNmLEEuU3ldKQpwKEEubVcsW0Eu -S1csQS5xNF0pCnEoQS5YSCxBLmVIKQpwKEEuWEgsW0EuUkcsQS5XQl0pCnEoQS5WUCxBLlJHKQpxKEEu -RGcsQS5WUCkKcShBLlpHLEEuV0IpCnEoQS5QZyxBLlpHKQpwKEEuUGcsW0EueGosQS5kRSxBLlpBLEEu -ZFQsQS5QcSxBLmVFLEEuVjZdKQpxKEEuaU0sQS5rUykKcChBLkF5LFtBLlZzLEEuRnQsQS55SCxBLmRh -LEEub1EsQS52cixBLnJ0LEEuS0YsQS5aTCxBLlJULEEucnEsQS5SVyxBLnVPLEEuRXYsQS5WcCxBLnhy -LEEuTnosQS5WVyxBLkxaLEEucDEsQS5uVCxBLk5ZLEEuUUxdKQpxKEEuWmYsQS5QZikKcShBLkppLEEu -bTApCnEoQS54ZCxBLk41KQpxKEEuWHYsQS5GUCkKcShBLkQwLEEuWHYpCnEoQS5WaixBLldZKQpwKEEu -VWssW0EuQ1YsQS5aaSxBLmJ5XSkKcShBLndJLEEua1QpCnAoQS53SSxbQS5VOCxBLm9qLEEuTXgsQS5F -MyxBLkdZXSkKcShBLks4LEEuVWQpCnEoQS50dSxBLlNoKQpxKEEudTUsQS5aaSkKcChBLnUsW0EuYkos -QS5lWV0pCnEoQS5xZSxBLkRuKQpwKEEuUFosW0EuS1YsQS53YSxBLks1LEEuQ21dKQpwKEEuS1YsW0Eu -Y3YsQS5ueCxBLlFGLEEuQ1FdKQpwKEEuY3YsW0EucUUsQS5oaV0pCnAoQS5xRSxbQS5HaCxBLmZZLEEu -closQS5RUCxBLmg0LEEuU04sQS5scCxBLlRiLEEuSXYsQS5XUCxBLnlZXSkKcShBLm9KLEEuWTgpCnEo -QS5oSCxBLkF6KQpxKEEuVmIsQS5RRikKcShBLmZKLEEud2EpCnAoQS5lYSxbQS53NixBLndWXSkKcShB -LkFqLEEudzYpCnEoQS5BcixBLm5xKQpxKEEuQkgsQS5BcikKcShBLnc0LEEuSUIpCnEoQS51ZixBLnRE -KQpxKEEucmgsQS51ZikKcShBLmk3LEEuY2YpCnEoQS5BcyxBLlZqKQpwKEEuQXMsW0EuSTQsQS5LZV0p -CnEoQS5STyxBLnFoKQpxKEEuQ3EsQS5STykKcShBLnhDLEEuTU8pCnEoQS5jdCxBLm02KQpxKEEuQmYs -QS5pSikKcChBLkU0LFtBLnI3LEEudmddKQpxKEEuVHosQS52ZykKcShBLm5kLEEuaGkpCnAoQS5EOCxb -QS52dCxBLmNEXSkKcChBLmNrLFtBLk85LEEuR2IsQS5IN10pCnEoQS5mdixBLnpMKQpwKEEuZnYsW0Eu -T0YsQS5ydSxBLklWXSkKcyhBLncyLEEuUmUpCnMoQS5RQyxBLmxEKQpzKEEuUkcsQS5sRCkKcyhBLlZQ -LEEuU1UpCnMoQS5XQixBLmxEKQpzKEEuWkcsQS5TVSkKcyhBLm5ZLEEubEQpCnMoQS5XWSxBLmxmKQpz -KEEuUlUsQS5LUCkKcyhBLkZQLEEubGYpCnMoQS5ZOCxBLmlkKQpzKEEubnEsQS5sRCkKcyhBLkFyLEEu -R20pCnMoQS50RCxBLmxEKQpzKEEudWYsQS5HbSkKcihBLnZnLEEubEQpfSkoKQp2YXIgdj17dHlwZVVu -aXZlcnNlOntlQzpuZXcgTWFwKCksdFI6e30sZVQ6e30sdFBWOnt9LHNFQTpbXX0sbWFuZ2xlZEdsb2Jh -bE5hbWVzOntLTjoiaW50IixDUDoiZG91YmxlIixaWjoibnVtIixxVToiU3RyaW5nIixhMjoiYm9vbCIs -Yzg6Ik51bGwiLHpNOiJMaXN0In0sbWFuZ2xlZE5hbWVzOnt9LHR5cGVzOlsifigpIiwifihBaikiLCJx -VShxVSkiLCJAKEApIiwiYTIocVUpIiwifih+KCkpIiwiYTIoY3YscVUscVUsSlEpIiwiYzgoQCkiLCJj -OCgpIiwifihjdikiLCJ+KE1oPyxNaD8pIiwiQCgpIiwifihxVSxAKSIsIn4objYscVUsS04pIiwifihx -VSxxVSkiLCJhMihrRikiLCJ+KE9sPHFVPikiLCJjOChlYSkiLCJiODx+PihBaikiLCJ+KHFVLEtOKSIs -In4ocVUsS04/KSIsIktOKEtOLEtOKSIsImM4KH4oKSkiLCJ+KHFVLHFVPykiLCJuNihALEApIiwifihL -TixAKSIsImEyKEtWKSIsIn4oTWhbR3o/XSkiLCJ+KGVhKSIsImM4KE1oLEd6KSIsInZzPEA+KEApIiwi -fihLVixLVj8pIiwifihALEApIiwiYzgoQCxAKSIsImEyKE9sPHFVPikiLCJhMihAKSIsInI3KEApIiwi -TWg/KEApIiwiRTQoQCkiLCJMTChAKSIsIlowPHFVLE1oPz4oTEwpIiwiQChxVSkiLCJAKEAscVUpIiwi -YzgoWjA8cVUsTWg/Pj8pIiwiYzgoTWgsQCkiLCJ+KEdELEApIiwifih3VikiLCJxVShaMDxALEA+KSIs -IlR6PEA+KEApIiwiS04oS04sTjM8cVUsek08ajg+PikiLCJhMihINykiLCJxVShxVT8pIiwiWjA8cVUs -cVU+KFowPHFVLHFVPixxVSkiLCJhMihNaD8sTWg/KSIsIktOKE1oPykiLCJ+KEApIiwiTWg/KE1oPyki -LCJjOChALEd6KSJdLGludGVyY2VwdG9yc0J5VGFnOm51bGwsbGVhZlRhZ3M6bnVsbCxhcnJheVJ0aTpT -eW1ib2woIiR0aSIpfQpBLnhiKHYudHlwZVVuaXZlcnNlLEpTT04ucGFyc2UoJ3siaUMiOiJ1MCIsImtk -IjoidTAiLCJjNSI6InUwIiwicngiOiJlYSIsImU1IjoiZWEiLCJZMCI6ImhpIiwidHAiOiJoaSIsIkc4 -Ijoid1YiLCJNciI6InFFIiwiZUwiOiJxRSIsIkkwIjoiS1YiLCJocyI6IktWIiwiWGciOiJRRiIsIm5y -IjoiQWoiLCJ5NCI6Inc2IiwiZE4iOiJDbSIsInhjIjoibngiLCJrSiI6Im54IiwiQnMiOiJjdiIsInpV -IjoiRGciLCJkZiI6ImVIIiwieUUiOnsiYTIiOltdfSwid2UiOnsiYzgiOltdfSwidTAiOnsidm0iOltd -fSwiamQiOnsiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIlBvIjp7ImpkIjpbIjEiXSwi -ek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sIm0xIjp7IkFuIjpbIjEiXX0sInFJIjp7IkNQ -IjpbXSwiWloiOltdfSwiTDciOnsiQ1AiOltdLCJLTiI6W10sIlpaIjpbXX0sImtEIjp7IkNQIjpbXSwi -WloiOltdfSwiRHIiOnsicVUiOltdLCJ2WCI6W119LCJCUiI6eyJjWCI6WyIyIl19LCJDZiI6eyJBbiI6 -WyIyIl19LCJaeSI6eyJCUiI6WyIxIiwiMiJdLCJjWCI6WyIyIl0sImNYLkUiOiIyIn0sIm9sIjp7Ilp5 -IjpbIjEiLCIyIl0sIkJSIjpbIjEiLCIyIl0sImJRIjpbIjIiXSwiY1giOlsiMiJdLCJjWC5FIjoiMiJ9 -LCJVcSI6eyJsRCI6WyIyIl0sInpNIjpbIjIiXSwiQlIiOlsiMSIsIjIiXSwiYlEiOlsiMiJdLCJjWCI6 -WyIyIl19LCJqViI6eyJVcSI6WyIxIiwiMiJdLCJsRCI6WyIyIl0sInpNIjpbIjIiXSwiQlIiOlsiMSIs -IjIiXSwiYlEiOlsiMiJdLCJjWCI6WyIyIl0sImxELkUiOiIyIiwiY1guRSI6IjIifSwiYyI6eyJYUyI6 -W119LCJxaiI6eyJsRCI6WyJLTiJdLCJSZSI6WyJLTiJdLCJ6TSI6WyJLTiJdLCJiUSI6WyJLTiJdLCJj -WCI6WyJLTiJdLCJsRC5FIjoiS04iLCJSZS5FIjoiS04ifSwiYlEiOnsiY1giOlsiMSJdfSwiYUwiOnsi -YlEiOlsiMSJdLCJjWCI6WyIxIl19LCJuSCI6eyJhTCI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJd -LCJhTC5FIjoiMSIsImNYLkUiOiIxIn0sImE3Ijp7IkFuIjpbIjEiXX0sImkxIjp7ImNYIjpbIjIiXSwi -Y1guRSI6IjIifSwieHkiOnsiaTEiOlsiMSIsIjIiXSwiYlEiOlsiMiJdLCJjWCI6WyIyIl0sImNYLkUi -OiIyIn0sIk1IIjp7IkFuIjpbIjIiXX0sImxKIjp7ImFMIjpbIjIiXSwiYlEiOlsiMiJdLCJjWCI6WyIy -Il0sImFMLkUiOiIyIiwiY1guRSI6IjIifSwiVTUiOnsiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJTTyI6 -eyJBbiI6WyIxIl19LCJBTSI6eyJjWCI6WyIxIl0sImNYLkUiOiIxIn0sImQ1Ijp7IkFNIjpbIjEiXSwi -YlEiOlsiMSJdLCJjWCI6WyIxIl0sImNYLkUiOiIxIn0sIlUxIjp7IkFuIjpbIjEiXX0sIk1CIjp7ImJR -IjpbIjEiXSwiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJGdSI6eyJBbiI6WyIxIl19LCJ1NiI6eyJjWCI6 -WyIxIl0sImNYLkUiOiIxIn0sIkpCIjp7IkFuIjpbIjEiXX0sIncyIjp7ImxEIjpbIjEiXSwiUmUiOlsi -MSJdLCJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiY1giOlsiMSJdfSwid3YiOnsiR0QiOltdfSwiUEQiOnsi -R2oiOlsiMSIsIjIiXSwiUlUiOlsiMSIsIjIiXSwiUG4iOlsiMSIsIjIiXSwiS1AiOlsiMSIsIjIiXSwi -WjAiOlsiMSIsIjIiXX0sIldVIjp7IlowIjpbIjEiLCIyIl19LCJMUCI6eyJXVSI6WyIxIiwiMiJdLCJa -MCI6WyIxIiwiMiJdfSwiWFIiOnsiY1giOlsiMSJdLCJjWC5FIjoiMSJ9LCJMSSI6eyJ2USI6W119LCJX -MCI6eyJFeiI6W10sIlhTIjpbXX0sImF6Ijp7IlhTIjpbXX0sInZWIjp7IlhTIjpbXX0sInRlIjp7IlJ6 -IjpbXX0sIlhPIjp7Ikd6IjpbXX0sIlRwIjp7IkVIIjpbXX0sIkF5Ijp7IkVIIjpbXX0sIkUxIjp7IkVI -IjpbXX0sImxjIjp7IkVIIjpbXX0sInp4Ijp7IkVIIjpbXX0sInJUIjp7IkVIIjpbXX0sIkVxIjp7IlhT -IjpbXX0sImtZIjp7IlhTIjpbXX0sIk41Ijp7IllrIjpbIjEiLCIyIl0sIkZvIjpbIjEiLCIyIl0sIlow -IjpbIjEiLCIyIl0sIllrLksiOiIxIiwiWWsuViI6IjIifSwiaTUiOnsiYlEiOlsiMSJdLCJjWCI6WyIx -Il0sImNYLkUiOiIxIn0sIk42Ijp7IkFuIjpbIjEiXX0sIlZSIjp7IndMIjpbXSwidlgiOltdfSwiRUsi -OnsiaWIiOltdLCJPZCI6W119LCJLVyI6eyJjWCI6WyJpYiJdLCJjWC5FIjoiaWIifSwiUGIiOnsiQW4i -OlsiaWIiXX0sInRRIjp7Ik9kIjpbXX0sInVuIjp7ImNYIjpbIk9kIl0sImNYLkUiOiJPZCJ9LCJTZCI6 -eyJBbiI6WyJPZCJdfSwiZUgiOnsiQVMiOltdfSwiWEgiOnsiWGoiOlsiMSJdLCJlSCI6W10sIkFTIjpb -XX0sIkRnIjp7ImxEIjpbIkNQIl0sIlhqIjpbIkNQIl0sInpNIjpbIkNQIl0sImVIIjpbXSwiYlEiOlsi -Q1AiXSwiQVMiOltdLCJjWCI6WyJDUCJdLCJTVSI6WyJDUCJdLCJsRC5FIjoiQ1AifSwiUGciOnsibEQi -OlsiS04iXSwiWGoiOlsiS04iXSwiek0iOlsiS04iXSwiZUgiOltdLCJiUSI6WyJLTiJdLCJBUyI6W10s -ImNYIjpbIktOIl0sIlNVIjpbIktOIl19LCJ4aiI6eyJsRCI6WyJLTiJdLCJYaiI6WyJLTiJdLCJ6TSI6 -WyJLTiJdLCJlSCI6W10sImJRIjpbIktOIl0sIkFTIjpbXSwiY1giOlsiS04iXSwiU1UiOlsiS04iXSwi -bEQuRSI6IktOIn0sImRFIjp7ImxEIjpbIktOIl0sIlhqIjpbIktOIl0sInpNIjpbIktOIl0sImVIIjpb -XSwiYlEiOlsiS04iXSwiQVMiOltdLCJjWCI6WyJLTiJdLCJTVSI6WyJLTiJdLCJsRC5FIjoiS04ifSwi -WkEiOnsibEQiOlsiS04iXSwiWGoiOlsiS04iXSwiek0iOlsiS04iXSwiZUgiOltdLCJiUSI6WyJLTiJd -LCJBUyI6W10sImNYIjpbIktOIl0sIlNVIjpbIktOIl0sImxELkUiOiJLTiJ9LCJkVCI6eyJsRCI6WyJL -TiJdLCJYaiI6WyJLTiJdLCJ6TSI6WyJLTiJdLCJlSCI6W10sImJRIjpbIktOIl0sIkFTIjpbXSwiY1gi -OlsiS04iXSwiU1UiOlsiS04iXSwibEQuRSI6IktOIn0sIlBxIjp7ImxEIjpbIktOIl0sIlhqIjpbIktO -Il0sInpNIjpbIktOIl0sImVIIjpbXSwiYlEiOlsiS04iXSwiQVMiOltdLCJjWCI6WyJLTiJdLCJTVSI6 -WyJLTiJdLCJsRC5FIjoiS04ifSwiZUUiOnsibEQiOlsiS04iXSwiWGoiOlsiS04iXSwiek0iOlsiS04i -XSwiZUgiOltdLCJiUSI6WyJLTiJdLCJBUyI6W10sImNYIjpbIktOIl0sIlNVIjpbIktOIl0sImxELkUi -OiJLTiJ9LCJWNiI6eyJsRCI6WyJLTiJdLCJuNiI6W10sIlhqIjpbIktOIl0sInpNIjpbIktOIl0sImVI -IjpbXSwiYlEiOlsiS04iXSwiQVMiOltdLCJjWCI6WyJLTiJdLCJTVSI6WyJLTiJdLCJsRC5FIjoiS04i -fSwia1MiOnsiWFMiOltdfSwiaU0iOnsiRXoiOltdLCJYUyI6W119LCJ2cyI6eyJiOCI6WyIxIl19LCJH -ViI6eyJBbiI6WyIxIl19LCJxNCI6eyJjWCI6WyIxIl0sImNYLkUiOiIxIn0sIkN3Ijp7IlhTIjpbXX0s -IlpmIjp7IlBmIjpbIjEiXX0sIm0wIjp7IlFtIjpbXX0sIkppIjp7Im0wIjpbXSwiUW0iOltdfSwieGQi -OnsiTjUiOlsiMSIsIjIiXSwiWWsiOlsiMSIsIjIiXSwiRm8iOlsiMSIsIjIiXSwiWjAiOlsiMSIsIjIi -XSwiWWsuSyI6IjEiLCJZay5WIjoiMiJ9LCJEMCI6eyJsZiI6WyIxIl0sIk9sIjpbIjEiXSwiYlEiOlsi -MSJdLCJjWCI6WyIxIl0sImxmLkUiOiIxIn0sImxtIjp7IkFuIjpbIjEiXX0sIm1XIjp7ImNYIjpbIjEi -XX0sInV5Ijp7ImxEIjpbIjEiXSwiek0iOlsiMSJdLCJiUSI6WyIxIl0sImNYIjpbIjEiXX0sImlsIjp7 -IllrIjpbIjEiLCIyIl0sIlowIjpbIjEiLCIyIl19LCJZayI6eyJaMCI6WyIxIiwiMiJdfSwiUG4iOnsi -WjAiOlsiMSIsIjIiXX0sIkdqIjp7IlJVIjpbIjEiLCIyIl0sIlBuIjpbIjEiLCIyIl0sIktQIjpbIjEi -LCIyIl0sIlowIjpbIjEiLCIyIl19LCJWaiI6eyJsZiI6WyIxIl0sIk9sIjpbIjEiXSwiYlEiOlsiMSJd -LCJjWCI6WyIxIl19LCJYdiI6eyJsZiI6WyIxIl0sIk9sIjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIx -Il19LCJ1dyI6eyJZayI6WyJxVSIsIkAiXSwiWjAiOlsicVUiLCJAIl0sIllrLksiOiJxVSIsIllrLlYi -OiJAIn0sImk4Ijp7ImFMIjpbInFVIl0sImJRIjpbInFVIl0sImNYIjpbInFVIl0sImFMLkUiOiJxVSIs -ImNYLkUiOiJxVSJ9LCJDViI6eyJVayI6WyJ6TTxLTj4iLCJxVSJdLCJVay5TIjoiek08S04+In0sIlpp -Ijp7IlVrIjpbInFVIiwiek08S04+Il19LCJVZCI6eyJYUyI6W119LCJLOCI6eyJYUyI6W119LCJieSI6 -eyJVayI6WyJNaD8iLCJxVSJdLCJVay5TIjoiTWg/In0sInU1Ijp7IlVrIjpbInFVIiwiek08S04+Il0s -IlVrLlMiOiJxVSJ9LCJDUCI6eyJaWiI6W119LCJLTiI6eyJaWiI6W119LCJ6TSI6eyJiUSI6WyIxIl0s -ImNYIjpbIjEiXX0sImliIjp7Ik9kIjpbXX0sIk9sIjp7ImJRIjpbIjEiXSwiY1giOlsiMSJdfSwicVUi -OnsidlgiOltdfSwiQzYiOnsiWFMiOltdfSwiRXoiOnsiWFMiOltdfSwiRiI6eyJFeiI6W10sIlhTIjpb -XX0sInUiOnsiWFMiOltdfSwiYkoiOnsiWFMiOltdfSwiZVkiOnsiWFMiOltdfSwibXAiOnsiWFMiOltd -fSwidWIiOnsiWFMiOltdfSwiZHMiOnsiWFMiOltdfSwibGoiOnsiWFMiOltdfSwiVVYiOnsiWFMiOltd -fSwiazUiOnsiWFMiOltdfSwiS1kiOnsiWFMiOltdfSwicCI6eyJYUyI6W119LCJDRCI6eyJSeiI6W119 -LCJhRSI6eyJSeiI6W119LCJaZCI6eyJHeiI6W119LCJSbiI6eyJCTCI6W119LCJEbiI6eyJpRCI6W119 -LCJVZiI6eyJpRCI6W119LCJxZSI6eyJpRCI6W119LCJjdiI6eyJLViI6W10sIlBaIjpbXX0sImZKIjp7 -IlBaIjpbXX0sIkFqIjp7ImVhIjpbXX0sIktWIjp7IlBaIjpbXX0sIndWIjp7ImVhIjpbXX0sIkpRIjp7 -ImtGIjpbXX0sInFFIjp7ImN2IjpbXSwiS1YiOltdLCJQWiI6W119LCJHaCI6eyJjdiI6W10sIktWIjpb -XSwiUFoiOltdfSwiZlkiOnsiY3YiOltdLCJLViI6W10sIlBaIjpbXX0sInJaIjp7ImN2IjpbXSwiS1Yi -OltdLCJQWiI6W119LCJRUCI6eyJjdiI6W10sIktWIjpbXSwiUFoiOltdfSwibngiOnsiS1YiOltdLCJQ -WiI6W119LCJRRiI6eyJLViI6W10sIlBaIjpbXX0sIklCIjp7InRuIjpbIlpaIl19LCJ3eiI6eyJsRCI6 -WyIxIl0sInpNIjpbIjEiXSwiYlEiOlsiMSJdLCJjWCI6WyIxIl0sImxELkUiOiIxIn0sImhIIjp7IkF6 -IjpbXX0sImg0Ijp7ImN2IjpbXSwiS1YiOltdLCJQWiI6W119LCJWYiI6eyJLViI6W10sIlBaIjpbXX0s -IndhIjp7IlBaIjpbXX0sImU3Ijp7ImxEIjpbIktWIl0sInpNIjpbIktWIl0sImJRIjpbIktWIl0sImNY -IjpbIktWIl0sImxELkUiOiJLViJ9LCJCSCI6eyJsRCI6WyJLViJdLCJHbSI6WyJLViJdLCJ6TSI6WyJL -ViJdLCJYaiI6WyJLViJdLCJiUSI6WyJLViJdLCJjWCI6WyJLViJdLCJsRC5FIjoiS1YiLCJHbS5FIjoi -S1YifSwiU04iOnsiY3YiOltdLCJLViI6W10sIlBaIjpbXX0sImxwIjp7ImN2IjpbXSwiS1YiOltdLCJQ -WiI6W119LCJUYiI6eyJjdiI6W10sIktWIjpbXSwiUFoiOltdfSwiSXYiOnsiY3YiOltdLCJLViI6W10s -IlBaIjpbXX0sIldQIjp7ImN2IjpbXSwiS1YiOltdLCJQWiI6W119LCJ5WSI6eyJjdiI6W10sIktWIjpb -XSwiUFoiOltdfSwidzYiOnsiZWEiOltdfSwiSzUiOnsiZEEiOltdLCJQWiI6W119LCJDbSI6eyJQWiI6 -W119LCJDUSI6eyJLViI6W10sIlBaIjpbXX0sInc0Ijp7InRuIjpbIlpaIl19LCJyaCI6eyJsRCI6WyJL -ViJdLCJHbSI6WyJLViJdLCJ6TSI6WyJLViJdLCJYaiI6WyJLViJdLCJiUSI6WyJLViJdLCJjWCI6WyJL -ViJdLCJsRC5FIjoiS1YiLCJHbS5FIjoiS1YifSwiY2YiOnsiWWsiOlsicVUiLCJxVSJdLCJaMCI6WyJx -VSIsInFVIl19LCJpNyI6eyJZayI6WyJxVSIsInFVIl0sIlowIjpbInFVIiwicVUiXSwiWWsuSyI6InFV -IiwiWWsuViI6InFVIn0sIlN5Ijp7IllrIjpbInFVIiwicVUiXSwiWjAiOlsicVUiLCJxVSJdLCJZay5L -IjoicVUiLCJZay5WIjoicVUifSwiSTQiOnsibGYiOlsicVUiXSwiT2wiOlsicVUiXSwiYlEiOlsicVUi -XSwiY1giOlsicVUiXSwibGYuRSI6InFVIn0sIlJPIjp7InFoIjpbIjEiXX0sIkNxIjp7IlJPIjpbIjEi -XSwicWgiOlsiMSJdfSwieEMiOnsiTU8iOlsiMSJdfSwidkQiOnsia0YiOltdfSwibTYiOnsia0YiOltd -fSwiY3QiOnsia0YiOltdfSwiT3ciOnsia0YiOltdfSwiVzkiOnsiQW4iOlsiMSJdfSwiZFciOnsiZEEi -OltdLCJQWiI6W119LCJtayI6eyJ5MCI6W119LCJLbyI6eyJvbiI6W119LCJBcyI6eyJsZiI6WyJxVSJd -LCJPbCI6WyJxVSJdLCJiUSI6WyJxVSJdLCJjWCI6WyJxVSJdfSwicjciOnsiRTQiOltdfSwiVHoiOnsi -bEQiOlsiMSJdLCJ6TSI6WyIxIl0sImJRIjpbIjEiXSwiRTQiOltdLCJjWCI6WyIxIl0sImxELkUiOiIx -In0sIm5kIjp7ImhpIjpbXSwiY3YiOltdLCJLViI6W10sIlBaIjpbXX0sIktlIjp7ImxmIjpbInFVIl0s -Ik9sIjpbInFVIl0sImJRIjpbInFVIl0sImNYIjpbInFVIl0sImxmLkUiOiJxVSJ9LCJoaSI6eyJjdiI6 -W10sIktWIjpbXSwiUFoiOltdfSwiUVciOnsiWFMiOltdLCJSeiI6W119LCJYQSI6eyJrRiI6W119LCJ2 -dCI6eyJEOCI6W119LCJjRCI6eyJEOCI6W119LCJkdiI6eyJSeiI6W119LCJPRiI6eyJmdiI6W119LCJy -dSI6eyJmdiI6W119LCJJViI6eyJmdiI6W119LCJuNiI6eyJ6TSI6WyJLTiJdLCJiUSI6WyJLTiJdLCJj -WCI6WyJLTiJdLCJBUyI6W119fScpKQpBLkZGKHYudHlwZVVuaXZlcnNlLEpTT04ucGFyc2UoJ3sidzIi -OjEsIlFDIjoyLCJYSCI6MSwia1QiOjIsIm1XIjoxLCJ1eSI6MSwiaWwiOjIsIlZqIjoxLCJYdiI6MSwi -blkiOjEsIldZIjoxLCJGUCI6MSwid0kiOjIsInZnIjoxfScpKQp2YXIgdT17bDoiQ2Fubm90IGV4dHJh -Y3QgYSBmaWxlIHBhdGggZnJvbSBhIFVSSSB3aXRoIGEgZnJhZ21lbnQgY29tcG9uZW50IixpOiJDYW5u -b3QgZXh0cmFjdCBhIGZpbGUgcGF0aCBmcm9tIGEgVVJJIHdpdGggYSBxdWVyeSBjb21wb25lbnQiLGo6 -IkNhbm5vdCBleHRyYWN0IGEgbm9uLVdpbmRvd3MgZmlsZSBwYXRoIGZyb20gYSBmaWxlIFVSSSB3aXRo -IGFuIGF1dGhvcml0eSIsYzoiRXJyb3IgaGFuZGxlciBtdXN0IGFjY2VwdCBvbmUgT2JqZWN0IG9yIG9u -ZSBPYmplY3QgYW5kIGEgU3RhY2tUcmFjZSBhcyBhcmd1bWVudHMsIGFuZCByZXR1cm4gYSB2YWx1ZSBv -ZiB0aGUgcmV0dXJuZWQgZnV0dXJlJ3MgdHlwZSIsZDoiYXJlYS1hbmFseXplcixhbmFseXplci1ubmJk -LW1pZ3JhdGlvbix0eXBlLWJ1ZyJ9CnZhciB0PShmdW5jdGlvbiBydGlpKCl7dmFyIHM9QS5OMApyZXR1 -cm57YnE6cygiR2giKSxuOnMoIkN3IiksY1I6cygicloiKSx3OnMoIkF6IikscjpzKCJRUCIpLGdGOnMo -IlBEPEdELEA+IiksVTpzKCJiUTxAPiIpLGg6cygiY3YiKSx1OnMoIlhTIiksQjpzKCJlYSIpLGc4OnMo -IlJ6IiksYzg6cygiaEgiKSxZOnMoIkVIIiksaTpzKCJiODxAPiIpLEo6cygiTEwiKSxncDpzKCJINyIp -LHg6cygiU2ciKSxvOnMoInZRIiksZWg6cygiY1g8S1Y+IiksTzpzKCJjWDxxVT4iKSxrOnMoImNYPEA+ -IiksZkE6cygiamQ8U2U+IiksZ2k6cygiamQ8ajg+IiksZDpzKCJqZDxaMDxxVSxNaD8+PiIpLGZoOnMo -ImpkPEQ4PiIpLEQ6cygiamQ8a0Y+IiksczpzKCJqZDxxVT4iKSxoaDpzKCJqZDx5RD4iKSxhSjpzKCJq -ZDx3Yj4iKSxnTjpzKCJqZDxuNj4iKSxiOnMoImpkPEA+IiksdDpzKCJqZDxLTj4iKSxkNDpzKCJqZDxx -VT8+IiksVDpzKCJ3ZSIpLGVIOnMoInZtIiksRTpzKCJjNSIpLGFVOnMoIlhqPEA+IiksYW06cygiVHo8 -QD4iKSxlbzpzKCJONTxHRCxAPiIpLGR6OnMoImhGIiksZjQ6cygiek08ajg+IiksYVM6cygiek08WjA8 -cVUsTWg/Pj4iKSxldzpzKCJ6TTxNaD4iKSxhOnMoInpNPHFVPiIpLGo6cygiek08QD4iKSxMOnMoInpN -PEtOPiIpLFc6cygiek08TWg/PiIpLEY6cygidTgiKSxlZTpzKCJOMzxxVSx6TTxqOD4+IiksaDY6cygi -WjA8cVUsTWg+IiksSTpzKCJaMDxxVSxxVT4iKSxmOnMoIlowPEAsQD4iKSxHOnMoIlowPHFVLE1oPz4i -KSxkdjpzKCJsSjxxVSxxVT4iKSxkbzpzKCJsSjxxVSxAPiIpLFY6cygiQWoiKSxkRTpzKCJlSCIpLEE6 -cygiS1YiKSxmNjpzKCJrRiIpLFA6cygiYzgiKSxLOnMoIk1oIikscDpzKCJ3ViIpLGdUOnMoIlZZIiks -cTpzKCJ0bjxaWj4iKSxmdjpzKCJ3TCIpLGN6OnMoImliIiksYU86cygibmQiKSxDOnMoIk9sPHFVPiIp -LGw6cygiR3oiKSxOOnMoInFVIiksZEc6cygicVUocVUpIiksZzc6cygiaGkiKSxmbzpzKCJHRCIpLGFX -OnMoInlZIiksZUs6cygiRXoiKSxhazpzKCJBUyIpLGdjOnMoIm42IiksYkk6cygia2QiKSxkdzpzKCJH -ajxxVSxxVT4iKSxkRDpzKCJpRCIpLGVKOnMoInU2PHFVPiIpLGc0OnMoIks1IiksY2k6cygiZEEiKSxn -MjpzKCJDbSIpLGJqOnMoIlpmPGZKPiIpLGg5OnMoIkNRIiksYWM6cygiZTciKSxROnMoIkNxPEFqPiIp -LFI6cygid3o8Y3Y+IiksYW86cygidnM8Zko+IiksYzpzKCJ2czxAPiIpLGZKOnMoInZzPEtOPiIpLGNy -OnMoIkpRIikseTpzKCJhMiIpLGFsOnMoImEyKE1oKSIpLGdSOnMoIkNQIiksejpzKCJAIiksZk86cygi -QCgpIiksdjpzKCJAKE1oKSIpLG06cygiQChNaCxHeikiKSxiVTpzKCJAKE9sPHFVPikiKSxkTzpzKCJA -KHFVKSIpLGI4OnMoIkAoQCxAKSIpLFM6cygiS04iKSxhdzpzKCIwJioiKSxfOnMoIk1oKiIpLGNoOnMo -IlBaPyIpLGJHOnMoImI4PGM4Pj8iKSxlczpzKCJFND8iKSxiTTpzKCJ6TTxAPj8iKSxncTpzKCJ6TTxN -aD8+PyIpLGM5OnMoIlowPHFVLEA+PyIpLGZuOnMoIlowPHFVLE1oPz4/IiksWDpzKCJNaD8iKSxkazpz -KCJxVT8iKSxlOnMoIkZlPEAsQD4/IiksZzpzKCJibj8iKSxiNzpzKCJhMihNaCk/IiksYnc6cygiQChl -YSk/IiksZlY6cygiTWg/KE1oPyxNaD8pPyIpLGRBOnMoIk1oPyhAKT8iKSxaOnMoIn4oKT8iKSxneDpz -KCJ+KHdWKT8iKSxkaTpzKCJaWiIpLEg6cygifiIpLE06cygifigpIiksZUE6cygifihxVSxxVSkiKSxj -QTpzKCJ+KHFVLEApIil9fSkoKTsoZnVuY3Rpb24gY29uc3RhbnRzKCl7dmFyIHM9aHVua0hlbHBlcnMu -bWFrZUNvbnN0TGlzdApCLnhuPUEuR2gucHJvdG90eXBlCkIuUlk9QS5RUC5wcm90b3R5cGUKQi5tSD1B -LmFlLnByb3RvdHlwZQpCLkJaPUEuVmIucHJvdG90eXBlCkIuRHQ9QS5mSi5wcm90b3R5cGUKQi5Paz1K -Lkd2LnByb3RvdHlwZQpCLk5tPUouamQucHJvdG90eXBlCkIuam49Si5MNy5wcm90b3R5cGUKQi5DRD1K -LnFJLnByb3RvdHlwZQpCLnhCPUouRHIucHJvdG90eXBlCkIuREc9Si5jNS5wcm90b3R5cGUKQi5VYj1K -Lk1GLnByb3RvdHlwZQpCLkV4PUEudTgucHJvdG90eXBlCkIuTkE9QS5WNi5wcm90b3R5cGUKQi50NT1B -LkJILnByb3RvdHlwZQpCLkx0PUEuU04ucHJvdG90eXBlCkIuWlE9Si5pQy5wcm90b3R5cGUKQi5JZT1B -LlRiLnByb3RvdHlwZQpCLnZCPUoua2QucHJvdG90eXBlCkIub2w9QS5LNS5wcm90b3R5cGUKQi55OD1u -ZXcgQS5VOCgpCkIuaDk9bmV3IEEuQ1YoKQpCLkd3PW5ldyBBLkZ1KEEuTjAoIkZ1PDAmPiIpKQpCLk80 -PWZ1bmN0aW9uIGdldFRhZ0ZhbGxiYWNrKG8pIHsKICB2YXIgcyA9IE9iamVjdC5wcm90b3R5cGUudG9T -dHJpbmcuY2FsbChvKTsKICByZXR1cm4gcy5zdWJzdHJpbmcoOCwgcy5sZW5ndGggLSAxKTsKfQpCLllx -PWZ1bmN0aW9uKCkgewogIHZhciB0b1N0cmluZ0Z1bmN0aW9uID0gT2JqZWN0LnByb3RvdHlwZS50b1N0 -cmluZzsKICBmdW5jdGlvbiBnZXRUYWcobykgewogICAgdmFyIHMgPSB0b1N0cmluZ0Z1bmN0aW9uLmNh -bGwobyk7CiAgICByZXR1cm4gcy5zdWJzdHJpbmcoOCwgcy5sZW5ndGggLSAxKTsKICB9CiAgZnVuY3Rp -b24gZ2V0VW5rbm93blRhZyhvYmplY3QsIHRhZykgewogICAgaWYgKC9eSFRNTFtBLVpdLipFbGVtZW50 -JC8udGVzdCh0YWcpKSB7CiAgICAgIHZhciBuYW1lID0gdG9TdHJpbmdGdW5jdGlvbi5jYWxsKG9iamVj -dCk7CiAgICAgIGlmIChuYW1lID09ICJbb2JqZWN0IE9iamVjdF0iKSByZXR1cm4gbnVsbDsKICAgICAg -cmV0dXJuICJIVE1MRWxlbWVudCI7CiAgICB9CiAgfQogIGZ1bmN0aW9uIGdldFVua25vd25UYWdHZW5l -cmljQnJvd3NlcihvYmplY3QsIHRhZykgewogICAgaWYgKHNlbGYuSFRNTEVsZW1lbnQgJiYgb2JqZWN0 -IGluc3RhbmNlb2YgSFRNTEVsZW1lbnQpIHJldHVybiAiSFRNTEVsZW1lbnQiOwogICAgcmV0dXJuIGdl -dFVua25vd25UYWcob2JqZWN0LCB0YWcpOwogIH0KICBmdW5jdGlvbiBwcm90b3R5cGVGb3JUYWcodGFn -KSB7CiAgICBpZiAodHlwZW9mIHdpbmRvdyA9PSAidW5kZWZpbmVkIikgcmV0dXJuIG51bGw7CiAgICBp -ZiAodHlwZW9mIHdpbmRvd1t0YWddID09ICJ1bmRlZmluZWQiKSByZXR1cm4gbnVsbDsKICAgIHZhciBj -b25zdHJ1Y3RvciA9IHdpbmRvd1t0YWddOwogICAgaWYgKHR5cGVvZiBjb25zdHJ1Y3RvciAhPSAiZnVu -Y3Rpb24iKSByZXR1cm4gbnVsbDsKICAgIHJldHVybiBjb25zdHJ1Y3Rvci5wcm90b3R5cGU7CiAgfQog -IGZ1bmN0aW9uIGRpc2NyaW1pbmF0b3IodGFnKSB7IHJldHVybiBudWxsOyB9CiAgdmFyIGlzQnJvd3Nl -ciA9IHR5cGVvZiBuYXZpZ2F0b3IgPT0gIm9iamVjdCI7CiAgcmV0dXJuIHsKICAgIGdldFRhZzogZ2V0 -VGFnLAogICAgZ2V0VW5rbm93blRhZzogaXNCcm93c2VyID8gZ2V0VW5rbm93blRhZ0dlbmVyaWNCcm93 -c2VyIDogZ2V0VW5rbm93blRhZywKICAgIHByb3RvdHlwZUZvclRhZzogcHJvdG90eXBlRm9yVGFnLAog -ICAgZGlzY3JpbWluYXRvcjogZGlzY3JpbWluYXRvciB9Owp9CkIud2I9ZnVuY3Rpb24oZ2V0VGFnRmFs -bGJhY2spIHsKICByZXR1cm4gZnVuY3Rpb24oaG9va3MpIHsKICAgIGlmICh0eXBlb2YgbmF2aWdhdG9y -ICE9ICJvYmplY3QiKSByZXR1cm4gaG9va3M7CiAgICB2YXIgdWEgPSBuYXZpZ2F0b3IudXNlckFnZW50 -OwogICAgaWYgKHVhLmluZGV4T2YoIkR1bXBSZW5kZXJUcmVlIikgPj0gMCkgcmV0dXJuIGhvb2tzOwog -ICAgaWYgKHVhLmluZGV4T2YoIkNocm9tZSIpID49IDApIHsKICAgICAgZnVuY3Rpb24gY29uZmlybShw -KSB7CiAgICAgICAgcmV0dXJuIHR5cGVvZiB3aW5kb3cgPT0gIm9iamVjdCIgJiYgd2luZG93W3BdICYm -IHdpbmRvd1twXS5uYW1lID09IHA7CiAgICAgIH0KICAgICAgaWYgKGNvbmZpcm0oIldpbmRvdyIpICYm -IGNvbmZpcm0oIkhUTUxFbGVtZW50IikpIHJldHVybiBob29rczsKICAgIH0KICAgIGhvb2tzLmdldFRh -ZyA9IGdldFRhZ0ZhbGxiYWNrOwogIH07Cn0KQi5LVT1mdW5jdGlvbihob29rcykgewogIGlmICh0eXBl -b2YgZGFydEV4cGVyaW1lbnRhbEZpeHVwR2V0VGFnICE9ICJmdW5jdGlvbiIpIHJldHVybiBob29rczsK -ICBob29rcy5nZXRUYWcgPSBkYXJ0RXhwZXJpbWVudGFsRml4dXBHZXRUYWcoaG9va3MuZ2V0VGFnKTsK -fQpCLmZRPWZ1bmN0aW9uKGhvb2tzKSB7CiAgdmFyIGdldFRhZyA9IGhvb2tzLmdldFRhZzsKICB2YXIg -cHJvdG90eXBlRm9yVGFnID0gaG9va3MucHJvdG90eXBlRm9yVGFnOwogIGZ1bmN0aW9uIGdldFRhZ0Zp -eGVkKG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWcobyk7CiAgICBpZiAodGFnID09ICJEb2N1bWVudCIp -IHsKICAgICAgaWYgKCEhby54bWxWZXJzaW9uKSByZXR1cm4gIiFEb2N1bWVudCI7CiAgICAgIHJldHVy -biAiIUhUTUxEb2N1bWVudCI7CiAgICB9CiAgICByZXR1cm4gdGFnOwogIH0KICBmdW5jdGlvbiBwcm90 -b3R5cGVGb3JUYWdGaXhlZCh0YWcpIHsKICAgIGlmICh0YWcgPT0gIkRvY3VtZW50IikgcmV0dXJuIG51 -bGw7CiAgICByZXR1cm4gcHJvdG90eXBlRm9yVGFnKHRhZyk7CiAgfQogIGhvb2tzLmdldFRhZyA9IGdl -dFRhZ0ZpeGVkOwogIGhvb2tzLnByb3RvdHlwZUZvclRhZyA9IHByb3RvdHlwZUZvclRhZ0ZpeGVkOwp9 -CkIuZGs9ZnVuY3Rpb24oaG9va3MpIHsKICB2YXIgdXNlckFnZW50ID0gdHlwZW9mIG5hdmlnYXRvciA9 -PSAib2JqZWN0IiA/IG5hdmlnYXRvci51c2VyQWdlbnQgOiAiIjsKICBpZiAodXNlckFnZW50LmluZGV4 -T2YoIkZpcmVmb3giKSA9PSAtMSkgcmV0dXJuIGhvb2tzOwogIHZhciBnZXRUYWcgPSBob29rcy5nZXRU -YWc7CiAgdmFyIHF1aWNrTWFwID0gewogICAgIkJlZm9yZVVubG9hZEV2ZW50IjogIkV2ZW50IiwKICAg -ICJEYXRhVHJhbnNmZXIiOiAiQ2xpcGJvYXJkIiwKICAgICJHZW9HZW9sb2NhdGlvbiI6ICJHZW9sb2Nh -dGlvbiIsCiAgICAiTG9jYXRpb24iOiAiIUxvY2F0aW9uIiwKICAgICJXb3JrZXJNZXNzYWdlRXZlbnQi -OiAiTWVzc2FnZUV2ZW50IiwKICAgICJYTUxEb2N1bWVudCI6ICIhRG9jdW1lbnQifTsKICBmdW5jdGlv -biBnZXRUYWdGaXJlZm94KG8pIHsKICAgIHZhciB0YWcgPSBnZXRUYWcobyk7CiAgICByZXR1cm4gcXVp -Y2tNYXBbdGFnXSB8fCB0YWc7CiAgfQogIGhvb2tzLmdldFRhZyA9IGdldFRhZ0ZpcmVmb3g7Cn0KQi54 -aT1mdW5jdGlvbihob29rcykgewogIHZhciB1c2VyQWdlbnQgPSB0eXBlb2YgbmF2aWdhdG9yID09ICJv -YmplY3QiID8gbmF2aWdhdG9yLnVzZXJBZ2VudCA6ICIiOwogIGlmICh1c2VyQWdlbnQuaW5kZXhPZigi -VHJpZGVudC8iKSA9PSAtMSkgcmV0dXJuIGhvb2tzOwogIHZhciBnZXRUYWcgPSBob29rcy5nZXRUYWc7 -CiAgdmFyIHF1aWNrTWFwID0gewogICAgIkJlZm9yZVVubG9hZEV2ZW50IjogIkV2ZW50IiwKICAgICJE -YXRhVHJhbnNmZXIiOiAiQ2xpcGJvYXJkIiwKICAgICJIVE1MRERFbGVtZW50IjogIkhUTUxFbGVtZW50 -IiwKICAgICJIVE1MRFRFbGVtZW50IjogIkhUTUxFbGVtZW50IiwKICAgICJIVE1MUGhyYXNlRWxlbWVu -dCI6ICJIVE1MRWxlbWVudCIsCiAgICAiUG9zaXRpb24iOiAiR2VvcG9zaXRpb24iCiAgfTsKICBmdW5j -dGlvbiBnZXRUYWdJRShvKSB7CiAgICB2YXIgdGFnID0gZ2V0VGFnKG8pOwogICAgdmFyIG5ld1RhZyA9 -IHF1aWNrTWFwW3RhZ107CiAgICBpZiAobmV3VGFnKSByZXR1cm4gbmV3VGFnOwogICAgaWYgKHRhZyA9 -PSAiT2JqZWN0IikgewogICAgICBpZiAod2luZG93LkRhdGFWaWV3ICYmIChvIGluc3RhbmNlb2Ygd2lu -ZG93LkRhdGFWaWV3KSkgcmV0dXJuICJEYXRhVmlldyI7CiAgICB9CiAgICByZXR1cm4gdGFnOwogIH0K -ICBmdW5jdGlvbiBwcm90b3R5cGVGb3JUYWdJRSh0YWcpIHsKICAgIHZhciBjb25zdHJ1Y3RvciA9IHdp -bmRvd1t0YWddOwogICAgaWYgKGNvbnN0cnVjdG9yID09IG51bGwpIHJldHVybiBudWxsOwogICAgcmV0 -dXJuIGNvbnN0cnVjdG9yLnByb3RvdHlwZTsKICB9CiAgaG9va3MuZ2V0VGFnID0gZ2V0VGFnSUU7CiAg -aG9va3MucHJvdG90eXBlRm9yVGFnID0gcHJvdG90eXBlRm9yVGFnSUU7Cn0KQi5pNz1mdW5jdGlvbiho -b29rcykgeyByZXR1cm4gaG9va3M7IH0KCkIuQ3Q9bmV3IEEuYnkoKQpCLkVxPW5ldyBBLms1KCkKQi56 -dD1uZXcgQS5IYigpCkIueE09bmV3IEEudTUoKQpCLlFrPW5ldyBBLkUzKCkKQi5Odj1uZXcgQS5rcigp -CkIuTlU9bmV3IEEuSmkoKQpCLnBkPW5ldyBBLlpkKCkKQi5BMz1uZXcgQS5NeChudWxsKQpCLm5YPW5l -dyBBLm9qKG51bGwpCkIuYWs9QS5RSShzKFswLDAsMzI3NzYsMzM3OTIsMSwxMDI0MCwwLDBdKSx0LnQp -CkIuY209QS5RSShzKFsiKjo6Y2xhc3MiLCIqOjpkaXIiLCIqOjpkcmFnZ2FibGUiLCIqOjpoaWRkZW4i -LCIqOjppZCIsIio6OmluZXJ0IiwiKjo6aXRlbXByb3AiLCIqOjppdGVtcmVmIiwiKjo6aXRlbXNjb3Bl -IiwiKjo6bGFuZyIsIio6OnNwZWxsY2hlY2siLCIqOjp0aXRsZSIsIio6OnRyYW5zbGF0ZSIsIkE6OmFj -Y2Vzc2tleSIsIkE6OmNvb3JkcyIsIkE6OmhyZWZsYW5nIiwiQTo6bmFtZSIsIkE6OnNoYXBlIiwiQTo6 -dGFiaW5kZXgiLCJBOjp0YXJnZXQiLCJBOjp0eXBlIiwiQVJFQTo6YWNjZXNza2V5IiwiQVJFQTo6YWx0 -IiwiQVJFQTo6Y29vcmRzIiwiQVJFQTo6bm9ocmVmIiwiQVJFQTo6c2hhcGUiLCJBUkVBOjp0YWJpbmRl -eCIsIkFSRUE6OnRhcmdldCIsIkFVRElPOjpjb250cm9scyIsIkFVRElPOjpsb29wIiwiQVVESU86Om1l -ZGlhZ3JvdXAiLCJBVURJTzo6bXV0ZWQiLCJBVURJTzo6cHJlbG9hZCIsIkJETzo6ZGlyIiwiQk9EWTo6 -YWxpbmsiLCJCT0RZOjpiZ2NvbG9yIiwiQk9EWTo6bGluayIsIkJPRFk6OnRleHQiLCJCT0RZOjp2bGlu -ayIsIkJSOjpjbGVhciIsIkJVVFRPTjo6YWNjZXNza2V5IiwiQlVUVE9OOjpkaXNhYmxlZCIsIkJVVFRP -Tjo6bmFtZSIsIkJVVFRPTjo6dGFiaW5kZXgiLCJCVVRUT046OnR5cGUiLCJCVVRUT046OnZhbHVlIiwi -Q0FOVkFTOjpoZWlnaHQiLCJDQU5WQVM6OndpZHRoIiwiQ0FQVElPTjo6YWxpZ24iLCJDT0w6OmFsaWdu -IiwiQ09MOjpjaGFyIiwiQ09MOjpjaGFyb2ZmIiwiQ09MOjpzcGFuIiwiQ09MOjp2YWxpZ24iLCJDT0w6 -OndpZHRoIiwiQ09MR1JPVVA6OmFsaWduIiwiQ09MR1JPVVA6OmNoYXIiLCJDT0xHUk9VUDo6Y2hhcm9m -ZiIsIkNPTEdST1VQOjpzcGFuIiwiQ09MR1JPVVA6OnZhbGlnbiIsIkNPTEdST1VQOjp3aWR0aCIsIkNP -TU1BTkQ6OmNoZWNrZWQiLCJDT01NQU5EOjpjb21tYW5kIiwiQ09NTUFORDo6ZGlzYWJsZWQiLCJDT01N -QU5EOjpsYWJlbCIsIkNPTU1BTkQ6OnJhZGlvZ3JvdXAiLCJDT01NQU5EOjp0eXBlIiwiREFUQTo6dmFs -dWUiLCJERUw6OmRhdGV0aW1lIiwiREVUQUlMUzo6b3BlbiIsIkRJUjo6Y29tcGFjdCIsIkRJVjo6YWxp -Z24iLCJETDo6Y29tcGFjdCIsIkZJRUxEU0VUOjpkaXNhYmxlZCIsIkZPTlQ6OmNvbG9yIiwiRk9OVDo6 -ZmFjZSIsIkZPTlQ6OnNpemUiLCJGT1JNOjphY2NlcHQiLCJGT1JNOjphdXRvY29tcGxldGUiLCJGT1JN -OjplbmN0eXBlIiwiRk9STTo6bWV0aG9kIiwiRk9STTo6bmFtZSIsIkZPUk06Om5vdmFsaWRhdGUiLCJG -T1JNOjp0YXJnZXQiLCJGUkFNRTo6bmFtZSIsIkgxOjphbGlnbiIsIkgyOjphbGlnbiIsIkgzOjphbGln -biIsIkg0OjphbGlnbiIsIkg1OjphbGlnbiIsIkg2OjphbGlnbiIsIkhSOjphbGlnbiIsIkhSOjpub3No -YWRlIiwiSFI6OnNpemUiLCJIUjo6d2lkdGgiLCJIVE1MOjp2ZXJzaW9uIiwiSUZSQU1FOjphbGlnbiIs -IklGUkFNRTo6ZnJhbWVib3JkZXIiLCJJRlJBTUU6OmhlaWdodCIsIklGUkFNRTo6bWFyZ2luaGVpZ2h0 -IiwiSUZSQU1FOjptYXJnaW53aWR0aCIsIklGUkFNRTo6d2lkdGgiLCJJTUc6OmFsaWduIiwiSU1HOjph -bHQiLCJJTUc6OmJvcmRlciIsIklNRzo6aGVpZ2h0IiwiSU1HOjpoc3BhY2UiLCJJTUc6OmlzbWFwIiwi -SU1HOjpuYW1lIiwiSU1HOjp1c2VtYXAiLCJJTUc6OnZzcGFjZSIsIklNRzo6d2lkdGgiLCJJTlBVVDo6 -YWNjZXB0IiwiSU5QVVQ6OmFjY2Vzc2tleSIsIklOUFVUOjphbGlnbiIsIklOUFVUOjphbHQiLCJJTlBV -VDo6YXV0b2NvbXBsZXRlIiwiSU5QVVQ6OmF1dG9mb2N1cyIsIklOUFVUOjpjaGVja2VkIiwiSU5QVVQ6 -OmRpc2FibGVkIiwiSU5QVVQ6OmlucHV0bW9kZSIsIklOUFVUOjppc21hcCIsIklOUFVUOjpsaXN0Iiwi -SU5QVVQ6Om1heCIsIklOUFVUOjptYXhsZW5ndGgiLCJJTlBVVDo6bWluIiwiSU5QVVQ6Om11bHRpcGxl -IiwiSU5QVVQ6Om5hbWUiLCJJTlBVVDo6cGxhY2Vob2xkZXIiLCJJTlBVVDo6cmVhZG9ubHkiLCJJTlBV -VDo6cmVxdWlyZWQiLCJJTlBVVDo6c2l6ZSIsIklOUFVUOjpzdGVwIiwiSU5QVVQ6OnRhYmluZGV4Iiwi -SU5QVVQ6OnR5cGUiLCJJTlBVVDo6dXNlbWFwIiwiSU5QVVQ6OnZhbHVlIiwiSU5TOjpkYXRldGltZSIs -IktFWUdFTjo6ZGlzYWJsZWQiLCJLRVlHRU46OmtleXR5cGUiLCJLRVlHRU46Om5hbWUiLCJMQUJFTDo6 -YWNjZXNza2V5IiwiTEFCRUw6OmZvciIsIkxFR0VORDo6YWNjZXNza2V5IiwiTEVHRU5EOjphbGlnbiIs -IkxJOjp0eXBlIiwiTEk6OnZhbHVlIiwiTElOSzo6c2l6ZXMiLCJNQVA6Om5hbWUiLCJNRU5VOjpjb21w -YWN0IiwiTUVOVTo6bGFiZWwiLCJNRU5VOjp0eXBlIiwiTUVURVI6OmhpZ2giLCJNRVRFUjo6bG93Iiwi -TUVURVI6Om1heCIsIk1FVEVSOjptaW4iLCJNRVRFUjo6dmFsdWUiLCJPQkpFQ1Q6OnR5cGVtdXN0bWF0 -Y2giLCJPTDo6Y29tcGFjdCIsIk9MOjpyZXZlcnNlZCIsIk9MOjpzdGFydCIsIk9MOjp0eXBlIiwiT1BU -R1JPVVA6OmRpc2FibGVkIiwiT1BUR1JPVVA6OmxhYmVsIiwiT1BUSU9OOjpkaXNhYmxlZCIsIk9QVElP -Tjo6bGFiZWwiLCJPUFRJT046OnNlbGVjdGVkIiwiT1BUSU9OOjp2YWx1ZSIsIk9VVFBVVDo6Zm9yIiwi -T1VUUFVUOjpuYW1lIiwiUDo6YWxpZ24iLCJQUkU6OndpZHRoIiwiUFJPR1JFU1M6Om1heCIsIlBST0dS -RVNTOjptaW4iLCJQUk9HUkVTUzo6dmFsdWUiLCJTRUxFQ1Q6OmF1dG9jb21wbGV0ZSIsIlNFTEVDVDo6 -ZGlzYWJsZWQiLCJTRUxFQ1Q6Om11bHRpcGxlIiwiU0VMRUNUOjpuYW1lIiwiU0VMRUNUOjpyZXF1aXJl -ZCIsIlNFTEVDVDo6c2l6ZSIsIlNFTEVDVDo6dGFiaW5kZXgiLCJTT1VSQ0U6OnR5cGUiLCJUQUJMRTo6 -YWxpZ24iLCJUQUJMRTo6Ymdjb2xvciIsIlRBQkxFOjpib3JkZXIiLCJUQUJMRTo6Y2VsbHBhZGRpbmci -LCJUQUJMRTo6Y2VsbHNwYWNpbmciLCJUQUJMRTo6ZnJhbWUiLCJUQUJMRTo6cnVsZXMiLCJUQUJMRTo6 -c3VtbWFyeSIsIlRBQkxFOjp3aWR0aCIsIlRCT0RZOjphbGlnbiIsIlRCT0RZOjpjaGFyIiwiVEJPRFk6 -OmNoYXJvZmYiLCJUQk9EWTo6dmFsaWduIiwiVEQ6OmFiYnIiLCJURDo6YWxpZ24iLCJURDo6YXhpcyIs -IlREOjpiZ2NvbG9yIiwiVEQ6OmNoYXIiLCJURDo6Y2hhcm9mZiIsIlREOjpjb2xzcGFuIiwiVEQ6Omhl -YWRlcnMiLCJURDo6aGVpZ2h0IiwiVEQ6Om5vd3JhcCIsIlREOjpyb3dzcGFuIiwiVEQ6OnNjb3BlIiwi -VEQ6OnZhbGlnbiIsIlREOjp3aWR0aCIsIlRFWFRBUkVBOjphY2Nlc3NrZXkiLCJURVhUQVJFQTo6YXV0 -b2NvbXBsZXRlIiwiVEVYVEFSRUE6OmNvbHMiLCJURVhUQVJFQTo6ZGlzYWJsZWQiLCJURVhUQVJFQTo6 -aW5wdXRtb2RlIiwiVEVYVEFSRUE6Om5hbWUiLCJURVhUQVJFQTo6cGxhY2Vob2xkZXIiLCJURVhUQVJF -QTo6cmVhZG9ubHkiLCJURVhUQVJFQTo6cmVxdWlyZWQiLCJURVhUQVJFQTo6cm93cyIsIlRFWFRBUkVB -Ojp0YWJpbmRleCIsIlRFWFRBUkVBOjp3cmFwIiwiVEZPT1Q6OmFsaWduIiwiVEZPT1Q6OmNoYXIiLCJU -Rk9PVDo6Y2hhcm9mZiIsIlRGT09UOjp2YWxpZ24iLCJUSDo6YWJiciIsIlRIOjphbGlnbiIsIlRIOjph -eGlzIiwiVEg6OmJnY29sb3IiLCJUSDo6Y2hhciIsIlRIOjpjaGFyb2ZmIiwiVEg6OmNvbHNwYW4iLCJU -SDo6aGVhZGVycyIsIlRIOjpoZWlnaHQiLCJUSDo6bm93cmFwIiwiVEg6OnJvd3NwYW4iLCJUSDo6c2Nv -cGUiLCJUSDo6dmFsaWduIiwiVEg6OndpZHRoIiwiVEhFQUQ6OmFsaWduIiwiVEhFQUQ6OmNoYXIiLCJU -SEVBRDo6Y2hhcm9mZiIsIlRIRUFEOjp2YWxpZ24iLCJUUjo6YWxpZ24iLCJUUjo6Ymdjb2xvciIsIlRS -OjpjaGFyIiwiVFI6OmNoYXJvZmYiLCJUUjo6dmFsaWduIiwiVFJBQ0s6OmRlZmF1bHQiLCJUUkFDSzo6 -a2luZCIsIlRSQUNLOjpsYWJlbCIsIlRSQUNLOjpzcmNsYW5nIiwiVUw6OmNvbXBhY3QiLCJVTDo6dHlw -ZSIsIlZJREVPOjpjb250cm9scyIsIlZJREVPOjpoZWlnaHQiLCJWSURFTzo6bG9vcCIsIlZJREVPOjpt -ZWRpYWdyb3VwIiwiVklERU86Om11dGVkIiwiVklERU86OnByZWxvYWQiLCJWSURFTzo6d2lkdGgiXSks -dC5zKQpCLlZDPUEuUUkocyhbMCwwLDY1NDkwLDQ1MDU1LDY1NTM1LDM0ODE1LDY1NTM0LDE4NDMxXSks -dC50KQpCLm1LPUEuUUkocyhbMCwwLDI2NjI0LDEwMjMsNjU1MzQsMjA0Nyw2NTUzNCwyMDQ3XSksdC50 -KQpCLmN3PW5ldyBBLkdiKDAsImFscmVhZHlNaWdyYXRlZCIpCkIuZGM9bmV3IEEuR2IoMSwiaW5kZXRl -cm1pbmF0ZSIpCkIuV0Q9bmV3IEEuR2IoMiwibWlncmF0aW5nIikKQi5Yaj1uZXcgQS5HYigzLCJvcHRp -bmdPdXQiKQpCLldHPUEuUUkocyhbQi5jdyxCLmRjLEIuV0QsQi5Yal0pLEEuTjAoImpkPEdiPiIpKQpC -LlNxPUEuUUkocyhbIkhFQUQiLCJBUkVBIiwiQkFTRSIsIkJBU0VGT05UIiwiQlIiLCJDT0wiLCJDT0xH -Uk9VUCIsIkVNQkVEIiwiRlJBTUUiLCJGUkFNRVNFVCIsIkhSIiwiSU1BR0UiLCJJTUciLCJJTlBVVCIs -IklTSU5ERVgiLCJMSU5LIiwiTUVUQSIsIlBBUkFNIiwiU09VUkNFIiwiU1RZTEUiLCJUSVRMRSIsIldC -UiJdKSx0LnMpCkIuZG49QS5RSShzKFtdKSxBLk4wKCJqZDxMTD4iKSkKQi54RD1BLlFJKHMoW10pLHQu -cykKQi5oVT1BLlFJKHMoW10pLHQuYikKQi5BZD1uZXcgQS5INygwLCJhZGROdWxsYWJsZUhpbnQiKQpC -Lm5lPW5ldyBBLkg3KDEsImFkZE5vbk51bGxhYmxlSGludCIpCkIubXk9bmV3IEEuSDcoMiwiY2hhbmdl -VG9OdWxsYWJsZUhpbnQiKQpCLnJ4PW5ldyBBLkg3KDMsImNoYW5nZVRvTm9uTnVsbGFibGVIaW50IikK -Qi53Vj1uZXcgQS5INyg0LCJyZW1vdmVOdWxsYWJsZUhpbnQiKQpCLmZSPW5ldyBBLkg3KDUsInJlbW92 -ZU5vbk51bGxhYmxlSGludCIpCkIuTW09QS5RSShzKFtCLkFkLEIubmUsQi5teSxCLnJ4LEIud1YsQi5m -Ul0pLEEuTjAoImpkPEg3PiIpKQpCLnRvPUEuUUkocyhbMCwwLDMyNzIyLDEyMjg3LDY1NTM0LDM0ODE1 -LDY1NTM0LDE4NDMxXSksdC50KQpCLkYzPUEuUUkocyhbMCwwLDI0NTc2LDEwMjMsNjU1MzQsMzQ4MTUs -NjU1MzQsMTg0MzFdKSx0LnQpCkIuZWE9QS5RSShzKFswLDAsMzI3NTQsMTEyNjMsNjU1MzQsMzQ4MTUs -NjU1MzQsMTg0MzFdKSx0LnQpCkIuWko9QS5RSShzKFswLDAsMzI3MjIsMTIyODcsNjU1MzUsMzQ4MTUs -NjU1MzQsMTg0MzFdKSx0LnQpCkIuV2Q9QS5RSShzKFswLDAsNjU0OTAsMTIyODcsNjU1MzUsMzQ4MTUs -NjU1MzQsMTg0MzFdKSx0LnQpCkIuUXg9QS5RSShzKFsiYmluZCIsImlmIiwicmVmIiwicmVwZWF0Iiwi -c3ludGF4Il0pLHQucykKQi5CST1BLlFJKHMoWyJBOjpocmVmIiwiQVJFQTo6aHJlZiIsIkJMT0NLUVVP -VEU6OmNpdGUiLCJCT0RZOjpiYWNrZ3JvdW5kIiwiQ09NTUFORDo6aWNvbiIsIkRFTDo6Y2l0ZSIsIkZP -Uk06OmFjdGlvbiIsIklNRzo6c3JjIiwiSU5QVVQ6OnNyYyIsIklOUzo6Y2l0ZSIsIlE6OmNpdGUiLCJW -SURFTzo6cG9zdGVyIl0pLHQucykKQi5EeD1uZXcgQS5MUCgwLHt9LEIueEQsQS5OMCgiTFA8cVUsek08 -ajg+PiIpKQpCLkNNPW5ldyBBLkxQKDAse30sQi54RCxBLk4wKCJMUDxxVSxxVT4iKSkKQi5pSD1BLlFJ -KHMoW10pLEEuTjAoImpkPEdEPiIpKQpCLldPPW5ldyBBLkxQKDAse30sQi5pSCxBLk4wKCJMUDxHRCxA -PiIpKQpCLlkyPW5ldyBBLk85KDAsImRpcmVjdG9yeSIpCkIucmY9bmV3IEEuTzkoMSwiZmlsZSIpCkIu -VGU9bmV3IEEud3YoImNhbGwiKQpCLkx5PUEueHEoIk1oIikKQi5vRT1uZXcgQS5HWSghMSkKQi53UT1u -ZXcgQS5GeShudWxsLDIpfSkoKTsoZnVuY3Rpb24gc3RhdGljRmllbGRzKCl7JC56bT1udWxsCiQueHU9 -bnVsbAokLmkwPW51bGwKJC5BbD1udWxsCiQuTkY9bnVsbAokLlRYPW51bGwKJC54Nz1udWxsCiQubnc9 -bnVsbAokLnZ2PW51bGwKJC5Cdj1udWxsCiQuUzY9bnVsbAokLms4PW51bGwKJC5tZz1udWxsCiQuVUQ9 -ITEKJC5YMz1CLk5VCiQuej1BLlFJKFtdLEEuTjAoImpkPE1oPiIpKQokLnhvPW51bGwKJC5CTz1udWxs -CiQubHQ9bnVsbAokLkVVPW51bGwKJC5vcj1BLkZsKHQuTix0LlkpCiQuSVI9bnVsbAokLkk2PW51bGwK -JC5GZj1udWxsfSkoKTsoZnVuY3Rpb24gbGF6eUluaXRpYWxpemVycygpe3ZhciBzPWh1bmtIZWxwZXJz -LmxhenlGaW5hbCxyPWh1bmtIZWxwZXJzLmxhenkKcygkLCJmYSIsInciLCgpPT5BLllnKCJfJGRhcnRf -ZGFydENsb3N1cmUiKSkKcygkLCJLcSIsIlNuIiwoKT0+QS5jTShBLlM3KHsKdG9TdHJpbmc6ZnVuY3Rp -b24oKXtyZXR1cm4iJHJlY2VpdmVyJCJ9fSkpKQpzKCQsIlluIiwibHEiLCgpPT5BLmNNKEEuUzcoeyRt -ZXRob2QkOm51bGwsCnRvU3RyaW5nOmZ1bmN0aW9uKCl7cmV0dXJuIiRyZWNlaXZlciQifX0pKSkKcygk -LCJSMSIsIk45IiwoKT0+QS5jTShBLlM3KG51bGwpKSkKcygkLCJmTiIsImlJIiwoKT0+QS5jTShmdW5j -dGlvbigpe3ZhciAkYXJndW1lbnRzRXhwciQ9IiRhcmd1bWVudHMkIgp0cnl7bnVsbC4kbWV0aG9kJCgk -YXJndW1lbnRzRXhwciQpfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKSkKcygkLCJxaSIsIlVO -IiwoKT0+QS5jTShBLlM3KHZvaWQgMCkpKQpzKCQsInB2IiwiWmgiLCgpPT5BLmNNKGZ1bmN0aW9uKCl7 -dmFyICRhcmd1bWVudHNFeHByJD0iJGFyZ3VtZW50cyQiCnRyeXsodm9pZCAwKS4kbWV0aG9kJCgkYXJn -dW1lbnRzRXhwciQpfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKSkKcygkLCJrcSIsInJOIiwo -KT0+QS5jTShBLk1qKG51bGwpKSkKcygkLCJ0dCIsImMzIiwoKT0+QS5jTShmdW5jdGlvbigpe3RyeXtu -dWxsLiRtZXRob2QkfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKSkKcygkLCJkdCIsIkhLIiwo -KT0+QS5jTShBLk1qKHZvaWQgMCkpKQpzKCQsIkE3IiwicjEiLCgpPT5BLmNNKGZ1bmN0aW9uKCl7dHJ5 -eyh2b2lkIDApLiRtZXRob2QkfWNhdGNoKHEpe3JldHVybiBxLm1lc3NhZ2V9fSgpKSkKcygkLCJXYyIs -InV0IiwoKT0+QS54ZygpKQpzKCQsImtoIiwicmYiLCgpPT5uZXcgQS54cigpLiQwKCkpCnMoJCwiZEgi -LCJIRyIsKCk9Pm5ldyBBLk56KCkuJDAoKSkKcygkLCJoaiIsIlY3IiwoKT0+bmV3IEludDhBcnJheShB -LlhGKEEuUUkoWy0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0y -LC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0yLC0y -LC0xLC0yLC0yLC0yLC0yLC0yLDYyLC0yLDYyLC0yLDYzLDUyLDUzLDU0LDU1LDU2LDU3LDU4LDU5LDYw -LDYxLC0yLC0yLC0yLC0xLC0yLC0yLC0yLDAsMSwyLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQs -MTUsMTYsMTcsMTgsMTksMjAsMjEsMjIsMjMsMjQsMjUsLTIsLTIsLTIsLTIsNjMsLTIsMjYsMjcsMjgs -MjksMzAsMzEsMzIsMzMsMzQsMzUsMzYsMzcsMzgsMzksNDAsNDEsNDIsNDMsNDQsNDUsNDYsNDcsNDgs -NDksNTAsNTEsLTIsLTIsLTIsLTIsLTJdLHQudCkpKSkKcygkLCJZZSIsIndRIiwoKT0+dHlwZW9mIHBy -b2Nlc3MhPSJ1bmRlZmluZWQiJiZPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwocHJvY2Vzcyk9 -PSJbb2JqZWN0IHByb2Nlc3NdIiYmcHJvY2Vzcy5wbGF0Zm9ybT09IndpbjMyIikKcygkLCJtZiIsIno0 -IiwoKT0+QS5udSgiXltcXC1cXC4wLTlBLVpfYS16fl0qJCIpKQpzKCQsIlgwIiwidDgiLCgpPT5BLkNV -KEIuTHkpKQpzKCQsIk9RIiwidloiLCgpPT5BLnV4KCkpCnMoJCwiU0MiLCJBTiIsKCk9PkEudE0oWyJB -IiwiQUJCUiIsIkFDUk9OWU0iLCJBRERSRVNTIiwiQVJFQSIsIkFSVElDTEUiLCJBU0lERSIsIkFVRElP -IiwiQiIsIkJESSIsIkJETyIsIkJJRyIsIkJMT0NLUVVPVEUiLCJCUiIsIkJVVFRPTiIsIkNBTlZBUyIs -IkNBUFRJT04iLCJDRU5URVIiLCJDSVRFIiwiQ09ERSIsIkNPTCIsIkNPTEdST1VQIiwiQ09NTUFORCIs -IkRBVEEiLCJEQVRBTElTVCIsIkREIiwiREVMIiwiREVUQUlMUyIsIkRGTiIsIkRJUiIsIkRJViIsIkRM -IiwiRFQiLCJFTSIsIkZJRUxEU0VUIiwiRklHQ0FQVElPTiIsIkZJR1VSRSIsIkZPTlQiLCJGT09URVIi -LCJGT1JNIiwiSDEiLCJIMiIsIkgzIiwiSDQiLCJINSIsIkg2IiwiSEVBREVSIiwiSEdST1VQIiwiSFIi -LCJJIiwiSUZSQU1FIiwiSU1HIiwiSU5QVVQiLCJJTlMiLCJLQkQiLCJMQUJFTCIsIkxFR0VORCIsIkxJ -IiwiTUFQIiwiTUFSSyIsIk1FTlUiLCJNRVRFUiIsIk5BViIsIk5PQlIiLCJPTCIsIk9QVEdST1VQIiwi -T1BUSU9OIiwiT1VUUFVUIiwiUCIsIlBSRSIsIlBST0dSRVNTIiwiUSIsIlMiLCJTQU1QIiwiU0VDVElP -TiIsIlNFTEVDVCIsIlNNQUxMIiwiU09VUkNFIiwiU1BBTiIsIlNUUklLRSIsIlNUUk9ORyIsIlNVQiIs -IlNVTU1BUlkiLCJTVVAiLCJUQUJMRSIsIlRCT0RZIiwiVEQiLCJURVhUQVJFQSIsIlRGT09UIiwiVEgi -LCJUSEVBRCIsIlRJTUUiLCJUUiIsIlRSQUNLIiwiVFQiLCJVIiwiVUwiLCJWQVIiLCJWSURFTyIsIldC -UiJdLHQuTikpCnMoJCwiWDQiLCJoRyIsKCk9PkEubnUoIl5cXFMrJCIpKQpzKCQsIndPIiwib3ciLCgp -PT5BLk5EKHNlbGYpKQpzKCQsImt0IiwiUjgiLCgpPT5BLllnKCJfJGRhcnRfZGFydE9iamVjdCIpKQpz -KCQsImZLIiwia0kiLCgpPT5mdW5jdGlvbiBEYXJ0T2JqZWN0KGEpe3RoaXMubz1hfSkKcygkLCJxdCIs -InpCIiwoKT0+bmV3IEEubVEoKSkKcygkLCJDayIsIlVFIiwoKT0+QS5oSyhCLm9sLmdtVyhBLngzKCkp -LmhyZWYpLmdoWSgpLnEoMCwiYXV0aFRva2VuIikpCnMoJCwiaFQiLCJ5UCIsKCk9PkEuWnIoKS5xdWVy -eVNlbGVjdG9yKCIuZWRpdC1saXN0IC5wYW5lbC1jb250ZW50IikpCnMoJCwiYVUiLCJEWiIsKCk9PkEu -WnIoKS5xdWVyeVNlbGVjdG9yKCIuZWRpdC1saXN0IC5wYW5lbC1oZWFkaW5nIikpCnMoJCwiVzYiLCJo -TCIsKCk9PkEuWnIoKS5xdWVyeVNlbGVjdG9yKCIuZWRpdC1wYW5lbCAucGFuZWwtY29udGVudCIpKQpz -KCQsIlRSIiwiRFciLCgpPT5BLlpyKCkucXVlcnlTZWxlY3RvcigiZm9vdGVyIikpCnMoJCwiRVkiLCJm -aSIsKCk9PkEuWnIoKS5xdWVyeVNlbGVjdG9yKCJoZWFkZXIiKSkKcygkLCJiQSIsImMwIiwoKT0+QS5a -cigpLnF1ZXJ5U2VsZWN0b3IoIiNtaWdyYXRlLXVuaXQtc3RhdHVzLWljb24iKSkKcygkLCJ0MCIsImJO -IiwoKT0+QS5acigpLnF1ZXJ5U2VsZWN0b3IoIiNtaWdyYXRlLXVuaXQtc3RhdHVzLWljb24tbGFiZWwi -KSkKcygkLCJhdiIsIkQ5IiwoKT0+QS5acigpLnF1ZXJ5U2VsZWN0b3IoIiN1bml0LW5hbWUiKSkKcigk -LCJmZSIsIktHIiwoKT0+bmV3IEEuWEEoKSkKcygkLCJlbyIsIm5VIiwoKT0+bmV3IEEubEkoQS5OMCgi -ZnYiKS5hKCQuSGsoKSkpKQpzKCQsInlyIiwiYkQiLCgpPT5uZXcgQS5PRihBLm51KCIvIiksQS5udSgi -W14vXSQiKSxBLm51KCJeLyIpKSkKcygkLCJNayIsIktrIiwoKT0+bmV3IEEuSVYoQS5udSgiWy9cXFxc -XSIpLEEubnUoIlteL1xcXFxdJCIpLEEubnUoIl4oXFxcXFxcXFxbXlxcXFxdK1xcXFxbXlxcXFwvXSt8 -W2EtekEtWl06Wy9cXFxcXSkiKSxBLm51KCJeWy9cXFxcXSg/IVsvXFxcXF0pIikpKQpzKCQsImFrIiwi -RWIiLCgpPT5uZXcgQS5ydShBLm51KCIvIiksQS5udSgiKF5bYS16QS1aXVstKy5hLXpBLVpcXGRdKjov -L3xbXi9dKSQiKSxBLm51KCJbYS16QS1aXVstKy5hLXpBLVpcXGRdKjovL1teL10qIiksQS5udSgiXi8i -KSkpCnMoJCwibHMiLCJIayIsKCk9PkEuUmgoKSl9KSgpOyhmdW5jdGlvbiBuYXRpdmVTdXBwb3J0KCl7 -IWZ1bmN0aW9uKCl7dmFyIHM9ZnVuY3Rpb24oYSl7dmFyIG09e30KbVthXT0xCnJldHVybiBPYmplY3Qu -a2V5cyhodW5rSGVscGVycy5jb252ZXJ0VG9GYXN0T2JqZWN0KG0pKVswXX0Kdi5nZXRJc29sYXRlVGFn -PWZ1bmN0aW9uKGEpe3JldHVybiBzKCJfX19kYXJ0XyIrYSt2Lmlzb2xhdGVUYWcpfQp2YXIgcj0iX19f -ZGFydF9pc29sYXRlX3RhZ3NfIgp2YXIgcT1PYmplY3Rbcl18fChPYmplY3Rbcl09T2JqZWN0LmNyZWF0 -ZShudWxsKSkKdmFyIHA9Il9aeFl4WCIKZm9yKHZhciBvPTA7O28rKyl7dmFyIG49cyhwKyJfIitvKyJf -IikKaWYoIShuIGluIHEpKXtxW25dPTEKdi5pc29sYXRlVGFnPW4KYnJlYWt9fXYuZGlzcGF0Y2hQcm9w -ZXJ0eU5hbWU9di5nZXRJc29sYXRlVGFnKCJkaXNwYXRjaF9yZWNvcmQiKX0oKQpodW5rSGVscGVycy5z -ZXRPclVwZGF0ZUludGVyY2VwdG9yc0J5VGFnKHtET01FcnJvcjpKLk1GLE1lZGlhRXJyb3I6Si5NRixO -YXZpZ2F0b3I6Si5NRixOYXZpZ2F0b3JDb25jdXJyZW50SGFyZHdhcmU6Si5NRixOYXZpZ2F0b3JVc2Vy -TWVkaWFFcnJvcjpKLk1GLE92ZXJjb25zdHJhaW5lZEVycm9yOkouTUYsUG9zaXRpb25FcnJvcjpKLk1G -LEdlb2xvY2F0aW9uUG9zaXRpb25FcnJvcjpKLk1GLFJhbmdlOkouTUYsRGF0YVZpZXc6QS5lSCxBcnJh -eUJ1ZmZlclZpZXc6QS5lSCxGbG9hdDMyQXJyYXk6QS5EZyxGbG9hdDY0QXJyYXk6QS5EZyxJbnQxNkFy -cmF5OkEueGosSW50MzJBcnJheTpBLmRFLEludDhBcnJheTpBLlpBLFVpbnQxNkFycmF5OkEuZFQsVWlu -dDMyQXJyYXk6QS5QcSxVaW50OENsYW1wZWRBcnJheTpBLmVFLENhbnZhc1BpeGVsQXJyYXk6QS5lRSxV -aW50OEFycmF5OkEuVjYsSFRNTEF1ZGlvRWxlbWVudDpBLnFFLEhUTUxCUkVsZW1lbnQ6QS5xRSxIVE1M -QnV0dG9uRWxlbWVudDpBLnFFLEhUTUxDYW52YXNFbGVtZW50OkEucUUsSFRNTENvbnRlbnRFbGVtZW50 -OkEucUUsSFRNTERMaXN0RWxlbWVudDpBLnFFLEhUTUxEYXRhRWxlbWVudDpBLnFFLEhUTUxEYXRhTGlz -dEVsZW1lbnQ6QS5xRSxIVE1MRGV0YWlsc0VsZW1lbnQ6QS5xRSxIVE1MRGlhbG9nRWxlbWVudDpBLnFF -LEhUTUxEaXZFbGVtZW50OkEucUUsSFRNTEVtYmVkRWxlbWVudDpBLnFFLEhUTUxGaWVsZFNldEVsZW1l -bnQ6QS5xRSxIVE1MSFJFbGVtZW50OkEucUUsSFRNTEhlYWRFbGVtZW50OkEucUUsSFRNTEhlYWRpbmdF -bGVtZW50OkEucUUsSFRNTEh0bWxFbGVtZW50OkEucUUsSFRNTElGcmFtZUVsZW1lbnQ6QS5xRSxIVE1M -SW1hZ2VFbGVtZW50OkEucUUsSFRNTElucHV0RWxlbWVudDpBLnFFLEhUTUxMSUVsZW1lbnQ6QS5xRSxI -VE1MTGFiZWxFbGVtZW50OkEucUUsSFRNTExlZ2VuZEVsZW1lbnQ6QS5xRSxIVE1MTGlua0VsZW1lbnQ6 -QS5xRSxIVE1MTWFwRWxlbWVudDpBLnFFLEhUTUxNZWRpYUVsZW1lbnQ6QS5xRSxIVE1MTWVudUVsZW1l -bnQ6QS5xRSxIVE1MTWV0YUVsZW1lbnQ6QS5xRSxIVE1MTWV0ZXJFbGVtZW50OkEucUUsSFRNTE1vZEVs -ZW1lbnQ6QS5xRSxIVE1MT0xpc3RFbGVtZW50OkEucUUsSFRNTE9iamVjdEVsZW1lbnQ6QS5xRSxIVE1M -T3B0R3JvdXBFbGVtZW50OkEucUUsSFRNTE9wdGlvbkVsZW1lbnQ6QS5xRSxIVE1MT3V0cHV0RWxlbWVu -dDpBLnFFLEhUTUxQYXJhbUVsZW1lbnQ6QS5xRSxIVE1MUGljdHVyZUVsZW1lbnQ6QS5xRSxIVE1MUHJl -RWxlbWVudDpBLnFFLEhUTUxQcm9ncmVzc0VsZW1lbnQ6QS5xRSxIVE1MUXVvdGVFbGVtZW50OkEucUUs -SFRNTFNjcmlwdEVsZW1lbnQ6QS5xRSxIVE1MU2hhZG93RWxlbWVudDpBLnFFLEhUTUxTbG90RWxlbWVu -dDpBLnFFLEhUTUxTb3VyY2VFbGVtZW50OkEucUUsSFRNTFNwYW5FbGVtZW50OkEucUUsSFRNTFN0eWxl -RWxlbWVudDpBLnFFLEhUTUxUYWJsZUNhcHRpb25FbGVtZW50OkEucUUsSFRNTFRhYmxlQ2VsbEVsZW1l -bnQ6QS5xRSxIVE1MVGFibGVEYXRhQ2VsbEVsZW1lbnQ6QS5xRSxIVE1MVGFibGVIZWFkZXJDZWxsRWxl -bWVudDpBLnFFLEhUTUxUYWJsZUNvbEVsZW1lbnQ6QS5xRSxIVE1MVGV4dEFyZWFFbGVtZW50OkEucUUs -SFRNTFRpbWVFbGVtZW50OkEucUUsSFRNTFRpdGxlRWxlbWVudDpBLnFFLEhUTUxUcmFja0VsZW1lbnQ6 -QS5xRSxIVE1MVUxpc3RFbGVtZW50OkEucUUsSFRNTFVua25vd25FbGVtZW50OkEucUUsSFRNTFZpZGVv -RWxlbWVudDpBLnFFLEhUTUxEaXJlY3RvcnlFbGVtZW50OkEucUUsSFRNTEZvbnRFbGVtZW50OkEucUUs -SFRNTEZyYW1lRWxlbWVudDpBLnFFLEhUTUxGcmFtZVNldEVsZW1lbnQ6QS5xRSxIVE1MTWFycXVlZUVs -ZW1lbnQ6QS5xRSxIVE1MRWxlbWVudDpBLnFFLEhUTUxBbmNob3JFbGVtZW50OkEuR2gsSFRNTEFyZWFF -bGVtZW50OkEuZlksSFRNTEJhc2VFbGVtZW50OkEuclosQmxvYjpBLkF6LEhUTUxCb2R5RWxlbWVudDpB -LlFQLENEQVRBU2VjdGlvbjpBLm54LENoYXJhY3RlckRhdGE6QS5ueCxDb21tZW50OkEubngsUHJvY2Vz -c2luZ0luc3RydWN0aW9uOkEubngsVGV4dDpBLm54LENTU1N0eWxlRGVjbGFyYXRpb246QS5vSixNU1N0 -eWxlQ1NTUHJvcGVydGllczpBLm9KLENTUzJQcm9wZXJ0aWVzOkEub0osWE1MRG9jdW1lbnQ6QS5RRixE -b2N1bWVudDpBLlFGLERPTUV4Y2VwdGlvbjpBLk5oLERPTUltcGxlbWVudGF0aW9uOkEuYWUsRE9NUmVj -dFJlYWRPbmx5OkEuSUIsRE9NVG9rZW5MaXN0OkEubjcsTWF0aE1MRWxlbWVudDpBLmN2LEVsZW1lbnQ6 -QS5jdixBYm9ydFBheW1lbnRFdmVudDpBLmVhLEFuaW1hdGlvbkV2ZW50OkEuZWEsQW5pbWF0aW9uUGxh -eWJhY2tFdmVudDpBLmVhLEFwcGxpY2F0aW9uQ2FjaGVFcnJvckV2ZW50OkEuZWEsQmFja2dyb3VuZEZl -dGNoQ2xpY2tFdmVudDpBLmVhLEJhY2tncm91bmRGZXRjaEV2ZW50OkEuZWEsQmFja2dyb3VuZEZldGNo -RmFpbEV2ZW50OkEuZWEsQmFja2dyb3VuZEZldGNoZWRFdmVudDpBLmVhLEJlZm9yZUluc3RhbGxQcm9t -cHRFdmVudDpBLmVhLEJlZm9yZVVubG9hZEV2ZW50OkEuZWEsQmxvYkV2ZW50OkEuZWEsQ2FuTWFrZVBh -eW1lbnRFdmVudDpBLmVhLENsaXBib2FyZEV2ZW50OkEuZWEsQ2xvc2VFdmVudDpBLmVhLEN1c3RvbUV2 -ZW50OkEuZWEsRGV2aWNlTW90aW9uRXZlbnQ6QS5lYSxEZXZpY2VPcmllbnRhdGlvbkV2ZW50OkEuZWEs -RXJyb3JFdmVudDpBLmVhLEV4dGVuZGFibGVFdmVudDpBLmVhLEV4dGVuZGFibGVNZXNzYWdlRXZlbnQ6 -QS5lYSxGZXRjaEV2ZW50OkEuZWEsRm9udEZhY2VTZXRMb2FkRXZlbnQ6QS5lYSxGb3JlaWduRmV0Y2hF -dmVudDpBLmVhLEdhbWVwYWRFdmVudDpBLmVhLEhhc2hDaGFuZ2VFdmVudDpBLmVhLEluc3RhbGxFdmVu -dDpBLmVhLE1lZGlhRW5jcnlwdGVkRXZlbnQ6QS5lYSxNZWRpYUtleU1lc3NhZ2VFdmVudDpBLmVhLE1l -ZGlhUXVlcnlMaXN0RXZlbnQ6QS5lYSxNZWRpYVN0cmVhbUV2ZW50OkEuZWEsTWVkaWFTdHJlYW1UcmFj -a0V2ZW50OkEuZWEsTWVzc2FnZUV2ZW50OkEuZWEsTUlESUNvbm5lY3Rpb25FdmVudDpBLmVhLE1JRElN -ZXNzYWdlRXZlbnQ6QS5lYSxNdXRhdGlvbkV2ZW50OkEuZWEsTm90aWZpY2F0aW9uRXZlbnQ6QS5lYSxQ -YWdlVHJhbnNpdGlvbkV2ZW50OkEuZWEsUGF5bWVudFJlcXVlc3RFdmVudDpBLmVhLFBheW1lbnRSZXF1 -ZXN0VXBkYXRlRXZlbnQ6QS5lYSxQb3BTdGF0ZUV2ZW50OkEuZWEsUHJlc2VudGF0aW9uQ29ubmVjdGlv -bkF2YWlsYWJsZUV2ZW50OkEuZWEsUHJlc2VudGF0aW9uQ29ubmVjdGlvbkNsb3NlRXZlbnQ6QS5lYSxQ -cm9taXNlUmVqZWN0aW9uRXZlbnQ6QS5lYSxQdXNoRXZlbnQ6QS5lYSxSVENEYXRhQ2hhbm5lbEV2ZW50 -OkEuZWEsUlRDRFRNRlRvbmVDaGFuZ2VFdmVudDpBLmVhLFJUQ1BlZXJDb25uZWN0aW9uSWNlRXZlbnQ6 -QS5lYSxSVENUcmFja0V2ZW50OkEuZWEsU2VjdXJpdHlQb2xpY3lWaW9sYXRpb25FdmVudDpBLmVhLFNl -bnNvckVycm9yRXZlbnQ6QS5lYSxTcGVlY2hSZWNvZ25pdGlvbkVycm9yOkEuZWEsU3BlZWNoUmVjb2du -aXRpb25FdmVudDpBLmVhLFNwZWVjaFN5bnRoZXNpc0V2ZW50OkEuZWEsU3RvcmFnZUV2ZW50OkEuZWEs -U3luY0V2ZW50OkEuZWEsVHJhY2tFdmVudDpBLmVhLFRyYW5zaXRpb25FdmVudDpBLmVhLFdlYktpdFRy -YW5zaXRpb25FdmVudDpBLmVhLFZSRGV2aWNlRXZlbnQ6QS5lYSxWUkRpc3BsYXlFdmVudDpBLmVhLFZS -U2Vzc2lvbkV2ZW50OkEuZWEsTW9qb0ludGVyZmFjZVJlcXVlc3RFdmVudDpBLmVhLFVTQkNvbm5lY3Rp -b25FdmVudDpBLmVhLElEQlZlcnNpb25DaGFuZ2VFdmVudDpBLmVhLEF1ZGlvUHJvY2Vzc2luZ0V2ZW50 -OkEuZWEsT2ZmbGluZUF1ZGlvQ29tcGxldGlvbkV2ZW50OkEuZWEsV2ViR0xDb250ZXh0RXZlbnQ6QS5l -YSxFdmVudDpBLmVhLElucHV0RXZlbnQ6QS5lYSxTdWJtaXRFdmVudDpBLmVhLEV2ZW50VGFyZ2V0OkEu -UFosRmlsZTpBLmhILEhUTUxGb3JtRWxlbWVudDpBLmg0LEhpc3Rvcnk6QS5icixIVE1MRG9jdW1lbnQ6 -QS5WYixYTUxIdHRwUmVxdWVzdDpBLmZKLFhNTEh0dHBSZXF1ZXN0RXZlbnRUYXJnZXQ6QS53YSxJbWFn -ZURhdGE6QS5TZyxMb2NhdGlvbjpBLnU4LE1vdXNlRXZlbnQ6QS5BaixEcmFnRXZlbnQ6QS5BaixQb2lu -dGVyRXZlbnQ6QS5BaixXaGVlbEV2ZW50OkEuQWosRG9jdW1lbnRGcmFnbWVudDpBLktWLFNoYWRvd1Jv -b3Q6QS5LVixEb2N1bWVudFR5cGU6QS5LVixOb2RlOkEuS1YsTm9kZUxpc3Q6QS5CSCxSYWRpb05vZGVM -aXN0OkEuQkgsSFRNTFBhcmFncmFwaEVsZW1lbnQ6QS5TTixQcm9ncmVzc0V2ZW50OkEud1YsUmVzb3Vy -Y2VQcm9ncmVzc0V2ZW50OkEud1YsSFRNTFNlbGVjdEVsZW1lbnQ6QS5scCxIVE1MVGFibGVFbGVtZW50 -OkEuVGIsSFRNTFRhYmxlUm93RWxlbWVudDpBLkl2LEhUTUxUYWJsZVNlY3Rpb25FbGVtZW50OkEuV1As -SFRNTFRlbXBsYXRlRWxlbWVudDpBLnlZLENvbXBvc2l0aW9uRXZlbnQ6QS53NixGb2N1c0V2ZW50OkEu -dzYsS2V5Ym9hcmRFdmVudDpBLnc2LFRleHRFdmVudDpBLnc2LFRvdWNoRXZlbnQ6QS53NixVSUV2ZW50 -OkEudzYsV2luZG93OkEuSzUsRE9NV2luZG93OkEuSzUsRGVkaWNhdGVkV29ya2VyR2xvYmFsU2NvcGU6 -QS5DbSxTZXJ2aWNlV29ya2VyR2xvYmFsU2NvcGU6QS5DbSxTaGFyZWRXb3JrZXJHbG9iYWxTY29wZTpB -LkNtLFdvcmtlckdsb2JhbFNjb3BlOkEuQ20sQXR0cjpBLkNRLENsaWVudFJlY3Q6QS53NCxET01SZWN0 -OkEudzQsTmFtZWROb2RlTWFwOkEucmgsTW96TmFtZWRBdHRyTWFwOkEucmgsSURCS2V5UmFuZ2U6QS5o -RixTVkdTY3JpcHRFbGVtZW50OkEubmQsU1ZHQUVsZW1lbnQ6QS5oaSxTVkdBbmltYXRlRWxlbWVudDpB -LmhpLFNWR0FuaW1hdGVNb3Rpb25FbGVtZW50OkEuaGksU1ZHQW5pbWF0ZVRyYW5zZm9ybUVsZW1lbnQ6 -QS5oaSxTVkdBbmltYXRpb25FbGVtZW50OkEuaGksU1ZHQ2lyY2xlRWxlbWVudDpBLmhpLFNWR0NsaXBQ -YXRoRWxlbWVudDpBLmhpLFNWR0RlZnNFbGVtZW50OkEuaGksU1ZHRGVzY0VsZW1lbnQ6QS5oaSxTVkdE -aXNjYXJkRWxlbWVudDpBLmhpLFNWR0VsbGlwc2VFbGVtZW50OkEuaGksU1ZHRkVCbGVuZEVsZW1lbnQ6 -QS5oaSxTVkdGRUNvbG9yTWF0cml4RWxlbWVudDpBLmhpLFNWR0ZFQ29tcG9uZW50VHJhbnNmZXJFbGVt -ZW50OkEuaGksU1ZHRkVDb21wb3NpdGVFbGVtZW50OkEuaGksU1ZHRkVDb252b2x2ZU1hdHJpeEVsZW1l -bnQ6QS5oaSxTVkdGRURpZmZ1c2VMaWdodGluZ0VsZW1lbnQ6QS5oaSxTVkdGRURpc3BsYWNlbWVudE1h -cEVsZW1lbnQ6QS5oaSxTVkdGRURpc3RhbnRMaWdodEVsZW1lbnQ6QS5oaSxTVkdGRUZsb29kRWxlbWVu -dDpBLmhpLFNWR0ZFRnVuY0FFbGVtZW50OkEuaGksU1ZHRkVGdW5jQkVsZW1lbnQ6QS5oaSxTVkdGRUZ1 -bmNHRWxlbWVudDpBLmhpLFNWR0ZFRnVuY1JFbGVtZW50OkEuaGksU1ZHRkVHYXVzc2lhbkJsdXJFbGVt -ZW50OkEuaGksU1ZHRkVJbWFnZUVsZW1lbnQ6QS5oaSxTVkdGRU1lcmdlRWxlbWVudDpBLmhpLFNWR0ZF -TWVyZ2VOb2RlRWxlbWVudDpBLmhpLFNWR0ZFTW9ycGhvbG9neUVsZW1lbnQ6QS5oaSxTVkdGRU9mZnNl -dEVsZW1lbnQ6QS5oaSxTVkdGRVBvaW50TGlnaHRFbGVtZW50OkEuaGksU1ZHRkVTcGVjdWxhckxpZ2h0 -aW5nRWxlbWVudDpBLmhpLFNWR0ZFU3BvdExpZ2h0RWxlbWVudDpBLmhpLFNWR0ZFVGlsZUVsZW1lbnQ6 -QS5oaSxTVkdGRVR1cmJ1bGVuY2VFbGVtZW50OkEuaGksU1ZHRmlsdGVyRWxlbWVudDpBLmhpLFNWR0Zv -cmVpZ25PYmplY3RFbGVtZW50OkEuaGksU1ZHR0VsZW1lbnQ6QS5oaSxTVkdHZW9tZXRyeUVsZW1lbnQ6 -QS5oaSxTVkdHcmFwaGljc0VsZW1lbnQ6QS5oaSxTVkdJbWFnZUVsZW1lbnQ6QS5oaSxTVkdMaW5lRWxl -bWVudDpBLmhpLFNWR0xpbmVhckdyYWRpZW50RWxlbWVudDpBLmhpLFNWR01hcmtlckVsZW1lbnQ6QS5o -aSxTVkdNYXNrRWxlbWVudDpBLmhpLFNWR01ldGFkYXRhRWxlbWVudDpBLmhpLFNWR1BhdGhFbGVtZW50 -OkEuaGksU1ZHUGF0dGVybkVsZW1lbnQ6QS5oaSxTVkdQb2x5Z29uRWxlbWVudDpBLmhpLFNWR1BvbHls -aW5lRWxlbWVudDpBLmhpLFNWR1JhZGlhbEdyYWRpZW50RWxlbWVudDpBLmhpLFNWR1JlY3RFbGVtZW50 -OkEuaGksU1ZHU2V0RWxlbWVudDpBLmhpLFNWR1N0b3BFbGVtZW50OkEuaGksU1ZHU3R5bGVFbGVtZW50 -OkEuaGksU1ZHU1ZHRWxlbWVudDpBLmhpLFNWR1N3aXRjaEVsZW1lbnQ6QS5oaSxTVkdTeW1ib2xFbGVt -ZW50OkEuaGksU1ZHVFNwYW5FbGVtZW50OkEuaGksU1ZHVGV4dENvbnRlbnRFbGVtZW50OkEuaGksU1ZH -VGV4dEVsZW1lbnQ6QS5oaSxTVkdUZXh0UGF0aEVsZW1lbnQ6QS5oaSxTVkdUZXh0UG9zaXRpb25pbmdF -bGVtZW50OkEuaGksU1ZHVGl0bGVFbGVtZW50OkEuaGksU1ZHVXNlRWxlbWVudDpBLmhpLFNWR1ZpZXdF -bGVtZW50OkEuaGksU1ZHR3JhZGllbnRFbGVtZW50OkEuaGksU1ZHQ29tcG9uZW50VHJhbnNmZXJGdW5j -dGlvbkVsZW1lbnQ6QS5oaSxTVkdGRURyb3BTaGFkb3dFbGVtZW50OkEuaGksU1ZHTVBhdGhFbGVtZW50 -OkEuaGksU1ZHRWxlbWVudDpBLmhpfSkKaHVua0hlbHBlcnMuc2V0T3JVcGRhdGVMZWFmVGFncyh7RE9N -RXJyb3I6dHJ1ZSxNZWRpYUVycm9yOnRydWUsTmF2aWdhdG9yOnRydWUsTmF2aWdhdG9yQ29uY3VycmVu -dEhhcmR3YXJlOnRydWUsTmF2aWdhdG9yVXNlck1lZGlhRXJyb3I6dHJ1ZSxPdmVyY29uc3RyYWluZWRF -cnJvcjp0cnVlLFBvc2l0aW9uRXJyb3I6dHJ1ZSxHZW9sb2NhdGlvblBvc2l0aW9uRXJyb3I6dHJ1ZSxS -YW5nZTp0cnVlLERhdGFWaWV3OnRydWUsQXJyYXlCdWZmZXJWaWV3OmZhbHNlLEZsb2F0MzJBcnJheTp0 -cnVlLEZsb2F0NjRBcnJheTp0cnVlLEludDE2QXJyYXk6dHJ1ZSxJbnQzMkFycmF5OnRydWUsSW50OEFy -cmF5OnRydWUsVWludDE2QXJyYXk6dHJ1ZSxVaW50MzJBcnJheTp0cnVlLFVpbnQ4Q2xhbXBlZEFycmF5 -OnRydWUsQ2FudmFzUGl4ZWxBcnJheTp0cnVlLFVpbnQ4QXJyYXk6ZmFsc2UsSFRNTEF1ZGlvRWxlbWVu -dDp0cnVlLEhUTUxCUkVsZW1lbnQ6dHJ1ZSxIVE1MQnV0dG9uRWxlbWVudDp0cnVlLEhUTUxDYW52YXNF -bGVtZW50OnRydWUsSFRNTENvbnRlbnRFbGVtZW50OnRydWUsSFRNTERMaXN0RWxlbWVudDp0cnVlLEhU -TUxEYXRhRWxlbWVudDp0cnVlLEhUTUxEYXRhTGlzdEVsZW1lbnQ6dHJ1ZSxIVE1MRGV0YWlsc0VsZW1l -bnQ6dHJ1ZSxIVE1MRGlhbG9nRWxlbWVudDp0cnVlLEhUTUxEaXZFbGVtZW50OnRydWUsSFRNTEVtYmVk -RWxlbWVudDp0cnVlLEhUTUxGaWVsZFNldEVsZW1lbnQ6dHJ1ZSxIVE1MSFJFbGVtZW50OnRydWUsSFRN -TEhlYWRFbGVtZW50OnRydWUsSFRNTEhlYWRpbmdFbGVtZW50OnRydWUsSFRNTEh0bWxFbGVtZW50OnRy -dWUsSFRNTElGcmFtZUVsZW1lbnQ6dHJ1ZSxIVE1MSW1hZ2VFbGVtZW50OnRydWUsSFRNTElucHV0RWxl -bWVudDp0cnVlLEhUTUxMSUVsZW1lbnQ6dHJ1ZSxIVE1MTGFiZWxFbGVtZW50OnRydWUsSFRNTExlZ2Vu -ZEVsZW1lbnQ6dHJ1ZSxIVE1MTGlua0VsZW1lbnQ6dHJ1ZSxIVE1MTWFwRWxlbWVudDp0cnVlLEhUTUxN -ZWRpYUVsZW1lbnQ6dHJ1ZSxIVE1MTWVudUVsZW1lbnQ6dHJ1ZSxIVE1MTWV0YUVsZW1lbnQ6dHJ1ZSxI -VE1MTWV0ZXJFbGVtZW50OnRydWUsSFRNTE1vZEVsZW1lbnQ6dHJ1ZSxIVE1MT0xpc3RFbGVtZW50OnRy -dWUsSFRNTE9iamVjdEVsZW1lbnQ6dHJ1ZSxIVE1MT3B0R3JvdXBFbGVtZW50OnRydWUsSFRNTE9wdGlv -bkVsZW1lbnQ6dHJ1ZSxIVE1MT3V0cHV0RWxlbWVudDp0cnVlLEhUTUxQYXJhbUVsZW1lbnQ6dHJ1ZSxI -VE1MUGljdHVyZUVsZW1lbnQ6dHJ1ZSxIVE1MUHJlRWxlbWVudDp0cnVlLEhUTUxQcm9ncmVzc0VsZW1l -bnQ6dHJ1ZSxIVE1MUXVvdGVFbGVtZW50OnRydWUsSFRNTFNjcmlwdEVsZW1lbnQ6dHJ1ZSxIVE1MU2hh -ZG93RWxlbWVudDp0cnVlLEhUTUxTbG90RWxlbWVudDp0cnVlLEhUTUxTb3VyY2VFbGVtZW50OnRydWUs -SFRNTFNwYW5FbGVtZW50OnRydWUsSFRNTFN0eWxlRWxlbWVudDp0cnVlLEhUTUxUYWJsZUNhcHRpb25F -bGVtZW50OnRydWUsSFRNTFRhYmxlQ2VsbEVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVEYXRhQ2VsbEVsZW1l -bnQ6dHJ1ZSxIVE1MVGFibGVIZWFkZXJDZWxsRWxlbWVudDp0cnVlLEhUTUxUYWJsZUNvbEVsZW1lbnQ6 -dHJ1ZSxIVE1MVGV4dEFyZWFFbGVtZW50OnRydWUsSFRNTFRpbWVFbGVtZW50OnRydWUsSFRNTFRpdGxl -RWxlbWVudDp0cnVlLEhUTUxUcmFja0VsZW1lbnQ6dHJ1ZSxIVE1MVUxpc3RFbGVtZW50OnRydWUsSFRN -TFVua25vd25FbGVtZW50OnRydWUsSFRNTFZpZGVvRWxlbWVudDp0cnVlLEhUTUxEaXJlY3RvcnlFbGVt -ZW50OnRydWUsSFRNTEZvbnRFbGVtZW50OnRydWUsSFRNTEZyYW1lRWxlbWVudDp0cnVlLEhUTUxGcmFt -ZVNldEVsZW1lbnQ6dHJ1ZSxIVE1MTWFycXVlZUVsZW1lbnQ6dHJ1ZSxIVE1MRWxlbWVudDpmYWxzZSxI -VE1MQW5jaG9yRWxlbWVudDp0cnVlLEhUTUxBcmVhRWxlbWVudDp0cnVlLEhUTUxCYXNlRWxlbWVudDp0 -cnVlLEJsb2I6ZmFsc2UsSFRNTEJvZHlFbGVtZW50OnRydWUsQ0RBVEFTZWN0aW9uOnRydWUsQ2hhcmFj -dGVyRGF0YTp0cnVlLENvbW1lbnQ6dHJ1ZSxQcm9jZXNzaW5nSW5zdHJ1Y3Rpb246dHJ1ZSxUZXh0OnRy -dWUsQ1NTU3R5bGVEZWNsYXJhdGlvbjp0cnVlLE1TU3R5bGVDU1NQcm9wZXJ0aWVzOnRydWUsQ1NTMlBy -b3BlcnRpZXM6dHJ1ZSxYTUxEb2N1bWVudDp0cnVlLERvY3VtZW50OmZhbHNlLERPTUV4Y2VwdGlvbjp0 -cnVlLERPTUltcGxlbWVudGF0aW9uOnRydWUsRE9NUmVjdFJlYWRPbmx5OmZhbHNlLERPTVRva2VuTGlz -dDp0cnVlLE1hdGhNTEVsZW1lbnQ6dHJ1ZSxFbGVtZW50OmZhbHNlLEFib3J0UGF5bWVudEV2ZW50OnRy -dWUsQW5pbWF0aW9uRXZlbnQ6dHJ1ZSxBbmltYXRpb25QbGF5YmFja0V2ZW50OnRydWUsQXBwbGljYXRp -b25DYWNoZUVycm9yRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hDbGlja0V2ZW50OnRydWUsQmFja2dy -b3VuZEZldGNoRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5kRmV0Y2hGYWlsRXZlbnQ6dHJ1ZSxCYWNrZ3JvdW5k -RmV0Y2hlZEV2ZW50OnRydWUsQmVmb3JlSW5zdGFsbFByb21wdEV2ZW50OnRydWUsQmVmb3JlVW5sb2Fk -RXZlbnQ6dHJ1ZSxCbG9iRXZlbnQ6dHJ1ZSxDYW5NYWtlUGF5bWVudEV2ZW50OnRydWUsQ2xpcGJvYXJk -RXZlbnQ6dHJ1ZSxDbG9zZUV2ZW50OnRydWUsQ3VzdG9tRXZlbnQ6dHJ1ZSxEZXZpY2VNb3Rpb25FdmVu -dDp0cnVlLERldmljZU9yaWVudGF0aW9uRXZlbnQ6dHJ1ZSxFcnJvckV2ZW50OnRydWUsRXh0ZW5kYWJs -ZUV2ZW50OnRydWUsRXh0ZW5kYWJsZU1lc3NhZ2VFdmVudDp0cnVlLEZldGNoRXZlbnQ6dHJ1ZSxGb250 -RmFjZVNldExvYWRFdmVudDp0cnVlLEZvcmVpZ25GZXRjaEV2ZW50OnRydWUsR2FtZXBhZEV2ZW50OnRy -dWUsSGFzaENoYW5nZUV2ZW50OnRydWUsSW5zdGFsbEV2ZW50OnRydWUsTWVkaWFFbmNyeXB0ZWRFdmVu -dDp0cnVlLE1lZGlhS2V5TWVzc2FnZUV2ZW50OnRydWUsTWVkaWFRdWVyeUxpc3RFdmVudDp0cnVlLE1l -ZGlhU3RyZWFtRXZlbnQ6dHJ1ZSxNZWRpYVN0cmVhbVRyYWNrRXZlbnQ6dHJ1ZSxNZXNzYWdlRXZlbnQ6 -dHJ1ZSxNSURJQ29ubmVjdGlvbkV2ZW50OnRydWUsTUlESU1lc3NhZ2VFdmVudDp0cnVlLE11dGF0aW9u -RXZlbnQ6dHJ1ZSxOb3RpZmljYXRpb25FdmVudDp0cnVlLFBhZ2VUcmFuc2l0aW9uRXZlbnQ6dHJ1ZSxQ -YXltZW50UmVxdWVzdEV2ZW50OnRydWUsUGF5bWVudFJlcXVlc3RVcGRhdGVFdmVudDp0cnVlLFBvcFN0 -YXRlRXZlbnQ6dHJ1ZSxQcmVzZW50YXRpb25Db25uZWN0aW9uQXZhaWxhYmxlRXZlbnQ6dHJ1ZSxQcmVz -ZW50YXRpb25Db25uZWN0aW9uQ2xvc2VFdmVudDp0cnVlLFByb21pc2VSZWplY3Rpb25FdmVudDp0cnVl -LFB1c2hFdmVudDp0cnVlLFJUQ0RhdGFDaGFubmVsRXZlbnQ6dHJ1ZSxSVENEVE1GVG9uZUNoYW5nZUV2 -ZW50OnRydWUsUlRDUGVlckNvbm5lY3Rpb25JY2VFdmVudDp0cnVlLFJUQ1RyYWNrRXZlbnQ6dHJ1ZSxT -ZWN1cml0eVBvbGljeVZpb2xhdGlvbkV2ZW50OnRydWUsU2Vuc29yRXJyb3JFdmVudDp0cnVlLFNwZWVj -aFJlY29nbml0aW9uRXJyb3I6dHJ1ZSxTcGVlY2hSZWNvZ25pdGlvbkV2ZW50OnRydWUsU3BlZWNoU3lu -dGhlc2lzRXZlbnQ6dHJ1ZSxTdG9yYWdlRXZlbnQ6dHJ1ZSxTeW5jRXZlbnQ6dHJ1ZSxUcmFja0V2ZW50 -OnRydWUsVHJhbnNpdGlvbkV2ZW50OnRydWUsV2ViS2l0VHJhbnNpdGlvbkV2ZW50OnRydWUsVlJEZXZp -Y2VFdmVudDp0cnVlLFZSRGlzcGxheUV2ZW50OnRydWUsVlJTZXNzaW9uRXZlbnQ6dHJ1ZSxNb2pvSW50 -ZXJmYWNlUmVxdWVzdEV2ZW50OnRydWUsVVNCQ29ubmVjdGlvbkV2ZW50OnRydWUsSURCVmVyc2lvbkNo -YW5nZUV2ZW50OnRydWUsQXVkaW9Qcm9jZXNzaW5nRXZlbnQ6dHJ1ZSxPZmZsaW5lQXVkaW9Db21wbGV0 -aW9uRXZlbnQ6dHJ1ZSxXZWJHTENvbnRleHRFdmVudDp0cnVlLEV2ZW50OmZhbHNlLElucHV0RXZlbnQ6 -ZmFsc2UsU3VibWl0RXZlbnQ6ZmFsc2UsRXZlbnRUYXJnZXQ6ZmFsc2UsRmlsZTp0cnVlLEhUTUxGb3Jt -RWxlbWVudDp0cnVlLEhpc3Rvcnk6dHJ1ZSxIVE1MRG9jdW1lbnQ6dHJ1ZSxYTUxIdHRwUmVxdWVzdDp0 -cnVlLFhNTEh0dHBSZXF1ZXN0RXZlbnRUYXJnZXQ6ZmFsc2UsSW1hZ2VEYXRhOnRydWUsTG9jYXRpb246 -dHJ1ZSxNb3VzZUV2ZW50OnRydWUsRHJhZ0V2ZW50OnRydWUsUG9pbnRlckV2ZW50OnRydWUsV2hlZWxF -dmVudDp0cnVlLERvY3VtZW50RnJhZ21lbnQ6dHJ1ZSxTaGFkb3dSb290OnRydWUsRG9jdW1lbnRUeXBl -OnRydWUsTm9kZTpmYWxzZSxOb2RlTGlzdDp0cnVlLFJhZGlvTm9kZUxpc3Q6dHJ1ZSxIVE1MUGFyYWdy -YXBoRWxlbWVudDp0cnVlLFByb2dyZXNzRXZlbnQ6dHJ1ZSxSZXNvdXJjZVByb2dyZXNzRXZlbnQ6dHJ1 -ZSxIVE1MU2VsZWN0RWxlbWVudDp0cnVlLEhUTUxUYWJsZUVsZW1lbnQ6dHJ1ZSxIVE1MVGFibGVSb3dF -bGVtZW50OnRydWUsSFRNTFRhYmxlU2VjdGlvbkVsZW1lbnQ6dHJ1ZSxIVE1MVGVtcGxhdGVFbGVtZW50 -OnRydWUsQ29tcG9zaXRpb25FdmVudDp0cnVlLEZvY3VzRXZlbnQ6dHJ1ZSxLZXlib2FyZEV2ZW50OnRy -dWUsVGV4dEV2ZW50OnRydWUsVG91Y2hFdmVudDp0cnVlLFVJRXZlbnQ6ZmFsc2UsV2luZG93OnRydWUs -RE9NV2luZG93OnRydWUsRGVkaWNhdGVkV29ya2VyR2xvYmFsU2NvcGU6dHJ1ZSxTZXJ2aWNlV29ya2Vy -R2xvYmFsU2NvcGU6dHJ1ZSxTaGFyZWRXb3JrZXJHbG9iYWxTY29wZTp0cnVlLFdvcmtlckdsb2JhbFNj -b3BlOnRydWUsQXR0cjp0cnVlLENsaWVudFJlY3Q6dHJ1ZSxET01SZWN0OnRydWUsTmFtZWROb2RlTWFw -OnRydWUsTW96TmFtZWRBdHRyTWFwOnRydWUsSURCS2V5UmFuZ2U6dHJ1ZSxTVkdTY3JpcHRFbGVtZW50 -OnRydWUsU1ZHQUVsZW1lbnQ6dHJ1ZSxTVkdBbmltYXRlRWxlbWVudDp0cnVlLFNWR0FuaW1hdGVNb3Rp -b25FbGVtZW50OnRydWUsU1ZHQW5pbWF0ZVRyYW5zZm9ybUVsZW1lbnQ6dHJ1ZSxTVkdBbmltYXRpb25F -bGVtZW50OnRydWUsU1ZHQ2lyY2xlRWxlbWVudDp0cnVlLFNWR0NsaXBQYXRoRWxlbWVudDp0cnVlLFNW -R0RlZnNFbGVtZW50OnRydWUsU1ZHRGVzY0VsZW1lbnQ6dHJ1ZSxTVkdEaXNjYXJkRWxlbWVudDp0cnVl -LFNWR0VsbGlwc2VFbGVtZW50OnRydWUsU1ZHRkVCbGVuZEVsZW1lbnQ6dHJ1ZSxTVkdGRUNvbG9yTWF0 -cml4RWxlbWVudDp0cnVlLFNWR0ZFQ29tcG9uZW50VHJhbnNmZXJFbGVtZW50OnRydWUsU1ZHRkVDb21w -b3NpdGVFbGVtZW50OnRydWUsU1ZHRkVDb252b2x2ZU1hdHJpeEVsZW1lbnQ6dHJ1ZSxTVkdGRURpZmZ1 -c2VMaWdodGluZ0VsZW1lbnQ6dHJ1ZSxTVkdGRURpc3BsYWNlbWVudE1hcEVsZW1lbnQ6dHJ1ZSxTVkdG -RURpc3RhbnRMaWdodEVsZW1lbnQ6dHJ1ZSxTVkdGRUZsb29kRWxlbWVudDp0cnVlLFNWR0ZFRnVuY0FF -bGVtZW50OnRydWUsU1ZHRkVGdW5jQkVsZW1lbnQ6dHJ1ZSxTVkdGRUZ1bmNHRWxlbWVudDp0cnVlLFNW -R0ZFRnVuY1JFbGVtZW50OnRydWUsU1ZHRkVHYXVzc2lhbkJsdXJFbGVtZW50OnRydWUsU1ZHRkVJbWFn -ZUVsZW1lbnQ6dHJ1ZSxTVkdGRU1lcmdlRWxlbWVudDp0cnVlLFNWR0ZFTWVyZ2VOb2RlRWxlbWVudDp0 -cnVlLFNWR0ZFTW9ycGhvbG9neUVsZW1lbnQ6dHJ1ZSxTVkdGRU9mZnNldEVsZW1lbnQ6dHJ1ZSxTVkdG -RVBvaW50TGlnaHRFbGVtZW50OnRydWUsU1ZHRkVTcGVjdWxhckxpZ2h0aW5nRWxlbWVudDp0cnVlLFNW -R0ZFU3BvdExpZ2h0RWxlbWVudDp0cnVlLFNWR0ZFVGlsZUVsZW1lbnQ6dHJ1ZSxTVkdGRVR1cmJ1bGVu -Y2VFbGVtZW50OnRydWUsU1ZHRmlsdGVyRWxlbWVudDp0cnVlLFNWR0ZvcmVpZ25PYmplY3RFbGVtZW50 -OnRydWUsU1ZHR0VsZW1lbnQ6dHJ1ZSxTVkdHZW9tZXRyeUVsZW1lbnQ6dHJ1ZSxTVkdHcmFwaGljc0Vs -ZW1lbnQ6dHJ1ZSxTVkdJbWFnZUVsZW1lbnQ6dHJ1ZSxTVkdMaW5lRWxlbWVudDp0cnVlLFNWR0xpbmVh -ckdyYWRpZW50RWxlbWVudDp0cnVlLFNWR01hcmtlckVsZW1lbnQ6dHJ1ZSxTVkdNYXNrRWxlbWVudDp0 -cnVlLFNWR01ldGFkYXRhRWxlbWVudDp0cnVlLFNWR1BhdGhFbGVtZW50OnRydWUsU1ZHUGF0dGVybkVs -ZW1lbnQ6dHJ1ZSxTVkdQb2x5Z29uRWxlbWVudDp0cnVlLFNWR1BvbHlsaW5lRWxlbWVudDp0cnVlLFNW -R1JhZGlhbEdyYWRpZW50RWxlbWVudDp0cnVlLFNWR1JlY3RFbGVtZW50OnRydWUsU1ZHU2V0RWxlbWVu -dDp0cnVlLFNWR1N0b3BFbGVtZW50OnRydWUsU1ZHU3R5bGVFbGVtZW50OnRydWUsU1ZHU1ZHRWxlbWVu -dDp0cnVlLFNWR1N3aXRjaEVsZW1lbnQ6dHJ1ZSxTVkdTeW1ib2xFbGVtZW50OnRydWUsU1ZHVFNwYW5F -bGVtZW50OnRydWUsU1ZHVGV4dENvbnRlbnRFbGVtZW50OnRydWUsU1ZHVGV4dEVsZW1lbnQ6dHJ1ZSxT -VkdUZXh0UGF0aEVsZW1lbnQ6dHJ1ZSxTVkdUZXh0UG9zaXRpb25pbmdFbGVtZW50OnRydWUsU1ZHVGl0 -bGVFbGVtZW50OnRydWUsU1ZHVXNlRWxlbWVudDp0cnVlLFNWR1ZpZXdFbGVtZW50OnRydWUsU1ZHR3Jh -ZGllbnRFbGVtZW50OnRydWUsU1ZHQ29tcG9uZW50VHJhbnNmZXJGdW5jdGlvbkVsZW1lbnQ6dHJ1ZSxT -VkdGRURyb3BTaGFkb3dFbGVtZW50OnRydWUsU1ZHTVBhdGhFbGVtZW50OnRydWUsU1ZHRWxlbWVudDpm -YWxzZX0pCkEuWEguJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmlldyIKQS5SRy4kbmF0 -aXZlU3VwZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpBLlZQLiRuYXRpdmVTdXBlcmNsYXNzVGFn -PSJBcnJheUJ1ZmZlclZpZXciCkEuRGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFycmF5QnVmZmVyVmll -dyIKQS5XQi4kbmF0aXZlU3VwZXJjbGFzc1RhZz0iQXJyYXlCdWZmZXJWaWV3IgpBLlpHLiRuYXRpdmVT -dXBlcmNsYXNzVGFnPSJBcnJheUJ1ZmZlclZpZXciCkEuUGcuJG5hdGl2ZVN1cGVyY2xhc3NUYWc9IkFy -cmF5QnVmZmVyVmlldyJ9KSgpCmNvbnZlcnRBbGxUb0Zhc3RPYmplY3QodykKY29udmVydFRvRmFzdE9i -amVjdCgkKTsoZnVuY3Rpb24oYSl7aWYodHlwZW9mIGRvY3VtZW50PT09InVuZGVmaW5lZCIpe2EobnVs -bCkKcmV0dXJufWlmKHR5cGVvZiBkb2N1bWVudC5jdXJyZW50U2NyaXB0IT0idW5kZWZpbmVkIil7YShk -b2N1bWVudC5jdXJyZW50U2NyaXB0KQpyZXR1cm59dmFyIHM9ZG9jdW1lbnQuc2NyaXB0cwpmdW5jdGlv -biBvbkxvYWQoYil7Zm9yKHZhciBxPTA7cTxzLmxlbmd0aDsrK3Epc1txXS5yZW1vdmVFdmVudExpc3Rl -bmVyKCJsb2FkIixvbkxvYWQsZmFsc2UpCmEoYi50YXJnZXQpfWZvcih2YXIgcj0wO3I8cy5sZW5ndGg7 -KytyKXNbcl0uYWRkRXZlbnRMaXN0ZW5lcigibG9hZCIsb25Mb2FkLGZhbHNlKX0pKGZ1bmN0aW9uKGEp -e3YuY3VycmVudFNjcmlwdD1hCnZhciBzPUEuSXEKaWYodHlwZW9mIGRhcnRNYWluUnVubmVyPT09ImZ1 -bmN0aW9uIilkYXJ0TWFpblJ1bm5lcihzLFtdKQplbHNlIHMoW10pfSl9KSgpCi8vIyBzb3VyY2VNYXBw -aW5nVVJMPW1pZ3JhdGlvbi5qcy5tYXAK -'''; diff --git a/pkg/nnbd_migration/lib/src/front_end/unit_link.dart b/pkg/nnbd_migration/lib/src/front_end/unit_link.dart deleted file mode 100644 index 8ab5c79a363c..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/unit_link.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; - -/// Information about a link to a compilation unit. -class UnitLink { - final String? fullPath; - final List pathParts; - final int editCount; - - /// The number of directories deep in which this compilation unit is found. - /// - /// A compilation unit in the root has a depth of 0. - final int depth; - - /// Whether this compilation unit was explicitly opted out of null safety at - /// the start of this migration. - final bool wasExplicitlyOptedOut; - - final UnitMigrationStatus? migrationStatus; - - /// Whether the migration status of this compilation unit can be changed in - /// the web interface. - final bool migrationStatusCanBeChanged; - - UnitLink( - this.fullPath, - this.pathParts, - this.editCount, - this.wasExplicitlyOptedOut, - this.migrationStatus, - this.migrationStatusCanBeChanged) - : depth = pathParts.length - 1; - - String get fileName => pathParts.last; -} diff --git a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart deleted file mode 100644 index 6b6257cc2651..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert' show HtmlEscape, HtmlEscapeMode, LineSplitter; - -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/web/file_details.dart'; -import 'package:path/path.dart' as path; - -/// Instrumentation display output for a library that was migrated to use -/// non-nullable types. -class UnitRenderer { - /// A converter which only escapes "&", "<", and ">". Safe for use in HTML - /// text, between HTML elements. - static const HtmlEscape _htmlEscape = - HtmlEscape(HtmlEscapeMode(escapeLtGt: true)); - - /// List of kinds of nullability fixes that should be displayed in the - /// "proposed edits" area, in the order in which they should be displayed. - @visibleForTesting - static const List kindPriorityOrder = [ - NullabilityFixKind.noValidMigrationForNull, - NullabilityFixKind.compoundAssignmentHasBadCombinedType, - NullabilityFixKind.compoundAssignmentHasNullableSource, - NullabilityFixKind.addThen, - NullabilityFixKind.removeDeadCode, - NullabilityFixKind.conditionTrueInStrongMode, - NullabilityFixKind.conditionFalseInStrongMode, - NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode, - NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode, - NullabilityFixKind.otherCastExpression, - NullabilityFixKind.changeMethodName, - NullabilityFixKind.checkExpression, - NullabilityFixKind.addRequired, - NullabilityFixKind.makeTypeNullable, - NullabilityFixKind.downcastExpression, - NullabilityFixKind.addType, - NullabilityFixKind.replaceVar, - NullabilityFixKind.removeAs, - NullabilityFixKind.addLate, - NullabilityFixKind.addLateDueToTestSetup, - NullabilityFixKind.removeNullableAnnotation, - NullabilityFixKind.addLateDueToHint, - NullabilityFixKind.addLateFinalDueToHint, - NullabilityFixKind.checkExpressionDueToHint, - NullabilityFixKind.makeTypeNullableDueToHint, - NullabilityFixKind.addImport, - NullabilityFixKind.removeLanguageVersionComment - ]; - - /// Displays information for a compilation unit. - final UnitInfo unitInfo; - - /// Information for a whole migration, so that libraries can reference each - /// other. - final MigrationInfo? migrationInfo; - - /// An object used to map the file paths of analyzed files to the file paths - /// of the HTML files used to view the content of those files. - final PathMapper? pathMapper; - - /// The auth token for the current site, for use in generating URIs. - final String authToken; - - /// Creates an output object for the given library info. - UnitRenderer( - this.unitInfo, this.migrationInfo, this.pathMapper, this.authToken); - - /// Return the path context used to manipulate paths. - path.Context get pathContext => migrationInfo!.pathContext; - - /// Builds a JSON view of the instrumentation information in [unitInfo]. - FileDetails render() { - return FileDetails( - regions: _computeRegionContent(unitInfo), - navigationContent: _computeNavigationContent(), - sourceCode: unitInfo.content, - edits: _computeEditList()); - } - - /// Returns the list of edits, as JSON. - Map> _computeEditList() { - var editListsByKind = >{}; - for (var region in unitInfo.regions) { - var kind = region.kind; - if (kind != null && region.isCounted) { - (editListsByKind[kind] ??= []).add(EditListItem( - line: region.lineNumber, - explanation: region.explanation, - offset: region.offset)); - } - } - // Order the lists and filter out empty categories. - var result = >{}; - for (var kind in kindPriorityOrder) { - var edits = editListsByKind[kind]; - if (edits != null) { - result[_headerForKind(kind, edits.length)] = edits; - } - } - return result; - } - - /// Returns the content of the file with navigation links and anchors added. - /// - /// The content of the file (not including added links and anchors) will be - /// HTML-escaped. - String _computeNavigationContent() { - var content = unitInfo.content; - var mapper = unitInfo.offsetMapper; - var openInsertions = {}; - var closeInsertions = {}; - // - // Compute insertions for navigation targets. - // - for (var region in unitInfo.targets) { - if (region.length > 0) { - var openOffset = mapper.map(region.offset); - if (openOffset == null) { - // Region has been deleted via a hint action. - continue; - } - var openInsertion = openInsertions[openOffset] ?? ''; - openInsertion = '$openInsertion'; - openInsertions[openOffset] = openInsertion; - - var closeOffset = openOffset + region.length; - var closeInsertion = closeInsertions[closeOffset] ?? ''; - closeInsertion = '$closeInsertion'; - closeInsertions[closeOffset] = closeInsertion; - } - } - // - // Compute insertions for navigation sources, but skip the sources that - // point at themselves. - // - for (var region in unitInfo.sources ?? []) { - if (region.length > 0) { - var openOffset = mapper.map(region.offset); - if (openOffset == null) { - // Region has been deleted via a hint action. - continue; - } - var target = region.target; - if (target.filePath != unitInfo.path || - region.offset != target.offset) { - var openInsertion = openInsertions[openOffset] ?? ''; - var targetUri = _uriForPath(pathMapper!.map(target.filePath), target); - openInsertion = - '$openInsertion'; - openInsertions[openOffset] = openInsertion; - - var closeOffset = openOffset + region.length; - var closeInsertion = closeInsertions[closeOffset] ?? ''; - closeInsertion = '$closeInsertion'; - closeInsertions[closeOffset] = closeInsertion; - } - } - } - // - // Apply the insertions that have been computed. - // - var offsets = [...openInsertions.keys, ...closeInsertions.keys]; - offsets.sort(); - var navContent2 = StringBuffer(); - var previousOffset = 0; - for (var offset in offsets) { - navContent2.write( - _htmlEscape.convert(content!.substring(previousOffset, offset))); - navContent2.write(closeInsertions[offset] ?? ''); - navContent2.write(openInsertions[offset] ?? ''); - previousOffset = offset; - } - if (previousOffset < content!.length) { - navContent2.write(_htmlEscape.convert(content.substring(previousOffset))); - } - return navContent2.toString(); - } - - /// Returns the content of regions, based on the [unitInfo] for both - /// unmodified and modified regions. - /// - /// The content of the file (not including added links and anchors) will be - /// HTML-escaped. - String _computeRegionContent(UnitInfo unit) { - var content = unitInfo.content!; - var rows = []; - var currentTextCell = StringBuffer(); - bool isAddedLine = false; - var lineNumber = 1; - - void finishRow(bool isAddedText) { - var line = currentTextCell.toString(); - if (isAddedLine) { - rows.add('(new)$line'); - } else { - rows.add('$lineNumber' - '$line'); - lineNumber++; - } - currentTextCell = StringBuffer(); - isAddedLine = isAddedText; - } - - void writeSplitLines( - String lines, { - String perLineOpeningTag = '', - String perLineClosingTag = '', - bool isAddedText = false, - }) { - var lineIterator = LineSplitter.split(lines).iterator; - lineIterator.moveNext(); - - while (true) { - currentTextCell.write(perLineOpeningTag); - currentTextCell.write(_htmlEscape.convert(lineIterator.current)); - currentTextCell.write(perLineClosingTag); - if (lineIterator.moveNext()) { - // If we're not on the last element, end this row, and get ready to - // start a new row. - finishRow(isAddedText); - } else { - break; - } - } - - if (lines.endsWith('\n')) { - finishRow(isAddedText); - } - } - - /// Returns the CSS class for a region with a given [RegionType]. - String classForRegion(RegionType type) { - switch (type) { - case RegionType.add: - return 'added-region'; - case RegionType.remove: - return 'removed-region'; - case RegionType.informative: - return 'informative-region'; - } - } - - var previousOffset = 0; - for (var region in unitInfo.regions) { - var offset = region.offset; - var length = region.length; - if (offset > previousOffset) { - // Display a region of unmodified content. - writeSplitLines(content.substring(previousOffset, offset)); - } - previousOffset = offset + length; - var shouldBeShown = region.kind != null; - var regionClass = classForRegion(region.regionType); - var regionSpanTag = shouldBeShown - ? '' - : ''; - writeSplitLines(content.substring(offset, offset + length), - perLineOpeningTag: regionSpanTag, - perLineClosingTag: shouldBeShown ? '' : '', - isAddedText: region.regionType == RegionType.add); - } - if (previousOffset < content.length) { - // Last region of unmodified content. - writeSplitLines(content.substring(previousOffset)); - } - finishRow(false); - return '' - '${rows.join()}
'; - } - - String _headerForKind(NullabilityFixKind kind, int count) { - var s = count == 1 ? '' : 's'; - var es = count == 1 ? '' : 'es'; - switch (kind) { - case NullabilityFixKind.addThen: - return '$count invocation$s of `.then` added'; - case NullabilityFixKind.addImport: - return '$count import$s added'; - case NullabilityFixKind.addLate: - return '$count late keyword$s added'; - case NullabilityFixKind.addLateDueToHint: - return '$count late hint$s converted to late keyword$s'; - case NullabilityFixKind.addLateDueToTestSetup: - return '$count late keyword$s added, due to assignment in `setUp`'; - case NullabilityFixKind.addLateFinalDueToHint: - return '$count late final hint$s converted to late and final keywords'; - case NullabilityFixKind.addRequired: - return '$count required keyword$s added'; - case NullabilityFixKind.addType: - return '$count type$s added'; - case NullabilityFixKind.changeMethodName: - return '$count method name$s changed'; - case NullabilityFixKind.downcastExpression: - return '$count downcast$s added'; - case NullabilityFixKind.otherCastExpression: - return '$count cast$s (non-downcast) added'; - case NullabilityFixKind.checkExpression: - return '$count null check$s added'; - case NullabilityFixKind.checkExpressionDueToHint: - return '$count null check hint$s converted to null check$s'; - case NullabilityFixKind.compoundAssignmentHasBadCombinedType: - return "$count compound assignment$s couldn't be migrated (bad " - 'combined type)'; - case NullabilityFixKind.compoundAssignmentHasNullableSource: - return "$count compound assignment$s couldn't be migrated (nullable " - 'source)'; - case NullabilityFixKind.conditionTrueInStrongMode: - return '$count condition$s will be true in strong checking mode'; - case NullabilityFixKind.conditionFalseInStrongMode: - return '$count condition$s will be false in strong checking mode'; - case NullabilityFixKind.makeTypeNullable: - return '$count type$s made nullable'; - case NullabilityFixKind.makeTypeNullableDueToHint: - return '$count nullability hint$s converted to ?$s'; - case NullabilityFixKind.noValidMigrationForNull: - return "$count literal `null`$s couldn't be migrated"; - case NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode: - return '$count null-aware access$es will be unnecessary in strong ' - 'checking mode'; - case NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode: - return '$count null-aware assignment$s will be unnecessary in strong ' - 'checking mode'; - case NullabilityFixKind.removeAs: - return '$count cast$s now unnecessary'; - case NullabilityFixKind.removeDeadCode: - return '$count dead code removal$s'; - case NullabilityFixKind.removeLanguageVersionComment: - return '$count language version comment$s removed'; - case NullabilityFixKind.replaceVar: - return "$count 'var' declaration$s replaced"; - case NullabilityFixKind.typeNotMadeNullable: - return '$count type$s not made nullable'; - case NullabilityFixKind.typeNotMadeNullableDueToHint: - return '$count type$s not made nullable due to hint$s'; - case NullabilityFixKind.removeNullableAnnotation: - return '$count @nullable annotation$s removed'; - } - } - - /// Returns the URL that will navigate to the given [target] in the file at - /// the given [relativePath]. - String _uriForPath(String path, NavigationTarget target) { - var queryParams = { - 'offset': target.offset, - if (target.line != null) 'line': target.line, - 'authToken': authToken, - }.entries.map((entry) => '${entry.key}=${entry.value}').join('&'); - return '$path?$queryParams'; - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart b/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart deleted file mode 100644 index 4699bed1173b..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/web/edit_details.dart +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/hint_action.dart'; - -/// Information about what should be populated into the "Edit Details" view of -/// the migration preview tool. -class EditDetails { - /// A list of edits that can be offered to the user related to this source - /// location (e.g. adding/removing hints). `null` if this feature is - /// disabled. - final List? edits; - - /// A string explanation of the edit. - final String? explanation; - - /// The line number of the edit. - final int? line; - - /// The path of the file that was edited, to be shown to the user. - final String? displayPath; - - /// The path of the file that was edited, as a URI. - final String? uriPath; - - /// A list of traces representing stacktrace-like views of why the change was - /// made, or the empty list if there are no traces for this change. - final List? traces; - - EditDetails( - {this.edits, - required this.explanation, - required this.line, - required this.displayPath, - required this.uriPath, - this.traces = const []}); - - EditDetails.fromJson(dynamic json) - : edits = _decodeEdits(json['edits'] as List?), - explanation = json['explanation'] as String?, - line = json['line'] as int?, - displayPath = json['displayPath'] as String?, - uriPath = json['uriPath'] as String?, - traces = _decodeTraces(json['traces'] as List?); - - Map toJson() => { - if (edits != null) 'edits': [for (var edit in edits!) edit.toJson()], - 'explanation': explanation, - 'line': line, - 'displayPath': displayPath, - 'uriPath': uriPath, - if (traces != null) - 'traces': [for (var trace in traces!) trace.toJson()], - }; - - static List? _decodeEdits(List? json) => - json == null ? null : [for (var edit in json) EditLink.fromJson(edit)]; - - static List? _decodeTraces(List? json) => - json == null ? null : [for (var trace in json) Trace.fromJson(trace)]; -} - -/// Information about a single link that should be included in the -/// "Edit Details" view of the migration preview tool, where the purpose of the -/// link is to allow the user to make a change to the source file (e.g. to add -/// or remove a hint). -class EditLink { - /// Description of the change to be performed. - final String? description; - - /// The href to link to. - final String? href; - - EditLink({required this.description, required this.href}); - - EditLink.fromJson(dynamic json) - : description = json['description'] as String?, - href = json['href'] as String?; - - Map toJson() => { - 'description': description, - 'href': href, - }; -} - -/// Information about a single link that should be included in the -/// "Edit Details" view of the migration preview tool, where the purpose of the -/// link is to allow the user to navigate to a source file containing -/// information about the rationale for a change. -class TargetLink { - /// The href to link to. - final String? href; - - /// The line number of the link. - final int? line; - - /// Relative path to the source file (intended for display). - final String? path; - - TargetLink({required this.href, required this.line, required this.path}); - - TargetLink.fromJson(dynamic json) - : href = json['href'] as String?, - line = json['line'] as int?, - path = json['path'] as String?; - - Map toJson() => { - 'href': href, - 'line': line, - 'path': path, - }; -} - -/// A trace of why a nullability decision was made. -class Trace { - /// Text description of the trace. - final String? description; - - /// List of trace entries. - final List entries; - - Trace({required this.description, required this.entries}); - - Trace.fromJson(dynamic json) - : description = json['description'] as String?, - entries = [ - for (var entry in json['entries'] as List) - TraceEntry.fromJson(entry) - ]; - - Map toJson() => { - 'description': description, - 'entries': [for (var entry in entries) entry.toJson()] - }; -} - -/// Information about a single entry in a nullability trace. -class TraceEntry { - /// Text description of the entry. - final String? description; - - /// The function associated with the entry. We display this before the link - /// so that the trace has the familiar appearance of a stacktrace. - /// - /// Null if not known. - final String? function; - - /// Source code location associated with the entry, or `null` if no source - /// code location is known. - final TargetLink? link; - - /// The hint actions available to affect this entry of the trace, or `[]` if - /// none. - final List hintActions; - - TraceEntry( - {required this.description, - this.function, - this.link, - this.hintActions = const []}); - - TraceEntry.fromJson(dynamic json) - : description = json['description'] as String?, - function = json['function'] as String?, - link = _decodeLink(json['link']), - hintActions = (json['hintActions'] as List?) - ?.map((value) => - HintAction.fromJson(value as Map)) - .toList() ?? - const []; - - Map toJson() => { - 'description': description, - if (function != null) 'function': function, - if (link != null) 'link': link!.toJson(), - if (hintActions.isNotEmpty) - 'hintActions': hintActions.map((action) => action.toJson()).toList() - }; - - static TargetLink? _decodeLink(dynamic json) => - json == null ? null : TargetLink.fromJson(json); -} diff --git a/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart b/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart deleted file mode 100644 index d9174a857dc4..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/web/file_details.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2020, 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. - -/// Information about an item that should show up in the "proposed edits" panel. -class EditListItem { - /// Line number of this edit. - final int? line; - - /// Human-readable explanation of this edit. - final String? explanation; - - /// File offset of this edit - final int? offset; - - EditListItem( - {required this.line, required this.explanation, required this.offset}); - - EditListItem.fromJson(dynamic json) - : line = json['line'] as int?, - explanation = json['explanation'] as String?, - offset = json['offset'] as int?; - - Map toJson() => - {'line': line, 'explanation': explanation, 'offset': offset}; -} - -/// Information about how a single file should be migrated. -class FileDetails { - /// HTML representation of the source file with spans added to represent - /// added, removed, and unchanged file regions. - /// - /// TODO(paulberry): this should be replaced by a more neutral data structure. - final String? regions; - - /// HTML representation of the source file with links added to allow - /// navigation through source files. - /// - /// Also contains line number text and the anchors targeted by links that link - /// to a specific line number. - /// - /// TODO(paulberry): this should be replaced by a more neutral data structure. - final String? navigationContent; - - /// Textual representation of the source file, including both added and - /// removed text. - final String? sourceCode; - - /// Items that should show up in the "proposed edits" panel for the file. - /// - /// Map keys are headers, and values are the list of edits under each header. - /// Map order is important--entries appearing earlier in the map are - /// considered more likely to be of interest to the user. - final Map> edits; - - FileDetails( - {required this.regions, - required this.navigationContent, - required this.sourceCode, - required this.edits}); - - FileDetails.empty() - : regions = '', - navigationContent = '', - sourceCode = '', - edits = const {}; - - FileDetails.fromJson(dynamic json) - : regions = json['regions'] as String?, - navigationContent = json['navigationContent'] as String?, - sourceCode = json['sourceCode'] as String?, - edits = { - for (var entry in (json['edits'] as Map).entries) - entry.key: [ - for (var edit in entry.value as Iterable) - EditListItem.fromJson(edit) - ] - }; - - Map toJson() => { - 'regions': regions, - 'navigationContent': navigationContent, - 'sourceCode': sourceCode, - 'edits': { - for (var entry in edits.entries) - entry.key: [for (var edit in entry.value) edit.toJson()] - } - }; -} diff --git a/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart b/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart deleted file mode 100644 index 4aeeb05d4e50..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/web/highlight_js.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:html'; -import 'dart:js'; - -final HighlightJs hljs = HighlightJs._(); - -/// A small wrapper around the JavaScript highlight.js APIs. -class HighlightJs { - static JsObject? get _hljs => context['hljs'] as JsObject?; - - HighlightJs._(); - - void highlightBlock(Element block) { - _hljs!.callMethod('highlightBlock', [block]); - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/web/migration.dart b/pkg/nnbd_migration/lib/src/front_end/web/migration.dart deleted file mode 100644 index 927f64ab47a2..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/web/migration.dart +++ /dev/null @@ -1,1151 +0,0 @@ -// Copyright (c) 2020, 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. - -// Explicitly opt out this file from null safety, for build reasons, inside -// Google. - -import 'dart:async'; -import 'dart:convert'; -import 'dart:html'; - -import 'package:nnbd_migration/src/front_end/web/edit_details.dart'; -import 'package:nnbd_migration/src/front_end/web/file_details.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:path/path.dart' as p; - -import 'highlight_js.dart'; - -// TODO(devoncarew): Fix the issue where we can't load source maps. - -// TODO(devoncarew): Include a favicon. - -void main() { - document.addEventListener('DOMContentLoaded', (event) { - var path = window.location.pathname; - var offset = getOffset(window.location.href); - var lineNumber = getLine(window.location.href); - loadNavigationTree(); - if (path != '/' && path != rootPath) { - // TODO(srawlins): replaceState? - loadFile(path!, offset, lineNumber, true, callback: () { - pushState(path, offset, lineNumber); - }); - } - - final applyMigrationButton = document.querySelector('.apply-migration')!; - applyMigrationButton.onClick.listen((event) { - if (window.confirm( - "This will apply the changes you've previewed to your working " - 'directory. It is recommended you commit any changes you made before ' - 'doing this.')) { - var navigationTreeJson = [ - for (var entity in navigationTree!) entity.toJson() - ]; - doPost('/apply-migration', {'navigationTree': navigationTreeJson}) - .then((xhr) { - document.body!.classes - ..remove('proposed') - ..add('applied'); - }).catchError((Object e, st) { - handleError("Couldn't apply migration", e, st); - }); - } - }); - - final rerunMigrationButton = document.querySelector('.rerun-migration')!; - rerunMigrationButton.onClick.listen((event) async { - try { - document.body!.classes.add('rerunning'); - var response = await doPost('/rerun-migration'); - if (response!['success'] as bool) { - window.location.reload(); - } else { - handleRerunFailure(response['errors'] as List); - } - } catch (e, st) { - handleError('Failed to rerun migration', e, st); - } finally { - document.body!.classes.remove('rerunning'); - } - }); - - final reportProblemButton = document.querySelector('.report-problem')!; - reportProblemButton.onClick.listen((_) { - window.open(getGitHubProblemUri().toString(), 'report-problem'); - }); - - document.querySelector('.popup-pane .close')!.onClick.listen( - (_) => document.querySelector('.popup-pane')!.style.display = 'none'); - - migrateUnitStatusIcon!.onClick.listen((MouseEvent event) { - var unitPath = unitName!.innerText; - var unitNavItem = document - .querySelector('.nav-panel [data-name*="${Css.escape(unitPath)}"]')! - .parentNode as Element; - var statusIcon = unitNavItem.querySelector('.status-icon'); - var entity = navigationTree!.find(unitPath); - if (entity is NavigationTreeFileNode && - entity.migrationStatusCanBeChanged!) { - toggleFileMigrationStatus(entity); - updateIconsForNode(statusIcon, entity); - updateParentIcons(unitNavItem, entity); - } - }); - }); - - window.addEventListener('popstate', (event) { - var path = window.location.pathname!; - var offset = getOffset(window.location.href); - var lineNumber = getLine(window.location.href); - if (path.length > 1) { - loadFile(path, offset, lineNumber, false); - } else { - // Blank out the page, for the index screen. - writeCodeAndRegions(path, FileDetails.empty(), true); - updatePage(' ', null); - } - }); -} - -/// Returns the "authToken" query parameter value of the current location. -// TODO(srawlins): This feels a little fragile, as the user can accidentally -// change/remove this text, and break their session. Normally auth tokens are -// stored in cookies, but there is no authentication step during which the -// server would attach such a token to cookies. We could do a little step where -// the first request to the server with the token is considered -// "authentication", and we subsequently store the token in cookies thereafter. -final String? authToken = - Uri.parse(window.location.href).queryParameters['authToken']; - -final Element? editListElement = - document.querySelector('.edit-list .panel-content'); - -final Element? editListHeading = - document.querySelector('.edit-list .panel-heading'); - -final Element? editPanel = document.querySelector('.edit-panel .panel-content'); - -final Element? footerPanel = document.querySelector('footer'); - -final Element? headerPanel = document.querySelector('header'); - -final Element? migrateUnitStatusIcon = - document.querySelector('#migrate-unit-status-icon'); - -final Element? migrateUnitStatusIconLabel = - document.querySelector('#migrate-unit-status-icon-label'); - -List? navigationTree; - -final Element? unitName = document.querySelector('#unit-name'); - -String get rootPath => querySelector('.root')!.text!.trim(); - -String? get sdkVersion => document.getElementById('sdk-version')!.text; - -void addArrowClickHandler(Element arrow, {bool startsCollapsed = false}) { - var childList = (arrow.parentNode as Element).querySelector(':scope > ul')!; - // Animating height from "auto" to "0" is not supported by CSS [1], so all we - // have are hacks. The `* 2` allows for events in which the list grows in - // height when resized, with additional text wrapping. - // [1] https://css-tricks.com/using-css-transitions-auto-dimensions/ - childList.style.maxHeight = '${childList.offsetHeight * 2}px'; - - void collapse() { - childList.classes.add('collapsed'); - arrow.classes.add('collapsed'); - } - - void expand() { - childList.classes.remove('collapsed'); - arrow.classes.remove('collapsed'); - } - - arrow.onClick.listen((MouseEvent event) { - if (!childList.classes.contains('collapsed')) { - collapse(); - } else { - expand(); - } - }); - if (startsCollapsed) collapse(); -} - -void addClickHandlers(String selector, bool clearEditDetails) { - var parentElement = document.querySelector(selector)!; - - // Add navigation handlers for navigation links in the source code. - List navLinks = parentElement.querySelectorAll('.nav-link'); - for (var link in navLinks) { - link.onClick.listen((event) => handleNavLinkClick(event, clearEditDetails)); - } - - List regions = parentElement.querySelectorAll('.region'); - if (regions.isNotEmpty) { - var table = parentElement.querySelector('table[data-path]')!; - var path = table.dataset['path']; - for (var anchor in regions) { - anchor.onClick.listen((event) { - var offset = int.parse(anchor.dataset['offset']!); - var line = int.parse(anchor.dataset['line']!); - loadAndPopulateEditDetails(path!, offset, line); - }); - } - } - - List addHintLinks = parentElement.querySelectorAll('.add-hint-link'); - for (var link in addHintLinks) { - link.onClick.listen(handleAddHintLinkClick); - } -} - -/// Creates an icon using a `` element and the Material Icons font. -Element createIcon([String name = '']) { - return document.createElement('span') - ..classes.add('material-icons') - ..innerText = name; -} - -/// Perform a GET request on the path, return the json decoded response. -/// -/// Returns a T so that the various json objects can be requested (lists, maps, -/// etc.). -Future doGet(String path, - {Map queryParameters = const {}}) => - doRequest(HttpRequest() - ..open('GET', pathWithQueryParameters(path, queryParameters), async: true) - ..setRequestHeader('Content-Type', 'application/json; charset=UTF-8')); - -/// Perform a POST request on the path, return the JSON-decoded response. -Future?> doPost(String path, [Object? body]) => doRequest( - HttpRequest() - ..open('POST', pathWithQueryParameters(path, {}), async: true) - ..setRequestHeader('Content-Type', 'application/json; charset=UTF-8'), - body); - -/// Execute the [HttpRequest], handle its error codes, and return or throw the -/// response. -/// -/// This is preferable over helper methods on [HttpRequest] because they ignore -/// the response body on a non-200 code. We want to get that response body in -/// that case, though, because it may be an error response from the server with -/// useful debugging information (stack trace etc). -Future doRequest(HttpRequest xhr, [Object? body]) async { - var completer = Completer(); - xhr.onLoad.listen((e) { - completer.complete(xhr); - }); - - xhr.onError.listen(completer.completeError); - - xhr.send(body == null ? null : jsonEncode(body)); - - try { - await completer.future; - } catch (e, st) { - if (xhr.readyState == HttpRequest.DONE && xhr.status == 0) { - // Request completed with error, and no status information. Most likely - // the server has terminated. - throw UserError('Error reaching migration preview server', ''' -This usually happens because the migration preview server has exited. For -example it may have been aborted with Ctrl-C, or you may have completed this -migration, or an exception may have occurred. Please check the console where -you invoked `dart migrate` to verify that the preview server is still running. -'''); - } else { - // The attempt to connect to the server failed in an unexpected way. - // Report as many details as possible so that the user's bug report will - // be easier to address. - var details = [ - 'readyState=${xhr.readyState}', - 'responseText=${jsonEncode(xhr.responseText)}', - 'responseType=${jsonEncode(xhr.responseType)}', - 'responseUrl=${jsonEncode(xhr.responseUrl)}', - 'status=${xhr.status}', - 'statusText=${jsonEncode(xhr.statusText)}', - ] - .map((detail) => - detail.length > 40 ? '${detail.substring(0, 40)}...' : detail) - .join(', '); - throw AsyncError('Error reaching migration preview server: $details', st); - } - } - - if (xhr.status == 401) { - // Server returned "unauthorized". It's not useful to try to decode the - // response text (since it's in HTML, not JSON). Just tell the user what - // happened. - throw UserError('Unauthorized response from migration preview server', ''' -The migration preview server has detected a mismatch between the authToken in -your URL and the token that was generated at the time that `dart migrate` was -run. Have you restarted the migration server recently? If so, you'll need to -check its output for a fresh URL, and use that URL to perform your migration. -'''); - } - final Object? json = jsonDecode(xhr.responseText!); - if (xhr.status == 200) { - // Request OK. - return json as T?; - } else { - throw json!; - } -} - -/// Returns the URL of the "new issue" form for the SDK repository, -/// pre-populating the title, some labels, using [description], [exception], and -/// [stackTrace] in the body. -Uri getGitHubErrorUri( - String description, Object? exception, Object? stackTrace) => - Uri.https('github.com', 'dart-lang/sdk/issues/new', { - 'title': 'Customer-reported issue with null safety migration tool: ' - '$description', - 'labels': 'area-analyzer,analyzer-nnbd-migration,type-bug', - 'body': ''' -$description - -Error: $exception - -Please fill in the following: - -**Name of package being migrated (if public)**: -**What I was doing when this issue occurred**: -**Is it possible to work around this issue**: -**Has this issue happened before, and if so, how often**: -**Dart SDK version**: $sdkVersion -**Additional details**: - -Thanks for filing! - -Stacktrace: _auto populated by migration preview tool._ - -``` -$stackTrace -``` -''', - }); - -/// Returns the URL of the "new issue" form for the SDK repository, -/// pre-populating some labels and a body template. -Uri getGitHubProblemUri() => - Uri.https('github.com', 'dart-lang/sdk/issues/new', { - 'title': 'Customer-reported issue with null safety migration tool', - 'labels': 'area-analyzer,analyzer-nnbd-migration,type-bug', - 'body': ''' -#### Steps to reproduce - -#### What did you expect to happen? - -#### What actually happened? - -_Screenshots are appreciated_ - -**Dart SDK version**: $sdkVersion - -Thanks for filing! -''', - }); - -int? getLine(String location) { - var str = Uri.parse(location).queryParameters['line']; - return str == null ? null : int.tryParse(str); -} - -int? getOffset(String location) { - var str = Uri.parse(location).queryParameters['offset']; - return str == null ? null : int.tryParse(str); -} - -void handleAddHintLinkClick(MouseEvent event) async { - var path = (event.currentTarget as Element).getAttribute('href')!; - - // Don't navigate on link click. - event.preventDefault(); - - try { - var previousScrollPosition = _getCurrentScrollPosition(); - // Directing the server to produce an edit; request it, then do work with - // the response. - await doPost(path); - await loadFile(window.location.pathname!, null, null, false); - document.body!.classes.add('needs-rerun'); - _scrollContentTo(previousScrollPosition); - } catch (e, st) { - handleError("couldn't add/remove hint", e, st); - } -} - -void handleError(String header, Object exception, Object? stackTrace) { - String? subheader; - Object? details; - if (exception is Map && - exception['success'] == false && - exception.containsKey('exception') && - exception.containsKey('stackTrace')) { - subheader = exception['exception'] as String?; - stackTrace = exception['stackTrace']; - } else if (exception is UserError) { - subheader = exception.message; - // Don't show the user a stacktrace; show them the detailed error message - // text instead. - details = exception.details; - } else { - subheader = exception.toString(); - } - // If there was no detailed error message, use the stacktrace instead. - details ??= stackTrace; - final popupPane = document.querySelector('.popup-pane')!; - popupPane.querySelector('h2')!.innerText = header; - popupPane.querySelector('p')!.innerText = subheader!; - popupPane.querySelector('pre')!.innerText = details.toString(); - var bottom = popupPane.querySelector('a.bottom') as AnchorElement; - bottom - ..href = getGitHubErrorUri(header, subheader, stackTrace).toString() - ..style.display = 'initial'; - popupPane.style.display = 'initial'; - logError('$header: $exception', stackTrace); -} - -void handleNavLinkClick(MouseEvent event, bool clearEditDetails) { - Element target = event.currentTarget as Element; - event.preventDefault(); - - var location = target.getAttribute('href')!; - var path = _stripQuery(location); - - var offset = getOffset(location); - var lineNumber = getLine(location); - - if (offset != null) { - navigate(path, offset, lineNumber, clearEditDetails, callback: () { - pushState(path, offset, lineNumber); - }); - } else { - navigate(path, null, null, clearEditDetails, callback: () { - pushState(path, null, null); - }); - } -} - -void handleRerunFailure(List errors) { - final popupPane = document.querySelector('.popup-pane')!; - popupPane.querySelector('h2')!.innerText = 'Failed to rerun from sources'; - popupPane.querySelector('p')!.innerText = - 'Sources contain static analysis errors:'; - popupPane.querySelector('pre')!.innerText = errors.cast().map((error) { - return '${error['severity']} - ${error['message']} ' - 'at ${error['location']} - (${error['code']})'; - }).join('\n'); - popupPane.querySelector('a.bottom')!.style.display = 'none'; - popupPane.style.display = 'initial'; - - // TODO(srawlins): I think we should lock down the entire web UI, except for - // the "Rerun from source" button. -} - -void highlightAllCode() { - document.querySelectorAll('.code').forEach((Element block) { - hljs.highlightBlock(block); - }); -} - -/// Loads the explanation for [region], into the ".panel-content" div. -void loadAndPopulateEditDetails(String path, int? offset, int? line) async { - try { - final responseJson = await doGet>(path, - queryParameters: {'region': 'region', 'offset': '$offset'}); - var response = EditDetails.fromJson(responseJson); - populateEditDetails(response); - pushState(path, offset, line); - addClickHandlers('.edit-panel .panel-content', false); - } catch (e, st) { - handleError("couldn't load edit details", e, st); - } -} - -/// Loads the file at [path] from the server, optionally scrolling [offset] into -/// view. -Future loadFile( - String path, - int? offset, - int? line, - bool clearEditDetails, { - VoidCallback? callback, -}) async { - // Handle the case where we're requesting a directory. - if (!path.endsWith('.dart')) { - writeCodeAndRegions(path, FileDetails.empty(), clearEditDetails); - updatePage(path); - if (callback != null) { - callback(); - } - - return; - } - - try { - // Navigating to another file; request it, then do work with the response. - final response = await doGet>(path, - queryParameters: {'inline': 'true'}); - writeCodeAndRegions(path, FileDetails.fromJson(response), clearEditDetails); - maybeScrollToAndHighlight(offset, line); - var filePathPart = _stripQuery(path); - updatePage(filePathPart, offset); - if (callback != null) { - callback(); - } - } catch (e, st) { - handleError("couldn't load dart file $path", e, st); - } -} - -/// Load the navigation tree into the ".nav-tree" div. -void loadNavigationTree() async { - var path = '/_preview/navigationTree.json'; - - // Request the navigation tree, then do work with the response. - try { - final response = (await doGet>(path))!; - var navTree = document.querySelector('.nav-tree')!; - navTree.text = ''; - navigationTree = NavigationTreeNode.listFromJson(response); - writeNavigationSubtree(navTree, navigationTree!, - enablePartialMigration: true); - } catch (e, st) { - handleError("couldn't load navigation tree", e, st); - } -} - -void logError(Object e, Object? st) { - window.console.error('$e'); - window.console.error('$st'); -} - -/// Scroll an element into view if it is not visible. -void maybeScrollIntoView(Element element) { - var rect = element.getBoundingClientRect(); - // A line of text in the code view is 14px high. Including it here means we - // only choose to _not_ scroll a line of code into view if the entire line is - // visible. - var lineHeight = 14; - var visibleCeiling = headerPanel!.offsetHeight + lineHeight; - var visibleFloor = - window.innerHeight! - (footerPanel!.offsetHeight + lineHeight); - if (rect.bottom > visibleFloor) { - element.scrollIntoView(); - } else if (rect.top < visibleCeiling) { - element.scrollIntoView(); - } -} - -/// Scrolls target with id [offset] into view if it is not currently in view. -/// -/// Falls back to [lineNumber] if a target with id "o$offset" does not exist. -/// -/// Also adds the "target" class, highlighting the target, and the "highlight" -/// class to the entire line on which the target lies. -/// -/// If [offset] is null, instead scrolls to the top of the file. -void maybeScrollToAndHighlight(int? offset, int? lineNumber) { - Element? target; - Element? line; - - if (offset != null) { - target = document.getElementById('o$offset'); - line = document.querySelector('.line-$lineNumber'); - if (target != null) { - maybeScrollIntoView(target); - target.classes.add('target'); - } else if (line != null) { - // If the target doesn't exist, but the line does, scroll that into view - // instead. - maybeScrollIntoView(line.parent!); - } - if (line != null) { - (line.parentNode as Element).classes.add('highlight'); - } - } else { - // If no offset is given, this is likely a navigation link, and we need to - // scroll back to the top of the page. - var lines = document.querySelectorAll('.line-no'); - if (lines.isEmpty) { - // I don't see how this could happen, but return anyhow. - return; - } - maybeScrollIntoView(lines.first); - } -} - -/// Navigate to [path] and optionally scroll [offset] into view. -/// -/// If [callback] is present, it will be called after the server response has -/// been processed, and the content has been updated on the page. -void navigate( - String? path, - int? offset, - int? lineNumber, - bool clearEditDetails, { - VoidCallback? callback, -}) { - var currentOffset = getOffset(window.location.href); - var currentLineNumber = getLine(window.location.href); - removeHighlight(currentOffset, currentLineNumber); - if (path == window.location.pathname) { - // Navigating to same file; just scroll into view. - maybeScrollToAndHighlight(offset, lineNumber); - if (callback != null) { - callback(); - } - } else { - loadFile(path!, offset, lineNumber, clearEditDetails, callback: callback); - } -} - -/// Returns [path], which may include query parameters, with a new path which -/// adds (or replaces) parameters from [queryParameters]. -/// -/// Additionally, the "authToken" parameter will be added with the authToken -/// found in the current location. -String pathWithQueryParameters( - String path, Map queryParameters) { - var uri = Uri.parse(path); - var mergedQueryParameters = { - ...uri.queryParameters, - ...queryParameters, - 'authToken': authToken - }; - return uri.replace(queryParameters: mergedQueryParameters).toString(); -} - -String pluralize(int count, String single, {String? multiple}) { - return count == 1 ? single : (multiple ?? '${single}s'); -} - -void populateEditDetails([EditDetails? response]) { - // Clear out any current edit details. - editPanel!.text = ''; - if (response == null) { - Element p = ParagraphElement() - ..text = 'See details about a proposed edit.' - ..classes = ['placeholder']; - editPanel!.append(p); - p.scrollIntoView(); - return; - } - - var fileDisplayPath = response.displayPath!; - var parentDirectory = p.dirname(fileDisplayPath); - - // 'Changed ... at foo.dart:12.' - var explanationMessage = response.explanation; - var relPath = p.relative(fileDisplayPath, from: rootPath); - var line = response.line; - Element explanation = document.createElement('p'); - editPanel!.append(explanation); - explanation - ..appendText('$explanationMessage at ') - ..append(AnchorElement( - href: pathWithQueryParameters( - response.uriPath!, {'line': line.toString()})) - ..appendText('$relPath:$line.')); - explanation.scrollIntoView(); - _populateEditTraces(response, editPanel, parentDirectory); - _populateEditLinks(response, editPanel); -} - -/// Write the contents of the Edit List, from JSON data [editListData]. -void populateProposedEdits( - String path, Map> edits, bool clearEditDetails) { - editListElement!.text = ''; - - var editCount = edits.length; - - if (editCount < 2) { - editListHeading!.innerText = 'Proposed Edits'; - } else { - int total = edits.entries.fold(0, (sum, edit) => sum + edit.value.length); - editListHeading!.innerText = '$total Proposed Edits'; - } - - if (editCount == 0) { - Element p = document.createElement('p'); - editListElement!.append(p); - p.append(Text('No proposed edits')); - } else { - for (var entry in edits.entries) { - Element p = document.createElement('p'); - editListElement!.append(p); - p.append(Text('${entry.key}:')); - - Element list = document.createElement('ul'); - editListElement!.append(list); - for (var edit in entry.value) { - Element item = document.createElement('li'); - list.append(item); - item.classes.add('edit'); - AnchorElement anchor = AnchorElement(); - item.append(anchor); - anchor.classes.add('edit-link'); - var offset = edit.offset; - anchor.dataset['offset'] = '$offset'; - var line = edit.line; - anchor.dataset['line'] = '$line'; - anchor.append(Text('line $line')); - anchor.setAttribute( - 'href', - pathWithQueryParameters(window.location.pathname!, { - 'line': '$line', - 'offset': '$offset', - })); - anchor.onClick.listen((MouseEvent event) { - event.preventDefault(); - navigate(window.location.pathname, offset, line, true, callback: () { - pushState(window.location.pathname, offset, line); - }); - loadAndPopulateEditDetails(path, offset, line); - }); - item.append(Text(': ${edit.explanation}')); - } - } - } - - if (clearEditDetails) { - populateEditDetails(); - } -} - -void pushState(String? path, int? offset, int? line) { - var uri = Uri.parse('${window.location.origin}$path'); - - var params = { - if (offset != null) 'offset': '$offset', - if (line != null) 'line': '$line', - 'authToken': authToken, - }; - - uri = uri.replace(queryParameters: params); - window.history.pushState({}, '', uri.toString()); -} - -/// If [path] lies within [root], return the relative path of [path] from [root]. -/// Otherwise, return [path]. -String relativePath(String path) { - var root = '${querySelector('.root')!.text!}/'; - if (path.startsWith(root)) { - return path.substring(root.length); - } else { - return path; - } -} - -/// Remove highlighting from [offset]. -void removeHighlight(int? offset, int? lineNumber) { - if (offset != null) { - var anchor = document.getElementById('o$offset'); - if (anchor != null) { - anchor.classes.remove('target'); - } - } - if (lineNumber != null) { - var line = document.querySelector('.line-$lineNumber'); - if (line != null) { - line.parent!.classes.remove('highlight'); - } - } -} - -void toggleDirectoryMigrationStatus(NavigationTreeDirectoryNode entity) { - switch (entity.migrationStatus) { - case UnitMigrationStatus.alreadyMigrated: - // This tree cannot be toggled. - break; - case UnitMigrationStatus.migrating: - // At least one child file is 'migrating' (some may be 'already - // migrated'). Toggle all 'migrating' children to opt out. - entity.toggleChildrenToOptOut(); - break; - case UnitMigrationStatus.optingOut: - // At least one child file is 'opting out' (some may be 'already - // migrated'). Toggle all 'migrating' children to migrate. - entity.toggleChildrenToMigrate(); - break; - case UnitMigrationStatus.indeterminate: - // At least one child file is 'migrating' and at least one child file is - // 'opting out' (some may be 'already migrated'). Toggle all 'migrating' - // children to migrate. - entity.toggleChildrenToMigrate(); - } -} - -void toggleFileMigrationStatus(NavigationTreeFileNode entity) { - switch (entity.migrationStatus) { - case UnitMigrationStatus.alreadyMigrated: - // This file cannot be toggled. - break; - case UnitMigrationStatus.migrating: - entity.migrationStatus = UnitMigrationStatus.optingOut; - break; - case UnitMigrationStatus.optingOut: - entity.migrationStatus = UnitMigrationStatus.migrating; - break; - case UnitMigrationStatus.indeterminate: - throw StateError('File ${entity.path} should not have ' - 'indeterminate migration status'); - default: - // TODO(paulberry): this should never happen. - break; - } -} - -/// Updates [icon] according to [status]. -void updateIconForStatus(Element? icon, UnitMigrationStatus? status) { - switch (status) { - case UnitMigrationStatus.alreadyMigrated: - icon!.innerText = 'check_box'; - icon.classes.add('already-migrated'); - icon.classes.add('disabled'); - icon.setAttribute('title', 'Already migrated'); - break; - case UnitMigrationStatus.migrating: - icon!.innerText = 'check_box'; - icon.classes.remove('opted-out'); - icon.classes.add('migrating'); - icon.setAttribute('title', 'Migrating to null safety'); - break; - case UnitMigrationStatus.optingOut: - icon!.innerText = 'check_box_outline_blank'; - icon.classes.remove('migrating'); - icon.classes.add('opted-out'); - icon.setAttribute('title', 'Opting out of null safety'); - break; - default: - icon!.innerText = 'indeterminate_check_box'; - icon.classes.remove('migrating'); - // 'opted-out' is the same style as 'indeterminate'. - icon.classes.add('opted-out'); - icon.setAttribute( - 'title', "Mixed statuses of 'migrating' and 'opting out'"); - break; - } -} - -/// Updates the navigation [icon] and current file icon according to the current -/// migration status of [entity]. -void updateIconsForNode(Element? icon, NavigationTreeNode entity) { - var status = entity.migrationStatus; - updateIconForStatus(icon, status); - // Update the status at the top of the file view if [entity] represents the - // current file. - var unitPath = unitName!.innerText; - if (entity.path == unitPath) { - if (entity is NavigationTreeFileNode && - !entity.migrationStatusCanBeChanged!) { - icon!.classes.add('disabled'); - } else { - icon!.classes.remove('disabled'); - } - updateIconForStatus(migrateUnitStatusIcon, status); - } -} - -/// Update the heading and navigation links. -/// -/// Call this after updating page content on a navigation. -void updatePage(String path, [int? offset]) { - path = relativePath(path); - // Update page heading. - unitName!.text = path; - // Update navigation styles. - document.querySelectorAll('.nav-panel .nav-link').forEach((Element link) { - var name = link.dataset['name']; - if (name == path) { - link.classes.add('selected-file'); - } else { - link.classes.remove('selected-file'); - } - }); - // Note: navigationTree might not be loaded yet if the user is clicking around - // fast, so we need to allow for the possibility that `navigationTree` might - // be `null`. - var entity = navigationTree?.find(path); - // Update migration status for files in current migration. - if (entity == null) { - migrateUnitStatusIconLabel!.classes.remove('visible'); - } else { - migrateUnitStatusIconLabel!.classes.add('visible'); - updateIconForStatus(migrateUnitStatusIcon, entity.migrationStatus); - } -} - -/// Updates the parent icons of [entity] with list item [element] in the -/// navigation tree. -void updateParentIcons(Element element, NavigationTreeNode entity) { - var parent = entity.parent; - if (parent != null) { - var parentElement = (element.parentNode as Element).parentNode as Element; - var statusIcon = parentElement.querySelector(':scope > .status-icon'); - updateIconsForNode(statusIcon, parent); - updateParentIcons(parentElement, parent); - } -} - -/// Updates subtree icons for the children [entity] with list item [element]. -void updateSubtreeIcons(Element? element, NavigationTreeDirectoryNode entity) { - for (var child in entity.subtree!) { - var childNode = - element!.querySelector('[data-name*="${Css.escape(child.path!)}"]'); - if (child is NavigationTreeDirectoryNode) { - updateSubtreeIcons(childNode, child); - var childIcon = childNode!.querySelector(':scope > .status-icon'); - updateIconsForNode(childIcon, entity); - } else { - var childIcon = (childNode!.parentNode as Element) - .querySelector(':scope > .status-icon'); - updateIconsForNode(childIcon, child); - } - } -} - -/// Load data from [data] into the .code and the .regions divs. -void writeCodeAndRegions(String path, FileDetails data, bool clearEditDetails) { - var regionsElement = document.querySelector('.regions')!; - var codeElement = document.querySelector('.code')!; - - _PermissiveNodeValidator.setInnerHtml(regionsElement, data.regions); - _PermissiveNodeValidator.setInnerHtml(codeElement, data.navigationContent); - populateProposedEdits(path, data.edits, clearEditDetails); - - // highlightAllCode is remarkably slow (about 4 seconds to handle a 300k file - // on a Pixelbook), so skip it for large files. - if (data.sourceCode!.length < 200000) { - highlightAllCode(); - } - addClickHandlers('.code', true); - addClickHandlers('.regions', true); -} - -void writeNavigationSubtree( - Element parentElement, List tree, - {bool enablePartialMigration = false}) { - Element ul = document.createElement('ul'); - parentElement.append(ul); - for (var entity in tree) { - Element li = document.createElement('li'); - ul.append(li); - if (entity is NavigationTreeDirectoryNode) { - li.classes.add('dir'); - li.dataset['name'] = entity.path!; - Element arrow = document.createElement('span'); - li.append(arrow); - arrow.classes.add('arrow'); - arrow.text = '\u25BC'; - var folderIcon = createIcon('folder_open'); - li.append(folderIcon); - li.append(Text(entity.name!)); - writeNavigationSubtree(li, entity.subtree!, - enablePartialMigration: enablePartialMigration); - if (enablePartialMigration) { - var statusIcon = createIcon('indeterminate_check_box') - ..classes.add('status-icon'); - updateIconsForNode(statusIcon, entity); - statusIcon.onClick.listen((MouseEvent event) { - toggleDirectoryMigrationStatus(entity); - updateSubtreeIcons(li, entity); - updateIconsForNode(statusIcon, entity); - updateParentIcons(li, entity); - }); - li.insertBefore(statusIcon, folderIcon); - } - addArrowClickHandler(arrow, - startsCollapsed: - entity.migrationStatus == UnitMigrationStatus.alreadyMigrated); - } else if (entity is NavigationTreeFileNode) { - if (enablePartialMigration) { - var statusIcon = createIcon()..classes.add('status-icon'); - if (!entity.migrationStatusCanBeChanged!) { - statusIcon.classes.add('disabled'); - } - updateIconsForNode(statusIcon, entity); - if (entity.migrationStatusCanBeChanged!) { - statusIcon.onClick.listen((MouseEvent event) { - toggleFileMigrationStatus(entity); - updateIconsForNode(statusIcon, entity); - updateParentIcons(li, entity); - }); - } - li.append(statusIcon); - } - li.append(createIcon('insert_drive_file')); - Element a = document.createElement('a'); - li.append(a); - a.classes.add('nav-link'); - a.dataset['name'] = entity.path!; - a.setAttribute('href', pathWithQueryParameters(entity.href!, {})); - a.append(Text(entity.name!)); - a.onClick.listen((MouseEvent event) => handleNavLinkClick(event, true)); - var editCount = entity.editCount!; - if (editCount > 0) { - Element editsBadge = document.createElement('span'); - li.append(editsBadge); - editsBadge.classes.add('edit-count'); - editsBadge.setAttribute( - 'title', '$editCount ${pluralize(editCount, 'proposed edit')}'); - editsBadge.append(Text(editCount.toString())); - } - } - } -} - -void _addHintAction(HintAction hintAction, Node drawer, TargetLink? link) { - drawer.append(ButtonElement() - ..onClick.listen((event) async { - try { - var previousScrollPosition = _getCurrentScrollPosition(); - await doPost( - pathWithQueryParameters('/apply-hint', {}), hintAction.toJson()); - var path = _stripQuery(link!.href!); - await loadFile(path, null, link.line, false); - document.body!.classes.add('needs-rerun'); - _scrollContentTo(previousScrollPosition); - } catch (e, st) { - handleError("couldn't apply hint", e, st); - } - }) - ..appendText(hintAction.kind.description!)); -} - -AnchorElement _aElementForLink(TargetLink link) { - var targetLine = link.line; - AnchorElement a = AnchorElement(); - a.append(Text('${link.path}:$targetLine')); - a.setAttribute('href', link.href!); - a.classes.add('nav-link'); - return a; -} - -int _getCurrentScrollPosition() => - document.querySelector('.content')!.scrollTop; - -void _populateEditLinks(EditDetails response, Element? editPanel) { - if (response.edits == null) { - return; - } - - var subheading = editPanel!.append(document.createElement('p')); - subheading.append(document.createElement('span') - ..classes = ['type-description'] - ..append(Text('Actions'))); - subheading.append(Text(':')); - - Element editParagraph = document.createElement('p'); - editPanel.append(editParagraph); - for (var edit in response.edits!) { - Element a = document.createElement('a'); - editParagraph.append(a); - a.append(Text(edit.description!)); - a.setAttribute('href', edit.href!); - a.classes = ['add-hint-link', 'before-apply', 'button']; - } -} - -void _populateEditTraces( - EditDetails response, Element? editPanel, String parentDirectory) { - for (var trace in response.traces!) { - var traceParagraph = - editPanel!.append(document.createElement('p')..classes = ['trace']); - traceParagraph.append(document.createElement('span') - ..classes = ['type-description'] - ..append(Text(trace.description!))); - traceParagraph.append(Text(':')); - var ul = traceParagraph - .append(document.createElement('ul')..classes = ['trace']); - for (var entry in trace.entries) { - Element li = document.createElement('li'); - ul.append(li); - li.append(document.createElement('span') - ..classes = ['function'] - ..appendTextWithBreaks(entry.function ?? 'unknown')); - var link = entry.link; - if (link != null) { - li.append(Text(' (')); - li.append(_aElementForLink(link)); - li.append(Text(')')); - } - li.append(Text(': ')); - li.appendTextWithBreaks(entry.description ?? 'unknown'); - - if (entry.hintActions.isNotEmpty) { - var drawer = li.append( - document.createElement('p')..classes = ['drawer', 'before-apply']); - for (final hintAction in entry.hintActions) { - _addHintAction(hintAction, drawer, link); - } - } - } - } -} - -void _scrollContentTo(int top) => - document.querySelector('.content')!.scrollTop = top; - -String _stripQuery(String path) => - path.contains('?') ? path.substring(0, path.indexOf('?')) : path; - -class UserError extends Error implements Exception { - final String message; - - final String details; - - UserError(this.message, this.details); - - String toString() => '$message:\n$details'; -} - -class _PermissiveNodeValidator implements NodeValidator { - static _PermissiveNodeValidator instance = _PermissiveNodeValidator(); - - @override - bool allowsAttribute(Element element, String attributeName, String value) { - return true; - } - - @override - bool allowsElement(Element element) { - return true; - } - - static void setInnerHtml(Element element, String? html) { - element.setInnerHtml(html, validator: instance); - } -} - -extension on List { - /// Finds the node with path equal to [path], recursively, or `null`. - NavigationTreeNode? find(String path) { - for (var node in this) { - if (node is NavigationTreeDirectoryNode) { - var foundInSubtree = node.subtree!.find(path); - if (foundInSubtree != null) return foundInSubtree; - } else { - assert(node is NavigationTreeFileNode); - if (node.path == path) return node; - } - } - return null; - } -} - -/// An extension on Element that fits into cascades. -extension on Element { - /// Append [text] to this, inserting a word break before each '.' character. - void appendTextWithBreaks(String text) { - var textParts = text.split('.'); - append(Text(textParts.first)); - for (var substring in textParts.skip(1)) { - // Replace the '.' with a zero-width space and a '.'. - appendHtml('​.'); - append(Text(substring)); - } - } -} diff --git a/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart b/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart deleted file mode 100644 index 1c4059075bcb..000000000000 --- a/pkg/nnbd_migration/lib/src/front_end/web/navigation_tree.dart +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2020, 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. - -class NavigationTreeDirectoryNode extends NavigationTreeNode { - /// If this is a directory node, list of nodes nested under this one. - /// Otherwise `null`. - final List? subtree; - - /// Creates a navigation tree node representing a directory. - NavigationTreeDirectoryNode( - {required super.name, required super.path, required this.subtree}) - : super._(); - - /// Returns the status by examining [subtree]: - /// - /// * If all children nodes have the same status, then that status is returned. - /// * Otherwise, if all children nodes are either 'alreadyMigrated' or - /// 'migrating', then [UnitMigrationStatus.migrating] is returned. - /// * Otherwise, if all children nodes are either 'alreadyMigrated' or - /// 'opting out', then [UnitMigrationStatus.optingOut] is returned. - /// * Otherwise, [UnitMigrationStatus.indeterminate] is returned. - UnitMigrationStatus get migrationStatus { - if (subtree!.isEmpty) return UnitMigrationStatus.alreadyMigrated; - var sharedStatus = subtree!.first.migrationStatus; - var allAreMigratedOrMigrating = true; - var allAreMigratedOrOptingOut = true; - for (var child in subtree!) { - var childMigrationStatus = child.migrationStatus; - - if (childMigrationStatus != sharedStatus) { - sharedStatus = null; - } - if (childMigrationStatus != UnitMigrationStatus.alreadyMigrated && - childMigrationStatus != UnitMigrationStatus.migrating) { - allAreMigratedOrMigrating = false; - } - if (childMigrationStatus != UnitMigrationStatus.alreadyMigrated && - childMigrationStatus != UnitMigrationStatus.optingOut) { - allAreMigratedOrOptingOut = false; - } - } - if (sharedStatus != null) { - return sharedStatus; - } - if (allAreMigratedOrMigrating) { - return UnitMigrationStatus.migrating; - } - if (allAreMigratedOrOptingOut) { - // TODO(srawlins): Is this confusing? Should there be an 'optingOutStar' - // which indicates that all opted out files will remain opted out, though - // some files exist in the subtree which are already migrated. - return UnitMigrationStatus.optingOut; - } - return UnitMigrationStatus.indeterminate; - } - - NavigationTreeNodeType get type => NavigationTreeNodeType.directory; - - void setSubtreeParents() { - if (subtree != null) { - for (var child in subtree!) { - child.parent = this; - } - } - } - - /// Toggle child nodes (recursively) to migrate to null safety. - /// - /// Only child nodes with 'opting out' or 'keep opted out' status are changed. - void toggleChildrenToMigrate() { - //assert(type == NavigationTreeNodeType.directory); - for (var child in subtree!) { - if (child is NavigationTreeDirectoryNode) { - child.toggleChildrenToMigrate(); - } else if (child is NavigationTreeFileNode && - child.migrationStatusCanBeChanged! && - child.migrationStatus == UnitMigrationStatus.optingOut) { - child.migrationStatus = UnitMigrationStatus.migrating; - } - } - } - - /// Toggle child nodes (recursively) to opt out of null safety. - /// - /// Only child nodes with 'migrating' status are changed. - void toggleChildrenToOptOut() { - for (var child in subtree!) { - if (child is NavigationTreeDirectoryNode) { - child.toggleChildrenToOptOut(); - } else if (child is NavigationTreeFileNode && - child.migrationStatusCanBeChanged! && - child.migrationStatus == UnitMigrationStatus.migrating) { - child.migrationStatus = UnitMigrationStatus.optingOut; - } - } - } - - Map toJson() => { - 'type': 'directory', - 'name': name, - 'subtree': NavigationTreeNode.listToJson(subtree!), - if (path != null) 'path': path, - }; -} - -class NavigationTreeFileNode extends NavigationTreeNode { - /// If this is a file node, href that should be used if the file is clicked - /// on, otherwise `null`. - final String? href; - - /// If this is a file node, number of edits that were made in the file, - /// otherwise `null`. - final int? editCount; - - final bool? wasExplicitlyOptedOut; - - UnitMigrationStatus? migrationStatus; - - final bool? migrationStatusCanBeChanged; - - /// Creates a navigation tree node representing a file. - NavigationTreeFileNode( - {required super.name, - required super.path, - required this.href, - required this.editCount, - required this.wasExplicitlyOptedOut, - required this.migrationStatus, - required this.migrationStatusCanBeChanged}) - : super._(); - - NavigationTreeNodeType get type => NavigationTreeNodeType.file; - - Map toJson() => { - 'type': 'file', - 'name': name, - if (path != null) 'path': path, - if (href != null) 'href': href, - if (editCount != null) 'editCount': editCount, - if (wasExplicitlyOptedOut != null) - 'wasExplicitlyOptedOut': wasExplicitlyOptedOut, - if (migrationStatus != null) 'migrationStatus': migrationStatus!.index, - if (migrationStatusCanBeChanged != null) - 'migrationStatusCanBeChanged': migrationStatusCanBeChanged, - }; -} - -/// Information about a node in the migration tool's navigation tree. -abstract class NavigationTreeNode { - /// Name of the node. - final String? name; - - /// Parent of this node, or `null` if this is a top-level node. - late final NavigationTreeNode? parent; - - /// Relative path to the file or directory from the package root. - final String? path; - - factory NavigationTreeNode.fromJson(dynamic json) { - var type = _decodeType(json['type'] as String?); - if (type == NavigationTreeNodeType.directory) { - return NavigationTreeDirectoryNode( - name: json['name'] as String?, - path: json['path'] as String?, - subtree: listFromJsonOrNull(json['subtree'])) - ..setSubtreeParents(); - } else { - return NavigationTreeFileNode( - name: json['name'] as String?, - path: json['path'] as String?, - href: json['href'] as String?, - editCount: json['editCount'] as int?, - wasExplicitlyOptedOut: json['wasExplicitlyOptedOut'] as bool?, - migrationStatus: - _decodeMigrationStatus(json['migrationStatus'] as int?), - migrationStatusCanBeChanged: - json['migrationStatusCanBeChanged'] as bool?, - ); - } - } - - NavigationTreeNode._({required this.name, required this.path}); - - /// The migration status of the file or directory. - UnitMigrationStatus? get migrationStatus; - - NavigationTreeNodeType get type; - - Map toJson(); - - /// Deserializes a list of navigation tree nodes from a JSON list. - static List listFromJson(List json) => - [for (var node in json) NavigationTreeNode.fromJson(node)]; - - /// Deserializes a list of navigation tree nodes from a possibly null JSON - /// list. If the argument is `null`, `null` is returned. - static List? listFromJsonOrNull(dynamic json) => - json == null ? null : listFromJson(json as List); - - /// Serializes a list of navigation tree nodes into JSON. - static List> listToJson( - List nodes) => - [for (var node in nodes) node.toJson()]; - - static UnitMigrationStatus? _decodeMigrationStatus(int? migrationStatus) { - if (migrationStatus == null) return null; - return UnitMigrationStatus.values[migrationStatus]; - } - - static NavigationTreeNodeType _decodeType(String? json) { - switch (json) { - case 'directory': - return NavigationTreeNodeType.directory; - case 'file': - return NavigationTreeNodeType.file; - default: - throw StateError('Unrecognized navigation tree node type: $json'); - } - } -} - -/// Enum representing the different types of [NavigationTreeNode]s. -enum NavigationTreeNodeType { - directory, - file, -} - -/// Enum representing the different statuses a compilation unit can have. -enum UnitMigrationStatus { - /// Indicates that a library was already migrated to null safety at the start - /// of the current migration. - alreadyMigrated, - - /// Indicates that a directory's status is indeterminate, because the statuses - /// of it's children libraries (recursive) are mixed. - indeterminate, - - /// Indicates that a library was not migrated to null safety at the start of - /// the current migration (either the package was not opted in, or the library - /// was explicitly opted out), and that the current migration does migrate the - /// library. - migrating, - - /// Indicates that the current migration opts the library out of null safety. - /// - /// This may mean that the library is explicitly opted out with a Dart - /// language version comment, or that the package is currently opted out of - /// null safety. - optingOut, -} diff --git a/pkg/nnbd_migration/lib/src/hint_action.dart b/pkg/nnbd_migration/lib/src/hint_action.dart deleted file mode 100644 index 4c868c7635e8..000000000000 --- a/pkg/nnbd_migration/lib/src/hint_action.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2021, 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. - -/// Everything the front end needs to know to tell the server to perform a hint -/// action. -class HintAction { - final HintActionKind kind; - final int? nodeId; - HintAction(this.kind, this.nodeId); - - HintAction.fromJson(Map json) - : nodeId = json['nodeId'] as int?, - kind = HintActionKind.values - .singleWhere((action) => action.index == json['kind']); - - Map toJson() => { - 'nodeId': nodeId, - 'kind': kind.index, - }; -} - -/// Enum describing the possible hints that can be performed on an edge or a -/// node. -/// -/// Which actions are available can be built by other visitors, and the hint can -/// be applied by visitors such as EditPlanner when the user requests it from -/// the front end. -enum HintActionKind { - /// Add a `/*?*/` hint to a type. - addNullableHint, - - /// Add a `/*!*/` hint to a type. - addNonNullableHint, - - /// Change a `/*!*/` hint to a `/*?*/` hint. - changeToNullableHint, - - /// Change a `/*?*/` hint to a `/*!*/` hint. - changeToNonNullableHint, - - /// Remove a `/*?*/` hint. - removeNullableHint, - - /// Remove a `/*!*/` hint. - removeNonNullableHint, -} - -/// Extension methods to make [HintActionKind] act as a smart enum. -extension HintActionKindBehaviors on HintActionKind { - /// Get the text description of a [HintActionKind], for display to users. - String? get description { - switch (this) { - case HintActionKind.addNullableHint: - return 'Add /*?*/ hint'; - case HintActionKind.addNonNullableHint: - return 'Add /*!*/ hint'; - case HintActionKind.removeNullableHint: - return 'Remove /*?*/ hint'; - case HintActionKind.removeNonNullableHint: - return 'Remove /*!*/ hint'; - case HintActionKind.changeToNullableHint: - return 'Change to /*?*/ hint'; - case HintActionKind.changeToNonNullableHint: - return 'Change to /*!*/ hint'; - } - } -} diff --git a/pkg/nnbd_migration/lib/src/messages.dart b/pkg/nnbd_migration/lib/src/messages.dart deleted file mode 100644 index 065d8f89407f..000000000000 --- a/pkg/nnbd_migration/lib/src/messages.dart +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/migration_cli.dart'; - -const String migratedAlready = - 'All sources appear to be already migrated. Nothing to do.'; -const String nnbdExperimentOff = - 'Analyzer seems to need the null safety experiment on in the SDK.'; -const String sdkNnbdOff = - 'Analysis seems to have an SDK without null safety enabled.'; -const String sdkPathEnvironmentVariableSet = - r'Note: $SDK_PATH environment variable is set and may point to outdated ' - 'dart:core sources'; -const String unmigratedDependenciesWarning = ''' -Warning: package has unmigrated dependencies. - -Continuing due to the presence of `$_skipImportCheckFlag`. To see a complete -list of the unmigrated dependencies, re-run without the `$_skipImportCheckFlag` -flag. -'''; -const String _skipImportCheckFlag = - '--${CommandLineOptions.skipImportCheckFlag}'; - -String unmigratedDependenciesError(List uris) => ''' -Error: package has unmigrated dependencies. - -Before migrating your package, we recommend ensuring that every library it -imports (either directly or indirectly) has been migrated to null safety, so -that you will be able to run your unit tests in sound null checking mode. You -are currently importing the following non-null-safe libraries: - - ${uris.join('\n ')} - -Please upgrade the packages containing these libraries to null safe versions -before continuing. To see what null safe package versions are available, run -the following command: `dart pub outdated --mode=null-safety`. - -To skip this check and try to migrate anyway, re-run with the flag -`$_skipImportCheckFlag`. -'''; diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart deleted file mode 100644 index 7d40f0ce3927..000000000000 --- a/pkg/nnbd_migration/lib/src/node_builder.dart +++ /dev/null @@ -1,1040 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:nnbd_migration/src/utilities/completeness_tracker.dart'; -import 'package:nnbd_migration/src/utilities/hint_utils.dart'; -import 'package:nnbd_migration/src/utilities/permissive_mode.dart'; -import 'package:nnbd_migration/src/utilities/resolution_utils.dart'; -import 'package:nnbd_migration/src/variables.dart'; - -import 'edge_origin.dart'; - -/// Visitor that builds nullability nodes based on visiting code to be migrated. -/// -/// The return type of each `visit...` method is a [DecoratedType] indicating -/// the static type of the element declared by the visited node, along with the -/// constraint variables that will determine its nullability. For `visit...` -/// methods that don't visit declarations, `null` will be returned. -class NodeBuilder extends GeneralizingAstVisitor - with - PermissiveModeVisitor, - CompletenessTracker { - /// Constraint variables and decorated types are stored here. - final Variables _variables; - - @override - final Source? source; - - /// If the parameters of a function or method are being visited, the - /// [DecoratedType]s of the function's named parameters that have been seen so - /// far. Otherwise `null`. - Map? _namedParameters; - - /// If the parameters of a function or method are being visited, the - /// [DecoratedType]s of the function's positional parameters that have been - /// seen so far. Otherwise `null`. - List? _positionalParameters; - - /// If the child types of a node are being visited, the - /// [NullabilityNodeTarget] that should be used in [visitTypeAnnotation]. - /// Otherwise `null`. - NullabilityNodeTarget? _target; - - /// [ClassDeclaration] for the current class or `null` if we are currently - /// not inside a class declaration. - ClassDeclaration? _classDeclaration; - - final NullabilityMigrationListener? listener; - - final NullabilityMigrationInstrumentation? instrumentation; - - final NullabilityGraph _graph; - - final TypeProvider _typeProvider; - - /// Indicates whether the declaration currently being visited is marked - /// `external`. - bool _visitingExternalDeclaration = false; - - NodeBuilder(this._variables, this.source, this.listener, this._graph, - this._typeProvider, - {this.instrumentation}); - - NullabilityNodeTarget get safeTarget { - var target = _target; - if (target != null) return target; - assert(false, 'Unknown nullability node target'); - return NullabilityNodeTarget.text('unknown'); - } - - bool get _isInjectable => - _classDeclaration?.metadata - .any((ann) => _isAngularConstructor(ann.element, 'Injectable')) ?? - false; - bool get _isInsideAngularComponent => - _classDeclaration?.metadata - .toList() - .any((ann) => _isAngularConstructor(ann.element, 'Component')) ?? - false; - - @override - DecoratedType? visitAsExpression(AsExpression node) { - node.expression.accept(this); - _pushNullabilityNodeTarget( - NullabilityNodeTarget.text('cast type'), () => node.type.accept(this)); - return null; - } - - @override - DecoratedType? visitCatchClause(CatchClause node) { - var exceptionElement = node.exceptionParameter?.declaredElement; - var target = exceptionElement == null - ? NullabilityNodeTarget.text('exception type') - : NullabilityNodeTarget.element(exceptionElement); - DecoratedType? exceptionType = _pushNullabilityNodeTarget( - target, () => node.exceptionType?.accept(this)); - if (node.exceptionParameter != null) { - // If there is no `on Type` part of the catch clause, the type is dynamic. - if (exceptionType == null) { - exceptionType = DecoratedType.forImplicitType(_typeProvider, - _typeProvider.dynamicType, _graph, target.withCodeRef(node)); - instrumentation?.implicitType( - source, node.exceptionParameter, exceptionType); - } - _variables.recordDecoratedElementType( - node.exceptionParameter?.declaredElement, exceptionType); - } - if (node.stackTraceParameter != null) { - // The type of stack traces is always StackTrace (non-nullable). - var target = NullabilityNodeTarget.text('stack trace').withCodeRef(node); - var nullabilityNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullableUnion(nullabilityNode, - StackTraceTypeOrigin(source, node.stackTraceParameter)); - var stackTraceType = - DecoratedType(_typeProvider.stackTraceType, nullabilityNode); - _variables.recordDecoratedElementType( - node.stackTraceParameter?.declaredElement, stackTraceType); - instrumentation?.implicitType( - source, node.stackTraceParameter, stackTraceType); - } - node.stackTraceParameter?.accept(this); - node.body.accept(this); - return null; - } - - @override - DecoratedType? visitClassDeclaration(ClassDeclaration node) { - node.metadata.accept(this); - node.typeParameters?.accept(this); - node.nativeClause?.accept(this); - _classDeclaration = node; - node.members.accept(this); - _classDeclaration = null; - var classElement = node.declaredElement!; - _handleSupertypeClauses(node, classElement, node.extendsClause?.superclass, - node.withClause, node.implementsClause, null); - var constructors = classElement.constructors; - if (constructors.length == 1) { - var constructorElement = constructors[0]; - if (constructorElement.isSynthetic) { - // Need to create a decorated type for the default constructor. - var decoratedReturnType = - _createDecoratedTypeForClass(classElement, node); - var functionType = DecoratedType(constructorElement.type, _graph.never, - returnType: decoratedReturnType, - positionalParameters: const [], - namedParameters: {}); - _variables.recordDecoratedElementType(constructorElement, functionType); - } - } - return null; - } - - @override - DecoratedType? visitClassTypeAlias(ClassTypeAlias node) { - node.metadata.accept(this); - node.typeParameters?.accept(this); - var classElement = node.declaredElement!; - _handleSupertypeClauses(node, classElement, node.superclass, - node.withClause, node.implementsClause, null); - for (var constructorElement in classElement.constructors) { - assert(constructorElement.isSynthetic); - var decoratedReturnType = - _createDecoratedTypeForClass(classElement, node); - var target = NullabilityNodeTarget.element(constructorElement); - var functionType = DecoratedType.forImplicitFunction( - _typeProvider, constructorElement.type, _graph.never, _graph, target, - returnType: decoratedReturnType); - _variables.recordDecoratedElementType(constructorElement, functionType); - for (var parameter in constructorElement.parameters) { - var parameterType = DecoratedType.forImplicitType( - _typeProvider, parameter.type, _graph, target); - _variables.recordDecoratedElementType(parameter, parameterType); - } - } - return null; - } - - @override - DecoratedType? visitCompilationUnit(CompilationUnit node) { - _graph.migrating(node.declaredElement!.library.source); - _graph.migrating(node.declaredElement!.source); - return super.visitCompilationUnit(node); - } - - @override - DecoratedType? visitConstructorDeclaration(ConstructorDeclaration node) { - _handleExecutableDeclaration( - node, - node.declaredElement!, - node.metadata, - null, - null, - node.parameters, - node.initializers, - node.body, - node.redirectedConstructor, - isExternal: node.externalKeyword != null); - return null; - } - - @override - DecoratedType? visitConstructorName(ConstructorName node) { - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('constructed type'), - () => node.type.accept(this)); - node.name?.accept(this); - return null; - } - - @override - DecoratedType? visitDeclaredIdentifier(DeclaredIdentifier node) { - node.metadata.accept(this); - var declaredElement = node.declaredElement!; - var target = NullabilityNodeTarget.element(declaredElement); - DecoratedType? type = - _pushNullabilityNodeTarget(target, () => node.type?.accept(this)); - if (type == null) { - type = DecoratedType.forImplicitType( - _typeProvider, declaredElement.type, _graph, target); - instrumentation?.implicitType(source, node, type); - } - _variables.recordDecoratedElementType(node.declaredElement, type); - return type; - } - - @override - DecoratedType? visitDefaultFormalParameter(DefaultFormalParameter node) { - var decoratedType = node.parameter.accept(this); - var hint = getPrefixHint(node.firstTokenAfterCommentAndMetadata!); - if (node.defaultValue != null) { - node.defaultValue!.accept(this); - return null; - } else if (node.declaredElement!.hasRequired) { - return null; - } else if (hint != null && hint.kind == HintCommentKind.required) { - _variables.recordRequiredHint(source, node, hint); - return null; - } - if (decoratedType == null) { - throw StateError('No type computed for ${node.parameter.runtimeType} ' - '(${node.parent!.parent!.toSource()}) offset=${node.offset}'); - } - decoratedType.node.trackPossiblyOptional(); - return null; - } - - @override - DecoratedType? visitEnumDeclaration(EnumDeclaration node) { - node.metadata.accept(this); - var classElement = node.declaredElement!; - _variables.recordDecoratedElementType( - classElement, DecoratedType(classElement.thisType, _graph.never)); - - makeNonNullNode(NullabilityNodeTarget target, [AstNode? forNode]) { - forNode ??= node; - final graphNode = NullabilityNode.forInferredType(target); - _graph.makeNonNullableUnion(graphNode, EnumValueOrigin(source, forNode)); - return graphNode; - } - - for (var item in node.constants) { - var declaredElement = item.declaredElement!; - var target = NullabilityNodeTarget.element(declaredElement); - _variables.recordDecoratedElementType(declaredElement, - DecoratedType(classElement.thisType, makeNonNullNode(target, item))); - } - final valuesGetter = classElement.getGetter('values')!; - var valuesTarget = NullabilityNodeTarget.element(valuesGetter); - _variables.recordDecoratedElementType( - valuesGetter, - DecoratedType(valuesGetter.type, makeNonNullNode(valuesTarget), - returnType: DecoratedType(valuesGetter.returnType, - makeNonNullNode(valuesTarget.returnType()), - typeArguments: [ - DecoratedType(classElement.thisType, - makeNonNullNode(valuesTarget.typeArgument(0))) - ]))); - return null; - } - - @override - DecoratedType? visitExtensionDeclaration(ExtensionDeclaration node) { - node.metadata.accept(this); - node.typeParameters?.accept(this); - var type = _pushNullabilityNodeTarget( - NullabilityNodeTarget.text('extended type'), - () => node.extendedType.accept(this)); - _variables.recordDecoratedElementType(node.declaredElement, type); - node.members.accept(this); - return null; - } - - @override - DecoratedType? visitFieldFormalParameter(FieldFormalParameter node) { - return _handleFormalParameter( - node, node.type, node.typeParameters, node.parameters); - } - - @override - DecoratedType? visitFormalParameterList(FormalParameterList node) { - int index = 0; - for (var parameter in node.parameters) { - var element = parameter.declaredElement!; - NullabilityNodeTarget newTarget; - if (element.isNamed) { - newTarget = safeTarget.namedParameter(element.name); - } else { - newTarget = safeTarget.positionalParameter(index++); - } - _pushNullabilityNodeTarget(newTarget, () => parameter.accept(this)); - } - return null; - } - - @override - DecoratedType? visitFunctionDeclaration(FunctionDeclaration node) { - _handleExecutableDeclaration( - node, - node.declaredElement!, - node.metadata, - node.returnType, - node.functionExpression.typeParameters, - node.functionExpression.parameters, - null, - node.functionExpression.body, - null, - isExternal: node.externalKeyword != null); - return null; - } - - @override - DecoratedType? visitFunctionExpression(FunctionExpression node) { - _handleExecutableDeclaration(node, node.declaredElement!, null, null, - node.typeParameters, node.parameters, null, node.body, null, - isExternal: false); - return null; - } - - @override - DecoratedType? visitFunctionExpressionInvocation( - FunctionExpressionInvocation node) { - node.function.accept(this); - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('type argument'), - () => node.typeArguments?.accept(this)); - node.argumentList.accept(this); - return null; - } - - @override - DecoratedType? visitFunctionTypeAlias(FunctionTypeAlias node) { - node.metadata.accept(this); - var declaredElement = node.declaredElement!; - var functionElement = - declaredElement.aliasedElement as GenericFunctionTypeElement; - var functionType = functionElement.type; - var returnType = node.returnType; - DecoratedType? decoratedReturnType; - var target = NullabilityNodeTarget.element(declaredElement); - if (returnType != null) { - _pushNullabilityNodeTarget(target.returnType(), () { - decoratedReturnType = returnType.accept(this); - }); - } else { - // Inferred return type. - decoratedReturnType = DecoratedType.forImplicitType( - _typeProvider, functionType.returnType, _graph, target.returnType()); - instrumentation?.implicitReturnType(source, node, decoratedReturnType); - } - var previousPositionalParameters = _positionalParameters; - var previousNamedParameters = _namedParameters; - _positionalParameters = []; - _namedParameters = {}; - DecoratedType decoratedFunctionType; - try { - node.typeParameters?.accept(this); - _pushNullabilityNodeTarget(target, () => node.parameters.accept(this)); - // Note: we don't pass _typeFormalBounds into DecoratedType because we're - // not defining a generic function type, we're defining a generic typedef - // of an ordinary (non-generic) function type. - decoratedFunctionType = DecoratedType(functionType, _graph.never, - returnType: decoratedReturnType, - positionalParameters: _positionalParameters, - namedParameters: _namedParameters); - } finally { - _positionalParameters = previousPositionalParameters; - _namedParameters = previousNamedParameters; - } - _variables.recordDecoratedElementType( - functionElement, decoratedFunctionType); - return null; - } - - @override - DecoratedType? visitFunctionTypedFormalParameter( - FunctionTypedFormalParameter node) { - return _handleFormalParameter( - node, node.returnType, node.typeParameters, node.parameters); - } - - @override - DecoratedType? visitGenericTypeAlias(GenericTypeAlias node) { - node.metadata.accept(this); - DecoratedType? decoratedFunctionType; - node.typeParameters?.accept(this); - var target = NullabilityNodeTarget.element(node.declaredElement!); - _pushNullabilityNodeTarget(target, () { - decoratedFunctionType = node.functionType!.accept(this); - }); - _variables.recordDecoratedElementType( - (node.declaredElement as TypeAliasElement).aliasedElement, - decoratedFunctionType); - return null; - } - - @override - DecoratedType? visitIsExpression(IsExpression node) { - node.expression.accept(this); - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('tested type'), - () => node.type.accept(this)); - return null; - } - - @override - DecoratedType? visitListLiteral(ListLiteral node) { - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('list element type'), - () => node.typeArguments?.accept(this)); - node.elements.accept(this); - return null; - } - - @override - DecoratedType? visitMethodDeclaration(MethodDeclaration node) { - var declaredElement = node.declaredElement; - var decoratedType = _handleExecutableDeclaration( - node, - declaredElement!, - node.metadata, - node.returnType, - node.typeParameters, - node.parameters, - null, - node.body, - null, - isExternal: node.externalKeyword != null); - if (declaredElement is PropertyAccessorElement) { - // Store a decorated type for the synthetic field so that in case we try - // to access it later we won't crash (this could happen due to errors in - // the source code). - if (declaredElement.isGetter) { - _variables.recordDecoratedElementType( - declaredElement.variable, decoratedType.returnType); - } else { - var type = decoratedType.positionalParameters![0]; - _variables.recordDecoratedElementType(declaredElement.variable, type, - soft: true); - if (_getAngularAnnotation(node.metadata) == _AngularAnnotation.child) { - _graph.makeNullable(type.node, AngularAnnotationOrigin(source, node)); - } - } - } - return null; - } - - @override - DecoratedType? visitMethodInvocation(MethodInvocation node) { - node.target?.accept(this); - node.methodName.accept(this); - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('type argument'), - () => node.typeArguments?.accept(this)); - node.argumentList.accept(this); - return null; - } - - @override - DecoratedType? visitMixinDeclaration(MixinDeclaration node) { - node.metadata.accept(this); - node.typeParameters?.accept(this); - node.members.accept(this); - _handleSupertypeClauses(node, node.declaredElement!, null, null, - node.implementsClause, node.onClause); - return null; - } - - @override - DecoratedType visitNamedType(NamedType node) { - namedTypeVisited(node); // Note this has been visited to NamedTypeTracker. - return visitTypeAnnotation(node); - } - - @override - DecoratedType? visitSetOrMapLiteral(SetOrMapLiteral node) { - var typeArguments = node.typeArguments; - if (typeArguments != null) { - var arguments = typeArguments.arguments; - if (arguments.length == 2) { - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('map key type'), - () => arguments[0].accept(this)); - _pushNullabilityNodeTarget(NullabilityNodeTarget.text('map value type'), - () => arguments[1].accept(this)); - } else { - _pushNullabilityNodeTarget( - NullabilityNodeTarget.text('set element type'), - () => typeArguments.accept(this)); - } - } - node.elements.accept(this); - return null; - } - - @override - DecoratedType? visitSimpleFormalParameter(SimpleFormalParameter node) { - return _handleFormalParameter(node, node.type, null, null); - } - - @override - DecoratedType visitTypeAnnotation(TypeAnnotation node) { - var type = node.type!; - var target = safeTarget.withCodeRef(node); - if (type is VoidType || type is DynamicType) { - var nullabilityNode = NullabilityNode.forTypeAnnotation(target); - var decoratedType = DecoratedType(type, nullabilityNode); - _variables.recordDecoratedTypeAnnotation(source, node, decoratedType); - if (_visitingExternalDeclaration) { - _graph.makeNullableUnion( - nullabilityNode, ExternalDynamicOrigin(source, node)); - } - return decoratedType; - } - var typeArguments = const []; - DecoratedType? decoratedReturnType; - List positionalParameters = const []; - Map namedParameters = - const {}; - if (type is InterfaceType && type.element.typeParameters.isNotEmpty) { - if (node is NamedType) { - if (node.typeArguments == null) { - int index = 0; - typeArguments = type.typeArguments - .map((t) => DecoratedType.forImplicitType( - _typeProvider, t, _graph, target.typeArgument(index++))) - .toList(); - instrumentation?.implicitTypeArguments(source, node, typeArguments); - } else { - int index = 0; - typeArguments = node.typeArguments!.arguments - .map((t) => _pushNullabilityNodeTarget( - target.typeArgument(index++), () => t.accept(this)!)) - .toList(); - } - } else { - assert(false); // TODO(paulberry): is this possible? - } - } - if (node is GenericFunctionType) { - var returnType = node.returnType; - if (returnType == null) { - decoratedReturnType = DecoratedType.forImplicitType(_typeProvider, - DynamicTypeImpl.instance, _graph, target.returnType()); - instrumentation?.implicitReturnType(source, node, decoratedReturnType); - } else { - // If [_target] is non-null, then it represents the return type for - // a FunctionTypeAlias. Otherwise, create a return type target for - // `target`. - _pushNullabilityNodeTarget(target.returnType(), () { - decoratedReturnType = returnType.accept(this); - }); - } - positionalParameters = []; - namedParameters = {}; - var previousPositionalParameters = _positionalParameters; - var previousNamedParameters = _namedParameters; - try { - _positionalParameters = positionalParameters; - _namedParameters = namedParameters; - node.typeParameters?.accept(this); - node.parameters.accept(this); - } finally { - _positionalParameters = previousPositionalParameters; - _namedParameters = previousNamedParameters; - } - } - NullabilityNode nullabilityNode; - if (typeIsNonNullableByContext(node)) { - nullabilityNode = _graph.never; - } else { - nullabilityNode = NullabilityNode.forTypeAnnotation(target); - nullabilityNode.hintActions - ..[HintActionKind.addNullableHint] = { - node.end: [AtomicEdit.insert('/*?*/')] - } - ..[HintActionKind.addNonNullableHint] = { - node.end: [AtomicEdit.insert('/*!*/')] - }; - } - DecoratedType decoratedType; - if (type is FunctionType && node is! GenericFunctionType) { - (node as NamedType).typeArguments?.accept(this); - // node is a reference to a typedef. Treat it like an inferred type (we - // synthesize new nodes for it). These nodes will be unioned with the - // typedef nodes by the edge builder. - decoratedType = DecoratedType.forImplicitFunction( - _typeProvider, type, nullabilityNode, _graph, target); - } else { - decoratedType = DecoratedType(type, nullabilityNode, - typeArguments: typeArguments, - returnType: decoratedReturnType, - positionalParameters: positionalParameters, - namedParameters: namedParameters); - } - _variables.recordDecoratedTypeAnnotation(source, node, decoratedType); - _handleNullabilityHint(node, decoratedType); - return decoratedType; - } - - @override - DecoratedType? visitTypeParameter(TypeParameter node) { - var element = node.declaredElement!; - var bound = node.bound; - DecoratedType? decoratedBound; - var target = NullabilityNodeTarget.typeParameterBound(element); - if (bound != null) { - decoratedBound = - _pushNullabilityNodeTarget(target, () => bound.accept(this)); - } else { - var nullabilityNode = NullabilityNode.forInferredType(target); - decoratedBound = DecoratedType(_typeProvider.objectType, nullabilityNode); - _graph.connect(_graph.always, nullabilityNode, - AlwaysNullableTypeOrigin.forElement(element, false)); - } - DecoratedTypeParameterBounds.current!.put(element, decoratedBound); - return null; - } - - @override - DecoratedType? visitVariableDeclarationList(VariableDeclarationList node) { - node.metadata.accept(this); - var typeAnnotation = node.type; - var declaredType = _pushNullabilityNodeTarget( - NullabilityNodeTarget.element(node.variables.first.declaredElement!), - () => typeAnnotation?.accept(this)); - var hint = getPrefixHint(node.firstTokenAfterCommentAndMetadata); - if (hint != null && hint.kind == HintCommentKind.late_) { - _variables.recordLateHint(source, node, hint); - } - if (hint != null && hint.kind == HintCommentKind.lateFinal) { - _variables.recordLateHint(source, node, hint); - } - var parent = node.parent; - for (var variable in node.variables) { - variable.metadata.accept(this); - var declaredElement = variable.declaredElement; - var type = declaredType; - if (type == null) { - var target = NullabilityNodeTarget.element(declaredElement!); - type = DecoratedType.forImplicitType( - _typeProvider, declaredElement.type, _graph, target); - instrumentation?.implicitType(source, node, type); - } - _variables.recordDecoratedElementType(declaredElement, type); - variable.initializer?.accept(this); - if (parent is FieldDeclaration) { - var angularAnnotation = _getAngularAnnotation(parent.metadata); - if (angularAnnotation != null) { - switch (angularAnnotation) { - case _AngularAnnotation.child: - _graph.makeNullable( - type.node, AngularAnnotationOrigin(source, node)); - break; - case _AngularAnnotation.children: - _graph.preventLate( - type.node, AngularAnnotationOrigin(source, node)); - break; - } - } - } - } - return null; - } - - DecoratedType _createDecoratedTypeForClass( - InterfaceElement classElement, AstNode? node) { - var typeArguments = classElement.typeParameters - .map((t) => t.instantiate(nullabilitySuffix: NullabilitySuffix.star)) - .toList(); - var decoratedTypeArguments = - typeArguments.map((t) => DecoratedType(t, _graph.never)).toList(); - return DecoratedType( - classElement.instantiate( - typeArguments: typeArguments, - nullabilitySuffix: NullabilitySuffix.star, - ), - _graph.never, - typeArguments: decoratedTypeArguments, - ); - } - - /// Determines if the given [metadata] contains a reference to one of the - /// Angular annotations that we have special behaviors for. If it does, - /// returns an enumerated value describing the type of annotation. - _AngularAnnotation? _getAngularAnnotation(NodeList metadata) { - for (var annotation in metadata) { - var element = annotation.element; - if (element is ConstructorElement) { - var name = element.enclosingElement.name; - if (_isAngularUri(element.librarySource.uri)) { - if (name == 'ViewChild' || name == 'ContentChild') { - return _AngularAnnotation.child; - } else if (name == 'ViewChildren' || name == 'ContentChildren') { - return _AngularAnnotation.children; - } - } - } - } - return null; - } - - /// Common handling of function and method declarations. - DecoratedType _handleExecutableDeclaration( - AstNode node, - ExecutableElement declaredElement, - NodeList? metadata, - TypeAnnotation? returnType, - TypeParameterList? typeParameters, - FormalParameterList? parameters, - NodeList? initializers, - FunctionBody body, - ConstructorName? redirectedConstructor, - {required bool isExternal}) { - metadata?.accept(this); - var previouslyVisitingExternalDeclaration = _visitingExternalDeclaration; - try { - if (isExternal) { - _visitingExternalDeclaration = true; - } - var functionType = declaredElement.type; - DecoratedType? decoratedReturnType; - var target = NullabilityNodeTarget.element(declaredElement); - if (returnType != null) { - _pushNullabilityNodeTarget(target.returnType(), () { - decoratedReturnType = returnType.accept(this); - }); - } else if (declaredElement is ConstructorElement) { - // Constructors have no explicit return type annotation, so use the - // implicit return type. - decoratedReturnType = _createDecoratedTypeForClass( - declaredElement.enclosingElement, parameters!.parent); - instrumentation?.implicitReturnType(source, node, decoratedReturnType); - } else { - // If the function expression just throws, analyzer will infer `Null` - // return type is legacy mode. This causes unnecessary casts at the - // fix generation phase. So change it back to `Never`. - if (body is ExpressionFunctionBody && - body.expression is ThrowExpression && - functionType.returnType == _typeProvider.nullType) { - functionType = FunctionTypeImpl( - typeFormals: functionType.typeFormals, - parameters: functionType.parameters, - returnType: _typeProvider.neverType, - nullabilitySuffix: functionType.nullabilitySuffix); - } - // Inferred return type. - decoratedReturnType = DecoratedType.forImplicitType( - _typeProvider, functionType.returnType, _graph, target); - instrumentation?.implicitReturnType(source, node, decoratedReturnType); - if (isExternal && functionType.returnType is DynamicType) { - _graph.makeNullableUnion( - decoratedReturnType.node, ExternalDynamicOrigin(source, node)); - } - } - var previousPositionalParameters = _positionalParameters; - var previousNamedParameters = _namedParameters; - _positionalParameters = []; - _namedParameters = {}; - DecoratedType decoratedFunctionType; - try { - typeParameters?.accept(this); - _pushNullabilityNodeTarget(target, () => parameters?.accept(this)); - redirectedConstructor?.accept(this); - initializers?.accept(this); - decoratedFunctionType = DecoratedType(functionType, _graph.never, - returnType: decoratedReturnType, - positionalParameters: _positionalParameters, - namedParameters: _namedParameters); - body.accept(this); - } finally { - _positionalParameters = previousPositionalParameters; - _namedParameters = previousNamedParameters; - } - _variables.recordDecoratedElementType( - declaredElement, decoratedFunctionType); - return decoratedFunctionType; - } finally { - _visitingExternalDeclaration = previouslyVisitingExternalDeclaration; - } - } - - DecoratedType? _handleFormalParameter( - FormalParameter node, - TypeAnnotation? type, - TypeParameterList? typeParameters, - FormalParameterList? parameters) { - var declaredElement = node.declaredElement!; - node.metadata.accept(this); - DecoratedType? decoratedType; - var target = safeTarget; - if (parameters == null) { - if (type != null) { - decoratedType = type.accept(this); - } else { - decoratedType = DecoratedType.forImplicitType( - _typeProvider, declaredElement.type, _graph, target); - if (_visitingExternalDeclaration) { - _graph.makeNullableUnion( - decoratedType.node, ExternalDynamicOrigin(source, node)); - } - instrumentation?.implicitType(source, node, decoratedType); - } - } else { - DecoratedType? decoratedReturnType; - if (type == null) { - decoratedReturnType = DecoratedType.forImplicitType(_typeProvider, - DynamicTypeImpl.instance, _graph, target.returnType()); - instrumentation?.implicitReturnType(source, node, decoratedReturnType); - if (_visitingExternalDeclaration) { - _graph.makeNullableUnion( - decoratedReturnType.node, ExternalDynamicOrigin(source, node)); - } - } else { - decoratedReturnType = type.accept(this); - } - if (typeParameters != null) { - // TODO(paulberry) - _unimplemented( - typeParameters, 'Function-typed parameter with type parameters'); - } - var positionalParameters = []; - var namedParameters = {}; - var previousPositionalParameters = _positionalParameters; - var previousNamedParameters = _namedParameters; - try { - _positionalParameters = positionalParameters; - _namedParameters = namedParameters; - parameters.accept(this); - } finally { - _positionalParameters = previousPositionalParameters; - _namedParameters = previousNamedParameters; - } - final nullabilityNode = NullabilityNode.forTypeAnnotation(target); - decoratedType = DecoratedType(declaredElement.type, nullabilityNode, - returnType: decoratedReturnType, - positionalParameters: positionalParameters, - namedParameters: namedParameters); - _handleNullabilityHint(node, decoratedType); - } - _variables.recordDecoratedElementType(declaredElement, decoratedType); - var isAnnotated = false; - for (var annotation in node.metadata) { - var element = annotation.element; - if (_isAngularConstructor(element, 'Optional') || - _isAngularConstructor(element, 'Attribute')) { - isAnnotated = true; - if (_isAngularConstructor(element, 'Optional') || - _isInsideAngularComponent) { - _graph.makeNullable( - decoratedType!.node, AngularAnnotationOrigin(source, node)); - } - } - } - final enclosingElement = declaredElement.enclosingElement; - if ((enclosingElement is ConstructorElement && - (_isInsideAngularComponent || _isInjectable) || - (enclosingElement is FunctionElement) && - _hasInjectable(enclosingElement)) && - !isAnnotated) { - _graph.makeNonNullable( - decoratedType!.node, AngularConstructorArgumentOrigin(source, node)); - } - - if (declaredElement.isNamed) { - _namedParameters![declaredElement.name] = decoratedType!; - } else { - _positionalParameters!.add(decoratedType!); - } - return decoratedType; - } - - /// Nullability hints can be added to [TypeAnnotation]s, - /// [FunctionTypedFormalParameter]s, and function-typed - /// [FieldFormalParameter]s. - void _handleNullabilityHint(AstNode node, DecoratedType decoratedType) { - assert(node is TypeAnnotation || - node is FunctionTypedFormalParameter || - (node is FieldFormalParameter && node.parameters != null)); - var hint = getPostfixHint(node.endToken); - if (hint != null) { - switch (hint.kind) { - case HintCommentKind.bang: - _graph.makeNonNullableUnion(decoratedType.node, - NullabilityCommentOrigin(source, node, false)); - _variables.recordNullabilityHint(source, node, hint); - decoratedType.node.hintActions[HintActionKind.removeNonNullableHint] = - hint.changesToRemove(source!.contents.data); - decoratedType.node.hintActions[HintActionKind.changeToNullableHint] = - hint.changesToReplace(source!.contents.data, '/*?*/'); - break; - case HintCommentKind.question: - _graph.makeNullableUnion( - decoratedType.node, NullabilityCommentOrigin(source, node, true)); - _variables.recordNullabilityHint(source, node, hint); - decoratedType.node.hintActions[HintActionKind.removeNullableHint] = - hint.changesToRemove(source!.contents.data); - decoratedType - .node.hintActions[HintActionKind.changeToNonNullableHint] = - hint.changesToReplace(source!.contents.data, '/*!*/'); - break; - default: - break; - } - - decoratedType.node.hintActions - ..remove(HintActionKind.addNonNullableHint) - ..remove(HintActionKind.addNullableHint); - } - } - - void _handleSupertypeClauses( - NamedCompilationUnitMember astNode, - InterfaceElement declaredElement, - NamedType? superclass, - WithClause? withClause, - ImplementsClause? implementsClause, - OnClause? onClause) { - var supertypes = []; - supertypes.add(superclass); - if (withClause != null) { - supertypes.addAll(withClause.mixinTypes); - } - if (implementsClause != null) { - supertypes.addAll(implementsClause.interfaces); - } - if (onClause != null) { - supertypes.addAll(onClause.superclassConstraints); - } - var decoratedSupertypes = {}; - _pushNullabilityNodeTarget( - NullabilityNodeTarget.element(declaredElement).supertype, () { - for (var supertype in supertypes) { - DecoratedType? decoratedSupertype; - if (supertype == null) { - var nullabilityNode = - NullabilityNode.forInferredType(_target!.withCodeRef(astNode)); - _graph.makeNonNullableUnion( - nullabilityNode, NonNullableObjectSuperclass(source, astNode)); - decoratedSupertype = - DecoratedType(_typeProvider.objectType, nullabilityNode); - } else { - decoratedSupertype = supertype.accept(this); - } - var class_ = (decoratedSupertype!.type as InterfaceType).element; - decoratedSupertypes[class_] = decoratedSupertype; - } - }); - _variables.recordDecoratedDirectSupertypes( - declaredElement, decoratedSupertypes); - } - - bool _hasInjectable(Element element) => element.metadata - .any((ann) => _isAngularConstructor(ann.element, 'Injectable')); - - /// Determines whether [element] is a constructor named [name] from Angular - /// package. - bool _isAngularConstructor(Element? element, String name) => - element is ConstructorElement && - element.enclosingElement.name == name && - _isAngularUri(element.librarySource.uri); - - /// Determines whether the given [uri] comes from the Angular package. - bool _isAngularUri(Uri uri) { - if (!uri.isScheme('package')) return false; - var packageName = uri.pathSegments[0]; - if (packageName == 'angular') return true; - if (packageName == 'third_party.dart_src.angular.angular') { - // This name is used for angular development internally at Google. - return true; - } - return false; - } - - T _pushNullabilityNodeTarget( - NullabilityNodeTarget target, T Function() fn) { - NullabilityNodeTarget? previousTarget = _target; - try { - _target = target; - return fn(); - } finally { - _target = previousTarget; - } - } - - Never _unimplemented(AstNode node, String message) { - CompilationUnit unit = node.root as CompilationUnit; - StringBuffer buffer = StringBuffer(); - buffer.write(message); - buffer.write(' in "'); - buffer.write(node.toSource()); - buffer.write('" on line '); - buffer.write(unit.lineInfo.getLocation(node.offset).lineNumber); - buffer.write(' of "'); - buffer.write(unit.declaredElement!.source.fullName); - buffer.write('"'); - throw UnimplementedError(buffer.toString()); - } -} - -/// Enum describing the kinds of annotations supplied by the angular package for -/// which we have special migration behaviors. -enum _AngularAnnotation { - /// Either the `@ViewChild` or `@ContentChild` annotation. Fields with these - /// annotations should always be nullable and should never be late. - child, - - /// Either the `@ViewChildren` or `@ContentChildren` annotation. Fields with - /// these annotations should never be late. - children, -} diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart deleted file mode 100644 index cceb307ad56b..000000000000 --- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/analysis/session.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type_system.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edge_builder.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/exceptions.dart'; -import 'package:nnbd_migration/src/fix_aggregator.dart'; -import 'package:nnbd_migration/src/fix_builder.dart'; -import 'package:nnbd_migration/src/node_builder.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/variables.dart'; -import 'package:pub_semver/pub_semver.dart'; - -/// Implementation of the [NullabilityMigration] public API. -class NullabilityMigrationImpl implements NullabilityMigration { - final NullabilityMigrationListener? listener; - - Variables? _variables; - - final NullabilityGraph _graph; - - final bool? _permissive; - - final NullabilityMigrationInstrumentation? _instrumentation; - - DecoratedClassHierarchy? _decoratedClassHierarchy; - - bool _propagated = false; - - /// Indicates whether code removed by the migration engine should be removed - /// by commenting it out. A value of `false` means to actually delete the - /// code that is removed. - final bool? removeViaComments; - - final bool warnOnWeakCode; - - final _decoratedTypeParameterBounds = DecoratedTypeParameterBounds(); - - /// Map from [Source] object to a boolean indicating whether the source is - /// opted in to null safety. - final Map _libraryOptInStatus = {}; - - /// Indicates whether the client has used the [unmigratedDependencies] getter. - bool _queriedUnmigratedDependencies = false; - - /// Map of additional package dependencies that will be required by the - /// migrated code. Keys are package names; values indicate the minimum - /// required version of each package. - final Map _neededPackages = {}; - - /// Prepares to perform nullability migration. - /// - /// If [permissive] is `true`, exception handling logic will try to proceed - /// as far as possible even though the migration algorithm is not yet - /// complete. TODO(paulberry): remove this mode once the migration algorithm - /// is fully implemented. - /// - /// Optional parameter [removeViaComments] indicates whether code that the - /// migration tool wishes to remove should instead be commenting it out. - /// - /// Optional parameter [warnOnWeakCode] indicates whether weak-only code - /// should be warned about or removed (in the way specified by - /// [removeViaComments]). - NullabilityMigrationImpl(NullabilityMigrationListener? listener, - {bool? permissive = false, - NullabilityMigrationInstrumentation? instrumentation, - bool? removeViaComments = false, - bool warnOnWeakCode = true}) - : this._( - listener, - NullabilityGraph(instrumentation: instrumentation), - permissive, - instrumentation, - removeViaComments, - warnOnWeakCode, - ); - - NullabilityMigrationImpl._( - this.listener, - this._graph, - this._permissive, - this._instrumentation, - this.removeViaComments, - this.warnOnWeakCode, - ) { - _instrumentation?.immutableNodes(_graph.never, _graph.always); - } - - @override - bool? get isPermissive => _permissive; - - @override - List get unmigratedDependencies { - _queriedUnmigratedDependencies = true; - var unmigratedDependencies = []; - for (var entry in _libraryOptInStatus.entries) { - if (_graph.isPathBeingMigrated(entry.key.fullName)) continue; - if (!entry.value) { - unmigratedDependencies.add(entry.key); - } - } - var badUris = { - for (var dependency in unmigratedDependencies) dependency.uri.toString() - }.toList(); - badUris.sort(); - return badUris; - } - - @override - void finalizeInput(ResolvedUnitResult result) { - if (result.unit.featureSet.isEnabled(Feature.non_nullable)) { - // This library has already been migrated; nothing more to do. - return; - } - ExperimentStatusException.sanityCheck(result); - if (!_propagated) { - _propagated = true; - _graph.propagate(); - } - var unit = result.unit; - var compilationUnit = unit.declaredElement!; - var library = compilationUnit.library; - var source = compilationUnit.source; - // Hierarchies were created assuming the libraries being migrated are opted - // out, but the FixBuilder will analyze assuming they're opted in. So we - // need to clear the hierarchies before we continue. - (result.session as AnalysisSessionImpl).clearHierarchies(); - var fixBuilder = FixBuilder( - source, - _decoratedClassHierarchy, - result.typeProvider, - library.typeSystem as TypeSystemImpl, - _variables, - library as LibraryElementImpl, - _permissive! ? listener : null, - unit, - warnOnWeakCode, - _graph, - _neededPackages); - try { - DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds; - fixBuilder.visitAll(); - } finally { - DecoratedTypeParameterBounds.current = null; - } - var changes = FixAggregator.run(unit, result.content, fixBuilder.changes, - removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode)!; - _instrumentation?.changes(source, changes); - final lineInfo = LineInfo.fromContent(source.contents.data); - var offsets = changes.keys.toList(); - offsets.sort(); - for (var offset in offsets) { - var edits = changes[offset]!; - var descriptions = edits - .map((edit) => edit.info) - .where((info) => info != null) - .map((info) => info!.description.appliedMessage) - .join(', '); - var sourceEdit = edits.toSourceEdit(offset!); - listener!.addSuggestion( - descriptions, _computeLocation(lineInfo, sourceEdit, source)); - listener!.addEdit(source, sourceEdit); - } - } - - Map finish() { - _instrumentation?.finished(); - return _neededPackages; - } - - void prepareInput(ResolvedUnitResult result) { - assert( - !_queriedUnmigratedDependencies, - 'Should only query unmigratedDependencies after all calls to ' - 'prepareInput'); - if (result.unit.featureSet.isEnabled(Feature.non_nullable)) { - // This library has already been migrated; nothing more to do. - return; - } - ExperimentStatusException.sanityCheck(result); - _recordTransitiveImportExportOptInStatus( - result.libraryElement.importedLibraries); - _recordTransitiveImportExportOptInStatus( - result.libraryElement.exportedLibraries); - var variables = _variables; - if (variables == null) { - variables = _variables = Variables(_graph, result.typeProvider, - instrumentation: _instrumentation); - _decoratedClassHierarchy = DecoratedClassHierarchy(variables, _graph); - } - var unit = result.unit; - try { - DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds; - unit.accept(NodeBuilder(variables, unit.declaredElement!.source, - _permissive! ? listener : null, _graph, result.typeProvider, - instrumentation: _instrumentation)); - } finally { - DecoratedTypeParameterBounds.current = null; - } - } - - void processInput(ResolvedUnitResult result) { - if (result.unit.featureSet.isEnabled(Feature.non_nullable)) { - // This library has already been migrated; nothing more to do. - return; - } - ExperimentStatusException.sanityCheck(result); - var unit = result.unit; - try { - DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds; - unit.accept(EdgeBuilder( - result.typeProvider, - result.typeSystem, - _variables!, - _graph, - unit.declaredElement!.source, - _permissive! ? listener : null, - _decoratedClassHierarchy, - result.libraryElement, - instrumentation: _instrumentation)); - } finally { - DecoratedTypeParameterBounds.current = null; - } - } - - @override - void update() { - _graph.update(); - } - - /// Records the opt in/out status of all libraries in [libraries], and any - /// libraries they transitively import or export, in [_libraryOptInStatus]. - void _recordTransitiveImportExportOptInStatus( - Iterable libraries) { - var librariesToCheck = libraries.toList(); - while (librariesToCheck.isNotEmpty) { - var library = librariesToCheck.removeLast(); - if (_libraryOptInStatus.containsKey(library.source)) continue; - _libraryOptInStatus[library.source] = library.isNonNullableByDefault; - librariesToCheck.addAll(library.importedLibraries); - librariesToCheck.addAll(library.exportedLibraries); - } - } - - static Location _computeLocation( - LineInfo lineInfo, SourceEdit edit, Source source) { - final startLocation = lineInfo.getLocation(edit.offset); - final endLocation = lineInfo.getLocation(edit.end); - var location = Location( - source.fullName, - edit.offset, - edit.length, - startLocation.lineNumber, - startLocation.columnNumber, - endLine: endLocation.lineNumber, - endColumn: endLocation.columnNumber, - ); - return location; - } -} diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart deleted file mode 100644 index 04890c7b2e44..000000000000 --- a/pkg/nnbd_migration/lib/src/nullability_node.dart +++ /dev/null @@ -1,1181 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:collection'; - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nullability_state.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; - -import 'edge_origin.dart'; - -/// Base class for steps that occur as part of downstream propagation, where the -/// nullability of a node is changed to a new state. -abstract class DownstreamPropagationStep extends PropagationStep - implements DownstreamPropagationStepInfo { - @override - NullabilityNodeMutable? targetNode; - - /// The state that the node's nullability was changed to. - /// - /// Any propagation step that took effect should have a non-null value here. - /// Propagation steps that are pending but have not taken effect yet, or that - /// never had an effect (e.g. because an edge was not triggered) will have a - /// `null` value for this field. - Nullability? newState; - - DownstreamPropagationStep(); - - @override - DownstreamPropagationStep? get principalCause; -} - -/// Base class for steps that occur as part of propagating exact nullability -/// upstream through the nullability graph. -abstract class ExactNullablePropagationStep extends DownstreamPropagationStep { - ExactNullablePropagationStep(); -} - -/// Conditions of the "lateness" of a [NullabilityNode]. -enum LateCondition { - /// The associated [NullabilityNode] does not represent the type of a late - /// variable. - notLate, - - /// The associated [NullabilityNode] represents the type of a late variable, - /// due to a `/*late*/` hint. - lateDueToHint, - - /// The associated [NullabilityNode] represents an variable which is possibly - /// late, due to the late-inferring algorithm. - possiblyLate, - - /// The associated [NullabilityNode] represents an variable which is possibly - /// late, due to being assigned in a function passed to a call to the test - /// package's `setUp` function. - possiblyLateDueToTestSetup, -} - -/// Data structure to keep track of the relationship from one [NullabilityNode] -/// object to another [NullabilityNode] that is "downstream" from it (meaning -/// that if the former node is nullable, then the latter node will either have -/// to be nullable, or null checks will have to be added). -class NullabilityEdge implements EdgeInfo { - @override - final NullabilityNode destinationNode; - - /// A set of upstream nodes. By convention, the first node is the source node - /// and the other nodes are "guards". The destination node will only need to - /// be made nullable if all the upstream nodes are nullable. - final List upstreamNodes; - - final _NullabilityEdgeKind _kind; - - /// The location in the source code that caused this edge to be built. - final CodeReference? codeReference; - - final String description; - - /// Whether this edge is the result of an uninitialized variable declaration. - final bool isUninit; - - /// Whether this edge is the result of an assignment within the test package's - /// `setUp` function. - final bool isSetupAssignment; - - NullabilityEdge._( - this.destinationNode, this.upstreamNodes, this._kind, this.description, - {this.codeReference, - this.isUninit = false, - this.isSetupAssignment = false}); - - @override - Iterable get guards => upstreamNodes.skip(1); - - /// Indicates whether it's possible for migration to cope with this edge being - /// unsatisfied by inserting a null check. Graph propagation favors - /// satisfying uncheckable edges over satisfying hard edges. - bool get isCheckable => - _kind == _NullabilityEdgeKind.soft || _kind == _NullabilityEdgeKind.hard; - - @override - bool get isHard => - _kind == _NullabilityEdgeKind.hard || - _kind == _NullabilityEdgeKind.union || - _kind == _NullabilityEdgeKind.uncheckableHard; - - @override - bool get isSatisfied { - if (!isTriggered) return true; - return destinationNode.isNullable; - } - - @override - bool get isTriggered { - for (var upstreamNode in upstreamNodes) { - if (!upstreamNode!.isNullable) return false; - } - return true; - } - - @override - bool get isUnion => _kind == _NullabilityEdgeKind.union; - - @override - bool get isUpstreamTriggered { - if (!isHard) return false; - return destinationNode.nonNullIntent.isPresent; - } - - @override - NullabilityNode? get sourceNode => upstreamNodes.first; - - @override - String toString({NodeToIdMapper? idMapper}) { - var edgeDecorations = []; - switch (_kind) { - case _NullabilityEdgeKind.soft: - break; - case _NullabilityEdgeKind.uncheckable: - edgeDecorations.add('uncheckable'); - break; - case _NullabilityEdgeKind.uncheckableHard: - edgeDecorations.add('uncheckableHard'); - break; - case _NullabilityEdgeKind.hard: - edgeDecorations.add('hard'); - break; - case _NullabilityEdgeKind.union: - edgeDecorations.add('union'); - break; - case _NullabilityEdgeKind.dummy: - edgeDecorations.add('dummy'); - break; - } - edgeDecorations.addAll(guards); - var edgeDecoration = - edgeDecorations.isEmpty ? '' : '-(${edgeDecorations.join(', ')})'; - return '${sourceNode!.toString(idMapper: idMapper)} $edgeDecoration-> ' - '${destinationNode.toString(idMapper: idMapper)}'; - } -} - -/// Data structure to keep track of the relationship between [NullabilityNode] -/// objects. -class NullabilityGraph { - /// Set this const to `true` to dump the nullability graph just before - /// propagation. - static const _debugBeforePropagation = false; - - /// Set this const to `true` to dump the nullability graph just before - /// propagation. - static const _debugAfterPropagation = false; - - final NullabilityMigrationInstrumentation? instrumentation; - - /// Returns a [NullabilityNode] that is a priori nullable. - /// - /// Propagation of nullability always proceeds downstream starting at this - /// node. - final NullabilityNode always; - - /// Returns a [NullabilityNode] that is a priori non-nullable. - /// - /// Propagation of nullability always proceeds upstream starting at this - /// node. - final NullabilityNode never; - - /// Set containing all sources being migrated. - final _sourcesBeingMigrated = {}; - - /// Set containing paths to all sources being migrated. - final _pathsBeingMigrated = {}; - - /// A set containing all of the nodes in the graph. - final Set nodes = {}; - - NullabilityGraph({this.instrumentation}) - : always = _NullabilityNodeImmutable('always', true), - never = _NullabilityNodeImmutable('never', false); - - /// Records that [sourceNode] is immediately upstream from [destinationNode]. - /// - /// Returns the edge created by the connection. - NullabilityEdge connect(NullabilityNode? sourceNode, - NullabilityNode destinationNode, EdgeOrigin origin, - {bool hard = false, - bool checkable = true, - List guards = const []}) { - var upstreamNodes = [sourceNode, ...guards]; - var kind = hard - ? checkable - ? _NullabilityEdgeKind.hard - : _NullabilityEdgeKind.uncheckableHard - : checkable - ? _NullabilityEdgeKind.soft - : _NullabilityEdgeKind.uncheckable; - return _connect(upstreamNodes, destinationNode, kind, origin); - } - - /// Records that [sourceNode] is immediately upstream from [always], via a - /// dummy edge. - NullabilityEdge connectDummy( - NullabilityNode? sourceNode, EdgeOrigin origin) => - _connect([sourceNode], always, _NullabilityEdgeKind.dummy, origin); - - /// Prints out a representation of the graph nodes. Useful in debugging - /// broken tests. - void debugDump() { - Set visitedNodes = {}; - Map shortNames = {}; - int counter = 0; - String nameNode(NullabilityNode node) { - if (node.isImmutable) { - var name = 'n${counter++}'; - print(' $name [label="$node" shape=none]'); - return name; - } - var name = shortNames[node]; - if (name == null) { - shortNames[node] = name = 'n${counter++}'; - String styleSuffix = node.isNullable ? ' style=filled' : ''; - String intentSuffix = - node.nonNullIntent.isPresent ? ', non-null intent' : ''; - String label = '$node (${node._nullability}$intentSuffix)'; - print(' $name [label="$label"$styleSuffix]'); - if (node is NullabilityNodeCompound) { - for (var component in node._components) { - print(' ${nameNode(component!)} -> $name [style=dashed]'); - } - } - } - return name; - } - - void visitNode(NullabilityNode? node) { - if (!visitedNodes.add(node)) return; - for (var edge in node!._upstreamEdges) { - String suffix; - if (edge.isUnion) { - suffix = ' [label="union"]'; - } else if (edge.isHard) { - suffix = ' [label="hard"]'; - } else if (edge.isCheckable) { - suffix = ''; - } else { - suffix = ' [label="uncheckable"]'; - } - var upstreamNodes = edge.upstreamNodes; - if (upstreamNodes.length == 1) { - print( - ' ${nameNode(upstreamNodes.single!)} -> ${nameNode(node)}$suffix'); - } else { - var tmpName = 'n${counter++}'; - print(' $tmpName [label=""]'); - print(' $tmpName -> ${nameNode(node)}$suffix}'); - for (var upstreamNode in upstreamNodes) { - print(' ${nameNode(upstreamNode!)} -> $tmpName'); - } - } - } - } - - print('digraph G {'); - print(' rankdir="LR"'); - visitNode(always); - visitNode(never); - for (var node in nodes) { - visitNode(node); - } - print('}'); - } - - /// Determine if [source] is in the code being migrated. - bool isBeingMigrated(Source source) { - return _sourcesBeingMigrated.contains(source); - } - - bool isPathBeingMigrated(String path) { - return _pathsBeingMigrated.contains(path); - } - - /// Creates a graph edge that will try to force the given [node] to be - /// non-nullable. - NullabilityEdge makeNonNullable(NullabilityNode? node, EdgeOrigin origin, - {bool hard = true, List guards = const []}) { - return connect(node, never, origin, hard: hard, guards: guards); - } - - /// Creates union edges that will guarantee that the given [node] is - /// non-nullable. - void makeNonNullableUnion(NullabilityNode node, EdgeOrigin origin) { - union(node, never, origin); - } - - /// Creates a graph edge that will try to force the given [node] to be - /// nullable. - void makeNullable(NullabilityNode node, EdgeOrigin origin, - {List guards = const []}) { - connect(always, node, origin, guards: guards); - } - - /// Creates a `union` graph edge that will try to force the given [node] to be - /// nullable. This is a stronger signal than [makeNullable] (it overrides - /// [makeNonNullable]). - void makeNullableUnion(NullabilityNode node, EdgeOrigin origin) { - union(always, node, origin); - } - - /// Record source as code that is being migrated. - void migrating(Source source) { - _sourcesBeingMigrated.add(source); - _pathsBeingMigrated.add(source.fullName); - } - - /// Creates a graph edge that will try to prevent the given [node] from being - /// marked `late`. - void preventLate(NullabilityNode node, EdgeOrigin origin) { - connect(node, always, origin); - } - - /// Determines the nullability of each node in the graph by propagating - /// nullability information from one node to another. - PropagationResult propagate() { - if (_debugBeforePropagation) debugDump(); - var propagationState = _PropagationState(always, never).result; - if (_debugAfterPropagation) debugDump(); - return propagationState; - } - - /// Records that nodes [x] and [y] should have exactly the same nullability. - void union(NullabilityNode x, NullabilityNode y, EdgeOrigin origin) { - _connect([x], y, _NullabilityEdgeKind.union, origin); - _connect([y], x, _NullabilityEdgeKind.union, origin); - } - - /// Update the graph after an edge has been added or removed. - void update() { - // - // Reset the state of the nodes. - // - // This is inefficient because we reset the state of some nodes more than - // once, but not all nodes are reachable from both `never` and `always`, so - // we need to traverse the graph from both directions. - // - for (var node in nodes) { - node!.resetState(); - } - // - // Reset the state of the listener. - // - instrumentation!.prepareForUpdate(); - // - // Re-run the propagation step. - // - propagate(); - } - - NullabilityEdge _connect( - List upstreamNodes, - NullabilityNode destinationNode, - _NullabilityEdgeKind kind, - EdgeOrigin origin) { - var isUninit = origin.kind == EdgeOriginKind.fieldNotInitialized || - origin.kind == EdgeOriginKind.implicitNullInitializer || - origin.kind == EdgeOriginKind.uninitializedRead; - var edge = NullabilityEdge._( - destinationNode, upstreamNodes, kind, origin.description, - codeReference: origin.codeReference, - isUninit: isUninit, - isSetupAssignment: origin.isSetupAssignment); - instrumentation?.graphEdge(edge, origin); - for (var upstreamNode in upstreamNodes) { - _connectDownstream(upstreamNode!, edge); - } - destinationNode._upstreamEdges.add(edge); - nodes.addAll(upstreamNodes); - nodes.add(destinationNode); - return edge; - } - - void _connectDownstream(NullabilityNode upstreamNode, NullabilityEdge edge) { - upstreamNode._downstreamEdges.add(edge); - if (upstreamNode is NullabilityNodeCompound) { - for (var component in upstreamNode._components) { - _connectDownstream(component!, edge); - } - } - } -} - -/// Same as [NullabilityGraph], but extended with extra methods for easier -/// testing. -@visibleForTesting -class NullabilityGraphForTesting extends NullabilityGraph { - final List _allEdges = []; - - final Map _edgeOrigins = {}; - - /// Iterates through all edges in the graph. - @visibleForTesting - Iterable getAllEdges() { - return _allEdges; - } - - /// Retrieves the [EdgeOrigin] object that was used to create [edge]. - @visibleForTesting - EdgeOrigin? getEdgeOrigin(NullabilityEdge edge) => _edgeOrigins[edge]; - - @override - NullabilityEdge _connect( - List upstreamNodes, - NullabilityNode destinationNode, - _NullabilityEdgeKind kind, - EdgeOrigin origin) { - var edge = super._connect(upstreamNodes, destinationNode, kind, origin); - _allEdges.add(edge); - _edgeOrigins[edge] = origin; - return edge; - } -} - -/// Representation of a single node in the nullability inference graph. -/// -/// Initially, this is just a wrapper over constraint variables, and the -/// nullability inference graph is encoded into the wrapped constraint -/// variables. Over time this will be replaced by a first class representation -/// of the nullability inference graph. -abstract class NullabilityNode implements NullabilityNodeInfo { - LateCondition _lateCondition = LateCondition.notLate; - - @override - final hintActions = >>{}; - - bool _isPossiblyOptional = false; - - /// List of [NullabilityEdge] objects describing this node's relationship to - /// other nodes that are "downstream" from it (meaning that if a key node is - /// nullable, then all the nodes in the corresponding value will either have - /// to be nullable, or null checks will have to be added). - final _downstreamEdges = []; - - /// List of edges that have this node as their destination. - final _upstreamEdges = []; - - /// List of compound nodes wrapping this node. - final List outerCompoundNodes = - []; - - /// Creates a [NullabilityNode] representing the nullability of a variable - /// whose type comes from an already-migrated library. - factory NullabilityNode.forAlreadyMigrated(NullabilityNodeTarget target) => - _NullabilityNodeSimple(target); - - /// Creates a [NullabilityNode] representing the nullability of an expression - /// which is nullable iff two other nullability nodes are both nullable. - /// - /// The caller is required to create the appropriate graph edges to ensure - /// that the appropriate relationship between the nodes' nullabilities holds. - factory NullabilityNode.forGLB() => _NullabilityNodeSimple( - NullabilityNodeTarget.text('(greatest lower bound)')); - - /// Creates a [NullabilityNode] representing the nullability of a variable - /// whose type is determined by the `??` operator. - factory NullabilityNode.forIfNotNull(AstNode node) => _NullabilityNodeSimple( - NullabilityNodeTarget.text('?? operator').withCodeRef(node)); - - /// Creates a [NullabilityNode] representing the nullability of a variable - /// whose type is determined by type inference. - factory NullabilityNode.forInferredType(NullabilityNodeTarget target) => - _NullabilityNodeSimple(target); - - /// Creates a [NullabilityNode] representing the nullability of an - /// expression which is nullable iff either [a] or [b] is nullable. - factory NullabilityNode.forLUB( - NullabilityNode? left, NullabilityNode? right) = NullabilityNodeForLUB._; - - /// Creates a [NullabilityNode] representing the nullability of a type - /// substitution where [outerNode] is the nullability node for the type - /// variable being eliminated by the substitution, and [innerNode] is the - /// nullability node for the type being substituted in its place. - /// - /// If either [innerNode] or [outerNode] is `null`, then the other node is - /// returned. - factory NullabilityNode.forSubstitution( - NullabilityNode? innerNode, NullabilityNode? outerNode) { - if (innerNode == null) return outerNode!; - if (outerNode == null) return innerNode; - return NullabilityNodeForSubstitution._(innerNode, outerNode); - } - - /// Creates a [NullabilityNode] representing the nullability of a type - /// annotation appearing explicitly in the user's program. - factory NullabilityNode.forTypeAnnotation(NullabilityNodeTarget target) => - _NullabilityNodeSimple(target); - - NullabilityNode._(); - - @override - CodeReference? get codeReference => null; - - /// Gets a string that can be appended to a type name during debugging to help - /// annotate the nullability of that type. - String get debugSuffix => '?($this)'; - - /// Gets a name for the nullability node that is suitable for display to the - /// user. - String get displayName; - - Iterable get downstreamEdges => _downstreamEdges; - - /// After nullability propagation, this getter can be used to query whether - /// the type associated with this node should be considered "exact nullable". - @visibleForTesting - bool get isExactNullable; - - /// After nullability propagation, this getter can be used to query whether - /// the type associated with this node should be considered nullable. - @override - bool get isNullable; - - /// Indicates whether this node is associated with a named parameter for which - /// nullability migration needs to decide whether it is optional or required. - bool get isPossiblyOptional => _isPossiblyOptional; - - /// Indicates whether this node is associated with a variable declaration - /// which should be annotated with "late". - LateCondition get lateCondition => _lateCondition; - - /// After nullability propagation, this getter can be used to query the node's - /// non-null intent state. - NonNullIntent get nonNullIntent; - - @override - Iterable get upstreamEdges => _upstreamEdges; - - @override - UpstreamPropagationStep? get whyNotNullable; - - Nullability? get _nullability; - - /// Records the fact that an invocation was made to a function with named - /// parameters, and the named parameter associated with this node was not - /// supplied. - void recordNamedParameterNotSupplied(List guards, - NullabilityGraph graph, NamedParameterNotSuppliedOrigin origin) { - if (isPossiblyOptional) { - graph.connect(graph.always, this, origin, guards: guards); - } - } - - /// Reset the state of this node to what it was before the graph was solved. - void resetState(); - - String toString({NodeToIdMapper? idMapper}) { - var name = displayName; - if (idMapper == null) { - return name; - } else { - return '${idMapper.idForNode(this)}: $name'; - } - } - - /// Tracks the possibility that this node is associated with a named parameter - /// for which nullability migration needs to decide whether it is optional or - /// required. - void trackPossiblyOptional() { - _isPossiblyOptional = true; - } -} - -/// Base class for nullability nodes that are nullable if at least one of a set -/// of other nodes is nullable, and non-nullable otherwise; the set of other -/// nodes are called "components". -abstract class NullabilityNodeCompound extends NullabilityNodeMutable { - NullabilityNodeCompound() : super._(); - - /// A map describing each of the node's components by name. - Map get componentsByName; - - @override - bool get isExactNullable => _components.any((c) => c!.isExactNullable); - - @override - bool get isNullable => _components.any((c) => c!.isNullable); - - Iterable get _components; -} - -/// Derived class for nullability nodes that arise from the least-upper-bound -/// implied by a conditional expression. -class NullabilityNodeForLUB extends NullabilityNodeCompound { - final NullabilityNode? left; - - final NullabilityNode? right; - - NullabilityNodeForLUB._(this.left, this.right) { - left!.outerCompoundNodes.add(this); - right!.outerCompoundNodes.add(this); - } - - @override - Map get componentsByName => - {'left': left, 'right': right}; - - @override - String get displayName => '${left!.displayName} or ${right!.displayName}'; - - @override - Iterable get _components => [left, right]; - - @override - void resetState() { - left!.resetState(); - right!.resetState(); - } -} - -/// Derived class for nullability nodes that arise from type variable -/// substitution. -class NullabilityNodeForSubstitution extends NullabilityNodeCompound - implements SubstitutionNodeInfo { - @override - final NullabilityNode innerNode; - - @override - final NullabilityNode outerNode; - - NullabilityNodeForSubstitution._(this.innerNode, this.outerNode) { - innerNode.outerCompoundNodes.add(this); - outerNode.outerCompoundNodes.add(this); - } - - @override - Map get componentsByName => - {'inner': innerNode, 'outer': outerNode}; - - @override - String get displayName => - '${innerNode.displayName} or ${outerNode.displayName}'; - - @override - Iterable get _components => [innerNode, outerNode]; - - @override - void resetState() { - innerNode.resetState(); - outerNode.resetState(); - } -} - -/// Base class for nullability nodes whose state can be mutated safely. -/// -/// Nearly all nullability nodes derive from this class; the only exceptions are -/// the fixed nodes "always "never". -abstract class NullabilityNodeMutable extends NullabilityNode { - Nullability? _nullability; - - NonNullIntent _nonNullIntent; - - DownstreamPropagationStep? _whyNullable; - - UpstreamPropagationStep? _whyNotNullable; - - NullabilityNodeMutable._( - {Nullability initialNullability = Nullability.nonNullable}) - : _nullability = initialNullability, - _nonNullIntent = NonNullIntent.none, - super._(); - - @override - bool get isExactNullable => _nullability!.isExactNullable; - - @override - bool get isImmutable => false; - - @override - bool get isNullable => _nullability!.isNullable; - - @override - NonNullIntent get nonNullIntent => _nonNullIntent; - - @override - UpstreamPropagationStep? get whyNotNullable => _whyNotNullable; - - @override - DownstreamPropagationStepInfo? get whyNullable => _whyNullable; - - @override - void resetState() { - _nullability = Nullability.nonNullable; - _nonNullIntent = NonNullIntent.none; - _whyNullable = null; - } -} - -/// Information produced by [NullabilityGraph.propagate] about the results of -/// graph propagation. -class PropagationResult { - /// A list of all edges that couldn't be satisfied. May contain duplicates. - final List unsatisfiedEdges = []; - - /// A list of all substitution nodes that couldn't be satisfied. - final List unsatisfiedSubstitutions = []; - - PropagationResult._(); -} - -/// Class representing a step taken by the nullability propagation algorithm. -abstract class PropagationStep implements PropagationStepInfo { - PropagationStep(); - - /// The location in the source code that caused this step to be necessary, - /// or `null` if not known. - CodeReference? get codeReference => null; - - /// The previous propagation step that led to this one, or `null` if there was - /// no previous step. - PropagationStep? get principalCause; - - @override - String toString({NodeToIdMapper? idMapper}); -} - -/// Propagation step where we consider mark one of the components of a -/// substitution node as nullable because the substitution node itself is -/// nullable. -class ResolveSubstitutionPropagationStep extends ExactNullablePropagationStep { - @override - final DownstreamPropagationStep principalCause; - - /// The substitution node that needed resolution. - final NullabilityNodeForSubstitution node; - - ResolveSubstitutionPropagationStep(this.principalCause, this.node); - - @override - EdgeInfo? get edge => null; - - @override - String toString({NodeToIdMapper? idMapper}) => - '${targetNode!.toString(idMapper: idMapper)} becomes $newState due to ' - '${node.toString(idMapper: idMapper)}'; -} - -/// Propagation step where we mark the destination of an edge as nullable, due -/// to its sources becoming nullable. -class SimpleDownstreamPropagationStep extends DownstreamPropagationStep { - @override - final DownstreamPropagationStep? principalCause; - - @override - final NullabilityEdge edge; - - SimpleDownstreamPropagationStep(this.principalCause, this.edge); - - @override - CodeReference? get codeReference => edge.codeReference; - - @override - String toString({NodeToIdMapper? idMapper}) => - '${targetNode!.toString(idMapper: idMapper)} becomes $newState due to ' - '${edge.toString(idMapper: idMapper)}'; -} - -/// Propagation step where we mark the source of an edge as exact nullable, due -/// to its destination becoming exact nullable. -class SimpleExactNullablePropagationStep extends ExactNullablePropagationStep { - @override - final ExactNullablePropagationStep principalCause; - - @override - final NullabilityEdge edge; - - SimpleExactNullablePropagationStep(this.principalCause, this.edge); - - @override - String toString({NodeToIdMapper? idMapper}) => - '${targetNode!.toString(idMapper: idMapper)} becomes $newState due to ' - '${edge.toString(idMapper: idMapper)}'; -} - -/// Propagation step where we mark a node as having non-null intent due to it -/// being upstream from another node with non-null intent. -class UpstreamPropagationStep extends PropagationStep - implements UpstreamPropagationStepInfo { - @override - final UpstreamPropagationStep? principalCause; - - /// The node being marked as having non-null intent. - final NullabilityNode node; - - /// The new state of the node's non-null intent. - final NonNullIntent newNonNullIntent; - - /// The nullability edge connecting [node] to the node it is upstream from, if - /// any. - final NullabilityEdge? edge; - - @override - final bool isStartingPoint; - - UpstreamPropagationStep( - this.principalCause, this.node, this.newNonNullIntent, this.edge, - {this.isStartingPoint = false}); - - @override - CodeReference? get codeReference => edge?.codeReference; - - @override - String toString({NodeToIdMapper? idMapper}) => - '${node.toString(idMapper: idMapper)} becomes $newNonNullIntent'; -} - -/// Kinds of nullability edges -enum _NullabilityEdgeKind { - /// Soft edge. Propagates nullability downstream only. May be overridden by - /// suggestions that the user intends non-nullability. - soft, - - /// Uncheckable edge. Propagates nullability downstream only. May not be - /// overridden by suggestions that the user intends non-nullability. - uncheckable, - - /// Hard edge. Propagates nullability downstream and non-nullability - /// upstream. - hard, - - /// Union edge. Indicates that two nodes should have exactly the same - /// nullability. - union, - - /// Uncheckable hard edge. Propagates nullability downstream and - /// non-nullability upstream. May not be overridden by suggestions that the - /// user intends non-nullability. - uncheckableHard, - - /// Dummy edge. Indicates that two edges are connected in a way that should - /// not propagate (non-)nullability in either direction. - dummy, -} - -class _NullabilityNodeImmutable extends NullabilityNode { - @override - final String displayName; - - @override - final bool isNullable; - - _NullabilityNodeImmutable(this.displayName, this.isNullable) : super._(); - - @override - String get debugSuffix => isNullable ? '?' : ''; - - @override - Map>> get hintActions => const {}; - - @override - // Note: the node "always" is not exact nullable, because exact nullability is - // a concept for contravariant generics which propagates upstream instead of - // downstream. "always" is not a contravariant generic, and does not have any - // upstream nodes, so it should not be considered *exact* nullable. - bool get isExactNullable => false; - - @override - bool get isImmutable => true; - - @override - NonNullIntent get nonNullIntent => - isNullable ? NonNullIntent.none : NonNullIntent.direct; - - @override - UpstreamPropagationStep? get whyNotNullable => null; - - @override - DownstreamPropagationStepInfo? get whyNullable => null; - - @override - Nullability get _nullability => - isNullable ? Nullability.ordinaryNullable : Nullability.nonNullable; - - @override - void resetState() { - // There is no state to reset. - } -} - -class _NullabilityNodeSimple extends NullabilityNodeMutable { - final NullabilityNodeTarget target; - - _NullabilityNodeSimple(this.target) : super._(); - - @override - CodeReference? get codeReference => target.codeReference; - - @override - String get displayName => target.displayName; -} - -/// Workspace for performing graph propagation. -/// -/// Graph propagation is performed immediately upon construction, so as soon as -/// the caller has constructed this object, the graph has been propagated and -/// the results of propagation can be retrieved from [result]. -class _PropagationState { - /// The result of propagation, for sharing with the client. - final PropagationResult result = PropagationResult._(); - - /// The graph's one and only "always" node. - final NullabilityNode _always; - - /// The graph's one and only "never" node. - final NullabilityNode _never; - - /// During any given stage of nullability propagation, a queue of all the - /// edges that need to be examined before the stage is complete. - final Queue _pendingDownstreamSteps = - Queue(); - - /// During execution of [_propagateDownstream], a list of all the substitution - /// nodes that have not yet been resolved. - List _pendingSubstitutions = []; - - _PropagationState(this._always, this._never) { - _propagateUpstream(); - _propagateDownstream(); - } - - /// Propagates nullability downstream. - void _propagateDownstream() { - assert(_pendingDownstreamSteps.isEmpty); - for (var edge in _always._downstreamEdges) { - _pendingDownstreamSteps.add(SimpleDownstreamPropagationStep(null, edge)); - } - while (true) { - while (_pendingDownstreamSteps.isNotEmpty) { - var step = _pendingDownstreamSteps.removeFirst(); - var edge = step.edge; - if (!edge.isTriggered) continue; - var node = edge.destinationNode; - if (edge.isUninit && !node.isNullable) { - // [edge] is an edge from always to an uninitialized variable - // declaration. - var isSetupAssigned = node.upstreamEdges - .any((e) => e is NullabilityEdge && e.isSetupAssignment); - - // Whether all downstream edges go to nodes with non-null intent. - var allDownstreamHaveNonNullIntent = false; - if (node.downstreamEdges.isNotEmpty) { - allDownstreamHaveNonNullIntent = node.downstreamEdges.every((e) { - var destination = e.destinationNode; - return destination is NullabilityNode && - destination.nonNullIntent.isPresent; - }); - } - if (allDownstreamHaveNonNullIntent) { - node._lateCondition = LateCondition.possiblyLate; - continue; - } else if (isSetupAssigned) { - node._lateCondition = LateCondition.possiblyLateDueToTestSetup; - continue; - } - } - var nonNullIntent = node.nonNullIntent; - if (nonNullIntent.isPresent) { - if (edge.isCheckable) { - // The node has already been marked as having non-null intent, and - // the edge can be addressed by adding a null check, so we prefer to - // leave the edge unsatisfied and let the null check happen. - result.unsatisfiedEdges.add(edge); - continue; - } - if (nonNullIntent.isDirect) { - // The node has direct non-null intent so we aren't in a position to - // mark it as nullable. - result.unsatisfiedEdges.add(edge); - continue; - } - } - if (node is NullabilityNodeMutable && !node.isNullable) { - assert(step.targetNode == null); - step.targetNode = node; - step.newState = Nullability.ordinaryNullable; - _setNullable(step); - node._lateCondition = LateCondition.notLate; - } - } - if (_pendingSubstitutions.isEmpty) break; - var oldPendingSubstitutions = _pendingSubstitutions; - _pendingSubstitutions = []; - for (var step in oldPendingSubstitutions) { - _resolvePendingSubstitution(step); - } - } - } - - /// Propagates non-null intent upstream along unconditional control flow - /// lines. - void _propagateUpstream() { - Queue pendingSteps = Queue(); - pendingSteps.add(UpstreamPropagationStep( - null, _never, NonNullIntent.direct, null, - isStartingPoint: true)); - while (pendingSteps.isNotEmpty) { - var cause = pendingSteps.removeFirst(); - var pendingNode = cause.node; - for (var edge in pendingNode._upstreamEdges) { - // We only propagate for nodes that are "upstream triggered". At this - // point of propagation, a node is upstream triggered if it is hard. - assert(edge.isUpstreamTriggered == edge.isHard); - if (!edge.isHard) continue; - var node = edge.sourceNode; - if (node is NullabilityNodeMutable) { - var oldNonNullIntent = node._nonNullIntent; - NonNullIntent newNonNullIntent; - if (edge.isUnion && edge.destinationNode == _never) { - // If a node is unioned with "never" then it's considered to have - // direct non-null intent. - newNonNullIntent = NonNullIntent.direct; - } else { - newNonNullIntent = oldNonNullIntent.addIndirect(); - } - var step = - UpstreamPropagationStep(cause, node, newNonNullIntent, edge); - _setNonNullIntent(step); - if (!oldNonNullIntent.isPresent) { - // We did not previously have non-null intent, so we need to - // propagate. - pendingSteps.add(step); - } - } - } - // If any compound node is forced to be non-nullable by this change, - // propagate to it. - for (var node in pendingNode.outerCompoundNodes) { - if (node._components - .any((component) => !component!.nonNullIntent.isPresent)) { - continue; - } - var oldNonNullIntent = node._nonNullIntent; - var newNonNullIntent = oldNonNullIntent.addIndirect(); - var step = UpstreamPropagationStep(cause, node, newNonNullIntent, null); - _setNonNullIntent(step); - if (!oldNonNullIntent.isPresent) { - // We did not previously have non-null intent, so we need to - // propagate. - pendingSteps.add(step); - } - } - } - } - - void _resolvePendingSubstitution(ResolveSubstitutionPropagationStep step) { - NullabilityNodeForSubstitution substitutionNode = step.node; - assert(substitutionNode._nullability!.isNullable); - // If both nodes pointed to by the substitution node have non-null intent, - // then no resolution is needed; the substitution node can’t be satisfied. - if (substitutionNode.innerNode.nonNullIntent.isPresent && - substitutionNode.outerNode.nonNullIntent.isPresent) { - result.unsatisfiedSubstitutions.add(substitutionNode); - return; - } - - // Otherwise, if the outer node is in a nullable state, then no resolution - // is needed because the substitution node is already satisfied. - if (substitutionNode.outerNode.isNullable) { - return; - } - - // Otherwise, if the inner node has non-null intent, then we set the outer - // node to the ordinary nullable state. - if (substitutionNode.innerNode.nonNullIntent.isPresent) { - assert(step.targetNode == null); - step.targetNode = substitutionNode.outerNode as NullabilityNodeMutable; - step.newState = Nullability.ordinaryNullable; - _setNullable(step); - return; - } - - // Otherwise, we set the inner node to the exact nullable state, and we - // propagate this state upstream as far as possible using the following - // rule: if there is an edge A → B, where A is in the undetermined or - // ordinary nullable state, and B is in the exact nullable state, then A’s - // state is changed to exact nullable. - var pendingExactNullableSteps = []; - var node = substitutionNode.innerNode; - if (node is NullabilityNodeMutable) { - assert(step.targetNode == null); - step.targetNode = node; - step.newState = Nullability.exactNullable; - var oldNullability = _setNullable(step); - if (!oldNullability.isExactNullable) { - // Was not previously in the "exact nullable" state. Need to - // propagate. - for (var edge in node._upstreamEdges) { - pendingExactNullableSteps - .add(SimpleExactNullablePropagationStep(step, edge)); - } - - // TODO(mfairhurst): should this propagate back up outerContainerNodes? - } - } - - while (pendingExactNullableSteps.isNotEmpty) { - var step = pendingExactNullableSteps.removeLast(); - var edge = step.edge; - var node = edge.sourceNode; - if (node is NullabilityNodeMutable && - !edge.isCheckable && - !node.nonNullIntent.isPresent) { - assert(step.targetNode == null); - step.targetNode = node; - step.newState = Nullability.exactNullable; - var oldNullability = _setNullable(step); - if (!oldNullability.isExactNullable) { - // Was not previously in the "exact nullable" state. Need to - // propagate. - for (var edge in node._upstreamEdges) { - pendingExactNullableSteps - .add(SimpleExactNullablePropagationStep(step, edge)); - } - } - } - } - } - - void _setNonNullIntent(UpstreamPropagationStep step) { - var node = step.node as NullabilityNodeMutable; - var newNonNullIntent = step.newNonNullIntent; - var oldNonNullIntent = node.nonNullIntent; - node._nonNullIntent = newNonNullIntent; - if (!oldNonNullIntent.isPresent) { - node._whyNotNullable = step; - } - } - - Nullability _setNullable(DownstreamPropagationStep step) { - var node = step.targetNode!; - var newState = step.newState; - var oldState = node._nullability!; - node._nullability = newState; - if (!oldState.isNullable) { - node._whyNullable = step; - // Was not previously nullable, so we need to propagate. - for (var edge in node._downstreamEdges) { - _pendingDownstreamSteps - .add(SimpleDownstreamPropagationStep(step, edge)); - } - if (node is NullabilityNodeForSubstitution) { - _pendingSubstitutions - .add(ResolveSubstitutionPropagationStep(step, node)); - } - } - return oldState; - } -} diff --git a/pkg/nnbd_migration/lib/src/nullability_node_target.dart b/pkg/nnbd_migration/lib/src/nullability_node_target.dart deleted file mode 100644 index 83199366ef3e..000000000000 --- a/pkg/nnbd_migration/lib/src/nullability_node_target.dart +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:nnbd_migration/instrumentation.dart'; - -String _computeElementName(Element? element) { - List parts = []; - while (element != null && element is! CompilationUnitElement) { - var name = element.name; - if (name == null || name.isEmpty) { - parts.add(''); - } else { - parts.add(name); - } - element = element.enclosingElement; - } - if (parts.isEmpty) { - assert(false, 'Could not compute a name for $element'); - return ''; - } - return parts.reversed.join('.'); -} - -/// Data structure tracking information about which type in the user's source -/// code is referenced by a given nullability node. -abstract class NullabilityNodeTarget { - /// Creates a [NullabilityNodeTarget] referring to a particular element. - factory NullabilityNodeTarget.element(Element element) = - _NullabilityNodeTarget_Element; - - /// Creates a [NullabilityNodeTarget] with a simple text description. - factory NullabilityNodeTarget.text(String name) = _NullabilityNodeTarget_Text; - - /// Creates a new [NullabilityNodeTarget] representing the bound of a type - /// parameter. - factory NullabilityNodeTarget.typeParameterBound( - TypeParameterElement element) = _NullabilityNodeTarget_TypeParameterBound; - - NullabilityNodeTarget._(); - - /// The source code location associated with this target, if known. Otherwise - /// `null`. - CodeReference? get codeReference => null; - - /// Gets a short description of this nullability node target suitable for - /// displaying to the user, not including a code reference. - String get description; - - /// Same as [description], but if there is a [codeReference], it is included - /// after the description in parentheses. - String get displayName { - if (codeReference == null) return description; - return '$description (${codeReference!.shortName})'; - } - - NullabilityNodeTarget get supertype => _NullabilityNodeTarget_Supertype(this); - - /// Creates a new [NullabilityNodeTarget] representing a named function - /// parameter of this target. - NullabilityNodeTarget namedParameter(String name) => - _NullabilityNodeTarget_NamedParameter(this, name); - - /// Creates a new [NullabilityNodeTarget] representing a positional function - /// parameter of this target. - NullabilityNodeTarget positionalParameter(int index) => - _NullabilityNodeTarget_PositionalParameter(this, index); - - /// Creates a new [NullabilityNodeTarget] representing a function return type - /// of this target. - NullabilityNodeTarget returnType() => _NullabilityNodeTarget_ReturnType(this); - - /// Creates a new [NullabilityNodeTarget] representing a type argument of this - /// target. - NullabilityNodeTarget typeArgument(int index) => - _NullabilityNodeTarget_TypeArgument(this, index); - - /// Creates a new [NullabilityNodeTarget] representing the bound of a formal - /// function type parameter of this target. - NullabilityNodeTarget typeFormalBound(String typeFormalName) => - _NullabilityNodeTarget_TypeFormalBound(this, typeFormalName); - - /// Creates a [NullabilityNodeTarget] referring to a particular point in the - /// source code. - NullabilityNodeTarget withCodeRef(AstNode astNode) => - _NullabilityNodeTarget_CodeRef(this, astNode); -} - -/// Nullability node target representing a reference to a specific location in -/// source code. -class _NullabilityNodeTarget_CodeRef extends NullabilityNodeTarget { - final NullabilityNodeTarget inner; - - final CodeReference codeReference; - - _NullabilityNodeTarget_CodeRef(this.inner, AstNode astNode) - : codeReference = CodeReference.fromAstNode(astNode), - super._(); - - @override - String get description => inner.description; -} - -/// Nullability node target representing the type of an element. -class _NullabilityNodeTarget_Element extends NullabilityNodeTarget { - final String name; - - final CodeReference codeReference; - - _NullabilityNodeTarget_Element(Element element) - : name = _computeElementName(element), - codeReference = CodeReference.fromElement(element), - super._(); - - @override - String get description => name; -} - -/// Nullability node target representing the type of a named function parameter. -class _NullabilityNodeTarget_NamedParameter - extends _NullabilityNodeTarget_Part { - final String name; - - _NullabilityNodeTarget_NamedParameter(super.inner, this.name); - - @override - String get description => 'parameter $name of ${inner.description}'; -} - -/// Nullability node target representing a type that forms part of a larger type -/// (e.g. the `int` part of `List`). -abstract class _NullabilityNodeTarget_Part extends NullabilityNodeTarget { - final NullabilityNodeTarget inner; - - _NullabilityNodeTarget_Part(this.inner) : super._(); - - @override - CodeReference? get codeReference => inner.codeReference; -} - -/// Nullability node target representing the type of a positional function -/// parameter. -class _NullabilityNodeTarget_PositionalParameter - extends _NullabilityNodeTarget_Part { - final int index; - - _NullabilityNodeTarget_PositionalParameter(super.inner, this.index); - - @override - String get description => 'parameter $index of ${inner.description}'; -} - -/// Nullability node target representing a function's return type. -class _NullabilityNodeTarget_ReturnType extends _NullabilityNodeTarget_Part { - _NullabilityNodeTarget_ReturnType(super.inner); - - @override - String get description => 'return type of ${inner.description}'; -} - -/// Nullability node target representing one of a class's supertypes. -class _NullabilityNodeTarget_Supertype extends _NullabilityNodeTarget_Part { - _NullabilityNodeTarget_Supertype(super.inner); - - @override - String get description => 'supertype of ${inner.description}'; -} - -/// Nullability node target for which we only know a string description. -class _NullabilityNodeTarget_Text extends NullabilityNodeTarget { - final String name; - - _NullabilityNodeTarget_Text(this.name) : super._(); - - @override - String get description => name; -} - -/// Nullability node target representing a type argument of an interface type or -/// or typedef. -class _NullabilityNodeTarget_TypeArgument extends _NullabilityNodeTarget_Part { - final int index; - - _NullabilityNodeTarget_TypeArgument(super.inner, this.index); - - @override - String get description => 'type argument $index of ${inner.description}'; -} - -/// Nullability node target representing a bound of a function type's formal -/// type parameter. -class _NullabilityNodeTarget_TypeFormalBound - extends _NullabilityNodeTarget_Part { - final String typeFormalName; - - _NullabilityNodeTarget_TypeFormalBound(super.inner, this.typeFormalName); - - @override - String get description => - 'bound of type formal $typeFormalName of ${inner.description}'; -} - -/// Nullability node target representing a type parameter bound. -class _NullabilityNodeTarget_TypeParameterBound extends NullabilityNodeTarget { - final String name; - - _NullabilityNodeTarget_TypeParameterBound(TypeParameterElement element) - : name = _computeElementName(element), - super._(); - - @override - String get description => 'bound of $name'; -} diff --git a/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart b/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart deleted file mode 100644 index d1ff55a32f32..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/dart_file_page.dart +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert'; - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/unit_renderer.dart'; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that is displayed when a preview of a valid Dart file is requested. -class DartFilePage extends PreviewPage { - /// The information about the file being previewed. - final UnitInfo unitInfo; - - /// Initialize a newly created Dart file page within the given [site]. The - /// [unitInfo] provides the information needed to render the page. - DartFilePage(PreviewSite site, this.unitInfo) - // TODO(brianwilkerson) The path needs to be converted to use '/' if that - // isn't already done as part of building the unitInfo. - : super(site, unitInfo.path!.substring(1)); - - @override - bool get requiresAuth => true; - - @override - void generateBody(Map params) { - throw UnsupportedError('generateBody'); - } - - @override - Future generatePage(Map params) async { - var renderer = UnitRenderer( - unitInfo, site.migrationInfo, site.pathMapper, site.serviceAuthToken); - buf.write(jsonEncode(renderer.render().toJson())); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart b/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart deleted file mode 100644 index 009a6718d182..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/dart_logo_page.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that contains the Dart logo. -class DartLogoPage extends PreviewPage { - /// Initialize a newly created Dart logo page within the given [site]. - DartLogoPage(PreviewSite site) - : super(site, PreviewSite.dartLogoPath.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - throw UnimplementedError(); - } - - @override - Future generatePage(Map params) async { - buf.write(resources.dart_192_png); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/exception_page.dart b/pkg/nnbd_migration/lib/src/preview/exception_page.dart deleted file mode 100644 index 2d8abcc766f6..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/exception_page.dart +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert'; - -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that is displayed when an exception is encountered in the process -/// of composing the content of a different page. -class ExceptionPage extends PreviewPage { - /// The message from the exception that caused this page to be displayed. - final String message; - - /// The stack trace of the exception that caused this page to be displayed. - final StackTrace stackTrace; - - /// Initialize a newly created exception page within the given [site]. The - /// [message] and [stackTrace] are used to describe the exception to the user. - ExceptionPage(PreviewSite site, String path, this.message, this.stackTrace) - : super(site, path.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - buf.write(''' -

500 Exception in preview

-

-We're sorry, but you've encountered a bug in the preview tool. Please visit - -github.com/dart-lang/sdk/issues/new to report the issue and include the -stack trace below. -

-

$message

-

-${htmlEscape.convert(stackTrace.toString())} -

-'''); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/highlight_css_page.dart b/pkg/nnbd_migration/lib/src/preview/highlight_css_page.dart deleted file mode 100644 index 5b13262a3395..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/highlight_css_page.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that contains the CSS used to style the semantic highlighting -/// within a Dart file. -class HighlightCssPage extends PreviewPage { - /// Initialize a newly created CSS page within the given [site]. - HighlightCssPage(PreviewSite site) - : super(site, PreviewSite.highlightCssPath.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - throw UnimplementedError(); - } - - @override - Future generatePage(Map params) async { - buf.write(resources.highlight_css); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/highlight_js_page.dart b/pkg/nnbd_migration/lib/src/preview/highlight_js_page.dart deleted file mode 100644 index 564c782fdae4..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/highlight_js_page.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that contains the JavaScript used to apply semantic highlighting -/// styles to a Dart file. -class HighlightJSPage extends PreviewPage { - /// Initialize a newly created JS page within the given [site]. - HighlightJSPage(PreviewSite site) - : super(site, PreviewSite.highlightJsPath.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - throw UnimplementedError(); - } - - @override - Future generatePage(Map params) async { - buf.write(resources.highlight_pack_js); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart b/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart deleted file mode 100644 index d27ac3de94bb..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/http_preview_server.dart +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:io'; - -import 'package:cli_util/cli_logging.dart'; -import 'package:nnbd_migration/src/front_end/migration_state.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// Instances of the class [AbstractGetHandler] handle GET requests. -abstract class AbstractGetHandler { - /// Handle a GET request received by the HTTP server. - Future handleGetRequest(HttpRequest request); -} - -/// Instances of the class [AbstractPostHandler] handle POST requests. -abstract class AbstractPostHandler { - /// Handle a POST request received by the HTTP server. - Future handlePostRequest(HttpRequest request); -} - -/// Instances of the class [HttpPreviewServer] implement a simple HTTP server -/// that serves up dartfix preview pages. -class HttpPreviewServer { - /// The state of the migration being previewed. - final MigrationState migrationState; - - /// The [PreviewSite] that can handle GET and POST requests. - PreviewSite? _previewSite; - - /// Future that is completed with the HTTP server once it is running. - Future? _serverFuture; - - // A function which allows the migration to be rerun, taking changed paths. - final Future Function() rerunFunction; - - /// Callback function that should be invoked after successfully applying - /// migration. - final void Function() applyHook; - - /// The internet address the server should bind to. Should be suitable for - /// passing to HttpServer.bind, i.e. either a [String] or an - /// [InternetAddress]. - final Object? bindAddress; - - /// Integer for a port to run the preview server on. If null or zero, allow - /// [HttpServer.bind] to pick one. - final int? preferredPort; - - final Logger _logger; - - /// Initialize a newly created HTTP server. - HttpPreviewServer(this.migrationState, this.rerunFunction, this.applyHook, - this.bindAddress, this.preferredPort, this._logger) - : assert(bindAddress is String || bindAddress is InternetAddress); - - Future get authToken async { - await _serverFuture; - return previewSite.serviceAuthToken; - } - - /// Return the port this server is bound to. - Future get boundHostname async { - return (await _serverFuture)?.address.host; - } - - /// Return the port this server is bound to. - Future get boundPort async { - return (await _serverFuture)?.port; - } - - PreviewSite get previewSite => _previewSite ??= - PreviewSite(migrationState, rerunFunction, applyHook, _logger); - - void close() { - _serverFuture?.then((HttpServer server) { - server.close(); - }); - } - - /// Begin serving HTTP requests over the given port. - Future serveHttp() async { - if (_serverFuture != null) { - return boundPort; - } - - try { - _serverFuture = HttpServer.bind(bindAddress, preferredPort ?? 0); - var server = await _serverFuture!; - _handleServer(server); - return server.port; - } catch (ignore) { - // If we can't bind to the specified port, don't remember the broken - // server. - _serverFuture = null; - // TODO(jcollins-g): Display a better error message? - rethrow; - } - } - - /// Handle a GET request received by the HTTP server. - Future _handleGetRequest(HttpRequest request) async { - await previewSite.handleGetRequest(request); - } - - /// Handle a POST request received by the HTTP server. - Future _handlePostRequest(HttpRequest request) async { - await previewSite.handlePostRequest(request); - } - - /// Attach a listener to a newly created HTTP server. - void _handleServer(HttpServer httpServer) { - httpServer.listen((HttpRequest request) async { - var updateValues = request.headers[HttpHeaders.upgradeHeader]; - if (request.method == 'GET') { - await _handleGetRequest(request); - } else if (request.method == 'POST') { - await _handlePostRequest(request); - } else if (updateValues != null && updateValues.contains('websocket')) { - // We do not support serving analysis server communications over - // WebSocket connections. - var response = request.response; - response.statusCode = HttpStatus.notFound; - response.headers.contentType = ContentType.text; - response.write( - 'WebSocket connections not supported (${request.uri.path}).'); - response.close(); - } else { - _returnUnknownRequest(request); - } - }); - } - - /// Return an error in response to an unrecognized request received by the HTTP - /// server. - void _returnUnknownRequest(HttpRequest request) { - var response = request.response; - response.statusCode = HttpStatus.notFound; - response.headers.contentType = ContentType.text; - response.write('Not found'); - response.close(); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/index_file_page.dart b/pkg/nnbd_migration/lib/src/preview/index_file_page.dart deleted file mode 100644 index 3e658e12973f..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/index_file_page.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/front_end/instrumentation_renderer.dart'; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that is displayed when the root of the included path is requested. -class IndexFilePage extends PreviewPage { - /// Initialize a newly created index file page within the given [site]. - IndexFilePage(PreviewSite site) - : super(site, site.migrationInfo!.includedRoot); - - @override - bool get requiresAuth => true; - - @override - void generateBody(Map params) { - throw UnsupportedError('generateBody'); - } - - @override - Future generatePage(Map params) async { - var renderer = InstrumentationRenderer(site.migrationInfo, site.pathMapper, - site.migrationState!.hasBeenApplied, site.migrationState!.needsRerun); - buf.write(renderer.render()); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/material_icons_page.dart b/pkg/nnbd_migration/lib/src/preview/material_icons_page.dart deleted file mode 100644 index df3dc90be015..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/material_icons_page.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that contains the CSS used to style the semantic highlighting -/// within a Dart file. -class MaterialIconsPage extends PreviewPage { - /// Initialize a newly created CSS page within the given [site]. - MaterialIconsPage(PreviewSite site) - : super(site, PreviewSite.materialIconsPath.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - throw UnimplementedError(); - } - - @override - Future generatePage(Map params) async { - buf.write(resources.MaterialIconsRegular_ttf); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart b/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart deleted file mode 100644 index 2ae2183a3adc..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/navigation_tree_page.dart +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:convert'; - -import 'package:nnbd_migration/src/front_end/navigation_tree_renderer.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The JSON that is displayed for the navigation tree. -class NavigationTreePage extends PreviewPage { - /// Initialize a newly created navigation tree page within the given [site]. - NavigationTreePage(PreviewSite site) - : super(site, site.migrationInfo!.includedRoot); - - @override - bool get requiresAuth => true; - - @override - // TODO(srawlins): Refactor JSON-returning pages like this to not inherit all - // of the HTML logic from [PreviewPage]. - void generateBody(Map params) { - throw UnsupportedError('generateBody'); - } - - @override - Future generatePage(Map params) async { - var renderer = NavigationTreeRenderer(site.migrationInfo, site.pathMapper); - buf.write(jsonEncode(NavigationTreeNode.listToJson(renderer.render()))); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/not_found_page.dart b/pkg/nnbd_migration/lib/src/preview/not_found_page.dart deleted file mode 100644 index efc86434c493..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/not_found_page.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/preview/preview_page.dart'; - -/// The page that is displayed when an invalid URL is requested. -class NotFoundPage extends PreviewPage { - /// Initialize a newly created file-not-found page within the given [site]. - /// The [id] is the portion of the path to the page that follows the initial - /// slash ('/'). - NotFoundPage(super.site, String super.id); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - buf.write(''' -

404 Not found

-

-'$path' not found. -

-'''); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/pages.dart b/pkg/nnbd_migration/lib/src/preview/pages.dart deleted file mode 100644 index 6a7829799aef..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/pages.dart +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2017, 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. - -import 'dart:convert'; -import 'dart:io'; - -/// An entity that knows how to serve itself over http. -abstract class Page { - final StringBuffer buf = StringBuffer(); - - final String? id; - - Page(this.id); - - String get path => '/$id'; - - Future generate(Map params) async { - buf.clear(); - // TODO(brianwilkerson) Determine if await is necessary, if so, change the - // return type of [generatePage] to `Future`. - await (generatePage(params) as dynamic); - return buf.toString(); - } - - Future generatePage(Map params); -} - -/// Contains a collection of Pages. -abstract class Site { - final String title; - final List pages = []; - - Site(this.title); - - Page createExceptionPage(String message, StackTrace trace); - - Page createUnknownPage(String unknownPath); - - Future handleGetRequest(HttpRequest request) async { - try { - var path = request.uri.path; - - if (path == '/') { - respondRedirect(request, pages.first.path); - return; - } - - for (var page in pages) { - if (page.path == path) { - var response = request.response; - response.headers.contentType = ContentType.html; - response.write(await page.generate(request.uri.queryParameters)); - response.close(); - return; - } - } - - await respond(request, createUnknownPage(path), HttpStatus.notFound); - } catch (e, st) { - try { - await respond(request, createExceptionPage('$e', st), - HttpStatus.internalServerError); - } catch (e, st) { - var response = request.response; - response.statusCode = HttpStatus.internalServerError; - response.headers.contentType = ContentType.text; - response.write('$e\n\n$st'); - response.close(); - } - } - } - - Future respond( - HttpRequest request, - Page page, [ - int code = HttpStatus.ok, - ]) async { - var response = request.response; - response.statusCode = code; - response.headers.contentType = ContentType.html; - response.write(await page.generate(request.uri.queryParameters)); - await response.close(); - } - - Future respondJson( - HttpRequest request, - Map json, [ - int code = HttpStatus.ok, - ]) async { - var response = request.response; - response.statusCode = code; - response.headers.contentType = ContentType.json; - response.write(jsonEncode(json)); - await response.close(); - } - - Future respondOk( - HttpRequest request, { - int code = HttpStatus.ok, - }) async { - if (request.headers.contentType!.subType == 'json') { - return respondJson(request, {'success': true}, code); - } - - var response = request.response; - response.statusCode = code; - await response.close(); - } - - Future respondRedirect(HttpRequest request, String pathFragment) async { - var response = request.response; - response.statusCode = HttpStatus.movedTemporarily; - await response.redirect(request.uri.resolve(pathFragment)); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/preview_page.dart b/pkg/nnbd_migration/lib/src/preview/preview_page.dart deleted file mode 100644 index e7b4479e738d..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/preview_page.dart +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/preview/pages.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// A page displayed on the preview site. -abstract class PreviewPage extends Page { - /// The site containing the page. - final PreviewSite site; - - /// Initialize a newly created page within the given [site]. The [id] is the - /// portion of the path to the page that follows the initial slash ('/'). - PreviewPage(this.site, String? id) : super(id); - - /// Whether pages of this type require authorization. - bool get requiresAuth; - - /// Generate the content of the body tag. - void generateBody(Map params); - - /// Generate the content of the head tag. - void generateHead() { - buf.writeln(''); - buf.writeln(''); - buf.writeln('${site.title}'); - } - - @override - Future generatePage(Map params) async { - buf.writeln(''); - buf.writeln(''); - buf.writeln(''); - generateHead(); - buf.writeln(''); - generateBody(params); - buf.writeln(''); - buf.writeln(''); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/preview_site.dart b/pkg/nnbd_migration/lib/src/preview/preview_site.dart deleted file mode 100644 index 1ac4052e57b7..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/preview_site.dart +++ /dev/null @@ -1,704 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert'; -import 'dart:io'; -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:analyzer/src/manifest/charcodes.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:cli_util/cli_logging.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/migration_state.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:nnbd_migration/src/preview/dart_file_page.dart'; -import 'package:nnbd_migration/src/preview/dart_logo_page.dart'; -import 'package:nnbd_migration/src/preview/exception_page.dart'; -import 'package:nnbd_migration/src/preview/highlight_css_page.dart'; -import 'package:nnbd_migration/src/preview/highlight_js_page.dart'; -import 'package:nnbd_migration/src/preview/http_preview_server.dart'; -import 'package:nnbd_migration/src/preview/index_file_page.dart'; -import 'package:nnbd_migration/src/preview/material_icons_page.dart'; -import 'package:nnbd_migration/src/preview/navigation_tree_page.dart'; -import 'package:nnbd_migration/src/preview/not_found_page.dart'; -import 'package:nnbd_migration/src/preview/pages.dart'; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/region_page.dart'; -import 'package:nnbd_migration/src/preview/roboto_mono_page.dart'; -import 'package:nnbd_migration/src/preview/roboto_page.dart'; -import 'package:nnbd_migration/src/preview/unauthorized_page.dart'; - -// The randomly generated auth token used to access the preview site. -String _makeAuthToken() { - final kTokenByteSize = 8; - var bytes = Uint8List(kTokenByteSize); - var random = Random.secure(); - for (var i = 0; i < kTokenByteSize; i++) { - bytes[i] = random.nextInt(256); - } - return base64Url.encode(bytes); -} - -/// A plan for an incremental migration. -/// -/// This plan uses [UnitMigrationStatus]es from [NavigationTreeNode]s to apply -/// different edits to different files: -/// -/// * migrating files will be edited according to a [SourceFileEdit], -/// * newly opted out files will be prepended with a Dart Language Version -/// comment specifying "2.9", -/// * already opted out files will remain unchanged. -class IncrementalPlan { - static final _nonWhitespaceChar = RegExp(r'\S'); - final MigrationInfo? migrationInfo; - final Map unitInfoMap; - final PathMapper? pathMapper; - final List edits; - final Logger? logger; - - /// The set of units which are to be opted out in this migration. - final Set optOutUnitPaths; - - /// Creates a new [IncrementalPlan], extracting all of the paths which are - /// "opting out" from [navigationTree]. - factory IncrementalPlan( - MigrationInfo? migrationInfo, - Map unitInfoMap, - PathMapper? pathMapper, - List edits, - Iterable navigationTree, - Logger? logger) { - var optOutUnitPaths = {}; - void addUnitsToOptOut(NavigationTreeNode entity) { - if (entity is NavigationTreeDirectoryNode) { - for (var child in entity.subtree!) { - addUnitsToOptOut(child); - } - } else { - if (entity.migrationStatus == UnitMigrationStatus.optingOut) { - optOutUnitPaths.add(entity.path); - } - } - } - - for (var entity in navigationTree) { - addUnitsToOptOut(entity); - } - - return IncrementalPlan._( - migrationInfo, unitInfoMap, pathMapper, edits, optOutUnitPaths, logger); - } - - IncrementalPlan._(this.migrationInfo, this.unitInfoMap, this.pathMapper, - this.edits, this.optOutUnitPaths, this.logger); - - /// Applies this migration to disk. - void apply() { - logger!.stdout('Applying migration suggestions to disk...'); - var migratedFiles = []; - for (final fileEdit in edits) { - var unit = unitInfoMap[fileEdit.file]; - // Decide whether to opt out; default to `false` files not included in - // [edits], like [pubspec.yaml]. - var unitIsOptOut = unit != null - ? optOutUnitPaths.contains(migrationInfo!.computeName(unit)) - : false; - if (!unitIsOptOut) { - final file = pathMapper!.provider.getFile(fileEdit.file); - var code = file.exists ? file.readAsStringSync() : ''; - code = SourceEdit.applySequence(code, fileEdit.edits); - file.writeAsStringSync(code); - migratedFiles.add(migrationInfo!.relativePathFromRoot(fileEdit.file)); - } - } - - // A file which is to be opted out may not be found in [edits], if all types - // were to be made non-nullable, etc. Iterate over [optOutUnitPaths] instead - // of [edits] to opt files out. - var newlyOptedOutFiles = []; - var keptOptedOutFiles = []; - for (var optOutUnitPath in optOutUnitPaths) { - var absolutePath = migrationInfo!.absolutePathFromRoot(optOutUnitPath); - var unit = unitInfoMap[absolutePath]!; - if (unit.wasExplicitlyOptedOut) { - // This unit was explicitly opted out of null safety with a Dart - // Language version comment. Leave the unit be. - keptOptedOutFiles.add(optOutUnitPath); - } else { - // This unit was not yet migrated at the start, was not explicitly - // opted out at the start, and is being opted out now. Add a Dart - // Language version comment. - final file = pathMapper!.provider.getFile(absolutePath); - var code = file.exists ? file.readAsStringSync() : ''; - file.writeAsStringSync(optCodeOutOfNullSafety(code)); - newlyOptedOutFiles.add(optOutUnitPath); - } - } - - _logFileStatus(migratedFiles, (text) => 'Migrated $text'); - _logFileStatus( - newlyOptedOutFiles, - (text) => - 'Opted $text out of null safety with a new Dart language version ' - 'comment'); - _logFileStatus( - keptOptedOutFiles, (text) => 'Kept $text opted out of null safety'); - } - - void _logFileStatus( - List files, String Function(String text) template) { - if (files.isNotEmpty) { - var count = files.length; - if (count <= 20) { - var s = count > 1 ? 's' : ''; - var text = '$count file$s'; - logger!.stdout('${template(text)}:'); - for (var path in files) { - logger!.stdout(' $path'); - } - } else { - var text = '$count files'; - logger!.stdout('${template(text)}.'); - } - } - } - - @visibleForTesting - static String optCodeOutOfNullSafety(String code) { - var newline = '\n'; - var length = code.length; - - if (length == 0) { - return '// @dart=2.9'; - } - - var index = 0; - - // Returns the next line and updates [index]. - // - // After this function returns, [index] points to the character after the - // end of the line which was returned. - String getLine() { - var nextIndex = code.indexOf('\n', index); - if (nextIndex < 0) { - // Last line. - var line = code.substring(index); - index = length; - return line; - } - var line = code.substring(index, nextIndex); - index = nextIndex + 1; - return line; - } - - // Skip past blank lines. - var line = getLine(); - if (code.codeUnitAt(index - 1) == $lf) { - if (index - 2 >= 0 && code.codeUnitAt(index - 2) == $cr) { - // Looks like Windows-style line endings ("\r\n"). Use "\r\n" for all - // inserted line endings. - newline = '\r\n'; - } - } - var lineStart = line.indexOf(_nonWhitespaceChar); - while (lineStart < 0) { - line = getLine(); - if (index == length) { - // [code] consists _only_ of blank lines. - return '// @dart=2.9$newline$newline$code'; - } - lineStart = line.indexOf(_nonWhitespaceChar); - } - - // [line] is the first non-blank line. - if (line.length > lineStart + 1 && - line.codeUnitAt(lineStart) == $slash && - line.codeUnitAt(lineStart + 1) == $slash) { - // [line] is a comment. - - if (index == length) { - // [code] consists _only_ of one comment line. - return '$code$newline$newline// @dart=2.9$newline'; - } - var previousLineIndex = index; - String newlinesAfterDlvc; - while (true) { - previousLineIndex = index; - line = getLine(); - lineStart = line.indexOf(_nonWhitespaceChar); - if (lineStart < 0) { - // Line of zero-or-more whitespace; end of block comment. - newlinesAfterDlvc = newline; - break; - } - if (line.length <= lineStart + 1) { - // Only one character; not a comment; end of block comment. - newlinesAfterDlvc = '$newline$newline'; - break; - } - if (line.codeUnitAt(lineStart) == $slash && - line.codeUnitAt(lineStart + 1) == $slash) { - // Comment line. - if (index == length) { - // [code] consists _only_ of this block comment. - return '$code$newline$newline// @dart=2.9$newline'; - } - continue; - } else { - // Non-blank, non-comment line. - newlinesAfterDlvc = '$newline$newline'; - break; - } - } - // [previousLineIndex] points to the start of [line], which is the first - // non-comment line following the first comment. - return '${code.substring(0, previousLineIndex)}$newline' - '// @dart=2.9$newlinesAfterDlvc' - '${code.substring(previousLineIndex)}'; - } else { - // [code] does not start with a block comment. - return '// @dart=2.9$newline$newline$code'; - } - } -} - -/// The site used to serve pages for the preview tool. -class PreviewSite extends Site - implements AbstractGetHandler, AbstractPostHandler { - /// The path of the CSS page used to style the semantic highlighting within a - /// Dart file. - static const highlightCssPath = '/highlight.css'; - - /// The path of the JS page used to associate highlighting within a Dart file. - static const highlightJsPath = '/highlight.pack.js'; - - /// The path of the Dart logo displayed in the toolbar. - static const dartLogoPath = '/dart_192.png'; - - /// The path of the Material icons font. - static const materialIconsPath = '/MaterialIconsRegular.ttf'; - - /// The path of the Roboto font. - static const robotoFontPath = '/RobotoRegular.ttf'; - - /// The path of the Roboto Mono font. - static const robotoMonoFontPath = '/RobotoMonoRegular.ttf'; - - static const navigationTreePath = '/_preview/navigationTree.json'; - - static const applyHintPath = '/apply-hint'; - - static const applyMigrationPath = '/apply-migration'; - - static const rerunMigrationPath = '/rerun-migration'; - - /// The state of the migration being previewed. - MigrationState? migrationState; - - /// A table mapping the paths of files to the information about the - /// compilation units at those paths. - final Map unitInfoMap = {}; - - // A function provided by DartFix to rerun the migration. - final Future Function() rerunFunction; - - /// Callback function that should be invoked after successfully applying - /// migration. - final void Function() applyHook; - - final Logger? logger; - - final String serviceAuthToken = _makeAuthToken(); - - /// Initialize a newly created site to serve a preview of the results of an - /// NNBD migration. - PreviewSite( - this.migrationState, this.rerunFunction, this.applyHook, this.logger) - : super('Null Safety Migration Preview') { - reset(); - } - - /// Return the information about the migration that will be used to serve up - /// pages. - MigrationInfo? get migrationInfo => migrationState!.migrationInfo; - - /// Return the path mapper used to map paths from the unit infos to the paths - /// being served. - PathMapper? get pathMapper => migrationState!.pathMapper; - - @override - Page createExceptionPage(String message, StackTrace trace) { - // Use createExceptionPageWithPath instead. - throw UnimplementedError(); - } - - /// Return a page used to display an exception that occurred while attempting - /// to render another page. The [path] is the path to the page that was being - /// rendered when the exception was thrown. The [message] and [stackTrace] are - /// those from the exception. - Page createExceptionPageWithPath( - String path, String message, StackTrace stackTrace) { - return ExceptionPage(this, path, message, stackTrace); - } - - /// Return a page used to display an exception that occurred while attempting - /// to render another page. The [path] is the path to the page that was being - /// rendered when the exception was thrown. The [message] and [stackTrace] are - /// those from the exception. - Page createJsonExceptionResponse( - String path, String message, StackTrace stackTrace) { - return ExceptionPage(this, path, message, stackTrace); - } - - Page createUnauthorizedPage(String unauthorizedPath) { - return UnauthorizedPage(this, unauthorizedPath.substring(1)); - } - - @override - Page createUnknownPage(String unknownPath) { - return NotFoundPage(this, unknownPath.substring(1)); - } - - @override - Future handleGetRequest(HttpRequest request) async { - var uri = request.uri; - var path = uri.path; - var decodedPath = pathMapper!.reverseMap(uri); - try { - if (path == highlightCssPath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, HighlightCssPage(this)); - } else if (path == highlightJsPath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, HighlightJSPage(this)); - } else if (path == navigationTreePath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, NavigationTreePage(this)); - } else if (path == dartLogoPath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, DartLogoPage(this)); - } else if (path == materialIconsPath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, MaterialIconsPage(this)); - } else if (path == robotoFontPath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, RobotoPage(this)); - } else if (path == robotoMonoFontPath) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, RobotoMonoPage(this)); - } else if (path == '/' || - decodedPath == migrationInfo!.includedRoot || - decodedPath == - '${migrationInfo!.includedRoot}${pathMapper!.separator}') { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, IndexFilePage(this)); - } - - var unitInfo = unitInfoMap[decodedPath]; - if (unitInfo != null) { - if (uri.queryParameters.containsKey('inline')) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, DartFilePage(this, unitInfo)); - } else if (uri.queryParameters.containsKey('region')) { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, RegionPage(this, unitInfo)); - } else { - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond(request, IndexFilePage(this)); - } - } - // Note: `return await` needed due to - // https://github.com/dart-lang/sdk/issues/39204 - return await respond( - request, createUnknownPage(path), HttpStatus.notFound); - } catch (exception, stackTrace) { - _respondInternalError(request, path, exception, stackTrace); - } - } - - @override - Future handlePostRequest(HttpRequest request) async { - var uri = request.uri; - var path = uri.path; - try { - // All POST requests must be authorized. - if (!_isAuthorized(request)) { - return _respondUnauthorized(request); - } - if (path == applyMigrationPath) { - var navigationTree = - ((await requestBodyJson(request))['navigationTree'] as List) - .map((encoded) => NavigationTreeNode.fromJson(encoded)); - performApply(navigationTree); - - respondOk(request); - return; - } else if (path == rerunMigrationPath) { - await rerunMigration(); - - if (migrationState!.hasErrors) { - return await respondJson( - request, - { - 'success': false, - 'errors': migrationState!.analysisResult!.toJson(), - }, - HttpStatus.ok); - } else { - respondOk(request); - } - return; - } else if (path == applyHintPath) { - final hintAction = HintAction.fromJson(await requestBodyJson(request)); - await performHintAction(hintAction); - respondOk(request); - return; - } else if (uri.queryParameters.containsKey('replacement')) { - await performEdit(uri); - - respondOk(request); - return; - } - } catch (exception, stackTrace) { - _respondInternalError(request, path, exception, stackTrace); - } - } - - /// Perform the migration. - void performApply(Iterable navigationTree) { - if (migrationState!.hasBeenApplied) { - throw StateError( - 'It looks like this migration has already been applied. Try' - ' restarting the migration tool if this is not the case.'); - } - - final edits = migrationState!.listener!.sourceChange.edits; - - // Perform a full check that no files have changed before touching the disk. - for (final fileEdit in edits) { - final file = pathMapper!.provider.getFile(fileEdit.file); - if (!file.path.endsWith('.dart')) { - continue; - } - var unitInfo = unitInfoMap[file.path]; - if (unitInfo == null) { - // No disk content was recorded for this path at the time migration was - // performed. This usually happens because the file is an unreferenced - // part (and therefore it didn't contribute to the migration at all). So - // just skip this file. - continue; - } - var code = file.exists ? file.readAsStringSync() : ''; - if (!unitInfo.hadDiskContent(code)) { - throw StateError('Cannot apply migration. Files on disk do not match' - ' the expected pre-migration state. Press the "rerun from sources"' - ' button and then try again. (Changed file path is ${file.path})'); - } - } - - // Eagerly mark the migration applied. If this throws, we cannot go back. - migrationState!.markApplied(); - IncrementalPlan(migrationInfo, unitInfoMap, pathMapper, edits, - navigationTree, logger) - .apply(); - applyHook(); - } - - /// Perform the edit indicated by the [uri]. - Future performEdit(Uri uri) async { - // - // Update the code on disk. - // - var params = uri.queryParameters; - var path = pathMapper!.reverseMap(uri); - var offset = int.parse(params['offset']!); - var end = int.parse(params['end']!); - var replacement = params['replacement']!; - var file = pathMapper!.provider.getFile(path); - var diskContent = file.readAsStringSync(); - if (!unitInfoMap[path]!.hadDiskContent(diskContent)) { - throw StateError('Cannot perform edit. This file has been changed since' - ' last migration run. Press the "rerun from sources" button and then' - ' try again. (Changed file path is ${file.path})'); - } - final unitInfo = unitInfoMap[path]!; - final diskMapper = unitInfo.diskChangesOffsetMapper; - final diskOffsetStart = diskMapper.map(offset); - final diskOffsetEnd = diskMapper.map(end); - if (diskOffsetStart == null || diskOffsetEnd == null) { - throw StateError('Cannot perform edit. Relevant code has been deleted by' - ' a previous hint action. Rerun the migration and try again.'); - } - unitInfo.handleSourceEdit(SourceEdit(offset, end - offset, replacement)); - migrationState!.needsRerun = true; - var newContent = - diskContent.replaceRange(diskOffsetStart, diskOffsetEnd, replacement); - file.writeAsStringSync(newContent); - unitInfo.diskContent = newContent; - } - - /// Perform the hint edit indicated by the [hintAction]. - Future performHintAction(HintAction hintAction) async { - final node = migrationState!.nodeMapper.nodeForId(hintAction.nodeId)!; - final edits = node.hintActions[hintAction.kind]; - if (edits == null) { - throw StateError('This edit was not available to perform.'); - } - // - // Update the code on disk. - // - var path = node.codeReference!.path; - var file = pathMapper!.provider.getFile(path); - var diskContent = file.readAsStringSync(); - if (!unitInfoMap[path]!.hadDiskContent(diskContent)) { - throw StateError('Cannot perform edit. This file has been changed since' - ' last migration run. Press the "rerun from sources" button and then' - ' try again. (Changed file path is ${file.path})'); - } - final unitInfo = unitInfoMap[path]!; - final diskMapper = unitInfo.diskChangesOffsetMapper; - var newContent = diskContent; - migrationState!.needsRerun = true; - for (final entry in edits.entries) { - final offset = entry.key!; - final edits = entry.value; - final diskOffset = diskMapper.map(offset); - if (diskOffset == null) { - throw StateError( - 'Cannot perform edit. Relevant code has been deleted by' - ' a previous hint action. Rerun the migration and try again.'); - } - final unmappedSourceEdit = edits.toSourceEdit(offset); - final diskSourceEdit = edits.toSourceEdit(diskMapper.map(offset)!); - unitInfo.handleSourceEdit(unmappedSourceEdit); - newContent = diskSourceEdit.apply(newContent); - } - file.writeAsStringSync(newContent); - unitInfo.diskContent = newContent; - } - - Future> requestBodyJson(HttpRequest request) async => - (await request - .map((entry) => entry.map((i) => i.toInt()).toList()) - .transform(Utf8Decoder()) - .transform(JsonDecoder()) - .single) as Map; - - Future rerunMigration() async { - migrationState = await rerunFunction(); - if (!migrationState!.hasErrors) { - reset(); - } - } - - void reset() { - unitInfoMap.clear(); - var unitInfos = migrationInfo!.units!; - var provider = pathMapper!.provider; - for (var unit in unitInfos) { - unitInfoMap[unit.path] = unit; - } - for (var unit in migrationInfo!.unitMap.values) { - if (!unitInfos.contains(unit)) { - if (unit.content == null) { - try { - unit.content = provider.getFile(unit.path!).readAsStringSync(); - } catch (_) { - // If we can't read the content of the file, then skip it. - continue; - } - } - unitInfoMap[unit.path] = unit; - } - } - } - - @override - Future respond(HttpRequest request, Page page, - [int code = HttpStatus.ok]) async { - if (page is PreviewPage && page.requiresAuth) { - if (!_isAuthorized(request)) { - return _respondUnauthorized(request); - } - } - var response = request.response; - response.statusCode = code; - if (page is HighlightCssPage) { - response.headers.contentType = - ContentType('text', 'css', charset: 'utf-8'); - } else if (page is HighlightJSPage) { - response.headers.contentType = - ContentType('application', 'javascript', charset: 'utf-8'); - } else if (page is DartLogoPage) { - response.headers.contentType = ContentType('image', 'png'); - } else if (page is MaterialIconsPage || - page is RobotoPage || - page is RobotoMonoPage) { - response.headers.contentType = ContentType('font', 'ttf'); - } else { - response.headers.contentType = ContentType.html; - } - response.write(await page.generate(request.uri.queryParameters)); - response.close(); - } - - /// Returns whether [request] is an authorized request. - bool _isAuthorized(HttpRequest request) { - var authToken = request.uri.queryParameters['authToken']; - return authToken == serviceAuthToken; - } - - Future _respondInternalError(HttpRequest request, String path, - dynamic exception, StackTrace stackTrace) async { - try { - if (request.headers.contentType!.subType == 'json') { - return await respondJson( - request, - { - 'success': false, - 'exception': exception.toString(), - 'stackTrace': stackTrace.toString(), - }, - HttpStatus.internalServerError); - } - await respond( - request, - createExceptionPageWithPath(path, '$exception', stackTrace), - HttpStatus.internalServerError); - } catch (exception, stackTrace) { - var response = request.response; - response.statusCode = HttpStatus.internalServerError; - response.headers.contentType = ContentType.text; - response.write('$exception\n\n$stackTrace'); - response.close(); - } - } - - /// Responds with a 401 Unauthorized response. - Future _respondUnauthorized(HttpRequest request) async { - var page = createUnauthorizedPage(request.uri.path); - var response = request.response; - response - ..statusCode = HttpStatus.unauthorized - ..headers.contentType = ContentType.html - ..write(await page.generate(request.uri.queryParameters)) - ..close(); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/region_page.dart b/pkg/nnbd_migration/lib/src/preview/region_page.dart deleted file mode 100644 index 2a6ac8fe9629..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/region_page.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:convert' show jsonEncode; - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/region_renderer.dart'; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The HTML that is displayed for a region of code. -class RegionPage extends PreviewPage { - /// The compilation unit information containing the region. - final UnitInfo unitInfo; - - /// Initialize a newly created region page within the given [site]. The - /// [unitInfo] provides the information needed to render the page. - RegionPage(PreviewSite site, this.unitInfo) - : super(site, unitInfo.path!.substring(1)); - - @override - bool get requiresAuth => true; - - @override - void generateBody(Map params) { - throw UnsupportedError('generateBody'); - } - - @override - Future generatePage(Map params) async { - var region = unitInfo.regionAt(int.parse(params['offset']!)); - var renderer = RegionRenderer(region, unitInfo, site.migrationInfo, - site.pathMapper, site.serviceAuthToken); - buf.write(jsonEncode(renderer.render().toJson())); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/roboto_mono_page.dart b/pkg/nnbd_migration/lib/src/preview/roboto_mono_page.dart deleted file mode 100644 index dbcaeb0f2345..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/roboto_mono_page.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that contains the CSS used to style the semantic highlighting -/// within a Dart file. -class RobotoMonoPage extends PreviewPage { - /// Initialize a newly created CSS page within the given [site]. - RobotoMonoPage(PreviewSite site) - : super(site, PreviewSite.robotoMonoFontPath.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - throw UnimplementedError(); - } - - @override - Future generatePage(Map params) async { - buf.write(resources.RobotoMonoRegular_ttf); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/roboto_page.dart b/pkg/nnbd_migration/lib/src/preview/roboto_page.dart deleted file mode 100644 index d7ea886bb882..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/roboto_page.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/resources/resources.g.dart' - as resources; -import 'package:nnbd_migration/src/preview/preview_page.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; - -/// The page that contains the CSS used to style the semantic highlighting -/// within a Dart file. -class RobotoPage extends PreviewPage { - /// Initialize a newly created CSS page within the given [site]. - RobotoPage(PreviewSite site) - : super(site, PreviewSite.robotoFontPath.substring(1)); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - throw UnimplementedError(); - } - - @override - Future generatePage(Map params) async { - buf.write(resources.RobotoRegular_ttf); - } -} diff --git a/pkg/nnbd_migration/lib/src/preview/unauthorized_page.dart b/pkg/nnbd_migration/lib/src/preview/unauthorized_page.dart deleted file mode 100644 index 7b6beb12c903..000000000000 --- a/pkg/nnbd_migration/lib/src/preview/unauthorized_page.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/preview/preview_page.dart'; - -/// The page that is displayed when a request could not be authenticated. -class UnauthorizedPage extends PreviewPage { - /// Initialize a newly created unauthorized page within the given [site]. - /// The [id] is the portion of the path to the page that follows the initial - /// slash ('/'). - UnauthorizedPage(super.site, String super.id); - - @override - bool get requiresAuth => false; - - @override - void generateBody(Map params) { - buf.write(''' -

401 Unauthorized

-

-Request for '$path' is unauthorized. -

-'''); - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/annotation_tracker.dart b/pkg/nnbd_migration/lib/src/utilities/annotation_tracker.dart deleted file mode 100644 index 2b7e6d249177..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/annotation_tracker.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; - -/// A simple class to find all [Annotation]s and track if they all get visited. -class AnnotationTracker extends RecursiveAstVisitor { - final Set _nodes = {}; - - void finalize() { - assert(_nodes.isEmpty, 'Annotation nodes not visited: $_nodes'); - } - - void nodeVisited(Annotation node) { - if (!_nodes.remove(node)) { - throw StateError('Visited unexpected annotation $node'); - } - } - - @override - void visitAnnotation(Annotation node) { - _nodes.add(node); - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/built_value_transformer.dart b/pkg/nnbd_migration/lib/src/utilities/built_value_transformer.dart deleted file mode 100644 index c4ed06fd419f..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/built_value_transformer.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2022, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; - -class BuiltValueTransformer { - static Annotation? findNullableAnnotation(MethodDeclaration node) { - if (node.isGetter && node.isAbstract) { - for (var annotation in node.metadata) { - if (annotation.arguments == null) { - var element = annotation.element; - if (element is PropertyAccessorElement && - element.name == 'nullable') { - if (element.enclosingElement is CompilationUnitElement) { - if (element.library.source.uri.toString() == - 'package:built_value/built_value.dart') { - return annotation; - } - } - } - } - } - } - return null; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart b/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart deleted file mode 100644 index d43b180fcfa8..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/completeness_tracker.dart +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:nnbd_migration/src/utilities/annotation_tracker.dart'; -import 'package:nnbd_migration/src/utilities/named_type_tracker.dart'; -import 'package:nnbd_migration/src/utilities/permissive_mode.dart'; - -/// Mixin that verifies (via assertion checks) that a visitor visits a -/// compilation unit to "completeness" -- currently tracks Annotations and -/// TypeNames. -/// -/// Mixing in this class should have very low overhead when assertions are -/// disabled. -mixin CompletenessTracker on AstVisitor, PermissiveModeVisitor { - AnnotationTracker? _annotationTracker; - NamedTypeTracker? _namedTypeTracker; - - void annotationVisited(Annotation node) { - assert(() { - _annotationTracker!.nodeVisited(node); - return true; - }()); - } - - void namedTypeVisited(NamedType node) { - assert(() { - _namedTypeTracker!.nodeVisited(node); - return true; - }()); - } - - @override - T? visitAnnotation(Annotation node) { - annotationVisited(node); - return super.visitAnnotation(node); - } - - @override - T? visitCompilationUnit(CompilationUnit node) { - T? result; - reportExceptionsIfPermissive(node, () { - assert(() { - assert(_annotationTracker == null); - assert(_namedTypeTracker == null); - _annotationTracker = AnnotationTracker()..visitCompilationUnit(node); - _namedTypeTracker = NamedTypeTracker()..visitCompilationUnit(node); - return true; - }()); - try { - result = super.visitCompilationUnit(node); - assert(() { - _annotationTracker!.finalize(); - _namedTypeTracker!.finalize(); - return true; - }()); - } finally { - _annotationTracker = null; - _namedTypeTracker = null; - } - }); - return result; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart deleted file mode 100644 index a6585e600de6..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:_fe_analyzer_shared/src/scanner/token.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; - -/// Determines if the given [token] is followed by a nullability hint, and if -/// so, returns information about it. Otherwise returns `null`. -HintComment? getPostfixHint(Token token) { - var commentToken = token.next!.precedingComments; - if (commentToken != null) { - HintCommentKind kind; - if (commentToken.lexeme == '/*!*/') { - kind = HintCommentKind.bang; - } else if (commentToken.lexeme == '/*?*/') { - kind = HintCommentKind.question; - } else { - return null; - } - return HintComment( - kind, - token.end, - commentToken.offset, - commentToken.offset + '/*'.length, - commentToken.end - '*/'.length, - commentToken.end, - commentToken.end); - } - return null; -} - -/// Determines if the given [token] is preceded by a hint, and if so, returns -/// information about it. Otherwise returns `null`. -HintComment? getPrefixHint(Token token) { - Token? commentToken = token.precedingComments; - if (commentToken != null) { - while (true) { - var nextComment = commentToken!.next; - if (nextComment == null) break; - commentToken = nextComment; - } - var lexeme = commentToken.lexeme; - if (lexeme.startsWith('/*') && - lexeme.endsWith('*/') && - lexeme.length >= '/*late*/'.length) { - var commentText = - lexeme.substring('/*'.length, lexeme.length - '*/'.length).trim(); - var commentOffset = commentToken.offset; - if (commentText == 'late') { - var lateOffset = commentOffset + commentToken.lexeme.indexOf('late'); - return HintComment( - HintCommentKind.late_, - commentOffset, - commentOffset, - lateOffset, - lateOffset + 'late'.length, - commentToken.end, - token.offset); - } else if (commentText == 'late final') { - var lateOffset = commentOffset + commentToken.lexeme.indexOf('late'); - return HintComment( - HintCommentKind.lateFinal, - commentOffset, - commentOffset, - lateOffset, - lateOffset + 'late final'.length, - commentToken.end, - token.offset); - } else if (commentText == 'required') { - var requiredOffset = - commentOffset + commentToken.lexeme.indexOf('required'); - return HintComment( - HintCommentKind.required, - commentOffset, - commentOffset, - requiredOffset, - requiredOffset + 'required'.length, - commentToken.end, - token.offset); - } - } - } - return null; -} - -/// Information about a hint found in a source file. -class HintComment { - static final _identifierCharRegexp = RegExp('[a-zA-Z0-9_]'); - - /// What kind of hint this is. - final HintCommentKind kind; - - /// The file offset of the first character that should be removed if the hint - /// is to be removed. - final int _removeOffset; - - /// The file offset of the first character of the hint comment itself. - final int _commentOffset; - - /// The file offset of the first character that should be kept if the hint is - /// to be replaced with the hinted text. - final int _keepOffset; - - /// The file offset just beyond the last character that should be kept if the - /// hint is to be replaced with the hinted text. - final int _keepEnd; - - /// The file offset just beyond the last character of the hint comment itself. - final int _commentEnd; - - /// The file offset just beyond the last character that should be removed if - /// the hint is to be removed. - final int _removeEnd; - - HintComment(this.kind, this._removeOffset, this._commentOffset, - this._keepOffset, this._keepEnd, this._commentEnd, this._removeEnd) - : assert(_removeOffset <= _commentOffset), - assert(_commentOffset < _keepOffset), - assert(_keepOffset < _keepEnd), - assert(_keepEnd < _commentEnd), - assert(_commentEnd <= _removeEnd); - - /// Creates the changes necessary to accept the given hint (replace it with - /// its contents and fix up whitespace). - Map> changesToAccept(String? sourceText, - {AtomicEditInfo? info}) { - bool prependSpace = false; - bool appendSpace = false; - var removeOffset = _removeOffset; - var removeEnd = _removeEnd; - if (_isIdentifierCharBeforeOffset(sourceText, removeOffset) && - _isIdentifierCharAtOffset(sourceText!, _keepOffset)) { - if (sourceText[removeOffset] == ' ') { - // We can just keep this space. - removeOffset++; - } else { - prependSpace = true; - } - } - if (_isIdentifierCharBeforeOffset(sourceText, _keepEnd) && - _isIdentifierCharAtOffset(sourceText!, removeEnd)) { - if (sourceText[removeEnd - 1] == ' ') { - // We can just keep this space. - removeEnd--; - } else { - appendSpace = true; - } - } - - return { - removeOffset: [ - if (prependSpace) AtomicEdit.insert(' '), - AtomicEdit.delete(_keepOffset - removeOffset, info: info) - ], - _keepEnd: [AtomicEdit.delete(removeEnd - _keepEnd, info: info)], - if (appendSpace) removeEnd: [AtomicEdit.insert(' ')] - }; - } - - /// Creates the changes necessary to remove the given hint (and fix up - /// whitespace). - Map> changesToRemove(String? sourceText, - {AtomicEditInfo? info}) { - bool appendSpace = false; - var removeOffset = _removeOffset; - if (_isIdentifierCharBeforeOffset(sourceText, removeOffset) && - _isIdentifierCharAtOffset(sourceText!, _removeEnd)) { - if (sourceText[removeOffset] == ' ') { - // We can just keep this space. - removeOffset++; - } else { - appendSpace = true; - } - } - return { - removeOffset: [ - AtomicEdit.delete(_removeEnd - removeOffset, info: info), - if (appendSpace) AtomicEdit.insert(' ') - ] - }; - } - - /// Creates the changes necessary to replace the given hint with a different - /// hint. - Map> changesToReplace( - String? sourceText, String replacement, - {AtomicEditInfo? info}) { - return { - _commentOffset: [ - AtomicEdit.replace(_commentEnd - _commentOffset, replacement, - info: info) - ] - }; - } - - static bool _isIdentifierCharAtOffset(String sourceText, int offset) { - return offset < sourceText.length && - _identifierCharRegexp.hasMatch(sourceText[offset]); - } - - static bool _isIdentifierCharBeforeOffset(String? sourceText, int offset) { - return offset > 0 && - _identifierCharRegexp.hasMatch(sourceText![offset - 1]); - } -} - -/// Types of hint comments -enum HintCommentKind { - /// The comment `/*!*/`, which indicates that the type should not have a `?` - /// appended. - bang, - - /// The comment `/*?*/`, which indicates that the type should have a `?` - /// appended. - question, - - /// The comment `/*late*/`, which indicates that the variable declaration - /// should be late. - late_, - - /// The comment `/*late final*/`, which indicates that the variable - /// declaration should be late and final. - lateFinal, - - /// The comment `/*required*/`, which indicates that the parameter should be - /// required. - required, -} - -extension FormalParameterExtensions on FormalParameter { - // TODO(srawlins): Add this to FormalParameter interface. - Token? get firstTokenAfterCommentAndMetadata { - var parameter = this is DefaultFormalParameter - ? (this as DefaultFormalParameter).parameter - : this as NormalFormalParameter; - if (parameter is FieldFormalParameter) { - if (parameter.keyword != null) { - return parameter.keyword; - } else if (parameter.type != null) { - return parameter.type!.beginToken; - } else { - return parameter.thisKeyword; - } - } else if (parameter is FunctionTypedFormalParameter) { - if (parameter.covariantKeyword != null) { - return parameter.covariantKeyword; - } else if (parameter.returnType != null) { - return parameter.returnType!.beginToken; - } else { - return parameter.name; - } - } else if (parameter is SimpleFormalParameter) { - if (parameter.covariantKeyword != null) { - return parameter.covariantKeyword; - } else if (parameter.keyword != null) { - return parameter.keyword; - } else if (parameter.type != null) { - return parameter.type!.beginToken; - } else { - return parameter.name; - } - } - return null; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/json.dart b/pkg/nnbd_migration/lib/src/utilities/json.dart deleted file mode 100644 index 732b9b0ef1a8..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/json.dart +++ /dev/null @@ -1,30 +0,0 @@ -/// Utilities for handling parsed JSON. -/// -/// It is recommended to import this library with a prefix. -library; - -/// Expects [map] to contain [key], a String key. -/// -/// If [map] has key [key], return the value paired with [key]; otherwise throw -/// a FormatException. -dynamic expectKey(Map map, String key) { - if (map.containsKey(key)) { - return map[key]; - } - throw FormatException( - 'Unexpected `pub outdated` JSON output: missing key ($key)', map); -} - -/// Expects [object] to be of type [T]. -/// -/// If [object] is of type [T], return it; otherwise throw a FormatException -/// with [errorKey] in the message. -T expectType(Object? object, String errorKey) { - if (object is T) { - return object; - } - throw FormatException( - 'Unexpected `pub outdated` JSON output: expected a ' - '$T at "$errorKey", but got a ${object.runtimeType}', - object); -} diff --git a/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart b/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart deleted file mode 100644 index d40da273fece..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/multi_future_tracker.dart +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:async'; - -/// This library helps run parallel thread-like closures asynchronously. -/// Borrowed from dartdoc:src/io_utils.dart. - -Future retryClosure(Future Function() closure, - {Duration baseInterval = const Duration(milliseconds: 200), - double factor = 2, - int retries = 5}) async { - Future handleError(Object _) async { - return await Future.delayed( - baseInterval, - () => retryClosure(closure, - baseInterval: baseInterval * factor, - factor: factor, - retries: retries - 1)); - } - - if (retries > 0) { - return await Future.sync(closure).catchError(handleError); - } else { - return closure(); - } -} - -// TODO(jcollins-g): like SubprocessLauncher, merge with io_utils in dartdoc -// before cut-and-paste gets out of hand. -class MultiFutureTracker { - /// Maximum number of simultaneously incomplete [Future]s. - final int parallel; - - final Set> _trackedFutures = >{}; - - MultiFutureTracker(this.parallel); - - /// Generates a [Future] from the given closure and adds it to the queue, - /// once the queue is sufficiently empty. The returned future completes - /// when the generated [Future] has been added to the queue. - Future addFutureFromClosure(Future Function() closure) async { - assert(_trackedFutures.length <= parallel); - // Can't use _waitUntil because we might not return directly to this - // invocation of addFutureFromClosure. - while (_trackedFutures.length > parallel - 1) { - await Future.any(_trackedFutures); - } - Future future = closure(); - _trackedFutures.add(future); - future.then((f) => _trackedFutures.remove(future)); - } - - /// Generates a [Future] from the given closure and adds it to the queue, - /// once the queue is sufficiently empty. Completes when the generated - /// closure completes. - Future runFutureFromClosure(FutureOr? Function() closure) async { - Completer futureComplete = Completer(); - await addFutureFromClosure(() async { - futureComplete.complete(await closure()); - }); - return futureComplete.future; - } - - /// Wait until all futures added so far have completed. - Future wait() => _waitUntil(0); - - /// Wait until fewer or equal to this many Futures are outstanding. - Future _waitUntil(int max) async { - assert(_trackedFutures.length <= parallel); - while (_trackedFutures.length > max) { - await Future.any(_trackedFutures); - } - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/named_type_tracker.dart b/pkg/nnbd_migration/lib/src/utilities/named_type_tracker.dart deleted file mode 100644 index 8af9550154c7..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/named_type_tracker.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; - -/// A simple class to find all [NamedType]s and track if they all get visited. -class NamedTypeTracker extends RecursiveAstVisitor { - final Set _nodes = {}; - - void finalize() { - assert(_nodes.isEmpty, 'Annotation nodes not visited: $_nodes'); - } - - void nodeVisited(NamedType node) { - if (_isTrueNamedType(node) && !_nodes.remove(node)) { - throw StateError('Visited unexpected type name $node'); - } - } - - @override - void visitNamedType(NamedType node) { - if (_isTrueNamedType(node)) { - _nodes.add(node); - } - super.visitNamedType(node); - } - - bool _isTrueNamedType(NamedType node) { - final parent = node.parent; - if (parent is ConstructorName) { - // We only need to visit C in `new C()`, just `int` in `new C()`. - return parent.type != node; - } - - return true; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart b/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart deleted file mode 100644 index 3e7a8974b73f..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/permissive_mode.dart +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; - -/// Mixin that catches exceptions when visiting an AST recursively, and reports -/// them to an optional listener. This is used to implement the migration -/// tool's "permissive mode". -/// -/// If the [listener] is `null`, exceptions are not caught. -mixin PermissiveModeVisitor on GeneralizingAstVisitor { - NullabilityMigrationListener? get listener; - - /// The file being analyzed. - Source? get source; - - /// Executes [callback]. If [listener] is not `null`, and an exception - /// occurs, the exception is caught and reported to the [listener]. - void reportExceptionsIfPermissive(AstNode node, void Function() callback) { - if (listener != null) { - try { - return callback(); - } catch (exception, stackTrace) { - listener!.reportException(source, node, exception, stackTrace); - } - } else { - callback(); - } - } - - @override - T? visitNode(AstNode node) { - if (listener != null) { - try { - return super.visitNode(node); - } catch (exception, stackTrace) { - listener!.reportException(source, node, exception, stackTrace); - return null; - } - } else { - return super.visitNode(node); - } - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart b/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart deleted file mode 100644 index b4bce92e7bff..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:io' hide File; -import 'dart:math'; - -import 'package:cli_util/cli_logging.dart'; - -/// A facility for drawing a progress bar in the terminal. -/// -/// The bar is instantiated with the total number of "ticks" to be completed, -/// and progress is made by calling [tick]. The bar is drawn across one entire -/// line, like so: -/// -/// [---------- ] -/// -/// The hyphens represent completed progress, and the whitespace represents -/// remaining progress. -/// -/// If there is no terminal, the progress bar will not be drawn. -class ProgressBar { - /// Whether the progress bar should be drawn. - late bool _shouldDrawProgress; - - /// The width of the terminal, in terms of characters. - late int _width; - - final Logger _logger; - - /// The inner width of the terminal, in terms of characters. - /// - /// This represents the number of characters available for drawing progress. - late int _innerWidth; - - final int _totalTickCount; - - int _tickCount = 0; - - ProgressBar(this._logger, this._totalTickCount) { - if (!stdout.hasTerminal) { - _shouldDrawProgress = false; - } else { - _shouldDrawProgress = true; - _width = stdout.terminalColumns; - _innerWidth = stdout.terminalColumns - 2; - _logger.write('[${' ' * _innerWidth}]'); - } - } - - /// Clear the progress bar from the terminal, allowing other logging to be - /// printed. - void clear() { - if (!_shouldDrawProgress) { - return; - } - _logger.write('\r${' ' * _width}\r'); - } - - /// Draw the progress bar as complete, and print two newlines. - void complete() { - if (!_shouldDrawProgress) { - return; - } - _logger.write('\r[${'-' * _innerWidth}]\n\n'); - } - - /// Progress the bar by one tick. - void tick() { - if (!_shouldDrawProgress) { - return; - } - _tickCount++; - var fractionComplete = - max(0, _tickCount * _innerWidth ~/ _totalTickCount - 1); - var remaining = _innerWidth - fractionComplete - 1; - var spinner = AnsiProgress.kAnimationItems[_tickCount % 4]; - _logger.write('\r[${'-' * fractionComplete}$spinner${' ' * remaining}]'); - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart b/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart deleted file mode 100644 index 3971a35e5767..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/resolution_utils.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; - -/// Determines whether a type annotation's position in the AST makes it -/// impossible to be nullable. -bool typeIsNonNullableByContext(TypeAnnotation node) { - var parent = node.parent; - return parent is ExtendsClause || - parent is ImplementsClause || - parent is WithClause || - parent is OnClause || - parent is ClassTypeAlias || - parent is GenericTypeAlias || - parent is ConstructorName; -} - -/// This mixin provides utilities that are useful to visitors implementing -/// resolution-like behaviors. -mixin ResolutionUtils { - List? _objectGetNames; - - TypeProvider get typeProvider; - - /// Determines whether the given getter or method name is declared on - /// `Object` (and is hence valid to call on a nullable type). - bool isDeclaredOnObject(String name) => - (_objectGetNames ??= _computeObjectGetNames()).contains(name); - - List _computeObjectGetNames() { - var result = []; - var objectClass = typeProvider.objectType.element; - for (var accessor in objectClass.accessors) { - assert(accessor.isGetter); - assert(!accessor.name.startsWith('_')); - result.add(accessor.name); - } - for (var method in objectClass.methods) { - assert(!method.name.startsWith('_')); - result.add(method.name); - } - return result; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart b/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart deleted file mode 100644 index c422246ce870..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/scoped_set.dart +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2019, 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. - -/// Tracks scopes of instances to be used by the [EdgeBuilder] for certain -/// analyses. -/// -/// For example, [EdgeBuilder] uses this to find post-dominating usages of -/// locals and parameters to decide where to insert hard edges. -/// -/// Currently does not implement Set because this has properties undesirable -/// of a Set, such as state that means different things at different times, and -/// some of the methods (such as `removeAll()`) may be ambiguous. However, it -/// may be reasonable to do so, carefully, in the future. -class ScopedSet { - /// The scope stack, where the last element is the current scope, and each - /// scope is a list of elements in that scope. - final _scopeStack = >[]; - - /// Get the current scope. Private so as not to expose to clients. - Set get _currentScope => _scopeStack.last; - - /// Add element to the current scope (and not its parent scopes). - void add(T element) { - if (_scopeStack.isNotEmpty) { - _currentScope.add(element); - } - } - - /// Clear each scope in the stack (the stack itself is not affected). - /// - /// This is useful in post-dominator analysis. Upon non-convergent branching, - /// all scopes of potentially post-dominated elements becomes empty. - void clearEachScope() { - for (var scope in _scopeStack) { - scope.clear(); - } - } - - /// Create a scope like [pushScope], and use it to perform some [action] - /// before popping it. - void doScoped( - {List elements = const [], - bool copyCurrent = false, - required void Function() action}) { - pushScope(elements: elements, copyCurrent: copyCurrent); - try { - action(); - } finally { - popScope(); - } - } - - /// Look up if the element is in the scope. - bool isInScope(T element) => - _scopeStack.isNotEmpty && _currentScope.contains(element); - - /// End the current scope. - void popScope() { - assert(_scopeStack.isNotEmpty); - _scopeStack.removeLast(); - } - - /// Begin a new scope, optionally with some known starting [elements], or - /// copying the current scope, as a starting state. - void pushScope({List elements = const [], bool copyCurrent = false}) => - _scopeStack.add({ - ...elements, - if (copyCurrent) ..._currentScope, - }); - - /// Remove element from the current scope and all containing scopes. - void removeFromAllScopes(T t) { - for (var scope in _scopeStack) { - scope.remove(t); - } - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart b/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart deleted file mode 100644 index 5ad801809e1a..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_formatter.dart +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:cli_util/cli_logging.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/utilities/source_edit_diff_output.dart'; - -/// Information about the style in which we display diffs. -abstract class DiffStyle { - /// Creates a DiffStyle suitable for use with a terminal whose ANSI support is - /// indicated by [ansi]. - /// - /// If the terminal supports ANSI, we use a compact diff style with intra-line - /// diffs, where insertions are represented by green reversed text and - /// deletions are represented by red reversed text. - /// - /// If the terminal does not support ANSI, we use a traditional diff style - /// with before and after lines. - factory DiffStyle(Ansi ansi) = _DiffStyleForProduction; - - /// Creates a DiffStyle suitable for unit testing. Instead of using ANSI - /// escape codes for color coding, we use `{+` and `+}` to surround added - /// text, and `{-` and `-}` to surround deleted text. - @visibleForTesting - factory DiffStyle.forTesting(bool compact) = _DiffStyleForTesting; - - DiffStyle._(); - - /// Formats a diff showing the result of applying the given [edits] to the - /// given [origText]. - List formatDiff(String origText, Map> edits); -} - -abstract class DiffStyleImpl extends DiffStyle { - DiffStyleImpl._() : super._(); - - String get bullet; - - String deleted(String text); - - @override - List formatDiff(String origText, Map> edits) { - var output = _createOutput(); - int prevOffset = 0; - for (var offset in edits.keys.toList()..sort()) { - if (offset > prevOffset) { - var text = origText.substring(prevOffset, offset); - var splitText = text.split('\n'); - output.addUnchangedText(splitText.first); - if (splitText.length > 1) { - output.skipUnchangedNewlines(splitText.length - 1); - output.addUnchangedText(splitText.last); - } - prevOffset = offset; - } - for (var edit in edits[offset]!) { - if (edit.length > 0) { - var offset = prevOffset + edit.length; - var text = origText.substring(prevOffset, offset); - var splitText = text.split('\n'); - output.addDeletedText(splitText.first); - for (int i = 1; i < splitText.length; i++) { - output.addDeletedNewline(); - output.addDeletedText(splitText[i]); - } - prevOffset = offset; - } - if (edit.replacement.isNotEmpty) { - var splitText = edit.replacement.split('\n'); - output.addInsertedText(splitText.first); - for (int i = 1; i < splitText.length; i++) { - output.addInsertedNewline(); - output.addInsertedText(splitText[i]); - } - } - } - } - var text = origText.substring(prevOffset); - var splitText = text.split('\n'); - output.addUnchangedText(splitText.first); - output.skipUnchangedNewlines(splitText.length - 1); - if (splitText.length > 1) { - output.addUnchangedText(splitText.last); - } - return output.finish(); - } - - String inserted(String text); - - String lineHeader(int? lineNum, String separator) { - const String emptyLineHeader = ' '; - if (lineNum == null) { - return emptyLineHeader + separator; - } else { - var text = 'line $lineNum'; - if (text.length < emptyLineHeader.length) { - text = text + ' ' * (emptyLineHeader.length - text.length); - } - return text + separator; - } - } - - String unchanged(String text); - - SourceEditDiffOutput _createOutput(); -} - -/// Implementation of [DiffStyle] used in production. -class _DiffStyleForProduction extends DiffStyleImpl { - final Ansi _ansi; - - _DiffStyleForProduction(this._ansi) : super._(); - - @override - String get bullet => '•'; - - @override - String deleted(String text) => - '${_ansi.red}${_ansi.reversed}$text${_ansi.none}'; - - @override - String inserted(String text) => - '${_ansi.green}${_ansi.reversed}$text${_ansi.none}'; - - @override - String unchanged(String text) => text; - - @override - SourceEditDiffOutput _createOutput() => - _ansi.useAnsi ? CompactOutput(this) : TraditionalOutput(this); -} - -/// Implementation of [DiffStyle] used in unit tests. Instead of using ANSI -/// escape sequences and unicode characters, we use characters easy to type into -/// unit tests. -class _DiffStyleForTesting extends DiffStyleImpl { - final bool _compact; - - _DiffStyleForTesting(this._compact) : super._(); - - @override - String get bullet => '*'; - - @override - String deleted(String text) => '{-$text-}'; - - @override - String inserted(String text) => '{+$text+}'; - - @override - String unchanged(String text) => text; - - @override - SourceEditDiffOutput _createOutput() => - _compact ? CompactOutput(this) : TraditionalOutput(this); -} diff --git a/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_output.dart b/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_output.dart deleted file mode 100644 index f9f19b61fb59..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/source_edit_diff_output.dart +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/utilities/source_edit_diff_formatter.dart'; - -/// Implementation of [SourceEditDiffOutput] for generating compact intra-line diffs. -class CompactOutput extends SourceEditDiffOutput { - final DiffStyleImpl style; - - /// Output generated so far. - final List _lines = []; - - /// Accumulator for the current output line. - final _currentLine = StringBuffer(); - - /// True if the string in [_currentLine] indicates changes. - bool _pendingChanges = false; - - /// The current line number, with reference to the original source text. - int _lineNum = 1; - - CompactOutput(this.style) { - _writeLineHeader(true); - } - - void addDeletedNewline() { - _flushLine(); - _lineNum++; - _writeLineHeader(true); - } - - void addDeletedText(String text) { - if (text.isNotEmpty) { - _currentLine.write(style.deleted(text)); - _pendingChanges = true; - } - } - - void addInsertedNewline() { - _flushLine(); - _writeLineHeader(false); - } - - void addInsertedText(String text) { - if (text.isNotEmpty) { - _currentLine.write(style.inserted(text)); - _pendingChanges = true; - } - } - - void addUnchangedText(String text) { - if (text.isNotEmpty) { - _currentLine.write(style.unchanged(text)); - } - } - - @override - List finish() { - assert(!_pendingChanges); - return _lines; - } - - void skipUnchangedNewlines(int count) { - _flushLine(suppressOutput: !_pendingChanges); - _lineNum += count; - _writeLineHeader(true); - } - - void _flushLine({bool suppressOutput = false}) { - if (!suppressOutput) { - _lines.add(_currentLine.toString()); - } - _currentLine.clear(); - _pendingChanges = false; - } - - void _writeLineHeader(bool includeLineNum) { - _currentLine.write( - style.lineHeader(includeLineNum ? _lineNum : null, '${style.bullet} ')); - } -} - -/// Abstract base class capable of generating diff output. -/// -/// Clients shouldn't interact with this class directly; they should use -/// [DiffStyle.formatDiff] instead. -abstract class SourceEditDiffOutput { - void addDeletedNewline(); - - void addDeletedText(String text); - - void addInsertedNewline(); - - void addInsertedText(String text); - - void addUnchangedText(String text); - - List finish(); - - void skipUnchangedNewlines(int count); -} - -/// Implementation of [SourceEditDiffOutput] for generating traditional diffs that show -/// before and after lines. -class TraditionalOutput extends SourceEditDiffOutput { - final DiffStyleImpl style; - - /// Output generated so far. - final List _lines = []; - - /// Accumulator for the "after" lines in the current diff hunk. - final List _afterLines = []; - - /// Accumulator for the current "before" line. - final _currentBeforeLine = StringBuffer(); - - /// Accumulator for the current "after" line. - final _currentAfterLine = StringBuffer(); - - /// True if a hunk is currently in progress that represents changes. - bool _pendingChanges = false; - - /// The current line number, with reference to the original source text. - int _lineNum = 1; - - TraditionalOutput(this.style); - - void addDeletedNewline() { - _flushBeforeLine(); - _lineNum++; - if (_currentAfterLine.isEmpty) { - // After text is at a line break, so we can output pending changes - _outputPendingChanges(); - } - } - - void addDeletedText(String text) { - if (text.isNotEmpty) { - _currentBeforeLine.write(style.deleted(text)); - _pendingChanges = true; - } - } - - void addInsertedNewline() { - if (_currentBeforeLine.isEmpty) { - // Before text is at a line break, so we are adding a whole line - _flushAfterLine(includeLineNum: true); - _outputPendingChanges(); - } else { - _flushAfterLine(); - } - } - - void addInsertedText(String text) { - if (text.isNotEmpty) { - _currentAfterLine.write(style.inserted(text)); - _pendingChanges = true; - } - } - - void addUnchangedText(String text) { - if (text.isNotEmpty) { - var styledText = style.unchanged(text); - _currentBeforeLine.write(styledText); - _currentAfterLine.write(styledText); - } - } - - @override - List finish() { - assert(!_pendingChanges); - assert(_afterLines.isEmpty); - return _lines; - } - - void skipUnchangedNewlines(int count) { - _flushBeforeLine(suppressOutput: !_pendingChanges); - _flushAfterLine(suppressOutput: !_pendingChanges); - _outputPendingChanges(); - _lineNum += count; - } - - void _flushAfterLine( - {bool suppressOutput = false, bool includeLineNum = false}) { - if (!suppressOutput) { - _afterLines.add(style.lineHeader(includeLineNum ? _lineNum : null, '+') + - _currentAfterLine.toString()); - } - _currentAfterLine.clear(); - } - - void _flushBeforeLine({bool suppressOutput = false}) { - if (!suppressOutput) { - _lines - .add(style.lineHeader(_lineNum, '-') + _currentBeforeLine.toString()); - } - _currentBeforeLine.clear(); - } - - void _outputPendingChanges() { - _lines.addAll(_afterLines); - _afterLines.clear(); - _pendingChanges = false; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/where_not_null_transformer.dart b/pkg/nnbd_migration/lib/src/utilities/where_not_null_transformer.dart deleted file mode 100644 index cc270312619c..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/where_not_null_transformer.dart +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2022, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; - -/// Information about a method call that we might want to transform into a call -/// to `whereNotNull` counterpart. See [WhereNotNullTransformer] for more -/// information. -class WhereNotNullTransformationInfo { - /// AST node of the method invocation. - final MethodInvocation methodInvocation; - - /// AST node of the argument of the method invocation. - final Expression argument; - - /// Original name of the method being called, prior to transformation. - final String originalName; - - WhereNotNullTransformationInfo( - this.methodInvocation, this.argument, this.originalName); - - /// New method to call, after transformation. - String get replacementName => 'whereNotNull'; -} - -/// Methods to assist in transforming calls to the `Iterable` method `where` -/// into calls to the `package:collection` method `whereNotNull`, where -/// possible. -/// -/// An example of the kind of code that can be transformed is: -/// -/// Iterable f(List x) => x.where((y) => y != null); -/// -/// We transform this into: -/// -/// Iterable f(List x) => x.whereNotNull(); -/// -/// Without this transformation, the migrated result would have been: -/// -/// Iterable f(List x) => x.where((y) => y != null); -/// -/// Which would have placed an otherwise unnecessary requirement on callers to -/// handle `null` values in the resulting iterable. -class WhereNotNullTransformer { - final TypeProvider _typeProvider; - - final TypeSystem _typeSystem; - - WhereNotNullTransformer(this._typeProvider, this._typeSystem); - - /// Transforms the [DecoratedType] of an invocation of `.where` to the - /// [DecoratedType] of the corresponding invocation of `.whereNotNull` that - /// will replace it. - /// - /// The transformation is that the type argument to `Iterable` is made - /// non-nullable, so that nullability doesn't unnecessarily propagate to other - /// parts of the code. - DecoratedType transformDecoratedInvocationType( - DecoratedType decoratedType, NullabilityGraph graph) { - var type = decoratedType.type; - var typeArguments = decoratedType.typeArguments; - if (type is InterfaceType && - type.element == _typeProvider.iterableElement && - typeArguments.length == 1) { - return DecoratedType(type, decoratedType.node, - typeArguments: [typeArguments.single?.withNode(graph.never)]); - } - return decoratedType; - } - - /// Transforms the post-migration type of an invocation of `.where` to the - /// type of the corresponding invocation of `.whereNotNull` that will replace - /// it. - /// - /// The transformation is that the type argument to `Iterable` is made - /// non-nullable, so that we don't try to introduce any unnecessary null - /// checks or type casts in other parts of the code. - DartType transformPostMigrationInvocationType(DartType type) { - if (type is InterfaceType && - type.element == _typeProvider.iterableElement) { - var typeArguments = type.typeArguments; - if (typeArguments.length == 1) { - return InterfaceTypeImpl( - element: type.element, - typeArguments: [_typeSystem.promoteToNonNull(typeArguments.single)], - nullabilitySuffix: type.nullabilitySuffix); - } - } - return type; - } - - /// If [node] is a call that can be transformed, returns information about the - /// transformable call; otherwise returns `null`. - WhereNotNullTransformationInfo? tryTransformMethodInvocation(AstNode? node) { - if (node is MethodInvocation) { - if (!_isTransformableMethod(node.methodName.staticElement)) return null; - var arguments = node.argumentList.arguments; - if (arguments.length != 1) return null; - var argument = arguments[0]; - if (!_isClosureCheckingNotNull(argument)) return null; - return WhereNotNullTransformationInfo( - node, argument, node.methodName.name); - } - return null; - } - - /// Checks whether [expression] is of the form `(x) => x != null`. - bool _isClosureCheckingNotNull(Expression expression) { - if (expression is! FunctionExpression) return false; - if (expression.typeParameters != null) return false; - var parameters = expression.parameters!.parameters; - if (parameters.length != 1) return false; - var parameter = parameters[0]; - if (parameter.isNamed) return false; - var body = expression.body; - if (body is! ExpressionFunctionBody) return false; - var returnedExpression = body.expression; - if (returnedExpression is! BinaryExpression) return false; - if (returnedExpression.operator.type != TokenType.BANG_EQ) return false; - var lhs = returnedExpression.leftOperand; - if (lhs is! SimpleIdentifier) return false; - if (lhs.staticElement != parameter.declaredElement) return false; - if (returnedExpression.rightOperand is! NullLiteral) return false; - return true; - } - - /// Determines if [element] is a declaration of `.where` for which calls can - /// be transformed. - bool _isTransformableMethod(Element? element) { - if (element is MethodElement) { - if (element.isStatic) return false; - if (element.name != 'where') return false; - var enclosingElement = element.declaration.enclosingElement; - if (enclosingElement is ClassElement) { - // If the class is `Iterable` or a subtype of it, we consider the user - // to be calling a transformable method. - return _typeSystem.isSubtypeOf( - enclosingElement.thisType, _typeProvider.iterableDynamicType); - } - } - return false; - } -} diff --git a/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart b/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart deleted file mode 100644 index d276ad5bf563..000000000000 --- a/pkg/nnbd_migration/lib/src/utilities/where_or_null_transformer.dart +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; - -/// Information about a method call that we might want to transform into its -/// "OrNull" counterpart. See [WhereOrNullTransformer] for more information. -class WhereOrNullTransformationInfo { - /// AST node of the method invocation. - final MethodInvocation methodInvocation; - - /// AST node of the "orElse" argument of the method invocation. - final NamedExpression orElseArgument; - - /// Original name of the method being called, prior to transformation. - final String originalName; - - /// New method to call, after transformation. - final String replacementName; - - WhereOrNullTransformationInfo(this.methodInvocation, this.orElseArgument, - this.originalName, this.replacementName); -} - -/// Methods to assist in transforming calls to the `Iterable` methods -/// `firstWhere`, `lastWhere`, and `singleWhere` into calls to the -/// `package:collection` methods `firstWhereOrNull`, `lastWhereOrNull`, or -/// `singleWhereOrNull`, where possible. -/// -/// An example of the kind of code that can be transformed is: -/// -/// int firstEven(Iterable x) -/// => x.firstWhere((x) => x.isEven, orElse: () => null); -/// -/// We transform this into: -/// -/// int firstEven(Iterable x) -/// => x.firstWhereOrNull((x) => x.isEven); -/// -/// Without this transformation, the migrated result would have been: -/// -/// int firstEven(Iterable x) -/// => x.firstWhere((x) => x.isEven, orElse: () => null); -/// -/// Which would have placed an otherwise unnecessary nullability requirement on -/// the type argument of the type of `x`. -class WhereOrNullTransformer { - static const _replacementNames = { - 'firstWhere': 'firstWhereOrNull', - 'lastWhere': 'lastWhereOrNull', - 'singleWhere': 'singleWhereOrNull' - }; - - final TypeProvider _typeProvider; - - final TypeSystem _typeSystem; - - WhereOrNullTransformer(this._typeProvider, this._typeSystem); - - /// If [expression] is the expression part of the `orElse` argument of a call - /// that can be transformed, returns information about the transformable call; - /// otherwise returns `null`. - WhereOrNullTransformationInfo? tryTransformOrElseArgument( - Expression? expression) { - var transformationInfo = - _tryTransformMethodInvocation(expression?.parent?.parent?.parent); - if (transformationInfo != null && - identical(transformationInfo.orElseArgument.expression, expression)) { - return transformationInfo; - } else { - return null; - } - } - - /// Searches [argumentList] for a named argument with the name "orElse". If - /// such an argument is found, and no other named arguments are found, it is - /// returned; otherwise `null` is returned. - NamedExpression? _findOrElseArgument(ArgumentList argumentList) { - NamedExpression? orElseArgument; - for (var argument in argumentList.arguments) { - if (argument is NamedExpression) { - if (argument.name.label.name == 'orElse') { - orElseArgument = argument; - } else { - // The presence of an unexpected named argument means the user is - // calling their own override of the method, and presumably they are - // using this named argument to trigger a special behavior of their - // override. So don't try to replace it. - return null; - } - } - } - return orElseArgument; - } - - /// Determines if [element] is a method that can be transformed; if it can, - /// the name of the replacement is returned; otherwise, `null` is returned. - String? _getTransformableMethodReplacementName(Element? element) { - if (element is MethodElement) { - if (element.isStatic) return null; - var replacementName = _replacementNames[element.name]; - if (replacementName == null) return null; - var enclosingElement = element.declaration.enclosingElement; - if (enclosingElement is ClassElement) { - // If the class is `Iterable` or a subtype of it, we consider the user - // to be calling a transformable method. - if (_typeSystem.isSubtypeOf( - enclosingElement.thisType, _typeProvider.iterableDynamicType)) { - return replacementName; - } - } - } - return null; - } - - /// Checks whether [expression] is of the form `() => null`. - bool _isClosureReturningNull(Expression expression) { - if (expression is FunctionExpression) { - if (expression.typeParameters != null) return false; - if (expression.parameters!.parameters.isNotEmpty) return false; - var body = expression.body; - if (body is ExpressionFunctionBody) { - if (body.expression is NullLiteral) return true; - } - } - return false; - } - - /// If [node] is a call that can be transformed, returns information about the - /// transformable call; otherwise returns `null`. - WhereOrNullTransformationInfo? _tryTransformMethodInvocation(AstNode? node) { - if (node is MethodInvocation) { - var replacementName = - _getTransformableMethodReplacementName(node.methodName.staticElement); - if (replacementName == null) return null; - var orElseArgument = _findOrElseArgument(node.argumentList); - if (orElseArgument == null) return null; - if (!_isClosureReturningNull(orElseArgument.expression)) return null; - return WhereOrNullTransformationInfo( - node, orElseArgument, node.methodName.name, replacementName); - } - return null; - } -} diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart deleted file mode 100644 index cd0ac04e2f91..000000000000 --- a/pkg/nnbd_migration/lib/src/variables.dart +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:math' as math; - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/member.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/generated/element_type_provider.dart'; -import 'package:analyzer/src/generated/utilities_dart.dart'; -import 'package:meta/meta.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/already_migrated_code_decorator.dart'; -import 'package:nnbd_migration/src/conditional_discard.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/expression_checks.dart'; -import 'package:nnbd_migration/src/fix_builder.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:nnbd_migration/src/utilities/hint_utils.dart'; - -/// Data structure used by [Variables.spanForUniqueIdentifier] to return an -/// offset/end pair. -class OffsetEndPair { - final int offset; - - final int end; - - OffsetEndPair(this.offset, this.end); - - @override - String toString() => '$offset-$end'; -} - -class Variables { - final NullabilityGraph _graph; - - final TypeProvider _typeProvider; - - final _conditionalDiscards = >{}; - - final _decoratedElementTypes = {}; - - final _decoratedDirectSupertypes = - >{}; - - final _decoratedTypeAnnotations = >{}; - - final _expressionChecks = >{}; - - final _lateHints = >{}; - - final _nullCheckHints = >{}; - - final _nullabilityHints = >{}; - - final _requiredHints = >{}; - - final _unnecessaryCasts = >{}; - - final AlreadyMigratedCodeDecorator _alreadyMigratedCodeDecorator; - - final NullabilityMigrationInstrumentation? instrumentation; - - Variables(this._graph, this._typeProvider, {this.instrumentation}) - : _alreadyMigratedCodeDecorator = - AlreadyMigratedCodeDecorator(_graph, _typeProvider); - - /// Given a [class_], gets the decorated type information for the superclasses - /// it directly implements/extends/etc. - Map decoratedDirectSupertypes( - InterfaceElement class_) { - return _decoratedDirectSupertypes[class_] ??= - _decorateDirectSupertypes(class_); - } - - /// Retrieves the [DecoratedType] associated with the static type of the given - /// [element]. - /// - /// If no decorated type is found for the given element, and the element is in - /// a library that's not being migrated, a decorated type is synthesized using - /// [DecoratedType.forElement]. - DecoratedType decoratedElementType(Element element) { - assert(element is! TypeParameterElement, - 'Use decoratedTypeParameterBound instead'); - return _decoratedElementTypes[element] ??= - _createDecoratedElementType(element); - } - - /// Gets the [DecoratedType] associated with the given [typeAnnotation]. - DecoratedType decoratedTypeAnnotation( - Source? source, TypeAnnotation typeAnnotation) { - var annotationsInSource = _decoratedTypeAnnotations[source]; - if (annotationsInSource == null) { - throw StateError('No declared type annotations in ${source!.fullName}; ' - 'expected one for ${typeAnnotation.toSource()} ' - '(offset ${typeAnnotation.offset})'); - } - DecoratedType? decoratedTypeAnnotation = annotationsInSource[ - uniqueIdentifierForSpan(typeAnnotation.offset, typeAnnotation.end)]; - if (decoratedTypeAnnotation == null) { - throw StateError('Missing declared type annotation' - ' in ${source!.fullName}; for ${typeAnnotation.toSource()}'); - } - return decoratedTypeAnnotation; - } - - /// Retrieves the decorated bound of the given [typeParameter]. - /// - /// Note: the optional argument [allowNullUnparentedBounds] is intended for - /// the FixBuilder stage only, to allow it to cope with the situation where - /// a type parameter element with a null parent doesn't have a decorated type - /// associated with it. This can arise because synthetic type parameter - /// elements get created as a result of type system operations during - /// resolution, and fortunately it isn't a problem during the FixBuilder stage - /// because at that point the types we are dealing with are all - /// post-migration types, so their bounds already reflect the correct - /// nullabilities. - DecoratedType? decoratedTypeParameterBound(TypeParameterElement typeParameter, - {bool allowNullUnparentedBounds = false}) { - var enclosingElement = typeParameter.enclosingElement; - var decoratedType = - DecoratedTypeParameterBounds.current!.get(typeParameter); - if (enclosingElement == null) { - if (decoratedType == null && !allowNullUnparentedBounds) { - throw StateError( - 'A decorated type for the bound of $typeParameter should ' - 'have been stored by the NodeBuilder via recordTypeParameterBound'); - } - } else { - if (decoratedType == null) { - if (_graph.isBeingMigrated(typeParameter.library!.source)) { - throw StateError( - 'A decorated type for the bound of $typeParameter should ' - 'have been stored by the NodeBuilder via ' - 'recordTypeParameterBound'); - } - var target = NullabilityNodeTarget.typeParameterBound(typeParameter); - decoratedType = _alreadyMigratedCodeDecorator.decorate( - typeParameter.preMigrationBound ?? DynamicTypeImpl.instance, - typeParameter, - target); - instrumentation?.externalDecoratedTypeParameterBound( - typeParameter, decoratedType); - DecoratedTypeParameterBounds.current!.put(typeParameter, decoratedType); - } - } - return decoratedType; - } - - /// Retrieves the [ExpressionChecks] object corresponding to the given - /// [expression], if one exists; otherwise null. - ExpressionChecks? expressionChecks(Source? source, Expression expression) { - return (_expressionChecks[source] ?? - {})[uniqueIdentifierForSpan(expression.offset, expression.end)]; - } - - ConditionalDiscard? getConditionalDiscard(Source? source, AstNode node) => - (_conditionalDiscards[source] ?? {})[node.offset]; - - /// If the given [node] is preceded by a `/*late*/` hint, returns the - /// HintComment for it; otherwise returns `null`. See [recordLateHint]. - HintComment? getLateHint(Source? source, VariableDeclarationList node) { - return (_lateHints[source] ?? {})[node.offset]; - } - - /// If the given [node] is followed by a `/*?*/` or /*!*/ hint, returns the - /// HintComment for it; otherwise returns `null`. See - /// [recordNullabilityHint]. - HintComment? getNullabilityHint(Source? source, AstNode node) { - assert(node is TypeAnnotation || - node is FunctionTypedFormalParameter || - (node is FieldFormalParameter && node.parameters != null)); - return (_nullabilityHints[source] ?? - {})[uniqueIdentifierForSpan(node.offset, node.end)]; - } - - /// If the given [expression] is followed by a null check hint (`/*!*/`), - /// returns the HintComment for it; otherwise returns `null`. See - /// [recordNullCheckHint]. - HintComment? getNullCheckHint(Source? source, Expression expression) { - return (_nullCheckHints[source] ?? - {})[uniqueIdentifierForSpan(expression.offset, expression.end)]; - } - - /// If the given [node] is preceded by a `/*required*/` hint, returns the - /// HintComment for it; otherwise returns `null`. See [recordRequiredHint]. - HintComment? getRequiredHint(Source? source, FormalParameter node) { - return (_requiredHints[source] ?? {})[node.offset]; - } - - /// Records conditional discard information for the given AST node (which is - /// an `if` statement or a conditional (`?:`) expression). - void recordConditionalDiscard( - Source? source, AstNode node, ConditionalDiscard conditionalDiscard) { - (_conditionalDiscards[source] ??= {})[node.offset] = conditionalDiscard; - } - - /// Associates a [interface] with decorated type information for the superclasses - /// it directly implements/extends/etc. - void recordDecoratedDirectSupertypes(InterfaceElement interface, - Map decoratedDirectSupertypes) { - _decoratedDirectSupertypes[interface] = decoratedDirectSupertypes; - } - - /// Associates decorated type information with the given [element]. - void recordDecoratedElementType(Element? element, DecoratedType? type, - {bool soft = false}) { - assert(() { - assert(element is! TypeParameterElement, - 'Use recordDecoratedTypeParameterBound instead'); - var library = element!.library; - if (library == null) { - // No problem; the element is probably a parameter of a function type - // expressed using new-style Function syntax. - } else { - assert(_graph.isBeingMigrated(library.source)); - } - return true; - }()); - if (soft && _decoratedElementTypes.containsKey(element)) { - return; - } - _decoratedElementTypes[element] = type; - } - - /// Associates decorated type information with the given expression [node]. - void recordDecoratedExpressionType(Expression node, DecoratedType? type) {} - - /// Associates decorated type information with the given [type] node. - void recordDecoratedTypeAnnotation( - Source? source, TypeAnnotation node, DecoratedType type) { - instrumentation?.explicitTypeNullability(source, node, type.node); - var id = uniqueIdentifierForSpan(node.offset, node.end); - (_decoratedTypeAnnotations[source] ??= {})[id] = type; - } - - /// Associates a set of nullability checks with the given expression [node]. - void recordExpressionChecks( - Source? source, Expression expression, ExpressionChecksOrigin origin) { - (_expressionChecks[source] ??= - {})[uniqueIdentifierForSpan(expression.offset, expression.end)] = - origin.checks; - } - - /// Records that the given [node] was preceded by a `/*late*/` hint. - void recordLateHint( - Source? source, VariableDeclarationList node, HintComment hint) { - (_lateHints[source] ??= {})[node.offset] = hint; - } - - /// Records that the given [node] was followed by a `/*?*/` or `/*!*/` hint. - void recordNullabilityHint( - Source? source, AstNode node, HintComment hintComment) { - assert(node is TypeAnnotation || - node is FunctionTypedFormalParameter || - (node is FieldFormalParameter && node.parameters != null)); - (_nullabilityHints[source] ??= - {})[uniqueIdentifierForSpan(node.offset, node.end)] = hintComment; - } - - /// Records that the given [expression] is followed by a null check hint - /// (`/*!*/`), for later recall by [hasNullCheckHint]. - void recordNullCheckHint( - Source? source, Expression expression, HintComment hintComment) { - (_nullCheckHints[source] ??= - {})[uniqueIdentifierForSpan(expression.offset, expression.end)] = - hintComment; - } - - /// Records that the given [node] was preceded by a `/*required*/` hint. - void recordRequiredHint( - Source? source, FormalParameter node, HintComment hint) { - (_requiredHints[source] ??= {})[node.offset] = hint; - } - - /// Records the fact that prior to migration, an unnecessary cast existed at - /// [node]. - void recordUnnecessaryCast(Source? source, AsExpression node) { - bool newlyAdded = (_unnecessaryCasts[source] ??= {}) - .add(uniqueIdentifierForSpan(node.offset, node.end)); - assert(newlyAdded); - } - - /// Convert this decorated type into the [DartType] that it will represent - /// after the code has been migrated. - /// - /// This method should be used after nullability propagation; it makes use of - /// the nullabilities associated with nullability nodes to determine which - /// types should be nullable and which types should not. - DartType toFinalType(DecoratedType decoratedType) { - var type = decoratedType.type!; - if (type is VoidType || type is DynamicType) return type; - if (type is NeverType) { - if (decoratedType.node.isNullable) { - return (_typeProvider.nullType as TypeImpl) - .withNullability(NullabilitySuffix.none); - } else { - return NeverTypeImpl.instance; - } - } else if (type.isDartCoreNull) { - return (_typeProvider.nullType as TypeImpl) - .withNullability(NullabilitySuffix.none); - } - var nullabilitySuffix = decoratedType.node.isNullable - ? NullabilitySuffix.question - : NullabilitySuffix.none; - if (type is FunctionType) { - var parameters = []; - for (int i = 0; i < type.parameters.length; i++) { - var origParameter = type.parameters[i]; - ParameterKind parameterKind; - DecoratedType? parameterType; - var name = origParameter.name; - if (origParameter.isNamed) { - // TODO(paulberry): infer ParameterKind.NAMED_REQUIRED when - // appropriate. See https://github.com/dart-lang/sdk/issues/38596. - parameterKind = ParameterKind.NAMED; - parameterType = decoratedType.namedParameters![name]; - } else { - parameterKind = origParameter.isOptional - ? ParameterKind.POSITIONAL - : ParameterKind.REQUIRED; - parameterType = decoratedType.positionalParameters![i]; - } - parameters.add(ParameterElementImpl.synthetic( - name, toFinalType(parameterType!), parameterKind)); - } - return FunctionTypeImpl( - typeFormals: type.typeFormals, - parameters: parameters, - returnType: toFinalType(decoratedType.returnType!), - nullabilitySuffix: nullabilitySuffix, - ); - } else if (type is InterfaceType) { - return InterfaceTypeImpl( - element: type.element, - typeArguments: [ - for (var arg in decoratedType.typeArguments) toFinalType(arg!) - ], - nullabilitySuffix: nullabilitySuffix, - ); - } else if (type is TypeParameterType) { - return TypeParameterTypeImpl( - element: type.element, - nullabilitySuffix: nullabilitySuffix, - ); - } else { - // The above cases should cover all possible types. On the off chance - // they don't, fall back on returning DecoratedType.type. - assert(false, 'Unexpected type (${type.runtimeType})'); - return type; - } - } - - /// Queries whether, prior to migration, an unnecessary cast existed at - /// [node]. - bool wasUnnecessaryCast(Source? source, AsExpression node) => - (_unnecessaryCasts[source] ?? const {}) - .contains(uniqueIdentifierForSpan(node.offset, node.end)); - - /// Creates a decorated type for the given [element], which should come from - /// an already-migrated library (or the SDK). - DecoratedType _createDecoratedElementType(Element element) { - if (_graph.isBeingMigrated(element.library!.source) && - !_isLoadLibraryElement(element)) { - Object? description; - if (ElementTypeProvider.current is MigrationResolutionHooksImpl) { - // Don't attempt to call toString() on element, or we will overflow. - description = element.location; - } else { - description = element; - } - throw StateError( - 'A decorated type for $description should have been stored ' - 'by the NodeBuilder via recordDecoratedElementType'); - } - - DecoratedType decoratedType; - if (element is Member) { - assert(element.isLegacy); - element = element.declaration; - } - - if (element is TypeAliasElement) { - // For `typedef F = Function(T)`, get the `function` which is (in this - // case) `Function(T)`. Without this we would get `Function(T)` which - // is incorrect. This is a known issue with `.type` on typedefs in the - // analyzer. - element = element.aliasedElement!; - } - - var target = NullabilityNodeTarget.element(element); - if (element is FunctionTypedElement) { - decoratedType = _alreadyMigratedCodeDecorator.decorate( - element.preMigrationType, element, target); - } else if (element is VariableElement) { - decoratedType = _alreadyMigratedCodeDecorator.decorate( - element.preMigrationType, element, target); - } else if (element is ExtensionElement) { - decoratedType = _alreadyMigratedCodeDecorator.decorate( - element.preMigrationExtendedType, element, target); - } else { - // TODO(paulberry) - throw UnimplementedError('Decorating ${element.runtimeType}'); - } - instrumentation?.externalDecoratedType(element, decoratedType); - return decoratedType; - } - - /// Creates an entry [_decoratedDirectSupertypes] for an already-migrated - /// class. - Map _decorateDirectSupertypes( - InterfaceElement class_) { - var result = {}; - for (var decoratedSupertype - in _alreadyMigratedCodeDecorator.getImmediateSupertypes(class_)) { - var class_ = (decoratedSupertype.type as InterfaceType).element; - result[class_] = decoratedSupertype; - } - return result; - } - - bool _isLoadLibraryElement(Element element) => - element.isSynthetic && - element is FunctionElement && - element.enclosingElement is LibraryElement && - element.name == 'loadLibrary'; - - /// Inverts the logic of [uniqueIdentifierForSpan], producing an (offset, end) - /// pair. - static OffsetEndPair spanForUniqueIdentifier(int span) { - // The formula for uniqueIdentifierForSpan was: - // span = end*(end + 1) / 2 + offset - // In other words, all encodings with the same `end` value are consecutive. - // So we just have to figure out the `end` value for this `span`, then - // use [uniqueIdentifierForSpan] to find the first encoding with this `end` - // value, and subtract to find the offset. - // - // To find the `end` value, we assume offset = 0 and solve for `end` using - // the quadratic formula: - // span = end*(end + 1) / 2 - // end^2 + end - 2*span = 0 - // end = -1 +/- sqrt(1 + 8*span) - // We can resolve the `+/-` to `+` (since the result we seek can't be - // negative), so that yields: - // end = sqrt(1 + 8*span) - 1 - int end = (math.sqrt(1 + 8.0 * span) - 1).floor(); - assert(end >= 0); - - // There's a slight chance of numerical instabilities in `sqrt` leading to - // a result for `end` that's off by 1, so we loop to find the correct - // result: - while (true) { - // Compute the first `span` value corresponding to this `end` value. - int firstSpanForThisEnd = uniqueIdentifierForSpan(0, end); - - // Offsets are encoded consecutively so we can find the offset by - // subtracting: - int offset = span - firstSpanForThisEnd; - - if (offset < 0) { - // Oops, `end` must have been too large. Decrement and try again. - assert(end > 0); - --end; - } else if (offset > end) { - // Oops, `end` must have been too small. Increment and try again. - ++end; - } else { - return OffsetEndPair(offset, end); - } - } - } - - /// Combine the given [offset] and [end] into a unique integer that depends - /// on both of them, taking advantage of the fact that `0 <= offset <= end`. - @visibleForTesting - static int uniqueIdentifierForSpan(int offset, int end) { - assert(0 <= offset && offset <= end); - // Our encoding is based on the observation that if you make a graph of the - // set of all possible (offset, end) pairs, marking those that satisfy - // `0 <= offset <= end` with an `x`, you get a triangle shape: - // - // offset - // +--------> - // |x - // |xx - // end |xxx - // |xxxx - // V - // - // If we assign integers to the `x`s in the order they appear in this graph, - // then the rows start with numbers 0, 1, 3, 6, 10, etc. This can be - // computed from `end` as `end*(end + 1)/2`. We use `~/` for integer - // division. - return end * (end + 1) ~/ 2 + offset; - } -} - -extension on TypeParameterElement { - DartType? get preMigrationBound { - var previousElementTypeProvider = ElementTypeProvider.current; - try { - ElementTypeProvider.current = const ElementTypeProvider(); - return bound; - } finally { - ElementTypeProvider.current = previousElementTypeProvider; - } - } -} - -extension on FunctionTypedElement { - FunctionType get preMigrationType { - var previousElementTypeProvider = ElementTypeProvider.current; - try { - ElementTypeProvider.current = const ElementTypeProvider(); - return type; - } finally { - ElementTypeProvider.current = previousElementTypeProvider; - } - } -} - -extension on VariableElement { - DartType get preMigrationType { - var previousElementTypeProvider = ElementTypeProvider.current; - try { - ElementTypeProvider.current = const ElementTypeProvider(); - return type; - } finally { - ElementTypeProvider.current = previousElementTypeProvider; - } - } -} - -extension on ExtensionElement { - DartType get preMigrationExtendedType { - var previousElementTypeProvider = ElementTypeProvider.current; - try { - ElementTypeProvider.current = const ElementTypeProvider(); - return extendedType; - } finally { - ElementTypeProvider.current = previousElementTypeProvider; - } - } -} diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml deleted file mode 100644 index 5fe1427da6e9..000000000000 --- a/pkg/nnbd_migration/pubspec.yaml +++ /dev/null @@ -1,30 +0,0 @@ -name: nnbd_migration - -# This package is not intended for consumption on pub.dev. DO NOT publish. -publish_to: none - -environment: - sdk: ^3.0.0 - -# Use 'any' constraints here; we get our versions from the DEPS file. -dependencies: - _fe_analyzer_shared: any - analyzer_plugin: any - analyzer: any - args: any - cli_util: any - collection: any - crypto: any - meta: any - path: any - pub_semver: any - source_span: any - yaml: any - -# Use 'any' constraints here; we get our versions from the DEPS file. -dev_dependencies: - analyzer_utilities: any - http: any - lints: any - test_reflective_loader: any - test: any diff --git a/pkg/nnbd_migration/test/abstract_context.dart b/pkg/nnbd_migration/test/abstract_context.dart deleted file mode 100644 index 489be641a50c..000000000000 --- a/pkg/nnbd_migration/test/abstract_context.dart +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'dart:convert'; - -import 'package:analyzer/dart/analysis/analysis_context.dart'; -import 'package:analyzer/dart/analysis/session.dart'; -import 'package:analyzer/file_system/file_system.dart'; -import 'package:analyzer/file_system/overlay_file_system.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart'; -import 'package:analyzer/src/dart/analysis/driver.dart'; -import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; -import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine; -import 'package:analyzer/src/test_utilities/mock_sdk.dart'; -import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; - -/// TODO(paulberry): this logic is duplicated from other packages. Find a way -/// share it, or avoid relying on it. -class AbstractContextTest with ResourceProviderMixin { - OverlayResourceProvider? overlayResourceProvider; - - AnalysisContextCollectionImpl? _analysisContextCollection; - AnalysisDriver? _driver; - - final Set knownPackages = {}; - - /// Whether the test should perform analysis with NNBD enabled. - /// - /// `false` by default. May be overridden in derived test classes. - bool get analyzeWithNnbd => false; - - AnalysisDriver? get driver { - if (_driver == null) { - _createAnalysisContexts(); - } - return _driver; - } - - String get homePath => '/home'; - - Folder get sdkRoot => newFolder('/sdk'); - - AnalysisSession get session => driver!.currentSession; - - String get testsPath => '$homePath/tests'; - - /// Makes a mock version of the Angular package available for unit testing. - /// - /// If optional argument [internalUris] is `true`, the mock Angular package - /// will be located in a package called `third_party.dart_src.angular.angular` - /// (as it is in Google3), and `package:angular` will simply re-export it; - /// this allows the test to reflect usage in internal sources. - void addAngularPackage({bool internalUris = false}) { - addPackageFile( - internalUris ? 'third_party.dart_src.angular.angular' : 'angular', - 'angular.dart', ''' -class Component { - const Component({ - required String selector, - // optional arguments skipped - }) -} -class Attribute { - const Attribute(String attributeName); -} -class ContentChild { - const ContentChild(Object selector, {Object? read}); -} -class ContentChildren { - const ContentChildren(Object selector, {Object? read}); -} -class Optional { - const Optional(); -} -class ViewChild { - const ViewChild(Object selector, {Object? read}); -} -class ViewChildren { - const ViewChildren(Object selector, {Object? read}); -} -class Injector { - dynamic get(Object token, [Object? notFoundValue]) => null; -} -class Injectable { - const Injectable(); -} -'''); - if (internalUris) { - addPackageFile('angular', 'angular.dart', ''' -export 'package:third_party.dart_src.angular.angular/angular.dart'; -'''); - } - } - - void addBuiltValuePackage() { - addPackageFile('built_value', 'built_value.dart', ''' -abstract class Built, B extends Builder> {} -abstract class Builder, B extends Builder> {} -const String nullable = 'nullable'; -class BuiltValueNullFieldError extends Error { - static T checkNotNull(T? value, String type, String field) => value!; -} -'''); - } - - void addMetaPackage() { - addPackageFile('meta', 'meta.dart', r''' -library meta; - -const Required required = const Required(); - -class Required { - final String reason; - const Required([this.reason]); -} -'''); - } - - /// Add a new file with the given [pathInLib] to the package with the given - /// [packageName]. Then ensure that the package under test depends on the - /// package. - File addPackageFile(String packageName, String pathInLib, String content) { - var packagePath = '/.pub-cache/$packageName'; - knownPackages.add(packageName); - return newFile('$packagePath/lib/$pathInLib', content); - } - - /// Add the quiver package and a library with URI, - /// "package:quiver/check.dart". - /// - /// Then ensure that the package under test depends on the package. - void addQuiverPackage() { - addPackageFile('quiver', 'check.dart', r''' -library quiver.check; - -T checkNotNull(T reference, {dynamic message}) => T; -'''); - } - - Source addSource(String path, String content, [Uri? uri]) { - File file = newFile(path, content); - return file.createSource(uri); - } - - /// Add the test_core package and a library with URI, - /// "package:test_core/test_core.dart". - /// - /// Then ensure that the package under test depends on the package. - void addTestCorePackage() { - addPackageFile('test_core', 'test_core.dart', r''' -library test_core; - -void setUp(dynamic callback()) {} -void group(dynamic description, dynamic body()) {} -void test(dynamic description, dynamic body()) {} -'''); - addPackageFile('test', 'test.dart', r''' -library test; -export 'package:test_core/test_core.dart'; -'''); - } - - /// Return the existing analysis context that should be used to analyze the - /// given [path], or throw [StateError] if the [path] is not analyzed in any - /// of the created analysis contexts. - AnalysisContext getContext(String path) { - return _getContext(path); - } - - /// Return the existing analysis driver that should be used to analyze the - /// given [path], or throw [StateError] if the [path] is not analyzed in any - /// of the created analysis contexts. - AnalysisDriver getDriver(String path) { - return _getContext(path).driver; - } - - void setUp() { - setupResourceProvider(); - overlayResourceProvider = OverlayResourceProvider(resourceProvider); - - createMockSdk( - resourceProvider: resourceProvider, - root: sdkRoot, - ); - - newFolder(testsPath); - newFile('$testsPath/.packages', ''' -tests:file://$testsPath/lib -'''); - var pubspecPath = '$testsPath/pubspec.yaml'; - // Subclasses may write out a different file first. - if (!getFile(pubspecPath).exists) { - newFile(pubspecPath, ''' -name: tests -version: 1.0.0 -environment: - sdk: '>=2.9.0 <3.0.0' -'''); - } - } - - void setupResourceProvider() {} - - void tearDown() { - AnalysisEngine.instance.clearCaches(); - } - - /// Create all analysis contexts in [_homePath]. - void _createAnalysisContexts() { - var packageConfigJson = { - 'configVersion': 2, - 'packages': [ - for (var packageName in knownPackages) - { - 'name': packageName, - 'rootUri': toUriStr('/.pub-cache/$packageName'), - 'packageUri': 'lib/', - 'languageVersion': '2.12' - }, - { - 'name': 'tests', - 'rootUri': '../', - 'packageUri': 'lib/', - 'languageVersion': analyzeWithNnbd ? '2.12' : '2.9' - } - ], - 'generated': '2020-10-21T21:13:05.186004Z', - 'generator': 'pub', - 'generatorVersion': '2.10.0' - }; - newFile('$testsPath/.dart_tool/package_config.json', - JsonEncoder.withIndent(' ').convert(packageConfigJson)); - _analysisContextCollection = AnalysisContextCollectionImpl( - includedPaths: [convertPath(homePath)], - enableIndex: true, - resourceProvider: overlayResourceProvider, - sdkPath: sdkRoot.path, - ); - - _driver = getDriver(convertPath(testsPath)); - } - - /// Return the existing analysis context that should be used to analyze the - /// given [path], or throw [StateError] if the [path] is not analyzed in any - /// of the created analysis contexts. - DriverBasedAnalysisContext _getContext(String path) { - if (_analysisContextCollection == null) { - _createAnalysisContexts(); - } - path = convertPath(path); - return _analysisContextCollection!.contextFor(path); - } -} diff --git a/pkg/nnbd_migration/test/abstract_single_unit.dart b/pkg/nnbd_migration/test/abstract_single_unit.dart deleted file mode 100644 index 842971e2acbc..000000000000 --- a/pkg/nnbd_migration/test/abstract_single_unit.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/error/error.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/error/codes.dart'; -import 'package:analyzer/src/test_utilities/find_element.dart'; -import 'package:analyzer/src/test_utilities/find_node.dart'; -import 'package:test/test.dart'; - -import 'abstract_context.dart'; - -/// TODO(paulberry): this logic is duplicated from other packages. Find a way -/// share it, or avoid relying on it. -class AbstractSingleUnitTest extends AbstractContextTest { - bool verifyNoTestUnitErrors = true; - - String? testCode; - late String testFile; - Uri? testUri; - Source? testSource; - late ResolvedUnitResult testAnalysisResult; - CompilationUnit? testUnit; - CompilationUnitElement? testUnitElement; - LibraryElement? testLibraryElement; - late FindNode findNode; - late FindElement findElement; - - void addTestSource(String code, [Uri? uri]) { - testCode = code; - testSource = addSource(testFile, code, uri); - } - - Future resolveTestUnit(String code) async { - addTestSource(code, testUri); - testAnalysisResult = - await session.getResolvedUnit(testFile) as ResolvedUnitResult; - testUnit = testAnalysisResult.unit; - if (verifyNoTestUnitErrors) { - expect(testAnalysisResult.errors.where((AnalysisError error) { - return error.errorCode != WarningCode.DEAD_CODE && - error.errorCode != WarningCode.UNUSED_CATCH_CLAUSE && - error.errorCode != WarningCode.UNUSED_CATCH_STACK && - error.errorCode != WarningCode.UNUSED_ELEMENT && - error.errorCode != WarningCode.UNUSED_ELEMENT_PARAMETER && - error.errorCode != WarningCode.UNUSED_FIELD && - error.errorCode != WarningCode.UNUSED_IMPORT && - error.errorCode != WarningCode.UNUSED_LOCAL_VARIABLE; - }), isEmpty); - } - testUnitElement = testUnit!.declaredElement; - testLibraryElement = testUnitElement!.library; - findNode = FindNode(code, testUnit!); - findElement = FindElement(testUnit!); - } - - @override - void setUp() { - var testRoot = testsPath; - if (analyzeWithNnbd) { - newFile('$testRoot/analysis_options.yaml', ''' -analyzer: - enable-experiment: - - non-nullable -'''); - } - if (analyzeWithNnbd) { - newPubspecYamlFile(testRoot, ''' -name: tests -version: 1.0.0 -environment: - sdk: '>=2.12.0 <3.0.0' -'''); - } - super.setUp(); - testFile = convertPath('$testRoot/lib/test.dart'); - testUri = Uri.parse('package:tests/test.dart'); - } -} diff --git a/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart b/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart deleted file mode 100644 index c599e9b597c6..000000000000 --- a/pkg/nnbd_migration/test/already_migrated_code_decorator_test.dart +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/generated/utilities_dart.dart'; -import 'package:analyzer/src/test_utilities/find_element.dart'; -import 'package:analyzer/src/test_utilities/find_node.dart'; -import 'package:analyzer/src/test_utilities/mock_sdk.dart'; -import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/already_migrated_code_decorator.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(_AlreadyMigratedCodeDecoratorTestNormal); - defineReflectiveTests(_AlreadyMigratedCodeDecoratorTestProvisional); - }); -} - -class _AlreadyMigratedCodeDecoratorTestBase { - final NullabilitySuffix suffix; - - final decoratedTypeParameterBounds = DecoratedTypeParameterBounds(); - - _AlreadyMigratedCodeDecoratorTestBase(this.suffix); - - DecoratedType? getDecoratedBound(TypeParameterElement element) => - decoratedTypeParameterBounds.get(element); - - void setUp() { - DecoratedTypeParameterBounds.current = decoratedTypeParameterBounds; - } - - void tearDown() { - DecoratedTypeParameterBounds.current = null; - } - - Future test_decorate_dynamic() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - withElement.checkDynamic( - withElement.decorate(withElement.typeProvider.dynamicType), - 'test type'); - } - - Future test_decorate_functionType_generic_bounded() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class A {}', - ); - var typeFormal = withUnit.findElement.typeParameter('T'); - var withElement = withUnit.withElement(typeFormal); - - var decoratedType = withElement.decorate( - FunctionTypeImpl( - typeFormals: [typeFormal], - parameters: const [], - returnType: TypeParameterTypeImpl( - element: typeFormal, - nullabilitySuffix: NullabilitySuffix.star, - ), - nullabilitySuffix: suffix, - ), - ); - withElement.checkNum( - getDecoratedBound(typeFormal)!, - withElement.checkExplicitlyNonNullable, - 'bound of type formal T of test type'); - withElement.checkTypeParameter( - decoratedType.returnType!, - withElement.checkExplicitlyNonNullable, - typeFormal, - 'return type of test type'); - } - - Future test_decorate_functionType_generic_no_explicit_bound() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class A {}', - ); - var typeFormal = withUnit.findElement.typeParameter('T'); - var withElement = withUnit.withElement(typeFormal); - - var decoratedType = withElement.decorate( - FunctionTypeImpl( - typeFormals: [typeFormal], - parameters: const [], - returnType: TypeParameterTypeImpl( - element: typeFormal, - nullabilitySuffix: NullabilitySuffix.star, - ), - nullabilitySuffix: suffix, - ), - ); - withElement.checkObject( - getDecoratedBound(typeFormal)!, - withElement.checkExplicitlyNullable, - 'bound of type formal T of test type'); - withElement.checkTypeParameter( - decoratedType.returnType!, - withElement.checkExplicitlyNonNullable, - typeFormal, - 'return type of test type'); - } - - Future test_decorate_functionType_named_parameter() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkDynamic( - withElement - .decorate( - FunctionTypeImpl( - typeFormals: const [], - parameters: [ - ParameterElementImpl.synthetic( - 'x', - withElement.typeProvider.dynamicType, - ParameterKind.NAMED, - ) - ], - returnType: withElement.typeProvider.voidType, - nullabilitySuffix: suffix, - ), - ) - .namedParameters!['x'], - 'parameter x of test type'); - } - - Future test_decorate_functionType_ordinary_parameters() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - var decoratedType = withElement.decorate( - FunctionTypeImpl( - typeFormals: const [], - parameters: [ - ParameterElementImpl.synthetic( - 'x', - withElement.typeProvider.dynamicType, - ParameterKind.REQUIRED, - ), - ParameterElementImpl.synthetic( - 'y', - withElement.typeProvider.dynamicType, - ParameterKind.REQUIRED, - ) - ], - returnType: withElement.typeProvider.voidType, - nullabilitySuffix: suffix, - ), - ); - withElement.checkDynamic( - decoratedType.positionalParameters![0], 'parameter 0 of test type'); - withElement.checkDynamic( - decoratedType.positionalParameters![1], 'parameter 1 of test type'); - } - - Future test_decorate_functionType_positional_parameter() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkDynamic( - withElement - .decorate( - FunctionTypeImpl( - typeFormals: const [], - parameters: [ - ParameterElementImpl.synthetic( - 'x', - withElement.typeProvider.dynamicType, - ParameterKind.POSITIONAL, - ) - ], - returnType: withElement.typeProvider.voidType, - nullabilitySuffix: suffix, - ), - ) - .positionalParameters![0], - 'parameter 0 of test type'); - } - - Future test_decorate_functionType_question() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkExplicitlyNullable( - withElement - .decorate( - FunctionTypeImpl( - typeFormals: const [], - parameters: const [], - returnType: withElement.typeProvider.voidType, - nullabilitySuffix: NullabilitySuffix.question, - ), - ) - .node, - 'test type'); - } - - Future test_decorate_functionType_returnType() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkDynamic( - withElement - .decorate( - FunctionTypeImpl( - typeFormals: const [], - parameters: const [], - returnType: withElement.typeProvider.dynamicType, - nullabilitySuffix: suffix, - ), - ) - .returnType, - 'return type of test type'); - } - - Future test_decorate_functionType_star() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkExplicitlyNonNullable( - withElement - .decorate( - FunctionTypeImpl( - typeFormals: const [], - parameters: const [], - returnType: withElement.typeProvider.voidType, - nullabilitySuffix: suffix, - ), - ) - .node, - 'test type'); - } - - Future test_decorate_interfaceType_parameters() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - var decoratedType = withElement.decorate(InterfaceTypeImpl( - element: withElement.typeProvider.mapElement, - typeArguments: [ - withElement.typeProvider.intType, - withElement.typeProvider.numType - ], - nullabilitySuffix: suffix)); - withElement.checkInt(decoratedType.typeArguments[0]!, - withElement.checkExplicitlyNonNullable, 'type argument 0 of test type'); - withElement.checkNum(decoratedType.typeArguments[1]!, - withElement.checkExplicitlyNonNullable, 'type argument 1 of test type'); - } - - Future test_decorate_interfaceType_simple_question() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkInt( - withElement.decorate( - InterfaceTypeImpl( - element: withElement.typeProvider.intElement, - typeArguments: const [], - nullabilitySuffix: NullabilitySuffix.question, - ), - ), - withElement.checkExplicitlyNullable, - 'test type'); - } - - Future test_decorate_interfaceType_simple_star() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkInt( - withElement.decorate( - InterfaceTypeImpl( - element: withElement.typeProvider.intElement, - typeArguments: const [], - nullabilitySuffix: suffix, - ), - ), - withElement.checkExplicitlyNonNullable, - 'test type'); - } - - Future test_decorate_iterable_dynamic() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - var decorated = - withElement.decorate(withElement.typeProvider.iterableDynamicType); - withElement.checkIterable(decorated, withElement.checkExplicitlyNonNullable, - withElement.checkDynamic, 'test type'); - } - - Future test_decorate_never() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkNever( - withElement.decorate(withElement.typeProvider.neverType), 'test type'); - } - - Future test_decorate_typeParameterType_question() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class A {}', - ); - var element = withUnit.findElement.typeParameter('T'); - var withElement = withUnit.withElement(element); - - withElement.checkTypeParameter( - withElement.decorate(TypeParameterTypeImpl( - element: element, nullabilitySuffix: NullabilitySuffix.question)), - withElement.checkExplicitlyNullable, - element, - 'test type'); - } - - Future test_decorate_typeParameterType_star() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class A {}', - ); - var element = withUnit.findElement.typeParameter('T'); - var withElement = withUnit.withElement(element); - - withElement.checkTypeParameter( - withElement.decorate( - TypeParameterTypeImpl(element: element, nullabilitySuffix: suffix)), - withElement.checkExplicitlyNonNullable, - element, - 'test type'); - } - - Future test_decorate_void() async { - var withElement = await _ContextWithFiles().withEmptyUnit(); - - withElement.checkVoid( - withElement.decorate(withElement.typeProvider.voidType), 'test type'); - } - - Future test_getImmediateSupertypes_future() async { - var withUnit = await _ContextWithFiles().buildUnitElement(''); - var element = withUnit.typeProvider.futureElement; - var withElement = withUnit.withElement(element); - - // var class_ = element = typeProvider.futureElement; - var class_ = withElement.typeProvider.futureElement; - var decoratedSupertypes = - withElement.decorator.getImmediateSupertypes(class_).toList(); - var typeParam = class_.typeParameters[0]; - expect(decoratedSupertypes, hasLength(2)); - // TODO(scheglov) Use location matcher. - withElement.checkObject(decoratedSupertypes[0], - withElement.checkExplicitlyNonNullable, 'Future (async.dart:8:16)'); - // Since Future is a subtype of FutureOr, we consider FutureOr to - // be an immediate supertype, even though the class declaration for Future - // doesn't mention FutureOr. - // TODO(scheglov) Use location matcher. - withElement.checkFutureOr( - decoratedSupertypes[1], - withElement.checkExplicitlyNonNullable, - (t, displayName) => withElement.checkTypeParameter( - t!, withElement.checkExplicitlyNonNullable, typeParam, displayName), - 'Future (async.dart:8:16)'); - } - - Future test_getImmediateSupertypes_generic() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class C extends Iterable {}', - ); - var unitElement = withUnit.unitElement; - var class_ = unitElement.classes.single; - var t = class_.typeParameters.single; - - var withElement = withUnit.withElement(class_); - - var decoratedSupertypes = - withElement.decorator.getImmediateSupertypes(class_).toList(); - expect(decoratedSupertypes, hasLength(1)); - withElement.checkIterable( - decoratedSupertypes[0], - withElement.checkExplicitlyNonNullable, - (type, displayName) => withElement.checkTypeParameter( - type!, withElement.checkExplicitlyNonNullable, t, displayName), - 'C (test.dart:1:7)'); - } - - Future test_getImmediateSupertypes_interface() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class C implements num {}', - ); - var unitElement = withUnit.unitElement; - var class_ = unitElement.classes.single; - - var withElement = withUnit.withElement(class_); - - var decoratedSupertypes = - withElement.decorator.getImmediateSupertypes(class_).toList(); - expect(decoratedSupertypes, hasLength(2)); - withElement.checkObject(decoratedSupertypes[0], - withElement.checkExplicitlyNonNullable, 'C (test.dart:1:7)'); - withElement.checkNum(decoratedSupertypes[1], - withElement.checkExplicitlyNonNullable, 'C (test.dart:1:7)'); - } - - Future test_getImmediateSupertypes_mixin() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class C with num {}', - ); - var unitElement = withUnit.unitElement; - var class_ = unitElement.classes.single; - - var withElement = withUnit.withElement(class_); - - var decoratedSupertypes = - withElement.decorator.getImmediateSupertypes(class_).toList(); - expect(decoratedSupertypes, hasLength(2)); - withElement.checkObject(decoratedSupertypes[0], - withElement.checkExplicitlyNonNullable, 'C (test.dart:1:7)'); - withElement.checkNum(decoratedSupertypes[1], - withElement.checkExplicitlyNonNullable, 'C (test.dart:1:7)'); - } - - Future test_getImmediateSupertypes_superclassConstraint() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'mixin C on num {}', - ); - var unitElement = withUnit.unitElement; - var mixin_ = unitElement.mixins.single; - - var withElement = withUnit.withElement(mixin_); - - var decoratedSupertypes = - withElement.decorator.getImmediateSupertypes(mixin_).toList(); - expect(decoratedSupertypes, hasLength(1)); - withElement.checkNum(decoratedSupertypes[0], - withElement.checkExplicitlyNonNullable, 'C (test.dart:1:7)'); - } - - Future test_getImmediateSupertypes_supertype() async { - var withUnit = await _ContextWithFiles().buildUnitElement( - 'class C {}', - ); - var unitElement = withUnit.unitElement; - var class_ = unitElement.classes.single; - - var withElement = withUnit.withElement(class_); - - var decoratedSupertypes = - withElement.decorator.getImmediateSupertypes(class_).toList(); - expect(decoratedSupertypes, hasLength(1)); - // TODO(paulberry): displayName should be 'Object supertype of C' - withElement.checkObject(decoratedSupertypes[0], - withElement.checkExplicitlyNonNullable, 'C (test.dart:1:7)'); - } -} - -/// Specialization of [_AlreadyMigratedCodeDecoratorTestBase] for testing the -/// situation where the already migrated code does not contain star types. In -/// the final product, by definition all already-migrated code will be free of -/// star types. However, since we do not yet migrate using a fully NNBD-aware -/// SDK, we need to handle both star and non-star variants on a short term -/// basis. -@reflectiveTest -class _AlreadyMigratedCodeDecoratorTestNormal - extends _AlreadyMigratedCodeDecoratorTestBase { - _AlreadyMigratedCodeDecoratorTestNormal() : super(NullabilitySuffix.none); -} - -/// Specialization of [_AlreadyMigratedCodeDecoratorTestBase] for testing the -/// situation where the already migrated code contains star types. In the final -/// product, this will never happen. However, since we do not yet migrate using -/// a fully NNBD-aware SDK, we need to handle both star and non-star variants on -/// a short term basis. -@reflectiveTest -class _AlreadyMigratedCodeDecoratorTestProvisional - extends _AlreadyMigratedCodeDecoratorTestBase { - _AlreadyMigratedCodeDecoratorTestProvisional() - : super(NullabilitySuffix.star); -} - -class _ContextWithElement with EdgeTester { - final _ContextWithUnitElement _withUnit; - final Element element; - - final NullabilityGraphForTesting graph = NullabilityGraphForTesting(); - - _ContextWithElement(this._withUnit, this.element); - - NullabilityNode get always => graph.always; - - AlreadyMigratedCodeDecorator get decorator { - return AlreadyMigratedCodeDecorator(graph, typeProvider); - } - - NullabilityNode get never => graph.never; - - TypeProvider get typeProvider { - return _withUnit.typeProvider; - } - - void checkAlwaysNullable(NullabilityNode node, String displayName) { - var edge = assertEdge(always, node, hard: true, checkable: false); - var origin = graph.getEdgeOrigin(edge)!; - expect(origin.kind, EdgeOriginKind.alwaysNullableType); - expect(origin.element, same(element)); - expect(node.displayName, displayName); - } - - void checkDynamic(DecoratedType? decoratedType, String displayName) { - expect(decoratedType!.type, same(typeProvider.dynamicType)); - checkAlwaysNullable(decoratedType.node, displayName); - } - - void checkExplicitlyNonNullable(NullabilityNode? node, String displayName) { - var edge = assertEdge(node, never, hard: true, checkable: false); - var origin = graph.getEdgeOrigin(edge)!; - expect(origin.kind, EdgeOriginKind.alreadyMigratedType); - expect(origin.element, same(element)); - expect(node!.displayName, displayName); - } - - void checkExplicitlyNullable(NullabilityNode? node, String displayName) { - var edge = assertEdge(always, node, hard: true, checkable: false); - var origin = graph.getEdgeOrigin(edge)!; - expect(origin.kind, EdgeOriginKind.alreadyMigratedType); - expect(origin.element, same(element)); - expect(node!.displayName, displayName); - } - - void checkFutureOr( - DecoratedType decoratedType, - void Function(NullabilityNode?, String) checkNullability, - void Function(DecoratedType?, String) checkArgument, - String displayName, - ) { - final type = decoratedType.type as InterfaceType; - expect(type.element, typeProvider.futureOrElement); - checkNullability(decoratedType.node, displayName); - checkArgument( - decoratedType.typeArguments[0], 'type argument 0 of $displayName'); - } - - void checkInt( - DecoratedType decoratedType, - void Function(NullabilityNode?, String) checkNullability, - String displayName, - ) { - final type = decoratedType.type as InterfaceType; - expect(type.element, typeProvider.intElement); - checkNullability(decoratedType.node, displayName); - } - - void checkIterable( - DecoratedType decoratedType, - void Function(NullabilityNode?, String) checkNullability, - void Function(DecoratedType?, String) checkArgument, - String displayName, - ) { - final type = decoratedType.type as InterfaceType; - expect(type.element, typeProvider.iterableElement); - checkNullability(decoratedType.node, displayName); - checkArgument( - decoratedType.typeArguments[0], 'type argument 0 of $displayName'); - } - - void checkNever(DecoratedType decoratedType, String displayName) { - expect(decoratedType.type, same(typeProvider.neverType)); - checkExplicitlyNonNullable(decoratedType.node, displayName); - } - - void checkNum( - DecoratedType decoratedType, - void Function(NullabilityNode?, String) checkNullability, - String displayName, - ) { - final type = decoratedType.type as InterfaceType; - expect(type.element, typeProvider.numElement); - checkNullability(decoratedType.node, displayName); - } - - void checkObject( - DecoratedType decoratedType, - void Function(NullabilityNode?, String) checkNullability, - String displayName, - ) { - final type = decoratedType.type as InterfaceType; - expect(type.element, typeProvider.objectType.element); - checkNullability(decoratedType.node, displayName); - } - - void checkTypeParameter( - DecoratedType decoratedType, - void Function(NullabilityNode?, String) checkNullability, - TypeParameterElement expectedElement, - String displayName, - ) { - var type = decoratedType.type as TypeParameterTypeImpl; - expect(type.element, same(expectedElement)); - checkNullability(decoratedType.node, displayName); - } - - void checkVoid(DecoratedType decoratedType, String displayName) { - expect(decoratedType.type, same(typeProvider.voidType)); - checkAlwaysNullable(decoratedType.node, displayName); - } - - DecoratedType decorate(DartType type) { - var decoratedType = decorator.decorate( - type, element, NullabilityNodeTarget.text('test type')); - expect(decoratedType.type, same(type)); - return decoratedType; - } -} - -class _ContextWithFiles with ResourceProviderMixin { - Future<_ContextWithUnitElement> buildUnitElement(String content) async { - var file = newFile('/home/test/lib/test.dart', content); - - var sdkRoot = newFolder('/sdk'); - createMockSdk( - resourceProvider: resourceProvider, - root: sdkRoot, - ); - - var contextCollection = AnalysisContextCollection( - resourceProvider: resourceProvider, - includedPaths: [file.path], - sdkPath: sdkRoot.path, - ); - var analysisContext = contextCollection.contextFor(file.path); - var analysisSession = analysisContext.currentSession; - var result = await analysisSession.getResolvedUnit(file.path); - return _ContextWithUnitElement(result as ResolvedUnitResult); - } - - Future<_ContextWithElement> withEmptyUnit() async { - var withUnit = await buildUnitElement(''); - return withUnit.withElement(withUnit.unitElement); - } -} - -class _ContextWithUnitElement { - final ResolvedUnitResult _unitResult; - - _ContextWithUnitElement(this._unitResult); - - FindElement get findElement { - return FindElement(_unitResult.unit); - } - - FindNode get findNode { - return FindNode(_unitResult.content, _unitResult.unit); - } - - TypeProvider get typeProvider { - return unitElement.library.typeProvider; - } - - CompilationUnitElement get unitElement { - return _unitResult.unit.declaredElement!; - } - - _ContextWithElement withElement(Element element) { - return _ContextWithElement(this, element); - } -} diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart deleted file mode 100644 index d3e373828f60..000000000000 --- a/pkg/nnbd_migration/test/api_test.dart +++ /dev/null @@ -1,10866 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/diagnostic/diagnostic.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'abstract_context.dart'; -import 'api_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(_ProvisionalApiTest); - defineReflectiveTests(_ProvisionalApiTestPermissive); - defineReflectiveTests(_ProvisionalApiTestWithReset); - }); -} - -/// Tests of the provisional API. -@reflectiveTest -class _ProvisionalApiTest extends _ProvisionalApiTestBase - with _ProvisionalApiTestCases { - @override - bool get _usePermissiveMode => false; -} - -/// Base class for provisional API tests. -abstract class _ProvisionalApiTestBase extends AbstractContextTest { - String? projectPath; - - bool get _usePermissiveMode; - - void setUp() { - projectPath = convertPath(testsPath); - super.setUp(); - } - - /// Hook invoked between stages of processing inputs. - void _betweenStages() {} - - /// Verifies that migration of the files in [input] produces the output in - /// [expectedOutput]. - /// - /// Optional parameter [removeViaComments] indicates whether dead code should - /// be removed in its entirety (the default) or removed by commenting it out. - Future _checkMultipleFileChanges( - Map input, Map expectedOutput, - {Map migratedInput = const {}, - bool removeViaComments = false, - bool warnOnWeakCode = false, - bool allowErrors = false}) async { - for (var path in migratedInput.keys) { - newFile(path, migratedInput[path]!); - } - for (var path in input.keys) { - newFile(path, input[path]!); - } - var listener = TestMigrationListener(); - var migration = NullabilityMigration(listener, - permissive: _usePermissiveMode, - removeViaComments: removeViaComments, - warnOnWeakCode: warnOnWeakCode); - for (var path in input.keys) { - var resolvedLibrary = await session.getResolvedLibrary(path); - if (resolvedLibrary is ResolvedLibraryResult) { - for (var unit in resolvedLibrary.units) { - var errors = - unit.errors.where((e) => e.severity == Severity.error).toList(); - if (!allowErrors && errors.isNotEmpty) { - fail('Unexpected error(s): $errors'); - } - migration.prepareInput(unit); - } - } - } - expect(migration.unmigratedDependencies, isEmpty); - _betweenStages(); - for (var path in input.keys) { - var resolvedLibrary = await session.getResolvedLibrary(path); - if (resolvedLibrary is ResolvedLibraryResult) { - for (var unit in resolvedLibrary.units) { - migration.processInput(unit); - } - } - } - _betweenStages(); - for (var path in input.keys) { - var resolvedLibrary = await session.getResolvedLibrary(path); - if (resolvedLibrary is ResolvedLibraryResult) { - for (var unit in resolvedLibrary.units) { - migration.finalizeInput(unit); - } - } - } - migration.finish(); - var sourceEdits = >{}; - for (var entry in listener.edits.entries) { - var path = entry.key.fullName; - expect(expectedOutput.keys, contains(path)); - sourceEdits[path] = entry.value; - } - for (var path in expectedOutput.keys) { - var sourceEditsForPath = sourceEdits[path] ?? []; - sourceEditsForPath.sort((a, b) => b.offset.compareTo(a.offset)); - expect(SourceEdit.applySequence(input[path]!, sourceEditsForPath), - expectedOutput[path]); - } - } - - /// Verifies that migration of the single file with the given [content] - /// produces the [expected] output. - /// - /// Optional parameter [removeViaComments] indicates whether dead code should - /// be removed in its entirety (the default) or removed by commenting it out. - Future _checkSingleFileChanges(String content, dynamic expected, - {Map migratedInput = const {}, - bool removeViaComments = false, - bool warnOnWeakCode = false, - bool allowErrors = false}) async { - var sourcePath = convertPath('$testsPath/lib/test.dart'); - await _checkMultipleFileChanges( - {sourcePath: content}, {sourcePath: expected}, - migratedInput: migratedInput, - removeViaComments: removeViaComments, - warnOnWeakCode: warnOnWeakCode, - allowErrors: allowErrors); - } -} - -/// Mixin containing test cases for the provisional API. -mixin _ProvisionalApiTestCases on _ProvisionalApiTestBase { - Future test_accept_required_hint() async { - var content = ''' -f({/*required*/ int i}) {} -'''; - var expected = ''' -f({required int i}) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_accept_required_hint_nullable() async { - var content = ''' -f({/*required*/ int i}) {} -g() { - f(i: null); -} -'''; - var expected = ''' -f({required int? i}) {} -g() { - f(i: null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_add_explicit_parameter_type() async { - var content = ''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => value + 1); -} -'''; - // Under the new NNBD rules, `value` gets an inferred type of `Object?`. We - // need to change this to `dynamic` to avoid an "undefined operator +" - // error. - var expected = ''' -abstract class C { - void m(T Function(T)? callback); -} -void test(C c) { - c.m((dynamic value) => value + 1); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40476') - Future test_add_explicit_parameter_type_interpolation() async { - var content = r''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => '$value'; -} -'''; - // Under the new NNBD rules, `value` gets an inferred type of `Object?`, - // whereas it previously had a type of `dynamic`. But we don't need to fix - // it because `Object?` supports `toString`. - var expected = r''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => '$value'; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40476') - Future test_add_explicit_parameter_type_object_method() async { - var content = ''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => value.toString()); -} -'''; - // Under the new NNBD rules, `value` gets an inferred type of `Object?`, - // whereas it previously had a type of `dynamic`. But we don't need to fix - // it because `Object?` supports `toString`. - var expected = ''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => value.toString()); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40476') - Future test_add_explicit_parameter_type_unused() async { - var content = ''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => 0); -} -'''; - // Under the new NNBD rules, `value` gets an inferred type of `Object?`, - // whereas it previously had a type of `dynamic`. But we don't need to fix - // it because it's unused. - var expected = ''' -abstract class C { - void m(T Function(T) callback); -} -void test(C c) { - c.m((value) => 0); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_add_required() async { - var content = ''' -int f({String s}) => s.length; -'''; - var expected = ''' -int f({required String s}) => s.length; -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39404') - Future test_add_type_parameter_bound() async { - /// After a migration, a bound may be made nullable. Instantiate-to-bounds - /// may need to be made explicit where the migration engine prefers a - /// non-null type. - var content = ''' -class C {} - -void main() { - C c = C(); -} -'''; - var expected = ''' -class C {} - -void main() { - C c = C(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_ambiguous_bang_hint_after_as() async { - var content = ''' -T f(Object/*?*/ x) => x as T/*!*/; -'''; - // The `/*!*/` is considered to apply to the type `T`, not to the expression - // `x as T`, so we shouldn't produce `(x as T)!`. - var expected = ''' -T f(Object? x) => x as T; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_ambiguous_bang_hint_after_as_assigned() async { - var content = ''' -T f(Object/*?*/ x, T/*!*/ y) => y = x as T/*!*/; -'''; - // The `/*!*/` is considered to apply to the type `T`, not to the expression - // `y = x as T`, so we shouldn't produce `(y = x as T)!`. - var expected = ''' -T f(Object? x, T y) => y = x as T; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_ambiguous_bang_hint_after_is() async { - var content = ''' -bool f(Object/*?*/ x) => x is T/*!*/; -'''; - // The `/*!*/` is considered to apply to the type `T`, not to the expression - // `x is T`, so we shouldn't produce `(x is T)!`. - var expected = ''' -bool f(Object? x) => x is T; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_ambiguous_bang_hint_after_is_conditional() async { - var content = ''' -dynamic f(Object/*?*/ x, dynamic y) => y ? y : x is T/*!*/; -'''; - // The `/*!*/` is considered to apply to the type `T`, not to the expression - // `y ? y : x is T`, so we shouldn't produce `(y ? y : x is T)!`. - var expected = ''' -dynamic f(Object? x, dynamic y) => y ? y : x is T; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_ambiguous_closure_parameter_in_local_variable() async { - var content = ''' -Object _f(Object Function(T) callback, Object obj) => 0; -g() { - var y = _f>( - (x) => x.keys, - _f>( - (x) => x.last, 0)); -} -'''; - var expected = ''' -Object _f(Object Function(T) callback, Object obj) => 0; -g() { - var y = _f>( - (x) => x.keys, - _f>( - (x) => x.last, 0)); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_component_attribute() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -@Component( - selector: 'my-component' -) -class MyComponent { - int foo; - MyComponent(@Attribute('foo') this.foo); -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -@Component( - selector: 'my-component' -) -class MyComponent { - int? foo; - MyComponent(@Attribute('foo') this.foo); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_component_constructor() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -@Component( - selector: 'my-component' -) -class MyComponent { - int foo; - MyComponent(this.foo); - void nullifyFoo() { foo = null; } -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -@Component( - selector: 'my-component' -) -class MyComponent { - int? foo; - MyComponent(int this.foo); - void nullifyFoo() { foo = null; } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_contentChild_field() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - // Initialize this.bar in the constructor just so the migration tool doesn't - // decide to make it nullable due to the lack of initializer. - MyComponent(this.bar); - - @ContentChild('foo') - Element bar; -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - // Initialize this.bar in the constructor just so the migration tool doesn't - // decide to make it nullable due to the lack of initializer. - MyComponent(this.bar); - - @ContentChild('foo') - Element? bar; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_contentChild_field_not_late() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - @ContentChild('foo') - Element bar; - Element baz; - - f(Element /*!*/ e) { - bar = e; - baz = e; - } - g() => bar.id; - h() => baz.id; -} -'''; - // `late` heuristics are disabled for `bar` since it's marked with - // `ContentChild`. But they do apply to `baz`. - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - @ContentChild('foo') - Element? bar; - late Element baz; - - f(Element e) { - bar = e; - baz = e; - } - g() => bar!.id; - h() => baz.id; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_contentChildren_field_not_late() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class myComponent { - @ContentChildren('foo') - Element bar; - Element baz; - - f(Element /*!*/ e) { - bar = e; - baz = e; - } - g() => bar.id; - h() => baz.id; -} -'''; - // `late` heuristics are disabled for `bar` since it's marked with - // `ContentChildren`. But they do apply to `baz`. - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class myComponent { - @ContentChildren('foo') - Element? bar; - late Element baz; - - f(Element e) { - bar = e; - baz = e; - } - g() => bar!.id; - h() => baz.id; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_injectable_constructor() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -@Injectable() -class MyClass { - int foo; - MyClass(this.foo); - void nullifyFoo() { foo = null; } -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -@Injectable() -class MyClass { - int? foo; - MyClass(int this.foo); - void nullifyFoo() { foo = null; } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_injectable_function() async { - addAngularPackage(); - var content = ''' -import 'package:angular/angular.dart'; - -class C {} - -@Injectable() -C createC(int n, @Optional() int x) => C(); -'''; - var expected = ''' -import 'package:angular/angular.dart'; - -class C {} - -@Injectable() -C createC(int n, @Optional() int? x) => C(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_optional_constructor_param() async { - addAngularPackage(); - var content = ''' -import 'package:angular/angular.dart'; - -class MyComponent { - MyComponent(@Optional() String foo); -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; - -class MyComponent { - MyComponent(@Optional() String? foo); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_optional_constructor_param_field_formal() async { - addAngularPackage(); - var content = ''' -import 'package:angular/angular.dart'; - -class MyComponent { - String foo; - MyComponent(@Optional() this.foo); -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; - -class MyComponent { - String? foo; - MyComponent(@Optional() this.foo); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_optional_constructor_param_internal() async { - addAngularPackage(internalUris: true); - var content = ''' -import 'package:angular/angular.dart'; - -class MyComponent { - MyComponent(@Optional() String foo); -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; - -class MyComponent { - MyComponent(@Optional() String? foo); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_viewChild_field() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - // Initialize this.bar in the constructor just so the migration tool doesn't - // decide to make it nullable due to the lack of initializer. - MyComponent(this.bar); - - @ViewChild('foo') - Element bar; -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - // Initialize this.bar in the constructor just so the migration tool doesn't - // decide to make it nullable due to the lack of initializer. - MyComponent(this.bar); - - @ViewChild('foo') - Element? bar; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_viewChild_field_internal() async { - addAngularPackage(internalUris: true); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - // Initialize this.bar in the constructor just so the migration tool doesn't - // decide to make it nullable due to the lack of initializer. - MyComponent(this.bar); - - @ViewChild('foo') - Element bar; -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - // Initialize this.bar in the constructor just so the migration tool doesn't - // decide to make it nullable due to the lack of initializer. - MyComponent(this.bar); - - @ViewChild('foo') - Element? bar; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_viewChild_field_not_late() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - @ViewChild('foo') - Element bar; - Element baz; - - f(Element /*!*/ e) { - bar = e; - baz = e; - } - g() => bar.id; - h() => baz.id; -} -'''; - // `late` heuristics are disabled for `bar` since it's marked with - // `ViewChild`. But they do apply to `baz`. - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - @ViewChild('foo') - Element? bar; - late Element baz; - - f(Element e) { - bar = e; - baz = e; - } - g() => bar!.id; - h() => baz.id; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_viewChild_setter() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - @ViewChild('foo') - set bar(Element element) {} -} -'''; - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class MyComponent { - @ViewChild('foo') - set bar(Element? element) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_angular_viewChildren_field_not_late() async { - addAngularPackage(); - var content = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class myComponent { - @ViewChildren('foo') - Element bar; - Element baz; - - f(Element /*!*/ e) { - bar = e; - baz = e; - } - g() => bar.id; - h() => baz.id; -} -'''; - // `late` heuristics are disabled for `bar` since it's marked with - // `ViewChildren`. But they do apply to `baz`. - var expected = ''' -import 'dart:html'; -import 'package:angular/angular.dart'; - -class myComponent { - @ViewChildren('foo') - Element? bar; - late Element baz; - - f(Element e) { - bar = e; - baz = e; - } - g() => bar!.id; - h() => baz.id; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_annotation_named_constructor() async { - var content = ''' -class C { - final List values; - const factory C.ints(List list) = C; - const C(this.values); -} - -@C.ints([1, 2, 3]) -class D {} -'''; - var expected = ''' -class C { - final List? values; - const factory C.ints(List? list) = C; - const C(this.values); -} - -@C.ints([1, 2, 3]) -class D {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_argumentError_checkNotNull_implies_non_null_intent() async { - var content = ''' -void f(int i) { - ArgumentError.checkNotNull(i); -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) { - ArgumentError.checkNotNull(i); -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_as_allows_null() async { - var content = ''' -int f(Object o) => (o as int)?.gcd(1); -main() { - f(null); -} -'''; - var expected = ''' -int? f(Object? o) => (o as int?)?.gcd(1); -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_assign_null_to_generic_type() async { - var content = ''' -main() { - List x = null; -} -'''; - var expected = ''' -main() { - List? x = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_assignment_to_promoted_var_can_undo_promotion() async { - var content = ''' -abstract class C { - void test() { - var x = f(); - while (x != null) { - x = f(); - } - } - int/*?*/ f(); -} -'''; - var expected = ''' -abstract class C { - void test() { - var x = f(); - while (x != null) { - x = f(); - } - } - int? f(); -} -'''; - // Prior to the fix for https://github.com/dart-lang/sdk/issues/41411, - // migration would consider the LHS of `x = f()` to have context type - // non-nullable `int`, so it would add a null check to the value returned - // from `f`. - await _checkSingleFileChanges(content, expected); - } - - Future test_at_required_to_required_in_redirecting_factory() async { - // Redirecting factory constructors have special logic to suppress some of - // the usual heuristics for adding `required`, since it's allowed for a - // redirecting factory constructor to have a non-required non-nullable - // argument with no default. But we need to make sure that we still convert - // `@required` to `required`. - addMetaPackage(); - var content = r''' -import 'package:meta/meta.dart'; -abstract class A { - int get v; - A._(); - factory A({@required int v}) = B._; -} -class B extends A { - @override - final int v; - B._({this.v}) : super._(); -} -'''; - var expected = r''' -import 'package:meta/meta.dart'; -abstract class A { - int? get v; - A._(); - factory A({required int v}) = B._; -} -class B extends A { - @override - final int? v; - B._({this.v}) : super._(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_avoid_redundant_future_or() async { - // FutureOr and FutureOr? are equivalent types; we never insert - // the redundant second `?`. - var content = ''' -import 'dart:async'; -abstract class C { - FutureOr f(); - FutureOr/*?*/ g(); - FutureOr h(bool b) => b ? f() : g(); -} -'''; - var expected = ''' -import 'dart:async'; -abstract class C { - FutureOr f(); - FutureOr? g(); - FutureOr h(bool b) => b ? f() : g(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_await_null() async { - var content = ''' -Future test() async { - return await null; -} -'''; - var expected = ''' -Future test() async { - return await null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_await_nullable_future_to_non_nullable() async { - var content = ''' -Future foo() async => null; - -Future bar() async { - return await foo(); -} -'''; - var expected = ''' -Future foo() async => null; - -Future bar() async { - return (await foo())!; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_back_propagation_stops_at_implicitly_typed_variables() async { - var content = ''' -class C { - int v; - C(this.v); -} -f(C c) { - var x = c.v; - print(x + 1); -} -main() { - C(null); -} -'''; - var expected = ''' -class C { - int? v; - C(this.v); -} -f(C c) { - var x = c.v!; - print(x + 1); -} -main() { - C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_built_value_non_nullable_getter() async { - addBuiltValuePackage(); - var root = '$projectPath/lib'; - var path1 = convertPath('$root/lib.dart'); - var file1 = r''' -import 'package:built_value/built_value.dart'; - -part 'lib.g.dart'; - -abstract class Foo implements Built { - int get value; - Foo._(); - factory Foo([void Function(FooBuilder) updates]) = _$Foo; -} -'''; - var expected1 = r''' -import 'package:built_value/built_value.dart'; - -part 'lib.g.dart'; - -abstract class Foo implements Built { - int get value; - Foo._(); - factory Foo([void Function(FooBuilder) updates]) = _$Foo; -} -'''; - // Note: in a real-world scenario the generated file would be in a different - // directory but we don't need to simulate that detail for this test. Also, - // the generated file would have a lot more code in it, but we don't need to - // simulate all the details of what is generated. - var path2 = convertPath('$root/lib.g.dart'); - var file2 = r''' -part of 'lib.dart'; - -class _$Foo extends Foo { - @override - final int value; - - factory _$Foo([void Function(FooBuilder) updates]) => throw ''; - - _$Foo._({this.value}) : super._() { - BuiltValueNullFieldError.checkNotNull(value, 'Foo', 'value'); - } -} - -class FooBuilder implements Builder { - int get value => throw ''; -} -'''; - await _checkMultipleFileChanges( - {path1: file1, path2: file2}, {path1: expected1, path2: anything}); - } - - Future test_built_value_nullable_getter() async { - addBuiltValuePackage(); - var root = '$projectPath/lib'; - var path1 = convertPath('$root/lib.dart'); - var file1 = r''' -import 'package:built_value/built_value.dart'; - -part 'lib.g.dart'; - -abstract class Foo implements Built { - @nullable - int get value; - Foo._(); - factory Foo([void Function(FooBuilder) updates]) = _$Foo; -} -'''; - var expected1 = r''' -import 'package:built_value/built_value.dart'; - -part 'lib.g.dart'; - -abstract class Foo implements Built { - int? get value; - Foo._(); - factory Foo([void Function(FooBuilder) updates]) = _$Foo; -} -'''; - // Note: in a real-world scenario the generated file would be in a different - // directory but we don't need to simulate that detail for this test. Also, - // the generated file would have a lot more code in it, but we don't need to - // simulate all the details of what is generated. - var path2 = convertPath('$root/lib.g.dart'); - var file2 = r''' -part of 'lib.dart'; - -class _$Foo extends Foo { - @override - final int value; - - factory _$Foo([void Function(FooBuilder) updates]) => throw ''; - - _$Foo._({this.value}) : super._(); -} - -class FooBuilder implements Builder { - int get value => throw ''; -} -'''; - await _checkMultipleFileChanges( - {path1: file1, path2: file2}, {path1: expected1, path2: anything}); - } - - Future test_built_value_nullable_getter_interface_only() async { - addBuiltValuePackage(); - var content = ''' -import 'package:built_value/built_value.dart'; - -abstract class Foo { - @nullable - int get value; -} -'''; - var expected = ''' -import 'package:built_value/built_value.dart'; - -abstract class Foo { - int? get value; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_already_migrated_extension() async { - var content = ''' -import 'already_migrated.dart'; -void f() { - [].g(); -} -'''; - var alreadyMigrated = ''' -// @dart=2.12 -extension Ext on List { - g() {} -} -'''; - var expected = ''' -import 'already_migrated.dart'; -void f() { - [].g(); -} -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/already_migrated.dart': alreadyMigrated - }); - } - - Future test_call_already_migrated_extension_null_aware() async { - var content = ''' -import 'already_migrated.dart'; -class C { - X m(V v) => v?.toX(); -} -'''; - var alreadyMigrated = ''' -// @dart=2.12 -class X {} -class V {} -extension Ext on V { - X toX() => X(); -} -'''; - var expected = ''' -import 'already_migrated.dart'; -class C { - X? m(V? v) => v?.toX(); -} -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/already_migrated.dart': alreadyMigrated - }); - } - - Future test_call_generic_function_returns_generic_class() async { - var content = ''' -class B implements List { - final C c; - B(this.c); - B cast() => c._castFrom(this); - noSuchMethod(invocation) => super.noSuchMethod(invocation); -} -abstract class C { - B _castFrom(B source); -} -'''; - var expected = ''' -class B implements List { - final C c; - B(this.c); - B cast() => c._castFrom(this); - noSuchMethod(invocation) => super.noSuchMethod(invocation); -} -abstract class C { - B _castFrom(B source); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_migrated_base_class_method_non_nullable() async { - var content = ''' -abstract class M implements Map {} -void _f(bool b, M m, int i) { - if (b) { - m['x'] = i; - } -} -void _g(bool b, M m) { - _f(b, m, null); -} -'''; - var expected = ''' -abstract class M implements Map {} -void _f(bool b, M m, int? i) { - if (b) { - m['x'] = i; - } -} -void _g(bool b, M m) { - _f(b, m, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_migrated_base_class_method_nullable() async { - var content = ''' -abstract class M implements Map {} -int f(M m) => m['x']; -'''; - var expected = ''' -abstract class M implements Map {} -int? f(M m) => m['x']; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_tearoff() async { - var content = ''' -class C { - void call() {} -} -void Function() f(C c) => c; -'''; - var expected = ''' -class C { - void call() {} -} -void Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_tearoff_already_migrated() async { - var content = ''' -import 'already_migrated.dart'; -void Function() f(C c) => c; -'''; - var alreadyMigrated = ''' -// @dart=2.12 -class C { - void call() {} -} -'''; - var expected = ''' -import 'already_migrated.dart'; -void Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/already_migrated.dart': alreadyMigrated - }); - } - - Future - test_call_tearoff_already_migrated_propagate_nullability() async { - var content = ''' -import 'already_migrated.dart'; -Map Function() f(C c) => c; -'''; - var alreadyMigrated = ''' -// @dart=2.12 -class C { - Map call() => {}; -} -'''; - var expected = ''' -import 'already_migrated.dart'; -Map Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/already_migrated.dart': alreadyMigrated - }); - } - - Future test_call_tearoff_already_migrated_with_substitution() async { - var content = ''' -import 'already_migrated.dart'; -Map Function() f(C c) => c; -'''; - var alreadyMigrated = ''' -// @dart=2.12 -class C { - Map call() => {}; -} -'''; - var expected = ''' -import 'already_migrated.dart'; -Map Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/already_migrated.dart': alreadyMigrated - }); - } - - Future test_call_tearoff_futureOr() async { - var content = ''' -import 'dart:async'; -class C { - void call() {} -} -FutureOr f(C c) => c; -'''; - var expected = ''' -import 'dart:async'; -class C { - void call() {} -} -FutureOr f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_tearoff_inherited() async { - var content = ''' -class B { - void call() {} -} -class C extends B {} -void Function() f(C c) => c; -'''; - var expected = ''' -class B { - void call() {} -} -class C extends B {} -void Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_tearoff_inherited_propagate_nullability() async { - var content = ''' -class B { - Map call() => {1: null}; -} -class C extends B {} -Map Function() f(C c) => c; -'''; - var expected = ''' -class B { - Map call() => {1: null}; -} -class C extends B {} -Map Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_tearoff_propagate_nullability() async { - var content = ''' -class C { - Map call() => {1: null}; -} -Map Function() f(C c) => c; -'''; - var expected = ''' -class C { - Map call() => {1: null}; -} -Map Function() f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_call_tearoff_raw_function() async { - var content = ''' -class C { - void call() {} -} -Function f(C c) => c; -'''; - var expected = ''' -class C { - void call() {} -} -Function f(C c) => c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_cascade_on_nullable() async { - var content = ''' -class C { - int /*?*/ x; - void f() { - x..isEven; - } -} -'''; - var expected = ''' -class C { - int? x; - void f() { - x!..isEven; - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_catch_simple() async { - var content = ''' -void f() { - try {} catch (ex, st) {} -} -'''; - var expected = ''' -void f() { - try {} catch (ex, st) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_catch_simple_with_modifications() async { - var content = ''' -void f(String x, StackTrace y) { - try {} catch (ex, st) { - ex = x; - st = y; - } -} -main() { - f(null, null); -} -'''; - var expected = ''' -void f(String? x, StackTrace? y) { - try {} catch (ex, st) { - ex = x; - st = y!; - } -} -main() { - f(null, null); -} -'''; - // Note: using allowErrors=true because variables introduced by a catch - // clause are final - await _checkSingleFileChanges(content, expected, allowErrors: true); - } - - Future test_catch_with_on() async { - var content = ''' -void f() { - try {} on String catch (ex, st) {} -} -'''; - var expected = ''' -void f() { - try {} on String catch (ex, st) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_catch_with_on_with_modifications() async { - var content = ''' -void f(String x, StackTrace y) { - try {} on String catch (ex, st) { - ex = x; - st = y; - } -} -main() { - f(null, null); -} -'''; - var expected = ''' -void f(String? x, StackTrace? y) { - try {} on String? catch (ex, st) { - ex = x; - st = y!; - } -} -main() { - f(null, null); -} -'''; - // Note: using allowErrors=true because variables introduced by a catch - // clause are final - await _checkSingleFileChanges(content, expected, allowErrors: true); - } - - Future test_class_alias_synthetic_constructor_with_parameters() async { - var content = ''' -void main() { - D d = D(null); -} -class C { - C(int i); -} -mixin M {} -class D = C with M; -'''; - var expected = ''' -void main() { - D d = D(null); -} -class C { - C(int? i); -} -mixin M {} -class D = C with M; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_class_alias_synthetic_constructor_with_parameters_and_subclass() async { - var content = ''' -void main() { - E e = E(null); -} -class C { - C(int i); -} -mixin M {} -class D = C with M; -class E extends D { - E(int i) : super(i); -} -'''; - var expected = ''' -void main() { - E e = E(null); -} -class C { - C(int? i); -} -mixin M {} -class D = C with M; -class E extends D { - E(int? i) : super(i); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_class_type_param_bound_references_class() async { - var content = ''' -class Node> { - final List nodes = []; -} -class C extends Node {} -main() { - var x = C(); - x.nodes.add(x); -} -'''; - var expected = ''' -class Node> { - final List nodes = []; -} -class C extends Node {} -main() { - var x = C(); - x.nodes.add(x); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_class_with_default_constructor() async { - var content = ''' -void main() => _f(Foo()); -_f(Foo f) {} -class Foo {} -'''; - var expected = ''' -void main() => _f(Foo()); -_f(Foo f) {} -class Foo {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_code_inside_switch_does_not_imply_non_null_intent() async { - var content = ''' -int _f(int i, int j) { - switch (i) { - case 0: - return j + 1; - default: - return 0; - } -} -int _g(int i, int j) { - if (i == 0) { - return _f(i, j); - } else { - return 0; - } -} -main() { - _g(0, null); -} -'''; - var expected = ''' -int _f(int i, int? j) { - switch (i) { - case 0: - return j! + 1; - default: - return 0; - } -} -int _g(int i, int? j) { - if (i == 0) { - return _f(i, j); - } else { - return 0; - } -} -main() { - _g(0, null); -} -'''; - // Note: prior to the fix for https://github.com/dart-lang/sdk/issues/41407, - // we would consider the use of `j` in `f` to establish non-null intent, so - // the null check would be erroneously placed in `g`'s call to `f`. - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_collection_literal_typed_list() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g([x, y]); -} -g(List/*!*/ z) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g([x!, y!]); -} -g(List z) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_collection_literal_typed_map() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g({x: y}); -} -g(Map/*!*/ z) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g({x!: y!}); -} -g(Map z) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_collection_literal_typed_set() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g({x, y}); -} -g(Set/*!*/ z) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g({x!, y!}); -} -g(Set z) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_collection_literal_untyped_list() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g([x, y]); -} -g(List/*!*/ z) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g([x!, y!]); -} -g(List z) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_collection_literal_untyped_map() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g({x: y}); -} -g(Map/*!*/ z) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g({x!: y!}); -} -g(Map z) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_collection_literal_untyped_set() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g({x, y}); -} -g(Set/*!*/ z) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g({x!, y!}); -} -g(Set z) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_comment_bang_implies_non_null_intent() async { - var content = ''' -void f(int/*!*/ i) {} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) {} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_comment_question_implies_nullable() async { - var content = ''' -void _f() { - int/*?*/ i = 0; -} -'''; - var expected = ''' -void _f() { - int? i = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_conditional_assert_statement_does_not_imply_non_null_intent() async { - var content = ''' -void f(bool b, int i) { - if (b) return; - assert(i != null); -} -void g(bool b, int i) { - if (b) f(b, i); -} -main() { - g(true, null); -} -'''; - var expected = ''' -void f(bool b, int? i) { - if (b) return; - assert(i != null); -} -void g(bool b, int? i) { - if (b) f(b, i); -} -main() { - g(true, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_conditional_dereference_does_not_imply_non_null_intent() async { - var content = ''' -void f(bool b, int i) { - if (b) i.abs(); -} -void g(bool b, int i) { - if (b) f(b, i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(bool b, int? i) { - if (b) i!.abs(); -} -void g(bool b, int? i) { - if (b) f(b, i); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_conditional_expression_futureOr() async { - var content = ''' -import 'dart:async'; -FutureOr f(bool b, FutureOr/*!*/ n) => b ? n : 0; -'''; - var expected = ''' -import 'dart:async'; -FutureOr f(bool b, FutureOr n) => b ? n : 0; -'''; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_conditional_expression_guard_subexpression() async { - var content = ''' -void f(String s, int x, int/*?*/ n) { - s == null ? (x = n) : (x = s.length); -} -'''; - var expected = ''' -void f(String? s, int? x, int? n) { - s == null ? (x = n) : (x = s.length); -} -'''; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_conditional_expression_guard_value_ifFalse() async { - var content = 'int f(String s, int/*?*/ n) => s != null ? s.length : n;'; - var expected = 'int? f(String? s, int? n) => s != null ? s.length : n;'; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_conditional_expression_guard_value_ifTrue() async { - var content = 'int f(String s, int/*?*/ n) => s == null ? n : s.length;'; - var expected = 'int? f(String? s, int? n) => s == null ? n : s.length;'; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future - test_conditional_non_null_usage_does_not_imply_non_null_intent() async { - var content = ''' -void _f(bool b, int i, int j) { - if (b) i.gcd(j); -} -void _g(bool b, int i, int j) { - if (b) _f(b, i, j); -} -main() { - _g(false, 0, null); -} -'''; - var expected = ''' -void _f(bool b, int i, int? j) { - if (b) i.gcd(j!); -} -void _g(bool b, int i, int? j) { - if (b) _f(b, i, j); -} -main() { - _g(false, 0, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_conditional_usage_does_not_propagate_non_null_intent() async { - var content = ''' -void _f(int i) { - assert(i != null); -} -void _g(bool b, int i) { - if (b) _f(i); -} -void _h(bool b1, bool b2, int i) { - if (b1) _g(b2, i); -} -main() { - _h(true, false, null); -} -'''; - var expected = ''' -void _f(int i) { - assert(i != null); -} -void _g(bool b, int? i) { - if (b) _f(i!); -} -void _h(bool b1, bool b2, int? i) { - if (b1) _g(b2, i); -} -main() { - _h(true, false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_conditionalExpression_typeParameter_bound() async { - var content = ''' -num _f1(bool b, num x, T y) => b ? x : y; -num _f2(bool b, num x, T y) => b ? x : y; -num _f3(bool b, num x, T y) => b ? x : y; -num _f4(bool b, num x, T y) => b ? x : y; - -void main() { - int x1 = _f1(true, 0, null); - int x2 = _f2(true, 0, null); - int x3 = _f3(true, null, 0); - int x4 = _f4(true, 0, 0); -} -'''; - var expected = ''' -num? _f1(bool b, num x, T y) => b ? x : y; -num? _f2(bool b, num x, T? y) => b ? x : y; -num? _f3(bool b, num? x, T y) => b ? x : y; -num _f4(bool b, num x, T y) => b ? x : y; - -void main() { - int? x1 = _f1(true, 0, null) as int?; - int? x2 = _f2(true, 0, null) as int?; - int? x3 = _f3(true, null, 0) as int?; - int x4 = _f4(true, 0, 0) as int; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructor_field_formal_resolves_to_getter() async { - var content = ''' -class C { - int get i => 0; - C(this.i); -} -'''; - // It doesn't matter what the migration produces; we just want to make sure - // there isn't a crash. - await _checkSingleFileChanges(content, anything, allowErrors: true); - } - - Future test_constructor_field_formal_resolves_to_setter() async { - var content = ''' -class C { - set i(int value) {} - C(this.i); -} -'''; - // It doesn't matter what the migration produces; we just want to make sure - // there isn't a crash. - await _checkSingleFileChanges(content, anything, allowErrors: true); - } - - Future test_constructor_field_formal_unresolved() async { - var content = ''' -class C { - C(this.i); -} -'''; - // It doesn't matter what the migration produces; we just want to make sure - // there isn't a crash. - await _checkSingleFileChanges(content, anything, allowErrors: true); - } - - Future test_constructor_optional_param_factory() async { - var content = ''' -class C { - factory C([int x]) => C._(); - C._([int x = 0]); -} -'''; - var expected = ''' -class C { - factory C([int? x]) => C._(); - C._([int x = 0]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_constructor_optional_param_factory_redirecting_named() async { - var content = ''' -class C { - factory C({int x}) = C._; - C._({int x = 0}); -} -'''; - var expected = ''' -class C { - factory C({int x}) = C._; - C._({int x = 0}); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_constructor_optional_param_factory_redirecting_unnamed() async { - var content = ''' -class C { - factory C([int x]) = C._; - C._([int x = 0]); -} -'''; - var expected = ''' -class C { - factory C([int x]) = C._; - C._([int x = 0]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructor_optional_param_normal() async { - var content = ''' -class C { - C([int x]); -} -'''; - var expected = ''' -class C { - C([int? x]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructor_optional_param_redirecting() async { - var content = ''' -class C { - C([int x]) : this._(); - C._([int x = 0]); -} -'''; - var expected = ''' -class C { - C([int? x]) : this._(); - C._([int x = 0]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructorDeclaration_factory_non_null_return() async { - var content = ''' -class C { - C._(); - factory C() { - C c = f(); - return c; - } -} -C f() => null; -'''; - var expected = ''' -class C { - C._(); - factory C() { - C c = f()!; - return c; - } -} -C? f() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructorDeclaration_factory_simple() async { - var content = ''' -class C { - C._(); - factory C(int i) => C._(); -} -main() { - C(null); -} -'''; - var expected = ''' -class C { - C._(); - factory C(int? i) => C._(); -} -main() { - C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructorDeclaration_named() async { - var content = ''' -class C { - C.named(int i); -} -main() { - C.named(null); -} -'''; - var expected = ''' -class C { - C.named(int? i); -} -main() { - C.named(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_constructorDeclaration_namedParameter() async { - var content = ''' -class C { - C({Key key}); -} -class Key {} -'''; - var expected = ''' -class C { - C({Key? key}); -} -class Key {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_convert_required() async { - addMetaPackage(); - var content = ''' -import 'package:meta/meta.dart'; -void f({@required String s}) {} -'''; - var expected = ''' -import 'package:meta/meta.dart'; -void f({required String s}) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_custom_future() async { - var content = ''' -class CustomFuture implements Future { - @override - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -_f(CustomFuture> x) async => (await x).first; -'''; - var expected = ''' -class CustomFuture implements Future { - @override - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -_f(CustomFuture> x) async => (await x).first; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_assignment_field() async { - var content = ''' -class C { - int x = 0; -} -void f(C c) { - c.x = null; -} -'''; - var expected = ''' -class C { - int? x = 0; -} -void f(C c) { - c.x = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_assignment_field_in_cascade() async { - var content = ''' -class C { - int x = 0; -} -void f(C c) { - c..x = null; -} -'''; - var expected = ''' -class C { - int? x = 0; -} -void f(C c) { - c..x = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_assignment_local() async { - var content = ''' -void main() { - int i = 0; - i = null; -} -'''; - var expected = ''' -void main() { - int? i = 0; - i = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_assignment_setter() async { - var content = ''' -class C { - void set s(int value) {} -} -void f(C c) { - c.s = null; -} -'''; - var expected = ''' -class C { - void set s(int? value) {} -} -void f(C c) { - c.s = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_field_read() async { - var content = ''' -class C { - int/*?*/ f = 0; -} -int f(C c) => c.f; -'''; - var expected = ''' -class C { - int? f = 0; -} -int? f(C c) => c.f; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_function_return_type() async { - var content = ''' -int Function() _f(int Function() x) => x; -int g() => null; -main() { - _f(g); -} -'''; - var expected = ''' -int? Function() _f(int? Function() x) => x; -int? g() => null; -main() { - _f(g); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_generic_contravariant_inward() async { - var content = ''' -class C { - void f(T t) {} -} -void g(C c, int i) { - c.f(i); -} -void test(C c) { - g(c, null); -} -'''; - - // Default behavior is to add nullability at the call site. Rationale: this - // is correct in the common case where the generic parameter represents the - // type of an item in a container. Also, if there are many callers that are - // largely independent, adding nullability to the callee would likely - // propagate to a field in the class, and thence (via return values of other - // methods) to most users of the class. Whereas if we add nullability at - // the call site it's possible that other call sites won't need it. - var expected = ''' -class C { - void f(T t) {} -} -void g(C c, int? i) { - c.f(i); -} -void test(C c) { - g(c, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_generic_contravariant_inward_function() async { - var content = ''' -T f(T t) => t; -int g(int x) => f(x); -void h() { - g(null); -} -'''; - - // As with the generic class case (see - // [test_data_flow_generic_contravariant_inward_function]), we favor adding - // nullability at the call site, so that other uses of `f` don't necessarily - // see a nullable return value. - var expected = ''' -T f(T t) => t; -int? g(int? x) => f(x); -void h() { - g(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_data_flow_generic_contravariant_inward_using_core_class() async { - var content = ''' -void f(List x, int i) { - x.add(i); -} -void test(List x) { - f(x, null); -} -'''; - var expected = ''' -void f(List x, int? i) { - x.add(i); -} -void test(List x) { - f(x, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_generic_covariant_outward() async { - var content = ''' -class C { - T getValue() => null; -} -int f(C x) => x.getValue(); -'''; - var expected = ''' -class C { - T? getValue() => null; -} -int? f(C x) => x.getValue(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_generic_covariant_substituted() async { - var content = ''' -abstract class C { - T getValue(); -} -int f(C x) => x.getValue(); -'''; - var expected = ''' -abstract class C { - T getValue(); -} -int? f(C x) => x.getValue(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_indexed_get_index_value() async { - var content = ''' -class C { - int operator[](int i) => 1; -} -int f(C c) => c[null]; -'''; - var expected = ''' -class C { - int operator[](int? i) => 1; -} -int f(C c) => c[null]; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_indexed_get_value() async { - var content = ''' -class C { - int operator[](int i) => null; -} -int _f(C c) => c[0]; -'''; - var expected = ''' -class C { - int? operator[](int? i) => null; -} -int? _f(C c) => c[0]; -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): doesn't check anything, arguments are nullable by default. - Future test_data_flow_indexed_set_index_value() async { - var content = ''' -class C { - void operator[]=(int i, int j) {} -} -void _f(C c) { - c[null] = 0; -} -'''; - var expected = ''' -class C { - void operator[]=(int? i, int? j) {} -} -void _f(C c) { - c[null] = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): doesn't check anything, arguments are nullable by default. - Future test_data_flow_indexed_set_index_value_in_cascade() async { - var content = ''' -class C { - void operator[]=(int i, int j) {} -} -void _f(C c) { - c..[null] = 0; -} -'''; - var expected = ''' -class C { - void operator[]=(int? i, int? j) {} -} -void _f(C c) { - c..[null] = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): doesn't check anything, arguments are nullable by default. - Future test_data_flow_indexed_set_value() async { - var content = ''' -class C { - void operator[]=(int i, int j) {} -} -void _f(C c) { - c[0] = null; -} -'''; - var expected = ''' -class C { - void operator[]=(int? i, int? j) {} -} -void _f(C c) { - c[0] = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_inward() async { - var content = ''' -int f(int i) => 0; -int g(int i) => f(i); -void test() { - g(null); -} -'''; - - var expected = ''' -int f(int? i) => 0; -int g(int? i) => f(i); -void test() { - g(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_inward_missing_type() async { - var content = ''' -int f(int i) => 0; -int g(i) => f(i); // TODO(danrubel): suggest type -void test() { - g(null); -} -'''; - - var expected = ''' -int f(int? i) => 0; -int g(i) => f(i); // TODO(danrubel): suggest type -void test() { - g(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_local_declaration() async { - var content = ''' -void f(int i) { - int j = i; -} -main() { - f(null); -} -'''; - var expected = ''' -void f(int? i) { - int? j = i; -} -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_local_reference() async { - var content = ''' -void f(int i) {} -void g(int i) { - int j = i; - f(i); -} -main() { - g(null); -} -'''; - var expected = ''' -void f(int? i) {} -void g(int? i) { - int? j = i; - f(i); -} -main() { - g(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_method_call_in_cascade() async { - var content = ''' -class C { - void m(int x) {} -} -void f(C c) { - c..m(null); -} -'''; - var expected = ''' -class C { - void m(int? x) {} -} -void f(C c) { - c..m(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_outward() async { - var content = ''' -int _f(int i) => null; -int _g(int i) => _f(i); -'''; - - var expected = ''' -int? _f(int i) => null; -int? _g(int i) => _f(i); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_data_flow_outward_missing_type() async { - var content = ''' -_f(int i) => null; // TODO(danrubel): suggest type -int _g(int i) => _f(i); -'''; - - var expected = ''' -_f(int i) => null; // TODO(danrubel): suggest type -int? _g(int i) => _f(i); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_definitely_assigned_value() async { - var content = ''' -String f(bool b) { - String s; - if (b) { - s = 'true'; - } else { - s = 'false'; - } - return s; -} -'''; - var expected = ''' -String f(bool b) { - String s; - if (b) { - s = 'true'; - } else { - s = 'false'; - } - return s; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): we don't discard anymore, remove? - Future test_discard_simple_condition_keep_else() async { - var content = ''' -int f(int i) { - if (i == null) { - return null; - } else { - return i + 1; - } -} -'''; - - var expected = ''' -int? f(int? i) { - if (i == null) { - return null; - } else { - return i + 1; - } -} -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - // TODO(yanok): we don't discard anymore, remove? - Future test_discard_simple_condition_keep_then() async { - var content = ''' -int f(int i) { - if (i != null) { - return i + 1; - } else { - return null; - } -} -'''; - - var expected = ''' -int? f(int? i) { - if (i != null) { - return i + 1; - } else { - return null; - } -} -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_do_not_add_question_to_null_type() async { - var content = ''' -Null f() => null; -'''; - var expected = ''' -Null f() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_do_not_propagate_non_null_intent_into_callback() async { - var content = ''' -void f(int/*!*/ Function(int) callback) { - callback(null); -} -int g(int x) => x; -void test() { - f(g); -} -'''; - // Even though `g` is passed to `f`'s `callback` parameter, non-null intent - // is not allowed to propagate backward from the return type of `callback` - // to the return type of `g`, because `g` might be used elsewhere in a - // context where it's important for its return type to be nullable. So no - // null check is added to `g`, and instead a cast (which is guaranteed to - // fail) is added at the site of the call to `f`. - // - // Note: https://github.com/dart-lang/sdk/issues/40471 tracks the fact that - // we ought to alert the user to the presence of such casts. - var expected = ''' -void f(int Function(int?) callback) { - callback(null); -} -int? g(int? x) => x; -void test() { - f(g as int Function(int?)); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_do_not_surround_named_expression() async { - var content = ''' -void f(int/*?*/ x, int/*?*/ y) { - g(named: [x, y]); -} -g({List/*!*/ named}) {} -'''; - var expected = ''' -void f(int? x, int? y) { - g(named: [x!, y!]); -} -g({required List named}) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_downcast_dynamic_function_to_functionType() async { - var content = ''' -void _f(Function a) { - int Function(String y) f1 = a; - Function b = null; - int Function(String y) f2 = b; -} -'''; - // Don't assume any new nullabilities, but keep known nullabilities. - var expected = ''' -void _f(Function a) { - int Function(String y) f1 = a as int Function(String); - Function? b = null; - int Function(String y)? f2 = b as int Function(String)?; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_downcast_dynamic_to_functionType() async { - var content = ''' -void _f(dynamic a) { - int Function(String y) f1 = a; - dynamic b = null; - int Function(String y) f2 = b; -} -'''; - // Don't assume any new nullabilities, but keep known nullabilities. - var expected = ''' -void _f(dynamic a) { - int Function(String y) f1 = a; - dynamic b = null; - int Function(String y)? f2 = b; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_downcast_dynamic_type_argument() async { - // This pattern is common and seems to have this as a best migration. It is - // less clear, but plausible, that this holds for other types of type - // parameter downcasts. - var content = ''' -List _f(List a) => a; -void main() { - _f([null]); -} -'''; - - var expected = ''' -List _f(List a) => a as List; -void main() { - _f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @failingTest - Future test_downcast_not_widest_type_type_parameters() async { - // Fails because a hard assignment from List to List - // doesn't create a hard edge from 1 to 2. Perhaps this is correct. In this - // example it seems incorrect. - var content = ''' -void f(dynamic a) { - List hardToNonNullNonNull = a; - List hardToNullNonNull = a; - List hardToNonNullNull = a; - List/*!*/ nonNullNonNull; - List/*!*/ nullNonNull; - List/*?*/ nonNullNull; - nonNullNonNull = hardToNonNullNonNull - nonNullNull = hardToNonNullNull - nullNonNull = hardToNullNonNull -} -'''; - var expected = ''' -void f(dynamic a) { - List hardToNonNullNonNull = a; - List hardToNullNonNull = a; - List? hardToNonNullNull = a; - List nonNullNonNull; - List nullNonNull; - List? nonNullNull; - nonNullNonNull = hardToNonNullNonNull - nonNullNull = hardToNonNullNull - nullNonNull = hardToNullNonNull -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_downcast_to_null() async { - // This probably doesn't arise too often for real-world code, since it is - // most likely a mistake. Still, we want to make sure we don't crash and - // fail at compile-time instead. - var content = ''' -test() { - var x = List.filled(3, null); - x[0] = 1; -} -'''; - var expected = ''' -test() { - var x = List.filled(3, null); - x[0] = 1; -} -'''; - // Note: using allowErrors=true because passing `1` where `Null` is - // expected is an error. - await _checkSingleFileChanges(content, expected, allowErrors: true); - } - - Future test_downcast_type_argument_preserve_nullability() async { - // There are no examples in front of us yet where anyone downcasts a type - // with a nullable type parameter. This is maybe correct, maybe not, and it - // unblocks us to find out which at a later point in time. - var content = ''' -List _f(Iterable a) => a; -void main() { - _f([null]); -} -'''; - - var expected = ''' -List _f(Iterable a) => a as List; -void main() { - _f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @failingTest - Future test_downcast_widest_type_from_related_type_parameters() async { - var content = ''' -List f(Iterable a) => a; -'''; - var expected = ''' -List f(Iterable a) => a; -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39368') - Future test_downcast_widest_type_from_top_type_parameters() async { - var content = ''' -List f1(dynamic a) => a; -List f2(Object b) => b; -'''; - // Note: even though the type `dynamic` permits `null`, the migration engine - // sees that there is no code path that could cause `f1` to be passed a null - // value, so it leaves its return type as non-nullable. - var expected = ''' -List f1(dynamic a) => a; -List f2(Object b) => b; -'''; - await _checkSingleFileChanges(content, expected); - } - - @failingTest - Future - test_downcast_widest_type_from_unrelated_type_parameters() async { - var content = ''' -abstract class C implements List {} -C f(List a) => a; -'''; - var expected = ''' -abstract class C implements List {} -C f(List a) => a; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_dynamic_dispatch_to_object_method() async { - var content = ''' -String f(dynamic x) => x.toString(); -'''; - var expected = ''' -String f(dynamic x) => x.toString(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_dynamic_method_call() async { - var content = ''' -class C { - int g(int i) => i; -} -int f(bool b, dynamic d) { - if (b) return 0; - return d.g(null); -} -main() { - f(true, null); - f(false, C()); -} -'''; - // TODO(yanok): this part is not tested anymore, since now g's argument - // is nullable by default. - // `d.g(null)` is a dynamic call, so we can't tell that it will target `C.g` - // at runtime. So we can't figure out that we need to make g's argument and - // return types nullable. - // - // We do, however, make f's return type nullable, since there is no way of - // knowing whether a dynamic call will return `null`. - var expected = ''' -class C { - int? g(int? i) => i; -} -int? f(bool b, dynamic d) { - if (b) return 0; - return d.g(null); -} -main() { - f(true, null); - f(false, C()); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_dynamic_property_access() async { - var content = ''' -class C { - int get g => 0; -} -int f(bool b, dynamic d) { - if (b) return 0; - return d.g; -} -main() { - f(true, null); - f(false, C()); -} -'''; - var expected = ''' -class C { - int get g => 0; -} -int? f(bool b, dynamic d) { - if (b) return 0; - return d.g; -} -main() { - f(true, null); - f(false, C()); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_dynamic_toString() async { - var content = ''' -String f(dynamic x) => x.toString(); -'''; - var expected = ''' -String f(dynamic x) => x.toString(); -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40174') - Future test_eliminate_dead_if_inside_for_element() async { - var content = ''' -List _f(List xs) => [for(var x in xs) if (x == null) 1]; -'''; - var expected = ''' -List _f(List xs) => []; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_enum() async { - var content = ''' -enum E { - value -} - -E f() => E.value; -int g() => f().index; - -void h() { - for(var value in E.values) {} - E.values.forEach((value) {}); - - f().toString(); - f().runtimeType; - f().hashCode; - f().noSuchMethod(throw ''); - f() == f(); -} -'''; - var expected = ''' -enum E { - value -} - -E f() => E.value; -int g() => f().index; - -void h() { - for(var value in E.values) {} - E.values.forEach((value) {}); - - f().toString(); - f().runtimeType; - f().hashCode; - f().noSuchMethod(throw ''); - f() == f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_exact_nullability_counterexample() async { - var content = ''' -void f(List x) { - x.add(1); -} -void g() { - f([null]); -} -void h(List x) { - f(x); -} -'''; - // The `null` in `g` causes `f`'s `x` argument to have type `List`. - // Even though `f` calls a method that uses `List`'s type parameter - // contravariantly (the `add` method), that is not sufficient to cause exact - // nullability propagation, since value passed to `add` has a - // non-nullable type. So nullability is *not* propagated back to `h`. - var expected = ''' -void f(List x) { - x.add(1); -} -void g() { - f([null]); -} -void h(List x) { - f(x); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_exact_nullability_doesnt_affect_function_args() async { - // Test attempting to create a bug from #40625. Currently passes, but if it - // breaks, that bug may need to be reopened. - var content = ''' -class C { - int Function(T) f; -} -void main(dynamic d) { - C c = d; - int Function(String) f1 = c.f; // should not have a nullable arg - c.f(null); // exact nullability induced here -} -'''; - var expected = ''' -class C { - int Function(T)? f; -} -void main(dynamic d) { - C c = d; - int Function(String)? f1 = c.f; // should not have a nullable arg - c.f!(null); // exact nullability induced here -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_exact_nullability_doesnt_affect_function_returns() async { - // Test attempting to create a bug from #40625. Currently passes, but if it - // breaks, that bug may need to be reopened. - var content = ''' -class C { - T Function(String) f; -} -int Function(String) f1; // should not have a nullable return -void main(dynamic d) { - C c = d; - c.f = f1; - c.f = (_) => null; // exact nullability induced here -} -'''; - var expected = ''' -class C { - T Function(String)? f; -} -int Function(String)? f1; // should not have a nullable return -void main(dynamic d) { - C c = d; - c.f = f1; - c.f = (_) => null; // exact nullability induced here -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_exact_nullability_doesnt_affect_typedef_args() async { - // Test attempting to create a bug from #40625. Currently passes, but if it - // breaks, that bug may need to be reopened. - var content = ''' -typedef F = int Function(T); -F f1; - -void main() { - f1(null); // induce exact nullability - int Function(String) f2 = f1; // shouldn't have a nullable arg -} -'''; - var expected = ''' -typedef F = int Function(T); -F? f1; - -void main() { - f1!(null); // induce exact nullability - int Function(String)? f2 = f1; // shouldn't have a nullable arg -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_exact_nullability_doesnt_affect_typedef_returns() async { - // Test attempting to create a bug from #40625. Currently passes, but if it - // breaks, that bug may need to be reopened. - var content = ''' -typedef F = T Function(String); -int Function(String) f1; // should not have a nullable return -void main() { - F f2 = f1; - f2 = (_) => null; // exact nullability induced here -} -'''; - var expected = ''' -typedef F = T Function(String); -int Function(String)? f1; // should not have a nullable return -void main() { - F? f2 = f1; - f2 = (_) => null; // exact nullability induced here -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/41409') - Future test_exact_nullability_in_nested_list() async { - var content = ''' -f(List y) { - var x = >[]; - x.add(y); -} -'''; - var expected = ''' -f(List y) { - var x = >[]; - x.add(y); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_explicit_nullable_overrides_hard_edge() async { - var content = ''' -int f(int/*?*/ i) => i + 1; -'''; - var expected = ''' -int f(int? i) => i! + 1; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_expression_bang_hint() async { - var content = ''' -int f(int/*?*/ i) => i/*!*/; -'''; - var expected = ''' -int f(int? i) => i!; -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_expression_bang_hint_in_as() async { - var content = ''' -int f(num/*?*/ i) => i as int/*!*/; -'''; - var expected = ''' -int f(num? i) => i as int; -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/41788') - Future test_expression_bang_hint_in_as_wrapped() async { - var content = ''' -int f(num/*?*/ i) => (i as int)/*!*/; -'''; - var expected = ''' -int f(num? i) => (i as int?)!; -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_expression_bang_hint_unnecessary() async { - var content = ''' -int/*?*/ f(int/*?*/ i) => i/*!*/; -'''; - // The user requested a null check so we should add it even if it's not - // required to avoid compile errors. - var expected = ''' -int? f(int? i) => i!; -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_expression_bang_hint_unnecessary_literal() async { - var content = 'int/*?*/ f() => 1/*!*/;'; - // The user requested a null check so we should add it even if it's not - // required to avoid compile errors. - var expected = 'int? f() => 1!;'; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_expression_bang_hint_with_cast() async { - var content = 'int f(Object/*?*/ o) => o/*!*/;'; - var expected = 'int f(Object? o) => o! as int;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_expression_nullable_cast_then_checked() async { - var content = ''' -int/*!*/ f(num/*?*/ i) => (i as int); -'''; - var expected = ''' -int f(num? i) => (i as int); -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/41788') - Future test_expression_wrapped_with_null_check_and_null_intent() async { - var content = ''' -int/*!*/ f(int/*?*/ i) => (i)/*!*/; -'''; - var expected = ''' -int f(int? i) => i!; -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_extension_complex() async { - var content = ''' -import 'already_migrated.dart'; -class D extends C {} -abstract class Foo { - D> get z; - List test() => z.x ?? []; -} -'''; - var alreadyMigrated = ''' -// @dart=2.12 -extension E on C { - T? get x => y; -} -class C { - U? y; -} -'''; - var expected = ''' -import 'already_migrated.dart'; -class D extends C {} -abstract class Foo { - D> get z; - List test() => z.x ?? []; -} -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/already_migrated.dart': alreadyMigrated - }); - } - - Future test_extension_extended_type_nullability_intent() async { - var content = ''' -extension E on C { - String foo() => this.bar(); -} - -class C { - String bar() => null; -} - -void test(C c, bool b) { - if (b) { - c.foo(); - } -} - -main() { - test(null, false); -} -'''; - // The call to `bar` from `foo` should be taken as a demonstration that the - // extension E is not intended to apply to nullable types, so the call to - // `foo` should be null checked. - var expected = ''' -extension E on C { - String? foo() => this.bar(); -} - -class C { - String? bar() => null; -} - -void test(C? c, bool b) { - if (b) { - c!.foo(); - } -} - -main() { - test(null, false); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_non_nullable_binary() async { - var content = ''' -class C {} -extension E on C/*!*/ { - void operator+(int other) {} -} -void f(C c, bool b) { - if (b) { - c + 0; - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on C { - void operator+(int? other) {} -} -void f(C? c, bool b) { - if (b) { - c! + 0; - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_non_nullable_generic() async { - var content = ''' -class C {} -extension E on T/*!*/ { - void m() {} -} -void f(C c, bool b) { - if (b) { - c.m(); - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on T { - void m() {} -} -void f(C? c, bool b) { - if (b) { - c!.m(); - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_non_nullable_index() async { - var content = ''' -class C {} -extension E on C/*!*/ { - void operator[](int index) {} -} -void f(C c, bool b) { - if (b) { - c[0]; - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on C { - void operator[](int? index) {} -} -void f(C? c, bool b) { - if (b) { - c![0]; - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_non_nullable_method() async { - var content = ''' -class C {} -extension E on C/*!*/ { - void m() {} -} -void f(C c, bool b) { - if (b) { - c.m(); - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on C { - void m() {} -} -void f(C? c, bool b) { - if (b) { - c!.m(); - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_non_nullable_prefix() async { - var content = ''' -class C {} -extension E on C/*!*/ { - void operator-() {} -} -void f(C c, bool b) { - if (b) { - -c; - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on C { - void operator-() {} -} -void f(C? c, bool b) { - if (b) { - -c!; - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_nullable() async { - var content = ''' -class C {} -extension E on C/*?*/ { - void m() {} -} -void f(C c, bool b) { - if (b) { - c.m(); - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on C? { - void m() {} -} -void f(C? c, bool b) { - if (b) { - c.m(); - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_nullable_generic() async { - var content = ''' -class C {} -extension E on T/*!*/ { - void m() {} -} -void f(C c, bool b) { - if (b) { - c.m(); - } -} -void g() => f(null, false); -'''; - var expected = ''' -class C {} -extension E on T { - void m() {} -} -void f(C? c, bool b) { - if (b) { - c.m(); - } -} -void g() => f(null, false); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_null_check_target() async { - var content = ''' -extension E on int/*!*/ { - int get plusOne => this + 1; -} -int f(int/*?*/ x) => x.plusOne; -'''; - var expected = ''' -extension E on int { - int get plusOne => this + 1; -} -int f(int? x) => x!.plusOne; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_nullable_target() async { - var content = ''' -extension E on int { - int get one => 1; -} -int f(int/*?*/ x) => x.one; -'''; - var expected = ''' -extension E on int? { - int get one => 1; -} -int f(int? x) => x.one; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_nullableOnType_addsNullCheckToThis() async { - var content = ''' -extension E on String /*?*/ { - void m() => this.length; -} -'''; - var expected = ''' -extension E on String? { - void m() => this!.length; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_nullableOnType_typeArgument() async { - var content = ''' -extension E on List { - void m() {} -} -void _f(List list) => list.m(); -void g() => _f([null]); -'''; - var expected = ''' -extension E on List { - void m() {} -} -void _f(List list) => list.m(); -void g() => _f([null]); -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40023') - Future test_extension_nullableOnType_typeVariable() async { - var content = ''' -extension E on List { - void m() {} -} -void f(List list) => list.m(); -void g() => f([null]); -'''; - var expected = ''' -extension E on List { - void m() {} -} -void f(List list) => list.m(); -void g() => f([null]); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_nullableOnType_viaExplicitInvocation() async { - var content = ''' -class C {} -extension E on C { - void m() {} -} -void f() => E(null).m(); -'''; - var expected = ''' -class C {} -extension E on C? { - void m() {} -} -void f() => E(null).m(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_nullableOnType_viaImplicitInvocation() async { - var content = ''' -class C {} -extension E on C { - void m() {} -} -void f(C c) => c.m(); -void g() => f(null); -'''; - var expected = ''' -class C {} -extension E on C? { - void m() {} -} -void f(C? c) => c.m(); -void g() => f(null); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_on_generic_type() async { - var content = ''' -class C { - final T value; - C(this.value); -} -extension E on Future> { - Future get asyncValue async => (await this).value; -} -'''; - var expected = ''' -class C { - final T value; - C(this.value); -} -extension E on Future> { - Future get asyncValue async => (await this).value; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_on_type_param_implementation() async { - var content = ''' -abstract class C { - C _clone(); -} -extension Cloner on T { - T clone() => _clone() as T; -} -'''; - var expected = ''' -abstract class C { - C _clone(); -} -extension Cloner on T { - T clone() => _clone() as T; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_on_type_param_usage() async { - var content = ''' -abstract class C { - C _clone(); -} -extension Cloner on T { - T clone() => throw Exception(); -} -C _f(C c) => c.clone(); -'''; - var expected = ''' -abstract class C { - C _clone(); -} -extension Cloner on T { - T clone() => throw Exception(); -} -C _f(C c) => c.clone(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_on_type_substitution() async { - var content = ''' -extension E on T { - T get foo => this; -} -List _f(List x) => x.foo; -'''; - // To see that the return type of `f` must be `List`) into - // the extension's "on" type. - var expected = ''' -extension E on T { - T get foo => this; -} -List _f(List x) => x.foo; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_override() async { - var content = ''' -extension E on int { - int get plusOne => this + 1; -} -int _f(int x) => E(x).plusOne; -'''; - var expected = ''' -extension E on int { - int get plusOne => this + 1; -} -int _f(int x) => E(x).plusOne; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_override_null_check_target() async { - var content = ''' -extension E on int/*!*/ { - int get plusOne => this + 1; -} -int f(int/*?*/ x) => E(x).plusOne; -'''; - var expected = ''' -extension E on int { - int get plusOne => this + 1; -} -int f(int? x) => E(x!).plusOne; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_override_nullable_result_type() async { - var content = ''' -extension E on int { - int get nullValue => null; -} -int _f(int x) => E(x).nullValue; -'''; - var expected = ''' -extension E on int { - int? get nullValue => null; -} -int? _f(int x) => E(x).nullValue; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_override_nullable_target() async { - var content = ''' -extension E on int { - int get one => 1; -} -int f(int/*?*/ x) => E(x).one; -'''; - var expected = ''' -extension E on int? { - int get one => 1; -} -int f(int? x) => E(x).one; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_extension_use_can_imply_non_null_intent() async { - var content = ''' -extension E on T/*!*/ { - void foo() {} -} -f(int i) { - i.foo(); -} -g(bool b, int/*?*/ j) { - if (b) { - f(j); - } -} -'''; - // Since the extension declaration says `T extends Object/*!*/`, `i` will - // not be made nullable. - var expected = ''' -extension E on T { - void foo() {} -} -f(int i) { - i.foo(); -} -g(bool b, int? j) { - if (b) { - f(j!); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_constructor() async { - var content = ''' -class C { - external C(dynamic Function(dynamic) callback); - static Object g(Object Function(Object) callback) => C(callback); -} -'''; - var expected = ''' -class C { - external C(dynamic Function(dynamic)? callback); - static Object g(Object Function(Object?)? callback) => C(callback); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_function() async { - var content = ''' -external dynamic f(); -Object g() => f(); -'''; - var expected = ''' -external dynamic f(); -Object? g() => f(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_function_implicit_return() async { - var content = ''' -external f(); -Object g() => f(); -'''; - var expected = ''' -external f(); -Object? g() => f(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_function_implicit_variance() async { - var content = ''' -external void f(callback(x)); -void g(Object Function(Object) callback) => f(callback); -'''; - var expected = ''' -external void f(callback(x)?); -void g(Object Function(Object?)? callback) => f(callback); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_function_implicit_variance_complex() async { - var content = ''' -external void f(callback(x())); -void g(Object Function(Object Function()) callback) => f(callback); -'''; - var expected = ''' -external void f(callback(x())?); -void g(Object Function(Object? Function())? callback) => f(callback); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_function_variance() async { - var content = ''' -external void f(dynamic Function(dynamic) callback); -void g(Object Function(Object) callback) => f(callback); -'''; - var expected = ''' -external void f(dynamic Function(dynamic)? callback); -void g(Object Function(Object?)? callback) => f(callback); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_method() async { - var content = ''' -class C { - external dynamic f(); - Object g() => f(); -} -'''; - var expected = ''' -class C { - external dynamic f(); - Object? g() => f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_external_method_implicit() async { - var content = ''' -class C { - external f(); - Object g() => f(); -} -'''; - var expected = ''' -class C { - external f(); - Object? g() => f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_final_uninitialized_used() async { - var content = ''' -class C { - final String s; - - f() { - g(s); - } -} -g(String /*!*/ s) {} -'''; - var expected = ''' -class C { - late final String s; - - f() { - g(s); - } -} -g(String s) {} -'''; - // Note: using allowErrors=true because an uninitialized field is an error - await _checkSingleFileChanges(content, expected, allowErrors: true); - } - - Future test_field_formal_param_typed() async { - var content = ''' -class C { - int i; - C(int this.i); -} -main() { - C(null); -} -'''; - var expected = ''' -class C { - int? i; - C(int? this.i); -} -main() { - C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_formal_param_typed_non_nullable() async { - var content = ''' -class C { - int/*!*/ i; - C(int this.i); -} -void f(int i, bool b) { - if (b) { - C(i); - } -} -main() { - f(null, false); -} -'''; - var expected = ''' -class C { - int i; - C(int this.i); -} -void f(int? i, bool b) { - if (b) { - C(i!); - } -} -main() { - f(null, false); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_formal_param_untyped() async { - var content = ''' -class C { - int i; - C(this.i); -} -main() { - C(null); -} -'''; - var expected = ''' -class C { - int? i; - C(this.i); -} -main() { - C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_formal_parameters_do_not_promote() async { - var content = ''' -class A {} - -class B extends A {} - -class C extends A {} - -abstract class D { - final A x; - D(this.x) { - if (x is B) { - visitB(x); - } else { - visitC(x as C); - } - } - - void visitB(B b); - - void visitC(C c); -} -'''; - var expected = ''' -class A {} - -class B extends A {} - -class C extends A {} - -abstract class D { - final A x; - D(this.x) { - if (x is B) { - visitB(x as B); - } else { - visitC(x as C); - } - } - - void visitB(B? b); - - void visitC(C? c); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initialized_at_declaration_site() async { - var content = ''' -class C { - int i = 0; - C(); -} -'''; - var expected = ''' -class C { - int i = 0; - C(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_field_initialized_at_declaration_site_no_constructor() async { - var content = ''' -class C { - int i = 0; -} -'''; - var expected = ''' -class C { - int i = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initialized_in_constructor() async { - var content = ''' -class C { - int i; - C() : i = 0; -} -'''; - var expected = ''' -class C { - int i; - C() : i = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_field_initialized_in_constructor_with_factories_and_redirects() async { - var content = ''' -class C { - int i; - C() : i = 0; - factory C.factoryConstructor() => C(); - factory C.factoryRedirect() = D; - C.redirect() : this(); -} -class D extends C {} -'''; - var expected = ''' -class C { - int i; - C() : i = 0; - factory C.factoryConstructor() => C(); - factory C.factoryRedirect() = D; - C.redirect() : this(); -} -class D extends C {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initializer_simple() async { - var content = ''' -class C { - int f; - C(int i) : f = i; -} -main() { - C(null); -} -'''; - var expected = ''' -class C { - int? f; - C(int? i) : f = i; -} -main() { - C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initializer_typed_list_literal() async { - var content = ''' -class C { - List f; - C() : f = [null]; -} -'''; - var expected = ''' -class C { - List f; - C() : f = [null]; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initializer_untyped_list_literal() async { - var content = ''' -class C { - List f; - C() : f = [null]; -} -'''; - var expected = ''' -class C { - List f; - C() : f = [null]; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initializer_untyped_map_literal() async { - var content = ''' -class C { - Map f; - C() : f = {"foo": null}; -} -'''; - var expected = ''' -class C { - Map f; - C() : f = {"foo": null}; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_initializer_untyped_set_literal() async { - var content = ''' -class C { - Set f; - C() : f = {null}; -} -'''; - var expected = ''' -class C { - Set f; - C() : f = {null}; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_not_initialized() async { - var content = ''' -class C { - int i; - C(); -} -'''; - var expected = ''' -class C { - int? i; - C(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_not_initialized_no_constructor() async { - var content = ''' -class C { - int i; -} -'''; - var expected = ''' -class C { - int? i; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_overrides_field_in_mixin() async { - var content = ''' -class C extends Object with M { - int x; -} - -mixin M { - int x; -} -'''; - var expected = ''' -class C extends Object with M { - int? x; -} - -mixin M { - int? x; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_overrides_getter() async { - var content = ''' -abstract class C { - int get i; -} -class D implements C { - @override - final int i; - D._() : i = computeI(); -} -int computeI() => null; -'''; - var expected = ''' -abstract class C { - int? get i; -} -class D implements C { - @override - final int? i; - D._() : i = computeI(); -} -int? computeI() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_type_inferred() async { - var content = ''' -int f() => null; -class C { - var x = 1; - void g() { - x = f(); - } -} -'''; - // The type of x is inferred as non-nullable from its initializer, but we - // try to assign a nullable value to it. So an explicit type must be added. - var expected = ''' -int? f() => null; -class C { - int? x = 1; - void g() { - x = f(); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_uninitialized_used() async { - var content = ''' -class C { - String s; - - f() { - g(s); - } -} -g(String /*!*/ s) {} -'''; - var expected = ''' -class C { - late String s; - - f() { - g(s); - } -} -g(String s) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_field_uninitialized_used_hint() async { - var content = ''' -class C { - String /*?*/ s; - - f() { - g(s); - } -} -g(String /*!*/ s) {} -'''; - var expected = ''' -class C { - String? s; - - f() { - g(s!); - } -} -g(String s) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_firstWhere_complex_target() async { - // See https://github.com/dart-lang/sdk/issues/43956 - var content = ''' -Iterable allMatches(String str) => 'x'.allMatches(str); - -Match matchAsPrefix(String str, [int start = 0]) { - return allMatches(str) - .firstWhere((match) => match.start == start, orElse: () => null); -} -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension; - -Iterable allMatches(String str) => 'x'.allMatches(str); - -Match? matchAsPrefix(String str, [int start = 0]) { - return allMatches(str) - .firstWhereOrNull((match) => match.start == start); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_firstWhere_non_nullable() async { - var content = ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_firstWhere_non_nullable_with_cast() async { - var content = ''' -int firstNonZero(Iterable x) - => x.firstWhere((x) => x != 0, orElse: () => null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstNonZero(Iterable x) - => x.firstWhereOrNull((x) => x != 0) as int?; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_firstWhere_non_nullable_with_non_null_assertion() async { - var content = ''' -int/*!*/ firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension; - -int firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven)!; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_firstWhere_nullable() async { - var content = ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -f() => firstEven([null]); -'''; - var expected = ''' -int? firstEven(Iterable x) - => x.firstWhere((x) => x!.isEven, orElse: () => null); -f() => firstEven([null]); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_firstWhere_nullable_with_cast() async { - var content = ''' -int firstNonZero(Iterable x) - => x.firstWhere((x) => x != 0, orElse: () => null); -f() => firstNonZero([null]); -'''; - var expected = ''' -int? firstNonZero(Iterable x) - => x.firstWhere((x) => x != 0, orElse: () => null) as int?; -f() => firstNonZero([null]); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_flow_analysis_complex() async { - var content = ''' -int f(int x) { - while (x == null) { - x = g(x); - } - return x; -} -int g(int x) => x == null ? 1 : null; -main() { - f(null); -} -'''; - // Flow analysis can tell that the loop only exits if x is non-null, so the - // return type of `f` can remain `int`, and no null check is needed. - var expected = ''' -int f(int? x) { - while (x == null) { - x = g(x); - } - return x; -} -int? g(int? x) => x == null ? 1 : null; -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_flow_analysis_simple() async { - var content = ''' -int f(int x) { - if (x == null) { - return 0; - } else { - return x; - } -} -main() { - f(null); -} -'''; - var expected = ''' -int f(int? x) { - if (x == null) { - return 0; - } else { - return x; - } -} -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_for_each_basic() async { - var content = ''' -void f(List l) { - for (var x in l) { - g(x); - } -} -void g(int x) {} -main() { - f([null]); -} -'''; - var expected = ''' -void f(List l) { - for (var x in l) { - g(x); - } -} -void g(int? x) {} -main() { - f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_for_each_variable_initialized() async { - var content = ''' -int sum(List list) { - int total = 0; - for (var i in list) { - total = total + i; - } - return total; -} -'''; - var expected = ''' -int sum(List list) { - int total = 0; - for (var i in list) { - total = total + i; - } - return total; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_expression() async { - var content = ''' -int f(int i) { - var g = (int j) => i; - return g(i); -} -main() { - f(null); -} -'''; - var expected = ''' -int? f(int? i) { - var g = (int? j) => i; - return g(i); -} -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_expression_invocation() async { - var content = ''' -abstract class C { - void Function(int) f(); - int/*?*/ Function() g(); -} -int test(C c) { - c.f()(null); - return c.g()(); -} -'''; - var expected = ''' -abstract class C { - void Function(int?) f(); - int? Function() g(); -} -int? test(C c) { - c.f()(null); - return c.g()(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_expression_invocation_via_getter() async { - var content = ''' -abstract class C { - void Function(int) get f; - int/*?*/ Function() get g; -} -int test(C c) { - c.f(null); - return c.g(); -} -'''; - var expected = ''' -abstract class C { - void Function(int?) get f; - int? Function() get g; -} -int? test(C c) { - c.f(null); - return c.g(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_expression_never() async { - var content = ''' -typedef CB = int Function(Object o); -abstract class C { - void m(CB cb); -} -void f(C c) { - c.m((_) => throw Exception()); -} -'''; - var expected = ''' -typedef CB = int Function(Object o); -abstract class C { - void m(CB? cb); -} -void f(C c) { - c.m((_) => throw Exception()); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_expression_return() async { - var content = ''' -void test({String foo}) async { - var f = () { - return "hello"; - }; - - foo.length; -} -'''; - var expected = ''' -void test({required String foo}) async { - var f = () { - return "hello"; - }; - - foo.length; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_typed_field_formal_param() async { - var content = ''' -class C { - void Function(int) f; - C(void this.f(int i)); -} -main() { - C(null); -} -'''; - var expected = ''' -class C { - void Function(int)? f; - C(void this.f(int i)?); -} -main() { - C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_typed_field_formal_param_accepts_hint() async { - var content = ''' -class C { - void Function(int) f; - C(void this.f(int i) /*?*/); -} -'''; - var expected = ''' -class C { - void Function(int)? f; - C(void this.f(int i)?); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_typed_field_formal_param_inner_types() async { - var content = ''' -class C { - int Function(int) f; - C(int this.f(int i)); -} -int g(int i) => i; -int test(int i) => C(g).f(i); -main() { - test(null); -} -'''; - var expected = ''' -class C { - int? Function(int?) f; - C(int? this.f(int? i)); -} -int? g(int? i) => i; -int? test(int? i) => C(g).f(i); -main() { - test(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_typed_formal_param() async { - var content = ''' -void f(g()) {} -void main() { - f(null); -} -'''; - var expected = ''' -void f(g()?) {} -void main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_typed_formal_param_accepts_hint() async { - var content = ''' -void f(g() /*?*/) {} -'''; - var expected = ''' -void f(g()?) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_function_typed_formal_param_inner_types() async { - var content = ''' -int f(int callback(int i), int j) => callback(j); -int g(int i) => i; -int test(int i) => f(g, i); -main() { - test(null); -} -'''; - var expected = ''' -int? f(int? callback(int? i), int? j) => callback(j); -int? g(int? i) => i; -int? test(int? i) => f(g, i); -main() { - test(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_future_nullability_mismatch() async { - var content = ''' -String foo; - -Future getNullableFoo() async { - return foo; -} - -Future getFoo() { - return getNullableFoo(); -} -'''; - var expected = ''' -String? foo; - -Future getNullableFoo() async { - return foo; -} - -Future getFoo() { - return getNullableFoo().then((value) => value!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): the cast of `foi2` looks wrong, I think it should be `int?` - // instead, but `DOWN(int?, FutureOr)` is `int` according to the spec. - Future test_future_or_t_downcast_to_t() async { - var content = ''' -import 'dart:async'; -void _f( - FutureOr foi1, - FutureOr foi2, - FutureOr/*?*/ foi3, - FutureOr/*?*/ foi4 -) { - int i1 = foi1; - int i2 = foi2; - int i3 = foi3; - int i4 = foi4; -} -'''; - var expected = ''' -import 'dart:async'; -void _f( - FutureOr foi1, - FutureOr foi2, - FutureOr? foi3, - FutureOr foi4 -) { - int i1 = foi1 as int; - int? i2 = foi2 as int; - int? i3 = foi3 as int?; - int? i4 = foi4 as int?; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_future_type_mismatch() async { - var content = ''' -Future> getNullableInts() async { - return [null]; -} - -Future> getInts() { - return getNullableInts(); -} -'''; - // TODO(paulberry): this is not a good migration. Really we should produce - // `getNullableInts().then((value) => value.cast());`. - var expected = ''' -Future> getNullableInts() async { - return [null]; -} - -Future> getInts() { - return getNullableInts().then((value) => value as List); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_generic_bound() async { - var content = ''' -abstract class C { - void f(); -} -void f(C s, C> i) { - s.f(); - i.f>(); -} -'''; - var expected = ''' -abstract class C { - void f(); -} -void f(C s, C> i) { - s.f(); - i.f>(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_generic_exact_propagation() async { - var content = ''' -class C { - List values; - C() : values = []; - void add(T t) => values.add(t); - T operator[](int i) => values[i]; -} -void f() { - C x = new C(); - g(x); -} -void g(C y) { - y.add(null); -} -'''; - var expected = ''' -class C { - List values; - C() : values = []; - void add(T t) => values.add(t); - T operator[](int i) => values[i]; -} -void f() { - C x = new C(); - g(x); -} -void g(C y) { - y.add(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_generic_exact_propagation_premigratedListClass() async { - var content = ''' -void f() { - List x = new List.empty(); - g(x); -} -void g(List y) { - y.add(null); -} -'''; - var expected = ''' -void f() { - List x = new List.empty(); - g(x); -} -void g(List y) { - y.add(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_generic_function_type_syntax_inferred_dynamic_return() async { - var content = ''' -abstract class C { - Function() f(); -} -Object g(C c) => c.f()(); -'''; - // Note: even though the type `dynamic` permits `null`, the migration engine - // sees that there is no code path that could cause `g` to return a null - // value, so it leaves its return type as `Object`, and there is an implicit - // downcast. - var expected = ''' -abstract class C { - Function() f(); -} -Object g(C c) => c.f()(); -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): this is the case I don't like. One would hope that usage - // in test would force comparison to be non-nullable. - Future - test_generic_typedef_respects_explicit_nullability_of_type_arg() async { - var content = ''' -class C { - final Comparator comparison; - C(int Function(int, int) comparison) : comparison = comparison; - void test() { - comparison(f(), f()); - } -} -int f() => null; -'''; - var expected = ''' -class C { - final Comparator? comparison; - C(int Function(int, int)? comparison) : comparison = comparison; - void test() { - comparison!(f()!, f()!); - } -} -int? f() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_genericType_noTypeArguments() async { - var content = ''' -void _f(C c) {} -class C {} -'''; - var expected = ''' -void _f(C c) {} -class C {} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39404') - Future test_genericType_noTypeArguments_use_bound() async { - var content = ''' -abstract class C { // (1) - void put(T t); - T get(); -} -Object f(C c) => c.get(); // (2) -void g(C c) { // (3) - c.put(null); // (4) -} -'''; - // (4) forces `...C...` at (3), which means (1) must be - // `...extends Object?`. Therefore (2) is equivalent to - // `...f(C c)...`, so the return type of `f` is `Object?`. - var expected = ''' -abstract class C { // (1) - void put(T t); - T get(); -} -Object? f(C c) => c.get(); // (2) -void g(C c) { // (3) - c.put(null); // (4) -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_getter_implicit_returnType_overrides_implicit_getter() async { - var content = ''' -class A { - final String s = "x"; -} -class C implements A { - get s => false ? "y" : null; -} -'''; - var expected = ''' -class A { - final String? s = "x"; -} -class C implements A { - get s => false ? "y" : null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_overrides_implicit_getter() async { - var content = ''' -class A { - final String s = "x"; -} -class C implements A { - String get s => false ? "y" : null; -} -'''; - var expected = ''' -class A { - final String? s = "x"; -} -class C implements A { - String? get s => false ? "y" : null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_overrides_implicit_getter_with_generics() async { - var content = ''' -class A { - final T value; - A(this.value); -} -class C implements A { - String get value => false ? "y" : null; -} -'''; - var expected = ''' -class A { - final T? value; - A(this.value); -} -class C implements A { - String? get value => false ? "y" : null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_getter_in_interface() async { - var content = ''' -class B { - int get x => null; -} -abstract class C implements B { - void set x(int value) {} -} -'''; - var expected = ''' -class B { - int? get x => null; -} -abstract class C implements B { - void set x(int? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_getter_in_interface_field() async { - var content = ''' -class B { - final int x = null; -} -abstract class C implements B { - void set x(int value) {} -} -'''; - var expected = ''' -class B { - final int? x = null; -} -abstract class C implements B { - void set x(int? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_getter_in_interfaces() async { - var content = ''' -class B1 { - int get x => null; -} -class B2 { - int get x => null; -} -abstract class C implements B1, B2 { - void set x(int value) {} -} -'''; - var expected = ''' -class B1 { - int? get x => null; -} -class B2 { - int? get x => null; -} -abstract class C implements B1, B2 { - void set x(int? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_getter_in_superclass() async { - var content = ''' -class B { - int get x => null; -} -class C extends B { - void set x(int value) {} -} -'''; - var expected = ''' -class B { - int? get x => null; -} -class C extends B { - void set x(int? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_getter_in_superclass_substituted() async { - var content = ''' -class B { - T get x => throw ''; -} -class C extends B> { - void set x(List value) {} -} -'''; - var expected = ''' -class B { - T get x => throw ''; -} -class C extends B> { - void set x(List value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_setter_in_interface() async { - var content = ''' -class B { - void set x(int value) {} -} -abstract class C implements B { - int get x => null; -} -'''; - var expected = ''' -class B { - void set x(int? value) {} -} -abstract class C implements B { - int? get x => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_setter_in_interface_field() async { - var content = ''' -class B { - int x; - B(this.x); -} -abstract class C implements B { - int get x => null; -} -'''; - var expected = ''' -class B { - int? x; - B(this.x); -} -abstract class C implements B { - int? get x => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_setter_in_interfaces() async { - var content = ''' -class B1 { - void set x(int value) {} -} -class B2 { - void set x(int value) {} -} -abstract class C implements B1, B2 { - int get x => null; -} -'''; - var expected = ''' -class B1 { - void set x(int? value) {} -} -class B2 { - void set x(int? value) {} -} -abstract class C implements B1, B2 { - int? get x => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_setter_in_superclass() async { - var content = ''' -class B { - void set x(int value) {} -} -class C extends B { - int get x => null; -} -'''; - var expected = ''' -class B { - void set x(int? value) {} -} -class C extends B { - int? get x => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_setter_in_superclass_substituted() async { - var content = ''' -class B { - void set x(T value) {} -} -class C extends B> { - List get x => [null]; -} -'''; - var expected = ''' -class B { - void set x(T value) {} -} -class C extends B> { - List get x => [null]; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_single_class() async { - var content = ''' -class C { - int get x => null; - void set x(int value) {} -} -'''; - var expected = ''' -class C { - int? get x => null; - void set x(int? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_single_class_generic() async { - var content = ''' -class C { - T get x => null; - void set x(T value) {} -} -'''; - var expected = ''' -class C { - T? get x => null; - void set x(T? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_static() async { - var content = ''' -class C { - static int get x => null; - static void set x(int value) {} -} -'''; - var expected = ''' -class C { - static int? get x => null; - static void set x(int? value) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_setter_top_level() async { - var content = ''' -int get x => null; -void set x(int value) {} -'''; - var expected = ''' -int? get x => null; -void set x(int? value) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_getter_topLevel() async { - var content = ''' -int get g => 0; -'''; - var expected = ''' -int get g => 0; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_hint_contradicts_exact_nullability() async { - var content = ''' -void f(List x) { - x.add(null); -} -void g() { - f([]); -} -'''; - // `f.x` needs to change to List to allow `null` to be added to the - // list. Ordinarily this would be propagated back to the explicit list in - // `g`, but we don't override the hint `/*!*/`. - // - // TODO(paulberry): we should probably issue some sort of warning to the - // user instead. - var expected = ''' -void f(List x) { - x.add(null); -} -void g() { - f([]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_ifStatement_nullCheck_noElse() async { - var content = ''' -int f(int x) { - if (x == null) return 0; - return x; -} -'''; - var expected = ''' -int f(int? x) { - if (x == null) return 0; - return x; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_implicit_parameter_type_override_does_not_union() async { - var content = ''' -abstract class A { - void f(int/*?*/ i); -} -abstract class B { - void f(int/*!*/ i); -} -class C implements A, B { - void f(i) {} -} -'''; - // Even though the parameter type of C.f is implicit, its nullability - // shouldn't be unioned with that of A and B, because that would - // unnecessarily force B.f's parameter type to be nullable. - var expected = ''' -abstract class A { - void f(int? i); -} -abstract class B { - void f(int i); -} -class C implements A, B { - void f(i) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_implicit_return_type_override_does_not_union() async { - var content = ''' -abstract class A { - int/*?*/ f(); -} -abstract class B { - int f(); -} -class C implements A, B { - f() => 0; -} -'''; - // Even though the return type of C.f is implicit, its nullability shouldn't - // be unioned with that of A and B, because that would unnecessarily force - // B.f's return type to be nullable. - var expected = ''' -abstract class A { - int? f(); -} -abstract class B { - int f(); -} -class C implements A, B { - f() => 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_implicit_tearoff_type_arguments() async { - var content = ''' -T f(T t) => t; -int Function(int) g() => f; -'''; - var expected = ''' -T f(T t) => t; -int Function(int) g() => f; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_implicit_type_parameter_bound_nullable() async { - var content = ''' -class C { - f(T t) { - Object o = t; - } -} -'''; - var expected = ''' -class C { - f(T t) { - Object? o = t; - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_infer_late_with_cascaded_usage() async { - var content = ''' -class A { - B b; -} -class B { - void f() {} - void g() {} -} -foo(A a) { - a.b..f()..g(); -} -bar(A a) { - a.b = B(); -} -'''; - var expected = ''' -class A { - late B b; -} -class B { - void f() {} - void g() {} -} -foo(A a) { - a.b..f()..g(); -} -bar(A a) { - a.b = B(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39376') - Future test_infer_required() async { - var content = ''' -void _f(bool b, {int x}) { - if (b) { - print(x + 1); - } -} -main() { - _f(true, x: 1); -} -'''; - var expected = ''' -void _f(bool b, {required int x}) { - if (b) { - print(x + 1); - } -} -main() { - _f(true, x: 1); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_inferred_method_return_type_non_nullable() async { - var content = ''' -class B { - int f() => 1; -} -class C extends B { - f() => 1; -} -int g(C c) => c.f(); -'''; - // B.f's return type is `int`. Since C.f's return type is inferred from - // B.f's, it has a return type of `int` too. Therefore g's return type - // must be `int`. - var expected = ''' -class B { - int f() => 1; -} -class C extends B { - f() => 1; -} -int g(C c) => c.f(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_insert_as_prefixed_type() async { - var content = ''' -import 'dart:async' as a; -Future _f(Object o) => o; -'''; - var expected = ''' -import 'dart:async' as a; -Future _f(Object o) => o as a.Future; -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40871') - Future test_insert_type_with_prefix() async { - var content = ''' -import 'dart:async' as a; -a.Future f(Object o) { - return o; -} -'''; - var expected = ''' -import 'dart:async' as a; -a.Future f(Object o) { - return o as a.Future; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/38469') - Future test_inserted_nodes_properly_wrapped() async { - addMetaPackage(); - var content = ''' -class C { - C operator+(C other) => null; -} -void f(C x, C y) { - C z = x + y; - assert(z != null); -} -'''; - var expected = ''' -class C { - C operator+(C other) => null; -} -void f(C x, C y) { - C z = (x + y)!; - assert(z != null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_instance_creation_generic() async { - var content = ''' -class C { - C(T t); -} -main() { - C c = C(null); -} -'''; - var expected = ''' -class C { - C(T t); -} -main() { - C c = C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_instance_creation_generic_explicit_nonNullable() async { - var content = ''' -class C { - C(T/*!*/ t); -} -test(int/*?*/ n) { - C c = C(n); -} -'''; - var expected = ''' -class C { - C(T t); -} -test(int? n) { - C c = C(n!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_instance_creation_generic_explicit_nonNullableParam() async { - var content = ''' -class C { - C(T/*!*/ t); -} -main() { - C c = C(null); -} -'''; - var expected = ''' -class C { - C(T t); -} -main() { - C c = C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_instance_creation_generic_implicit_nonNullable() async { - var content = ''' -class C { - C(T/*!*/ t); -} -test(int/*?*/ n) { - C c = C(n); -} -'''; - var expected = ''' -class C { - C(T t); -} -test(int? n) { - C c = C(n!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_instance_creation_generic_implicit_nonNullableParam() async { - var content = ''' -class C { - C(T/*!*/ t); -} -main() { - C c = C(null); -} -'''; - var expected = ''' -class C { - C(T t); -} -main() { - C c = C(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_instanceCreation_noTypeArguments_noParameters() async { - var content = ''' -void main() { - C c = C(); - c.length; -} -class C { - int get length => 0; -} -'''; - var expected = ''' -void main() { - C c = C(); - c.length; -} -class C { - int get length => 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_int_double_coercion() async { - var content = ''' -double f() => 0; -'''; - var expected = ''' -double f() => 0; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_is_promotion_implies_non_nullable() async { - var content = ''' -bool f(Object o) => o is int && o.isEven; -main() { - f(null); -} -'''; - var expected = ''' -bool f(Object? o) => o is int && o.isEven; -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_is_promotion_implies_non_nullable_generic() async { - var content = ''' -int f(T o) => o is List ? o.length : 0; -main() { - f(null); -} -'''; - var expected = ''' -int f(T o) => o is List ? o.length : 0; -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_isExpression_typeName_typeArguments() async { - var content = ''' -bool f(a) => a is List; -'''; - var expected = ''' -bool f(a) => a is List; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_isExpression_with_function_type() async { - var content = ''' -void _test(Function f) { - if (f is void Function()) { - f(); - } -} -'''; - var expected = ''' -void _test(Function f) { - if (f is void Function()) { - f(); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_issue_40181() async { - // This contrived example created an "exact nullable" type parameter bound - // which propagated back to *all* instantiations of that parameter. - var content = ''' -class B { - B([C t]); -} - -abstract class C { - void f(T t); -} - -class E { - final C _base; - E([C base]) : _base = base; - f(Object t) { - _base.f(t); - } -} - -void main() { - E e = E(); - e.f(null); -} -'''; - var expected = ''' -class B { - B([C? t]); -} - -abstract class C { - void f(T t); -} - -class E { - final C? _base; - E([C? base]) : _base = base; - f(Object? t) { - _base!.f(t); - } -} - -void main() { - E e = E(); - e.f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_issue_41397() async { - var content = ''' -void repro(){ - List l = []; - for(final dynamic e in l) { - final List a = (e['query'] as String).split('&'); - } -} -'''; - var expected = ''' -void repro(){ - List l = []; - for(final dynamic e in l) { - final List a = (e['query'] as String).split('&'); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_lastWhere_non_nullable() async { - var content = ''' -int lastEven(Iterable x) - => x.lastWhere((x) => x.isEven, orElse: () => null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? lastEven(Iterable x) - => x.lastWhereOrNull((x) => x.isEven); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_lastWhere_nullable() async { - var content = ''' -int lastEven(Iterable x) - => x.lastWhere((x) => x.isEven, orElse: () => null); -f() => lastEven([null]); -'''; - var expected = ''' -int? lastEven(Iterable x) - => x.lastWhere((x) => x!.isEven, orElse: () => null); -f() => lastEven([null]); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_final_hint_instance_field_without_constructor() async { - var content = ''' -class C { - /*late final*/ int x; - f() { - x = 1; - } - int g() => x; -} -'''; - var expected = ''' -class C { - late final int x; - f() { - x = 1; - } - int g() => x; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_final_hint_local_variable() async { - var content = ''' -int f(bool b1, bool b2) { - /*late final*/ int x; - if (b1) { - x = 1; - } - if (b2) { - return x; - } - return 0; -} -'''; - var expected = ''' -int f(bool b1, bool b2) { - late final int x; - if (b1) { - x = 1; - } - if (b2) { - return x; - } - return 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_final_hint_top_level_var() async { - var content = ''' -/*late final*/ int x; -f() { - x = 1; -} -int g() => x; -'''; - var expected = ''' -late final int x; -f() { - x = 1; -} -int g() => x; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_hint_followed_by_underscore() async { - var content = ''' -class _C {} -/*late*/ _C c; -'''; - var expected = ''' -class _C {} -late _C c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_hint_instance_field_with_constructor() async { - var content = ''' -class C { - C(); - /*late*/ int x; - f() { - x = 1; - } - int g() => x; -} -'''; - var expected = ''' -class C { - C(); - late int x; - f() { - x = 1; - } - int g() => x; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_hint_instance_field_without_constructor() async { - var content = ''' -class C { - /*late*/ int x; - f() { - x = 1; - } - int g() => x; -} -'''; - var expected = ''' -class C { - late int x; - f() { - x = 1; - } - int g() => x; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_hint_local_variable() async { - var content = ''' -int f(bool b1, bool b2) { - /*late*/ int x; - if (b1) { - x = 1; - } - if (b2) { - return x; - } - return 0; -} -'''; - var expected = ''' -int f(bool b1, bool b2) { - late int x; - if (b1) { - x = 1; - } - if (b2) { - return x; - } - return 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_hint_static_field() async { - var content = ''' -class C { - static /*late*/ int x; - f() { - x = 1; - } - int g() => x; -} -'''; - var expected = ''' -class C { - static late int x; - f() { - x = 1; - } - int g() => x; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_late_hint_top_level_var() async { - var content = ''' -/*late*/ int x; -f() { - x = 1; -} -int g() => x; -'''; - var expected = ''' -late int x; -f() { - x = 1; -} -int g() => x; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_leave_downcast_from_dynamic_implicit() async { - var content = 'int _f(dynamic n) => n;'; - var expected = 'int _f(dynamic n) => n;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_libraryWithParts() async { - var root = '$projectPath/lib'; - var path1 = convertPath('$root/lib.dart'); - var file1 = ''' -part 'src/foo/part.dart'; -'''; - var expected1 = ''' -part 'src/foo/part.dart'; -'''; - var path2 = convertPath('$root/src/foo/part.dart'); - var file2 = ''' -part of '../../lib.dart'; -class C { - static void m(C c) {} -} -'''; - var expected2 = ''' -part of '../../lib.dart'; -class C { - static void m(C? c) {} -} -'''; - await _checkMultipleFileChanges( - {path2: file2, path1: file1}, {path1: expected1, path2: expected2}); - } - - Future test_libraryWithParts_add_questions() async { - var root = '$projectPath/lib'; - var path1 = convertPath('$root/lib.dart'); - var file1 = ''' -part 'src/foo/part.dart'; - -int f() => null; -'''; - var expected1 = ''' -part 'src/foo/part.dart'; - -int? f() => null; -'''; - var path2 = convertPath('$root/src/foo/part.dart'); - var file2 = ''' -part of '../../lib.dart'; - -int g() => null; -'''; - var expected2 = ''' -part of '../../lib.dart'; - -int? g() => null; -'''; - await _checkMultipleFileChanges( - {path2: file2, path1: file1}, {path1: expected1, path2: expected2}); - } - - Future test_list_conditional_element() async { - var content = ''' -void _bar(List l) {} - -void _test({String foo}) { - _bar([if (foo != null) foo]); -} -'''; - var expected = ''' -void _bar(List l) {} - -void _test({String? foo}) { - _bar([if (foo != null) foo]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_literal_null_without_valid_migration() async { - var content = ''' -void f(int/*!*/ x) {} -void g() { - f(null); -} -'''; - var expected = ''' -void f(int x) {} -void g() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_literals_maintain_nullability() async { - // See #40590. Without exact nullability, this would migrate to - // `List list = [1, 2]`. While the function of exact nullability - // may change, this case should continue to work. - var content = r''' -void f() { - List list = [1, 2]; - list.add(null); - Set set_ = {1, 2}; - set_.add(null); - Map map = {1: 2}; - map[null] = null; -} -'''; - var expected = r''' -void f() { - List list = [1, 2]; - list.add(null); - Set set_ = {1, 2}; - set_.add(null); - Map map = {1: 2}; - map[null] = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_loadLibrary_call() async { - var testPath = convertPath('$testsPath/lib/test.dart'); - var otherPath = convertPath('$testsPath/lib/other.dart'); - var content = { - testPath: ''' -import 'other.dart' deferred as other; -Future f() => other.loadLibrary(); -''', - otherPath: '' - }; - var expected = { - testPath: ''' -import 'other.dart' deferred as other; -Future f() => other.loadLibrary(); -''', - otherPath: '' - }; - await _checkMultipleFileChanges(content, expected); - } - - Future test_loadLibrary_tearOff() async { - var testPath = convertPath('$testsPath/lib/test.dart'); - var otherPath = convertPath('$testsPath/lib/other.dart'); - var content = { - testPath: ''' -import 'other.dart' deferred as other; -Future Function() f() => other.loadLibrary; -''', - otherPath: '' - }; - var expected = { - testPath: ''' -import 'other.dart' deferred as other; -Future Function() f() => other.loadLibrary; -''', - otherPath: '' - }; - await _checkMultipleFileChanges(content, expected); - } - - Future test_local_function() async { - var content = ''' -int f(int i) { - int g(int j) => i; - return g(i); -} -main() { - f(null); -} -'''; - var expected = ''' -int? f(int? i) { - int? g(int? j) => i; - return g(i); -} -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_local_function_doesnt_assign() async { - var content = ''' -int f() { - int i; - g(int j) { - i = 1; - }; - ((int j) { - i = 1; - }); - return i + 1; -} -'''; - var expected = ''' -int f() { - late int i; - g(int j) { - i = 1; - }; - ((int j) { - i = 1; - }); - return i + 1; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_local_function_expression_inhibits_non_null_intent() async { - var content = ''' -void call(void Function() callback) { - callback(); -} -test(int i, int j) { - call(() { - i = j; - }); - print(i + 1); -} -main() { - test(null, 0); -} -'''; - // `print(i + 1)` does *not* demonstrate non-null intent for `i` because it - // is write captured by the local function expression, so it's not - // guaranteed that a null value of `i` on entry to the function will lead to - // an exception. - var expected = ''' -void call(void Function() callback) { - callback(); -} -test(int? i, int? j) { - call(() { - i = j; - }); - print(i! + 1); -} -main() { - test(null, 0); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_local_function_inhibits_non_null_intent() async { - var content = ''' -void call(void Function() callback) { - callback(); -} -test(int i, int j) { - void f() { - i = j; - } - call(f); - print(i + 1); -} -main() { - test(null, 0); -} -'''; - // `print(i + 1)` does *not* demonstrate non-null intent for `i` because it - // is write captured by the local function expression, so it's not - // guaranteed that a null value of `i` on entry to the function will lead to - // an exception. - var expected = ''' -void call(void Function() callback) { - callback(); -} -test(int? i, int? j) { - void f() { - i = j; - } - call(f); - print(i! + 1); -} -main() { - test(null, 0); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_local_function_return() async { - var content = ''' -void test({String foo}) async { - String f() { - return "hello"; - } - - foo.length; -} -'''; - var expected = ''' -void test({required String foo}) async { - String f() { - return "hello"; - } - - foo.length; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_localVariable_type_inferred() async { - var content = ''' -int f() => null; -void main() { - var x = 1; - x = f(); -} -'''; - // The type of x is inferred as non-nullable from its initializer, but we - // try to assign a nullable value to it. So an explicit type must be added. - var expected = ''' -int? f() => null; -void main() { - int? x = 1; - x = f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_localVariable_uninitialized_assigned_non_nullable() async { - var content = ''' -f() { - String s; - if (1 == 2) s = g(); - h(s); -} -String /*!*/ g() => "Hello"; -h(String /*!*/ s) {} -'''; - var expected = ''' -f() { - late String s; - if (1 == 2) s = g(); - h(s); -} -String g() => "Hello"; -h(String s) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_localVariable_uninitialized_used() async { - var content = ''' -f() { - String s; - if (1 == 2) s = "Hello"; - g(s); -} -g(String /*!*/ s) {} -'''; - var expected = ''' -f() { - late String s; - if (1 == 2) s = "Hello"; - g(s); -} -g(String s) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_localVariable_uninitialized_usedInComparison() async { - var content = ''' -f() { - String s; - if (s == null) {} -} -'''; - var expected = ''' -f() { - String? s; - if (s == null) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_localVariable_uninitialized_usedInExpressionStatement() async { - var content = ''' -f() { - String s; - s; -} -'''; - var expected = ''' -f() { - String? s; - s; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_localVariable_uninitialized_usedInForUpdaters() async { - var content = ''' -f() { - String s; - for (s;;) {} -} -'''; - var expected = ''' -f() { - String? s; - for (s;;) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_localVariable_uninitialized_usedInForVariable() async { - var content = ''' -f() { - String s; - for (;; s) {} -} -'''; - var expected = ''' -f() { - String? s; - for (;; s) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_loop_var_is_field() async { - var content = ''' -class C { - int x; - C(this.x); - f(List y) { - for (x in y) {} - } -} -'''; - var expected = ''' -class C { - int? x; - C(this.x); - f(List y) { - for (x in y) {} - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_loop_var_is_inherited_field_with_substitution() async { - var content = ''' -class B { - T x; - B(this.x); -} -abstract class C implements B { - f(List y) { - for (x in y) {} - } -} -'''; - var expected = ''' -class B { - T x; - B(this.x); -} -abstract class C implements B { - f(List y) { - for (x in y) {} - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_make_downcast_explicit() async { - var content = 'int f(num/*!*/ n) => n;'; - var expected = 'int f(num n) => n as int;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_many_type_variables() async { - try { - assert(false); - } catch (_) { - // When assertions are enabled, this test fails, so skip it. - // See https://github.com/dart-lang/sdk/issues/43945. - return; - } - var content = ''' -void test(C x, double Function(C) y) { - x.f(y); -} -class C { - U f(U Function(C) z) => throw 'foo'; -} -'''; - var expected = ''' -void test(C x, double Function(C)? y) { - x.f(y); -} -class C { - U f(U Function(C)? z) => throw 'foo'; -} -'''; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_map_nullable_input() async { - var content = ''' -Iterable f(List x) => x.map((y) => g(y)); -int g(int x) => x + 1; -main() { - f([null]); -} -'''; - var expected = ''' -Iterable f(List x) => x.map((y) => g(y!)); -int g(int x) => x + 1; -main() { - f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_map_nullable_input_tearoff() async { - var content = ''' -Iterable f(List x) => x.map(g); -int g(int x) => x + 1; -main() { - f([null]); -} -'''; - var expected = ''' -Iterable f(List x) => x.map(g); -int g(int? x) => x! + 1; -main() { - f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_map_nullable_output() async { - var content = ''' -Iterable _f(List x) => x.map((y) => _g(y)); -int _g(int x) => null; -main() { - _f([1]); -} -'''; - var expected = ''' -Iterable _f(List x) => x.map((y) => _g(y)); -int? _g(int x) => null; -main() { - _f([1]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_map_read_does_not_require_index_cast() async { - var content = ''' -int _f(Map m, Object o) => m[o]; -'''; - var expected = ''' -int? _f(Map m, Object o) => m[o]; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_map_write_requires_index_cast() async { - var content = ''' -void _f(Map m, Object o, int i) => m[o] = i; -'''; - var expected = ''' -void _f(Map m, Object o, int i) => m[o as String] = i; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_methodInvocation_extension_invocation() async { - var content = ''' -extension on bool { - void f() {} -} -bool g(T x) => true; -void main() { - g(null).f(); -} -'''; - var expected = ''' -extension on bool { - void f() {} -} -bool g(T x) => true; -void main() { - g(null).f(); -} -'''; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_methodInvocation_typeArguments_explicit() async { - var content = ''' -T f(T t) => t; -void g() { - int x = f(null); -} -'''; - var expected = ''' -T f(T t) => t; -void g() { - int? x = f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_methodInvocation_typeArguments_explicit_nonNullable() async { - var content = ''' -T f(T/*!*/ t) => t; -void g(int/*?*/ n) { - int x = f(n); -} -'''; - var expected = ''' -T f(T t) => t; -void g(int? n) { - int x = f(n!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_methodInvocation_typeArguments_explicit_nonNullableParam() async { - var content = ''' -T f(T/*!*/ t) => t; -void g() { - int x = f(null); -} -'''; - var expected = ''' -T f(T t) => t; -void g() { - int? x = f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_methodInvocation_typeArguments_inferred() async { - var content = ''' -T f(T t) => t; -void g() { - int x = f(null); -} -'''; - var expected = ''' -T f(T t) => t; -void g() { - int? x = f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_methodInvocation_typeArguments_inferred_nonNullable() async { - var content = ''' -T f(T/*!*/ t) => t; -void g(int/*?*/ n) { - int x = f(n); -} -'''; - var expected = ''' -T f(T t) => t; -void g(int? n) { - int x = f(n!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_methodInvocation_typeArguments_inferred_nonNullableParam() async { - var content = ''' -T f(T/*!*/ t) => t; -void g() { - int x = f(null); -} -'''; - var expected = ''' -T f(T t) => t; -void g() { - int? x = f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_migrate_reference_to_never() async { - var content = ''' -import 'dart:io'; -int f() => - exit(1); // this returns `Never` which used to cause a crash. -'''; - var expected = ''' -import 'dart:io'; -int f() => - exit(1); // this returns `Never` which used to cause a crash. -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_migratedMethod_namedParameter() async { - var content = ''' -void f(Iterable a) { - a.toList(growable: false); -} -'''; - var expected = ''' -void f(Iterable a) { - a.toList(growable: false); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_multiDeclaration_innerUsage() async { - var content = ''' -void test() { - // here non-null is OK. - int i1 = 0, i2 = i1.gcd(2); - // here non-null is not OK. - int i3 = 0, i4 = i3.gcd(2), i5 = null; -} -'''; - var expected = ''' -void test() { - // here non-null is OK. - int i1 = 0, i2 = i1.gcd(2); - // here non-null is not OK. - int? i3 = 0, i4 = i3.gcd(2), i5 = null; -} -'''; - - await _checkSingleFileChanges(content, expected); - } - - Future test_multiDeclaration_softEdges() async { - var content = ''' -int _nullable(int i1, int i2) { - int i3 = i1, i4 = i2; - return i3; -} -int _nonNull(int i1, int i2) { - int i3 = i1, i4 = i2; - return i3; -} -int _both(int i1, int i2) { - int i3 = i1, i4 = i2; - return i3; -} -void main() { - _nullable(null, null); - _nonNull(0, 1); - _both(0, null); -} -'''; - var expected = ''' -int? _nullable(int? i1, int? i2) { - int? i3 = i1, i4 = i2; - return i3; -} -int _nonNull(int i1, int i2) { - int i3 = i1, i4 = i2; - return i3; -} -int? _both(int i1, int? i2) { - int? i3 = i1, i4 = i2; - return i3; -} -void main() { - _nullable(null, null); - _nonNull(0, 1); - _both(0, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_add_required() async { - var content = ''' -void f({String s}) { - assert(s != null); -} -'''; - var expected = ''' -void f({required String s}) { - assert(s != null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_add_required_function_typed() async { - var content = ''' -void f({void g(int i)}) { - assert(g != null); -} -'''; - var expected = ''' -void f({required void g(int i)}) { - assert(g != null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_unused() async { - var content = ''' -void f({String s}) {} -main() { - f(); -} -'''; - var expected = ''' -void f({String? s}) {} -main() { - f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_unused_propagate() async { - var content = ''' -void f(String s) {} -void g({String s}) { - f(s); -} -main() { - g(); -} -'''; - var expected = ''' -void f(String? s) {} -void g({String? s}) { - f(s); -} -main() { - g(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_unused_required() async { - // The `@required` annotation overrides the assumption of nullability. - // The call at `f()` is presumed to be in error. - addMetaPackage(); - var content = ''' -import 'package:meta/meta.dart'; -void f({@required String s}) {} -main() { - f(); -} -'''; - var expected = ''' -import 'package:meta/meta.dart'; -void f({required String s}) {} -main() { - f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_used_non_null() async { - var content = ''' -void f({String s}) {} -main() { - f(s: 'x'); -} -'''; - var expected = ''' -void f({String? s}) {} -main() { - f(s: 'x'); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_used_non_null_propagate() async { - var content = ''' -void f(String s) {} -void g({String s}) { - f(s); -} -main() { - g(s: 'x'); -} -'''; - var expected = ''' -void f(String? s) {} -void g({String? s}) { - f(s); -} -main() { - g(s: 'x'); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_used_null_option2() async { - var content = ''' -void f({String s}) {} -main() { - f(s: null); -} -'''; - var expected = ''' -void f({String? s}) {} -main() { - f(s: null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_no_default_used_null_required() async { - // Explicitly passing `null` forces the parameter to be nullable even though - // it is required. - addMetaPackage(); - var content = ''' -import 'package:meta/meta.dart'; -void f({@required String s}) {} -main() { - f(s: null); -} -'''; - var expected = ''' -import 'package:meta/meta.dart'; -void f({required String? s}) {} -main() { - f(s: null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_named_parameter_with_non_null_default_unused_option2() async { - var content = ''' -void f({String s: 'foo'}) {} -main() { - f(); -} -'''; - var expected = ''' -void f({String s: 'foo'}) {} -main() { - f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_named_parameter_with_non_null_default_used_non_null_option2() async { - var content = ''' -void f({String s: 'foo'}) {} -main() { - f(s: 'bar'); -} -'''; - var expected = ''' -void f({String s: 'foo'}) {} -main() { - f(s: 'bar'); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_named_parameter_with_non_null_default_used_null_option2() async { - var content = ''' -void f({String s: 'foo'}) {} -main() { - f(s: null); -} -'''; - var expected = ''' -void f({String? s: 'foo'}) {} -main() { - f(s: null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_named_parameter_with_null_default_unused_option2() async { - var content = ''' -void f({String s: null}) {} -main() { - f(); -} -'''; - var expected = ''' -void f({String? s: null}) {} -main() { - f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_non_null_assertion() async { - var content = ''' -int _f(int i, [int j]) { - if (i == 0) return i; - return i + j; -} -'''; - - var expected = ''' -int _f(int i, [int? j]) { - if (i == 0) return i; - return i + j!; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_non_null_intent_field_formal_assert() async { - var content = ''' -class C { - int i; - C(this.i) { - assert(i != null); - } -} -f(int j, bool b) { - if (b) { - C(j); - } -} -g() { - f(null, false); -} -'''; - var expected = ''' -class C { - int i; - C(this.i) { - assert(i != null); - } -} -f(int? j, bool b) { - if (b) { - C(j!); - } -} -g() { - f(null, false); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_non_null_intent_field_formal_use() async { - var content = ''' -class C { - int i; - C(this.i) { - f(i); - } -} -f(int j) { - assert(j != null); -} -g(int k, bool b) { - if (b) { - C(k); - } -} -h() { - g(null, false); -} -'''; - var expected = ''' -class C { - int i; - C(this.i) { - f(i); - } -} -f(int j) { - assert(j != null); -} -g(int? k, bool b) { - if (b) { - C(k!); - } -} -h() { - g(null, false); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_non_null_intent_propagated_through_substitution_nodes() async { - var content = ''' -abstract class C { - void f(List x, int y) { - x.add(y); - } - int/*?*/ g(); - void test() { - f([], g()); - } -} -'''; - var expected = ''' -abstract class C { - void f(List x, int y) { - x.add(y); - } - int? g(); - void test() { - f([], g()!); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_non_nullable_hint_comment_overrides_uncheckable_edge() async { - var content = ''' -Iterable f(List x) => x.map(g); -int g(int/*!*/ x) => x + 1; -main() { - f([null]); -} -'''; - // TODO(paulberry): we should do something to flag the fact that g can't be - // safely passed to f. - var expected = ''' -Iterable f(List x) => x.map(g as int Function(int?)); -int g(int x) => x + 1; -main() { - f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_not_definitely_assigned_value() async { - var content = ''' -String f(bool b) { - String s; - if (b) { - s = 'true'; - } - return s; -} -'''; - var expected = ''' -String? f(bool b) { - String? s; - if (b) { - s = 'true'; - } - return s; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_call_followed_by_if_null() async { - var content = ''' -typedef MapGetter = Map Function(); -void _f(Map m) {} -void _g(MapGetter/*?*/ mapGetter) { - _f(mapGetter?.call() ?? {}); -} -'''; - var expected = ''' -typedef MapGetter = Map Function(); -void _f(Map m) {} -void _g(MapGetter? mapGetter) { - _f(mapGetter?.call() ?? {}); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_call_on_closure_param_not_nullable() async { - // The null-aware access on `i` is *not* considered a strong enough signal - // that `i` is meant to be nullable, because the migration tool can see all - // callers of the closure, so it can tell whether it needs to be nullable or - // not. - // - // (Note: this is not strictly true, because the closure could be called - // from elsewhere. But it's a heuristic that seems to be usually right in - // the cases we've found so far.) - var content = ''' -main() { - var x = (int i) => i?.abs(); -} -'''; - var expected = ''' -main() { - var x = (int i) => i.abs(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_call_on_local_param_not_nullable() async { - // The null-aware access on `i` is *not* considered a strong enough signal - // that `i` is meant to be nullable, because the migration tool can see all - // callers of `f`, so it can tell whether it needs to be nullable or not. - // - // (Note: this is not strictly true, because the local function could be - // torn off and called from elsewhere. But it's a heuristic that seems to - // be usually right in the cases we've found so far.) - var content = ''' -main() { - int f(int i) => i?.abs(); -} -'''; - var expected = ''' -main() { - int f(int i) => i.abs(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_call_on_migrated_get() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D get g; -} -abstract class D { - int f2(); -} -'''; - // Since `.g` is a non-nullable getter in an already-migrated class, the - // `?.` can safely be replaced with `.`. We can safely make this change - // even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C c) => c.g?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.g.f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_call_on_migrated_get_null_shorting() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D get g; -} -abstract class D { - int f2(); -} -'''; - // Since `.g` is a non-nullable getter in an already-migrated class, the - // `?.` can safely be replaced with `.`. We can safely make this change - // even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C/*?*/ c) => c?.g?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -f(C? c) => c?.g.f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_call_on_migrated_method() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D f(); -} -abstract class D { - int f2(); -} -'''; - // Since `.f()` is a method with a non-nullable return type in an - // already-migrated class, the `?.` can safely be replaced with `.`. We can - // safely make this change even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C c) => c.f()?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.f().f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_call_on_migrated_method_null_shorting() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D f(); -} -abstract class D { - int f2(); -} -'''; - // Since `.f()` is a method with a non-nullable return type in an - // already-migrated class, the `?.` can safely be replaced with `.`. We can - // safely make this change even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C/*?*/ c) => c?.f()?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -f(C? c) => c?.f().f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_call_on_nullable_get() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D? get g; -} -abstract class D { - int f2(); -} -'''; - // Since `.g` is a nullable getter in an already-migrated class, we don't - // replace `?.` with `.`. - var content = ''' -import 'migrated.dart'; -f(C c) => c.g?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.g?.f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}); - } - - Future test_null_aware_call_on_nullable_method() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D? f(); -} -abstract class D { - int f2(); -} -'''; - // Since `.f()` is a method with a nullable return type in an - // already-migrated class, we don't replace `?.` with `.`. - var content = ''' -import 'migrated.dart'; -f(C c) => c.f()?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.f()?.f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}); - } - - Future test_null_aware_call_on_private_param_not_nullable() async { - // The null-aware access on `i` is *not* considered a strong enough signal - // that `i` is meant to be nullable, because the migration tool can see all - // callers of `_f`, so it can tell whether it needs to be nullable or not. - var content = 'int _f(int i) => i?.abs();'; - var expected = 'int _f(int i) => i.abs();'; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_call_on_public_param_implies_nullable() async { - // The null-aware access on `i` is considered a strong signal that `i` is - // meant to be nullable. - var content = 'int f(int i) => i?.abs();'; - var expected = 'int? f(int? i) => i?.abs();'; - await _checkSingleFileChanges(content, expected); - } - - Future - test_null_aware_call_on_public_param_overridable_by_hint() async { - // The null-aware access on `i` is considered a strong signal that `i` is - // meant to be nullable, but an explicit `/*!*/` is a stronger signal. - var content = 'int f(int/*!*/ i) => i?.abs();'; - var expected = 'int f(int i) => i.abs();'; - await _checkSingleFileChanges(content, expected); - } - - Future - test_null_aware_call_on_public_param_overridable_by_intent() async { - // The null-aware access on `i` is considered a strong signal that `i` is - // meant to be nullable, but non-null intent is a stronger signal. - var content = ''' -int f(int i) { - print(i + 1); - return i?.abs(); -} -'''; - var expected = ''' -int f(int i) { - print(i + 1); - return i.abs(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_call_on_unmigrated_get() async { - var migratedInput = ''' -// @dart=2.12 -abstract class D { - int f2(); -} -'''; - // Since `.g` is unmigrated, we don't replace `?.` with `.` in "warn on weak - // code" mode. - var content = ''' -import 'migrated.dart'; -abstract class C { - D get g; -} -f(C c) => c.g?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -abstract class C { - D get g; -} -f(C c) => c.g?.f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_call_on_unmigrated_method() async { - var migratedInput = ''' -// @dart=2.12 -abstract class D { - int f2(); -} -'''; - // Since `.f()` is unmigrated, we don't replace `?.` with `.` in "warn on - // weak code" mode. - var content = ''' -import 'migrated.dart'; -abstract class C { - D f(); -} -f(C c) => c.f()?.f2(); -'''; - var expected = ''' -import 'migrated.dart'; -abstract class C { - D f(); -} -f(C c) => c.f()?.f2(); -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_call_tearoff() async { - // Kind of a weird use case because `f?.call` is equivalent to `f`, but - // let's make sure we analyze it correctly. - var content = - 'int Function(int) g(int/*?*/ Function(int)/*?*/ f) => f?.call;'; - var expected = 'int? Function(int)? g(int? Function(int)? f) => f?.call;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_get_on_closure_param_not_nullable() async { - // The null-aware access on `i` is *not* considered a strong enough signal - // that `i` is meant to be nullable, because the migration tool can see all - // callers of the closure, so it can tell whether it needs to be nullable or - // not. - // - // (Note: this is not strictly true, because the closure could be called - // from elsewhere. But it's a heuristic that seems to be usually right in - // the cases we've found so far.) - var content = ''' -main() { - var x = (int i) => i?.isEven; -} -'''; - var expected = ''' -main() { - var x = (int i) => i.isEven; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_get_on_local_param_not_nullable() async { - // The null-aware access on `i` is *not* considered a strong enough signal - // that `i` is meant to be nullable, because the migration tool can see all - // callers of `f`, so it can tell whether it needs to be nullable or not. - // - // (Note: this is not strictly true, because the local function could be - // torn off and called from elsewhere. But it's a heuristic that seems to - // be usually right in the cases we've found so far.) - var content = ''' -main() { - bool f(int i) => i?.isEven; -} -'''; - var expected = ''' -main() { - bool f(int i) => i.isEven; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_get_on_migrated_get() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D get g; -} -abstract class D { - int get g2; -} -'''; - // Since `.g` is a non-nullable getter in an already-migrated class, the - // `?.` can safely be replaced with `.`. We can safely make this change - // even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C c) => c.g?.g2; -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.g.g2; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_get_on_migrated_get_null_shorting() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D get g; -} -abstract class D { - int get g2; -} -'''; - // Since `.g` is a non-nullable getter in an already-migrated class, the - // `?.` can safely be replaced with `.`. We can safely make this change - // even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C/*?*/ c) => c?.g?.g2; -'''; - var expected = ''' -import 'migrated.dart'; -f(C? c) => c?.g.g2; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_get_on_migrated_method() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D f(); -} -abstract class D { - int get g2; -} -'''; - // Since `.f()` is a method with a non-nullable return type in an - // already-migrated class, the `?.` can safely be replaced with `.`. We can - // safely make this change even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C c) => c.f()?.g2; -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.f().g2; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_get_on_migrated_method_null_shorting() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D f(); -} -abstract class D { - int get g2; -} -'''; - // Since `.f()` is a method with a non-nullable return type in an - // already-migrated class, the `?.` can safely be replaced with `.`. We can - // safely make this change even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C/*?*/ c) => c?.f()?.g2; -'''; - var expected = ''' -import 'migrated.dart'; -f(C? c) => c?.f().g2; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_get_on_private_param_not_nullable() async { - // The null-aware access on `i` is *not* considered a strong enough signal - // that `i` is meant to be nullable, because the migration tool can see all - // callers of `_f`, so it can tell whether it needs to be nullable or not. - var content = 'bool _f(int i) => i?.isEven;'; - var expected = 'bool _f(int i) => i.isEven;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_get_on_public_param_implies_nullable() async { - // The null-aware access on `i` is considered a strong signal that `i` is - // meant to be nullable. - var content = 'bool f(int i) => i?.isEven;'; - var expected = 'bool? f(int? i) => i?.isEven;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_get_on_public_param_overridable_by_hint() async { - // The null-aware access on `i` is considered a strong signal that `i` is - // meant to be nullable, but an explicit `/*!*/` is a stronger signal. - var content = 'bool f(int/*!*/ i) => i?.isEven;'; - var expected = 'bool f(int i) => i.isEven;'; - await _checkSingleFileChanges(content, expected); - } - - Future - test_null_aware_get_on_public_param_overridable_by_intent() async { - // The null-aware access on `i` is considered a strong signal that `i` is - // meant to be nullable, but non-null intent is a stronger signal. - var content = ''' -bool f(int i) { - print(i + 1); - return i?.isEven; -} -'''; - var expected = ''' -bool f(int i) { - print(i + 1); - return i.isEven; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_getter_invocation() async { - var content = ''' -bool f(int i) => i?.isEven; -main() { - f(null); -} -'''; - var expected = ''' -bool? f(int? i) => i?.isEven; -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_method_invocation() async { - var content = ''' -int f(int i) => i?.abs(); -main() { - f(null); -} -'''; - var expected = ''' -int? f(int? i) => i?.abs(); -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_set_on_closure_param_not_nullable() async { - // The null-aware access on `c` is *not* considered a strong enough signal - // that `c` is meant to be nullable, because the migration tool can see all - // callers of the closure, so it can tell whether it needs to be nullable or - // not. - // - // (Note: this is not strictly true, because the closure could be called - // from elsewhere. But it's a heuristic that seems to be usually right in - // the cases we've found so far.) - var content = ''' -class C { - int i = 0; -} -main() { - var x = (C c) { c?.i = 0; }; -} -'''; - var expected = ''' -class C { - int i = 0; -} -main() { - var x = (C c) { c.i = 0; }; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_set_on_local_param_not_nullable() async { - // The null-aware access on `c` is *not* considered a strong enough signal - // that `c` is meant to be nullable, because the migration tool can see all - // callers of `f`, so it can tell whether it needs to be nullable or not. - // - // (Note: this is not strictly true, because the local function could be - // torn off and called from elsewhere. But it's a heuristic that seems to - // be usually right in the cases we've found so far.) - var content = ''' -class C { - int i = 0; -} -main() { - void f(C c) { c?.i = 0; } -} -'''; - var expected = ''' -class C { - int i = 0; -} -main() { - void f(C c) { c.i = 0; } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_set_on_migrated_get() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D get g; -} -abstract class D { - set s(int i); -} -'''; - // Since `.g` is a non-nullable getter in an already-migrated class, the - // `?.` can safely be replaced with `.`. We can safely make this change - // even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C c) => c.g?.s = 0; -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.g.s = 0; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_set_on_migrated_get_null_shorting() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D get g; -} -abstract class D { - set s(int i); -} -'''; - // Since `.g` is a non-nullable getter in an already-migrated class, the - // `?.` can safely be replaced with `.`. We can safely make this change - // even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C/*?*/ c) => c?.g?.s = 0; -'''; - var expected = ''' -import 'migrated.dart'; -f(C? c) => c?.g.s = 0; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_set_on_migrated_method() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D f(); -} -abstract class D { - set s(int i); -} -'''; - // Since `.f()` is a method with a non-nullable return type in an - // already-migrated class, the `?.` can safely be replaced with `.`. We can - // safely make this change even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C c) => c.f()?.s = 0; -'''; - var expected = ''' -import 'migrated.dart'; -f(C c) => c.f().s = 0; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_set_on_migrated_method_null_shorting() async { - var migratedInput = ''' -// @dart=2.12 -abstract class C { - D f(); -} -abstract class D { - set s(int i); -} -'''; - // Since `.f()` is a method with a non-nullable return type in an - // already-migrated class, the `?.` can safely be replaced with `.`. We can - // safely make this change even if we are in "warn on weak code" mode. - var content = ''' -import 'migrated.dart'; -f(C/*?*/ c) => c?.f()?.s = 0; -'''; - var expected = ''' -import 'migrated.dart'; -f(C? c) => c?.f().s = 0; -'''; - await _checkSingleFileChanges(content, expected, - migratedInput: {'$projectPath/lib/migrated.dart': migratedInput}, - warnOnWeakCode: true); - } - - Future test_null_aware_set_on_private_param_not_nullable() async { - // The null-aware access on `c` is *not* considered a strong enough signal - // that `c` is meant to be nullable, because the migration tool can see all - // callers of `_f`, so it can tell whether it needs to be nullable or not. - var content = ''' -class C { - int i = 0; -} -void _f(C c) { c?.i = 0; } -'''; - var expected = ''' -class C { - int i = 0; -} -void _f(C c) { c.i = 0; } -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_set_on_public_param_implies_nullable() async { - // The null-aware access on `c` is considered a strong signal that `c` is - // meant to be nullable. - var content = ''' -class C { - int i = 0; -} -void f(C c) { c?.i = 0; } -'''; - var expected = ''' -class C { - int i = 0; -} -void f(C? c) { c?.i = 0; } -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_set_on_public_param_overridable_by_hint() async { - // The null-aware access on `c` is considered a strong signal that `c` is - // meant to be nullable, but an explicit `/*!*/` is a stronger signal. - var content = ''' -class C { - int i = 0; -} -void f(C/*!*/ c) { c?.i = 0; } -'''; - var expected = ''' -class C { - int i = 0; -} -void f(C c) { c.i = 0; } -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_null_aware_set_on_public_param_overridable_by_intent() async { - // The null-aware access on `c` is considered a strong signal that `c` is - // meant to be nullable, but non-null intent is a stronger signal. - var content = ''' -class C { - int i = 0; -} -void f(C c) { - print(c.i); - c?.i = 0; -} -'''; - var expected = ''' -class C { - int i = 0; -} -void f(C c) { - print(c.i); - c.i = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_setter_invocation_null_target() async { - var content = ''' -class C { - void set x(int value) {} -} -int f(C c) => c?.x = 1; -main() { - f(null); -} -'''; - var expected = ''' -class C { - void set x(int value) {} -} -int? f(C? c) => c?.x = 1; -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_aware_setter_invocation_null_value() async { - var content = ''' -class C { - void set x(int value) {} -} -int f(C c) => c?.x = 1; -main() { - f(null); -} -'''; - var expected = ''' -class C { - void set x(int value) {} -} -int? f(C? c) => c?.x = 1; -main() { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_check_in_cascade_target() async { - var content = ''' -class _C { - f() {} -} -_C g(int/*!*/ i) => _C(); -test(int/*?*/ j) { - g(j)..f(); -} -'''; - var expected = ''' -class _C { - f() {} -} -_C g(int i) => _C(); -test(int? j) { - g(j!)..f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_check_type_parameter_type_with_nullable_bound() async { - var content = ''' -abstract class C/*?*/> { - void f(T iter) { - for(var i in iter) {} - } -} -'''; - var expected = ''' -abstract class C?> { - void f(T iter) { - for(var i in iter!) {} - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_in_conditional_expression() async { - var content = ''' -void f() { - List x = false ? [] : null; -} -'''; - var expected = ''' -void f() { - List? x = false ? [] : null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_null_typed_expression_wiithout_valid_migration() async { - var content = ''' -void f(int/*!*/ x) {} -void g() { - f(h()); -} -Null h() => null; -'''; - var expected = ''' -void f(int x) {} -void g() { - f(h()); -} -Null h() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_nullable_use_of_typedef() async { - var content = ''' -typedef F = int Function(T); -F f = null; -void main() { - f('foo'); -} -'''; - var expected = ''' -typedef F = int Function(T); -F? f = null; -void main() { - f!('foo'); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_nullTestOnGenericType_explicitBound() async { - var content = ''' -void f(T x, T y) { - if (x == null) return; - if (y == null) return; -} -g() => f(1, null); -'''; - var expected = ''' -void f(T? x, T? y) { - if (x == null) return; - if (y == null) return; -} -g() => f(1, null); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_nullTestOnGenericType_implicitBound() async { - var content = ''' -void f(T x, T y) { - if (x == null) return; - if (y == null) return; -} -g() => f(1, null); -'''; - var expected = ''' -void f(T? x, T? y) { - if (x == null) return; - if (y == null) return; -} -g() => f(1, null); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_nullTestOnGenericType_nonNullableBound() async { - var content = ''' -void f(T x, T y) { - if (x == null) return; - if (y == null) return; -} -'''; - var expected = ''' -void f(T? x, T? y) { - if (x == null) return; - if (y == null) return; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_nullTestOnGenericType_nullableBound() async { - var content = ''' -void f(T x, T y) { - if (x == null) return; - if (y == null) return; -} -g() => f(1, null); -'''; - var expected = ''' -void f(T? x, T? y) { - if (x == null) return; - if (y == null) return; -} -g() => f(1, null); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_operator_eq_with_inferred_parameter_type() async { - var content = ''' -class C { - operator==(Object other) { - return other is C; - } -} -'''; - var expected = ''' -class C { - operator==(Object other) { - return other is C; - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_object_from_type_parameter() async { - var content = ''' -class C { - f(T t) {} -} -class D extends C { - @override - f(Object t) {} -} -'''; - var expected = ''' -class C { - f(T t) {} -} -class D extends C { - @override - f(Object? t) {} -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_parameter_type_non_nullable() async { - var content = ''' -abstract class Base { - void f(int i); -} -class Derived extends Base { - void f(int i) { - i + 1; - } -} -void _g(int i, bool b, Base base) { - if (b) { - base.f(i); - } -} -void _h(Base base) { - _g(null, false, base); -} -'''; - var expected = ''' -abstract class Base { - void f(int? i); -} -class Derived extends Base { - void f(int? i) { - i! + 1; - } -} -void _g(int? i, bool b, Base base) { - if (b) { - base.f(i); - } -} -void _h(Base base) { - _g(null, false, base); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_parameter_type_nullable() async { - var content = ''' -abstract class Base { - void f(int i); -} -class Derived extends Base { - void f(int i) {} -} -void _g(int i, Base base) { - base.f(null); -} -'''; - var expected = ''' -abstract class Base { - void f(int? i); -} -class Derived extends Base { - void f(int? i) {} -} -void _g(int i, Base base) { - base.f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_parameter_type_unknown() async { - var content = ''' -abstract class Base { - void f(int/*!*/ i, int/*!*/ j); -} -class Derived extends Base { - void f(int i, int j) { - i + 1; - } -} -'''; - var expected = ''' -abstract class Base { - void f(int i, int j); -} -class Derived extends Base { - void f(int i, int j) { - i + 1; - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_return_type_non_nullable() async { - var content = ''' -abstract class Base { - int/*!*/ f(); -} -class Derived extends Base { - int f() => g(); -} -int g() => null; -'''; - var expected = ''' -abstract class Base { - int f(); -} -class Derived extends Base { - int f() => g()!; -} -int? g() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_return_type_nullable() async { - var content = ''' -abstract class Base { - int f(); -} -class Derived extends Base { - int f() => null; -} -'''; - var expected = ''' -abstract class Base { - int? f(); -} -class Derived extends Base { - int? f() => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_return_type_nullable_substitution_complex() async { - var content = ''' -abstract class Base { - T f(); -} -class Derived extends Base> { - List f() => [null]; -} -'''; - var expected = ''' -abstract class Base { - T f(); -} -class Derived extends Base> { - List f() => [null]; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_override_return_type_nullable_substitution_simple() async { - var content = ''' -abstract class Base { - T f(); -} -class Derived extends Base { - int f() => null; -} -'''; - var expected = ''' -abstract class Base { - T f(); -} -class Derived extends Base { - int? f() => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_parameter_genericFunctionType() async { - var content = ''' -int _f(int x, int Function(int i) g) { - return g(x); -} -'''; - var expected = ''' -int _f(int x, int Function(int i) g) { - return g(x); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): cfg is no longer altered here, remove? - Future test_postdominating_usage_after_cfg_altered() async { - // By altering the control-flow graph, we can create new postdominators, - // which are not recognized as such. This is not a problem as we only do - // hard edges on a best-effort basis, and this case would be a lot of - // additional complexity. - var content = ''' -int f(int a, int b, int c) { - if (a != null) { - b.toDouble(); - } else { - return null; - } - c.toDouble; -} - -void main() { - f(1, null, null); -} -'''; - var expected = ''' -int? f(int? a, int? b, int? c) { - if (a != null) { - b!.toDouble(); - } else { - return null; - } - c!.toDouble; -} - -void main() { - f(1, null, null); -} -'''; - await _checkSingleFileChanges(content, expected, removeViaComments: true); - } - - Future test_prefix_minus() async { - var content = ''' -class C { - D operator-() => null; -} -class D {} -D test(C c) => -c; -'''; - var expected = ''' -class C { - D? operator-() => null; -} -class D {} -D? test(C c) => -c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_prefix_minus_substitute() async { - var content = ''' -abstract class C { - D operator-(); -} -class D {} -D test(C c) => -c; -'''; - var expected = ''' -abstract class C { - D operator-(); -} -class D {} -D test(C c) => -c; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_prefixes() async { - var root = '$projectPath/lib'; - var path1 = convertPath('$root/file1.dart'); - var file1 = ''' -import 'file2.dart'; -int x; -int f() => null; -'''; - var expected1 = ''' -import 'file2.dart'; -int? x; -int? f() => null; -'''; - var path2 = convertPath('$root/file2.dart'); - var file2 = ''' -import 'file1.dart' as f1; -void main() { - f1.x = f1.f(); -} -'''; - var expected2 = ''' -import 'file1.dart' as f1; -void main() { - f1.x = f1.f(); -} -'''; - await _checkMultipleFileChanges( - {path1: file1, path2: file2}, {path1: expected1, path2: expected2}); - } - - Future test_prefixExpression_bang() async { - var content = ''' -bool f(bool b) => !b; -void g(bool b1, bool b2) { - if (b1) { - f(b2); - } -} -main() { - g(false, null); -} -'''; - var expected = ''' -bool f(bool b) => !b; -void g(bool b1, bool? b2) { - if (b1) { - f(b2!); - } -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_promotion_conditional_variableRead() async { - var content = ''' -_f({int i}) { - i = i == null ? 0 : i; - _g(i); -} - -_g(int j) {} -'''; - var expected = ''' -_f({int? i}) { - i = i == null ? 0 : i; - _g(i); -} - -_g(int j) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_promotion_ifNull_variableRead() async { - var content = ''' -_f({int i}) { - i ??= 3; - _g(i); -} - -_g(int j) {} -'''; - var expected = ''' -_f({int? i}) { - i ??= 3; - _g(i); -} - -_g(int j) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_promotion_ifNull_variableRead_alreadyPromoted() async { - var content = ''' -_f({num i}) { - if (i is int /*?*/) { - i ??= 3; - _g(i); - } -} - -_g(int j) {} -'''; - var expected = ''' -_f({num? i}) { - if (i is int?) { - i ??= 3; - _g(i); - } -} - -_g(int j) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_promotion_ifNull_variableRead_subType() async { - var content = ''' -_f({num i}) { - i ??= 3; - _g(i); -} - -_g(int j) {} -'''; - var expected = ''' -_f({num? i}) { - i ??= 3; - _g(i as int); -} - -_g(int j) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_promotion_preserves_complex_types() async { - var content = ''' -int/*!*/ f(List/*?*/ x) { - x ??= [0]; - return x[0]; -} -'''; - // `x ??= [0]` promotes x from List? to List. Since there is - // still a `?` on the `int`, `x[0]` must be null checked. - var expected = ''' -int f(List? x) { - x ??= [0]; - return x[0]!; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_propagate_non_null_intent_into_function_literal() async { - var content = ''' -void f(int/*!*/ Function(int) callback) { - callback(null); -} -void test() { - f((int x) => x); -} -'''; - // Since the function literal `(int x) => x` is created right here at the - // point where it's passed to `f`'s `callback` parameter, non-null intent is - // allowed to propagate backward from the return type of `callback` to the - // return type of the function literal. As a result, the reference to `x` - // in the function literal is null checked. - var expected = ''' -void f(int Function(int?) callback) { - callback(null); -} -void test() { - f((int? x) => x!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_property_access_on_cascade_result() async { - var content = ''' -int f(List l) { - l..first.isEven - ..firstWhere((_) => true).isEven - ..[0].isEven; -} - -void g() { - f([null]); -} -'''; - var expected = ''' -int f(List l) { - l..first!.isEven - ..firstWhere((_) => true)!.isEven - ..[0]!.isEven; -} - -void g() { - f([null]); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_quiver_checkNotNull_field_formal_initializer() async { - addQuiverPackage(); - var content = ''' -import 'package:quiver/check.dart'; -class C { - final int i; - C(this.i) { - checkNotNull(i); - } -} -void f(bool b, int i) { - if (b) new C(i); -} -main() { - f(false, null); -} -'''; - // Note: since the reference to `i` in `checkNotNull(i)` refers to the field - // rather than the formal parameter, this isn't considered sufficient to - // mark the field as non-nullable (even though that's the clear intention - // in this case). Changing the behavior to match user intent would require - // more development work; for now we just want to make sure we provide a - // fairly reasonable migration without crashing. - var expected = ''' -import 'package:quiver/check.dart'; -class C { - final int? i; - C(this.i) { - checkNotNull(i); - } -} -void f(bool b, int? i) { - if (b) new C(i); -} -main() { - f(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_quiver_checkNotNull_implies_non_null_intent() async { - addQuiverPackage(); - var content = ''' -import 'package:quiver/check.dart'; -void f(int i) { - checkNotNull(i); -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -import 'package:quiver/check.dart'; -void f(int i) { - checkNotNull(i); -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_redirecting_constructor_factory() async { - var content = ''' -class C { - factory C(int i, int j) = D; -} -class D implements C { - D(int i, int j); -} -main() { - C(null, 1); -} -'''; - var expected = ''' -class C { - factory C(int? i, int? j) = D; -} -class D implements C { - D(int? i, int? j); -} -main() { - C(null, 1); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_redirecting_constructor_ordinary() async { - var content = ''' -class C { - C(int i, int j) : this.named(j, i); - C.named(int j, int i); -} -main() { - C(null, 1); -} -'''; - var expected = ''' -class C { - C(int? i, int? j) : this.named(j, i); - C.named(int? j, int? i); -} -main() { - C(null, 1); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_redirecting_constructor_ordinary_to_unnamed() async { - var content = ''' -class C { - C.named(int i, int j) : this(j, i); - C(int j, int i); -} -main() { - C.named(null, 1); -} -'''; - var expected = ''' -class C { - C.named(int? i, int? j) : this(j, i); - C(int? j, int? i); -} -main() { - C.named(null, 1); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_reference_to_mixin_getter() async { - var content = ''' -mixin M { - Object f() => this.x; - - Object get x => null; -} -'''; - var expected = ''' -mixin M { - Object? f() => this.x; - - Object? get x => null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_regression_40551() async { - var content = ''' -class B { // bound should not be made nullable - void f(T t) { // parameter should not be made nullable - // Create an edge from the bound to some type - List x = [t]; - // and make that type exact nullable - x[0] = null; - } -} -'''; - var expected = ''' -class B { // bound should not be made nullable - void f(T t) { // parameter should not be made nullable - // Create an edge from the bound to some type - List x = [t]; - // and make that type exact nullable - x[0] = null; - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_regression_40552() async { - var content = ''' -void _f(Object o) { // parameter should not be made nullable - // Create an edge from the bound to some type - List x = [o]; - // and make that type exact nullable - x[0] = null; -} -'''; - var expected = ''' -void _f(Object o) { // parameter should not be made nullable - // Create an edge from the bound to some type - List x = [o]; - // and make that type exact nullable - x[0] = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_regression_42374() async { - var content = ''' -class C { - R m(dynamic x) { - assert(x is R); - return x as R; - } -} - -void main() { - C().m(null/*!*/); -} -'''; - var expected = ''' -class C { - R m(dynamic x) { - assert(x is R); - return x as R; - } -} - -void main() { - C().m(null!); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_remove_question_from_question_dot() async { - var content = '_f(int/*!*/ i) => i?.isEven;'; - var expected = '_f(int i) => i.isEven;'; - await _checkSingleFileChanges(content, expected); - } - - Future test_remove_question_from_question_dot_and_add_bang() async { - var content = ''' -class C { - int/*?*/ i; -} -int/*!*/ f(C/*!*/ c) => c?.i; -'''; - var expected = ''' -class C { - int? i; -} -int f(C c) => c.i!; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_remove_question_from_question_dot_method() async { - var content = '_f(int/*!*/ i) => i?.abs();'; - var expected = '_f(int i) => i.abs();'; - await _checkSingleFileChanges(content, expected); - } - - Future test_remove_question_from_question_dot_shortcut() async { - var content = ''' -class C { - int/*!*/ i; -} -bool/*?*/ f(C/*?*/ c) => c?.i?.isEven; -'''; - var expected = ''' -class C { - int i; -} -bool? f(C? c) => c?.i.isEven; -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): element is not removed anymore, remove test? - Future test_removed_if_element_doesnt_introduce_nullability() async { - // Failing because we don't yet remove the dead list element - // `if (x == null) recover()`. - var content = ''' -f(int x) { - [if (x == null) recover(), 0]; -} -int recover() { - assert(false); - return null; -} -'''; - var expected = ''' -f(int? x) { - [if (x == null) recover(), 0]; -} -int? recover() { - assert(false); - return null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_requiredness_does_not_propagate_between_field_formal_params() async { - addMetaPackage(); - var content = ''' -import 'package:meta/meta.dart'; -class C { - final bool x; - C.one({this.x}); - C.two({@required this.x}) : assert(x != null); -} -test() => C.one(); -'''; - var expected = ''' -import 'package:meta/meta.dart'; -class C { - final bool? x; - C.one({this.x}); - C.two({required bool this.x}) : assert(x != null); -} -test() => C.one(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_return_future_or_null_from_async_method() async { - var content = ''' -import 'dart:async'; -Future f() async => g(); -FutureOr g() => null; -'''; - var expected = ''' -import 'dart:async'; -Future f() async => g(); -FutureOr g() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_setter_overrides_implicit_setter() async { - var content = ''' -class A { - String s = "x"; -} -class C implements A { - String get s => "x"; - void set s(String value) {} -} -f() => A().s = null; -'''; - var expected = ''' -class A { - String? s = "x"; -} -class C implements A { - String get s => "x"; - void set s(String? value) {} -} -f() => A().s = null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_single_file_multiple_changes() async { - var content = ''' -int f() => null; -int g() => null; -'''; - var expected = ''' -int? f() => null; -int? g() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_single_file_single_change() async { - var content = ''' -int f() => null; -'''; - var expected = ''' -int? f() => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_singleWhere_non_nullable() async { - var content = ''' -int singleEven(Iterable x) - => x.singleWhere((x) => x.isEven, orElse: () => null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? singleEven(Iterable x) - => x.singleWhereOrNull((x) => x.isEven); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_singleWhere_nullable() async { - var content = ''' -int singleEven(Iterable x) - => x.singleWhere((x) => x.isEven, orElse: () => null); -f() => singleEven([null]); -'''; - var expected = ''' -int? singleEven(Iterable x) - => x.singleWhere((x) => x!.isEven, orElse: () => null); -f() => singleEven([null]); -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40728') - Future test_soft_edge_for_assigned_variable() async { - var content = ''' -void f(int i) { - print(i + 1); - i = null; - print(i); -} -main() { - f(0); -} -'''; - var expected = ''' -void f(int? i) { - print(i! + 1); - i = null; - print(i); -} -main() { - f(0); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_tearoff_parameter_matching_named() async { - var content = ''' -void f(int x, void Function({int x}) callback) { - callback(x: x); -} -void g({int x}) { - assert(x != null); -} -void h() { - f(null, g); -} -'''; - // Normally the assertion in g would cause g's `x` argument to be - // non-nullable (and thus required). However, since g is torn off and - // passed to f, which requires a callback that accepts null, g's `x` - // argument is nullable (and thus not required). - var expected = ''' -void f(int? x, void Function({int? x}) callback) { - callback(x: x); -} -void g({int? x}) { - assert(x != null); -} -void h() { - f(null, g); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_assignedInjectorGet() async { - addAngularPackage(); - addTestCorePackage(); - var content = ''' -import 'package:angular/angular.dart'; -import 'package:test/test.dart'; -void main() { - int i; - setUp(() { - var injector = Injector(); - i = injector.get(int); - }); - test('a', () { - i.isEven; - }); -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; -import 'package:test/test.dart'; -void main() { - late int i; - setUp(() { - var injector = Injector(); - i = injector.get(int); - }); - test('a', () { - i.isEven; - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_assignedInjectorGet_inDeclaration() async { - addAngularPackage(); - addTestCorePackage(); - var content = ''' -import 'package:angular/angular.dart'; -import 'package:test/test.dart'; -void main() { - setUp(() { - var injector = Injector(); - int i = injector.get(int); - i.isEven; - }); -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; -import 'package:test/test.dart'; -void main() { - setUp(() { - var injector = Injector(); - int i = injector.get(int); - i.isEven; - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_assignedInjectorGet_nullableUse() async { - addAngularPackage(); - addTestCorePackage(); - var content = ''' -import 'package:angular/angular.dart'; -import 'package:test/test.dart'; -void f(int /*?*/ i) {} -void main() { - int i; - setUp(() { - var injector = Injector(); - i = injector.get(int); - }); - test('a', () { - f(i); - }); -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; -import 'package:test/test.dart'; -void f(int? i) {} -void main() { - late int i; - setUp(() { - var injector = Injector(); - i = injector.get(int); - }); - test('a', () { - f(i); - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_assignedInjectorGet_outsideSetup() async { - addAngularPackage(); - addTestCorePackage(); - var content = ''' -import 'package:angular/angular.dart'; -void main() { - int i; - var injector = Injector(); - i = injector.get(int); - i.isEven; -} -'''; - var expected = ''' -import 'package:angular/angular.dart'; -void main() { - int? i; - var injector = Injector(); - i = injector.get(int); - i!.isEven; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_assignedNullableValue() async { - addTestCorePackage(); - var content = ''' -import 'package:test/test.dart'; -void main() { - int i; - setUp(() { - i = null; - }); - test('a', () { - i.isEven; - }); -} -'''; - var expected = ''' -import 'package:test/test.dart'; -void main() { - int? i; - setUp(() { - i = null; - }); - test('a', () { - i!.isEven; - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_downstreamAllNonNull() async { - addTestCorePackage(); - var content = ''' -import 'package:test/test.dart'; -void main() { - int i; - setUp(() { - i = 1; - }); - test('a', () { - i.isEven; - }); -} -'''; - var expected = ''' -import 'package:test/test.dart'; -void main() { - late int i; - setUp(() { - i = 1; - }); - test('a', () { - i.isEven; - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_hasInitializer() async { - addTestCorePackage(); - var content = ''' -import 'package:test/test.dart'; -void main() { - int i = 1; - setUp(() { - i = 1; - }); -} -'''; - var expected = ''' -import 'package:test/test.dart'; -void main() { - int i = 1; - setUp(() { - i = 1; - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_testVariable_usedAsNullable() async { - addTestCorePackage(); - var content = ''' -import 'package:test/test.dart'; -void main() { - int i; - setUp(() { - i = 1; - }); - f(int /*?*/ i) {} - test('a', () { - f(i); - }); -} -'''; - var expected = ''' -import 'package:test/test.dart'; -void main() { - late int i; - setUp(() { - i = 1; - }); - f(int? i) {} - test('a', () { - f(i); - }); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_this_inside_extension() async { - var content = ''' -class C { - T field; - C(this.field); -} -extension on C { - f() { - this.field = null; - } -} -extension on C> { - f() { - this.field = [null]; - } -} -'''; - var expected = ''' -class C { - T field; - C(this.field); -} -extension on C { - f() { - this.field = null; - } -} -extension on C> { - f() { - this.field = [null]; - } -} -'''; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_topLevelFunction_parameterType_implicit_dynamic() async { - var content = ''' -Object _f(x) => x; -'''; - // Note: even though the type `dynamic` permits `null`, the migration engine - // sees that there is no code path that passes a null value to `f`, so it - // leaves its return type as `Object`, and there is an implicit downcast. - var expected = ''' -Object _f(x) => x; -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39369') - Future test_topLevelFunction_returnType_implicit_dynamic() async { - var content = ''' -f() {} -Object g() => f(); -'''; - var expected = ''' -f() {} -Object? g() => f(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_topLevelVariable_type_inferred() async { - var content = ''' -int f() => null; -var x = 1; -void main() { - x = f(); -} -'''; - // The type of x is inferred as non-nullable from its initializer, but we - // try to assign a nullable value to it. So an explicit type must be added. - var expected = ''' -int? f() => null; -int? x = 1; -void main() { - x = f(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_topLevelVariable_uninitialized_used() async { - var content = ''' -String s; -f() { - g(s); -} -g(String /*!*/ s) {} -'''; - var expected = ''' -late String s; -f() { - g(s); -} -g(String s) {} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_two_files() async { - var root = '$projectPath/lib'; - var path1 = convertPath('$root/file1.dart'); - var file1 = ''' -import 'file2.dart'; -int f() => null; -int h() => g(); -'''; - var expected1 = ''' -import 'file2.dart'; -int? f() => null; -int? h() => g(); -'''; - var path2 = convertPath('$root/file2.dart'); - var file2 = ''' -import 'file1.dart'; -int g() => f(); -'''; - var expected2 = ''' -import 'file1.dart'; -int? g() => f(); -'''; - await _checkMultipleFileChanges( - {path1: file1, path2: file2}, {path1: expected1, path2: expected2}); - } - - Future test_type_argument_flows_to_bound() async { - // The inference of C forces class C to be declared as - // C. - var content = ''' -abstract class C { - void m(T t); -} -abstract class D { - void m(T t); -} -_f(C c, D d) { - c.m(null); -} -'''; - var expected = ''' -abstract class C { - void m(T t); -} -abstract class D { - void m(T t); -} -_f(C c, D d) { - c.m(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_complex() async { - var content = ''' -typedef F = Function(R); - -class C { - F _f; - - C(this._f) { - f(null); - } - - f(Object o) { - _f(o as T); - } -} -'''; - var expected = ''' -typedef F = Function(R); - -class C { - F _f; - - C(this._f) { - f(null); - } - - f(Object? o) { - _f(o as T?); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_migrated_lhs_parameters() async { - var content = ''' -import 'migrated_typedef.dart'; -void main(F f) { - f(null); -} -'''; - var expected = ''' -import 'migrated_typedef.dart'; -void main(F f) { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/migrated_typedef.dart': ''' -// @dart=2.12 -typedef F = Function(R); -''' - }); - } - - Future test_typedef_assign_null_migrated_lhs_rhs_parameters() async { - var content = ''' -import 'migrated_typedef.dart'; -void f1(F f) { - f(null, null); -} -void f2(F f) { - f(0, null); -} -void f3(F f) { - f(null, 1); -} -void f4(F f) { - f(0, 1); -} -'''; - var expected = ''' -import 'migrated_typedef.dart'; -void f1(F f) { - f(null, null); -} -void f2(F f) { - f(0, null); -} -void f3(F f) { - f(null, 1); -} -void f4(F f) { - f(0, 1); -} -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/migrated_typedef.dart': ''' -// @dart=2.12 -typedef F = Function(T, R); -''' - }); - } - - Future test_typedef_assign_null_migrated_rhs_parameters() async { - var content = ''' -import 'migrated_typedef.dart'; -void main(F f) { - f(null); -} -'''; - var expected = ''' -import 'migrated_typedef.dart'; -void main(F f) { - f(null); -} -'''; - await _checkSingleFileChanges(content, expected, migratedInput: { - '$projectPath/lib/migrated_typedef.dart': ''' -// @dart=2.12 -typedef F = Function(R); -''' - }); - } - - Future test_typedef_assign_null_parameter() async { - var content = ''' -typedef F = Function(int); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F = Function(int?); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_return() async { - var content = ''' -typedef F = int Function(); - -F _f = () => null; -'''; - var expected = ''' -typedef F = int? Function(); - -F _f = () => null; -'''; - await _checkSingleFileChanges(content, expected); - } - -// @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40388') -// TODO(yanok): the test stopped failing since we don't emit casts for -// unrelated types anymore, but the issue mentioned still exists. - Future test_typedef_assign_null_return_type_formal() async { - var content = ''' -typedef F = T Function(); - -F _f = () => null; -'''; - var expected = ''' -typedef F = T? Function(); - -F _f = () => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_return_type_parameter() async { - var content = ''' -typedef F = T Function(); - -F _f = () => null; -'''; - var expected = ''' -typedef F = T Function(); - -F _f = () => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_type_formal() async { - var content = ''' -typedef F = Function(T); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F = Function(T); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_type_formal_with_parameter() async { - var content = ''' -typedef F = Function(T); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F = Function(T); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_type_parameter() async { - var content = ''' -typedef F = Function(T); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F = Function(T); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_type_parameter_non_null() async { - var content = ''' -typedef F = Function(T); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F = Function(T); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_assign_null_type_return_value_nested() async { - var content = ''' -typedef F = T Function(); - -F> f = () => () => null; -'''; - var expected = ''' -typedef F = T Function(); - -F> f = () => () => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_old_assign_null_parameter() async { - var content = ''' -typedef F(int x); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F(int? x); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_old_assign_null_return() async { - var content = ''' -typedef int F(); - -F _f = () => null; -'''; - var expected = ''' -typedef int? F(); - -F _f = () => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_old_assign_null_return_type_parameter() async { - var content = ''' -typedef T F(); - -F _f = () => null; -'''; - var expected = ''' -typedef T F(); - -F _f = () => null; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_old_assign_null_type_parameter() async { - var content = ''' -typedef F(T t); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F(T t); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_typedef_old_assign_null_type_parameter_non_null() async { - var content = ''' -typedef F(T t); - -F/*!*/ _f; - -f() { - _f(null); -} -'''; - var expected = ''' -typedef F(T t); - -late F _f; - -f() { - _f(null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_assert_is_statement_implies_non_null_intent() async { - var content = ''' -void f(Object i) { - assert(i is int); -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(Object i) { - assert(i is int); -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_assert_statement_implies_non_null_intent() async { - var content = ''' -void f(int i) { - assert(i != null); -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) { - assert(i != null); -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_binary_expression_implies_non_null_intent() async { - var content = ''' -void f(int i) { - i + 1; -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) { - i + 1; -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_cascaded_indexed_set_implies_non_null_intent() async { - var content = ''' -class C { - operator[]=(int i, int j) {} -} -void _f(C c) { - c..[1] = 2; -} -void _g(bool b, C c) { - if (b) _f(c); -} -main() { - _g(false, null); -} -'''; - var expected = ''' -class C { - operator[]=(int? i, int? j) {} -} -void _f(C c) { - c..[1] = 2; -} -void _g(bool b, C? c) { - if (b) _f(c!); -} -main() { - _g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_cascaded_method_call_implies_non_null_intent() async { - var content = ''' -void f(int i) { - i..abs(); -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) { - i..abs(); -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_cascaded_property_set_implies_non_null_intent() async { - var content = ''' -class C { - int x = 0; -} -void f(C c) { - c..x = 1; -} -void g(bool b, C c) { - if (b) f(c); -} -main() { - g(false, null); -} -'''; - var expected = ''' -class C { - int x = 0; -} -void f(C c) { - c..x = 1; -} -void g(bool b, C? c) { - if (b) f(c!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_unconditional_method_call_implies_non_null_intent() async { - var content = ''' -void f(int i) { - i.abs(); -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) { - i.abs(); -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_method_call_implies_non_null_intent_after_conditions() async { - var content = ''' -void g(bool b, int i1, int i2) { - int i3 = i1; - if (b) { - b; - } - i3.toDouble(); - int i4 = i2; - if (b) { - b; - return; - } - i4.toDouble(); -} -test(int/*?*/ n) { - g(false, n, null); -} -'''; - var expected = ''' -void g(bool b, int i1, int? i2) { - int i3 = i1; - if (b) { - b; - } - i3.toDouble(); - int? i4 = i2; - if (b) { - b; - return; - } - i4!.toDouble(); -} -test(int? n) { - g(false, n!, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_method_call_implies_non_null_intent_in_condition() async { - var content = ''' -void g(bool b, int _i) { - if (b) { - int i = _i; - i.toDouble(); - } -} -main() { - g(false, null); -} -'''; - var expected = ''' -void g(bool b, int? _i) { - if (b) { - int i = _i!; - i.toDouble(); - } -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_non_null_usage_implies_non_null_intent() async { - var content = ''' -void _f(int i, int j) { - i.gcd(j); -} -void _g(bool b, int i, int j) { - if (b) _f(i, j); -} -main() { - _g(false, 0, null); -} -'''; - var expected = ''' -void _f(int i, int j) { - i.gcd(j); -} -void _g(bool b, int i, int? j) { - if (b) _f(i, j!); -} -main() { - _g(false, 0, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_property_access_implies_non_null_intent() async { - var content = ''' -void f(int i) { - i.isEven; -} -void g(bool b, int i) { - if (b) f(i); -} -main() { - g(false, null); -} -'''; - var expected = ''' -void f(int i) { - i.isEven; -} -void g(bool b, int? i) { - if (b) f(i!); -} -main() { - g(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_unconditional_usage_propagates_non_null_intent() async { - var content = ''' -void f(int i) { - assert(i != null); -} -void g(int i) { - f(i); -} -void h(bool b, int i) { - if (b) g(i); -} -main() { - h(false, null); -} -'''; - var expected = ''' -void f(int i) { - assert(i != null); -} -void g(int i) { - f(i); -} -void h(bool b, int? i) { - if (b) g(i!); -} -main() { - h(false, null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_use_of_field_formal_param_does_not_create_hard_edge() async { - var content = ''' -class C { - int i; - int j; - C.one(this.i) : j = i + 1; - C.two() : i = null, j = 0; -} -'''; - var expected = ''' -class C { - int? i; - int j; - C.one(int this.i) : j = i + 1; - C.two() : i = null, j = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future - test_unconditional_use_of_field_formal_param_does_not_create_hard_edge_generic() async { - var content = ''' -class C { - List i; - int j; - C.one(this.i) : j = i.length; - C.two() : i = null, j = 0; -} -'''; - var expected = ''' -class C { - List? i; - int j; - C.one(List this.i) : j = i.length; - C.two() : i = null, j = 0; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_uninitialized_instance_field_is_nullable() async { - var content = ''' -class C { - int i; - f() { - print(i == null); - } -} -'''; - var expected = ''' -class C { - int? i; - f() { - print(i == null); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_uninitialized_static_field_is_nullable() async { - var content = ''' -class C { - static int i; - f() { - print(i == null); - } -} -'''; - var expected = ''' -class C { - static int? i; - f() { - print(i == null); - } -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_uninitialized_toplevel_var_is_nullable() async { - var content = ''' -int i; -f() { - print(i == null); -} -'''; - var expected = ''' -int? i; -f() { - print(i == null); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_unnecessary_cast_remove() async { - var content = ''' -_f(Object x) { - if (x is! int) return; - print((x as int) + 1); -} -'''; - var expected = ''' -_f(Object x) { - if (x is! int) return; - print(x + 1); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/44012') - Future test_use_import_prefix_when_adding_re_exported_type() async { - addPackageFile('http', 'http.dart', ''' -export 'src/base_client.dart'; -export 'src/client.dart'; -'''); - addPackageFile('http', 'src/base_client.dart', ''' -import 'client.dart'; -abstract class BaseClient implements Client {} -'''); - addPackageFile('http', 'src/client.dart', ''' -abstract class Client {} -'''); - var content = ''' -import 'package:http/http.dart' as http; -http.BaseClient downcast(http.Client x) => x; -'''; - var expected = ''' -import 'package:http/http.dart' as http; -http.BaseClient downcast(http.Client x) => x as http.BaseClient; -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_var_with_different_types() async { - // Based on https://github.com/dart-lang/sdk/issues/47669 - var content = ''' -class C { - T m() => throw 'foo'; -} -f(bool b, List> cs) { - var x = !b, - y = cs.first, - z = y.m(); -} -'''; - var expected = ''' -class C { - T m() => throw 'foo'; -} -f(bool b, List> cs) { - var x = !b, - y = cs.first, - z = y.m(); -} -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_var_with_different_types_becoming_explicit() async { - // When types need to be added to some variables in a declaration but not - // others, we handle it by introducing `as` casts. - var content = ''' -_f(int i, String s) { - var x = i, y = s; - x = null; -} -'''; - var expected = ''' -_f(int i, String s) { - var x = i as int?, y = s; - x = null; -} -'''; - await _checkSingleFileChanges(content, expected); - } - - // TODO(yanok): does it still make sense? - Future test_weak_if_visit_weak_subexpression() async { - var content = ''' -int f(int x, int/*?*/ y) { - if (x == null) { - print(y.toDouble()); - } else { - print(y.toDouble()); - } -} -'''; - var expected = ''' -int f(int? x, int? y) { - if (x == null) { - print(y!.toDouble()); - } else { - print(y!.toDouble()); - } -} -'''; - await _checkSingleFileChanges(content, expected, warnOnWeakCode: true); - } - - Future test_whereNotNull() async { - var content = ''' -Iterable f(Iterable it) => it.where((s) => s != null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableNullableExtension; - -Iterable f(Iterable it) => it.whereNotNull(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_whereNotNull_and_firstWhereOrNull() async { - var content = ''' -Iterable f(Iterable it) => it.where((s) => s != null); -int g(Iterable it) => it.firstWhere((i) => i != 0, orElse: () => null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableExtension, IterableNullableExtension; - -Iterable f(Iterable it) => it.whereNotNull(); -int? g(Iterable it) => it.firstWhereOrNull((i) => i != 0); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_whereNotNull_complexType() async { - var content = ''' -Iterable> f(Iterable/*?*/> it) - => it.where((m) => m != null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableNullableExtension; - -Iterable> f(Iterable?> it) - => it.whereNotNull(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_whereNotNull_iterable_dynamic() async { - var content = ''' -f(Iterable it) => it.where((s) => s != null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableNullableExtension; - -f(Iterable it) => it.whereNotNull(); -'''; - await _checkSingleFileChanges(content, expected); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/49103') - Future test_whereNotNull_iterable_U() async { - var content = ''' -f(Iterable it) => it.where((s) => s != null); -'''; - // whereNotNull cannot be used in this case, because its signature is: - // - // extension IterableNullableExtension on Iterable { - // Iterable whereNotNull() => ...; - // } - // - // When the type system tries to solve for a substitution T=... that makes - // the extension apply, it gets T=U, but that doesn't work because U is not - // a subtype of Object. - // - // So the migration tool shouldn't change the `where` to `whereNotNull`. - var expected = ''' -f(Iterable it) => it.where((s) => s != null); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_whereNotNull_iterable_U_extends_object() async { - var content = ''' -f(Iterable it) => it.where((s) => s != null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableNullableExtension; - -f(Iterable it) => it.whereNotNull(); -'''; - await _checkSingleFileChanges(content, expected); - } - - Future test_whereNotNull_noContext() async { - var content = ''' -f(Iterable it) => it.where((s) => s != null); -'''; - var expected = ''' -import 'package:collection/collection.dart' show IterableNullableExtension; - -f(Iterable it) => it.whereNotNull(); -'''; - await _checkSingleFileChanges(content, expected); - } -} - -@reflectiveTest -class _ProvisionalApiTestPermissive extends _ProvisionalApiTestBase - with _ProvisionalApiTestCases { - @override - bool get _usePermissiveMode => true; -} - -/// Tests of the provisional API, where the driver is reset between calls to -/// `prepareInput` and `processInput`, ensuring that the migration algorithm -/// sees different AST and element objects during different phases. -@reflectiveTest -class _ProvisionalApiTestWithReset extends _ProvisionalApiTestBase - with _ProvisionalApiTestCases { - @override - bool get _usePermissiveMode => false; - - @override - void _betweenStages() { - driver!.clearLibraryContext(); - } -} diff --git a/pkg/nnbd_migration/test/api_test_base.dart b/pkg/nnbd_migration/test/api_test_base.dart deleted file mode 100644 index 61e59a080c69..000000000000 --- a/pkg/nnbd_migration/test/api_test_base.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:test/test.dart'; - -class TestMigrationListener implements NullabilityMigrationListener { - final edits = >{}; - - List details = []; - - @override - void addEdit(Source source, SourceEdit edit) { - (edits[source] ??= []).add(edit); - } - - @override - void addSuggestion(String descriptions, Location location) {} - - @override - void reportException( - Source? source, AstNode? node, Object exception, StackTrace stackTrace) { - fail('Exception reported: $exception\n$stackTrace'); - } -} diff --git a/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart b/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart deleted file mode 100644 index 15a4dcd63060..000000000000 --- a/pkg/nnbd_migration/test/decorated_class_hierarchy_test.dart +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(DecoratedClassHierarchyTest); - }); -} - -@reflectiveTest -class DecoratedClassHierarchyTest extends MigrationVisitorTestBase { - late DecoratedClassHierarchy _hierarchy; - - @override - Future analyze(String code) async { - var unit = await super.analyze(code); - _hierarchy = DecoratedClassHierarchy(variables, graph); - return unit; - } - - Future test_asInstanceOf_complex() async { - await analyze(''' -class Base {} -class Derived extends Base> {} -Derived x; -'''); - var decoratedType = decoratedTypeAnnotation('Derived'); - var asInstanceOfBase = - _hierarchy.asInstanceOf(decoratedType, findElement.class_('Base')); - _assertType(asInstanceOfBase.type!, 'Base>'); - expect(asInstanceOfBase.node, same(decoratedType.node)); - var listOfUType = decoratedTypeAnnotation('List'); - expect(asInstanceOfBase.typeArguments[0]!.node, same(listOfUType.node)); - var substitution = asInstanceOfBase.typeArguments[0]!.typeArguments[0]!.node - as NullabilityNodeForSubstitution; - expect(substitution.innerNode, same(decoratedType.typeArguments[0]!.node)); - expect(substitution.outerNode, same(listOfUType.typeArguments[0]!.node)); - } - - Future test_getDecoratedSupertype_complex() async { - await analyze(''' -class Base {} -class Intermediate extends Base> {} -class Derived extends Intermediate> {} -'''); - var decoratedSupertype = _hierarchy.getDecoratedSupertype( - findElement.class_('Derived'), findElement.class_('Base')); - var listRef = decoratedTypeAnnotation('List'); - var uRef = decoratedTypeAnnotation('U>>'); - var mapRef = decoratedTypeAnnotation('Map'); - var intRef = decoratedTypeAnnotation('int'); - var vRef = decoratedTypeAnnotation('V>>'); - _assertType(decoratedSupertype.type!, 'Base>>'); - expect(decoratedSupertype.node, same(never)); - var baseArgs = decoratedSupertype.typeArguments; - expect(baseArgs, hasLength(1)); - _assertType(baseArgs[0]!.type!, 'List>'); - expect(baseArgs[0]!.node, same(listRef.node)); - var listArgs = baseArgs[0]!.typeArguments; - expect(listArgs, hasLength(1)); - _assertType(listArgs[0]!.type!, 'Map'); - var mapNode = listArgs[0]!.node as NullabilityNodeForSubstitution; - expect(mapNode.innerNode, same(mapRef.node)); - expect(mapNode.outerNode, same(uRef.node)); - var mapArgs = listArgs[0]!.typeArguments; - expect(mapArgs, hasLength(2)); - _assertType(mapArgs[0]!.type!, 'int'); - expect(mapArgs[0]!.node, same(intRef.node)); - _assertType(mapArgs[1]!.type!, 'V'); - expect(mapArgs[1]!.node, same(vRef.node)); - } - - Future test_getDecoratedSupertype_extends_simple() async { - await analyze(''' -class Base {} -class Derived extends Base {} -'''); - var decoratedSupertype = _hierarchy.getDecoratedSupertype( - findElement.class_('Derived'), findElement.class_('Base')); - var vRef = decoratedTypeAnnotation('V, W> {'); - var wRef = decoratedTypeAnnotation('W> {'); - _assertType(decoratedSupertype.type!, 'Base'); - expect(decoratedSupertype.node, same(never)); - expect(decoratedSupertype.typeArguments, hasLength(2)); - _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V'); - expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node)); - _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W'); - expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node)); - } - - Future test_getDecoratedSupertype_implements_simple() async { - await analyze(''' -class Base {} -class Derived implements Base {} -'''); - var decoratedSupertype = _hierarchy.getDecoratedSupertype( - findElement.class_('Derived'), findElement.class_('Base')); - var vRef = decoratedTypeAnnotation('V, W> {'); - var wRef = decoratedTypeAnnotation('W> {'); - _assertType(decoratedSupertype.type!, 'Base'); - expect(decoratedSupertype.node, same(never)); - expect(decoratedSupertype.typeArguments, hasLength(2)); - _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V'); - expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node)); - _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W'); - expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node)); - } - - Future test_getDecoratedSupertype_not_generic() async { - await analyze(''' -class Base {} -class Derived extends Base {} -'''); - var decoratedSupertype = _hierarchy.getDecoratedSupertype( - findElement.class_('Derived'), findElement.class_('Base')); - _assertType(decoratedSupertype.type!, 'Base'); - expect(decoratedSupertype.node, same(never)); - expect(decoratedSupertype.typeArguments, isEmpty); - } - - Future test_getDecoratedSupertype_on_simple() async { - await analyze(''' -class Base {} -mixin Derived on Base {} -'''); - var decoratedSupertype = _hierarchy.getDecoratedSupertype( - findElement.mixin('Derived'), findElement.class_('Base')); - var vRef = decoratedTypeAnnotation('V, W> {'); - var wRef = decoratedTypeAnnotation('W> {'); - _assertType(decoratedSupertype.type!, 'Base'); - expect(decoratedSupertype.node, same(never)); - expect(decoratedSupertype.typeArguments, hasLength(2)); - _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V'); - expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node)); - _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W'); - expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node)); - } - - Future test_getDecoratedSupertype_unrelated_type() async { - await analyze(''' -class A {} -class B {} -'''); - expect( - () => _hierarchy.getDecoratedSupertype( - findElement.class_('A'), findElement.class_('B')), - throwsA(TypeMatcher())); - } - - Future test_getDecoratedSupertype_with_simple() async { - await analyze(''' -class Base {} -class Derived extends Object with Base {} -'''); - var decoratedSupertype = _hierarchy.getDecoratedSupertype( - findElement.class_('Derived'), findElement.class_('Base')); - var vRef = decoratedTypeAnnotation('V, W> {'); - var wRef = decoratedTypeAnnotation('W> {'); - _assertType(decoratedSupertype.type!, 'Base'); - expect(decoratedSupertype.node, same(never)); - expect(decoratedSupertype.typeArguments, hasLength(2)); - _assertType(decoratedSupertype.typeArguments[0]!.type!, 'V'); - expect(decoratedSupertype.typeArguments[0]!.node, same(vRef.node)); - _assertType(decoratedSupertype.typeArguments[1]!.type!, 'W'); - expect(decoratedSupertype.typeArguments[1]!.node, same(wRef.node)); - } - - void _assertType(DartType type, String expected) { - var typeStr = type.getDisplayString(withNullability: false); - expect(typeStr, expected); - } -} diff --git a/pkg/nnbd_migration/test/decorated_type_test.dart b/pkg/nnbd_migration/test/decorated_type_test.dart deleted file mode 100644 index c98109201aa1..000000000000 --- a/pkg/nnbd_migration/test/decorated_type_test.dart +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/generated/element_type_provider.dart'; -import 'package:analyzer/src/generated/testing/test_type_provider.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/variables.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(DecoratedTypeTest); - }); -} - -@reflectiveTest -class DecoratedTypeTest extends Object - with DecoratedTypeTester - implements DecoratedTypeTesterBase { - final NullabilityGraph graph; - - final TypeProvider typeProvider; - - final Variables _variables; - - final _ElementTypeProvider _elementTypeProvider; - - @override - final decoratedTypeParameterBounds = DecoratedTypeParameterBounds(); - - // ignore: unreachable_from_main - factory DecoratedTypeTest() { - var typeProvider = TestTypeProvider(); - var graph = NullabilityGraph(); - var variables = Variables(graph, typeProvider); - return DecoratedTypeTest._(graph, typeProvider, variables); - } - - DecoratedTypeTest._(this.graph, this.typeProvider, this._variables) - : _elementTypeProvider = _ElementTypeProvider(_variables); - - NullabilityNode get always => graph.always; - - void assertDartType(DartType type, String expected) { - // Note: by default DartType.getDisplayString doesn't print nullability - // suffixes, so we have to override that behavior in order to make sure the - // nullability suffixes are correct. - expect(type.getDisplayString(withNullability: true), expected); - } - - void setUp() { - DecoratedTypeParameterBounds.current = decoratedTypeParameterBounds; - ElementTypeProvider.current = _elementTypeProvider; - } - - void tearDown() { - DecoratedTypeParameterBounds.current = null; - ElementTypeProvider.current = const ElementTypeProvider(); - } - - void test_equal_dynamic_and_void() { - expect(dynamic_ == dynamic_, isTrue); - expect(dynamic_ == void_, isFalse); - expect(void_ == dynamic_, isFalse); - expect(void_ == void_, isTrue); - } - - void test_equal_functionType_different_nodes() { - var returnType = int_(); - expect( - function(returnType, node: newNode()) == - function(returnType, node: newNode()), - isFalse); - } - - void test_equal_functionType_named_different_names() { - var node = newNode(); - var argType = int_(); - expect( - function(dynamic_, named: {'x': argType}, node: node) == - function(dynamic_, named: {'y': argType}, node: node), - isFalse); - } - - void test_equal_functionType_named_different_types() { - var node = newNode(); - expect( - function(dynamic_, named: {'x': int_()}, node: node) == - function(dynamic_, named: {'x': int_()}, node: node), - isFalse); - } - - void test_equal_functionType_named_extra() { - var node = newNode(); - var argType = int_(); - var t1 = function(dynamic_, named: {'x': argType}, node: node); - var t2 = function(dynamic_, node: node); - expect(t1 == t2, isFalse); - expect(t2 == t1, isFalse); - } - - void test_equal_functionType_named_same() { - var node = newNode(); - var argType = int_(); - expect( - function(dynamic_, named: {'x': argType}, node: node) == - function(dynamic_, named: {'x': argType}, node: node), - isTrue); - } - - void test_equal_functionType_positional_different() { - var node = newNode(); - expect( - function(dynamic_, positional: [int_()], node: node) == - function(dynamic_, positional: [int_()], node: node), - isFalse); - } - - void test_equal_functionType_positional_same() { - var node = newNode(); - var argType = int_(); - expect( - function(dynamic_, positional: [argType], node: node) == - function(dynamic_, positional: [argType], node: node), - isTrue); - } - - void test_equal_functionType_required_different() { - var node = newNode(); - expect( - function(dynamic_, required: [int_()], node: node) == - function(dynamic_, required: [int_()], node: node), - isFalse); - } - - void test_equal_functionType_required_same() { - var node = newNode(); - var argType = int_(); - expect( - function(dynamic_, required: [argType], node: node) == - function(dynamic_, required: [argType], node: node), - isTrue); - } - - void test_equal_functionType_required_vs_positional() { - var node = newNode(); - var argType = int_(); - expect( - function(dynamic_, required: [argType], node: node) == - function(dynamic_, positional: [argType], node: node), - isFalse); - } - - void test_equal_functionType_return_different() { - var node = newNode(); - expect( - function(int_(), node: node) == function(int_(), node: node), isFalse); - } - - void test_equal_functionType_return_same() { - var node = newNode(); - var returnType = int_(); - expect(function(returnType, node: node) == function(returnType, node: node), - isTrue); - } - - void test_equal_functionType_typeFormals_different_bounds() { - var n1 = newNode(); - var n2 = newNode(); - var t = typeParameter('T', object()); - var u = typeParameter('U', int_()); - expect( - function(typeParameterType(t, node: n1), typeFormals: [t], node: n2) == - function(typeParameterType(u, node: n1), - typeFormals: [u], node: n2), - isFalse); - } - - void - test_equal_functionType_typeFormals_equivalent_bounds_after_substitution() { - var n1 = newNode(); - var n2 = newNode(); - var n3 = newNode(); - var n4 = newNode(); - var bound = object(); - var t = typeParameter('T', bound); - var u = typeParameter('U', typeParameterType(t, node: n1)); - var v = typeParameter('V', bound); - var w = typeParameter('W', typeParameterType(v, node: n1)); - expect( - function(void_, - typeFormals: [t, u], - required: [ - typeParameterType(t, node: n2), - typeParameterType(u, node: n3) - ], - node: n4) == - function(void_, - typeFormals: [v, w], - required: [ - typeParameterType(v, node: n2), - typeParameterType(w, node: n3) - ], - node: n4), - isTrue); - } - - void test_equal_functionType_typeFormals_same_bounds_named() { - var n1 = newNode(); - var n2 = newNode(); - var bound = object(); - var t = typeParameter('T', bound); - var u = typeParameter('U', bound); - expect( - function(void_, - typeFormals: [t], - named: {'x': typeParameterType(t, node: n1)}, - node: n2) == - function(void_, - typeFormals: [u], - named: {'x': typeParameterType(u, node: n1)}, - node: n2), - isTrue); - } - - void test_equal_functionType_typeFormals_same_bounds_positional() { - var n1 = newNode(); - var n2 = newNode(); - var bound = object(); - var t = typeParameter('T', bound); - var u = typeParameter('U', bound); - expect( - function(void_, - typeFormals: [t], - positional: [typeParameterType(t, node: n1)], - node: n2) == - function(void_, - typeFormals: [u], - positional: [typeParameterType(u, node: n1)], - node: n2), - isTrue); - } - - void test_equal_functionType_typeFormals_same_bounds_required() { - var n1 = newNode(); - var n2 = newNode(); - var bound = object(); - var t = typeParameter('T', bound); - var u = typeParameter('U', bound); - expect( - function(void_, - typeFormals: [t], - required: [typeParameterType(t, node: n1)], - node: n2) == - function(void_, - typeFormals: [u], - required: [typeParameterType(u, node: n1)], - node: n2), - isTrue); - } - - void test_equal_functionType_typeFormals_same_bounds_return() { - var n1 = newNode(); - var n2 = newNode(); - var bound = object(); - var t = typeParameter('T', bound); - var u = typeParameter('U', bound); - expect( - function(typeParameterType(t, node: n1), typeFormals: [t], node: n2) == - function(typeParameterType(u, node: n1), - typeFormals: [u], node: n2), - isTrue); - } - - void test_equal_functionType_typeFormals_same_parameters() { - var n1 = newNode(); - var n2 = newNode(); - var t = typeParameter('T', object()); - expect( - function(typeParameterType(t, node: n1), typeFormals: [t], node: n2) == - function(typeParameterType(t, node: n1), - typeFormals: [t], node: n2), - isTrue); - } - - void test_equal_interfaceType_different_args() { - var node = newNode(); - expect(list(int_(), node: node) == list(int_(), node: node), isFalse); - } - - void test_equal_interfaceType_different_classes() { - var node = newNode(); - expect(int_(node: node) == object(node: node), isFalse); - } - - void test_equal_interfaceType_different_nodes() { - expect(int_() == int_(), isFalse); - } - - void test_equal_interfaceType_same() { - var node = newNode(); - expect(int_(node: node) == int_(node: node), isTrue); - } - - void test_equal_interfaceType_same_generic() { - var argType = int_(); - var node = newNode(); - expect(list(argType, node: node) == list(argType, node: node), isTrue); - } - - void test_toFinalType_bottom_non_nullable() { - var type = - _variables.toFinalType(DecoratedType(NeverTypeImpl.instance, never)); - assertDartType(type, 'Never'); - } - - void test_toFinalType_bottom_nullable() { - var type = - _variables.toFinalType(DecoratedType(NeverTypeImpl.instance, always)); - assertDartType(type, 'Null'); - } - - void test_toFinalType_dynamic() { - var type = _variables.toFinalType(dynamic_); - assertDartType(type, 'dynamic'); - } - - void test_toFinalType_function_generic_bound_dynamic() { - var t = typeParameter('T', dynamic_); - var type = _variables.toFinalType( - function(dynamic_, typeFormals: [t], node: never)) as FunctionType; - assertDartType(type, 'dynamic Function()'); - assertDartType( - _elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!, - 'dynamic'); - } - - void test_toFinalType_function_generic_bound_num_question() { - var t = typeParameter('T', num_(node: always)); - var type = _variables.toFinalType( - function(dynamic_, typeFormals: [t], node: never)) as FunctionType; - assertDartType(type, 'dynamic Function()'); - assertDartType( - _elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!, - 'num?'); - } - - void test_toFinalType_function_generic_bound_object_question() { - var t = typeParameter('T', object(node: always)); - var type = _variables.toFinalType( - function(dynamic_, typeFormals: [t], node: never)) as FunctionType; - assertDartType(type, 'dynamic Function()'); - assertDartType( - _elementTypeProvider.getTypeParameterBound(type.typeFormals[0])!, - 'Object?'); - } - - void test_toFinalType_function_generic_substitute_bounds() { - var u = typeParameter('U', object(node: never)); - var t = typeParameter( - 'T', list(typeParameterType(u, node: never), node: never)); - var v = typeParameter( - 'V', list(typeParameterType(u, node: never), node: never)); - var type = _variables.toFinalType( - function(dynamic_, typeFormals: [t, u, v], node: never)) - as FunctionType; - assertDartType( - type, - 'dynamic Function, U extends Object, ' - 'V extends List>()'); - expect(type.typeFormals[0], same(t)); - expect(type.typeFormals[1], same(u)); - expect(type.typeFormals[2], same(v)); - expect( - ((type.typeFormals[0].bound as InterfaceType).typeArguments[0] - as TypeParameterType) - .element, - same(type.typeFormals[1])); - expect( - ((type.typeFormals[2].bound as InterfaceType).typeArguments[0] - as TypeParameterType) - .element, - same(type.typeFormals[1])); - } - - void test_toFinalType_function_generic_substitute_named() { - var t = typeParameter('T', object(node: never)); - var type = _variables.toFinalType(function(dynamic_, - typeFormals: [t], - named: {'x': list(typeParameterType(t, node: never), node: never)}, - node: never)) as FunctionType; - assertDartType(type, 'dynamic Function({List x})'); - expect(type.typeFormals[0], same(t)); - expect( - ((type.parameters[0].type as InterfaceType).typeArguments[0] - as TypeParameterType) - .element, - same(type.typeFormals[0])); - } - - void test_toFinalType_function_generic_substitute_optional() { - var t = typeParameter('T', object(node: never)); - var type = _variables.toFinalType(function(dynamic_, - typeFormals: [t], - positional: [list(typeParameterType(t, node: never), node: never)], - node: never)) as FunctionType; - assertDartType(type, 'dynamic Function([List])'); - expect(type.typeFormals[0], same(t)); - expect( - ((type.parameters[0].type as InterfaceType).typeArguments[0] - as TypeParameterType) - .element, - same(type.typeFormals[0])); - } - - void test_toFinalType_function_generic_substitute_required() { - var t = typeParameter('T', object()); - var type = _variables.toFinalType(function(dynamic_, - typeFormals: [t], - required: [list(typeParameterType(t, node: never), node: never)], - node: never)) as FunctionType; - assertDartType(type, 'dynamic Function(List)'); - expect(type.typeFormals[0], same(t)); - expect( - ((type.parameters[0].type as InterfaceType).typeArguments[0] - as TypeParameterType) - .element, - same(type.typeFormals[0])); - } - - void test_toFinalType_function_generic_substitute_return_type() { - var t = typeParameter('T', object(node: never)); - var type = _variables.toFinalType(function( - list(typeParameterType(t, node: never), node: never), - typeFormals: [t], - node: never)) as FunctionType; - assertDartType(type, 'List Function()'); - expect(type.typeFormals[0], same(t)); - expect( - ((type.returnType as InterfaceType).typeArguments[0] - as TypeParameterType) - .element, - same(type.typeFormals[0])); - } - - void test_toFinalType_function_named_parameter_non_nullable() { - var xType = int_(node: never); - var type = _variables - .toFinalType(function(dynamic_, named: {'x': xType}, node: never)); - assertDartType(type, 'dynamic Function({int x})'); - } - - void test_toFinalType_function_named_parameter_nullable() { - var xType = int_(node: always); - var type = _variables - .toFinalType(function(dynamic_, named: {'x': xType}, node: never)); - assertDartType(type, 'dynamic Function({int? x})'); - } - - void test_toFinalType_function_non_nullable() { - var type = _variables.toFinalType(function(dynamic_, node: never)); - assertDartType(type, 'dynamic Function()'); - } - - void test_toFinalType_function_nullable() { - var type = _variables.toFinalType(function(dynamic_, node: always)); - assertDartType(type, 'dynamic Function()?'); - } - - void test_toFinalType_function_optional_parameter_non_nullable() { - var argType = int_(node: never); - var type = _variables - .toFinalType(function(dynamic_, positional: [argType], node: never)); - assertDartType(type, 'dynamic Function([int])'); - } - - void test_toFinalType_function_optional_parameter_nullable() { - var argType = int_(node: always); - var type = _variables - .toFinalType(function(dynamic_, positional: [argType], node: never)); - assertDartType(type, 'dynamic Function([int?])'); - } - - void test_toFinalType_function_required_parameter_non_nullable() { - var argType = int_(node: never); - var type = _variables - .toFinalType(function(dynamic_, required: [argType], node: never)); - assertDartType(type, 'dynamic Function(int)'); - } - - void test_toFinalType_function_required_parameter_nullable() { - var argType = int_(node: always); - var type = _variables - .toFinalType(function(dynamic_, required: [argType], node: never)); - assertDartType(type, 'dynamic Function(int?)'); - } - - void test_toFinalType_function_return_type_non_nullable() { - var returnType = int_(node: never); - var type = _variables.toFinalType(function(returnType, node: never)); - assertDartType(type, 'int Function()'); - } - - void test_toFinalType_function_return_type_nullable() { - var returnType = int_(node: always); - var type = _variables.toFinalType(function(returnType, node: never)); - assertDartType(type, 'int? Function()'); - } - - void test_toFinalType_interface_non_nullable() { - var type = _variables.toFinalType(int_(node: never)); - assertDartType(type, 'int'); - } - - void test_toFinalType_interface_nullable() { - var type = _variables.toFinalType(int_(node: always)); - assertDartType(type, 'int?'); - } - - void test_toFinalType_interface_type_argument_non_nullable() { - var argType = int_(node: never); - var type = _variables.toFinalType(list(argType, node: never)); - assertDartType(type, 'List'); - } - - void test_toFinalType_interface_type_argument_nullable() { - var argType = int_(node: always); - var type = _variables.toFinalType(list(argType, node: never)); - assertDartType(type, 'List'); - } - - void test_toFinalType_null_non_nullable() { - // We never change explicit `Null` types to `Never`, even if we can't find - // any reason they need to be nullable. - var type = _variables.toFinalType(DecoratedType(null_.type, never)); - assertDartType(type, 'Null'); - } - - void test_toFinalType_null_nullable() { - var type = _variables.toFinalType(DecoratedType(null_.type, always)); - assertDartType(type, 'Null'); - } - - void test_toFinalType_typeParameter_non_nullable() { - var t = typeParameter('T', object(node: never)); - var type = _variables.toFinalType(typeParameterType(t, node: never)); - expect(type, TypeMatcher()); - assertDartType(type, 'T'); - } - - void test_toFinalType_typeParameter_nullable() { - var t = typeParameter('T', object(node: never)); - var type = _variables.toFinalType(typeParameterType(t, node: always)); - expect(type, TypeMatcher()); - assertDartType(type, 'T?'); - } - - void test_toFinalType_void() { - var type = _variables.toFinalType(void_); - assertDartType(type, 'void'); - } - - void test_toString_bottom() { - var node = newNode(); - var decoratedType = DecoratedType(NeverTypeImpl.instance, node); - expect(decoratedType.toString(), 'Never?($node)'); - } - - void test_toString_interface_type_argument() { - var argType = int_(); - var decoratedType = list(argType, node: always); - expect(decoratedType.toString(), 'List<$argType>?'); - } - - void test_toString_named_parameter() { - var xType = int_(); - var decoratedType = function(dynamic_, named: {'x': xType}, node: always); - expect(decoratedType.toString(), 'dynamic Function({x: $xType})?'); - } - - void test_toString_normal_and_named_parameter() { - var xType = int_(); - var yType = int_(); - var decoratedType = function(dynamic_, - required: [xType], named: {'y': yType}, node: always); - expect(decoratedType.toString(), 'dynamic Function($xType, {y: $yType})?'); - } - - void test_toString_normal_and_optional_parameter() { - var xType = int_(); - var yType = int_(); - var decoratedType = function(dynamic_, - required: [xType], positional: [yType], node: always); - expect(decoratedType.toString(), 'dynamic Function($xType, [$yType])?'); - } - - void test_toString_normal_parameter() { - var xType = int_(); - var decoratedType = function(dynamic_, required: [xType], node: always); - expect(decoratedType.toString(), 'dynamic Function($xType)?'); - } - - void test_toString_optional_parameter() { - var xType = int_(); - var decoratedType = function(dynamic_, positional: [xType], node: always); - expect(decoratedType.toString(), 'dynamic Function([$xType])?'); - } -} - -class _ElementTypeProvider extends ElementTypeProvider { - final Variables variables; - - _ElementTypeProvider(this.variables); - - void freshTypeParameterCreated(TypeParameterElement newTypeParameter, - TypeParameterElement oldTypeParameter) { - DecoratedTypeParameterBounds.current!.put(newTypeParameter, - DecoratedTypeParameterBounds.current!.get(oldTypeParameter)); - } - - DartType? getTypeParameterBound(TypeParameterElement element) { - var decoratedType = variables.decoratedTypeParameterBound(element, - allowNullUnparentedBounds: true); - if (decoratedType == null) return element.bound; - return variables.toFinalType(decoratedType); - } -} diff --git a/pkg/nnbd_migration/test/edge_builder_flow_analysis_test.dart b/pkg/nnbd_migration/test/edge_builder_flow_analysis_test.dart deleted file mode 100644 index 2516cd95d0e3..000000000000 --- a/pkg/nnbd_migration/test/edge_builder_flow_analysis_test.dart +++ /dev/null @@ -1,1588 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(EdgeBuilderFlowAnalysisTest); - }); -} - -@reflectiveTest -class EdgeBuilderFlowAnalysisTest extends EdgeBuilderTestBase { - Future test_as() async { - await analyze(''' -void f(num n) { - h(n); - n as int; - g(n); -} -void g(int i) {} -void h(num m) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var nNode = decoratedTypeAnnotation('num n').node; - var mNode = decoratedTypeAnnotation('num m').node; - // No edge from n to i because n is known to be non-nullable at the site of - // the call to g - assertNoEdge(nNode, iNode); - // But there is an edge from n to m. - assertEdge(nNode, mNode, hard: true); - } - - Future test_assert_initializer_condition_promotes_to_message() async { - await analyze(''' -class C { - C(int i) - : assert(i == null, g(i)) { - h(i); - } -} -String g(int j) => 'foo'; -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i is known to be non-nullable at the site of - // the call to g - assertNoEdge(iNode, jNode); - // But there is an edge from i to k. - assertEdge(iNode, kNode, hard: true); - } - - Future test_assert_initializer_does_not_promote_beyond_assert() async { - await analyze(''' -class C { - C(int i) - : assert(i != null) { - g(i); - if (i == null) return; - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // There is an edge from i to j because i not promoted by the assert. - assertEdge(iNode, jNode, hard: true); - // But there is no edge from i to k. - assertNoEdge(iNode, kNode); - } - - Future test_assert_statement_condition_promotes_to_message() async { - await analyze(''' -void f(int i) { - assert(i == null, g(i)); - h(i); -} -String g(int j) => 'foo'; -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i is known to be non-nullable at the site of - // the call to g - assertNoEdge(iNode, jNode); - // But there is an edge from i to k. - assertEdge(iNode, kNode, hard: true); - } - - Future test_assert_statement_does_not_promote_beyond_assert() async { - await analyze(''' -void f(int i) { - assert(i != null); - g(i); - if (i == null) return; - h(i); -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // There is an edge from i to j because i not promoted by the assert. - assertEdge(iNode, jNode, hard: true); - // But there is no edge from i to k. - assertNoEdge(iNode, kNode); - } - - Future test_assignmentExpression() async { - await analyze(''' -void f(int i, int j) { - if (i != null) { - g(i); - i = j; - h(i); - } -} -void g(int k) {} -void h(int l) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - var lNode = decoratedTypeAnnotation('int l').node; - // No edge from i to k because i's type is promoted to non-nullable - assertNoEdge(iNode, kNode); - // But there is an edge from i to l, because it is after the assignment - assertEdge(iNode, lNode, hard: false); - // And there is an edge from j to i, because a null value of j would lead to - // a null value for i. - assertEdge(jNode, iNode, hard: false); - } - - Future test_assignmentExpression_lhs_before_rhs() async { - await analyze(''' -void f(int i, List l) { - if (i != null) { - l[i = g(i)] = h(i); - } -} -int g(int j) => 1; -int h(int k) => 1; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - var gReturnNode = decoratedTypeAnnotation('int g').node; - // No edge from i to j, because i's type is promoted before the call to g. - assertNoEdge(iNode, jNode); - // But there is an edge from i to k, because the call to h happens after the - // assignment. - assertEdge(iNode, kNode, hard: false); - // And there is an edge from g's return type to i, due to the assignment. - assertEdge(gReturnNode, iNode, hard: false); - } - - Future test_assignmentExpression_null_aware() async { - await analyze(''' -void f(bool b, int i, int j) { - if (b) { - j ??= i is int ? i : throw 'foo'; - g(i); - j = i is int ? i : throw 'foo'; - h(i); - } -} -void g(int k) {} -void h(int l) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var kNode = decoratedTypeAnnotation('int k').node; - var lNode = decoratedTypeAnnotation('int l').node; - // No edge from i to l because i's type is promoted to non-nullable - assertNoEdge(iNode, lNode); - // But there is an edge from i to k, because the RHS of the `??=` is not - // guaranteed to execute - assertEdge(iNode, kNode, hard: false); - } - - Future test_assignmentExpression_write_after_rhs() async { - await analyze(''' -void f(int i) { - if (i != null) { - i = g(i); - h(i); - } -} -int g(int j) => 1; -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - var gReturnNode = decoratedTypeAnnotation('int g').node; - // No edge from i to j because i's type is promoted before the call to g. - assertNoEdge(iNode, jNode); - // But there is an edge from i to k, because the call to h happens after the - // assignment. - assertEdge(iNode, kNode, hard: false); - // And there is an edge from g's return type to i, due to the assignment. - assertEdge(gReturnNode, iNode, hard: false); - } - - Future test_binaryExpression_ampersandAmpersand_left() async { - await analyze(''' -bool f(int i) => i != null && i.isEven; -bool g(int j) => j.isEven; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future test_binaryExpression_ampersandAmpersand_right() async { - await analyze(''' -void f(bool b, int i, int j) { - if (b && i != null) { - print(i.isEven); - print(j.isEven); - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_binaryExpression_barBar_left() async { - await analyze(''' -bool f(int i) => i == null || i.isEven; -bool g(int j) => j.isEven; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future test_binaryExpression_barBar_right() async { - await analyze(''' -void f(bool b, int i, int j) { - if (b || i == null) {} else { - print(i.isEven); - print(j.isEven); - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_booleanLiteral_false() async { - await analyze(''' -void f(int i, int j) { - if (i != null || false) {} else return; - if (j != null || true) {} else return; - i.isEven; - j.isEven; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never i is known to be non-nullable at the site of - // the call to i.isEven - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_booleanLiteral_true() async { - await analyze(''' -void f(int i, int j) { - if (i == null && true) return; - if (j == null && false) return; - i.isEven; - j.isEven; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never i is known to be non-nullable at the site of - // the call to i.isEven - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_break_labeled() async { - await analyze(''' -void f(int i) { - L: while(true) { - while (b()) { - if (i != null) break L; - } - g(i); - } - h(i); -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_break_unlabeled() async { - await analyze(''' -void f(int i) { - while (true) { - if (i != null) break; - g(i); - } - h(i); -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future - test_catch_cancels_promotions_based_on_assignments_in_body() async { - await analyze(''' -void f(int i) { - if (i == null) return; - try { - g(i); - i = null; - if (i == null) return; - g(i); - } catch (_) { - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i is promoted at the time of both calls to g. - assertNoEdge(iNode, jNode); - // But there is an edge from i to k, because there is no guarantee that i is - // promoted at all times during the execution of the try block. - assertEdge(iNode, kNode, hard: false); - } - - Future test_catch_falls_through_to_after_try() async { - await analyze(''' -void f(int i) { - try { - g(i); - return; - } catch (_) { - if (i == null) return; - } - h(i); -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i's type is promoted to non-nullable - assertNoEdge(iNode, kNode); - // But there is an edge from i to j. - assertEdge(iNode, jNode, hard: true); - } - - Future test_catch_resets_to_state_before_try() async { - await analyze(''' -void f(int i) { - try { - if (i == null) return; - g(i); - } catch (_) { - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i's type is promoted to non-nullable - assertNoEdge(iNode, jNode); - // But there is an edge from i to k, since we assume an exception might - // occur at any time during the body of the try. - assertEdge(iNode, kNode, hard: false); - } - - Future test_conditionalExpression() async { - await analyze(''' -int f(int i, int l) => i == null ? g(l) : h(i); -int g(int j) => 1; -int h(int k) => 1; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - var lNode = decoratedTypeAnnotation('int l').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from l to j - assertEdge(lNode, jNode, hard: false, guards: [iNode]); - } - - Future test_conditionalExpression_propagates_promotions() async { - await analyze(''' -void f(bool b, int i, int j, int k) { - if (b ? (i != null && j != null) : (i != null && k != null)) { - i.isEven; - j.isEven; - k.isEven; - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there are edges from j and k to never. - assertEdge(jNode, inSet(pointsToNever), hard: false); - assertEdge(kNode, inSet(pointsToNever), hard: false); - } - - Future test_constructorDeclaration_assert() async { - await analyze(''' -class C { - C(int i, int j) : assert(i == null || i.isEven, j.isEven); -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future test_constructorDeclaration_initializer() async { - await analyze(''' -class C { - bool b1; - bool b2; - C(int i, int j) : b1 = i == null || i.isEven, b2 = j.isEven; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future test_constructorDeclaration_redirection() async { - await analyze(''' -class C { - C(bool b1, bool b2); - C.redirect(int i, int j) : this(i == null || i.isEven, j.isEven); -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future test_continue_labeled() async { - await analyze(''' -void f(int i) { - L: do { - do { - if (i != null) continue L; - } while (g(i)); - break; - } while (h(i)); -} -bool g(int j) => true; -bool h(int k) => true; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_continue_unlabeled() async { - await analyze(''' -void f(int i) { - do { - if (i != null) continue; - h(i); - break; - } while (g(i)); -} -bool g(int j) => true; -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i is promoted at the time of the call to g. - assertNoEdge(iNode, jNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to h. - assertEdge(iNode, kNode, hard: false); - } - - Future test_do_break_target() async { - await analyze(''' -void f(int i) { - L: do { - do { - if (i != null) break L; - if (b()) break; - } while (true); - g(i); - } while (true); - h(i); -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_do_cancels_promotions_for_assignments_in_body() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - do { - i.isEven; - j.isEven; - j = null; - } while (true); -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_do_cancels_promotions_for_assignments_in_condition() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - do {} while (i.isEven && j.isEven && g(j = null)); -} -bool g(int k) => true; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_do_continue_target() async { - await analyze(''' -void f(int i) { - L: do { - do { - if (i != null) continue L; - g(i); - } while (true); - } while (h(i)); -} -void g(int j) {} -bool h(int k) => true; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_field_initializer() async { - await analyze(''' -bool b1 = true; -bool b2 = true; -class C { - bool b = b1 || b2; -} -'''); - // No assertions; we just want to verify that the presence of `||` inside a - // field doesn't cause flow analysis to crash. - } - - Future test_finally_promotions_are_preserved() async { - await analyze(''' -void f(int i) { - try { - g(i); - } finally { - if (i == null) return; - } - h(i); -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i's type is promoted to non-nullable in the - // finally block. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j. - assertEdge(iNode, jNode, hard: true); - } - - Future test_finally_temporarily_resets_to_state_before_try() async { - await analyze(''' -void f(int i) { - try { - if (i == null) return; - g(i); - } finally { - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i's type is promoted to non-nullable in the - // try-block. - assertNoEdge(iNode, jNode); - // But there is an edge from i to k, since we assume an exception might - // occur at any time during the body of the try. - assertEdge(iNode, kNode, hard: false); - } - - Future test_for_break_target() async { - await analyze(''' -void f(int i) { - L: for (;;) { - for (;;) { - if (i != null) break L; - if (b()) break; - } - g(i); - } - h(i); -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_for_cancels_promotions_for_assignments_in_body() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - for (;;) { - i.isEven; - j.isEven; - j = null; - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_for_cancels_promotions_for_assignments_in_updaters() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - for (;; j = null) { - i.isEven; - j.isEven; - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_for_collection_cancels_promotions_for_assignments_in_body() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - [for (;;) [i.isEven, j.isEven, (j = null)]]; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_for_collection_cancels_promotions_for_assignments_in_updaters() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - [for (;; j = null) [i.isEven, j.isEven]]; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_for_collection_preserves_promotions_for_assignments_in_initializer() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - [for(var v = h(i.isEven && j.isEven && g(i = null));;) null]; -} -bool g(int k) => true; -int h(bool b) => 0; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because it is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_for_continue_target() async { - await analyze(''' -void f(int i) { - L: for (; b(); h(i)) { - for (; b(); g(i)) { - if (i != null) continue L; - } - return; - } -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_for_each_assigns_to_declared_var() async { - await analyze(''' -void f(Iterable x) { - for (int i in x) { - g(i); - } -} -void g(int j) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - // No edge from never to i because it is assigned before it is used. - assertNoEdge(never, iNode); - } - - Future test_for_each_assigns_to_identifier() async { - await analyze(''' -void f(Iterable x) { - int i; - for (i in x) { - g(i); - } -} -void g(int j) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - // No edge from never to i because it is assigned before it is used. - assertNoEdge(never, iNode); - } - - Future - test_for_each_cancels_promotions_for_assignments_in_body() async { - await analyze(''' -void f(int i, int j, Iterable x) { - if (i == null) return; - if (j == null) return; - for (var v in x) { - i.isEven; - j.isEven; - j = null; - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_for_each_collection_assigns_to_declared_var() async { - await analyze(''' -void f(Iterable x) { - [for (int i in x) g(i)]; -} -void g(int j) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - // No edge from never to i because it is assigned before it is used. - assertNoEdge(never, iNode); - } - - Future test_for_each_collection_assigns_to_identifier() async { - await analyze(''' -void f(Iterable x) { - int i; - [for (i in x) g(i)]; -} -void g(int j) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - // No edge from never to i because it is assigned before it is used. - assertNoEdge(never, iNode); - } - - Future - test_for_each_collection_cancels_promotions_for_assignments_in_body() async { - await analyze(''' -void f(int i, int j, Iterable x) { - if (i == null) return; - if (j == null) return; - [for (var v in x) [i.isEven, j.isEven, (j = null)]]; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_for_each_collection_preserves_promotions_for_assignments_in_iterable() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - [for(var v in h(i.isEven && j.isEven && g(i = null))) null]; -} -bool g(int k) => true; -Iterable h(bool b) => []; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because it is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_for_each_preserves_promotions_for_assignments_in_iterable() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - for(var v in h(i.isEven && j.isEven && g(i = null))) {} -} -bool g(int k) => true; -Iterable h(bool b) => []; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because it is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_for_preserves_promotions_for_assignments_in_initializer() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - for(var v = h(i.isEven && j.isEven && g(i = null));;) {} -} -bool g(int k) => true; -int h(bool b) => 0; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because it is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_function_withFormals() async { - await analyze(''' -void f(Function() f) { - if (f == null) return; - f(); -} -'''); - var fNode = decoratedGenericFunctionTypeAnnotation('Function() f').node; - // No edge to never because it had been promoted before invoked. - assertNoEdge(fNode, graph.never); - } - - Future test_functionDeclaration() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - print(i.isEven); - print(j.isEven); -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_functionDeclaration_expression_body() async { - await analyze(''' -bool f(int i) => i == null || i.isEven; -bool g(int j) => j.isEven; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future - test_functionDeclaration_resets_unconditional_control_flow() async { - await analyze(''' -void _f(bool b, int i, int j) { - assert(i != null); - if (b) return; - assert(j != null); -} -void _g(int k) { - assert(k != null); -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - assertNoEdge(decoratedTypeAnnotation('int j').node, never); - assertEdge(always, decoratedTypeAnnotation('int j').node, hard: false); - assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true); - } - - Future test_functionExpression_parameters() async { - await analyze(''' -void f() { - var g = (int i, int j) { - if (i == null) return; - print(i.isEven); - print(j.isEven); - }; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_if() async { - await analyze(''' -void f(int i) { - if (i == null) { - g(i); - } else { - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from i to j - assertEdge(iNode, jNode, hard: false, guards: [iNode]); - } - - Future test_if_without_else() async { - await analyze(''' -void f(int i) { - if (i == null) { - g(i); - return; - } - h(i); -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from i to j - assertEdge(iNode, jNode, hard: false, guards: [iNode]); - } - - Future test_ifNull() async { - await analyze(''' -void f(int i, int x) { - x ?? (i == null ? throw 'foo' : g(i)); - h(i); -} -int g(int j) => 0; -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to j because i's type is promoted to non-nullable - assertNoEdge(iNode, jNode); - // But there is an edge from i to k, because the RHS of the `??` isn't - // guaranteed to execute. - assertEdge(iNode, kNode, hard: true); - } - - Future test_is() async { - await analyze(''' -void f(num n) { - if (n is int) { - g(n); - } - h(n); -} -void g(int i) {} -void h(num m) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var nNode = decoratedTypeAnnotation('num n').node; - var mNode = decoratedTypeAnnotation('num m').node; - // No edge from n to i because n is known to be non-nullable at the site of - // the call to g - assertNoEdge(nNode, iNode); - // But there is an edge from n to m. - assertEdge(nNode, mNode, hard: true); - } - - Future test_is_not() async { - await analyze(''' -void f(num n) { - if (n is! int) {} else { - g(n); - } - h(n); -} -void g(int i) {} -void h(num m) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var nNode = decoratedTypeAnnotation('num n').node; - var mNode = decoratedTypeAnnotation('num m').node; - // No edge from n to i because n is known to be non-nullable at the site of - // the call to g - assertNoEdge(nNode, iNode); - // But there is an edge from n to m. - assertEdge(nNode, mNode, hard: true); - } - - Future test_local_function_parameters() async { - await analyze(''' -void f() { - void g(int i, int j) { - if (i == null) return; - print(i.isEven); - print(j.isEven); - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_not() async { - await analyze(''' -void f(int i) { - if (!(i == null)) { - h(i); - } else { - g(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from i to j - assertEdge(iNode, jNode, hard: false); - } - - Future test_not_null_then_promote() async { - await analyze(''' -void f(dynamic n) { - if (n == null) { - return; - } else if (n is List) { - n.length; // Ensure this doesn't crash during method lookup. - g(n); // Test this is wired correctly in graph. - } -} -void g(List j) {} -'''); - var nNode = decoratedTypeAnnotation('dynamic n').node; - var jNode = decoratedTypeAnnotation('List j').node; - var jParamNode = decoratedTypeAnnotation('int> j').node; - var intParamNode = decoratedTypeAnnotation('int>)').node; - // No edge from n to g because i is known to be non-nullable at the site of - // the call to g() - assertNoEdge(nNode, jNode); - // But there is an edge from nonNull(List) to j - assertEdge(inSet(neverClosure), jNode, hard: false); - assertEdge(intParamNode, jParamNode, hard: false, checkable: false); - } - - Future test_postfixDecrement() async { - await analyze(''' -void f(C c1) { - if (c1 != null) { - g(c1); - c1--; - h(c1); - } -} -void g(C c2) {} -void h(C c3) {} -class C { - C operator-(int i) => this; -} -'''); - var c1Node = decoratedTypeAnnotation('C c1').node; - var c2Node = decoratedTypeAnnotation('C c2').node; - var c3Node = decoratedTypeAnnotation('C c3').node; - // No edge from c1 to c2 because c1's type is promoted to non-nullable - assertNoEdge(c1Node, c2Node); - // But there is an edge from c1 to c3, because the decrement un-does the - // promotion. - assertEdge(c1Node, c3Node, hard: false); - } - - Future test_postfixIncrement() async { - await analyze(''' -void f(C c1) { - if (c1 != null) { - g(c1); - c1++; - h(c1); - } -} -void g(C c2) {} -void h(C c3) {} -class C { - C operator+(int i) => this; -} -'''); - var c1Node = decoratedTypeAnnotation('C c1').node; - var c2Node = decoratedTypeAnnotation('C c2').node; - var c3Node = decoratedTypeAnnotation('C c3').node; - // No edge from c1 to c2 because c1's type is promoted to non-nullable - assertNoEdge(c1Node, c2Node); - // But there is an edge from c1 to c3, because the increment un-does the - // promotion. - assertEdge(c1Node, c3Node, hard: false); - } - - Future test_prefixDecrement() async { - await analyze(''' -void f(C c1) { - if (c1 != null) { - g(c1); - --c1; - h(c1); - } -} -void g(C c2) {} -void h(C c3) {} -class C { - C operator-(int i) => this; -} -'''); - var c1Node = decoratedTypeAnnotation('C c1').node; - var c2Node = decoratedTypeAnnotation('C c2').node; - var c3Node = decoratedTypeAnnotation('C c3').node; - // No edge from c1 to c2 because c1's type is promoted to non-nullable - assertNoEdge(c1Node, c2Node); - // But there is an edge from c1 to c3, because the decrement un-does the - // promotion. - assertEdge(c1Node, c3Node, hard: false); - } - - Future test_prefixIncrement() async { - await analyze(''' -void f(C c1) { - if (c1 != null) { - g(c1); - ++c1; - h(c1); - } -} -void g(C c2) {} -void h(C c3) {} -class C { - C operator+(int i) => this; -} -'''); - var c1Node = decoratedTypeAnnotation('C c1').node; - var c2Node = decoratedTypeAnnotation('C c2').node; - var c3Node = decoratedTypeAnnotation('C c3').node; - // No edge from c1 to c2 because c1's type is promoted to non-nullable - assertNoEdge(c1Node, c2Node); - // But there is an edge from c1 to c3, because the increment un-does the - // promotion. - assertEdge(c1Node, c3Node, hard: false); - } - - Future test_rethrow() async { - await analyze(''' -void f(int i, int j) { - try { - g(); - } catch (_) { - if (i == null) rethrow; - print(i.isEven); - print(j.isEven); - } -} -void g() {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_return() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - print(i.isEven); - print(j.isEven); -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_switch_break_target() async { - await analyze(''' -void f(int i, int x, int y) { - L: switch (x) { - default: - switch (y) { - default: - if (i != null) break L; - if (b()) break; - return; - } - g(i); - return; - } - h(i); -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_switch_cancels_promotions_for_labeled_cases() async { - await analyze(''' -void f(int i, int x, bool b) { - if (i == null) return; - switch (x) { - L: - case 1: - g(i); - break; - case 2: - h(i); - i = null; - if (b) continue L; - break; - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i's type is promoted to non-nullable at the - // time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j. - assertEdge(iNode, jNode, hard: false); - } - - Future test_switch_default() async { - await analyze(''' -void f(int i, int j, int x, int y) { - if (i == null) { - switch (x) { - default: return; - } - } - if (j == null) { - switch (y) { - case 0: return; - } - } - i.isEven; - j.isEven; -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because the switch statement is guaranteed to - // complete by returning, so i is promoted to non-nullable. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never, because the switch statement is not - // guaranteed to complete by returning, so j is not promoted. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_throw() async { - await analyze(''' -void f(int i, int j) { - if (i == null) throw 'foo'; - print(i.isEven); - print(j.isEven); -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to `never` because i's type is promoted to non-nullable - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to `never`. - assertEdge(jNode, inSet(pointsToNever), hard: true); - } - - Future test_topLevelVar_initializer() async { - await analyze(''' -bool b1 = true; -bool b2 = true; -bool b3 = b1 || b2; -'''); - // No assertions; we just want to verify that the presence of `||` inside a - // top level variable doesn't cause flow analysis to crash. - } - - Future test_try_falls_through_to_after_try() async { - await analyze(''' -void f(int i) { - try { - g(i); - if (i == null) return; - } catch (_) { - return; - } - h(i); -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i's type is promoted to non-nullable - assertNoEdge(iNode, kNode); - // But there is an edge from i to j. - assertEdge(iNode, jNode, hard: true); - } - - Future test_while_break_target() async { - await analyze(''' -void f(int i) { - L: while (true) { - while (true) { - if (i != null) break L; - if (b()) break; - } - g(i); - } - h(i); -} -bool b() => true; -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is promoted at the time of the call to h. - assertNoEdge(iNode, kNode); - // But there is an edge from i to j, because i is not promoted at the time - // of the call to g. - assertEdge(iNode, jNode, hard: false); - } - - Future test_while_cancels_promotions_for_assignments_in_body() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - while (true) { - i.isEven; - j.isEven; - j = null; - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future - test_while_cancels_promotions_for_assignments_in_condition() async { - await analyze(''' -void f(int i, int j) { - if (i == null) return; - if (j == null) return; - while (i.isEven && j.isEven && g(j = null)) {} -} -bool g(int k) => true; -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never because its promotion was cancelled. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } - - Future test_while_promotes() async { - await analyze(''' -void f(int i, int j) { - while (i != null) { - i.isEven; - j.isEven; - } -} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - // No edge from i to never because i is promoted. - assertNoEdge(iNode, inSet(pointsToNever)); - // But there is an edge from j to never. - assertEdge(jNode, inSet(pointsToNever), hard: false); - } -} diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart deleted file mode 100644 index 85d5bf403337..000000000000 --- a/pkg/nnbd_migration/test/edge_builder_test.dart +++ /dev/null @@ -1,8356 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/analysis/experiments.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/type_provider.dart'; -import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl; -import 'package:analyzer/src/error/codes.g.dart'; -import 'package:analyzer/src/generated/testing/test_type_provider.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edge_builder.dart'; -import 'package:nnbd_migration/src/edge_origin.dart'; -import 'package:nnbd_migration/src/expression_checks.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:pub_semver/pub_semver.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(AssignmentCheckerTest); - defineReflectiveTests(EdgeBuilderTest); - }); -} - -@reflectiveTest -class AssignmentCheckerTest extends Object - with EdgeTester, DecoratedTypeTester { - static const EdgeOrigin origin = _TestEdgeOrigin(); - - LibraryElementImpl? _myLibrary; - - ClassElement? _myListOfListClass; - - late DecoratedType _myListOfListSupertype; - - @override - final TypeProvider typeProvider; - - @override - final NullabilityGraphForTesting graph; - - @override - final decoratedTypeParameterBounds = DecoratedTypeParameterBounds(); - - final AssignmentCheckerForTesting checker; - - // ignore: unreachable_from_main - factory AssignmentCheckerTest() { - var typeProvider = TestTypeProvider().asLegacy; - _setCoreLibrariesTypeSystem(typeProvider); - - var graph = NullabilityGraphForTesting(); - var decoratedClassHierarchy = _DecoratedClassHierarchyForTesting(); - var checker = AssignmentCheckerForTesting( - TypeSystemImpl( - isNonNullableByDefault: false, - strictCasts: false, - strictInference: false, - typeProvider: typeProvider, - ), - typeProvider, - graph, - decoratedClassHierarchy); - var assignmentCheckerTest = - AssignmentCheckerTest._(typeProvider, graph, checker); - decoratedClassHierarchy.assignmentCheckerTest = assignmentCheckerTest; - return assignmentCheckerTest; - } - - AssignmentCheckerTest._(this.typeProvider, this.graph, this.checker); - - void assign(DecoratedType source, DecoratedType destination, - {bool hard = false}) { - checker.checkAssignment(origin, - source: source, destination: destination, hard: hard); - } - - DecoratedType myListOfList(DecoratedType elementType) { - _initMyLibrary(); - if (_myListOfListClass == null) { - var t = typeParameter('T', object()); - _myListOfListSupertype = list(list(typeParameterType(t))); - _myListOfListClass = ClassElementImpl('MyListOfList', 0) - ..enclosingElement = _myLibrary!.definingCompilationUnit - ..typeParameters = [t] - ..supertype = _myListOfListSupertype.type as InterfaceType?; - } - return DecoratedType( - InterfaceTypeImpl( - element: _myListOfListClass!, - typeArguments: [elementType.type!], - nullabilitySuffix: NullabilitySuffix.star, - ), - newNode(), - typeArguments: [elementType], - ); - } - - void test_bottom_to_generic() { - var t = list(object()); - assign(bottom, t); - assertEdge(never, t.node, hard: false); - assertNoEdge(anyNode, t.typeArguments[0]!.node); - } - - void test_bottom_to_simple() { - var t = object(); - assign(bottom, t); - assertEdge(never, t.node, hard: false); - } - - void test_complex_to_typeParam() { - var bound = list(object()); - var t1 = list(object()); - var t2 = typeParameterType(typeParameter('T', bound)); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.node, bound.node, hard: false); - // TODO(40622): Should this be a checkable edge? - assertEdge(t1.typeArguments[0]!.node, bound.typeArguments[0]!.node, - hard: false, checkable: false); - } - - void test_dynamic_to_dynamic() { - assign(dynamic_, dynamic_); - // Note: no assertions to do; just need to make sure there wasn't a crash. - } - - void test_dynamic_to_void() { - assign(dynamic_, void_); - // Note: no assertions to do; just need to make sure there wasn't a crash. - } - - void test_function_type_named_parameter() { - var t1 = function(dynamic_, named: {'x': object()}); - var t2 = function(dynamic_, named: {'x': object()}); - assign(t1, t2, hard: true); - // Note: t1 and t2 are swapped due to contravariance. - assertEdge(t2.namedParameters!['x']!.node, t1.namedParameters!['x']!.node, - hard: false, checkable: false); - } - - void test_function_type_named_to_no_parameter() { - var t1 = function(dynamic_, named: {'x': object()}); - var t2 = function(dynamic_); - assign(t1, t2); - // Note: no assertions to do; just need to make sure there wasn't a crash. - } - - void test_function_type_positional_parameter() { - var t1 = function(dynamic_, positional: [object()]); - var t2 = function(dynamic_, positional: [object()]); - assign(t1, t2, hard: true); - // Note: t1 and t2 are swapped due to contravariance. - assertEdge( - t2.positionalParameters![0].node, t1.positionalParameters![0].node, - hard: false, checkable: false); - } - - void test_function_type_positional_to_no_parameter() { - var t1 = function(dynamic_, positional: [object()]); - var t2 = function(dynamic_); - assign(t1, t2); - // Note: no assertions to do; just need to make sure there wasn't a crash. - } - - void test_function_type_positional_to_required_parameter() { - var t1 = function(dynamic_, positional: [object()]); - var t2 = function(dynamic_, required: [object()]); - assign(t1, t2, hard: true); - // Note: t1 and t2 are swapped due to contravariance. - assertEdge( - t2.positionalParameters![0].node, t1.positionalParameters![0].node, - hard: false, checkable: false); - } - - void test_function_type_required_parameter() { - var t1 = function(dynamic_, required: [object()]); - var t2 = function(dynamic_, required: [object()]); - assign(t1, t2); - // Note: t1 and t2 are swapped due to contravariance. - assertEdge( - t2.positionalParameters![0].node, t1.positionalParameters![0].node, - hard: false, checkable: false); - } - - void test_function_type_return_type() { - var t1 = function(object()); - var t2 = function(object()); - assign(t1, t2, hard: true); - assertEdge(t1.returnType!.node, t2.returnType!.node, - hard: false, checkable: false); - } - - void test_function_void_to_function_object() { - // This is not an ideal pattern, but void is assignable to Object in certain - // cases such as those with compound types here. We must support it. - var t1 = function(void_); - var t2 = function(object()); - assign(t1, t2, hard: true); - assertEdge(t1.returnType!.node, t2.returnType!.node, - hard: false, checkable: false); - } - - void test_future_int_to_future_or_int() { - var t1 = future(int_()); - var t2 = futureOr(int_()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: true, checkable: false); - } - - void test_future_or_int_to_future_int() { - var t1 = futureOr(int_()); - var t2 = future(int_()); - assign(t1, t2, hard: true); - // FutureOr? is nullable, so Future? should be. - assertEdge(t1.node, t2.node, hard: true); - // FutureOr is nullable, so Future? should be. - assertEdge(t1.typeArguments[0]!.node, t2.node, hard: true); - // FutureOr may hold a Future, so carry that forward. - assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: false); - // FutureOr? does not accept a Future, so don't draw this. - assertNoEdge(t1.node, t2.typeArguments[0]!.node); - } - - void test_future_or_int_to_int() { - var t1 = futureOr(int_()); - var t2 = int_(); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.node, hard: false); - } - - void test_future_or_list_object_to_list_int() { - var t1 = futureOr(list(object())); - var t2 = list(int_()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.node, hard: false); - assertEdge( - t1.typeArguments[0]!.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: false); - } - - void test_future_or_object_to_future_or_int() { - var t1 = futureOr(object()); - var t2 = futureOr(int_()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: false); - } - - void test_future_or_to_future_or() { - var t1 = futureOr(int_()); - var t2 = futureOr(int_()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: false); - } - - void test_generic_to_dynamic() { - var t = list(object()); - assign(t, dynamic_); - assertEdge(t.node, always, hard: false); - assertNoEdge(t.typeArguments[0]!.node, anyNode); - } - - void test_generic_to_generic_downcast() { - var t1 = list(list(object())); - var t2 = myListOfList(object()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - // Let A, B, and C be nullability nodes such that: - // - t2 is MyListOfList - var a = t2.typeArguments[0]!.node; - // - t1 is List> - var b = t1.typeArguments[0]!.typeArguments[0]!.node; - // - the supertype of MyListOfList is List> - var c = _myListOfListSupertype.typeArguments[0]!.typeArguments[0]!.node; - // Then there should be an edge from b to substitute(a, c) - assertEdge(b, substitutionNode(a, c), hard: false); - } - - void test_generic_to_generic_downcast_of_type_parameter() { - var t = typeParameterType(typeParameter('T', object())); - var t1 = iterable(t); - var t2 = list(t); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - var a = t1.typeArguments[0]!.node; - var b = t2.typeArguments[0]!.node; - assertEdge(a, b, hard: false); - } - - void test_generic_to_generic_downcast_same_element() { - var t1 = list(object()); - var t2 = list(int_()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: false); - } - - void test_generic_to_generic_same_element() { - var t1 = list(object()); - var t2 = list(object()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(t1.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: true, checkable: false); - } - - void test_generic_to_generic_upcast() { - var t1 = myListOfList(object()); - var t2 = list(list(object())); - assign(t1, t2); - assertEdge(t1.node, t2.node, hard: false); - // Let A, B, and C be nullability nodes such that: - // - t1 is MyListOfList - var a = t1.typeArguments[0]!.node; - // - t2 is List> - var b = t2.typeArguments[0]!.typeArguments[0]!.node; - // - the supertype of MyListOfList is List> - var c = _myListOfListSupertype.typeArguments[0]!.typeArguments[0]!.node; - // Then there should be an edge from substitute(a, c) to b. - assertEdge(substitutionNode(a, c), b, hard: false, checkable: false); - } - - void test_generic_to_object() { - var t1 = list(object()); - var t2 = object(); - assign(t1, t2); - assertEdge(t1.node, t2.node, hard: false); - assertNoEdge(t1.typeArguments[0]!.node, anyNode); - } - - void test_generic_to_void() { - var t = list(object()); - assign(t, void_); - assertEdge(t.node, always, hard: false); - assertNoEdge(t.typeArguments[0]!.node, anyNode); - } - - void test_int_to_future_or_int() { - var t1 = int_(); - var t2 = futureOr(int_()); - assign(t1, t2, hard: true); - // Note: given code like: - // int x = null; - // FutureOr y = x; - // There are two possible migrations for `FutureOr`: we could change it - // to either `FutureOr` or `FutureOr?`. We choose to do - // `FutureOr?` because it is a narrower type, so it is less likely to - // cause a proliferation of nullable types in the user's program. - assertEdge(t1.node, t2.node, hard: true); - assertNoEdge(t1.node, t2.typeArguments[0]!.node); - } - - void test_iterable_object_to_list_void() { - assign(iterable(object()), list(void_)); - // Note: no assertions to do; just need to make sure there wasn't a crash. - } - - void test_null_to_generic() { - var t = list(object()); - assign(null_, t); - assertEdge(always, t.node, hard: false); - assertNoEdge(anyNode, t.typeArguments[0]!.node); - } - - void test_null_to_simple() { - var t = object(); - assign(null_, t); - assertEdge(always, t.node, hard: false); - } - - void test_object_to_void() { - assign(object(), void_); - // Note: no assertions to do; just need to make sure there wasn't a crash. - } - - void test_simple_to_dynamic() { - var t = object(); - assign(t, dynamic_); - assertEdge(t.node, always, hard: false); - } - - void test_simple_to_simple() { - var t1 = object(); - var t2 = object(); - assign(t1, t2); - assertEdge(t1.node, t2.node, hard: false); - } - - void test_simple_to_simple_hard() { - var t1 = object(); - var t2 = object(); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - } - - void test_simple_to_void() { - var t = object(); - assign(t, void_); - assertEdge(t.node, always, hard: false); - } - - void test_typeParam_to_complex() { - var bound = list(object()); - var t1 = typeParameterType(typeParameter('T', bound)); - var t2 = list(object()); - assign(t1, t2, hard: true); - assertEdge(t1.node, t2.node, hard: true); - assertEdge(bound.node, t2.node, hard: false); - // TODO(40622): Should this be a checkable edge? - assertEdge(bound.typeArguments[0]!.node, t2.typeArguments[0]!.node, - hard: false, checkable: false); - } - - void test_typeParam_to_object() { - var t1 = typeParameterType(typeParameter('T', object())); - var t2 = object(); - assign(t1, t2); - assertEdge(t1.node, t2.node, hard: false); - } - - void test_typeParam_to_typeParam() { - var t = typeParameter('T', object()); - var t1 = typeParameterType(t); - var t2 = typeParameterType(t); - assign(t1, t2); - assertEdge(t1.node, t2.node, hard: false); - } - - @override - TypeParameterElement typeParameter(String name, DecoratedType bound) { - var t = super.typeParameter(name, bound); - checker.bounds[t] = bound; - return t; - } - - void _initMyLibrary() { - if (_myLibrary != null) { - return; - } - - var coreLibrary = typeProvider.boolElement.library as LibraryElementImpl; - var analysisContext = coreLibrary.context; - var analysisSession = coreLibrary.session; - var typeSystem = coreLibrary.typeSystem; - - var uriStr = 'package:test/test.dart'; - var uri = Uri.parse(uriStr); - var source = _MockSource(uri); - - var definingUnit = CompilationUnitElementImpl( - source: source, - librarySource: source, - lineInfo: LineInfo([0]), - ); - - _myLibrary = LibraryElementImpl( - analysisContext, - analysisSession, - uriStr, - -1, - 0, - FeatureSet.fromEnableFlags2( - sdkLanguageVersion: Version.parse('2.10.0'), - flags: [EnableString.non_nullable], - ), - ) - ..definingCompilationUnit = definingUnit - ..typeProvider = coreLibrary.typeProvider - ..typeSystem = typeSystem; - } - - static void _setCoreLibrariesTypeSystem(TypeProviderImpl typeProvider) { - var typeSystem = TypeSystemImpl( - isNonNullableByDefault: false, - strictCasts: false, - strictInference: false, - typeProvider: typeProvider, - ); - _setLibraryTypeSystem( - typeProvider.objectElement.library, - typeProvider, - typeSystem, - ); - _setLibraryTypeSystem( - typeProvider.futureElement.library, - typeProvider, - typeSystem, - ); - } - - static void _setLibraryTypeSystem( - LibraryElement libraryElement, - TypeProvider typeProvider, - TypeSystem typeSystem, - ) { - var libraryElementImpl = libraryElement as LibraryElementImpl; - libraryElementImpl.typeProvider = typeProvider as TypeProviderImpl; - libraryElementImpl.typeSystem = typeSystem as TypeSystemImpl; - } -} - -@reflectiveTest -class EdgeBuilderTest extends EdgeBuilderTestBase { - void assertGLB( - NullabilityNode? node, NullabilityNode? left, NullabilityNode? right) { - expect(node, isNot(TypeMatcher())); - assertEdge(left, node, hard: false, guards: [right]); - assertEdge(node, left, hard: false); - assertEdge(node, right, hard: false); - } - - void assertLUB(NullabilityNode node, Object? left, Object? right) { - var conditionalNode = node as NullabilityNodeForLUB; - var leftMatcher = NodeMatcher(left); - var rightMatcher = NodeMatcher(right); - expect(leftMatcher.matches(conditionalNode.left), true); - expect(rightMatcher.matches(conditionalNode.right), true); - } - - /// Checks that there are no nullability nodes upstream from [node] that could - /// cause it to become nullable. - void assertNoUpstreamNullability(NullabilityNode? node) { - // Store `neverClosure` in a local variable so that we avoid the - // computational expense of recomputing it each time through the loop below. - var neverClosure = this.neverClosure; - - // Any node with a hard edge to never (or never itself) won't become - // nullable, even if it has nodes upstream from it. - if (neverClosure.contains(node)) return; - - // Otherwise, make sure that every node directly upstream from this node - // has a hard edge to never. - for (var edge in getEdges(anyNode, node)) { - expect(neverClosure, contains(edge.sourceNode)); - } - } - - /// Verifies that a null check will occur when the given edge is unsatisfied. - /// - /// [expressionChecks] is the object tracking whether or not a null check is - /// needed. - void assertNullCheck( - ExpressionChecksOrigin expressionChecks, NullabilityEdge expectedEdge) { - expect(expressionChecks.checks.edges.values, contains(expectedEdge)); - } - - /// Gets the [ExpressionChecks] associated with the expression whose text - /// representation is [text], or `null` if the expression has no - /// [ExpressionChecks] associated with it. - ExpressionChecksOrigin? checkExpression(String text) { - return variables.checkExpression(findNode.expression(text)); - } - - /// Gets the [DecoratedType] associated with the expression whose text - /// representation is [text], or `null` if the expression has no - /// [DecoratedType] associated with it. - DecoratedType? decoratedExpressionType(String text) { - return variables.decoratedExpressionType(findNode.expression(text)); - } - - bool hasNullCheckHint(Expression expression) => - variables.getNullCheckHint(testSource, expression) != null; - - Future test_already_migrated_field() async { - await analyze(''' -double f() => double.nan; -'''); - var nanElement = typeProvider.doubleElement.getField('nan')!; - assertEdge(variables.decoratedElementType(nanElement).node, - decoratedTypeAnnotation('double f').node, - hard: false); - } - - Future test_ArgumentError_checkNotNull_not_postDominating() async { - await analyze(''' -void f(bool b, int i, int j) { - ArgumentError.checkNotNull(j); - if (b) return; - ArgumentError.checkNotNull(i); -} -'''); - - // Asserts after ifs don't demonstrate non-null intent. - assertNoEdge(decoratedTypeAnnotation('int i').node, never); - // But asserts before ifs do - assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); - } - - Future test_ArgumentError_checkNotNull_postDominating() async { - await analyze(''' -void f(int i) { - ArgumentError.checkNotNull(i); -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - } - - Future test_ArgumentError_checkNotNull_prefixed() async { - await analyze(''' -import 'dart:core' as core; -void f(core.int i) { - core.ArgumentError.checkNotNull(i); -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - } - - Future test_as_dynamic() async { - await analyze(''' -void f(Object o) { - (o as dynamic).gcd(1); -} -'''); - assertEdge(decoratedTypeAnnotation('Object o').node, - decoratedTypeAnnotation('dynamic').node, - hard: true); - assertEdge(decoratedTypeAnnotation('dynamic').node, never, hard: true); - } - - Future test_as_int() async { - await analyze(''' -void f(Object o) { - (o as int).gcd(1); -} -'''); - assertEdge(decoratedTypeAnnotation('Object o').node, - decoratedTypeAnnotation('int').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); - expect( - variables.wasUnnecessaryCast(testSource, findNode.as_('o as')), false); - } - - Future test_as_int_null_ok() async { - await analyze(''' -void f(Object o) { - (o as int)?.gcd(1); -} -'''); - assertEdge(decoratedTypeAnnotation('Object o').node, - decoratedTypeAnnotation('int').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int').node, never); - } - - Future test_as_int_unnecessary() async { - verifyNoTestUnitErrors = false; - await analyze(''' -void f(int i) { - (i as int).gcd(1); -} -'''); - expect(testAnalysisResult.errors.single.errorCode, - WarningCode.UNNECESSARY_CAST); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int)').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int)').node, never, hard: true); - expect( - variables.wasUnnecessaryCast(testSource, findNode.as_('i as')), true); - } - - Future test_as_side_cast() async { - await analyze(''' -class A {} -class B {} -class C implements A, B {} -B f(A a) { - // possible via f(C()); - return a as B; -} -'''); - assertEdge( - decoratedTypeAnnotation('A a').node, decoratedTypeAnnotation('B;').node, - hard: true); - } - - Future test_as_side_cast_generics() async { - await analyze(''' -class A {} -class B {} -class C implements A, B {} -B f(A a) { - // possible via f(C()); - return a as B; -} -'''); - assertEdge(decoratedTypeAnnotation('A a').node, - decoratedTypeAnnotation('B;').node, - hard: true); - assertEdge(decoratedTypeAnnotation('bool>;').node, - decoratedTypeAnnotation('bool> f').node, - hard: true, checkable: false); - assertNoEdge(anyNode, decoratedTypeAnnotation('bool>;').node); - assertNoEdge(anyNode, decoratedTypeAnnotation('int> a').node); - // int> a should be connected to the bound of T in A, but nothing else. - expect( - decoratedTypeAnnotation('int> a').node.downstreamEdges, hasLength(1)); - } - - Future test_assert_demonstrates_non_null_intent() async { - await analyze(''' -void f(int i) { - assert(i != null); -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - } - - Future test_assert_initializer_demonstrates_non_null_intent() async { - await analyze(''' -class C { - C(int i) - : assert(i != null); -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - } - - Future test_assert_is_demonstrates_non_null_intent() async { - // Note, this could also be handled via improved flow analysis rather than a - // hard edge. - await analyze(''' -void f(dynamic i) { - assert(i is int); -} -'''); - - assertEdge(decoratedTypeAnnotation('dynamic i').node, never, hard: true); - } - - Future test_assign_bound_to_type_parameter() async { - await analyze(''' -class C> { - T f(List x) => x; -} -'''); - var boundType = decoratedTypeAnnotation('List>'); - var parameterType = decoratedTypeAnnotation('List x'); - var tType = decoratedTypeAnnotation('T f'); - assertEdge(parameterType.node, tType.node, hard: true); - assertEdge(parameterType.node, boundType.node, hard: false); - // TODO(mfairhurst): Confirm we want this edge. - // TODO(40622): Should this be a checkable edge? - assertEdge( - parameterType.typeArguments[0]!.node, boundType.typeArguments[0]!.node, - hard: false, checkable: false); - } - - Future test_assign_dynamic_to_other_type() async { - await analyze(''' -int f(dynamic d) => d; -'''); - // There is no explicit null check necessary, since `dynamic` is - // downcastable to any type, nullable or not. - expect(checkExpression('d;'), isNull); - // But we still create an edge, to make sure that the possibility of `null` - // propagates to callees. - assertEdge(decoratedTypeAnnotation('dynamic').node, - decoratedTypeAnnotation('int').node, - hard: true); - } - - Future test_assign_function_type_to_function_interface_type() async { - await analyze(''' -Function f(void Function() x) => x; -'''); - assertEdge(decoratedGenericFunctionTypeAnnotation('void Function()').node, - decoratedTypeAnnotation('Function f').node, - hard: true); - } - - Future test_assign_future_to_futureOr_complex() async { - await analyze(''' -import 'dart:async'; -FutureOr> f(Future> x) => x; -'''); - // If `x` is `Future>`, then the only way to migrate is to make - // the return type `FutureOr>`. - assertEdge(decoratedTypeAnnotation('int>> x').node, - decoratedTypeAnnotation('int>> f').node, - hard: true, checkable: false); - assertNoEdge(decoratedTypeAnnotation('int>> x').node, - decoratedTypeAnnotation('List> f').node); - assertNoEdge(decoratedTypeAnnotation('int>> x').node, - decoratedTypeAnnotation('FutureOr> f').node); - } - - Future test_assign_future_to_futureOr_simple() async { - await analyze(''' -import 'dart:async'; -FutureOr f(Future x) => x; -'''); - // If `x` is nullable, then there are two migrations possible: we could make - // the return type `FutureOr` or we could make it `FutureOr?`. - // We choose `FutureOr?` because it's strictly more conservative (it's - // a subtype of `FutureOr`). - assertEdge(decoratedTypeAnnotation('Future x').node, - decoratedTypeAnnotation('FutureOr').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('Future x').node, - decoratedTypeAnnotation('int> f').node); - // If `x` is `Future`, then the only way to migrate is to make the - // return type `FutureOr`. - - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> x').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int> f').node, - hard: true, - checkable: false); - assertNoEdge(decoratedTypeAnnotation('int> x').node, - decoratedTypeAnnotation('FutureOr').node); - } - - Future test_assign_non_future_to_futureOr_complex() async { - await analyze(''' -import 'dart:async'; -FutureOr> f(List x) => x; -'''); - // If `x` is `List`, then the only way to migrate is to make the - // return type `FutureOr>`. - assertEdge(decoratedTypeAnnotation('int> x').node, - decoratedTypeAnnotation('int>> f').node, - hard: false, checkable: false); - assertNoEdge(decoratedTypeAnnotation('int> x').node, - decoratedTypeAnnotation('List> f').node); - assertNoEdge(decoratedTypeAnnotation('int> x').node, - decoratedTypeAnnotation('FutureOr> f').node); - } - - Future test_assign_non_future_to_futureOr_simple() async { - await analyze(''' -import 'dart:async'; -FutureOr f(int x) => x; -'''); - // If `x` is nullable, then there are two migrations possible: we could make - // the return type `FutureOr` or we could make it `FutureOr?`. - // We choose `FutureOr?` because it's strictly more conservative (it's - // a subtype of `FutureOr`). - assertEdge(decoratedTypeAnnotation('int x').node, - decoratedTypeAnnotation('FutureOr').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int x').node, - decoratedTypeAnnotation('int>').node); - } - - Future test_assign_null_to_generic_type() async { - await analyze(''' -main() { - List x = null; -} -'''); - // TODO(paulberry): edge should be hard. - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('List').node, - hard: false); - } - - Future test_assign_to_bound_as() async { - // TODO(mfairhurst): support downcast to type params with bounds - await analyze(''' -class C {} -void f(Object o) { - o as C; -} -'''); - // For now, edge to `anyNode`, because the true bound is inferred. - assertEdge(decoratedTypeAnnotation('int').node, anyNode, hard: true); - } - - Future test_assign_to_bound_class_alias() async { - await analyze(''' -class C {} -class D {} -mixin M {} -class F = C with M implements D; -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object/*1*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('num').node, - decoratedTypeAnnotation('Object/*2*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('String').node, - decoratedTypeAnnotation('Object/*3*/').node, - hard: true); - } - - Future test_assign_to_bound_class_extends() async { - await analyze(''' -class A {} -class C extends A {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_class_implements() async { - await analyze(''' -class A {} -class C implements A {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_class_with() async { - await analyze(''' -class A {} -class C extends Object with A {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_assign_to_bound_extension_extended_type() async { - await analyze(''' -class C {} -extension E on C {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_assign_to_bound_field_formal_typed() async { - await analyze(''' -class C {} -class D { - dynamic i; - D(C this.i); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_field_formal_typed_function() async { - await analyze(''' -class C {} -class D { - dynamic i; - D(this.i(C name)); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_for() async { - await analyze(''' -class C {} -void main() { - for (C c = null ;;) {} -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_assign_to_bound_for_element() async { - await analyze(''' -class C {} -void main() { - [for (C c = null ;;) c]; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_assign_to_bound_for_in() async { - await analyze(''' -class C {} -void main() { - for (C c in []) {} -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_assign_to_bound_for_in_element() async { - await analyze(''' -class C {} -void main() { - [for (C c in []) c]; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_assign_to_bound_function_invocation_type_argument() async { - await analyze(''' -void f() {} -void main() { - (f)(); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_in_return_type() async { - await analyze(''' -class C {} -C f() => null; -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_in_type_argument() async { - await analyze(''' -class C {} -C> f() => null; -'''); - assertEdge(decoratedTypeAnnotation('C').node, - decoratedTypeAnnotation('Object').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_instance_creation() async { - await analyze(''' -class C {} -void main() { - C(); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_list_literal() async { - await analyze(''' -class C {} -void main() { - >[]; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_local_variable() async { - await analyze(''' -class C {} -main() { - C c = null; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_map_literal() async { - await analyze(''' -class C {} -void main() { - , C>{}; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - assertEdge(decoratedTypeAnnotation('String').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_method_bound() async { - await analyze(''' -class C {} -class D { - f>() {} -} -'''); - } - - Future test_assign_to_bound_method_call_type_argument() async { - await analyze(''' -void f() {} -void main() { - f(); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_mixin_implements() async { - await analyze(''' -class A {} -mixin C implements A {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_mixin_on() async { - await analyze(''' -class A {} -mixin C on A {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_mixin_type_parameter_bound() async { - await analyze(''' -class C {} -mixin M> {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_redirecting_constructor_argument() async { - await analyze(''' -class A {} -class C { - factory C() = D>; -} -class D implements C {} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_set_literal() async { - await analyze(''' -class C {} -void main() { - >{}; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_assign_to_bound_within_bound() async { - await analyze(''' -class A {} -class B> {} - '''); - var aBound = decoratedTypeAnnotation('Object').node; - var aBoundInt = decoratedTypeAnnotation('int').node; - assertEdge(aBoundInt, aBound, hard: true); - } - - Future test_assign_to_bound_within_bound_method() async { - await analyze(''' -class C {} -void f>() {} -'''); - var cBound = decoratedTypeAnnotation('Object').node; - var fcInt = decoratedTypeAnnotation('int').node; - assertEdge(fcInt, cBound, hard: true); - } - - Future test_assign_type_parameter_to_bound() async { - await analyze(''' -class C> { - List f(T x) => x; -} -'''); - var boundType = decoratedTypeAnnotation('List>'); - var returnType = decoratedTypeAnnotation('List f'); - var tType = decoratedTypeAnnotation('T x'); - assertEdge(tType.node, returnType.node, hard: true); - assertEdge(boundType.node, returnType.node, hard: false); - // TODO(40622): Should this be a checkable edge? - assertEdge( - boundType.typeArguments[0]!.node, returnType.typeArguments[0]!.node, - hard: false, checkable: false); - } - - Future test_assign_upcast_generic() async { - await analyze(''' -void f(Iterable x) {} -void g(List x) { - f(x); -} -'''); - - var iterableInt = decoratedTypeAnnotation('Iterable'); - var listInt = decoratedTypeAnnotation('List'); - assertEdge(listInt.node, iterableInt.node, hard: true); - assertEdge( - substitutionNode(listInt.typeArguments[0]!.node, inSet(pointsToNever)), - iterableInt.typeArguments[0]!.node, - hard: true, - checkable: false); - } - - Future test_assignment_code_reference() async { - await analyze(''' -void f(int i) { - int j = i; -} -'''); - var edge = assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int j').node, - hard: true); - var codeReference = edge.codeReference!; - expect(codeReference, isNotNull); - expect(codeReference.path, contains('test.dart')); - expect(codeReference.line, 2); - expect(codeReference.column, 11); - } - - Future test_assignmentExpression_compound_dynamic() async { - await analyze(''' -void f(dynamic x, int y) { - x += y; -} -'''); - // No assertions; just making sure this doesn't crash. - } - - Future test_assignmentExpression_compound_simple() async { - var code = ''' -abstract class C { - C operator+(C x); -} -C f(C y, C z) => (y += z); -'''; - await analyze(code); - var targetEdge = assertEdge( - decoratedTypeAnnotation('C y').node, inSet(pointsToNever), - hard: true); - expect( - (graph.getEdgeOrigin(targetEdge) as CompoundAssignmentOrigin) - .node! - .operator - .offset, - code.indexOf('+=')); - assertNullCheck( - checkExpression('z);')!, - assertEdge(decoratedTypeAnnotation('C z').node, - decoratedTypeAnnotation('C x').node, - hard: true)); - var operatorReturnEdge = assertEdge( - decoratedTypeAnnotation('C operator').node, - decoratedTypeAnnotation('C y').node, - hard: false); - expect( - (graph.getEdgeOrigin(operatorReturnEdge) as CompoundAssignmentOrigin) - .node! - .operator - .offset, - code.indexOf('+=')); - var fReturnEdge = assertEdge(decoratedTypeAnnotation('C operator').node, - decoratedTypeAnnotation('C f').node, - hard: false); - assertNullCheck(checkExpression('(y += z)')!, fReturnEdge); - } - - Future test_assignmentExpression_compound_withSubstitution() async { - // Failing due to a side-cast from incorrectly instantiating the operator. - var code = ''' -abstract class C { - C operator+(C x); -} -C f(C y, C z) => (y += z); -'''; - await analyze(code); - var targetEdge = assertEdge( - decoratedTypeAnnotation('C y').node, inSet(pointsToNever), - hard: true); - expect( - (graph.getEdgeOrigin(targetEdge) as CompoundAssignmentOrigin) - .node! - .operator - .offset, - code.indexOf('+=')); - assertNullCheck( - checkExpression('z);')!, - assertEdge(decoratedTypeAnnotation('C z').node, - decoratedTypeAnnotation('C x').node, - hard: true)); - var operatorReturnEdge = assertEdge( - decoratedTypeAnnotation('C operator').node, - decoratedTypeAnnotation('C y').node, - hard: false); - expect( - (graph.getEdgeOrigin(operatorReturnEdge) as CompoundAssignmentOrigin) - .node! - .operator - .offset, - code.indexOf('+=')); - var fReturnEdge = assertEdge(decoratedTypeAnnotation('C operator').node, - decoratedTypeAnnotation('C f').node, - hard: false); - assertNullCheck(checkExpression('(y += z)')!, fReturnEdge); - } - - Future test_assignmentExpression_field() async { - await analyze(''' -class C { - int x = 0; -} -void f(C c, int i) { - c.x = i; -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_assignmentExpression_field_cascaded() async { - await analyze(''' -class C { - int x = 0; -} -void f(C c, int i) { - c..x = i; -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_assignmentExpression_field_target_check() async { - await analyze(''' -class C { - int x = 0; -} -void f(C c, int i) { - c.x = i; -} -'''); - assertNullCheck(checkExpression('c.x')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_assignmentExpression_field_target_check_cascaded() async { - await analyze(''' -class C { - int x = 0; -} -void f(C c, int i) { - c..x = i; -} -'''); - assertNullCheck(checkExpression('c..x')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_assignmentExpression_indexExpression_index() async { - await analyze(''' -class C { - void operator[]=(int a, int b) {} -} -void f(C c, int i, int j) { - c[i] = j; -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int a').node, - hard: true); - } - - Future test_assignmentExpression_indexExpression_return_value() async { - await analyze(''' -class C { - void operator[]=(int a, int b) {} -} -int f(C c, int i, int j) => c[i] = j; -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_assignmentExpression_indexExpression_target_check() async { - await analyze(''' -class C { - void operator[]=(int a, int b) {} -} -void f(C c, int i, int j) { - c[i] = j; -} -'''); - assertNullCheck(checkExpression('c[')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_assignmentExpression_indexExpression_value() async { - await analyze(''' -class C { - void operator[]=(int a, int b) {} -} -void f(C c, int i, int j) { - c[i] = j; -} -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int b').node, - hard: true); - } - - Future - test_assignmentExpression_nullAware_complex_contravariant() async { - await analyze(''' -void Function(int) f(void Function(int) x, void Function(int) y) => x ??= y; -'''); - var xNullable = - decoratedGenericFunctionTypeAnnotation('void Function(int) x').node; - var xParamNullable = decoratedTypeAnnotation('int) x').node; - var yParamNullable = decoratedTypeAnnotation('int) y').node; - var returnParamNullable = decoratedTypeAnnotation('int) f').node; - assertEdge(xParamNullable, yParamNullable, - hard: false, checkable: false, guards: [xNullable]); - assertEdge(returnParamNullable, xParamNullable, - hard: false, checkable: false); - } - - Future test_assignmentExpression_nullAware_complex_covariant() async { - await analyze(''' -List f(List x, List y) => x ??= y; -'''); - var xNullable = decoratedTypeAnnotation('List x').node; - var yNullable = decoratedTypeAnnotation('List y').node; - var xElementNullable = decoratedTypeAnnotation('int> x').node; - var yElementNullable = decoratedTypeAnnotation('int> y').node; - var returnElementNullable = decoratedTypeAnnotation('int> f').node; - assertEdge(yNullable, xNullable, hard: false, guards: [xNullable]); - assertEdge(yElementNullable, xElementNullable, - hard: false, checkable: false, guards: [xNullable]); - assertEdge(xElementNullable, returnElementNullable, - hard: false, checkable: false); - } - - Future test_assignmentExpression_nullAware_simple() async { - await analyze(''' -int f(int x, int y) => (x ??= y); -'''); - var yNullable = decoratedTypeAnnotation('int y').node; - var xNullable = decoratedTypeAnnotation('int x').node; - var returnNullable = decoratedTypeAnnotation('int f').node; - var glbNode = decoratedExpressionType('(x ??= y)')!.node; - assertEdge(yNullable, xNullable, hard: false, guards: [xNullable]); - assertEdge(yNullable, glbNode, hard: false, guards: [xNullable]); - assertEdge(glbNode, xNullable, hard: false); - assertEdge(glbNode, yNullable, hard: false); - assertEdge(glbNode, returnNullable, hard: false); - } - - Future test_assignmentExpression_operands() async { - await analyze(''' -void f(int i, int j) { - i = j; -} -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node, - hard: true); - } - - Future test_assignmentExpression_return_value() async { - await analyze(''' -void f(int i, int j) { - g(i = j); -} -void g(int k) {} -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int k').node, - hard: false); - } - - Future test_assignmentExpression_setter() async { - await analyze(''' -class C { - void set s(int value) {} -} -void f(C c, int i) { - c.s = i; -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int value').node, - hard: true); - } - - Future test_assignmentExpression_setter_null_aware() async { - await analyze(''' -class C { - void set s(int value) {} -} -int f(C c, int i) => (c?.s = i); -'''); - var lubNode = - decoratedExpressionType('(c?.s = i)')!.node as NullabilityNodeForLUB; - expect(lubNode.left, same(decoratedTypeAnnotation('C c').node)); - expect(lubNode.right, same(decoratedTypeAnnotation('int i').node)); - assertEdge(lubNode, decoratedTypeAnnotation('int f').node, hard: false); - } - - Future test_assignmentExpression_setter_target_check() async { - await analyze(''' -class C { - void set s(int value) {} -} -void f(C c, int i) { - c.s = i; -} -'''); - assertNullCheck(checkExpression('c.s')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - @failingTest - Future test_awaitExpression_future_nonNullable() async { - await analyze(''' -Future f() async { - int x = await g(); -} -Future g() async => 3; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - @failingTest - Future test_awaitExpression_future_nullable() async { - await analyze(''' -Future f() async { - int x = await g(); -} -Future g() async => null; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future test_awaitExpression_nonFuture() async { - await analyze(''' -Future f() async { - int x = await 3; -} -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future test_binaryExpression_ampersand_result_not_null() async { - await analyze(''' -int f(int i, int j) => i & j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_ampersandAmpersand() async { - await analyze(''' -bool f(bool i, bool j) => i && j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool i').node); - } - - Future test_binaryExpression_bar_result_not_null() async { - await analyze(''' -int f(int i, int j) => i | j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_barBar() async { - await analyze(''' -bool f(bool i, bool j) => i || j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool i').node); - } - - Future test_binaryExpression_caret_result_not_null() async { - await analyze(''' -int f(int i, int j) => i ^ j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_equal() async { - await analyze(''' -bool f(int i, int j) => i == j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); - } - - Future test_binaryExpression_equal_null() async { - await analyze(''' -void f(int i) { - if (i == null) { - g(i); - } else { - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from i to j - assertEdge(iNode, jNode, hard: false, guards: [iNode]); - } - - Future test_binaryExpression_equal_null_null() async { - await analyze(''' -void f(int i) { - if (null == null) { - g(i); - } -} -void g(int j) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - assertEdge(iNode, jNode, - hard: false, - guards: TypeMatcher>() - .having((g) => g.single, 'single value', isIn(alwaysPlus))); - } - - Future test_binaryExpression_equal_null_yoda_condition() async { - await analyze(''' -void f(int i) { - if (null == i) { - g(i); - } else { - h(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from i to j - assertEdge(iNode, jNode, hard: false, guards: [iNode]); - } - - Future test_binaryExpression_gt_result_not_null() async { - await analyze(''' -bool f(int i, int j) => i > j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); - } - - Future test_binaryExpression_gtEq_result_not_null() async { - await analyze(''' -bool f(int i, int j) => i >= j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); - } - - Future test_binaryExpression_gtGt_result_not_null() async { - await analyze(''' -int f(int i, int j) => i >> j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_left_dynamic() async { - await analyze(''' -Object f(dynamic x, int y) => x + g(y); -int g(int z) => z; -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int z').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int g').node, anyNode); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('Object f').node, - hard: false); - } - - Future test_binaryExpression_lt_result_not_null() async { - await analyze(''' -bool f(int i, int j) => i < j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); - } - - Future test_binaryExpression_ltEq_result_not_null() async { - await analyze(''' -bool f(int i, int j) => i <= j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); - } - - Future test_binaryExpression_ltLt_result_not_null() async { - await analyze(''' -int f(int i, int j) => i << j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_minus_result_not_null() async { - await analyze(''' -int f(int i, int j) => i - j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_notEqual() async { - await analyze(''' -bool f(int i, int j) => i != j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node); - } - - Future test_binaryExpression_notEqual_null() async { - await analyze(''' -void f(int i) { - if (i != null) { - h(i); - } else { - g(i); - } -} -void g(int j) {} -void h(int k) {} -'''); - var iNode = decoratedTypeAnnotation('int i').node; - var jNode = decoratedTypeAnnotation('int j').node; - var kNode = decoratedTypeAnnotation('int k').node; - // No edge from i to k because i is known to be non-nullable at the site of - // the call to h() - assertNoEdge(iNode, kNode); - // But there is an edge from i to j - assertEdge(iNode, jNode, hard: false, guards: [iNode]); - } - - Future test_binaryExpression_percent_result_not_null() async { - await analyze(''' -int f(int i, int j) => i % j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_plus_left_check() async { - await analyze(''' -int f(int i, int j) => i + j; -'''); - - assertNullCheck(checkExpression('i +')!, - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true)); - } - - Future test_binaryExpression_plus_left_check_custom() async { - await analyze(''' -class Int { - Int operator+(Int other) => this; -} -Int f(Int i, Int j) => i + j; -'''); - - assertNullCheck(checkExpression('i +')!, - assertEdge(decoratedTypeAnnotation('Int i').node, never, hard: true)); - } - - Future test_binaryExpression_plus_result_custom() async { - await analyze(''' -class Int { - Int operator+(Int other) => this; -} -Int f(Int i, Int j) => (i + j); -'''); - - assertNullCheck( - checkExpression('(i + j)')!, - assertEdge(decoratedTypeAnnotation('Int operator+').node, - decoratedTypeAnnotation('Int f').node, - hard: false)); - } - - Future test_binaryExpression_plus_result_not_null() async { - await analyze(''' -int f(int i, int j) => i + j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_plus_right_check() async { - await analyze(''' -int f(int i, int j) => i + j; -'''); - - assertNullCheck( - checkExpression('j;')!, - assertEdge(decoratedTypeAnnotation('int j').node, inSet(pointsToNever), - hard: true)); - } - - Future test_binaryExpression_plus_right_check_custom() async { - await analyze(''' -class Int { - Int operator+(Int other) => this; -} -Int f(Int i, Int j) => i + j/*check*/; -'''); - - assertNullCheck( - checkExpression('j/*check*/')!, - assertEdge(decoratedTypeAnnotation('Int j').node, - decoratedTypeAnnotation('Int other').node, - hard: true)); - } - - Future test_binaryExpression_plus_substituted() async { - await analyze(''' -class _C { - T operator+(U u) => throw 'foo'; -} -Object _f(_C c, String s) => c + s; -'''); - assertEdge( - decoratedTypeAnnotation('String s').node, - substitutionNode(decoratedTypeAnnotation('String>').node, - decoratedTypeAnnotation('U u').node), - hard: true); - assertEdge( - substitutionNode(decoratedTypeAnnotation('int,').node, - decoratedTypeAnnotation('T operator').node), - decoratedTypeAnnotation('Object _f').node, - hard: false); - } - - Future test_binaryExpression_questionQuestion() async { - await analyze(''' -int f(int i, int j) => i ?? j; -'''); - - var left = decoratedTypeAnnotation('int i').node; - var right = decoratedTypeAnnotation('int j').node; - var expression = decoratedExpressionType('??')!.node; - assertEdge(right, expression, guards: [left], hard: false); - expect(expression.displayName, '?? operator (test.dart:1:24)'); - } - - Future - test_binaryExpression_questionQuestion_genericReturnType() async { - await analyze(''' -class C { - C operator +(C c) => this; -} -C f(C i, C j) => i ?? j; -'''); - } - - Future test_binaryExpression_right_dynamic() async { - await analyze(''' -class C { - C operator+(C other) => other; -} -C f(C x, dynamic y) => x + y; -'''); - assertNullCheck(checkExpression('x +')!, - assertEdge(decoratedTypeAnnotation('C x').node, never, hard: true)); - assertEdge(decoratedTypeAnnotation('C operator').node, - decoratedTypeAnnotation('C f').node, - hard: false); - } - - Future test_binaryExpression_slash_result_not_null() async { - await analyze(''' -double f(int i, int j) => i / j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('double f').node); - } - - Future test_binaryExpression_star_result_not_null() async { - await analyze(''' -int f(int i, int j) => i * j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_binaryExpression_tildeSlash_result_not_null() async { - await analyze(''' -int f(int i, int j) => i ~/ j; -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node); - } - - Future test_boolLiteral() async { - await analyze(''' -bool f() { - return true; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); - } - - Future test_cascadeExpression() async { - await analyze(''' -class C { - int x = 0; -} -C f(C c, int i) => c..x = i; -'''); - assertEdge(decoratedTypeAnnotation('C c').node, - decoratedTypeAnnotation('C f').node, - hard: false); - } - - Future test_cast_type_used_as_non_nullable() async { - await analyze(''' -void f(int/*!*/ i) {} -void g(num/*?*/ j) { - f(j as int); -} -'''); - assertEdge(decoratedTypeAnnotation('int)').node, - decoratedTypeAnnotation('int/*!*/').node, - hard: true); - } - - Future test_catch_clause() async { - await analyze(''' -foo() => 1; -main() { - try { foo(); } on Exception catch (e) { print(e); } -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_catch_clause_no_type() async { - await analyze(''' -foo() => 1; -main() { - try { foo(); } catch (e) { print(e); } -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future - test_class_alias_synthetic_constructor_with_parameters_complex() async { - await analyze(''' -class MyList {} -class C { - C(MyList/*1*/ x); -} -mixin M {} -class D = C with M; -D f(MyList/*2*/ x) => D(x); -'''); - var syntheticConstructor = findElement.unnamedConstructor('D'); - var constructorType = variables.decoratedElementType(syntheticConstructor); - var constructorParameterType = constructorType.positionalParameters![0]; - assertEdge(decoratedTypeAnnotation('MyList/*2*/').node, - constructorParameterType.node, - hard: true); - assertEdge(decoratedTypeAnnotation('int>/*2*/').node, - constructorParameterType.typeArguments[0]!.node, - hard: true, checkable: false); - assertUnion(constructorParameterType.node, - decoratedTypeAnnotation('MyList/*1*/').node); - assertUnion(constructorParameterType.typeArguments[0]!.node, - decoratedTypeAnnotation('int>/*1*/').node); - } - - Future - test_class_alias_synthetic_constructor_with_parameters_generic() async { - await analyze(''' -class C { - C(T t); -} -mixin M {} -class D = C with M; -'''); - var syntheticConstructor = findElement.unnamedConstructor('D'); - var constructorType = variables.decoratedElementType(syntheticConstructor); - var constructorParameterType = constructorType.positionalParameters![0]; - assertUnion( - constructorParameterType.node, decoratedTypeAnnotation('T t').node); - } - - Future - test_class_alias_synthetic_constructor_with_parameters_named() async { - await analyze(''' -class C { - C({int/*1*/ i}); -} -mixin M {} -class D = C with M; -D f(int/*2*/ i) => D(i: i); -'''); - var syntheticConstructor = findElement.unnamedConstructor('D'); - var constructorType = variables.decoratedElementType(syntheticConstructor); - var constructorParameterType = constructorType.namedParameters!['i']!; - assertEdge( - decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node, - hard: true); - assertUnion(constructorParameterType.node, - decoratedTypeAnnotation('int/*1*/').node); - } - - Future - test_class_alias_synthetic_constructor_with_parameters_optional() async { - await analyze(''' -class C { - C([int/*1*/ i]); -} -mixin M {} -class D = C with M; -D f(int/*2*/ i) => D(i); -'''); - var syntheticConstructor = findElement.unnamedConstructor('D'); - var constructorType = variables.decoratedElementType(syntheticConstructor); - var constructorParameterType = constructorType.positionalParameters![0]; - assertEdge( - decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node, - hard: true); - assertUnion(constructorParameterType.node, - decoratedTypeAnnotation('int/*1*/').node); - } - - Future - test_class_alias_synthetic_constructor_with_parameters_required() async { - await analyze(''' -class C { - C(int/*1*/ i); -} -mixin M {} -class D = C with M; -D f(int/*2*/ i) => D(i); -'''); - var syntheticConstructor = findElement.unnamedConstructor('D'); - var constructorType = variables.decoratedElementType(syntheticConstructor); - var constructorParameterType = constructorType.positionalParameters![0]; - assertEdge( - decoratedTypeAnnotation('int/*2*/').node, constructorParameterType.node, - hard: true); - assertUnion(constructorParameterType.node, - decoratedTypeAnnotation('int/*1*/').node); - } - - Future test_class_metadata() async { - await analyze(''' -@deprecated -class C {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_conditionalExpression_condition_check() async { - await analyze(''' -int f(bool b, int i, int j) { - return (b ? i : j); -} -'''); - - var nullable_b = decoratedTypeAnnotation('bool b').node; - var check_b = checkExpression('b ?')!; - assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true)); - } - - Future test_conditionalExpression_false_guard() async { - await analyze('int f(int x, int y, int z) => x != null ? null : y = z;'); - var guard = decoratedTypeAnnotation('int x').node; - assertEdge(decoratedTypeAnnotation('int z').node, - decoratedTypeAnnotation('int y').node, - hard: false, guards: [guard]); - var conditionalDiscard = - variables.conditionalDiscard(findNode.conditionalExpression('!='))!; - expect(conditionalDiscard, isNotNull); - expect(conditionalDiscard.trueGuard, isNull); - expect(conditionalDiscard.falseGuard, same(guard)); - } - - Future test_conditionalExpression_functionTyped_namedParameter() async { - await analyze(''' -void f(bool b, void Function({int p}) x, void Function({int p}) y) { - (b ? x : y); -} -'''); - var xType = - decoratedGenericFunctionTypeAnnotation('void Function({int p}) x'); - var yType = - decoratedGenericFunctionTypeAnnotation('void Function({int p}) y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertGLB(resultType.namedParameters!['p']!.node, - xType.namedParameters!['p']!.node, yType.namedParameters!['p']!.node); - } - - Future - test_conditionalExpression_functionTyped_normalParameter() async { - await analyze(''' -void f(bool b, void Function(int) x, void Function(int) y) { - (b ? x : y); -} -'''); - var xType = decoratedGenericFunctionTypeAnnotation('void Function(int) x'); - var yType = decoratedGenericFunctionTypeAnnotation('void Function(int) y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertGLB( - resultType.positionalParameters![0].node, - xType.positionalParameters![0].node, - yType.positionalParameters![0].node); - } - - Future - test_conditionalExpression_functionTyped_normalParameters() async { - await analyze(''' -void f(bool b, void Function(int, int) x, void Function(int, int) y) { - (b ? x : y); -} -'''); - var xType = - decoratedGenericFunctionTypeAnnotation('void Function(int, int) x'); - var yType = - decoratedGenericFunctionTypeAnnotation('void Function(int, int) y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertGLB( - resultType.positionalParameters![0].node, - xType.positionalParameters![0].node, - yType.positionalParameters![0].node); - assertGLB( - resultType.positionalParameters![1].node, - xType.positionalParameters![1].node, - yType.positionalParameters![1].node); - } - - Future - test_conditionalExpression_functionTyped_optionalParameter() async { - await analyze(''' -void f(bool b, void Function([int]) x, void Function([int]) y) { - (b ? x : y); -} -'''); - var xType = - decoratedGenericFunctionTypeAnnotation('void Function([int]) x'); - var yType = - decoratedGenericFunctionTypeAnnotation('void Function([int]) y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertGLB( - resultType.positionalParameters![0].node, - xType.positionalParameters![0].node, - yType.positionalParameters![0].node); - } - - Future test_conditionalExpression_functionTyped_returnType() async { - await analyze(''' -void f(bool b, int Function() x, int Function() y) { - (b ? x : y); -} -'''); - var xType = decoratedGenericFunctionTypeAnnotation('int Function() x'); - var yType = decoratedGenericFunctionTypeAnnotation('int Function() y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertLUB(resultType.returnType!.node, xType.returnType!.node, - yType.returnType!.node); - } - - Future - test_conditionalExpression_functionTyped_returnType_void() async { - await analyze(''' -void f(bool b, void Function() x, void Function() y) { - (b ? x : y); -} -'''); - var xType = decoratedGenericFunctionTypeAnnotation('void Function() x'); - var yType = decoratedGenericFunctionTypeAnnotation('void Function() y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - expect(resultType.returnType!.node.isImmutable, false); - } - - Future test_conditionalExpression_general() async { - await analyze(''' -int f(bool b, int i, int j) { - return (b ? i : j); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, nullable_i, nullable_j); - var nullable_return = decoratedTypeAnnotation('int f').node; - assertNullCheck(checkExpression('(b ? i : j)')!, - assertEdge(nullable_conditional, nullable_return, hard: false)); - } - - Future test_conditionalExpression_generic() async { - await analyze(''' -void f(bool b, Map x, Map y) { - (b ? x : y); -} -'''); - var xType = decoratedTypeAnnotation('Map x'); - var yType = decoratedTypeAnnotation('Map y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertLUB(resultType.typeArguments[0]!.node, xType.typeArguments[0]!.node, - yType.typeArguments[0]!.node); - assertLUB(resultType.typeArguments[1]!.node, xType.typeArguments[1]!.node, - yType.typeArguments[1]!.node); - } - - Future test_conditionalExpression_generic_lub() async { - await analyze(''' -class A {} -class B extends A {} -class C extends A {} -A f(bool b, B x, C y) { - return (b ? x : y); -} -'''); - var bType = decoratedTypeAnnotation('B x'); - var cType = decoratedTypeAnnotation('C y'); - var bInA = decoratedTypeAnnotation('T/*b*/'); - var cInA = decoratedTypeAnnotation('T/*c*/'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, bType.node, cType.node); - assertLUB( - resultType.typeArguments[0]!.node, - substitutionNode(bType.typeArguments[0]!.node, bInA.node), - substitutionNode(cType.typeArguments[0]!.node, cInA.node)); - } - - Future test_conditionalExpression_generic_lub_leftSubtype() async { - await analyze(''' -class A {} -class B extends A {} -A f(bool b, B x, A y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('A y'); - var bType = decoratedTypeAnnotation('B x'); - var bInA = decoratedTypeAnnotation('T/*b*/'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, bType.node, aType.node); - assertLUB( - resultType.typeArguments[0]!.node, - substitutionNode(bType.typeArguments[0]!.node, bInA.node), - aType.typeArguments[0]!.node); - } - - Future test_conditionalExpression_generic_lub_rightSubtype() async { - await analyze(''' -class A {} -class B extends A {} -A f(bool b, A x, B y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('A x'); - var bType = decoratedTypeAnnotation('B y'); - var bInA = decoratedTypeAnnotation('T/*b*/'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, aType.node, bType.node); - assertLUB(resultType.typeArguments[0]!.node, aType.typeArguments[0]!.node, - substitutionNode(bType.typeArguments[0]!.node, bInA.node)); - } - - Future test_conditionalExpression_generic_typeParameter_bound() async { - await analyze(''' -List f>(bool b, List x, T y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('List x'); - var bType = decoratedTypeAnnotation('T y'); - var bBound = decoratedTypeAnnotation('List>'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB( - resultType.node, aType.node, substitutionNode(bBound.node, bType.node)); - assertLUB(resultType.typeArguments[0]!.node, aType.typeArguments[0]!.node, - bBound.typeArguments[0]!.node); - } - - Future test_conditionalExpression_left_never() async { - await analyze(''' -List f(bool b, List i) { - return (b ? (throw i) : i); -} -'''); - - var nullable_i = decoratedTypeAnnotation('List i').node; - var nullable_conditional = - decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; - var nullable_throw = nullable_conditional.left; - assertNoUpstreamNullability(nullable_throw); - assertLUB(nullable_conditional, nullable_throw, nullable_i); - } - - Future test_conditionalExpression_left_non_null() async { - await analyze(''' -int f(bool b, int i) { - return (b ? (throw i) : i); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_conditional = - decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; - var nullable_throw = nullable_conditional.left; - assertNoUpstreamNullability(nullable_throw); - assertLUB(nullable_conditional, nullable_throw, nullable_i); - } - - Future test_conditionalExpression_left_null() async { - await analyze(''' -int f(bool b, int i) { - return (b ? null : i); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i); - } - - Future test_conditionalExpression_left_null_right_function() async { - await analyze(''' -bool Function(int) g(bool b, bool Function(int) f) { - return (b ? null : f); -} -'''); - - var nullable_i = - decoratedGenericFunctionTypeAnnotation('bool Function(int) f').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i); - } - - Future - test_conditionalExpression_left_null_right_parameterType() async { - await analyze(''' -T g(bool b, T t) { - return (b ? null : t); -} -'''); - - var nullable_t = decoratedTypeAnnotation('T t').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_t); - } - - Future test_conditionalExpression_left_null_right_typeArgs() async { - await analyze(''' -List f(bool b, List l) { - return (b ? null : l); -} -'''); - - var nullable_i = decoratedTypeAnnotation('List l').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, inSet(alwaysPlus), nullable_i); - } - - Future test_conditionalExpression_nullTyped_nullParameter() async { - await analyze(''' -void f(bool b, void Function(Null p) x, void Function(List p) y) { - (b ? x : y); -} -'''); - var xType = - decoratedGenericFunctionTypeAnnotation('void Function(Null p) x'); - var yType = - decoratedGenericFunctionTypeAnnotation('void Function(List p) y'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, xType.node, yType.node); - assertGLB( - resultType.positionalParameters![0].node, - xType.positionalParameters![0].node, - yType.positionalParameters![0].node); - } - - Future test_conditionalExpression_parameterType() async { - await analyze(''' -T g(bool b, T x, T y) { - return (b ? x : y); -} -'''); - - var nullable_x = decoratedTypeAnnotation('T x').node; - var nullable_y = decoratedTypeAnnotation('T y').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, nullable_x, nullable_y); - } - - Future test_conditionalExpression_right_never() async { - await analyze(''' -List f(bool b, List i) { - return (b ? i : (throw i)); -} -'''); - - var nullable_i = decoratedTypeAnnotation('List i').node; - var nullable_conditional = - decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; - var nullable_throw = nullable_conditional.right; - assertNoUpstreamNullability(nullable_throw); - assertLUB(nullable_conditional, nullable_i, nullable_throw); - } - - Future test_conditionalExpression_right_non_null() async { - await analyze(''' -int f(bool b, int i) { - return (b ? i : (throw i)); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_conditional = - decoratedExpressionType('(b ?')!.node as NullabilityNodeForLUB; - var nullable_throw = nullable_conditional.right; - assertNoUpstreamNullability(nullable_throw); - assertLUB(nullable_conditional, nullable_i, nullable_throw); - } - - Future test_conditionalExpression_right_null() async { - await analyze(''' -int f(bool b, int i) { - return (b ? i : null); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus)); - } - - Future test_conditionalExpression_right_null_left_function() async { - await analyze(''' -bool Function(int) g(bool b, bool Function(int) f) { - return (b ? f : null); -} -'''); - - var nullable_i = - decoratedGenericFunctionTypeAnnotation('bool Function(int) f').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus)); - } - - Future test_conditionalExpression_right_null_left_typeArgs() async { - await analyze(''' -List f(bool b, List l) { - return (b ? l : null); -} -'''); - - var nullable_i = decoratedTypeAnnotation('List l').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, nullable_i, inSet(alwaysPlus)); - } - - Future - test_conditionalExpression_right_null_left_typeParameter() async { - await analyze(''' -T f(bool b, T t) { - return (b ? t : null); -} -'''); - - var nullable_t = decoratedTypeAnnotation('T t').node; - var nullable_conditional = decoratedExpressionType('(b ?')!.node; - assertLUB(nullable_conditional, nullable_t, inSet(alwaysPlus)); - } - - Future test_conditionalExpression_true_guard() async { - await analyze('int f(int x, int y, int z) => x == null ? y = z : null;'); - var guard = decoratedTypeAnnotation('int x').node; - assertEdge(decoratedTypeAnnotation('int z').node, - decoratedTypeAnnotation('int y').node, - hard: false, guards: [guard]); - var conditionalDiscard = - variables.conditionalDiscard(findNode.conditionalExpression('=='))!; - expect(conditionalDiscard, isNotNull); - expect(conditionalDiscard.trueGuard, same(guard)); - expect(conditionalDiscard.falseGuard, isNull); - } - - Future test_conditionalExpression_typeParameter_bound() async { - await analyze(''' -num f(bool b, num x, T y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('num x'); - var bType = decoratedTypeAnnotation('T y'); - var bBound = decoratedTypeAnnotation('num>'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB( - resultType.node, aType.node, substitutionNode(bBound.node, bType.node)); - } - - Future test_conditionalExpression_typeParameter_bound_bound() async { - await analyze(''' -num f(bool b, num x, T y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('num x'); - var bType = decoratedTypeAnnotation('T y'); - var bBound = decoratedTypeAnnotation('R,'); - var bBoundBound = decoratedTypeAnnotation('num>'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB( - resultType.node, - aType.node, - substitutionNode( - bBoundBound.node, substitutionNode(bBound.node, bType.node))); - } - - Future test_conditionalExpression_typeParameter_dynamic() async { - // "dynamic" can short circuit LUB, incorrectly we may lose nullabilities. - await analyze(''' -dynamic f(bool b, dynamic x, T y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('dynamic x'); - var bType = decoratedTypeAnnotation('T y'); - var bBound = decoratedTypeAnnotation('num>'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB( - resultType.node, aType.node, substitutionNode(bBound.node, bType.node)); - } - - Future test_conditionalExpression_typeParameters_bound() async { - await analyze(''' -num f(bool b, R x, T y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('R x'); - var bType = decoratedTypeAnnotation('T y'); - var aBound = decoratedTypeAnnotation('num>'); - var bBound = decoratedTypeAnnotation('num,'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB(resultType.node, substitutionNode(aBound.node, aType.node), - substitutionNode(bBound.node, bType.node)); - } - - Future - test_conditionalExpression_typeParameters_bound_left_to_right() async { - await analyze(''' -R f(bool b, R x, T y) { - return (b ? x : y); -} -'''); - var aType = decoratedTypeAnnotation('R x'); - var bType = decoratedTypeAnnotation('T y'); - var bBound = decoratedTypeAnnotation('R,'); - var resultType = decoratedExpressionType('(b ?')!; - assertLUB( - resultType.node, aType.node, substitutionNode(bBound.node, bType.node)); - } - - Future test_constructor_default_parameter_value_bool() async { - await analyze(''' -class C { - C([bool b = true]); -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool b').node); - } - - Future test_constructor_named() async { - await analyze(''' -class C { - C.named(); -} -'''); - // No assertions; just need to make sure that the test doesn't cause an - // exception to be thrown. - } - - Future test_constructor_superInitializer() async { - await analyze(''' -class C { - C.named(int i); -} -class D extends C { - D(int j) : super.named(j); -} -'''); - - var namedConstructor = findElement.constructor('named', of: 'C'); - var constructorType = variables.decoratedElementType(namedConstructor); - var constructorParameterType = constructorType.positionalParameters![0]; - assertEdge( - decoratedTypeAnnotation('int j').node, constructorParameterType.node, - hard: true); - } - - Future test_constructor_superInitializer_withTypeArgument() async { - await analyze(''' -class C { - C.named(T/*1*/ i); -} -class D extends C { - D(int/*3*/ j) : super.named(j); -} -'''); - - var nullable_t1 = decoratedTypeAnnotation('T/*1*/').node; - var nullable_int2 = decoratedTypeAnnotation('int/*2*/').node; - var nullable_int3 = decoratedTypeAnnotation('int/*3*/').node; - assertEdge(nullable_int3, substitutionNode(nullable_int2, nullable_t1), - hard: true); - } - - Future test_constructor_superInitializer_withTypeVariable() async { - await analyze(''' -class C { - C.named(T/*1*/ i); -} -class D extends C { - D(U/*3*/ j) : super.named(j); -} -'''); - - var nullable_t1 = decoratedTypeAnnotation('T/*1*/').node; - var nullable_u2 = decoratedTypeAnnotation('U/*2*/').node; - var nullable_u3 = decoratedTypeAnnotation('U/*3*/').node; - assertEdge(nullable_u3, substitutionNode(nullable_u2, nullable_t1), - hard: true); - } - - Future test_constructorDeclaration_returnType_generic() async { - await analyze(''' -class C { - C(); -} -'''); - var constructor = findElement.unnamedConstructor('C'); - var constructorDecoratedType = variables.decoratedElementType(constructor); - _assertType(constructorDecoratedType.type!, 'C Function()'); - expect(constructorDecoratedType.node, same(never)); - expect(constructorDecoratedType.typeFormals, isEmpty); - expect(constructorDecoratedType.returnType!.node, same(never)); - _assertType(constructorDecoratedType.returnType!.type!, 'C'); - var typeArguments = constructorDecoratedType.returnType!.typeArguments; - expect(typeArguments, hasLength(2)); - _assertType(typeArguments[0]!.type!, 'T'); - expect(typeArguments[0]!.node, same(never)); - _assertType(typeArguments[1]!.type!, 'U'); - expect(typeArguments[1]!.node, same(never)); - } - - Future test_constructorDeclaration_returnType_generic_implicit() async { - await analyze(''' -class C {} -'''); - var constructor = findElement.unnamedConstructor('C'); - var constructorDecoratedType = variables.decoratedElementType(constructor); - _assertType(constructorDecoratedType.type!, 'C Function()'); - expect(constructorDecoratedType.node, same(never)); - expect(constructorDecoratedType.typeFormals, isEmpty); - expect(constructorDecoratedType.returnType!.node, same(never)); - _assertType(constructorDecoratedType.returnType!.type!, 'C'); - var typeArguments = constructorDecoratedType.returnType!.typeArguments; - expect(typeArguments, hasLength(2)); - _assertType(typeArguments[0]!.type!, 'T'); - expect(typeArguments[0]!.node, same(never)); - _assertType(typeArguments[1]!.type!, 'U'); - expect(typeArguments[1]!.node, same(never)); - } - - Future test_constructorDeclaration_returnType_simple() async { - await analyze(''' -class C { - C(); -} -'''); - var constructorDecoratedType = - variables.decoratedElementType(findElement.unnamedConstructor('C')); - _assertType(constructorDecoratedType.type!, 'C Function()'); - expect(constructorDecoratedType.node, same(never)); - expect(constructorDecoratedType.typeFormals, isEmpty); - expect(constructorDecoratedType.returnType!.node, same(never)); - expect(constructorDecoratedType.returnType!.typeArguments, isEmpty); - } - - Future test_constructorDeclaration_returnType_simple_implicit() async { - await analyze(''' -class C {} -'''); - var constructorDecoratedType = - variables.decoratedElementType(findElement.unnamedConstructor('C')); - _assertType(constructorDecoratedType.type!, 'C Function()'); - expect(constructorDecoratedType.node, same(never)); - expect(constructorDecoratedType.typeFormals, isEmpty); - expect(constructorDecoratedType.returnType!.node, same(never)); - expect(constructorDecoratedType.returnType!.typeArguments, isEmpty); - } - - Future test_constructorFieldInitializer_generic() async { - await analyze(''' -class C { - C(T/*1*/ x) : f = x; - T/*2*/ f; -} -'''); - assertEdge(decoratedTypeAnnotation('T/*1*/').node, - decoratedTypeAnnotation('T/*2*/').node, - hard: true); - } - - Future test_constructorFieldInitializer_simple() async { - await analyze(''' -class C { - C(int/*1*/ i) : f = i; - int/*2*/ f; -} -'''); - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*2*/').node, - hard: true); - } - - Future test_constructorFieldInitializer_via_this() async { - await analyze(''' -class C { - C(int/*1*/ i) : this.f = i; - int/*2*/ f; -} -'''); - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*2*/').node, - hard: true); - } - - Future test_do_while_condition() async { - await analyze(''' -void f(bool b) { - do {} while (b); -} -'''); - - assertNullCheck(checkExpression('b);')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - } - - Future test_doubleLiteral() async { - await analyze(''' -double f() { - return 1.0; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('double').node); - } - - Future test_dummyNode_fromEqualityComparison_left() async { - await analyze(''' -f() { - int i; - if (i == 7) {} -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - assertDummyEdge(nullable_i); - } - - Future test_dummyNode_fromEqualityComparison_right() async { - await analyze(''' -f() { - int i; - if (7 == i) {} -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - assertDummyEdge(nullable_i); - } - - Future test_dummyNode_fromExpressionStatement() async { - await analyze(''' -f() { - int i; - i; -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - assertDummyEdge(nullable_i); - } - - Future test_dummyNode_fromForLoopUpdaters() async { - await analyze(''' -f() { - int i; - int j; - for (;; i, j) {} -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - assertDummyEdge(nullable_i); - assertDummyEdge(nullable_j); - } - - Future test_dummyNode_fromForLoopVariables() async { - await analyze(''' -f() { - int i; - for (i;;) {} -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - assertDummyEdge(nullable_i); - } - - Future test_edgeOrigin_call_from_function() async { - await analyze(''' -void f(int i) {} -void g(int j) { - f(j); -} -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node, - hard: true, - codeReference: - matchCodeRef(offset: findNode.simple('j);').offset, function: 'g')); - } - - Future test_edgeOrigin_call_from_method() async { - await analyze(''' -class C { - void f(int i) {} - void g(int j) { - f(j); - } -} -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node, - hard: true, - codeReference: matchCodeRef( - offset: findNode.simple('j);').offset, function: 'C.g')); - } - - Future test_export_metadata() async { - await analyze(''' -@deprecated -export 'dart:async'; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_extension_metadata() async { - await analyze(''' -@deprecated -extension E on String {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_extension_on_class_with_generic_type_arguments() async { - await analyze(''' -class C {} -void f(C x) {} -extension E on C { - g() => f(this); -} -'''); - // No assertions yet. This test crashes. When it stops crashing, consider - // adding assertion(s). - } - - Future test_extension_on_function_type() async { - await analyze(''' -extension CurryFunction on R Function(S, T) { - /// Curry a binary function with its first argument. - R Function(T) curry(S first) => (T second) => this(first, second); -} -'''); - // No assertions yet. This test crashes. When it stops crashing, consider - // adding assertion(s). - } - - Future test_extension_this_non_null_intent_explicit_direct() async { - await analyze(''' -extension on int { - f() => g(this); -} -void g(int i) {} -'''); - assertEdge(decoratedTypeAnnotation('int {').node, - decoratedTypeAnnotation('int i').node, - hard: true); - } - - Future test_extension_this_non_null_intent_explicit_method() async { - await analyze(''' -extension on int { - f() => this.abs(); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); - } - - Future - test_extension_this_non_null_intent_explicit_property_get() async { - await analyze(''' -extension on int { - f() => this.isEven; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); - } - - Future - test_extension_this_non_null_intent_explicit_property_set() async { - await analyze(''' -class C { - int x; -} -extension on C /*reference*/ { - f() { - this.x = 0; - } -} -'''); - assertEdge(decoratedTypeAnnotation('C /*reference*/').node, never, - hard: true); - } - - Future test_extension_this_non_null_intent_implicit_method() async { - await analyze(''' -extension on int { - f() => abs(); -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); - } - - Future test_extension_this_non_null_intent_implicit_property() async { - await analyze(''' -extension on int { - f() => isEven; -} -'''); - assertEdge(decoratedTypeAnnotation('int').node, never, hard: true); - } - - Future - test_extension_this_non_null_intent_implicit_property_set() async { - await analyze(''' -class C { - int x; -} -extension on C /*reference*/ { - f() { - x = 0; - } -} -'''); - assertEdge(decoratedTypeAnnotation('C /*reference*/').node, never, - hard: true); - } - - Future test_field_final_does_not_override_setter() async { - await analyze(''' -abstract class A { - void set i(int value); -} -abstract class C implements A { - final int i; - C(this.i); -} -'''); - var baseNode = decoratedTypeAnnotation('int value').node; - var derivedNode = decoratedTypeAnnotation('int i').node; - assertNoEdge(derivedNode, baseNode); - assertNoEdge(baseNode, derivedNode); - } - - Future test_field_initialized_in_constructor() async { - await analyze(''' -class C { - int i; - C() : i = 0; -} -'''); - // There is no edge from always to the type of i, because it is initialized - // in the constructor. - assertNoEdge(always, decoratedTypeAnnotation('int').node); - } - - Future test_field_metadata() async { - await analyze(''' -class A { - const A(); -} -class C { - @A() - int f; -} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_field_overrides_field() async { - await analyze(''' -abstract class A { - int i; // A -} -class C implements A { - int i; // C -} -'''); - var baseNode = decoratedTypeAnnotation('int i; // A').node; - var derivedNode = decoratedTypeAnnotation('int i; // C').node; - assertEdge(baseNode, derivedNode, hard: true); - assertEdge(derivedNode, baseNode, hard: true); - } - - Future test_field_overrides_field_final() async { - await analyze(''' -abstract class A { - final int i; // A - A(this.i); -} -class C implements A { - int i; // C -} -'''); - var baseNode = decoratedTypeAnnotation('int i; // A').node; - var derivedNode = decoratedTypeAnnotation('int i; // C').node; - assertEdge(derivedNode, baseNode, hard: true); - assertNoEdge(baseNode, derivedNode); - } - - Future test_field_overrides_getter() async { - await analyze(''' -abstract class A { - int get i; -} -class C implements A { - int i; -} -'''); - var baseNode = decoratedTypeAnnotation('int get i').node; - var derivedNode = decoratedTypeAnnotation('int i').node; - assertEdge(derivedNode, baseNode, hard: true); - assertNoEdge(baseNode, derivedNode); - } - - Future test_field_overrides_setter() async { - await analyze(''' -abstract class A { - void set i(int value); -} -class C implements A { - int i; -} -'''); - var baseNode = decoratedTypeAnnotation('int value').node; - var derivedNode = decoratedTypeAnnotation('int i').node; - assertEdge(baseNode, derivedNode, hard: true); - assertNoEdge(derivedNode, baseNode); - } - - Future test_field_static_implicitInitializer() async { - await analyze(''' -class C { - static int i; -} -'''); - assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); - } - - Future test_field_type_inferred() async { - await analyze(''' -int f() => 1; -class C { - var x = f(); -} -'''); - var xType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - assertEdge(decoratedTypeAnnotation('int').node, xType.node, hard: false); - } - - Future test_fieldFormalParameter_function_typed() async { - await analyze(''' -class C { - int Function(int, {int j}) f; - C(int this.f(int i, {int j})); -} -'''); - var ctorParamType = variables - .decoratedElementType(findElement.unnamedConstructor('C')) - .positionalParameters![0]; - var fieldType = variables.decoratedElementType(findElement.field('f')); - assertEdge(ctorParamType.node, fieldType.node, hard: true); - assertEdge(ctorParamType.returnType!.node, fieldType.returnType!.node, - hard: false, checkable: false); - assertEdge(fieldType.positionalParameters![0].node, - ctorParamType.positionalParameters![0].node, - hard: false, checkable: false); - assertEdge(fieldType.namedParameters!['j']!.node, - ctorParamType.namedParameters!['j']!.node, - hard: false, checkable: false); - } - - Future test_fieldFormalParameter_typed() async { - await analyze(''' -class C { - int i; - C(int this.i); -} -'''); - assertEdge(decoratedTypeAnnotation('int this').node, - decoratedTypeAnnotation('int i').node, - hard: true); - } - - Future test_fieldFormalParameter_untyped() async { - await analyze(''' -class C { - int i; - C.named(this.i); -} -'''); - var decoratedConstructorParamType = - decoratedConstructorDeclaration('named').positionalParameters![0]; - assertEdge(decoratedConstructorParamType.node, - decoratedTypeAnnotation('int i').node, - hard: true); - } - - Future test_firstWhere_edges() async { - await analyze(''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -'''); - - // Normally there would be an edge from the return type of `() => null` to - // a substitution node that pointed to the type argument to the type of `x`, - // and another substitution node would point from this to the return type of - // `firstEven`. However, since we may replace `firstWhere` with - // `firstWhereOrNull` in order to avoid having to make `x`'s type argument - // nullable, we need a synthetic edge to ensure that the return type of - // `firstEven` is nullable. - var closureReturnType = decoratedExpressionType('() => null')!.returnType!; - var firstWhereReturnType = variables - .decoratedExpressionType(findNode.methodInvocation('firstWhere'))!; - assertEdge(closureReturnType.node, firstWhereReturnType.node, hard: false); - - // There should also be an edge from a substitution node to the return type - // of `firstWhere`, to account for the normal data flow (when the element is - // found). - var typeParameterType = decoratedTypeAnnotation('int>'); - var firstWhereType = variables.decoratedElementType(findNode - .methodInvocation('firstWhere') - .methodName - .staticElement! - .declaration!); - assertEdge( - substitutionNode( - typeParameterType.node, firstWhereType.returnType!.node), - firstWhereReturnType.node, - hard: false); - } - - Future test_for_each_element_with_declaration() async { - await analyze(''' -void f(List l) { - [for (int i in l) 0]; -} -'''); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> l').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int i').node, - hard: false); - } - - Future test_for_each_element_with_declaration_implicit_type() async { - await analyze(''' -void f(List l) { - [for (var i in l) _g(i)]; -} -int _g(int j) => 0; -'''); - var jNode = decoratedTypeAnnotation('int j').node; - var iMatcher = anyNode; - assertEdge(iMatcher, jNode, hard: false); - var iNode = iMatcher.matchingNode; - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> l').node, inSet(pointsToNever)), - iNode, - hard: false); - } - - Future test_for_each_element_with_identifier() async { - await analyze(''' -void f(List l) { - int x; - [for (x in l) 0]; -} -'''); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> l').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int x').node, - hard: false); - } - - Future test_for_each_on_type_parameter_type() async { - await analyze(''' -void f>(T l) { - for (int i in l) {} -} -'''); - // TODO(mfairhurst): fix this: https://github.com/dart-lang/sdk/issues/39852 - //assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge(decoratedTypeAnnotation('T l').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int>').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int i').node, - hard: false); - } - - Future test_for_each_on_type_parameter_type_bound_bound() async { - await analyze(''' -void f>(T l) { - for (int i in l) {} -} -'''); - // TODO(mfairhurst): fix this: https://github.com/dart-lang/sdk/issues/39852 - //assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge(decoratedTypeAnnotation('T l').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int>').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int i').node, - hard: false); - } - - Future test_for_each_with_declaration() async { - await analyze(''' -void f(List l) { - for (int i in l) {} -} -'''); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> l').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int i').node, - hard: false); - } - - Future test_for_each_with_declaration_implicit_type() async { - await analyze(''' -void f(List l) { - for (var i in l) { - _g(i); - } -} -void _g(int j) {} -'''); - var jNode = decoratedTypeAnnotation('int j').node; - var iMatcher = anyNode; - assertEdge(iMatcher, jNode, hard: false); - var iNode = iMatcher.matchingNode; - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> l').node, inSet(pointsToNever)), - iNode, - hard: false); - } - - Future test_for_each_with_identifier() async { - await analyze(''' -void f(List l) { - int x; - for (x in l) {} -} -'''); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode( - decoratedTypeAnnotation('int> l').node, inSet(pointsToNever)), - decoratedTypeAnnotation('int x').node, - hard: false); - } - - Future test_for_element_list() async { - await analyze(''' -void f(List ints) { - [for(int i in ints) i]; -} -'''); - - assertNullCheck( - checkExpression('ints) i')!, - assertEdge(decoratedTypeAnnotation('List ints').node, never, - hard: true)); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int>[').node, - hard: false); - } - - Future test_for_element_map() async { - await analyze(''' -void f(List strs, List ints) { - { - for (String s in strs) - for (int i in ints) - s: i, - }; -} -'''); - - assertNullCheck( - checkExpression('strs)\n')!, - assertEdge(decoratedTypeAnnotation('List strs').node, never, - hard: true)); - assertNullCheck( - checkExpression('ints)\n')!, - assertEdge(decoratedTypeAnnotation('List ints').node, never, - hard: false)); - - var keyTypeNode = decoratedTypeAnnotation('String, int>{').node; - var valueTypeNode = decoratedTypeAnnotation('int>{').node; - assertEdge(decoratedTypeAnnotation('String s').node, keyTypeNode, - hard: false); - assertEdge(decoratedTypeAnnotation('int i').node, valueTypeNode, - hard: false); - } - - Future test_for_element_set() async { - await analyze(''' -void f(List ints) { - {for(int i in ints) i}; -} -'''); - - assertNullCheck( - checkExpression('ints) i')!, - assertEdge(decoratedTypeAnnotation('List ints').node, never, - hard: true)); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int>{').node, - hard: false); - } - - Future test_for_with_declaration() async { - await analyze(''' -main() { - for (int i in [1, 2, 3]) { print(i); } -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_for_with_var() async { - await analyze(''' -main() { - for (var i in [1, 2, 3]) { print(i); } -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_forStatement_empty() async { - await analyze(''' - -void test() { - for (; ; ) { - return; - } -} -'''); - } - - Future test_function_assignment() async { - await analyze(''' -class C { - void f1(String message) {} - void f2(String message) {} -} -foo(C c, bool flag) { - Function(String message) out = flag ? c.f1 : c.f2; - out('hello'); -} -bar() { - foo(C(), true); - foo(C(), false); -} -'''); - var type = decoratedTypeAnnotation('Function(String message)'); - expect(type.returnType, isNotNull); - } - - Future test_function_metadata() async { - await analyze(''' -@deprecated -void f() {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_functionDeclaration_expression_body() async { - await analyze(''' -int/*1*/ f(int/*2*/ i) => i/*3*/; -'''); - - assertNullCheck( - checkExpression('i/*3*/')!, - assertEdge(decoratedTypeAnnotation('int/*2*/').node, - decoratedTypeAnnotation('int/*1*/').node, - hard: true)); - } - - Future - test_functionDeclaration_parameter_named_default_listConst() async { - await analyze(''' -void f({List i = const []}) {} -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('List').node); - assertEdge(decoratedTypeAnnotation('int/*2*/').node, - decoratedTypeAnnotation('int/*1*/').node, - hard: true, checkable: false); - } - - Future - test_functionDeclaration_parameter_named_default_notNull() async { - await analyze(''' -void f({int i = 1}) {} -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future test_functionDeclaration_parameter_named_default_null() async { - await analyze(''' -void f({int i = null}) {} -'''); - - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false); - } - - Future test_functionDeclaration_parameter_named_no_default() async { - await analyze(''' -void f({int i}) {} -'''); - - assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); - } - - Future - test_functionDeclaration_parameter_named_no_default_required() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -void f({@required int i}) {} -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future - test_functionDeclaration_parameter_named_no_default_required_hint() async { - await analyze(''' -void f({/*required*/ int i}) {} -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future - test_functionDeclaration_parameter_positionalOptional_default_notNull() async { - await analyze(''' -void f([int i = 1]) {} -'''); - - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future - test_functionDeclaration_parameter_positionalOptional_default_null() async { - await analyze(''' -void f([int i = null]) {} -'''); - - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false); - } - - Future - test_functionDeclaration_parameter_positionalOptional_no_default() async { - await analyze(''' -void f([int i]) {} -'''); - - assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); - } - - Future test_functionExpressionInvocation_bangHint() async { - await analyze(''' -int f1(int Function() g1) => g1(); -int f2(int Function() g2) => g2()/*!*/; -'''); - assertEdge(decoratedTypeAnnotation('int Function() g1').node, - decoratedTypeAnnotation('int f1').node, - hard: false); - assertNoEdge(decoratedTypeAnnotation('int Function() g2').node, - decoratedTypeAnnotation('int f2').node); - expect(hasNullCheckHint(findNode.functionExpressionInvocation('g2()')), - isTrue); - } - - Future test_functionExpressionInvocation_parameterType() async { - await analyze(''' -abstract class C { - void Function(int) f(); -} -void g(C c, int i) { - c.f()(i); -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int)').node, - hard: true); - } - - Future test_functionExpressionInvocation_returnType() async { - await analyze(''' -abstract class C { - int Function() f(); -} -int g(C c) => c.f()(); -'''); - assertEdge(decoratedTypeAnnotation('int Function').node, - decoratedTypeAnnotation('int g').node, - hard: false); - } - - Future test_functionInvocation_parameter_fromLocalParameter() async { - await analyze(''' -void f(int/*1*/ i) {} -void test(int/*2*/ i) { - f(i/*3*/); -} -'''); - - var int_1 = decoratedTypeAnnotation('int/*1*/'); - var int_2 = decoratedTypeAnnotation('int/*2*/'); - var i_3 = checkExpression('i/*3*/')!; - assertNullCheck(i_3, assertEdge(int_2.node, int_1.node, hard: true)); - assertEdge(int_2.node, int_1.node, hard: true); - } - - Future test_functionInvocation_parameter_functionTyped() async { - await analyze(''' -void _f(void g()) {} -void test() { - _f(null); -} -'''); - - var parameter = variables.decoratedElementType( - findNode.functionTypedFormalParameter('void g()').declaredElement!); - assertNullCheck(checkExpression('null')!, - assertEdge(inSet(alwaysPlus), parameter.node, hard: false)); - } - - Future - test_functionInvocation_parameter_functionTyped_named_missing() async { - await analyze(''' -void f({void g()}) {} -void h() { - f(); -} -'''); - var parameter = variables.decoratedElementType( - findNode.functionTypedFormalParameter('void g()').declaredElement!); - expect(getEdges(always, parameter.node), isNotEmpty); - } - - Future test_functionInvocation_parameter_named() async { - await analyze(''' -void f({int i = 0}) {} -void g(int j) { - f(i: j/*check*/); -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - assertNullCheck(checkExpression('j/*check*/')!, - assertEdge(nullable_j, nullable_i, hard: true)); - } - - Future test_functionInvocation_parameter_named_missing() async { - await analyze(''' -void f({int i}) {} -void g() { - f(); -} -'''); - var optional_i = decoratedTypeAnnotation('int i').node; - expect(getEdges(always, optional_i), isNotEmpty); - } - - Future - test_functionInvocation_parameter_named_missing_required() async { - addMetaPackage(); - verifyNoTestUnitErrors = false; - await analyze(''' -import 'package:meta/meta.dart'; -void f({@required int i}) {} -void g() { - f(); -} -'''); - // The call at `f()` is presumed to be in error; no constraint is recorded. - var nullable_i = decoratedTypeAnnotation('int i').node; - assertNoUpstreamNullability(nullable_i); - } - - Future - test_functionInvocation_parameter_named_missing_required_hint() async { - verifyNoTestUnitErrors = false; - await analyze(''' -void f({/*required*/ int i}) {} -void g() { - f(); -} -'''); - // The call at `f()` is presumed to be in error; no constraint is recorded. - var nullable_i = decoratedTypeAnnotation('int i').node; - assertNoUpstreamNullability(nullable_i); - } - - Future test_functionInvocation_parameter_null() async { - await analyze(''' -void _f(int i) {} -void test() { - _f(null); -} -'''); - - assertNullCheck( - checkExpression('null')!, - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false)); - } - - Future test_functionInvocation_return() async { - await analyze(''' -int/*1*/ f() => 0; -int/*2*/ g() { - return (f()); -} -'''); - - assertNullCheck( - checkExpression('(f())')!, - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*2*/').node, - hard: false)); - } - - Future test_functionInvocation_typeParameter_inferred() async { - await analyze(''' -T h(T t) => t; -T Function(T) get f => h; -void g() { - int y; - int x = f(y); -} -'''); - var int_y = decoratedTypeAnnotation('int y').node; - var int_x = decoratedTypeAnnotation('int x').node; - var t_ret = decoratedTypeAnnotation('T Function').node; - var t_param = decoratedTypeAnnotation('T)').node; - - assertEdge(substitutionNode(anyNode, t_ret), int_x, hard: false); - assertEdge(int_y, substitutionNode(anyNode, t_param), hard: true); - } - - Future test_functionTypeAlias_inExpression() async { - await analyze(''' -typedef bool _P(T value); -bool f(Object x) => x is _P; -'''); - // No assertions here; just don't crash. This test can be repurposed for - // a more specific test with assertions. - } - - Future test_genericMethodInvocation() async { - await analyze(''' -class Base { - T foo(T x) => x; -} -class Derived extends Base {} -int bar(Derived d, int i) => d.foo(i); -'''); - var implicitTypeArgumentMatcher = anyNode; - assertEdge( - decoratedTypeAnnotation('int i').node, - substitutionNode( - implicitTypeArgumentMatcher, decoratedTypeAnnotation('T x').node), - hard: true); - var implicitTypeArgumentNullability = - implicitTypeArgumentMatcher.matchingNode; - assertEdge( - substitutionNode(implicitTypeArgumentNullability, - decoratedTypeAnnotation('T foo').node), - decoratedTypeAnnotation('int bar').node, - hard: false); - } - - Future test_genericMethodInvocation_withBoundSubstitution() async { - await analyze(''' -class Base { - U foo(U x) => x; -} -class Derived extends Base> {} -bar(Derived d, List x) => d.foo(x); -'''); - // Don't bother checking any edges; the assertions in the DecoratedType - // constructor verify that we've substituted the bound correctly. - } - - Future - test_genericMethodInvocation_withBoundSubstitution_noFreshParameters() async { - await analyze(''' -class Base { - U foo(U x) => x; -} -class Derived extends Base {} -int bar(Derived d, int i) => d.foo(i); -'''); - var implicitTypeArgumentMatcher = anyNode; - assertEdge( - decoratedTypeAnnotation('int i').node, - substitutionNode( - implicitTypeArgumentMatcher, decoratedTypeAnnotation('U x').node), - hard: true); - var implicitTypeArgumentNullability = - implicitTypeArgumentMatcher.matchingNode; - assertEdge( - substitutionNode(implicitTypeArgumentNullability, - decoratedTypeAnnotation('U foo').node), - decoratedTypeAnnotation('int bar').node, - hard: false); - } - - Future test_genericMethodInvocation_withSubstitution() async { - await analyze(''' -class Base { - U foo(U x, T y) => x; -} -class Derived extends Base> {} -int bar(Derived d, int i, List j) => d.foo(i, j); -'''); - assertEdge( - decoratedTypeAnnotation('String> j').node, - substitutionNode(decoratedTypeAnnotation('String> d').node, - decoratedTypeAnnotation('V>>').node), - hard: true, - checkable: false); - assertEdge( - decoratedTypeAnnotation('List j').node, - substitutionNode(decoratedTypeAnnotation('List>').node, - decoratedTypeAnnotation('T y').node), - hard: true); - var implicitTypeArgumentMatcher = anyNode; - assertEdge( - decoratedTypeAnnotation('int i').node, - substitutionNode( - implicitTypeArgumentMatcher, decoratedTypeAnnotation('U x').node), - hard: true); - var implicitTypeArgumentNullability = - implicitTypeArgumentMatcher.matchingNode; - assertEdge( - substitutionNode(implicitTypeArgumentNullability, - decoratedTypeAnnotation('U foo').node), - decoratedTypeAnnotation('int bar').node, - hard: false); - } - - Future test_genericTypeAlias_inExpression() async { - await analyze(''' -typedef _P = bool Function(T value); -bool f(Object x) => x is _P; -'''); - // No assertions here; just don't crash. This test can be repurposed for - // a more specific test with assertions. - } - - Future test_getter_overrides_implicit_getter() async { - await analyze(''' -class A { - final String/*1*/ s = "x"; -} -class C implements A { - String/*2*/ get s => false ? "y" : null; -} -'''); - var string1 = decoratedTypeAnnotation('String/*1*/'); - var string2 = decoratedTypeAnnotation('String/*2*/'); - assertEdge(string2.node, string1.node, hard: true); - } - - Future test_if_condition() async { - await analyze(''' -void f(bool b) { - if (b) {} -} -'''); - - assertNullCheck(checkExpression('b) {}')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - } - - Future test_if_conditional_control_flow_after() async { - await analyze(''' -void f(bool b, int i, int j) { - assert(j != null); - if (b) return; - assert(i != null); -} -'''); - - // Asserts after ifs don't demonstrate non-null intent. - assertNoEdge(decoratedTypeAnnotation('int i').node, never); - // But asserts before ifs do - assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); - } - - Future - test_if_conditional_control_flow_after_normal_completion() async { - await analyze(''' -void f(bool b1, bool b2, int i, int j) { - if (b1) {} - assert(j != null); - if (b2) return; - assert(i != null); -} -'''); - - // Asserts after `if (...) return` s don't demonstrate non-null intent. - assertNoEdge(decoratedTypeAnnotation('int i').node, never); - // But asserts after `if (...) {}` do, since both branches of the `if` - // complete normally, so the assertion is unconditionally reachable. - assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); - } - - Future test_if_conditional_control_flow_within() async { - await analyze(''' -void f(bool b, int i, int j) { - assert(j != null); - if (b) { - assert(i != null); - } else { - assert(i != null); - } -} -'''); - - // Asserts inside ifs don't demonstrate non-null intent. - assertNoEdge(decoratedTypeAnnotation('int i').node, never); - // But asserts outside ifs do. - assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); - } - - Future test_if_element_guard_equals_null() async { - await analyze(''' -dynamic f(int i, int j, int k) { - [if (i == null) j/*check*/ else k/*check*/]; -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - var nullable_k = decoratedTypeAnnotation('int k').node; - var nullable_itemType = decoratedTypeAnnotation('int>[').node; - assertNullCheck( - checkExpression('j/*check*/')!, - assertEdge(nullable_j, nullable_itemType, - guards: [nullable_i], hard: false)); - assertNullCheck(checkExpression('k/*check*/')!, - assertEdge(nullable_k, nullable_itemType, hard: false)); - var discard = elementDiscard('if (i == null)')!; - expect(discard.trueGuard, same(nullable_i)); - expect(discard.falseGuard, null); - expect(discard.pureCondition, true); - } - - Future test_if_element_list() async { - await analyze(''' -void f(bool b) { - int i1 = null; - int i2 = null; - [if (b) i1 else i2]; -} -'''); - - assertNullCheck(checkExpression('b) i1')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertEdge(decoratedTypeAnnotation('int i1').node, - decoratedTypeAnnotation('int>[').node, - hard: false); - assertEdge(decoratedTypeAnnotation('int i2').node, - decoratedTypeAnnotation('int>[').node, - hard: false); - } - - Future test_if_element_map() async { - await analyze(''' -void f(bool b) { - int i1 = null; - int i2 = null; - String s1 = null; - String s2 = null; - {if (b) s1: i1 else s2: i2}; -} -'''); - - assertNullCheck(checkExpression('b) s1')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - - var keyTypeNode = decoratedTypeAnnotation('String, int>{').node; - var valueTypeNode = decoratedTypeAnnotation('int>{').node; - assertEdge(decoratedTypeAnnotation('String s1').node, keyTypeNode, - hard: false); - assertEdge(decoratedTypeAnnotation('String s2').node, keyTypeNode, - hard: false); - assertEdge(decoratedTypeAnnotation('int i1').node, valueTypeNode, - hard: false); - assertEdge(decoratedTypeAnnotation('int i2').node, valueTypeNode, - hard: false); - } - - Future test_if_element_nested() async { - await analyze(''' -void f(bool b1, bool b2) { - int i1 = null; - int i2 = null; - int i3 = null; - [if (b1) if (b2) i1 else i2 else i3]; -} -'''); - - assertNullCheck(checkExpression('b1)')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - assertNullCheck( - checkExpression('b2) i1')!, - assertEdge(decoratedTypeAnnotation('bool b2').node, never, - hard: false)); - assertEdge(decoratedTypeAnnotation('int i1').node, - decoratedTypeAnnotation('int>[').node, - hard: false); - assertEdge(decoratedTypeAnnotation('int i2').node, - decoratedTypeAnnotation('int>[').node, - hard: false); - assertEdge(decoratedTypeAnnotation('int i3').node, - decoratedTypeAnnotation('int>[').node, - hard: false); - } - - Future test_if_element_set() async { - await analyze(''' -void f(bool b) { - int i1 = null; - int i2 = null; - {if (b) i1 else i2}; -} -'''); - - assertNullCheck(checkExpression('b) i1')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertEdge(decoratedTypeAnnotation('int i1').node, - decoratedTypeAnnotation('int>{').node, - hard: false); - assertEdge(decoratedTypeAnnotation('int i2').node, - decoratedTypeAnnotation('int>{').node, - hard: false); - } - - Future test_if_guard_equals_null() async { - await analyze(''' -int f(int i, int j, int k) { - if (i == null) { - return j/*check*/; - } else { - return k/*check*/; - } -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - var nullable_k = decoratedTypeAnnotation('int k').node; - var nullable_return = decoratedTypeAnnotation('int f').node; - assertNullCheck( - checkExpression('j/*check*/')!, - assertEdge(nullable_j, nullable_return, - guards: [nullable_i], hard: false)); - assertNullCheck(checkExpression('k/*check*/')!, - assertEdge(nullable_k, nullable_return, hard: false)); - var discard = statementDiscard('if (i == null)')!; - expect(discard.trueGuard, same(nullable_i)); - expect(discard.falseGuard, null); - expect(discard.pureCondition, true); - } - - Future test_if_simple() async { - await analyze(''' -int f(bool b, int i, int j) { - if (b) { - return i/*check*/; - } else { - return j/*check*/; - } -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - var nullable_return = decoratedTypeAnnotation('int f').node; - assertNullCheck(checkExpression('i/*check*/')!, - assertEdge(nullable_i, nullable_return, hard: false)); - assertNullCheck(checkExpression('j/*check*/')!, - assertEdge(nullable_j, nullable_return, hard: false)); - } - - Future test_if_without_else() async { - await analyze(''' -int f(bool b, int i) { - if (b) { - return i/*check*/; - } - return 0; -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_return = decoratedTypeAnnotation('int f').node; - assertNullCheck(checkExpression('i/*check*/')!, - assertEdge(nullable_i, nullable_return, hard: false)); - } - - Future test_import_metadata() async { - await analyze(''' -@deprecated -import 'dart:async'; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_indexExpression_bangHint() async { - await analyze(''' -abstract class C { - int operator[](int index); -} -int f1(C c) => c[0]; -int f2(C c) => c[0]/*!*/; -'''); - assertEdge(decoratedTypeAnnotation('int operator').node, - decoratedTypeAnnotation('int f1').node, - hard: false); - assertNoEdge(decoratedTypeAnnotation('int operator').node, - decoratedTypeAnnotation('int f2').node); - expect(hasNullCheckHint(findNode.index('c[0]/*!*/')), isTrue); - } - - Future test_indexExpression_dynamic() async { - await analyze(''' -int f(dynamic d, int i) { - return d[i]; -} -'''); - // We assume that the index expression might evaluate to anything, including - // `null`. - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_indexExpression_index() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -int f(C c, int j) => c[j]; -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node, - hard: true); - } - - Future test_indexExpression_index_cascaded() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -C f(C c, int j) => c..[j]; -'''); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node, - hard: true); - } - - Future test_indexExpression_return_type() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -int f(C c) => c[0]; -'''); - assertEdge(decoratedTypeAnnotation('int operator').node, - decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_indexExpression_target_check() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -int f(C c) => c[0]; -'''); - assertNullCheck(checkExpression('c[')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_indexExpression_target_check_cascaded() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -C f(C c) => c..[0]; -'''); - assertNullCheck(checkExpression('c..[')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future - test_indexExpression_target_demonstrates_non_null_intent() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -int f(C c) => c[0]; -'''); - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true); - } - - Future - test_indexExpression_target_demonstrates_non_null_intent_cascaded() async { - await analyze(''' -class C { - int operator[](int i) => 1; -} -C f(C c) => c..[0]; -'''); - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true); - } - - Future test_instanceCreation_generic() async { - await analyze(''' -class C {} -C f() => C(); -'''); - assertEdge(decoratedTypeAnnotation('int>(').node, - decoratedTypeAnnotation('int> f').node, - hard: false, checkable: false); - } - - Future test_instanceCreation_generic_bound() async { - await analyze(''' -class C {} -C f() => C(); -'''); - assertEdge(decoratedTypeAnnotation('int>(').node, - decoratedTypeAnnotation('int> f').node, - hard: false, checkable: false); - assertEdge(decoratedTypeAnnotation('int>(').node, - decoratedTypeAnnotation('Object').node, - hard: true); - } - - Future test_instanceCreation_generic_dynamic() async { - await analyze(''' -class C {} -C f() => C(); -'''); - assertEdge(decoratedTypeAnnotation('dynamic').node, - decoratedTypeAnnotation('Object').node, - hard: false, checkable: false); - } - - Future test_instanceCreation_generic_inferredParameterType() async { - await analyze(''' -class C { - C(List x); -} -C f(List x) => C(x); -'''); - var edge = assertEdge(anyNode, decoratedTypeAnnotation('int> f').node, - hard: false, checkable: false); - var inferredTypeArgument = edge.sourceNode; - assertEdge( - decoratedTypeAnnotation('int> x').node, - substitutionNode( - inferredTypeArgument, decoratedTypeAnnotation('T> x').node), - hard: true, - checkable: false); - } - - Future test_instanceCreation_generic_parameter() async { - await analyze(''' -class C { - C(T t); -} -f(int i) => C(i/*check*/); -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_c_t = decoratedTypeAnnotation('C').typeArguments[0]!.node; - var nullable_t = decoratedTypeAnnotation('T t').node; - var check_i = checkExpression('i/*check*/')!; - var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]! - .destinationNode as NullabilityNodeForSubstitution; - expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t)); - expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t)); - assertNullCheck(check_i, - assertEdge(nullable_i, nullable_c_t_or_nullable_t, hard: true)); - } - - Future test_instanceCreation_generic_parameter_named() async { - await analyze(''' -class C { - C({T t}); -} -f(int i) => C(t: i/*check*/); -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_c_t = decoratedTypeAnnotation('C').typeArguments[0]!.node; - var nullable_t = decoratedTypeAnnotation('T t').node; - var check_i = checkExpression('i/*check*/')!; - var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]! - .destinationNode as NullabilityNodeForSubstitution; - expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t)); - expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t)); - assertNullCheck(check_i, - assertEdge(nullable_i, nullable_c_t_or_nullable_t, hard: true)); - } - - Future test_instanceCreation_implicit_type_params_names() async { - await analyze(''' -class C {} -void main() { - C x = C(); -} -'''); - var edge0 = assertEdge( - anyNode, decoratedTypeAnnotation('C').node, - hard: false); - expect(edge0.sourceNode!.displayName, 'constructed type (test.dart:3:25)'); - var edge1 = assertEdge(anyNode, decoratedTypeAnnotation('Object,').node, - hard: false, checkable: false); - expect(edge1.sourceNode!.displayName, - 'type argument 0 of constructed type (test.dart:3:25)'); - var edge2 = assertEdge(anyNode, decoratedTypeAnnotation('Object>').node, - hard: false, checkable: false); - expect(edge2.sourceNode!.displayName, - 'type argument 1 of constructed type (test.dart:3:25)'); - } - - Future test_instanceCreation_parameter_named_optional() async { - await analyze(''' -class C { - C({int x = 0}); -} -void f(int y) { - C(x: y); -} -'''); - - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_instanceCreation_parameter_positional_optional() async { - await analyze(''' -class C { - C([int x]); -} -void f(int y) { - C(y); -} -'''); - - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_instanceCreation_parameter_positional_required() async { - await analyze(''' -class C { - C(int x); -} -void f(int y) { - C(y); -} -'''); - - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_integerLiteral() async { - await analyze(''' -int f() { - return 0; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('int').node); - } - - Future test_invocation_arguments() async { - await analyze(''' -int f(Function g, int i, int j) => g(h(i), named: h(j)); -int h(int x) => 0; -'''); - // Make sure the appropriate edges get created for the calls to h(). - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int x').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_invocation_arguments_parenthesized() async { - await analyze(''' -int f(Function g, int i, int j) => (g)(h(i), named: h(j)); -int h(int x) => 0; -'''); - // Make sure the appropriate edges get created for the calls to h(). - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int x').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_invocation_dynamic() async { - await analyze(''' -int f(dynamic g) => g(); -'''); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_invocation_dynamic_parenthesized() async { - await analyze(''' -int f(dynamic g) => (g)(); -'''); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_invocation_function() async { - await analyze(''' -int f(Function g) => g(); -'''); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - assertNullCheck( - checkExpression('g(')!, - assertEdge(decoratedTypeAnnotation('Function g').node, never, - hard: true)); - } - - Future test_invocation_function_parenthesized() async { - await analyze(''' -int f(Function g) => (g)(); -'''); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - assertNullCheck( - checkExpression('g)(')!, - assertEdge(decoratedTypeAnnotation('Function g').node, never, - hard: true)); - } - - Future test_invocation_type_arguments() async { - await analyze(''' -int f(Function g) => g>(); -class C {} -'''); - // Make sure the appropriate edge gets created for the instantiation of C. - assertEdge(decoratedTypeAnnotation('int>').node, - decoratedTypeAnnotation('num>').node, - hard: true); - } - - Future test_invocation_type_arguments_parenthesized() async { - await analyze(''' -int f(Function g) => (g)>(); -class C {} -'''); - // Make sure the appropriate edge gets created for the instantiation of C. - assertEdge(decoratedTypeAnnotation('int>').node, - decoratedTypeAnnotation('num>').node, - hard: true); - } - - @failingTest - Future test_isExpression_directlyRelatedTypeParameter() async { - await analyze(''' -bool f(List list) => list is List -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: false); - assertEdge(decoratedTypeAnnotation('num').node, - decoratedTypeAnnotation('int').node, - hard: false); - } - - Future test_isExpression_genericFunctionType() async { - await analyze(''' -bool f(a) => a is int Function(String); -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); - } - - @failingTest - Future test_isExpression_indirectlyRelatedTypeParameter() async { - await analyze(''' -bool f(Iterable iter) => iter is List -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: false); - assertEdge(decoratedTypeAnnotation('num').node, - decoratedTypeAnnotation('int').node, - hard: false); - } - - Future test_isExpression_typeName_noTypeArguments() async { - await analyze(''' -bool f(a) => a is String; -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); - assertEdge(decoratedTypeAnnotation('String').node, never, hard: true); - } - - Future test_isExpression_typeName_typeArguments() async { - await analyze(''' -bool f(a) => a is List; -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node); - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertNoEdge(always, decoratedTypeAnnotation('int').node); - } - - Future test_library_metadata() async { - await analyze(''' -@deprecated -library foo; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_libraryDirective() async { - await analyze(''' -library foo; -'''); - // Passes if no exceptions are thrown. - } - - @FailingTest(reason: 'Default list constructor has been removed.') - Future test_list_constructor_length() async { - await analyze(''' -void main() { - List list = List(10); -} -'''); - final variableParam = decoratedTypeAnnotation('int/*1*/'); - final filledParam = decoratedTypeAnnotation('int/*2*/'); - - assertEdge(filledParam.node, variableParam.node, - hard: false, checkable: false); - assertEdge(always, filledParam.node, hard: false); - } - - @FailingTest(reason: 'Default list constructor has been removed.') - Future test_list_constructor_length_implicitParam() async { - await analyze(''' -void main() { - List list = List(10); -} -'''); - final variableParam = decoratedTypeAnnotation('int/*1*/'); - - assertEdge(inSet(alwaysPlus), variableParam.node, - hard: false, checkable: false); - } - - Future test_listLiteral_noTypeArgument_noNullableElements() async { - await analyze(''' -List f() { - return ['a', 'b']; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('List').node); - final returnTypeNode = decoratedTypeAnnotation('String').node; - final returnTypeEdges = getEdges(anyNode, returnTypeNode); - - expect(returnTypeEdges.length, 1); - final returnTypeEdge = returnTypeEdges.single; - - final listArgType = returnTypeEdge.sourceNode!; - assertNoUpstreamNullability(listArgType); - expect(listArgType.displayName, 'list element type (test.dart:2:10)'); - } - - Future test_listLiteral_noTypeArgument_nullableElement() async { - await analyze(''' -List f() { - return ['a', null, 'c']; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('List').node); - final returnTypeNode = decoratedTypeAnnotation('String').node; - final returnTypeEdges = getEdges(anyNode, returnTypeNode); - - expect(returnTypeEdges.length, 1); - final returnTypeEdge = returnTypeEdges.single; - - final listArgType = returnTypeEdge.sourceNode; - assertEdge(inSet(alwaysPlus), listArgType, hard: false); - } - - Future test_listLiteral_typeArgument_noNullableElements() async { - await analyze(''' -List f() { - return ['a', 'b']; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('List').node); - var typeArgForLiteral = decoratedTypeAnnotation('String>[').node; - var typeArgForReturnType = decoratedTypeAnnotation('String> ').node; - assertNoUpstreamNullability(typeArgForLiteral); - assertEdge(typeArgForLiteral, typeArgForReturnType, - hard: true, checkable: false); - } - - Future test_listLiteral_typeArgument_nullableElement() async { - await analyze(''' -List f() { - return ['a', null, 'c']; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('List').node); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('String>[').node, - hard: false); - } - - Future test_localVariable_type_inferred() async { - await analyze(''' -int f() => 1; -main() { - var x = f(); -} -'''); - var xType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - assertEdge(decoratedTypeAnnotation('int').node, xType.node, hard: false); - } - - Future test_localVariable_unused() async { - await analyze(''' -main() { - int i; -} -'''); - // There is no edge from always to the type of `i`, because `i` is never - // used, so it's ok that it's not initialized. - assertNoEdge(always, decoratedTypeAnnotation('int').node); - } - - Future test_method_parameterType_inferred() async { - await analyze(''' -class B { - void f/*B*/(int x) {} -} -class C extends B { - void f/*C*/(x) {} -} -'''); - var bReturnType = decoratedMethodType('f/*B*/').positionalParameters![0]; - var cReturnType = decoratedMethodType('f/*C*/').positionalParameters![0]; - assertEdge(bReturnType.node, cReturnType.node, hard: true); - } - - Future test_method_parameterType_inferred_named() async { - await analyze(''' -class B { - void f/*B*/({int x = 0}) {} -} -class C extends B { - void f/*C*/({x = 0}) {} -} -'''); - var bReturnType = decoratedMethodType('f/*B*/').namedParameters!['x']!; - var cReturnType = decoratedMethodType('f/*C*/').namedParameters!['x']!; - assertEdge(bReturnType.node, cReturnType.node, hard: true); - } - - Future test_method_returnType_inferred() async { - await analyze(''' -class B { - int f/*B*/() => 1; -} -class C extends B { - f/*C*/() => 1; -} -'''); - var bReturnType = decoratedMethodType('f/*B*/').returnType!; - var cReturnType = decoratedMethodType('f/*C*/').returnType!; - assertEdge(cReturnType.node, bReturnType.node, hard: true); - } - - Future - test_methodDeclaration_doesntAffect_unconditional_control_flow() async { - await analyze(''' -class C { - void f(bool b, int i, int j) { - assert(i != null); - if (b) {} - assert(j != null); - } - void g(int k) { - assert(k != null); - } -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); - assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true); - } - - Future - test_methodDeclaration_resets_unconditional_control_flow() async { - await analyze(''' -class C { - void _f(bool b, int i, int j) { - assert(i != null); - if (b) return; - assert(j != null); - } - void _g(int k) { - assert(k != null); - } -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - assertNoEdge(decoratedTypeAnnotation('int j').node, never); - assertEdge(always, decoratedTypeAnnotation('int j').node, hard: false); - assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true); - } - - Future test_methodInvocation_bangHint() async { - await analyze(''' -abstract class C { - int m1(); - int m2(); -} -int f1(C c) => c.m1(); -int f2(C c) => c.m2()/*!*/; -'''); - assertEdge(decoratedTypeAnnotation('int m1').node, - decoratedTypeAnnotation('int f1').node, - hard: false); - assertNoEdge(decoratedTypeAnnotation('int m2').node, - decoratedTypeAnnotation('int f2').node); - expect(hasNullCheckHint(findNode.methodInvocation('c.m2')), isTrue); - } - - Future test_methodInvocation_call_functionTyped() async { - await analyze(''' -void f(void Function(int x) callback, int y) => callback.call(y); -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_methodInvocation_call_interfaceTyped() async { - // Make sure that we don't try to treat all methods called `call` as though - // the underlying type is a function type. - await analyze(''' -abstract class C { - void call(int x); -} -void f(C c, int y) => c.call(y); -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_methodInvocation_dynamic() async { - await analyze(''' -class C { - int g(int i) => i; -} -int f(dynamic d, int j) { - return d.g(j); -} -'''); - // The call `d.g(j)` is dynamic, so we can't tell what method it resolves - // to. There's no reason to assume it resolves to `C.g`. - assertNoEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node); - assertNoEdge(decoratedTypeAnnotation('int g').node, - decoratedTypeAnnotation('int f').node); - // We do, however, assume that it might return anything, including `null`. - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_methodInvocation_dynamic_arguments() async { - await analyze(''' -int f(dynamic d, int i, int j) { - return d.g(h(i), named: h(j)); -} -int h(int x) => 0; -'''); - // Make sure the appropriate edges get created for the calls to h(). - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int x').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_methodInvocation_dynamic_type_arguments() async { - await analyze(''' -int f(dynamic d, int i, int j) { - return d.g>(); -} -class C {} -'''); - // Make sure the appropriate edge gets created for the instantiation of C. - assertEdge(decoratedTypeAnnotation('int>').node, - decoratedTypeAnnotation('num>').node, - hard: true); - } - - Future test_methodInvocation_extension_conflict() async { - await analyze(''' -class C { - void f(int w) {} -} -extension E on C { - void f(int x) {} - void g(int y) { - this.f(y); - } - void h(int z) { - f(z); - } -} -'''); - // `this.f(y)` refers to [C.f], not [E.f]. - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int w').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node); - - // `f(z)` refers to [E.f], not [C.f]. - assertEdge(decoratedTypeAnnotation('int z').node, - decoratedTypeAnnotation('int x').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int z').node, - decoratedTypeAnnotation('int w').node); - } - - Future test_methodInvocation_extension_explicitThis() async { - await analyze(''' -class C { - void f(int x) {} -} -extension E on C { - void g(int y) { - this.f(y); - } -} -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_methodInvocation_extension_implicitThis() async { - await analyze(''' -class C { - void f(int x) {} -} -extension E on C { - void g(int y) { - f(y); - } -} -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_methodInvocation_extension_nullTarget() async { - await analyze(''' -class C {} -extension on C /*1*/ { - void m() {} -} -void f() { - C c = null; - c.m(); -} -'''); - assertEdge(decoratedTypeAnnotation('C c').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: true); - } - - Future test_methodInvocation_extension_unnamed() async { - await analyze(''' -class C { - void f(int x) {} -} -extension on C { - void g(int y) { - f(y); - } -} -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - } - - Future test_methodInvocation_generic_onResultOfImplicitSuper() async { - await analyze(''' -class Base { - Base noop() => this; -} - -class Sub extends Base { - void implicitSuper() => noop().noop(); -} -'''); - // Don't bother checking any edges; the assertions in the DecoratedType - // constructor verify that we've substituted the bound correctly. - } - - Future test_methodInvocation_implicitSuper_generic() async { - await analyze(''' -class Base { - Base f(T1 x) => this; -} - -class Sub extends Base { - void g() => f(null); -} -'''); - assertEdge( - inSet(alwaysPlus), - substitutionNode( - substitutionNode( - anyNode, // non-null for `this`. - decoratedTypeAnnotation('T2> {').node, - ), - decoratedTypeAnnotation('T1 x').node, - ), - hard: false); - } - - Future test_methodInvocation_implicitSuper_tearOff() async { - await analyze(''' -class Base { - Base f(T1 x) => this; -} - -class Sub extends Base { - void g() => (f)(null); -} -'''); - assertEdge( - inSet(alwaysPlus), - substitutionNode( - substitutionNode( - anyNode, // non-null for `this`. - decoratedTypeAnnotation('T2> {').node, - ), - decoratedTypeAnnotation('T1 x').node, - ), - hard: false); - } - - Future test_methodInvocation_mixin_super() async { - await analyze(''' -class C { - void f(int x) {} -} -mixin D on C { - void g(int y) { - super.f(y); - } - @override - void f(int z) { - } -} -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int z').node); - } - - Future test_methodInvocation_object_method() async { - await analyze(''' -String f(int i) => i.toString(); -'''); - // No edge from i to `never` because it is safe to call `toString` on - // `null`. - assertNoEdge(decoratedTypeAnnotation('int').node, never); - } - - Future - test_methodInvocation_object_method_on_non_interface_type() async { - await analyze(''' -String f(void Function() g) => g.toString(); -'''); - var toStringReturnType = variables - .decoratedElementType( - typeProvider.objectType.element.getMethod('toString')!) - .returnType!; - assertEdge( - toStringReturnType.node, decoratedTypeAnnotation('String f').node, - hard: false); - } - - Future test_methodInvocation_parameter_contravariant() async { - await analyze(''' -class C { - void f(T t) {} -} -void g(C c, int i) { - c.f(i/*check*/); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_c_t = decoratedTypeAnnotation('C').typeArguments[0]!.node; - var nullable_t = decoratedTypeAnnotation('T t').node; - var check_i = checkExpression('i/*check*/')!; - var nullable_c_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]! - .destinationNode as NullabilityNodeForSubstitution; - expect(nullable_c_t_or_nullable_t.innerNode, same(nullable_c_t)); - expect(nullable_c_t_or_nullable_t.outerNode, same(nullable_t)); - assertNullCheck(check_i, - assertEdge(nullable_i, nullable_c_t_or_nullable_t, hard: true)); - } - - Future - test_methodInvocation_parameter_contravariant_from_migrated_class() async { - await analyze(''' -void f(List x, int i) { - x.add(i/*check*/); -} -'''); - - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_list_t = - decoratedTypeAnnotation('List').typeArguments[0]!.node; - var addMethod = - findNode.methodInvocation('x.add').methodName.staticElement!; - var nullable_t = variables - .decoratedElementType(addMethod.declaration!) - .positionalParameters![0] - .node; - assertEdge(nullable_t, never, hard: true, checkable: false); - var check_i = checkExpression('i/*check*/')!; - var nullable_list_t_or_nullable_t = check_i - .checks - .edges[FixReasonTarget.root]! - .destinationNode as NullabilityNodeForSubstitution; - expect(nullable_list_t_or_nullable_t.innerNode, same(nullable_list_t)); - expect(nullable_list_t_or_nullable_t.outerNode, same(nullable_t)); - assertNullCheck(check_i, - assertEdge(nullable_i, nullable_list_t_or_nullable_t, hard: true)); - } - - Future test_methodInvocation_parameter_contravariant_function() async { - await analyze(''' -void f(T t) {} -void g(int i) { - f(i/*check*/); -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_f_t = decoratedTypeAnnotation('int>').node; - var nullable_t = decoratedTypeAnnotation('T t').node; - var check_i = checkExpression('i/*check*/')!; - var nullable_f_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]! - .destinationNode as NullabilityNodeForSubstitution; - expect(nullable_f_t_or_nullable_t.innerNode, same(nullable_f_t)); - expect(nullable_f_t_or_nullable_t.outerNode, same(nullable_t)); - assertNullCheck(check_i, - assertEdge(nullable_i, nullable_f_t_or_nullable_t, hard: true)); - } - - Future test_methodInvocation_parameter_generic() async { - await analyze(''' -class C {} -void f(C/*2*/ c) {} -void g(C/*4*/ c) { - f(c/*check*/); -} -'''); - - assertEdge(decoratedTypeAnnotation('int/*3*/').node, - decoratedTypeAnnotation('int/*1*/').node, - hard: true, checkable: false); - assertNullCheck( - checkExpression('c/*check*/')!, - assertEdge(decoratedTypeAnnotation('C/*4*/').node, - decoratedTypeAnnotation('C/*2*/').node, - hard: true)); - } - - Future test_methodInvocation_parameter_named() async { - await analyze(''' -class C { - void f({int i = 0}) {} -} -void g(C c, int j) { - c.f(i: j/*check*/); -} -'''); - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_j = decoratedTypeAnnotation('int j').node; - assertNullCheck(checkExpression('j/*check*/')!, - assertEdge(nullable_j, nullable_i, hard: true)); - } - - Future test_methodInvocation_parameter_named_differentPackage() async { - addPackageFile('foo', 'c.dart', ''' -class C { - void f({int i}) {} -} -'''); - await analyze(''' -import "package:foo/c.dart"; -void g(C c, int j) { - c.f(i: j/*check*/); -} -'''); - var nullable_j = decoratedTypeAnnotation('int j'); - assertNullCheck(checkExpression('j/*check*/')!, - assertEdge(nullable_j.node, inSet(pointsToNever), hard: true)); - } - - Future test_methodInvocation_promoted_in_new_flow_analysis() async { - await analyze(''' -class C { - void f(T t) {} -} -void g(C c, int i) { - if (c is! C) { - return; - } - - c.f(i/*check*/); -} -'''); - - // Mostly here to check DecoratedType's assertions, but here are some edge - // checks anyways. - var nullable_i = decoratedTypeAnnotation('int i').node; - var nullable_t = decoratedTypeAnnotation('T t').node; - assertEdge(nullable_i, substitutionNode(anyNode, nullable_t), hard: false); - } - - Future test_methodInvocation_resolves_to_getter() async { - await analyze(''' -abstract class C { - int/*1*/ Function(int/*2*/ i) get f; -} -int/*3*/ g(C c, int/*4*/ i) => c.f(i); -'''); - assertEdge(decoratedTypeAnnotation('int/*4*/').node, - decoratedTypeAnnotation('int/*2*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*3*/').node, - hard: false); - } - - Future test_methodInvocation_return_type() async { - await analyze(''' -class C { - bool m() => true; -} -bool f(C c) => c.m(); -'''); - assertEdge(decoratedTypeAnnotation('bool m').node, - decoratedTypeAnnotation('bool f').node, - hard: false); - } - - Future test_methodInvocation_return_type_generic_function() async { - await analyze(''' -T f(T t) => t; -int g() => (f(1)); -'''); - var check_i = checkExpression('(f(1))')!; - var t_bound = decoratedTypeAnnotation('Object').node; - var nullable_f_t = decoratedTypeAnnotation('int>').node; - var nullable_f_t_or_nullable_t = check_i.checks.edges[FixReasonTarget.root]! - .sourceNode as NullabilityNodeForSubstitution; - var nullable_t = decoratedTypeAnnotation('T f').node; - expect(nullable_f_t_or_nullable_t.innerNode, same(nullable_f_t)); - expect(nullable_f_t_or_nullable_t.outerNode, same(nullable_t)); - var nullable_return = decoratedTypeAnnotation('int g').node; - assertNullCheck(check_i, - assertEdge(nullable_f_t_or_nullable_t, nullable_return, hard: false)); - assertEdge(nullable_f_t, t_bound, hard: true); - } - - Future test_methodInvocation_return_type_null_aware() async { - await analyze(''' -class C { - bool m() => true; -} -bool f(C c) => (c?.m()); -'''); - var lubNode = - decoratedExpressionType('(c?.m())')!.node as NullabilityNodeForLUB; - expect(lubNode.left, same(decoratedTypeAnnotation('C c').node)); - expect(lubNode.right, same(decoratedTypeAnnotation('bool m').node)); - assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false); - } - - Future test_methodInvocation_static_on_generic_class() async { - await analyze(''' -class C { - static int f(int x) => 0; -} -int g(int y) => C.f(y); -'''); - assertEdge(decoratedTypeAnnotation('int y').node, - decoratedTypeAnnotation('int x').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int f').node, - decoratedTypeAnnotation('int g').node, - hard: false); - } - - Future test_methodInvocation_target_check() async { - await analyze(''' -class C { - void m() {} -} -void test(C c) { - c.m(); -} -'''); - - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_methodInvocation_target_check_cascaded() async { - await analyze(''' -class C { - void m() {} -} -void test(C c) { - c..m(); -} -'''); - - assertNullCheck(checkExpression('c..m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future - test_methodInvocation_target_demonstrates_non_null_intent() async { - await analyze(''' -class C { - void m() {} -} -void test(C c) { - c.m(); -} -'''); - - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true); - } - - Future - test_methodInvocation_target_demonstrates_non_null_intent_cascaded() async { - await analyze(''' -class C { - void m() {} -} -void test(C c) { - c..m(); -} -'''); - - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true); - } - - Future test_methodInvocation_target_generic_in_base_class() async { - await analyze(''' -abstract class B { - void m(T/*1*/ t); -} -abstract class C extends B {} -void f(C c, int/*3*/ i) { - c.m(i); -} -'''); - // nullable(3) -> substitute(nullable(2), nullable(1)) - var nullable1 = decoratedTypeAnnotation('T/*1*/').node; - var nullable2 = decoratedTypeAnnotation('int/*2*/').node; - var nullable3 = decoratedTypeAnnotation('int/*3*/').node; - assertEdge(nullable3, substitutionNode(nullable2, nullable1), hard: true); - } - - Future test_methodInvocation_typeParameter_inferred() async { - await analyze(''' -T f(T t) => t; -void g() { - int y; - int x = f(y); -} -'''); - var int_y = decoratedTypeAnnotation('int y').node; - var int_x = decoratedTypeAnnotation('int x').node; - var t_ret = decoratedTypeAnnotation('T f').node; - var t_param = decoratedTypeAnnotation('T t').node; - - assertEdge(substitutionNode(anyNode, t_ret), int_x, hard: false); - assertEdge(int_y, substitutionNode(anyNode, t_param), hard: true); - assertEdge(t_param, t_ret, hard: true); - } - - @failingTest - Future - test_methodInvocation_typeParameter_inferred_inGenericClass() async { - // this creates an edge case because the typeArguments are not equal in - // length the typeFormals of the calleeType, due to the enclosing - // generic class. - await analyze(''' -class C { - void g() { - // use a local fn because generic methods aren't implemented. - T f(T t) => t; - int y; - int x = f(y); - } -} -'''); - var int_y = decoratedTypeAnnotation('int y').node; - var int_x = decoratedTypeAnnotation('int x').node; - var t_ret = decoratedTypeAnnotation('T f').node; - var t_param = decoratedTypeAnnotation('T t').node; - - assertEdge(int_y, t_param, hard: true); - assertEdge(t_param, t_ret, hard: true); - assertEdge(t_ret, int_x, hard: false); - } - - @failingTest - Future - test_methodInvocation_typeParameter_inferred_inGenericExtreme() async { - // this creates an edge case because the typeArguments are not equal in - // length the typeFormals of the calleeType, due to the enclosing - // generic class/functions. - await analyze(''' -class C { - void g() { - // use local fns because generic methods aren't implemented. - void f2() { - void f3() { - T f(T t) => t; - int y; - int x = f(y); - } - } - } -} -'''); - var int_y = decoratedTypeAnnotation('int y').node; - var int_x = decoratedTypeAnnotation('int x').node; - var t_ret = decoratedTypeAnnotation('T f').node; - var t_param = decoratedTypeAnnotation('T t').node; - - assertEdge(int_y, t_param, hard: true); - assertEdge(t_param, t_ret, hard: true); - assertEdge(t_ret, int_x, hard: false); - } - - Future test_methodInvocation_variable_typeParameter_inferred() async { - await analyze(''' -T h(T t) => t; -class C { - void g() { - T Function(T) f = h; - int y; - int x = f(y); - } -} -'''); - var int_y = decoratedTypeAnnotation('int y').node; - var int_x = decoratedTypeAnnotation('int x').node; - var t_ret = decoratedTypeAnnotation('T Function').node; - var t_param = decoratedTypeAnnotation('T)').node; - - assertEdge(substitutionNode(anyNode, t_ret), int_x, hard: false); - assertEdge(int_y, substitutionNode(anyNode, t_param), hard: true); - } - - Future test_never() async { - await analyze(''); - - expect(never.isNullable, isFalse); - } - - Future test_override_mixin_method() async { - await analyze(''' - mixin M { - int m(int/*1*/ x); - } - - class C with M { - @override - int m(int/*2*/ x) => x; - } - '''); - final int1 = decoratedTypeAnnotation('int/*1*/'); - final int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int1.node, int2.node, hard: false, checkable: false); - } - - Future test_override_parameter_function_typed() async { - await analyze(''' -abstract class Base { - void f(void g(int i)/*1*/); -} -class Derived extends Base { - void f(void g(int i)/*2*/) {} -} -'''); - var p1 = variables.decoratedElementType(findNode - .functionTypedFormalParameter('void g(int i)/*1*/') - .declaredElement!); - var p2 = variables.decoratedElementType(findNode - .functionTypedFormalParameter('void g(int i)/*2*/') - .declaredElement!); - assertEdge(p1.node, p2.node, hard: false, checkable: false); - } - - Future test_override_parameter_type_named() async { - await analyze(''' -abstract class Base { - void f({int/*1*/ i}); -} -class Derived extends Base { - void f({int/*2*/ i}) {} -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int1.node, int2.node, hard: false, checkable: false); - } - - Future test_override_parameter_type_named_over_none() async { - await analyze(''' -abstract class Base { - void f(); -} -class Derived extends Base { - void f({int i}) {} -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_override_parameter_type_operator() async { - await analyze(''' -abstract class Base { - Base operator+(Base/*1*/ b); -} -class Derived extends Base { - Base operator+(Base/*2*/ b) => this; -} -'''); - var base1 = decoratedTypeAnnotation('Base/*1*/'); - var base2 = decoratedTypeAnnotation('Base/*2*/'); - assertEdge(base1.node, base2.node, hard: false, checkable: false); - } - - Future test_override_parameter_type_optional() async { - await analyze(''' -abstract class Base { - void f([int/*1*/ i]); -} -class Derived extends Base { - void f([int/*2*/ i]) {} -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int1.node, int2.node, hard: false, checkable: false); - } - - Future test_override_parameter_type_optional_over_none() async { - await analyze(''' -abstract class Base { - void f(); -} -class Derived extends Base { - void f([int i]) {} -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_override_parameter_type_optional_over_required() async { - await analyze(''' -abstract class Base { - void f(int/*1*/ i); -} -class Derived extends Base { - void f([int/*2*/ i]) {} -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int1.node, int2.node, hard: false, checkable: false); - } - - Future test_override_parameter_type_required() async { - await analyze(''' -abstract class Base { - void f(int/*1*/ i); -} -class Derived extends Base { - void f(int/*2*/ i) {} -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int1.node, int2.node, hard: false, checkable: false); - } - - Future test_override_parameter_type_setter() async { - await analyze(''' -abstract class Base { - void set x(int/*1*/ value); -} -class Derived extends Base { - void set x(int/*2*/ value) {} -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int1.node, int2.node, hard: false, checkable: false); - } - - Future test_override_return_type_getter() async { - await analyze(''' -abstract class Base { - int/*1*/ get x; -} -class Derived extends Base { - int/*2*/ get x => null; -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int2.node, int1.node, hard: true); - } - - Future test_override_return_type_method() async { - await analyze(''' -abstract class Base { - int/*1*/ f(); -} -class Derived extends Base { - int/*2*/ f() => null; -} -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int2.node, int1.node, hard: true); - } - - Future test_override_return_type_operator() async { - await analyze(''' -abstract class Base { - Base/*1*/ operator-(); -} -class Derived extends Base { - Derived/*2*/ operator-() => null; -} -'''); - var base1 = decoratedTypeAnnotation('Base/*1*/'); - var derived2 = decoratedTypeAnnotation('Derived/*2*/'); - assertEdge(derived2.node, base1.node, hard: true); - } - - Future test_parameter_field_metadata() async { - await analyze(''' -const bar = null; -class C { - int foo; - C(@bar this.foo); -} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_named_field_metadata() async { - await analyze(''' -const bar = null; -class C { - int foo; - C({@bar this.foo}); -} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_named_field_with_default_metadata() async { - await analyze(''' -const bar = null; -class C { - int foo; - C({@bar this.foo = 0}); -} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_named_metadata() async { - await analyze(''' -void f({@deprecated int foo}) {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_named_with_default_metadata() async { - await analyze(''' -void f({@deprecated int foo = 0}) {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_normal_metadata() async { - await analyze(''' -const foo = null; -void f(@foo int foo) {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_optional_positional_field_metadata() async { - await analyze(''' -const bar = null; -class C { - int foo; - C([@bar this.foo]); -} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parameter_optional_positional_metadata() async { - await analyze(''' -const foo = null; -void f([@foo int foo]) {} -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_parenthesizedExpression() async { - await analyze(''' -int f() { - return (null); -} -'''); - - assertNullCheck( - checkExpression('(null)')!, - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false)); - } - - Future test_parenthesizedExpression_bangHint() async { - await analyze(''' -int f1(int i1) => (i1); -int f2(int i2) => (i2)/*!*/; -'''); - assertEdge(decoratedTypeAnnotation('int i1').node, - decoratedTypeAnnotation('int f1').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int i2').node, - decoratedTypeAnnotation('int f2').node); - expect(hasNullCheckHint(findNode.parenthesized('(i2)')), isTrue); - } - - Future test_part_metadata() async { - var pathContext = resourceProvider.pathContext; - addSource(pathContext.join(pathContext.dirname(testFile), 'part.dart'), ''' -part of 'test.dart'; -'''); - await analyze(''' -@deprecated -part 'part.dart'; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_part_of_identifier() async { - var pathContext = resourceProvider.pathContext; - var testFileName = pathContext.basename(testFile); - var libPath = pathContext.join(pathContext.dirname(testFile), 'lib.dart'); - addSource(libPath, ''' -library test; -part '$testFileName'; -'''); - // Discover the library for the part. - session.getParsedUnit(libPath); - await analyze(''' -part of test; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_part_of_metadata() async { - var pathContext = resourceProvider.pathContext; - var testFileName = pathContext.basename(testFile); - var libPath = pathContext.join(pathContext.dirname(testFile), 'lib.dart'); - addSource(libPath, ''' -part '$testFileName'; -'''); - // TODO(scheglov) This should not be necessary, we use URI, so can find it. - // Discover the library for the part. - session.getParsedUnit(libPath); - await analyze(''' -@deprecated -part of 'lib.dart'; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_part_of_path() async { - var pathContext = resourceProvider.pathContext; - var testFileName = pathContext.basename(testFile); - var libPath = pathContext.join(pathContext.dirname(testFile), 'lib.dart'); - addSource(libPath, ''' -part '$testFileName'; -'''); - // TODO(scheglov) This should not be necessary, we use URI, so can find it. - // Discover the library for the part. - session.getParsedUnit(libPath); - await analyze(''' -part of 'lib.dart'; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_postDominators_assert() async { - await analyze(''' -void test(bool b1, bool b2, bool b3, bool _b) { - assert(b1 != null); - if (_b) { - assert(b2 != null); - } - assert(b3 != null); -} -'''); - - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true); - assertNoEdge(decoratedTypeAnnotation('bool b2').node, never); - assertEdge(decoratedTypeAnnotation('bool b3').node, never, hard: true); - } - - Future - test_postDominators_assignment_with_same_var_on_lhs_and_in_rhs() async { - await analyze(''' -void f(int i) { - i = g(i); -} -int g(int j) => 0; -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int j').node, - hard: true); - } - - Future test_postDominators_break() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b1, C _c) { - while (b1/*check*/) { - bool b2 = b1; - C c = _c; - if (b2/*check*/) { - break; - } - c.m(); - } -} -'''); - - assertNullCheck(checkExpression('b1/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - assertNullCheck(checkExpression('b2/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true)); - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false)); - } - - Future test_postDominators_continue() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b1, C _c) { - while (b1/*check*/) { - bool b2 = b1; - C c = _c; - if (b2/*check*/) { - continue; - } - c.m(); - } -} -'''); - - assertNullCheck(checkExpression('b1/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - assertNullCheck(checkExpression('b2/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true)); - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false)); - } - - Future test_postDominators_doWhileStatement_conditional() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b, C c) { - do { - return; - } while(b/*check*/); - - c.m(); -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: false)); - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false)); - } - - Future test_postDominators_doWhileStatement_unconditional() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b, C c1, C c2) { - do { - C c3 = C(); - c1.m(); - c3.m(); - } while(b/*check*/); - - c2.m(); -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: true)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true)); - } - - Future test_postDominators_forElement() async { - await analyze(''' -class C { - int m() => 0; -} - -void test(bool _b, C c1, C c2) { - [for (bool b1 = _b; b1/*check*/; c2.m()) c1.m()]; -} -'''); - - assertNullCheck(checkExpression('b1/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - } - - Future test_postDominators_forInElement() async { - await analyze(''' -class C { - int m() => 0; -} -void test(List l, C c1) { - [for (C _c in l/*check*/) c1.m()]; - [for (C c2 in []) c2.m()]; -} -'''); - - assertNullCheck( - checkExpression('l/*check*/')!, - assertEdge(decoratedTypeAnnotation('List l').node, never, - hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - } - - Future test_postDominators_forInStatement_unconditional() async { - await analyze(''' -class C { - void m() {} -} -void test(List l, C c1, C c2) { - for (C c3 in l/*check*/) { - c1.m(); - c3.m(); - } - - c2.m(); -} -'''); - - assertNullCheck( - checkExpression('l/*check*/')!, - assertEdge(decoratedTypeAnnotation('List l').node, never, - hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false)); - } - - Future test_postDominators_forStatement_conditional() async { - await analyze(''' - -class C { - void m() {} -} -void test(bool b1, C c1, C c2, C c3) { - for (; b1/*check*/; c2.m()) { - C c4 = c1; - c4.m(); - return; - } - - c3.m(); -} -'''); - - assertNullCheck(checkExpression('b1/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - assertNullCheck(checkExpression('c4.m')!, - assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: true)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false)); - } - - Future test_postDominators_forStatement_unconditional() async { - await analyze(''' - -class C { - void m() {} -} -void test(bool b1, C c1, C c2, C c3) { - for (bool b2 = b1, b3 = b1; b1/*check*/ & b2/*check*/; c3.m()) { - c1.m(); - assert(b3 != null); - } - - c2.m(); -} -'''); - - assertNullCheck(checkExpression('b1/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - //TODO(mfairhurst): enable this check - //assertNullCheck(checkExpression('b2/*check*/'), - // assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true)); - //assertEdge(decoratedTypeAnnotation('b3 =').node, never, hard: false); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false)); - } - - Future test_postDominators_ifElement() async { - await analyze(''' -class C { - int m() => 0; -} -void test(bool b, C c1, C c2, C c3) { - [if (b) c1.m() else c2.m()]; - c3.m(); -} -'''); - - assertNullCheck(checkExpression('b)')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true)); - } - - Future test_postDominators_ifStatement_conditional() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b, C c1, C c2) { - if (b/*check*/) { - C c3 = C(); - C c4 = C(); - c1.m(); - c3.m(); - - // Divergence breaks post-dominance. - return; - c4.m(); - - } - c2.m(); -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true)); - assertNullCheck(checkExpression('c4.m')!, - assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: false)); - } - - Future test_postDominators_ifStatement_unconditional() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b, C c1, C c2) { - if (b/*check*/) { - C c3 = C(); - C c4 = C(); - c1.m(); - c3.m(); - - // We ignore exceptions for post-dominance. - throw ''; - c4.m(); - - } - c2.m(); -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true)); - assertNullCheck(checkExpression('c4.m')!, - assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: true)); - } - - Future test_postDominators_inReturn_local() async { - await analyze(''' -class C { - int m() => 0; -} -int test(C c) { - return c.m(); -} -'''); - - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_postDominators_loopReturn() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b1, C _c) { - C c1 = _c; - while (b1/*check*/) { - bool b2 = b1; - C c2 = _c; - if (b2/*check*/) { - return; - } - c2.m(); - } - c1.m(); -} -'''); - - assertNullCheck(checkExpression('b1/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b1').node, never, hard: true)); - assertNullCheck(checkExpression('b2/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b2').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - } - - Future test_postDominators_multiDeclaration() async { - // Multi declarations cannot use hard edges as shown below. - await analyze(''' -void test() { - int i1 = 0, i2 = null; - i1.toDouble(); -} -'''); - - // i1.toDouble() cannot be a hard edge or i2 will fail assignment - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: false); - // i2 gets a soft edge to always due to null assignment - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int i').node, - hard: false); - } - - Future test_postDominators_questionQuestionOperator() async { - await analyze(''' -class C { - Object m() => null; -} -Object test(C x, C y) => x.m() ?? y.m(); -'''); - // There is a hard edge from x to `never` because `x.m()` is unconditionally - // reachable from the top of `test`. - assertEdge(decoratedTypeAnnotation('C x').node, never, hard: true); - // However, the edge from y to `never` is soft because `y.m()` is only - // executed if `x.m()` returned `null`. - assertEdge(decoratedTypeAnnotation('C y').node, never, - hard: false, guards: [decoratedTypeAnnotation('Object m').node]); - } - - Future test_postDominators_reassign() async { - await analyze(''' -void test(bool b, int i1, int i2) { - i1 = null; - i1.toDouble(); - if (b) { - i2 = null; - } - i2.toDouble(); -} -'''); - - assertNullCheck(checkExpression('i1.toDouble')!, - assertEdge(decoratedTypeAnnotation('int i1').node, never, hard: false)); - - assertNullCheck(checkExpression('i2.toDouble')!, - assertEdge(decoratedTypeAnnotation('int i2').node, never, hard: false)); - } - - Future test_postDominators_shortCircuitOperators() async { - await analyze(''' -class C { - bool m() => true; -} -void test(C c1, C c2, C c3, C c4) { - c1.m() && c2.m(); - c3.m() || c4.m(); -} -'''); - - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: true)); - - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true)); - - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - - assertNullCheck(checkExpression('c4.m')!, - assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: false)); - } - - Future test_postDominators_subFunction() async { - await analyze(''' -class C { - void m() {} -} -void test() { - (C c) { - c.m(); - }; -} -'''); - - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - @failingTest - Future test_postDominators_subFunction_ifStatement_conditional() async { - // Failing because function expressions aren't implemented - await analyze(''' -class C { - void m() {} -} -void test() { - (bool b, C c) { - if (b/*check*/) { - return; - } - c.m(); - }; -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: false)); - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: false)); - } - - Future - test_postDominators_subFunction_ifStatement_unconditional() async { - await analyze(''' -class C { - void m() {} -} -void test() { - (bool b, C c) { - if (b/*check*/) { - } - c.m(); - }; -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertNullCheck(checkExpression('c.m')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_postDominators_subFunction_nested() async { - await analyze(''' -f1(int a, int b, int c, int d) { - f2() { - a = 0; - f3() { - b = 0; - } - c = 0; - } - a + 1; - b + 1; - c + 1; - d + 1; -} -'''); - // a, b, and c may all be written to prior to their use, so their use sites - // don't demonstrate non-null intent. - assertEdge(decoratedTypeAnnotation('int a').node, never, hard: false); - assertEdge(decoratedTypeAnnotation('int b').node, never, hard: false); - assertEdge(decoratedTypeAnnotation('int c').node, never, hard: false); - // However, the use of `d` does demonstrate non-null intent because there's - // no write to `d`. - assertEdge(decoratedTypeAnnotation('int d').node, never, hard: true); - } - - Future test_postDominators_subFunction_nested_closure() async { - await analyze(''' -f1(int a, int b, int c, int d) { - var f2 = () { - a = 0; - var f3 = () { - b = 0; - }; - c = 0; - }; - a + 1; - b + 1; - c + 1; - d + 1; -} -'''); - // a, b, and c may all be written to prior to their use, so their use sites - // don't demonstrate non-null intent. - assertEdge(decoratedTypeAnnotation('int a').node, never, hard: false); - assertEdge(decoratedTypeAnnotation('int b').node, never, hard: false); - assertEdge(decoratedTypeAnnotation('int c').node, never, hard: false); - // However, the use of `d` does demonstrate non-null intent because there's - // no write to `d`. - assertEdge(decoratedTypeAnnotation('int d').node, never, hard: true); - } - - Future test_postDominators_ternaryOperator() async { - await analyze(''' -class C { - bool m() => true; -} -void test(C c1, C c2, C c3, C c4) { - c1.m() ? c2.m() : c3.m(); - - c4.m(); -} -'''); - - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: true)); - - assertNullCheck(checkExpression('c4.m')!, - assertEdge(decoratedTypeAnnotation('C c4').node, never, hard: true)); - - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: false)); - - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: false)); - } - - Future test_postDominators_tryCatch() async { - await analyze(''' -void test(int i) { - try {} catch (_) { - i.isEven; - } -} -'''); - // Edge should not be hard because the call to `i.isEven` does not - // post-dominate the declaration of `i`. - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: false); - } - - Future test_postDominators_whileStatement_unconditional() async { - await analyze(''' -class C { - void m() {} -} -void test(bool b, C c1, C c2) { - while (b/*check*/) { - C c3 = C(); - c1.m(); - c3.m(); - } - - c2.m(); -} -'''); - - assertNullCheck(checkExpression('b/*check*/')!, - assertEdge(decoratedTypeAnnotation('bool b').node, never, hard: true)); - assertNullCheck(checkExpression('c1.m')!, - assertEdge(decoratedTypeAnnotation('C c1').node, never, hard: false)); - assertNullCheck(checkExpression('c2.m')!, - assertEdge(decoratedTypeAnnotation('C c2').node, never, hard: true)); - assertNullCheck(checkExpression('c3.m')!, - assertEdge(decoratedTypeAnnotation('C c3').node, never, hard: true)); - } - - Future test_postfixExpression_minusMinus() async { - await analyze(''' -int f(int i) { - return i--; -} -'''); - - var declaration = decoratedTypeAnnotation('int i').node; - var use = checkExpression('i--')!; - assertNullCheck(use, assertEdge(declaration, never, hard: true)); - - var returnType = decoratedTypeAnnotation('int f').node; - assertEdge(declaration, returnType, hard: false); - } - - Future test_postfixExpression_plusPlus() async { - await analyze(''' -int f(int i) { - return i++; -} -'''); - - var declaration = decoratedTypeAnnotation('int i').node; - var use = checkExpression('i++')!; - assertNullCheck(use, assertEdge(declaration, never, hard: true)); - - var returnType = decoratedTypeAnnotation('int f').node; - assertEdge(declaration, returnType, hard: false); - } - - Future test_postfixExpression_plusPlus_dynamic() async { - await analyze(''' -Object f(dynamic d) { - return d++; -} -'''); - assertEdge(decoratedTypeAnnotation('dynamic d').node, - decoratedTypeAnnotation('Object f').node, - hard: false); - } - - Future test_postfixExpression_plusPlus_substituted() async { - await analyze(''' -abstract class C { - C operator+(int x); -} -C f(C c) { - return c++; -} -'''); - - var cType = decoratedTypeAnnotation('C c'); - var returnType = decoratedTypeAnnotation('C f'); - assertNullCheck( - checkExpression('c++')!, assertEdge(cType.node, never, hard: true)); - assertEdge(cType.node, returnType.node, hard: false); - assertEdge(cType.typeArguments[0]!.node, returnType.typeArguments[0]!.node, - hard: false, checkable: false); - } - - Future test_prefixedIdentifier_bangHint() async { - await analyze(''' -import 'dart:math' as m; -double f1() => m.pi; -double f2() => m.pi/*!*/; -'''); - expect( - assertEdge(anyNode, decoratedTypeAnnotation('double f1').node, - hard: false) - .sourceNode, - isNot(never)); - expect( - assertEdge(anyNode, decoratedTypeAnnotation('double f2').node, - hard: false) - .sourceNode, - never); - expect(hasNullCheckHint(findNode.prefixed('m.pi/*!*/')), isTrue); - } - - Future test_prefixedIdentifier_extension_nullTarget_get() async { - await analyze(''' -class C {} -extension on C /*1*/ { - int get x => 0; -} -void f() { - C c = null; - c.x; -} -'''); - assertEdge(decoratedTypeAnnotation('C c').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: true); - } - - Future test_prefixedIdentifier_extension_nullTarget_set() async { - await analyze(''' -class C {} -extension on C /*1*/ { - set x(int value) {} -} -void f() { - C c = null; - c.x = 0; -} -'''); - assertEdge(decoratedTypeAnnotation('C c').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: true); - } - - Future test_prefixedIdentifier_field_type() async { - await analyze(''' -class C { - bool b = true; -} -bool f(C c) => c.b; -'''); - assertEdge(decoratedTypeAnnotation('bool b').node, - decoratedTypeAnnotation('bool f').node, - hard: false); - } - - Future test_prefixedIdentifier_getter_type() async { - await analyze(''' -class C { - bool get b => true; -} -bool f(C c) => c.b; -'''); - assertEdge(decoratedTypeAnnotation('bool get').node, - decoratedTypeAnnotation('bool f').node, - hard: false); - } - - Future test_prefixedIdentifier_getter_type_in_generic() async { - await analyze(''' -class C { - List _x; - List get x => _x; -} -List f(C c) => c.x; -'''); - assertEdge(decoratedTypeAnnotation('List get').node, - decoratedTypeAnnotation('List f').node, - hard: false); - assertEdge( - substitutionNode(decoratedTypeAnnotation('int> c').node, - decoratedTypeAnnotation('T> get').node), - decoratedTypeAnnotation('int> f').node, - hard: false, - checkable: false); - } - - Future test_prefixedIdentifier_target_check() async { - await analyze(''' -class C { - int get x => 1; -} -void test(C c) { - c.x; -} -'''); - - assertNullCheck(checkExpression('c.x')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future - test_prefixedIdentifier_target_demonstrates_non_null_intent() async { - await analyze(''' -class C { - int get x => 1; -} -void test(C c) { - c.x; -} -'''); - - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true); - } - - Future test_prefixedIdentifier_tearoff() async { - await analyze(''' -abstract class C { - int f(int i); -} -int Function(int) g(C c) => c.f; -'''); - var fType = variables.decoratedElementType(findElement.method('f')); - var gReturnType = - variables.decoratedElementType(findElement.function('g')).returnType!; - assertEdge(fType.returnType!.node, gReturnType.returnType!.node, - hard: false, checkable: false); - assertEdge(gReturnType.positionalParameters![0].node, - fType.positionalParameters![0].node, - hard: false, checkable: false); - } - - Future test_prefixExpression_bang() async { - await analyze(''' -bool f(bool b) { - return !b; -} -'''); - - var nullable_b = decoratedTypeAnnotation('bool b').node; - var check_b = checkExpression('b;')!; - assertNullCheck(check_b, assertEdge(nullable_b, never, hard: true)); - - var return_f = decoratedTypeAnnotation('bool f').node; - assertEdge(inSet(pointsToNever), return_f, hard: false); - } - - Future test_prefixExpression_bang_dynamic() async { - await analyze(''' -Object f(dynamic d) { - return !d; -} -'''); - var return_f = decoratedTypeAnnotation('Object f').node; - assertEdge(inSet(pointsToNever), return_f, hard: false); - } - - Future test_prefixExpression_minus() async { - await analyze(''' -abstract class C { - C operator-(); -} -C test(C c) => -c/*check*/; -'''); - assertEdge(decoratedTypeAnnotation('C operator').node, - decoratedTypeAnnotation('C test').node, - hard: false); - assertNullCheck(checkExpression('c/*check*/')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_prefixExpression_minus_dynamic() async { - await analyze(''' -Object test(dynamic d) => -d; -'''); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('Object test').node, - hard: false); - assertEdge(decoratedTypeAnnotation('dynamic d').node, never, hard: true); - } - - Future test_prefixExpression_minus_substituted() async { - await analyze(''' -abstract class C { - List operator-(); -} -List test(C c) => -c/*check*/; -'''); - var operatorReturnType = decoratedTypeAnnotation('List operator'); - var cType = decoratedTypeAnnotation('C c'); - var testReturnType = decoratedTypeAnnotation('List test'); - assertEdge(operatorReturnType.node, testReturnType.node, hard: false); - assertNullCheck(checkExpression('c/*check*/')!, - assertEdge(cType.node, never, hard: true)); - assertEdge( - substitutionNode(cType.typeArguments[0]!.node, - operatorReturnType.typeArguments[0]!.node), - testReturnType.typeArguments[0]!.node, - hard: false, - checkable: false); - } - - Future test_prefixExpression_minusMinus() async { - await analyze(''' -int f(int i) { - return --i; -} -'''); - - var declaration = decoratedTypeAnnotation('int i').node; - var use = checkExpression('i;')!; - assertNullCheck(use, assertEdge(declaration, never, hard: true)); - - var returnType = decoratedTypeAnnotation('int f').node; - assertEdge(inSet(pointsToNever), returnType, hard: false); - } - - Future test_prefixExpression_plusPlus() async { - await analyze(''' -int f(int i) { - return ++i; -} -'''); - - var declaration = decoratedTypeAnnotation('int i').node; - var use = checkExpression('i;')!; - assertNullCheck(use, assertEdge(declaration, never, hard: true)); - - var returnType = decoratedTypeAnnotation('int f').node; - assertEdge(inSet(pointsToNever), returnType, hard: false); - } - - Future test_prefixExpression_plusPlus_dynamic() async { - await analyze(''' -Object f(dynamic d) { - return ++d; -} -'''); - var returnType = decoratedTypeAnnotation('Object f').node; - assertEdge(inSet(alwaysPlus), returnType, hard: false); - } - - Future test_prefixExpression_plusPlus_substituted() async { - await analyze(''' -abstract class C { - C operator+(int i); -} -C f(C x) => ++x; - '''); - var xType = decoratedTypeAnnotation('C x'); - var plusReturnType = decoratedTypeAnnotation('C operator'); - var fReturnType = decoratedTypeAnnotation('C f'); - assertEdge(xType.node, never, hard: true); - assertEdge(plusReturnType.node, fReturnType.node, hard: false); - assertEdge( - substitutionNode(xType.typeArguments[0]!.node, - plusReturnType.typeArguments[0]!.node), - fReturnType.typeArguments[0]!.node, - hard: false, - checkable: false); - } - - Future test_property_generic_onResultOfImplicitSuper() async { - await analyze(''' -class Base { - Base x; -} - -class Sub extends Base { - void implicitSuper() => x.x; -} -'''); - // Don't bother checking any edges; the assertions in the DecoratedType - // constructor verify that we've substituted the bound correctly. - } - - Future test_property_implicitSuper_assignment() async { - await analyze(''' -class Base { - T1 x; -} - -class Sub extends Base { - void g() => x = null; -} -'''); - assertEdge( - inSet(alwaysPlus), - substitutionNode( - substitutionNode( - anyNode, // non-null for `this`. - decoratedTypeAnnotation('T2> {').node, - ), - decoratedTypeAnnotation('T1 x').node, - ), - hard: false); - } - - Future test_propertyAccess_bangHint() async { - await analyze(''' -abstract class C { - int get i1; - int get i2; -} -int f1(C c) => (c).i1; -int f2(C c) => (c).i2/*!*/; -'''); - assertEdge(decoratedTypeAnnotation('int get i1').node, - decoratedTypeAnnotation('int f1').node, - hard: false); - assertNoEdge(decoratedTypeAnnotation('int get i2').node, - decoratedTypeAnnotation('int f2').node); - expect(hasNullCheckHint(findNode.propertyAccess('(c).i2')), isTrue); - } - - Future test_propertyAccess_call_functionTyped() async { - await analyze(''' -String/*1*/ Function(int/*2*/) f(String/*3*/ Function(int/*4*/) callback) - => callback.call; -'''); - assertEdge(decoratedTypeAnnotation('String/*3*/').node, - decoratedTypeAnnotation('String/*1*/').node, - hard: false, checkable: false); - assertEdge(decoratedTypeAnnotation('int/*2*/').node, - decoratedTypeAnnotation('int/*4*/').node, - hard: false, checkable: false); - var tearOffNodeMatcher = anyNode; - assertEdge( - tearOffNodeMatcher, - decoratedGenericFunctionTypeAnnotation('String/*1*/ Function(int/*2*/)') - .node, - hard: false); - assertEdge(never, tearOffNodeMatcher.matchingNode, - hard: true, checkable: false); - } - - Future test_propertyAccess_call_interfaceTyped() async { - // Make sure that we don't try to treat all methods called `call` as though - // the underlying type is a function type. - await analyze(''' -abstract class C { - String call(int x); -} -String Function(int) f(C c) => c.call; -'''); - assertEdge(decoratedTypeAnnotation('String call').node, - decoratedTypeAnnotation('String Function').node, - hard: false, checkable: false); - assertEdge(decoratedTypeAnnotation('int) f').node, - decoratedTypeAnnotation('int x').node, - hard: false, checkable: false); - assertEdge(never, - decoratedGenericFunctionTypeAnnotation('String Function(int)').node, - hard: false); - } - - Future test_propertyAccess_dynamic() async { - await analyze(''' -class C { - int get g => 0; -} -int f(dynamic d) { - return d.g; -} -'''); - // The call `d.g` is dynamic, so we can't tell what method it resolves - // to. There's no reason to assume it resolves to `C.g`. - assertNoEdge(decoratedTypeAnnotation('int get g').node, - decoratedTypeAnnotation('int f').node); - // We do, however, assume that it might return anything, including `null`. - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_propertyAccess_extension_nullTarget_get() async { - await analyze(''' -class C {} -extension on C /*1*/ { - int get x => 0; -} -void f() { - C g() => null; - g().x; -} -'''); - assertEdge(decoratedTypeAnnotation('C g()').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: false); - } - - Future test_propertyAccess_extension_nullTarget_get_explicit() async { - await analyze(''' -class C {} -extension E on C /*1*/ { - int get x => 0; -} -void f() { - C g() => null; - E(g()).x; -} -'''); - assertEdge(decoratedTypeAnnotation('C g()').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: false); - } - - Future test_propertyAccess_extension_nullTarget_set() async { - await analyze(''' -class C {} -extension on C /*1*/ { - set x(int value) {} -} -void f() { - C g() => null; - g().x = 0; -} -'''); - assertEdge(decoratedTypeAnnotation('C g()').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: false); - } - - Future test_propertyAccess_extension_nullTarget_set_explicit() async { - await analyze(''' -class C {} -extension E on C /*1*/ { - set x(int value) {} -} -void f() { - C g() => null; - E(g()).x = 0; -} -'''); - assertEdge(decoratedTypeAnnotation('C g()').node, - decoratedTypeAnnotation('C /*1*/').node, - hard: false); - } - - Future test_propertyAccess_object_property() async { - await analyze(''' -int f(int i) => i.hashCode; -'''); - // No edge from i to `never` because it is safe to call `hashCode` on - // `null`. - assertNoEdge(decoratedTypeAnnotation('int i').node, never); - } - - Future test_propertyAccess_object_property_on_function_type() async { - await analyze('int f(void Function() g) => g.hashCode;'); - var hashCodeReturnType = variables - .decoratedElementType( - typeProvider.objectType.element.getGetter('hashCode')!) - .returnType!; - assertEdge(hashCodeReturnType.node, decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_propertyAccess_return_type() async { - await analyze(''' -class C { - bool get b => true; -} -bool f(C c) => (c).b; -'''); - assertEdge(decoratedTypeAnnotation('bool get').node, - decoratedTypeAnnotation('bool f').node, - hard: false); - } - - Future test_propertyAccess_return_type_null_aware() async { - await analyze(''' -class C { - bool get b => true; -} -bool f(C c) => (c?.b); -'''); - var lubNode = - decoratedExpressionType('(c?.b)')!.node as NullabilityNodeForLUB; - expect(lubNode.left, same(decoratedTypeAnnotation('C c').node)); - expect(lubNode.right, same(decoratedTypeAnnotation('bool get b').node)); - assertEdge(lubNode, decoratedTypeAnnotation('bool f').node, hard: false); - } - - Future test_propertyAccess_static_on_generic_class() async { - await analyze(''' -class C { - static int x = 1; -} -int f() => C.x; -'''); - assertEdge(decoratedTypeAnnotation('int x').node, - decoratedTypeAnnotation('int f').node, - hard: false); - } - - Future test_propertyAccess_target_check() async { - await analyze(''' -class C { - int get x => 1; -} -void test(C c) { - (c).x; -} -'''); - - assertNullCheck(checkExpression('c).x')!, - assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true)); - } - - Future test_quiver_checkNotNull_not_postDominating() async { - addQuiverPackage(); - await analyze(''' -import 'package:quiver/check.dart'; -void f(bool b, int i, int j) { - checkNotNull(j); - if (b) return; - checkNotNull(i); -} -'''); - - // Asserts after ifs don't demonstrate non-null intent. - assertNoEdge(decoratedTypeAnnotation('int i').node, never); - // But asserts before ifs do - assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true); - } - - Future test_quiver_checkNotNull_postDominating() async { - addQuiverPackage(); - await analyze(''' -import 'package:quiver/check.dart'; -void f(int i) { - checkNotNull(i); -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - } - - Future test_quiver_checkNotNull_prefixed() async { - addQuiverPackage(); - await analyze(''' -import 'package:quiver/check.dart' as quiver; -void f(int i) { - quiver.checkNotNull(i); -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true); - } - - Future test_redirecting_constructor_factory() async { - await analyze(''' -class C { - factory C(int/*1*/ i, {int/*2*/ j}) = D; -} -class D implements C { - D(int/*3*/ i, {int/*4*/ j}); -} -'''); - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*3*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int/*2*/').node, - decoratedTypeAnnotation('int/*4*/').node, - hard: true); - } - - Future - test_redirecting_constructor_factory_from_generic_to_generic() async { - await analyze(''' -class C { - factory C(T/*1*/ t) = D; -} -class D implements C { - D(U/*3*/ u); -} -'''); - var nullable_t1 = decoratedTypeAnnotation('T/*1*/').node; - var nullable_t2 = decoratedTypeAnnotation('T/*2*/').node; - var nullable_u3 = decoratedTypeAnnotation('U/*3*/').node; - assertEdge(nullable_t1, substitutionNode(nullable_t2, nullable_u3), - hard: true); - } - - Future test_redirecting_constructor_factory_to_generic() async { - await analyze(''' -class C { - factory C(int/*1*/ i) = D; -} -class D implements C { - D(T/*3*/ i); -} -'''); - var nullable_i1 = decoratedTypeAnnotation('int/*1*/').node; - var nullable_i2 = decoratedTypeAnnotation('int/*2*/').node; - var nullable_t3 = decoratedTypeAnnotation('T/*3*/').node; - assertEdge(nullable_i1, substitutionNode(nullable_i2, nullable_t3), - hard: true); - } - - Future test_redirecting_constructor_ordinary() async { - await analyze(''' -class C { - C(int/*1*/ i, int/*2*/ j) : this.named(j, i); - C.named(int/*3*/ j, int/*4*/ i); -} -'''); - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*4*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int/*2*/').node, - decoratedTypeAnnotation('int/*3*/').node, - hard: true); - } - - Future test_redirecting_constructor_ordinary_to_unnamed() async { - await analyze(''' -class C { - C.named(int/*1*/ i, int/*2*/ j) : this(j, i); - C(int/*3*/ j, int/*4*/ i); -} -'''); - assertEdge(decoratedTypeAnnotation('int/*1*/').node, - decoratedTypeAnnotation('int/*4*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int/*2*/').node, - decoratedTypeAnnotation('int/*3*/').node, - hard: true); - } - - Future test_return_from_async_bottom() async { - await analyze(''' -Future f() async => throw ''; -'''); - assertNoEdge(always, decoratedTypeAnnotation('Future').node); - assertNoEdge(always, decoratedTypeAnnotation('int').node); - } - - Future test_return_from_async_closureBody_future() async { - await analyze(''' -Future f() { - return () async { - return g(); - }(); -} -int g() => 1; -'''); - assertEdge( - decoratedTypeAnnotation('int g').node, - // TODO(40621): This should be a checkable edge. - assertEdge(anyNode, decoratedTypeAnnotation('int>').node, - hard: false, checkable: false) - .sourceNode, - hard: false, - // TODO(40621): This should be a checkable edge. - checkable: false); - } - - Future test_return_from_async_closureExpression_future() async { - await analyze(''' -Future Function() f() { - return () async => g(); -} -int g() => 1; -'''); - assertEdge( - decoratedTypeAnnotation('int g').node, - // TODO(40621): This should be a checkable edge. - assertEdge(anyNode, decoratedTypeAnnotation('int>').node, - hard: true, checkable: false) - .sourceNode, - hard: false, - // TODO(40621): This should be a checkable edge. - checkable: false); - } - - Future test_return_from_async_expressionBody_future() async { - await analyze(''' -Future f() async => g(); -int g() => 1; -'''); - // TODO(40621): This should be a checkable edge. - assertEdge(decoratedTypeAnnotation('int g').node, - decoratedTypeAnnotation('int>').node, - hard: false, checkable: false); - } - - Future test_return_from_async_future() async { - await analyze(''' -Future f() async { - return g(); -} -int g() => 1; -'''); - // TODO(40621): This should be a checkable edge. - assertEdge(decoratedTypeAnnotation('int g').node, - decoratedTypeAnnotation('int>').node, - hard: false, checkable: false); - } - - Future test_return_from_async_future_void() async { - await analyze(''' -Future f() async { - return; -} -int g() => 1; -'''); - assertNoEdge(always, decoratedTypeAnnotation('Future').node); - } - - Future test_return_from_async_futureOr() async { - await analyze(''' -import 'dart:async'; -FutureOr f() async { - return g(); -} -int g() => 1; -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_return_from_async_futureOr_to_future() async { - await analyze(''' -import 'dart:async'; -Future f(FutureOr x) async => x; -'''); - var lubNodeMatcher = anyNode; - assertEdge(lubNodeMatcher, decoratedTypeAnnotation('Object').node, - hard: true, checkable: false); - var lubNode = lubNodeMatcher.matchingNode as NullabilityNodeForLUB; - expect(lubNode.left, same(decoratedTypeAnnotation('int> x').node)); - expect(lubNode.right, same(decoratedTypeAnnotation('FutureOr').node)); - } - - Future test_return_from_async_list_to_future() async { - await analyze(''' -import 'dart:async'; -Future f(List x) async => x; -'''); - assertEdge(decoratedTypeAnnotation('List').node, - decoratedTypeAnnotation('Object').node, - hard: true, checkable: false); - } - - Future test_return_from_async_null() async { - await analyze(''' -Future f() async { - return null; -} -'''); - // TODO(40621): This should be a checkable edge. - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int>').node, - hard: false, checkable: false); - } - - Future test_return_function_type_simple() async { - await analyze(''' -int/*1*/ Function() f(int/*2*/ Function() x) => x; -'''); - var int1 = decoratedTypeAnnotation('int/*1*/'); - var int2 = decoratedTypeAnnotation('int/*2*/'); - assertEdge(int2.node, int1.node, hard: false, checkable: false); - } - - Future test_return_implicit_null() async { - verifyNoTestUnitErrors = false; - await analyze(''' -int f() { - return; -} -'''); - - var edge = assertEdge( - inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false); - expect( - edge.sourceNode!.displayName, 'implicit null return (test.dart:2:3)'); - } - - Future test_return_in_asyncStar() async { - await analyze(''' -Stream f() async* { - yield 1; - return; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Stream').node); - } - - Future test_return_in_syncStar() async { - await analyze(''' -Iterable f() sync* { - yield 1; - return; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Iterable').node); - } - - Future test_return_null() async { - await analyze(''' -int f() { - return null; -} -'''); - - var edge = assertEdge( - inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false); - assertNullCheck(checkExpression('null')!, edge); - expect(edge.sourceNode!.displayName, 'null literal (test.dart:2:10)'); - } - - Future test_return_null_generic() async { - await analyze(''' -class C { - T f() { - return null; - } -} -'''); - var tNode = decoratedTypeAnnotation('T f').node; - assertEdge(inSet(alwaysPlus), tNode, hard: false); - assertNullCheck(checkExpression('null')!, - assertEdge(inSet(alwaysPlus), tNode, hard: false)); - } - - Future - test_setOrMapLiteral_map_noTypeArgument_noNullableKeysAndValues() async { - await analyze(''' -Map f() { - return {'a' : 1, 'b' : 2}; -} -'''); - var keyNode = decoratedTypeAnnotation('String').node; - var valueNode = decoratedTypeAnnotation('int').node; - var mapNode = decoratedTypeAnnotation('Map').node; - - assertNoUpstreamNullability(mapNode); - var keyEdge = assertEdge(anyNode, keyNode, hard: true, checkable: false); - assertNoUpstreamNullability(keyEdge.sourceNode); - expect(keyEdge.sourceNode!.displayName, 'map key type (test.dart:2:10)'); - var valueEdge = - assertEdge(anyNode, valueNode, hard: true, checkable: false); - assertNoUpstreamNullability(valueEdge.sourceNode); - expect( - valueEdge.sourceNode!.displayName, 'map value type (test.dart:2:10)'); - } - - Future test_setOrMapLiteral_map_noTypeArgument_nullableKey() async { - await analyze(''' -Map f() { - return {'a' : 1, null : 2, 'c' : 3}; -} -'''); - var keyNode = decoratedTypeAnnotation('String').node; - var valueNode = decoratedTypeAnnotation('int').node; - var mapNode = decoratedTypeAnnotation('Map').node; - - assertNoUpstreamNullability(mapNode); - assertEdge(inSet(alwaysPlus), - assertEdge(anyNode, keyNode, hard: true, checkable: false).sourceNode, - hard: false); - assertNoUpstreamNullability( - assertEdge(anyNode, valueNode, hard: true, checkable: false) - .sourceNode); - } - - Future - test_setOrMapLiteral_map_noTypeArgument_nullableKeyAndValue() async { - await analyze(''' -Map f() { - return {'a' : 1, null : null, 'c' : 3}; -} -'''); - var keyNode = decoratedTypeAnnotation('String').node; - var valueNode = decoratedTypeAnnotation('int').node; - var mapNode = decoratedTypeAnnotation('Map').node; - - assertNoUpstreamNullability(mapNode); - assertEdge(inSet(alwaysPlus), - assertEdge(anyNode, keyNode, hard: true, checkable: false).sourceNode, - hard: false); - assertEdge(inSet(alwaysPlus), - assertEdge(anyNode, valueNode, hard: true, checkable: false).sourceNode, - hard: false); - } - - Future test_setOrMapLiteral_map_noTypeArgument_nullableValue() async { - await analyze(''' -Map f() { - return {'a' : 1, 'b' : null, 'c' : 3}; -} -'''); - var keyNode = decoratedTypeAnnotation('String').node; - var valueNode = decoratedTypeAnnotation('int').node; - var mapNode = decoratedTypeAnnotation('Map').node; - - assertNoUpstreamNullability(mapNode); - assertNoUpstreamNullability( - assertEdge(anyNode, keyNode, hard: true, checkable: false).sourceNode); - assertEdge(inSet(alwaysPlus), - assertEdge(anyNode, valueNode, hard: true, checkable: false).sourceNode, - hard: false); - } - - Future - test_setOrMapLiteral_map_typeArguments_noNullableKeysAndValues() async { - await analyze(''' -Map f() { - return {'a' : 1, 'b' : 2}; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - - var keyForLiteral = decoratedTypeAnnotation('String, int>{').node; - var keyForReturnType = decoratedTypeAnnotation('String, int> ').node; - assertNoUpstreamNullability(keyForLiteral); - assertEdge(keyForLiteral, keyForReturnType, hard: true, checkable: false); - - var valueForLiteral = decoratedTypeAnnotation('int>{').node; - var valueForReturnType = decoratedTypeAnnotation('int> ').node; - assertNoUpstreamNullability(valueForLiteral); - assertEdge(valueForLiteral, valueForReturnType, - hard: true, checkable: false); - } - - Future test_setOrMapLiteral_map_typeArguments_nullableKey() async { - await analyze(''' -Map f() { - return {'a' : 1, null : 2, 'c' : 3}; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('String, int>{').node, - hard: false); - assertNoUpstreamNullability(decoratedTypeAnnotation('int>{').node); - } - - Future - test_setOrMapLiteral_map_typeArguments_nullableKeyAndValue() async { - await analyze(''' -Map f() { - return {'a' : 1, null : null, 'c' : 3}; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('String, int>{').node, - hard: false); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int>{').node, - hard: false); - } - - Future test_setOrMapLiteral_map_typeArguments_nullableValue() async { - await analyze(''' -Map f() { - return {'a' : 1, 'b' : null, 'c' : 3}; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node); - assertNoUpstreamNullability(decoratedTypeAnnotation('String, int>{').node); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int>{').node, - hard: false); - } - - Future - test_setOrMapLiteral_set_noTypeArgument_noNullableElements() async { - await analyze(''' -Set f() { - return {'a', 'b'}; -} -'''); - var valueNode = decoratedTypeAnnotation('String').node; - var setNode = decoratedTypeAnnotation('Set').node; - - assertNoUpstreamNullability(setNode); - var edge = assertEdge(anyNode, valueNode, hard: true, checkable: false); - assertNoUpstreamNullability(edge.sourceNode); - expect(edge.sourceNode!.displayName, 'set element type (test.dart:2:10)'); - } - - Future test_setOrMapLiteral_set_noTypeArgument_nullableElement() async { - await analyze(''' -Set f() { - return {'a', null, 'c'}; -} -'''); - var valueNode = decoratedTypeAnnotation('String').node; - var setNode = decoratedTypeAnnotation('Set').node; - - assertNoUpstreamNullability(setNode); - assertEdge(inSet(alwaysPlus), - assertEdge(anyNode, valueNode, hard: true, checkable: false).sourceNode, - hard: false); - } - - Future - test_setOrMapLiteral_set_typeArgument_noNullableElements() async { - await analyze(''' -Set f() { - return {'a', 'b'}; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node); - var typeArgForLiteral = decoratedTypeAnnotation('String>{').node; - var typeArgForReturnType = decoratedTypeAnnotation('String> ').node; - assertNoUpstreamNullability(typeArgForLiteral); - assertEdge(typeArgForLiteral, typeArgForReturnType, - hard: true, checkable: false); - } - - Future test_setOrMapLiteral_set_typeArgument_nullableElement() async { - await analyze(''' -Set f() { - return {'a', null, 'c'}; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('String>{').node, - hard: false); - } - - Future test_setter_overrides_implicit_setter() async { - await analyze(''' -class A { - String/*1*/ s = "x"; -} -class C implements A { - String get s => "x"; - void set s(String/*2*/ value) {} -} -f() => A().s = null; -'''); - var string1 = decoratedTypeAnnotation('String/*1*/'); - var string2 = decoratedTypeAnnotation('String/*2*/'); - assertEdge(string1.node, string2.node, hard: true); - } - - Future test_setupAssignment_assignment_inDistantSetUp() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart'; -void main() { - int i; - // There could be tests here in which [i] is not certain to have been - // assigned. - - group('g2', () { - setUp(() { - i = 1; - }); - }); -} -'''); - - assertNoEdge(graph.never, decoratedTypeAnnotation('int').node); - } - - Future test_setupAssignment_assignment_inSetUp() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart'; -void main() { - int i; - int j = 1; - setUp(() { - i = j; - }); -} -'''); - - assertNullCheck( - checkExpression('j;')!, - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int i').node, - hard: false, isSetupAssignment: true)); - } - - Future test_setupAssignment_assignment_inUnrelatedSetUp() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart'; -void main() { - group('g1', () { - int/*1*/ i; - }); - - group('g2', () { - int/*2*/ i; - int j = 1; - setUp(() { - i = j; - }); - }); -} -'''); - - assertNoEdge(graph.never, decoratedTypeAnnotation('int/*1*/').node); - assertNullCheck( - checkExpression('j;')!, - assertEdge(decoratedTypeAnnotation('int j').node, - decoratedTypeAnnotation('int/*2*/').node, - hard: false, isSetupAssignment: true)); - } - - Future test_setupAssignment_assignment_inWrongSetUp() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart' as t; -void main() { - int i; - setUp(() { - i = 1; - }); -} -void setUp(dynamic callback()) {} -'''); - - assertNoEdge(graph.never, decoratedTypeAnnotation('int').node); - } - - Future test_setupAssignment_assignment_outsideSetUp() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart'; -void main() { - int i; - i = 1; -} -'''); - - assertNoEdge(graph.never, decoratedTypeAnnotation('int').node); - } - - Future test_setupAssignment_assignment_toField() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart'; -void main() { - setUp(() { - C c = C(); - c.i = 1; - }); -} -class C { - int i; -} -'''); - - assertNoEdge(graph.never, decoratedTypeAnnotation('int').node); - } - - Future test_setupAssignment_nullAwareAssignment_inSetUp() async { - addTestCorePackage(); - await analyze(''' -import 'package:test/test.dart'; -void main() { - int i; - int j = 1; - setUp(() { - i ??= j; - }); -} -'''); - - var iNullable = decoratedTypeAnnotation('int i').node; - assertNullCheck( - checkExpression('j;')!, - assertEdge(decoratedTypeAnnotation('int j').node, iNullable, - hard: false, guards: [iNullable], isSetupAssignment: true)); - } - - Future test_simpleIdentifier_bangHint() async { - await analyze(''' -int f1(int i1) => i1; -int f2(int i2) => i2/*!*/; -'''); - assertEdge(decoratedTypeAnnotation('int i1').node, - decoratedTypeAnnotation('int f1').node, - hard: true); - assertNoEdge(decoratedTypeAnnotation('int i2').node, - decoratedTypeAnnotation('int f2').node); - expect(hasNullCheckHint(findNode.simple('i2/*!*/')), isTrue); - } - - Future test_simpleIdentifier_function() async { - await analyze(''' -int f() => null; -main() { - int Function() g = f; -} -'''); - - assertEdge(decoratedTypeAnnotation('int f').node, - decoratedTypeAnnotation('int Function').node, - hard: false, checkable: false); - } - - Future test_simpleIdentifier_local() async { - await analyze(''' -main() { - int i = 0; - int j = i; -} -'''); - - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int j').node, - hard: true); - } - - Future test_simpleIdentifier_tearoff_function() async { - await analyze(''' -int f(int i) => 0; -int Function(int) g() => f; -'''); - var fType = variables.decoratedElementType(findElement.function('f')); - var gReturnType = - variables.decoratedElementType(findElement.function('g')).returnType!; - assertEdge(fType.returnType!.node, gReturnType.returnType!.node, - hard: false, checkable: false); - assertEdge(gReturnType.positionalParameters![0].node, - fType.positionalParameters![0].node, - hard: false, checkable: false); - } - - Future test_simpleIdentifier_tearoff_method() async { - await analyze(''' -abstract class C { - int f(int i); - int Function(int) g() => f; -} -'''); - var fType = variables.decoratedElementType(findElement.method('f')); - var gReturnType = - variables.decoratedElementType(findElement.method('g')).returnType!; - assertEdge(fType.returnType!.node, gReturnType.returnType!.node, - hard: false, checkable: false); - assertEdge(gReturnType.positionalParameters![0].node, - fType.positionalParameters![0].node, - hard: false, checkable: false); - } - - Future test_skipDirectives() async { - await analyze(''' -import "dart:core" as one; -main() {} -'''); - // No test expectations. - // Just verifying that the test passes - } - - Future test_soft_edge_for_non_variable_reference() async { - // Edges originating in things other than variable references should be - // soft. - await analyze(''' -int f() => null; -'''); - assertEdge(inSet(alwaysPlus), decoratedTypeAnnotation('int').node, - hard: false); - } - - Future test_spread_element_list() async { - await analyze(''' -void f(List ints) { - [...ints]; -} -'''); - - assertEdge(decoratedTypeAnnotation('List').node, never, hard: true); - assertEdge( - substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode), - decoratedTypeAnnotation('int>[').node, - hard: true, - checkable: false); - } - - Future test_spread_element_list_dynamic() async { - await analyze(''' -void f(dynamic ints) { - [...ints]; -} -'''); - - // Mostly just check this doesn't crash. - assertEdge(decoratedTypeAnnotation('dynamic').node, never, hard: true); - } - - Future test_spread_element_list_nullable() async { - await analyze(''' -void f(List ints) { - [...?ints]; -} -'''); - - assertNoEdge(decoratedTypeAnnotation('List').node, never); - assertEdge( - substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode), - decoratedTypeAnnotation('int>[').node, - hard: true, - checkable: false); - } - - Future test_spread_element_map() async { - await analyze(''' -void f(Map map) { - {...map}; -} -'''); - - assertEdge(decoratedTypeAnnotation('Map').node, never, - hard: true); - assertEdge(decoratedTypeAnnotation('String, int> map').node, - decoratedTypeAnnotation('String, int>{').node, - hard: true, checkable: false); - assertEdge(decoratedTypeAnnotation('int> map').node, - decoratedTypeAnnotation('int>{').node, - hard: true, checkable: false); - } - - Future test_spread_element_set() async { - await analyze(''' -void f(Set ints) { - {...ints}; -} -'''); - - assertEdge(decoratedTypeAnnotation('Set').node, never, hard: true); - assertEdge( - substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode), - decoratedTypeAnnotation('int>{').node, - hard: true, - checkable: false); - } - - Future test_spread_element_subtype() async { - await analyze(''' -abstract class C implements Iterable {} -void f(C ints) { - [...ints]; -} -'''); - - assertEdge(decoratedTypeAnnotation('C').node, never, - hard: true); - assertEdge( - substitutionNode(decoratedTypeAnnotation('int> ints').node, - decoratedTypeAnnotation('R> {}').node), - decoratedTypeAnnotation('int>[').node, - hard: true, - checkable: false); - } - - Future test_static_method_call_prefixed() async { - await analyze(''' -import 'dart:async' as a; -void f(void Function() callback) { - a.Timer.run(callback); -} -'''); - // No assertions. Just making sure this doesn't crash. - } - - Future test_stringLiteral() async { - // TODO(paulberry): also test string interpolations - await analyze(''' -String f() { - return 'x'; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('String').node); - } - - Future test_superExpression() async { - await analyze(''' -class B { - void f(int/*1*/ i, int/*2*/ j) {} -} -class C extends B { - void f(int/*3*/ i, int/*4*/ j) => super.f(j, i); -} -'''); - assertEdge(decoratedTypeAnnotation('int/*3*/').node, - decoratedTypeAnnotation('int/*2*/').node, - hard: true); - assertEdge(decoratedTypeAnnotation('int/*4*/').node, - decoratedTypeAnnotation('int/*1*/').node, - hard: true); - } - - Future test_superExpression_generic() async { - await analyze(''' -class B { - U g() => null; -} -class C extends B { - T f() => super.g(); -} -'''); - assertEdge( - substitutionNode( - substitutionNode( - inSet(pointsToNever), decoratedTypeAnnotation('T> {').node), - decoratedTypeAnnotation('U g').node), - decoratedTypeAnnotation('T f').node, - hard: false); - } - - Future test_symbolLiteral() async { - await analyze(''' -Symbol f() { - return #symbol; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Symbol').node); - } - - Future test_this_bangHint() async { - await analyze(''' -extension on int { - int f1() => this; - int f2() => this/*!*/; -} -'''); - expect( - assertEdge(anyNode, decoratedTypeAnnotation('int f1').node, hard: true) - .sourceNode, - isNot(never)); - expect( - assertEdge(anyNode, decoratedTypeAnnotation('int f2').node, hard: true) - .sourceNode, - never); - expect(hasNullCheckHint(findNode.this_('this/*!*/')), isTrue); - } - - Future test_thisExpression() async { - await analyze(''' -class C { - C f() => this; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('C f').node); - } - - Future test_thisExpression_generic() async { - await analyze(''' -class C { - C f() => this; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('C f').node); - assertNoUpstreamNullability(decoratedTypeAnnotation('T> f').node); - } - - Future test_throwExpression() async { - await analyze(''' -int f() { - return throw null; -} -'''); - var intNode = decoratedTypeAnnotation('int').node; - assertNoUpstreamNullability(intNode); - var edge = assertEdge(anyNode, intNode, hard: false); - expect(edge.sourceNode!.displayName, 'throw expression (test.dart:2:10)'); - } - - Future test_top_level_annotation_begins_flow_analysis() async { - await analyze(''' -class C { - const C(bool x); -} -@C(true) -int x; -'''); - } - - Future test_topLevelSetter() async { - await analyze(''' -void set x(int value) {} -main() { x = 1; } -'''); - var setXType = decoratedTypeAnnotation('int value'); - assertEdge(inSet(pointsToNever), setXType.node, hard: false); - } - - Future test_topLevelSetter_nullable() async { - await analyze(''' -void set x(int value) {} -main() { x = null; } -'''); - var setXType = decoratedTypeAnnotation('int value'); - assertEdge(inSet(alwaysPlus), setXType.node, hard: false); - } - - Future test_topLevelVar_implicitInitializer() async { - await analyze('int i;'); - assertEdge(always, decoratedTypeAnnotation('int').node, hard: false); - } - - Future test_topLevelVar_metadata() async { - await analyze(''' -class A { - const A(); -} -@A() -int v; -'''); - // No assertions needed; the AnnotationTracker mixin verifies that the - // metadata was visited. - } - - Future test_topLevelVar_reference() async { - await analyze(''' -double pi = 3.1415; -double get myPi => pi; -'''); - var piType = decoratedTypeAnnotation('double pi'); - var myPiType = decoratedTypeAnnotation('double get'); - assertEdge(piType.node, myPiType.node, hard: false); - } - - Future test_topLevelVar_reference_differentPackage() async { - addPackageFile('foo', 'piConst.dart', ''' -double pi = 3.1415; -'''); - await analyze(''' -import "package:foo/piConst.dart"; -double get myPi => pi; -'''); - var myPiType = decoratedTypeAnnotation('double get'); - assertEdge(inSet(pointsToNever), myPiType.node, hard: false); - } - - Future test_topLevelVariable_type_inferred() async { - await analyze(''' -int f() => 1; -var x = f(); -'''); - var xType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - assertEdge(decoratedTypeAnnotation('int').node, xType.node, hard: false); - } - - Future test_type_argument_explicit_bound() async { - await analyze(''' -class C {} -void f(C c) {} -'''); - assertEdge(decoratedTypeAnnotation('int>').node, - decoratedTypeAnnotation('Object>').node, - hard: true); - } - - Future test_type_parameter_method_call_bound() async { - await analyze(''' -class Foo { - void bar(int x) {} -} - -void f(T t) { - t.bar(null); -} -'''); - assertEdge(decoratedTypeAnnotation('T t').node, never, hard: true); - // TODO(mfairhurst): fix this: https://github.com/dart-lang/sdk/issues/39852 - //assertEdge(decoratedTypeAnnotation('Foo>').node, never, hard: true); - // There is a direct soft edge from always, since it's a public method, - // but we want to test there is also an edge resulting from t.bar(null) - // usage in f. - assertEdge(inSet(alwaysPlus.difference({always})), - decoratedTypeAnnotation('int x').node, - hard: false); - } - - Future test_type_parameter_method_call_bound_bound() async { - await analyze(''' -class Foo { - void bar(int x) {} -} - -void f(T t) { - t.bar(null); -} -'''); - assertEdge(decoratedTypeAnnotation('T t').node, never, hard: true); - // TODO(mfairhurst): fix this: https://github.com/dart-lang/sdk/issues/39852 - //assertEdge(decoratedTypeAnnotation('Foo>').node, never, hard: true); - // There is a direct soft edge from always, since it's a public method, - // but we want to test there is also an edge resulting from t.bar(null) - // usage in f. - assertEdge(inSet(alwaysPlus.difference({always})), - decoratedTypeAnnotation('int x').node, - hard: false); - } - - Future test_type_parameter_method_call_bound_generic() async { - await analyze(''' -class Foo { - void bar(int x) {} -} - -void f>(T t) { - t.bar(null); -} -'''); - assertEdge(decoratedTypeAnnotation('T t').node, never, hard: true); - // TODO(mfairhurst): fix this: https://github.com/dart-lang/sdk/issues/39852 - //assertEdge(decoratedTypeAnnotation('Foo>').node, never, hard: true); - // There is a direct soft edge from always, since it's a public method, - // but we want to test there is also an edge resulting from t.bar(null) - // usage in f. - assertEdge(inSet(alwaysPlus.difference({always})), - decoratedTypeAnnotation('int x').node, - hard: false); - } - - Future test_type_parameter_method_call_bound_generic_complex() async { - await analyze(''' -class Foo { - void bar(T x) {} -} - -void f>(T t) { - t.bar(null); -} -'''); - assertEdge(decoratedTypeAnnotation('T t').node, never, hard: true); - // TODO(mfairhurst): fix this: https://github.com/dart-lang/sdk/issues/39852 - //assertEdge(decoratedTypeAnnotation('Foo>').node, never, hard: true); - assertEdge( - inSet(alwaysPlus), - substitutionNode(decoratedTypeAnnotation('R>').node, - decoratedTypeAnnotation('T x').node), - hard: false); - } - - Future test_type_parameterized_migrated_bound_class() async { - await analyze(''' -import 'dart:math'; -void f(Point x) {} -'''); - var pointClass = findNode.namedType('Point').element as ClassElement; - var pointBound = - variables.decoratedTypeParameterBound(pointClass.typeParameters[0])!; - _assertType(pointBound.type!, 'num'); - assertEdge(decoratedTypeAnnotation('int>').node, pointBound.node, - hard: true); - } - - Future test_type_parameterized_migrated_bound_dynamic() async { - await analyze(''' -void f(List x) {} -'''); - var listClass = typeProvider.listElement; - var listBound = - variables.decoratedTypeParameterBound(listClass.typeParameters[0])!; - _assertType(listBound.type!, 'dynamic'); - assertEdge(decoratedTypeAnnotation('int>').node, listBound.node, - hard: true); - } - - Future test_typedef_rhs_not_linked_to_usage() async { - await analyze(''' -typedef F = void Function(); -F f; -'''); - var rhs = decoratedGenericFunctionTypeAnnotation('void Function()'); - var usage = decoratedTypeAnnotation('F f'); - assertNoEdge(rhs.node, usage.node); - } - - Future test_typeName_class() async { - await analyze(''' -class C {} -Type f() => C; -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node); - } - - Future test_typeName_from_sdk() async { - await analyze(''' -Type f() { - return int; -} -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node); - } - - Future test_typeName_from_sdk_prefixed() async { - await analyze(''' -import 'dart:async' as a; -Type f() => a.Future; -'''); - assertEdge(inSet(neverClosure), decoratedTypeAnnotation('Type').node, - hard: false); - } - - Future test_typeName_functionTypeAlias() async { - await analyze(''' -typedef void F(); -Type f() => F; -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node); - } - - Future test_typeName_genericTypeAlias() async { - await analyze(''' -typedef F = void Function(); -Type f() => F; -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node); - } - - Future test_typeName_mixin() async { - await analyze(''' -mixin M {} -Type f() => M; -'''); - assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node); - } - - Future test_typeName_with_bound() async { - await analyze(''' -class C {} -void f(C c) {} -'''); - var cType = decoratedTypeAnnotation('C c'); - var cBound = decoratedTypeAnnotation('Object'); - assertEdge(cType.typeArguments[0]!.node, cBound.node, hard: true); - } - - Future test_typeName_with_bound_function_type() async { - await analyze(''' -class C {} -void f(C c) {} -'''); - var cType = decoratedTypeAnnotation('C c'); - var cBound = decoratedGenericFunctionTypeAnnotation('int Function()'); - assertEdge(cType.typeArguments[0]!.node, cBound.node, hard: true); - assertEdge( - cType.typeArguments[0]!.returnType!.node, cBound.returnType!.node, - hard: true); - } - - Future test_typeName_with_bounds() async { - await analyze(''' -class C {} -void f(C c) {} -'''); - var cType = decoratedTypeAnnotation('C c'); - var tBound = decoratedTypeAnnotation('Object,'); - var uBound = decoratedTypeAnnotation('Object>'); - assertEdge(cType.typeArguments[0]!.node, tBound.node, hard: true); - assertEdge(cType.typeArguments[1]!.node, uBound.node, hard: true); - } - - Future test_variableDeclaration() async { - await analyze(''' -void f(int i) { - int j = i; -} -'''); - assertEdge(decoratedTypeAnnotation('int i').node, - decoratedTypeAnnotation('int j').node, - hard: true); - } - - void _assertType(DartType type, String expected) { - var typeStr = type.getDisplayString(withNullability: false); - expect(typeStr, expected); - } -} - -class _DecoratedClassHierarchyForTesting implements DecoratedClassHierarchy { - late AssignmentCheckerTest assignmentCheckerTest; - - @override - DecoratedType asInstanceOf(DecoratedType type, InterfaceElement? superclass) { - var class_ = (type.type as InterfaceType).element; - if (class_ == superclass) return type; - if (superclass!.name == 'Object') { - return DecoratedType( - superclass.instantiate( - typeArguments: const [], - nullabilitySuffix: NullabilitySuffix.star, - ), - type.node, - ); - } - if (class_.name == 'MyListOfList' && superclass.name == 'List') { - return assignmentCheckerTest._myListOfListSupertype - .substitute({class_.typeParameters[0]: type.typeArguments[0]!}); - } - if (class_.name == 'List' && superclass.name == 'Iterable') { - return DecoratedType( - superclass.instantiate( - typeArguments: [type.typeArguments[0]!.type!], - nullabilitySuffix: NullabilitySuffix.star, - ), - type.node, - typeArguments: [type.typeArguments[0]], - ); - } - if (class_.name == 'Future' && superclass.name == 'FutureOr') { - return DecoratedType( - superclass.instantiate( - typeArguments: [type.typeArguments[0]!.type!], - nullabilitySuffix: NullabilitySuffix.star, - ), - type.node, - typeArguments: [type.typeArguments[0]], - ); - } - throw UnimplementedError( - 'TODO(paulberry): asInstanceOf($type, $superclass)'); - } - - @override - DecoratedType getDecoratedSupertype( - InterfaceElement class_, InterfaceElement superclass) { - throw UnimplementedError('TODO(paulberry)'); - } -} - -class _MockSource implements Source { - @override - final Uri uri; - - _MockSource(this.uri); - - @override - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -class _TestEdgeOrigin implements EdgeOrigin { - const _TestEdgeOrigin(); - - @override - CodeReference? get codeReference => null; - - @override - String get description => 'Test edge'; - - @override - bool get isSetupAssignment => false; - - @override - EdgeOriginKind? get kind => null; - - @override - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} diff --git a/pkg/nnbd_migration/test/edit_plan_test.dart b/pkg/nnbd_migration/test/edit_plan_test.dart deleted file mode 100644 index 058bdd0c5a09..000000000000 --- a/pkg/nnbd_migration/test/edit_plan_test.dart +++ /dev/null @@ -1,1718 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/precedence.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/utilities/hint_utils.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'abstract_single_unit.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(EditPlanTest); - defineReflectiveTests(EndsInCascadeTest); - defineReflectiveTests(PrecedenceTest); - }); -} - -@reflectiveTest -class EditPlanTest extends AbstractSingleUnitTest { - String? code; - - EditPlanner? _planner; - - @override - bool get analyzeWithNnbd => true; - - EditPlanner? get planner { - if (_planner == null) createPlanner(); - return _planner; - } - - Future analyze(String code) async { - this.code = code; - await resolveTestUnit(code); - } - - Map> checkPlan(EditPlan plan, String expected, - {String? expectedIncludingInformative}) { - expectedIncludingInformative ??= expected; - var changes = planner!.finalize(plan)!; - expect(changes.applyTo(code!), expected); - expect(changes.applyTo(code!, includeInformative: true), - expectedIncludingInformative); - return changes; - } - - void createPlanner({bool removeViaComments = false}) { - _planner = EditPlanner(testUnit!.lineInfo, code, - removeViaComments: removeViaComments); - } - - NodeProducingEditPlan extract(AstNode inner, AstNode? outer) => - planner!.extract(outer, planner!.passThrough(inner)); - - Future test_acceptLateHint() async { - var code = '/* late */ int x = 0;'; - await analyze(code); - var hint = getPrefixHint(findNode.namedType('int').name2)!; - var changes = checkPlan( - planner!.acceptPrefixHint( - planner!.passThrough(findNode.namedType('int')), hint), - 'late int x = 0;'); - expect(changes.keys, unorderedEquals([0, 7])); - expect(changes[7], hasLength(1)); - expect(changes[7]![0].length, 3); - } - - Future test_acceptLateHint_space_needed_after() async { - var code = '/* late */int x = 0;'; - await analyze(code); - var hint = getPrefixHint(findNode.namedType('int').name2)!; - checkPlan( - planner!.acceptPrefixHint( - planner!.passThrough(findNode.namedType('int')), hint), - 'late int x = 0;'); - } - - Future test_acceptLateHint_space_needed_before() async { - var code = '@deprecated/* late */ int x = 0;'; - await analyze(code); - var hint = getPrefixHint(findNode.namedType('int').name2)!; - checkPlan( - planner!.acceptPrefixHint( - planner!.passThrough(findNode.namedType('int')), hint), - '@deprecated late int x = 0;'); - } - - Future - test_acceptNullabilityHint_function_typed_field_formal_parameter() async { - await analyze(''' -class C { - void Function(int) f; - C(void this.f(int i) /*?*/); -} -'''); - var parameter = findNode.fieldFormalParameter('void this.f(int i)'); - var typeName = planner!.passThrough(parameter); - checkPlan( - planner!.acceptSuffixHint( - typeName, getPostfixHint(parameter.parameters!.rightParenthesis)!), - ''' -class C { - void Function(int) f; - C(void this.f(int i)?); -} -'''); - } - - Future test_acceptNullabilityHint_function_typed_parameter() async { - await analyze('f(void g(int i) /*?*/) {}'); - var parameter = findNode.functionTypedFormalParameter('void g(int i)'); - var typeName = planner!.passThrough(parameter); - checkPlan( - planner!.acceptSuffixHint( - typeName, getPostfixHint(parameter.parameters.rightParenthesis)!), - 'f(void g(int i)?) {}'); - } - - Future test_acceptNullabilityOrNullCheckHint() async { - var code = 'int /*?*/ x = 0;'; - await analyze(code); - var intRef = findNode.namedType('int'); - var typeName = planner!.passThrough(intRef); - checkPlan( - planner!.acceptSuffixHint(typeName, getPostfixHint(intRef.name2)!), - 'int? x = 0;'); - } - - Future test_acceptNullabilityOrNullCheckHint_inside_extract() async { - var code = 'f(x) => 3 * x /*!*/ * 4;'; - await analyze(code); - var xRef = findNode.simple('x /*'); - checkPlan( - planner!.extract( - xRef.parent!.parent, - planner!.acceptSuffixHint( - planner!.passThrough(xRef), getPostfixHint(xRef.token)!)), - 'f(x) => x!;'); - } - - Future test_addBinaryPostfix_assignment_right_associative() async { - await analyze('_f(a, b, c) => a = b;'); - // Admittedly this is sort of a bogus test case, since the code it produces - // (`(a = b) = c`) is non-grammatical. But we still want to verify that it - // *doesn't* produce `a = b = c`, which would be grammatical but which would - // break apart the subexpression `a = b`. - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.assignment('a = b')), - TokenType.EQ, - 'c'), - '_f(a, b, c) => (a = b) = c;'); - } - - Future test_addBinaryPostfix_associative() async { - await analyze('var x = 1 - 2;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.binary('-')), TokenType.MINUS, '3'), - 'var x = 1 - 2 - 3;'); - } - - Future test_addBinaryPostfix_endsInCascade() async { - await analyze('f(x) => x..y = 1;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.integerLiteral('1')), - TokenType.PLUS, - '2'), - 'f(x) => x..y = 1 + 2;'); - } - - Future test_addBinaryPostfix_equality_non_associative() async { - await analyze('var x = 1 == 2;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.binary('==')), TokenType.EQ_EQ, '3'), - 'var x = (1 == 2) == 3;'); - } - - Future test_addBinaryPostfix_inner_precedence() async { - await analyze('var x = 1 < 2;'); - checkPlan( - planner!.addBinaryPostfix(planner!.passThrough(findNode.binary('<')), - TokenType.EQ_EQ, 'true'), - 'var x = 1 < 2 == true;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.binary('<')), TokenType.AS, 'bool'), - 'var x = (1 < 2) as bool;'); - } - - Future test_addBinaryPostfix_outer_precedence() async { - await analyze('var x = 1 == true;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.integerLiteral('1')), - TokenType.LT, - '2'), - 'var x = 1 < 2 == true;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.integerLiteral('1')), - TokenType.EQ_EQ, - '2'), - 'var x = (1 == 2) == true;'); - } - - Future test_addBinaryPostfix_to_expression_function() async { - await analyze('var x = () => null;'); - checkPlan( - planner!.addBinaryPostfix( - planner!.passThrough(findNode.functionExpression('()')), - TokenType.AS, - 'Object'), - 'var x = (() => null) as Object;'); - } - - Future test_addBinaryPrefix_allowCascade() async { - await analyze('f(x) => 1..isEven;'); - checkPlan( - planner!.addBinaryPrefix( - 'x..y', TokenType.EQ, planner!.passThrough(findNode.cascade('..'))), - 'f(x) => x..y = (1..isEven);'); - checkPlan( - planner!.addBinaryPrefix( - 'x', TokenType.EQ, planner!.passThrough(findNode.cascade('..')), - allowCascade: true), - 'f(x) => x = 1..isEven;'); - } - - Future test_addBinaryPrefix_assignment_right_associative() async { - await analyze('_f(a, b, c) => b = c;'); - checkPlan( - planner!.addBinaryPrefix('a', TokenType.EQ, - planner!.passThrough(findNode.assignment('b = c'))), - '_f(a, b, c) => a = b = c;'); - } - - Future test_addBinaryPrefix_associative() async { - await analyze('var x = 1 - 2;'); - checkPlan( - planner!.addBinaryPrefix( - '0', TokenType.MINUS, planner!.passThrough(findNode.binary('-'))), - 'var x = 0 - (1 - 2);'); - } - - Future test_addBinaryPrefix_outer_precedence() async { - await analyze('var x = 2 == true;'); - checkPlan( - planner!.addBinaryPrefix('1', TokenType.LT, - planner!.passThrough(findNode.integerLiteral('2'))), - 'var x = 1 < 2 == true;'); - checkPlan( - planner!.addBinaryPrefix('1', TokenType.EQ_EQ, - planner!.passThrough(findNode.integerLiteral('2'))), - 'var x = (1 == 2) == true;'); - } - - Future test_addBinaryPrefix_to_expression_function() async { - await analyze('f(x) => () => null;'); - checkPlan( - planner!.addBinaryPrefix('x', TokenType.EQ, - planner!.passThrough(findNode.functionExpression('()'))), - 'f(x) => x = () => null;'); - } - - Future test_addCommentPostfix_before_closer() async { - await analyze('f(g) => g(0);'); - checkPlan( - planner!.addCommentPostfix( - planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'), - 'f(g) => g(0 /* zero */);'); - } - - Future test_addCommentPostfix_before_other() async { - await analyze('f() => 0.isEven;'); - checkPlan( - planner!.addCommentPostfix( - planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'), - 'f() => 0 /* zero */ .isEven;'); - } - - Future test_addCommentPostfix_before_semicolon() async { - await analyze('f() => 0;'); - checkPlan( - planner!.addCommentPostfix( - planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'), - 'f() => 0 /* zero */;'); - } - - Future test_addCommentPostfix_before_space() async { - await analyze('f() => 0 + 1;'); - checkPlan( - planner!.addCommentPostfix( - planner!.passThrough(findNode.integerLiteral('0')), '/* zero */'), - 'f() => 0 /* zero */ + 1;'); - } - - Future test_addCommentPostfix_informative() async { - await analyze('f() => 0.isEven;'); - checkPlan( - planner!.addCommentPostfix( - planner!.passThrough(findNode.integerLiteral('0')), '/* zero */', - isInformative: true), - 'f() => 0.isEven;', - expectedIncludingInformative: 'f() => 0 /* zero */ .isEven;'); - } - - Future test_addPostfix_inner_precedence_add_parens() async { - await analyze('f(x) => -x;'); - checkPlan( - planner! - .addPostfix(planner!.passThrough(findNode.prefix('-x')), '.abs()'), - 'f(x) => (-x).abs();'); - } - - Future test_addPostfix_inner_precedence_no_parens() async { - await analyze('f(x) => x++;'); - checkPlan( - planner!.addPostfix( - planner!.passThrough(findNode.postfix('x++')), '.abs()'), - 'f(x) => x++.abs();'); - } - - Future test_addPostfix_outer_precedence() async { - await analyze('f(x) => x/*!*/;'); - checkPlan( - planner!.addPostfix( - planner!.passThrough(findNode.simple('x/*!*/')), '.abs()'), - 'f(x) => x.abs()/*!*/;'); - } - - Future test_addUnaryPostfix_inner_precedence_add_parens() async { - await analyze('f(x) => -x;'); - checkPlan( - planner!.addUnaryPostfix( - planner!.passThrough(findNode.prefix('-x')), TokenType.BANG), - 'f(x) => (-x)!;'); - } - - Future test_addUnaryPostfix_inner_precedence_no_parens() async { - await analyze('f(x) => x++;'); - checkPlan( - planner!.addUnaryPostfix( - planner!.passThrough(findNode.postfix('x++')), TokenType.BANG), - 'f(x) => x++!;'); - } - - Future test_addUnaryPostfix_outer_precedence() async { - await analyze('f(x) => x/*!*/;'); - checkPlan( - planner!.addUnaryPostfix( - planner!.passThrough(findNode.simple('x/*!*/')), - TokenType.PLUS_PLUS), - 'f(x) => x++/*!*/;'); - } - - Future test_addUnaryPrefix_inner_precedence_add_parens() async { - await analyze('f(x, y) => x * y;'); - checkPlan( - planner!.addUnaryPrefix( - TokenType.MINUS, planner!.passThrough(findNode.binary('*'))), - 'f(x, y) => -(x * y);'); - } - - Future test_addUnaryPrefix_inner_precedence_no_parens() async { - await analyze('f(x) => -x;'); - // TODO(paulberry): if we added a `-` instead of a `~`, the result would - // scan as a single `--` token, so we would need parens. Add support for - // this corner case. - checkPlan( - planner!.addUnaryPrefix( - TokenType.TILDE, planner!.passThrough(findNode.prefix('-x'))), - 'f(x) => ~-x;'); - } - - Future test_addUnaryPrefix_outer_precedence_add_parens() async { - await analyze('f(x) => x!;'); - checkPlan( - planner!.addUnaryPrefix( - TokenType.MINUS, planner!.passThrough(findNode.simple('x!'))), - 'f(x) => (-x)!;'); - } - - Future test_addUnaryPrefix_outer_precedence_no_parens() async { - await analyze('f(x) => -x;'); - // TODO(paulberry): if we added a `-` instead of a `~`, the result would - // scan as a single `--` token, so we would need parens. Add support for - // this corner case. - checkPlan( - planner!.addUnaryPrefix( - TokenType.TILDE, planner!.passThrough(findNode.simple('x;'))), - 'f(x) => -~x;'); - } - - Future test_cascadeSearchLimit() async { - // Ok, we have to ask each parent if it represents a cascade section. - // If we create a passThrough at node N, then when we create an enclosing - // passThrough, the first thing we'll check is N's parent. - await analyze('f(a, c) => a..b = c = 1;'); - var cascade = findNode.cascade('..'); - var outerAssignment = findNode.assignment('= c'); - assert(identical(cascade, outerAssignment.parent)); - var innerAssignment = findNode.assignment('= 1'); - assert(identical(outerAssignment, innerAssignment.parent)); - var one = findNode.integerLiteral('1'); - assert(identical(innerAssignment, one.parent)); - // The tests below will be based on an inner plan that adds `..isEven` after - // the `1`. - EditPlan makeInnerPlan() => planner!.surround(planner!.passThrough(one), - suffix: [AtomicEdit.insert('..isEven')], endsInCascade: true); - { - // If we make a plan that passes through `c = 1`, containing a plan that - // adds `..isEven` to `1`, then we don't necessarily want to add parens yet, - // because we might not keep the cascade section above it. - var plan = - planner!.passThrough(innerAssignment, innerPlans: [makeInnerPlan()]); - // `endsInCascade` returns true because we haven't committed to adding - // parens, so we need to remember that the presence of `..isEven` may - // require parens later. - expect(plan.endsInCascade, true); - checkPlan(planner!.extract(cascade, plan), 'f(a, c) => c = 1..isEven;'); - } - { - // If we make a plan that passes through `..b = c = 1`, containing a plan - // that adds `..isEven` to `1`, then we do necessarily want to add parens, - // because we're committed to keeping the cascade section. - var plan = - planner!.passThrough(outerAssignment, innerPlans: [makeInnerPlan()]); - // We can tell that the parens have been finalized because `endsInCascade` - // returns false now. - expect(plan.endsInCascade, false); - checkPlan(plan, 'f(a, c) => a..b = (c = 1..isEven);'); - } - } - - Future test_dropNullabilityHint() async { - var code = 'int /*!*/ x = 0;'; - await analyze(code); - var intRef = findNode.namedType('int'); - var typeName = planner!.passThrough(intRef); - checkPlan( - planner!.dropNullabilityHint(typeName, getPostfixHint(intRef.name2)!), - 'int x = 0;'); - } - - Future test_dropNullabilityHint_space_before_must_be_kept() async { - var code = 'int /*!*/x = 0;'; - await analyze(code); - var intRef = findNode.namedType('int'); - var typeName = planner!.passThrough(intRef); - var changes = checkPlan( - planner!.dropNullabilityHint(typeName, getPostfixHint(intRef.name2)!), - 'int x = 0;'); - expect(changes.keys, unorderedEquals([code.indexOf('/*')])); - } - - Future test_dropNullabilityHint_space_needed() async { - var code = 'int/*!*/x = 0;'; - await analyze(code); - var intRef = findNode.namedType('int'); - var typeName = planner!.passThrough(intRef); - checkPlan( - planner!.dropNullabilityHint(typeName, getPostfixHint(intRef.name2)!), - 'int x = 0;'); - } - - Future test_dropNullabilityHint_tight_no_space_needed() async { - // We try to minimize how much we alter the source code, so we don't insert - // a space in this example even though it would look prettier to do so. - var code = 'void Function()/*!*/x = () {};'; - await analyze(code); - var functionType = findNode.genericFunctionType('Function'); - var typeName = planner!.passThrough(functionType); - checkPlan( - planner!.dropNullabilityHint( - typeName, getPostfixHint(functionType.endToken)!), - 'void Function()x = () {};'); - } - - Future test_explainNonNullable() async { - await analyze('int x = 0;'); - checkPlan( - planner!.explainNonNullable( - planner!.passThrough(findNode.typeAnnotation('int'))), - 'int x = 0;', - expectedIncludingInformative: 'int x = 0;'); - } - - Future test_extract_add_parens() async { - await analyze('f(g) => 1 * g(2, 3 + 4, 5);'); - checkPlan( - extract( - findNode.binary('+'), findNode.functionExpressionInvocation('+')), - 'f(g) => 1 * (3 + 4);'); - } - - Future test_extract_inner_endsInCascade() async { - await analyze('f(a, g) => a..b = g(0, 1..isEven, 2);'); - expect( - extract(findNode.cascade('1..isEven'), - findNode.functionExpressionInvocation('g(')) - .endsInCascade, - true); - expect( - extract(findNode.integerLiteral('1'), - findNode.functionExpressionInvocation('g(')) - .endsInCascade, - false); - } - - Future test_extract_left() async { - await analyze('var x = 1 + 2;'); - checkPlan(extract(findNode.integerLiteral('1'), findNode.binary('+')), - 'var x = 1;'); - } - - Future test_extract_no_parens_needed() async { - await analyze('var x = 1 + 2 * 3;'); - checkPlan(extract(findNode.integerLiteral('2'), findNode.binary('*')), - 'var x = 1 + 2;'); - } - - Future test_extract_preserve_parens() async { - // Note: extra spaces to verify that we are really preserving the parens - // rather than removing them and adding new ones. - await analyze('var x = ( 1 << 2 ) * 3 + 4;'); - checkPlan(extract(findNode.binary('<<'), findNode.binary('*')), - 'var x = ( 1 << 2 ) + 4;'); - } - - Future test_extract_remove_parens() async { - await analyze('var x = (1 + 2) * 3 << 4;'); - checkPlan(extract(findNode.binary('+'), findNode.binary('*')), - 'var x = 1 + 2 << 4;'); - } - - Future test_extract_remove_redundant_parens() async { - await analyze('var x = (1 * 2) + 3;'); - var times = findNode.binary('*'); - checkPlan(extract(times, times.parent), 'var x = 1 * 2 + 3;'); - } - - Future test_extract_try_to_remove_necessary_parens() async { - // This is a weird corner case. We try to extract the expression `1 + 2` - // from `( 1 + 2 )`, meaning we should remove parens. But the parens are - // necessary. So we create fresh ones (without the spaces). - await analyze('var x = ( 1 + 2 ) * 3;'); - var plus = findNode.binary('+'); - checkPlan(extract(plus, plus.parent), 'var x = (1 + 2) * 3;'); - } - - Future test_extract_using_comments_inner() async { - await analyze('var x = 1 + 2 * 3;'); - createPlanner(removeViaComments: true); - checkPlan(extract(findNode.integerLiteral('2'), findNode.binary('+')), - 'var x = /* 1 + */ 2 /* * 3 */;'); - } - - Future test_extract_using_comments_left() async { - await analyze('var x = 1 + 2;'); - createPlanner(removeViaComments: true); - checkPlan(extract(findNode.integerLiteral('1'), findNode.binary('+')), - 'var x = 1 /* + 2 */;'); - } - - Future test_extract_using_comments_right() async { - await analyze('var x = 1 + 2;'); - createPlanner(removeViaComments: true); - checkPlan(extract(findNode.integerLiteral('2'), findNode.binary('+')), - 'var x = /* 1 + */ 2;'); - } - - Future test_finalize_compilationUnit() async { - // Verify that an edit plan referring to the entire compilation unit can be - // finalized. (This is an important corner case because the entire - // compilation unit is an AstNode with no parent). - await analyze('var x = 0;'); - checkPlan( - planner!.surround(planner!.passThrough(testUnit), - suffix: [AtomicEdit.insert(' var y = 0;')]), - 'var x = 0; var y = 0;'); - } - - Future test_informativeMessageForToken() async { - await analyze('f(x) => x + 1;'); - var sum = findNode.binary('+'); - var info = _MockInfo(); - var changes = checkPlan( - planner!.passThrough(sum, innerPlans: [ - planner!.informativeMessageForToken(sum, sum.operator, info: info) - ]), - 'f(x) => x + 1;', - expectedIncludingInformative: 'f(x) => x 1;'); - var expectedOffset = sum.operator.offset; - expect(changes.keys, unorderedEquals([expectedOffset])); - expect(changes[expectedOffset], hasLength(1)); - expect(changes[expectedOffset]![0].length, '+'.length); - expect(changes[expectedOffset]![0].replacement, ''); - expect(changes[expectedOffset]![0].isInformative, isTrue); - expect(changes[expectedOffset]![0].info, same(info)); - } - - Future test_insertText() async { - await analyze('final x = 1;'); - var variableDeclarationList = findNode.variableDeclarationList('final'); - checkPlan( - planner!.insertText( - variableDeclarationList, - variableDeclarationList.variables.first.offset, - [AtomicEdit.insert('int ')]), - 'final int x = 1;'); - } - - Future test_makeNullable() async { - await analyze('int x = 0;'); - checkPlan( - planner! - .makeNullable(planner!.passThrough(findNode.typeAnnotation('int'))), - 'int? x = 0;'); - } - - Future test_passThrough_remove_statement() async { - await analyze(''' -void f() { - var x = () { - 1; - 2; - 3; - }; -} -'''); - var innerPlan = planner!.removeNode(findNode.statement('2')); - var outerPlan = planner!.passThrough(findNode.variableDeclaration('x'), - innerPlans: [innerPlan]); - checkPlan(outerPlan, ''' -void f() { - var x = () { - 1; - 3; - }; -} -'''); - } - - Future test_remove_all_list_elements_with_trailing_separator() async { - await analyze('var x = [ 1, 2, ];'); - var i1 = findNode.integerLiteral('1'); - var i2 = findNode.integerLiteral('2'); - checkPlan( - planner!.passThrough(i1.parent, - innerPlans: [planner!.removeNode(i1), planner!.removeNode(i2)]), - 'var x = [];'); - } - - Future test_remove_argument() async { - await analyze('f(dynamic d) => d(1, 2, 3);'); - var i2 = findNode.integerLiteral('2'); - var changes = - checkPlan(planner!.removeNode(i2), 'f(dynamic d) => d(1, 3);'); - expect(changes.keys, [i2.offset]); - } - - Future test_remove_class_member() async { - await analyze(''' -class C { - int? x; - int? y; - int? z; -} -'''); - var declaration = findNode.fieldDeclaration('y'); - var changes = checkPlan(planner!.removeNode(declaration), ''' -class C { - int? x; - int? z; -} -'''); - expect(changes.keys, [declaration.offset - 2]); - } - - Future - test_remove_elements_of_related_lists_at_different_levels() async { - await analyze('var x = [[1, 2], 3, 4];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - checkPlan( - planner!.passThrough(testUnit, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]), - 'var x = [[1], 4];'); - } - - Future - test_remove_elements_of_sibling_lists_passThrough_container() async { - await analyze('var x = [[1, 2], [3, 4]];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - checkPlan( - planner!.passThrough(i2.parent!.parent, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]), - 'var x = [[1], [4]];'); - } - - Future test_remove_elements_of_sibling_lists_passThrough_unit() async { - await analyze('var x = [[1, 2], [3, 4]];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - checkPlan( - planner!.passThrough(testUnit, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]), - 'var x = [[1], [4]];'); - } - - Future test_remove_enum_constant() async { - await analyze(''' -enum E { - A, - B, - C -} -'''); - var enumConstant = findNode.enumConstantDeclaration('B'); - var changes = checkPlan(planner!.removeNode(enumConstant), ''' -enum E { - A, - C -} -'''); - expect(changes.keys, [enumConstant.offset - 2]); - } - - Future test_remove_field_declaration() async { - await analyze(''' -class C { - int? x, y, z; -} -'''); - var declaration = findNode.variableDeclaration('y'); - var changes = checkPlan(planner!.removeNode(declaration), ''' -class C { - int? x, z; -} -'''); - expect(changes.keys, [declaration.offset]); - } - - Future test_remove_list_element() async { - await analyze('var x = [1, 2, 3];'); - var i2 = findNode.integerLiteral('2'); - var changes = checkPlan(planner!.removeNode(i2), 'var x = [1, 3];'); - expect(changes.keys, [i2.offset]); - } - - Future test_remove_list_element_at_list_end() async { - await analyze('var x = [1, 2, 3];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - var changes = checkPlan(planner!.removeNode(i3), 'var x = [1, 2];'); - expect(changes.keys, [i2.end]); - } - - Future test_remove_list_element_singleton() async { - await analyze('var x = [1];'); - var i1 = findNode.integerLiteral('1'); - checkPlan(planner!.removeNode(i1), 'var x = [];'); - } - - Future test_remove_list_element_with_trailing_separator() async { - await analyze('var x = [1, 2, 3, ];'); - var i3 = findNode.integerLiteral('3'); - checkPlan(planner!.removeNode(i3), 'var x = [1, 2, ];'); - } - - Future test_remove_list_elements() async { - await analyze('var x = [1, 2, 3, 4, 5];'); - var i2 = findNode.integerLiteral('2'); - var i4 = findNode.integerLiteral('4'); - var changes = checkPlan( - planner!.passThrough(i2.parent, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i4)]), - 'var x = [1, 3, 5];'); - expect(changes.keys, unorderedEquals([i2.offset, i4.offset])); - } - - Future test_remove_list_elements_all() async { - await analyze('var x = [1, 2];'); - var i1 = findNode.integerLiteral('1'); - var i2 = findNode.integerLiteral('2'); - checkPlan( - planner!.passThrough(i1.parent, - innerPlans: [planner!.removeNode(i1), planner!.removeNode(i2)]), - 'var x = [];'); - } - - Future test_remove_list_elements_all_asUnit() async { - await analyze('var x = [1, 2];'); - var i1 = findNode.integerLiteral('1'); - var i2 = findNode.integerLiteral('2'); - checkPlan(planner!.removeNodes(i1, i2), 'var x = [];'); - } - - Future test_remove_list_elements_all_passThrough_unit() async { - await analyze('var x = [1, 2];'); - var i1 = findNode.integerLiteral('1'); - var i2 = findNode.integerLiteral('2'); - checkPlan( - planner!.passThrough(testUnit, - innerPlans: [planner!.removeNode(i1), planner!.removeNode(i2)]), - 'var x = [];'); - } - - Future test_remove_list_elements_at_list_end() async { - await analyze('var x = [1, 2, 3];'); - var i1 = findNode.integerLiteral('1'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - var changes = checkPlan( - planner!.passThrough(i2.parent, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]), - 'var x = [1];'); - expect(changes.keys, unorderedEquals([i1.end, i2.end])); - } - - Future test_remove_list_elements_at_list_end_asUnit() async { - await analyze('var x = [1, 2, 3];'); - var i1 = findNode.integerLiteral('1'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - var changes = checkPlan(planner!.removeNodes(i2, i3), 'var x = [1];'); - expect(changes.keys, [i1.end]); - } - - Future test_remove_list_elements_consecutive_asUnit() async { - await analyze('var x = [1, 2, 3, 4];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - var changes = checkPlan(planner!.removeNodes(i2, i3), 'var x = [1, 4];'); - expect(changes.keys, [i2.offset]); - } - - Future - test_remove_list_elements_consecutive_at_list_end_using_comments() async { - await analyze('var x = [1, 2, 3];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - createPlanner(removeViaComments: true); - checkPlan( - planner!.passThrough(i2.parent, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]), - 'var x = [1/* , 2 *//* , 3 */];'); - } - - Future test_remove_list_elements_consecutive_using_comments() async { - await analyze('var x = [1, 2, 3, 4];'); - var i2 = findNode.integerLiteral('2'); - var i3 = findNode.integerLiteral('3'); - createPlanner(removeViaComments: true); - checkPlan( - planner!.passThrough(i2.parent, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i3)]), - 'var x = [1, /* 2, */ /* 3, */ 4];'); - } - - Future test_remove_list_elements_using_comments() async { - await analyze('var x = [1, 2, 3, 4, 5];'); - var i2 = findNode.integerLiteral('2'); - var i4 = findNode.integerLiteral('4'); - createPlanner(removeViaComments: true); - checkPlan( - planner!.passThrough(i2.parent, - innerPlans: [planner!.removeNode(i2), planner!.removeNode(i4)]), - 'var x = [1, /* 2, */ 3, /* 4, */ 5];'); - } - - Future test_remove_map_element() async { - await analyze('var x = {1: 2, 3: 4, 5: 6};'); - var entry = findNode.integerLiteral('3').parent!; - var changes = - checkPlan(planner!.removeNode(entry), 'var x = {1: 2, 5: 6};'); - expect(changes.keys, [entry.offset]); - } - - Future - test_remove_metadata_from_defaultFormalParameter_all_full_line() async { - await analyze(''' -f({ - @deprecated - int? x}) {}'''); - var deprecated = findNode.annotation('@deprecated'); - checkPlan( - planner!.passThrough(deprecated.parent, - innerPlans: [planner!.removeNode(deprecated)]), - ''' -f({ - int? x}) {}'''); - } - - Future test_remove_metadata_from_method_declaration() async { - await analyze(''' -class C { - @deprecated - f() {} -} -'''); - var deprecated = findNode.annotation('@deprecated'); - checkPlan( - planner!.passThrough(deprecated.parent, - innerPlans: [planner!.removeNode(deprecated)]), - ''' -class C { - f() {} -} -'''); - } - - Future test_remove_parameter() async { - await analyze('f(int x, int y, int z) => null;'); - var parameter = findNode.simpleFormalParameter('y'); - var changes = - checkPlan(planner!.removeNode(parameter), 'f(int x, int z) => null;'); - expect(changes.keys, [parameter.offset]); - } - - Future test_remove_set_element() async { - await analyze('var x = {1, 2, 3};'); - var i2 = findNode.integerLiteral('2'); - var changes = checkPlan(planner!.removeNode(i2), 'var x = {1, 3};'); - expect(changes.keys, [i2.offset]); - } - - Future test_remove_statement() async { - await analyze(''' -void f() { - 1; - 2; - 3; -} -'''); - checkPlan(planner!.removeNode(findNode.statement('2')), ''' -void f() { - 1; - 3; -} -'''); - } - - Future test_remove_statement_first_of_many_on_line() async { - await analyze(''' -void f() { - 1; - 2; 3; - 4; -} -'''); - checkPlan(planner!.removeNode(findNode.statement('2')), ''' -void f() { - 1; - 3; - 4; -} -'''); - } - - Future test_remove_statement_last_of_many_on_line() async { - await analyze(''' -void f() { - 1; - 2; 3; - 4; -} -'''); - checkPlan(planner!.removeNode(findNode.statement('3')), ''' -void f() { - 1; - 2; - 4; -} -'''); - } - - Future test_remove_statement_middle_of_many_on_line() async { - await analyze(''' -void f() { - 1; - 2; 3; 4; - 5; -} -'''); - checkPlan(planner!.removeNode(findNode.statement('3')), ''' -void f() { - 1; - 2; 4; - 5; -} -'''); - } - - Future test_remove_statement_using_comments() async { - await analyze(''' -void f() { - 1; - 2; - 3; -} -'''); - createPlanner(removeViaComments: true); - checkPlan(planner!.removeNode(findNode.statement('2')), ''' -void f() { - 1; - /* 2; */ - 3; -} -'''); - } - - Future test_remove_statements_asUnit() async { - await analyze(''' -void f() { - 1; - 2; - - 3; - 4; -} -'''); - var s2 = findNode.statement('2'); - var s3 = findNode.statement('3'); - var changes = checkPlan(planner!.removeNodes(s2, s3), ''' -void f() { - 1; - 4; -} -'''); - expect(changes, hasLength(1)); - } - - Future test_remove_statements_consecutive_three() async { - await analyze(''' -void f() { - 1; - 2; - - 3; - - 4; - 5; -} -'''); - var s2 = findNode.statement('2'); - var s3 = findNode.statement('3'); - var s4 = findNode.statement('4'); - var changes = checkPlan( - planner!.passThrough(s2.parent, innerPlans: [ - planner!.removeNode(s2), - planner!.removeNode(s3), - planner!.removeNode(s4) - ]), - ''' -void f() { - 1; - 5; -} -'''); - expect(changes.keys, - unorderedEquals([s2.offset - 2, s3.offset - 2, s4.offset - 2])); - } - - Future test_remove_statements_consecutive_two() async { - await analyze(''' -void f() { - 1; - 2; - - 3; - 4; -} -'''); - var s2 = findNode.statement('2'); - var s3 = findNode.statement('3'); - var changes = checkPlan( - planner!.passThrough(s2.parent, - innerPlans: [planner!.removeNode(s2), planner!.removeNode(s3)]), - ''' -void f() { - 1; - 4; -} -'''); - expect(changes.keys, unorderedEquals([s2.offset - 2, s3.offset - 2])); - } - - Future test_remove_statements_nonconsecutive() async { - await analyze(''' -void f() { - 1; - 2; - 3; - 4; - 5; -} -'''); - var s2 = findNode.statement('2'); - var s4 = findNode.statement('4'); - var changes = checkPlan( - planner!.passThrough(s2.parent, - innerPlans: [planner!.removeNode(s2), planner!.removeNode(s4)]), - ''' -void f() { - 1; - 3; - 5; -} -'''); - expect(changes, hasLength(2)); - } - - Future test_remove_statements_singleton() async { - await analyze(''' -void f() { - 1; -} -'''); - checkPlan(planner!.removeNode(findNode.statement('1')), ''' -void f() {} -'''); - } - - Future test_remove_statements_singleton_with_following_comment() async { - await analyze(''' -void f() { - 1; - // Foo -} -'''); - checkPlan(planner!.removeNode(findNode.statement('1')), ''' -void f() { - // Foo -} -'''); - } - - Future test_remove_statements_singleton_with_preceding_comment() async { - await analyze(''' -void f() { - // Foo - 1; -} -'''); - checkPlan(planner!.removeNode(findNode.statement('1')), ''' -void f() { - // Foo -} -'''); - } - - Future test_remove_statements_using_comments() async { - await analyze(''' -void f() { - 1; - 2; - 3; - 4; -} -'''); - createPlanner(removeViaComments: true); - var s2 = findNode.statement('2'); - var s3 = findNode.statement('3'); - checkPlan( - planner!.passThrough(s2.parent, - innerPlans: [planner!.removeNode(s2), planner!.removeNode(s3)]), - ''' -void f() { - 1; - /* 2; */ - /* 3; */ - 4; -} -'''); - } - - Future test_remove_top_level_declaration() async { - await analyze(''' -class C {} -class D {} -class E {} -'''); - var declaration = findNode.classDeclaration('D'); - var changes = checkPlan(planner!.removeNode(declaration), ''' -class C {} -class E {} -'''); - expect(changes.keys, [declaration.offset]); - } - - Future test_remove_top_level_directive() async { - await analyze(''' -import 'dart:io'; -import 'dart:async'; -import 'dart:math'; -'''); - var directive = findNode.import('async'); - var changes = checkPlan(planner!.removeNode(directive), ''' -import 'dart:io'; -import 'dart:math'; -'''); - expect(changes.keys, [directive.offset]); - } - - Future test_remove_type_argument() async { - await analyze(''' -class C {} -C? c; -'''); - var typeArgument = findNode.namedType('double'); - var changes = checkPlan(planner!.removeNode(typeArgument), ''' -class C {} -C? c; -'''); - expect(changes.keys, [typeArgument.offset]); - } - - Future test_remove_type_parameter() async { - await analyze('class C {}'); - var parameter = findNode.typeParameter('U'); - var changes = checkPlan(planner!.removeNode(parameter), 'class C {}'); - expect(changes.keys, [parameter.offset]); - } - - Future test_remove_variable_declaration() async { - await analyze('int? x, y, z;'); - var declaration = findNode.variableDeclaration('y'); - var changes = checkPlan(planner!.removeNode(declaration), 'int? x, z;'); - expect(changes.keys, [declaration.offset]); - } - - Future - test_removeNullAwarenessFromMethodInvocation_change_arguments() async { - await analyze('f(x) => x?.m(0);'); - var methodInvocation = findNode.methodInvocation('?.'); - checkPlan( - planner!.passThrough(methodInvocation, innerPlans: [ - planner!.removeNullAwareness(methodInvocation), - planner!.passThrough(methodInvocation.argumentList, innerPlans: [ - planner! - .replace(findNode.integerLiteral('0'), [AtomicEdit.insert('1')]) - ]) - ]), - 'f(x) => x.m(1);'); - } - - Future - test_removeNullAwarenessFromMethodInvocation_change_methodName() async { - await analyze('f(x) => x?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - checkPlan( - planner!.passThrough(methodInvocation, innerPlans: [ - planner!.removeNullAwareness(methodInvocation), - planner!.replace(findNode.simple('m'), [AtomicEdit.insert('n')]) - ]), - 'f(x) => x.n();'); - } - - Future - test_removeNullAwarenessFromMethodInvocation_change_target() async { - await analyze('f(x) => x?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - checkPlan( - planner!.passThrough(methodInvocation, innerPlans: [ - planner!.replace(findNode.simple('x?.'), [AtomicEdit.insert('y')]), - planner!.removeNullAwareness(methodInvocation) - ]), - 'f(x) => y.m();'); - } - - Future - test_removeNullAwarenessFromMethodInvocation_change_typeArguments() async { - await analyze('f(x) => x?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - checkPlan( - planner!.passThrough(methodInvocation, innerPlans: [ - planner!.removeNullAwareness(methodInvocation), - planner!.passThrough(methodInvocation.typeArguments, innerPlans: [ - planner! - .replace(findNode.namedType('int'), [AtomicEdit.insert('num')]) - ]) - ]), - 'f(x) => x.m();'); - } - - Future test_removeNullAwarenessFromMethodInvocation_simple() async { - await analyze('f(x) => x?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - checkPlan( - planner!.passThrough(methodInvocation, - innerPlans: [planner!.removeNullAwareness(methodInvocation)]), - 'f(x) => x.m();'); - } - - Future test_removeNullAwarenessFromPropertyAccess_change_both() async { - await analyze('f(x) => x?.y;'); - var propertyAccess = findNode.propertyAccess('?.'); - checkPlan( - planner!.passThrough(propertyAccess, innerPlans: [ - planner!.replace(findNode.simple('x?.'), [AtomicEdit.insert('z')]), - planner!.removeNullAwareness(propertyAccess), - planner!.replace(findNode.simple('y'), [AtomicEdit.insert('w')]) - ]), - 'f(x) => z.w;'); - } - - Future - test_removeNullAwarenessFromPropertyAccess_change_propertyName() async { - await analyze('f(x) => x?.y;'); - var propertyAccess = findNode.propertyAccess('?.'); - checkPlan( - planner!.passThrough(propertyAccess, innerPlans: [ - planner!.removeNullAwareness(propertyAccess), - planner!.replace(findNode.simple('y'), [AtomicEdit.insert('w')]) - ]), - 'f(x) => x.w;'); - } - - Future - test_removeNullAwarenessFromPropertyAccess_change_target() async { - await analyze('f(x) => x?.y;'); - var propertyAccess = findNode.propertyAccess('?.'); - checkPlan( - planner!.passThrough(propertyAccess, innerPlans: [ - planner!.replace(findNode.simple('x?.'), [AtomicEdit.insert('z')]), - planner!.removeNullAwareness(propertyAccess) - ]), - 'f(x) => z.y;'); - } - - Future test_removeNullAwarenessFromPropertyAccess_simple() async { - await analyze('f(x) => x?.y;'); - var propertyAccess = findNode.propertyAccess('?.'); - checkPlan( - planner!.passThrough(propertyAccess, - innerPlans: [planner!.removeNullAwareness(propertyAccess)]), - 'f(x) => x.y;'); - } - - Future test_replace_expression() async { - await analyze('var x = 1 + 2 * 3;'); - checkPlan(planner!.replace(findNode.binary('*'), [AtomicEdit.insert('6')]), - 'var x = 1 + 6;'); - } - - Future test_replace_expression_add_parens_due_to_cascade() async { - await analyze('var x = 1 + 2 * 3;'); - checkPlan( - planner!.replace(findNode.binary('*'), [AtomicEdit.insert('4..isEven')], - endsInCascade: true), - 'var x = 1 + (4..isEven);'); - } - - Future test_replace_expression_add_parens_due_to_precedence() async { - await analyze('var x = 1 + 2 * 3;'); - checkPlan( - planner!.replace(findNode.binary('*'), [AtomicEdit.insert('y = z')], - precedence: Precedence.assignment), - 'var x = 1 + (y = z);'); - } - - Future test_replaceToken() async { - await analyze('var x = 1;'); - var variableDeclarationList = findNode.variableDeclarationList('var x'); - checkPlan( - planner!.replaceToken( - variableDeclarationList, variableDeclarationList.keyword!, 'int'), - 'int x = 1;'); - } - - Future test_surround_allowCascade() async { - await analyze('f(x) => 1..isEven;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.cascade('..')), - prefix: [AtomicEdit.insert('x..y = ')]), - 'f(x) => x..y = (1..isEven);'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.cascade('..')), - prefix: [AtomicEdit.insert('x = ')], allowCascade: true), - 'f(x) => x = 1..isEven;'); - } - - Future test_surround_associative() async { - await analyze('var x = 1 - 2;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.binary('-')), - suffix: [AtomicEdit.insert(' - 3')], - innerPrecedence: Precedence.additive, - associative: true), - 'var x = 1 - 2 - 3;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.binary('-')), - prefix: [AtomicEdit.insert('0 - ')], - innerPrecedence: Precedence.additive), - 'var x = 0 - (1 - 2);'); - } - - Future test_surround_endsInCascade() async { - await analyze('f(x) => x..y = 1;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert(' + 2')]), - 'f(x) => x..y = 1 + 2;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert('..isEven')], endsInCascade: true), - 'f(x) => x..y = (1..isEven);'); - } - - Future - test_surround_endsInCascade_does_not_propagate_through_added_parens() async { - await analyze('f(a) => a..b = 0;'); - checkPlan( - planner!.surround( - planner!.surround(planner!.passThrough(findNode.cascade('..')), - prefix: [AtomicEdit.insert('1 + ')], - innerPrecedence: Precedence.additive), - prefix: [AtomicEdit.insert('true ? ')], - suffix: [AtomicEdit.insert(' : 2')]), - 'f(a) => true ? 1 + (a..b = 0) : 2;'); - checkPlan( - planner!.surround( - planner!.surround(planner!.passThrough(findNode.cascade('..')), - prefix: [AtomicEdit.insert('throw ')], allowCascade: true), - prefix: [AtomicEdit.insert('true ? ')], - suffix: [AtomicEdit.insert(' : 2')]), - 'f(a) => true ? (throw a..b = 0) : 2;'); - } - - Future test_surround_endsInCascade_internal_throw() async { - await analyze('f(x, g) => g(0, throw x, 1);'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.simple('x, 1')), - suffix: [AtomicEdit.insert('..y')], endsInCascade: true), - 'f(x, g) => g(0, throw x..y, 1);'); - } - - Future test_surround_endsInCascade_propagates() async { - await analyze('f(a) => a..b = 0;'); - checkPlan( - planner!.surround( - planner!.surround(planner!.passThrough(findNode.cascade('..')), - prefix: [AtomicEdit.insert('throw ')], allowCascade: true), - prefix: [AtomicEdit.insert('true ? ')], - suffix: [AtomicEdit.insert(' : 2')]), - 'f(a) => true ? (throw a..b = 0) : 2;'); - checkPlan( - planner!.surround( - planner!.surround( - planner!.passThrough(findNode.integerLiteral('0')), - prefix: [AtomicEdit.insert('throw ')], - allowCascade: true), - prefix: [AtomicEdit.insert('true ? ')], - suffix: [AtomicEdit.insert(' : 2')]), - 'f(a) => a..b = true ? throw 0 : 2;'); - } - - Future test_surround_precedence() async { - await analyze('var x = 1 == true;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert(' < 2')], - outerPrecedence: Precedence.relational), - 'var x = 1 < 2 == true;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert(' == 2')], - outerPrecedence: Precedence.equality), - 'var x = (1 == 2) == true;'); - } - - Future test_surround_prefix() async { - await analyze('var x = 1;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - prefix: [AtomicEdit.insert('throw ')]), - 'var x = throw 1;'); - } - - Future test_surround_suffix() async { - await analyze('var x = 1;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert('..isEven')]), - 'var x = 1..isEven;'); - } - - Future test_surround_suffix_parenthesized() async { - await analyze('var x = (1);'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert('..isEven')]), - 'var x = 1..isEven;'); - } - - Future test_surround_suffix_parenthesized_passThrough_unit() async { - await analyze('var x = (1);'); - checkPlan( - planner!.passThrough(testUnit, innerPlans: [ - planner!.surround(planner!.passThrough(findNode.integerLiteral('1')), - suffix: [AtomicEdit.insert('..isEven')]) - ]), - 'var x = 1..isEven;'); - } - - Future test_surround_threshold() async { - await analyze('var x = 1 < 2;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.binary('<')), - suffix: [AtomicEdit.insert(' == true')], - innerPrecedence: Precedence.equality), - 'var x = 1 < 2 == true;'); - checkPlan( - planner!.surround(planner!.passThrough(findNode.binary('<')), - suffix: [AtomicEdit.insert(' as bool')], - innerPrecedence: Precedence.relational), - 'var x = (1 < 2) as bool;'); - } -} - -@reflectiveTest -class EndsInCascadeTest extends AbstractSingleUnitTest { - Future test_ignore_subexpression_not_at_end() async { - await resolveTestUnit('f(g) => g(0..isEven, 1);'); - expect(findNode.functionExpressionInvocation('g(').endsInCascade, false); - expect(findNode.cascade('..').endsInCascade, true); - } - - Future test_no_cascade() async { - await resolveTestUnit('var x = 0;'); - expect(findNode.integerLiteral('0').endsInCascade, false); - } - - Future test_stop_searching_when_parens_encountered() async { - await resolveTestUnit('f(x) => x = (x = 0..isEven);'); - expect(findNode.assignment('= (x').endsInCascade, false); - expect(findNode.parenthesized('(x =').endsInCascade, false); - expect(findNode.assignment('= 0').endsInCascade, true); - expect(findNode.cascade('..').endsInCascade, true); - } -} - -/// Tests of the precedence logic underlying [EditPlan]. -/// -/// The way these tests operate is as follows: we have several short snippets of -/// Dart code exercising Dart syntax with no unnecessary parentheses. We -/// recursively visit the AST of each snippet and use [EditPlanner.passThrough] -/// to create an edit plan based on each AST node. Then we use -/// [EditPlanner.parensNeededFromContext] to check whether parentheses are -/// needed around each node, and assert that the result agrees with the set of -/// parentheses that are actually present. -@reflectiveTest -class PrecedenceTest extends AbstractSingleUnitTest { - Future checkPrecedence(String content) async { - await resolveTestUnit(content); - testUnit!.accept(_PrecedenceChecker(testUnit!.lineInfo, testCode)); - } - - Future test_precedence_as() async { - await checkPrecedence(''' -f(a) => (a as num) as int; -g(a, b) => a | b as int; -'''); - } - - Future test_precedence_assignment() async { - await checkPrecedence('f(a, b, c) => a = b = c;'); - } - - Future test_precedence_assignment_in_cascade_with_parens() async { - await checkPrecedence('f(a, c, e) => a..b = (c..d = e);'); - } - - Future test_precedence_await() async { - await checkPrecedence(''' -f(a) async => await -a; -g(a, b) async => await (a*b); - '''); - } - - Future test_precedence_binary_equality() async { - await checkPrecedence(''' -f(a, b, c) => (a == b) == c; -g(a, b, c) => a == (b == c); -'''); - } - - Future test_precedence_binary_left_associative() async { - // Associativity logic is the same for all operators except relational and - // equality, so we just test `+` as a stand-in for all the others. - await checkPrecedence(''' -f(a, b, c) => a + b + c; -g(a, b, c) => a + (b + c); -'''); - } - - Future test_precedence_binary_relational() async { - await checkPrecedence(''' -f(a, b, c) => (a < b) < c; -g(a, b, c) => a < (b < c); -'''); - } - - Future test_precedence_conditional() async { - await checkPrecedence(''' -g(a, b, c, d, e, f) => a ?? b ? c = d : e = f; -h(a, b, c, d, e) => (a ? b : c) ? d : e; -'''); - } - - Future test_precedence_extension_override() async { - await checkPrecedence(''' -extension E on Object { - void f() {} -} -void g(x) => E(x).f(); -'''); - } - - Future test_precedence_functionExpression_ifNotNull() async { - await checkPrecedence('f(b, c) => ((a) => b) ?? c;'); - } - - Future test_precedence_functionExpressionInvocation() async { - await checkPrecedence(''' -f(g) => g[0](1); -h(x) => (x + 2)(3); -'''); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/40536') - Future test_precedence_ifNotNull_functionExpression() async { - await checkPrecedence('f(a, c) => a ?? (b) => c;'); - } - - Future test_precedence_is() async { - await checkPrecedence(''' -f(a) => (a as num) is int; -g(a, b) => a | b is int; -'''); - } - - Future test_precedence_postfix_and_index() async { - await checkPrecedence(''' -f(a, b, c) => a[b][c]; -g(a, b) => a[b]++; -h(a, b) => (-a)[b]; -'''); - } - - Future test_precedence_prefix() async { - await checkPrecedence(''' -f(a) => ~-a; -g(a, b) => -(a*b); -'''); - } - - Future test_precedence_prefixedIdentifier() async { - await checkPrecedence('f(a) => a.b;'); - } - - Future test_precedence_propertyAccess() async { - await checkPrecedence(''' -f(a) => a?.b?.c; -g(a) => (-a)?.b; -'''); - } - - Future test_precedence_throw() async { - await checkPrecedence(''' -f(a, b) => throw a = b; -g(a, c) => a..b = throw (c..d); -'''); - } - - Future test_precedenceChecker_detects_unnecessary_paren() async { - await resolveTestUnit('var x = (1);'); - expect( - () => - testUnit!.accept(_PrecedenceChecker(testUnit!.lineInfo, testCode)), - throwsA(TypeMatcher())); - } -} - -class _MockInfo implements AtomicEditInfo { - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} - -class _PrecedenceChecker extends UnifyingAstVisitor { - final EditPlanner planner; - - _PrecedenceChecker(LineInfo? lineInfo, String? sourceText) - : planner = EditPlanner(lineInfo, sourceText); - - @override - void visitNode(AstNode node) { - expect(planner.passThrough(node).parensNeededFromContext(null), false); - node.visitChildren(this); - } - - @override - void visitParenthesizedExpression(ParenthesizedExpression node) { - expect(planner.passThrough(node).parensNeededFromContext(null), true); - node.expression.visitChildren(this); - } -} diff --git a/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart b/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart deleted file mode 100644 index 37bb09ba4a6a..000000000000 --- a/pkg/nnbd_migration/test/edit_planner_pass_through_merging_test.dart +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'abstract_single_unit.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(PassThroughMergingTest); - }); -} - -/// Tests to make sure the algorithm used by [EditPlaner.passThrough] is -/// correct. -@reflectiveTest -class PassThroughMergingTest extends AbstractSingleUnitTest { - Future test_creates_pass_through_plans_stepwise() async { - await resolveTestUnit('var x = [[[1]]];'); - var plan = _EditPlannerForTesting(testCode!).passThrough( - findNode.listLiteral('[[['), - innerPlans: [_MockPlan(findNode.integerLiteral('1'))]); - expect(plan.toString(), - 'plan([[[1]]], [plan([[1]], [plan([1], [plan(1)])])])'); - } - - Future test_merge_plans_at_lower_level() async { - await resolveTestUnit('var x = [[1, 2]];'); - var plan = _EditPlannerForTesting(testCode!) - .passThrough(findNode.listLiteral('[['), innerPlans: [ - _MockPlan(findNode.integerLiteral('1')), - _MockPlan(findNode.integerLiteral('2')) - ]); - expect( - plan.toString(), 'plan([[1, 2]], [plan([1, 2], [plan(1), plan(2)])])'); - } - - Future test_merge_plans_at_top_level() async { - await resolveTestUnit('var x = [[1], [2]];'); - var plan = _EditPlannerForTesting(testCode!) - .passThrough(findNode.listLiteral('[['), innerPlans: [ - _MockPlan(findNode.integerLiteral('1')), - _MockPlan(findNode.integerLiteral('2')) - ]); - expect(plan.toString(), - 'plan([[1], [2]], [plan([1], [plan(1)]), plan([2], [plan(2)])])'); - } - - Future test_merge_plans_at_varying_levels() async { - await resolveTestUnit('var x = [1, [2, 3], 4];'); - var plan = _EditPlannerForTesting(testCode!) - .passThrough(findNode.listLiteral('[1'), innerPlans: [ - _MockPlan(findNode.integerLiteral('1')), - _MockPlan(findNode.integerLiteral('2')), - _MockPlan(findNode.integerLiteral('3')), - _MockPlan(findNode.integerLiteral('4')) - ]); - expect( - plan.toString(), - 'plan([1, [2, 3], 4], [plan(1), plan([2, 3], [plan(2), plan(3)]), ' - 'plan(4)])'); - } -} - -class _EditPlannerForTesting extends EditPlanner { - _EditPlannerForTesting(String content) - : super(LineInfo.fromContent(content), content); - - @override - PassThroughBuilder createPassThroughBuilder(AstNode? node) => - _MockPassThroughBuilder(node); -} - -class _MockPassThroughBuilder implements PassThroughBuilder { - final List _innerPlans = []; - - @override - final AstNode? node; - - _MockPassThroughBuilder(this.node); - - @override - void add(EditPlan innerPlan) { - _innerPlans.add(innerPlan); - } - - @override - NodeProducingEditPlan finish(EditPlanner planner) { - return _MockPlan(node, _innerPlans); - } -} - -class _MockPlan implements NodeProducingEditPlan { - final AstNode? _node; - - final List _innerPlans; - - _MockPlan(this._node, [List innerPlans = const []]) - : _innerPlans = innerPlans; - - @override - AstNode? get parentNode => _node!.parent; - - @override - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); - - @override - String toString() => - _innerPlans.isEmpty ? 'plan($_node)' : 'plan($_node, $_innerPlans)'; -} diff --git a/pkg/nnbd_migration/test/fix_aggregator_test.dart b/pkg/nnbd_migration/test/fix_aggregator_test.dart deleted file mode 100644 index 8c4279d6f5e5..000000000000 --- a/pkg/nnbd_migration/test/fix_aggregator_test.dart +++ /dev/null @@ -1,2149 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/type_provider.dart'; -import 'package:analyzer/src/generated/utilities_dart.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/fix_aggregator.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'abstract_single_unit.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(FixAggregatorTest); - }); -} - -@reflectiveTest -class FixAggregatorTest extends FixAggregatorTestBase { - TypeProviderImpl get nnbdTypeProvider => - (testAnalysisResult.typeProvider as TypeProviderImpl) - .asNonNullableByDefault; - - Future test_addImport_after_library() async { - await analyze(''' -library foo; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:collection/collection.dart', 'IterableExtension') - })!; - expect(previewInfo.applyTo(code!), ''' -library foo; - -import 'package:collection/collection.dart' show IterableExtension; - -main() {} -'''); - } - - Future test_addImport_after_library_before_other() async { - addPackageFile('fixnum', 'fixnum.dart', ''); - await analyze(''' -library foo; - -import 'package:fixnum/fixnum.dart'; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:collection/collection.dart', 'IterableExtension') - })!; - expect(previewInfo.applyTo(code!), ''' -library foo; - -import 'package:collection/collection.dart' show IterableExtension; -import 'package:fixnum/fixnum.dart'; - -main() {} -'''); - } - - Future test_addImport_atEnd_multiple() async { - addPackageFile('args', 'args.dart', ''); - await analyze(''' -import 'package:args/args.dart'; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:fixnum/fixnum.dart', 'Int32') - ..addImport('package:collection/collection.dart', 'IterableExtension') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:args/args.dart'; -import 'package:collection/collection.dart' show IterableExtension; -import 'package:fixnum/fixnum.dart' show Int32; - -main() {} -'''); - } - - Future test_addImport_atStart_multiple() async { - addPackageFile('fixnum', 'fixnum.dart', ''); - await analyze(''' -import 'package:fixnum/fixnum.dart'; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:collection/collection.dart', 'IterableExtension') - ..addImport('package:args/args.dart', 'ArgParser') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:args/args.dart' show ArgParser; -import 'package:collection/collection.dart' show IterableExtension; -import 'package:fixnum/fixnum.dart'; - -main() {} -'''); - } - - Future test_addImport_before_export() async { - await analyze(''' -export 'dart:async'; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:collection/collection.dart', 'IterableExtension') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:collection/collection.dart' show IterableExtension; -export 'dart:async'; - -main() {} -'''); - } - - Future test_addImport_no_previous_imports_multiple() async { - await analyze(''' -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('dart:async', 'Future') - ..addImport('dart:math', 'sin') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'dart:async' show Future; -import 'dart:math' show sin; - -main() {} -'''); - } - - Future test_addImport_recursive() async { - addPackageFile('args', 'args.dart', ''); - addPackageFile('fixnum', 'fixnum.dart', 'class Int32 {}'); - await analyze(''' -import 'package:args/args.dart'; -import 'package:fixnum/fixnum.dart' show Int32; - -main() => null; -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:collection/collection.dart', 'IterableExtension'), - findNode.import('package:fixnum').combinators[0]: - NodeChangeForShowCombinator()..addName('Int64'), - findNode.expression('null'): NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:args/args.dart'; -import 'package:collection/collection.dart' show IterableExtension; -import 'package:fixnum/fixnum.dart' show Int32, Int64; - -main() => null!; -'''); - } - - Future test_addImport_sort_shown_names() async { - await analyze(''' -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('dart:async', 'Stream') - ..addImport('dart:async', 'Future') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'dart:async' show Future, Stream; - -main() {} -'''); - } - - Future test_addImport_sorted() async { - addPackageFile('args', 'args.dart', ''); - addPackageFile('fixnum', 'fixnum.dart', ''); - await analyze(''' -import 'package:args/args.dart'; -import 'package:fixnum/fixnum.dart'; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:collection/collection.dart', 'IterableExtension') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:args/args.dart'; -import 'package:collection/collection.dart' show IterableExtension; -import 'package:fixnum/fixnum.dart'; - -main() {} -'''); - } - - Future test_addImport_sorted_multiple() async { - addPackageFile('collection', 'collection.dart', ''); - await analyze(''' -import 'package:collection/collection.dart'; - -main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..addImport('package:fixnum/fixnum.dart', 'Int32') - ..addImport('package:args/args.dart', 'ArgParser') - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:args/args.dart' show ArgParser; -import 'package:collection/collection.dart'; -import 'package:fixnum/fixnum.dart' show Int32; - -main() {} -'''); - } - - Future test_addRequired() async { - await analyze('f({int x}) => 0;'); - var previewInfo = run({ - findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter() - ..addRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), 'f({required int x}) => 0;'); - } - - Future test_addRequired_afterMetadata() async { - await analyze('f({@deprecated int x}) => 0;'); - var previewInfo = run({ - findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter() - ..addRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), 'f({@deprecated required int x}) => 0;'); - } - - Future test_addRequired_afterMetadata_andRequiredAnnotation() async { - addMetaPackage(); - var content = ''' -import 'package:meta/meta.dart'; -f({@required @deprecated int x}) {} -'''; - await analyze(content); - var annotation = findNode.annotation('required'); - var previewInfo = run({ - findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter() - ..addRequiredKeyword = true - ..annotationToRemove = annotation - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:meta/meta.dart'; -f({@deprecated required int x}) {} -'''); - expect(previewInfo.values, hasLength(2)); - - expect(previewInfo[content.indexOf('int ')], hasLength(1)); - expect(previewInfo[content.indexOf('int ')]!.single.isInsertion, true); - expect(previewInfo[content.indexOf('@required')], isNotNull); - expect(previewInfo[content.indexOf('@required')]!.single.isDeletion, true); - } - - Future test_addRequired_afterMetadata_beforeFinal() async { - await analyze('f({@deprecated final int x}) => 0;'); - var previewInfo = run({ - findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter() - ..addRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), - 'f({@deprecated required final int x}) => 0;'); - } - - Future test_addRequired_afterMetadata_beforeFunctionTyped() async { - await analyze('f({@deprecated int x()}) => 0;'); - var previewInfo = run({ - findNode.defaultParameter('int x'): NodeChangeForDefaultFormalParameter() - ..addRequiredKeyword = true - })!; - expect( - previewInfo.applyTo(code!), 'f({@deprecated required int x()}) => 0;'); - } - - Future test_addShownName_atEnd_multiple() async { - await analyze("import 'dart:math' show cos;"); - var previewInfo = run({ - findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator() - ..addName('tan') - ..addName('sin') - })!; - expect( - previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;"); - } - - Future test_addShownName_atStart_multiple() async { - await analyze("import 'dart:math' show tan;"); - var previewInfo = run({ - findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator() - ..addName('sin') - ..addName('cos') - })!; - expect( - previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;"); - } - - Future test_addShownName_sorted() async { - await analyze("import 'dart:math' show cos, tan;"); - var previewInfo = run({ - findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator() - ..addName('sin') - })!; - expect( - previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;"); - } - - Future test_addShownName_sorted_multiple() async { - await analyze("import 'dart:math' show sin;"); - var previewInfo = run({ - findNode.import('dart:math').combinators[0]: NodeChangeForShowCombinator() - ..addName('tan') - ..addName('cos') - })!; - expect( - previewInfo.applyTo(code!), "import 'dart:math' show cos, sin, tan;"); - } - - Future test_adjacentFixes() async { - await analyze('f(a, b) => a + b;'); - var aRef = findNode.simple('a +'); - var bRef = findNode.simple('b;'); - var previewInfo = run({ - aRef: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()), - bRef: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()), - findNode.binary('a + b'): NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a, b) => (a! + b!)!;'); - } - - Future test_argument_list_drop_all_arguments() async { - var content = ''' -f([int x, int y]) => null; -g(int x, int y) => f(x, y); -'''; - await analyze(content); - var previewInfo = run({ - findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList() - ..dropArgument(findNode.simple('y);'), null) - ..dropArgument(findNode.simple('x, y'), null) - })!; - expect(previewInfo.applyTo(code!), ''' -f([int x, int y]) => null; -g(int x, int y) => f(); -'''); - } - - Future test_argument_list_drop_one_argument() async { - var content = ''' -f([int x, int y]) => null; -g(int x, int y) => f(x, y); -'''; - await analyze(content); - var previewInfo = run({ - findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList() - ..dropArgument(findNode.simple('y);'), null) - })!; - expect(previewInfo.applyTo(code!), ''' -f([int x, int y]) => null; -g(int x, int y) => f(x); -'''); - } - - Future test_argument_list_recursive_changes() async { - var content = ''' -f([int x, int y]) => null; -g(int x, int y) => f(x, y); -'''; - await analyze(content); - var previewInfo = run({ - findNode.methodInvocation('f(x').argumentList: NodeChangeForArgumentList() - ..dropArgument(findNode.simple('y);'), null), - findNode.simple('x, y'): NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), ''' -f([int x, int y]) => null; -g(int x, int y) => f(x!); -'''); - } - - Future test_assignment_add_null_check() async { - var content = 'f(int x, int y) => x += y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('+='): NodeChangeForAssignment() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x, int y) => (x += y)!;'); - } - - Future test_assignment_change_lhs() async { - var content = 'f(List x, int y) => x[0] += y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('+='): NodeChangeForAssignment(), - findNode.index('[0]').target: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(List x, int y) => x![0] += y;'); - } - - Future test_assignment_change_rhs() async { - var content = 'f(int x, int y) => x += y;'; - await analyze(content); - var assignment = findNode.assignment('+='); - var previewInfo = run({ - assignment: NodeChangeForAssignment(), - assignment.rightHandSide: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x, int y) => x += y!;'); - } - - Future test_assignment_compound_with_bad_combined_type() async { - var content = 'f(int x, int y) => x += y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('+='): NodeChangeForAssignment() - ..hasBadCombinedType = true - })!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('+=')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.compoundAssignmentHasBadCombinedType); - expect(edit.isInformative, isTrue); - expect(edit.length, '+='.length); - } - - Future test_assignment_compound_with_nullable_source() async { - var content = 'f(int x, int y) => x += y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('+='): NodeChangeForAssignment() - ..hasNullableSource = true - })!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('+=')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.compoundAssignmentHasNullableSource); - expect(edit.isInformative, isTrue); - expect(edit.length, '+='.length); - } - - Future test_assignment_introduce_as() async { - var content = 'f(int x, int y) => x += y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('+='): NodeChangeForAssignment() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false), - null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x, int y) => (x += y) as int;'); - } - - Future test_assignment_weak_null_aware() async { - var content = 'f(int x, int y) => x ??= y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('??='): NodeChangeForAssignment() - ..isWeakNullAware = true - }, warnOnWeakCode: true)!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('??=')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.nullAwareAssignmentUnnecessaryInStrongMode); - expect(edit.isInformative, isTrue); - expect(edit.length, '??='.length); - } - - Future test_assignment_weak_null_aware_remove() async { - var content = 'f(int x, int y) => x ??= y;'; - await analyze(content); - var previewInfo = run({ - findNode.assignment('??='): NodeChangeForAssignment() - ..isWeakNullAware = true - }, warnOnWeakCode: false)!; - expect(previewInfo.applyTo(code!), 'f(int x, int y) => x;'); - } - - Future test_eliminateDeadIf_changesInKeptCode() async { - await analyze(''' -f(int i, int/*?*/ j) { - if (i != null) j.isEven; -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true, - findNode.simple('j.isEven'): NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), ''' -f(int i, int/*?*/ j) { - j!.isEven; -} -'''); - } - - Future test_eliminateDeadIf_changesInKeptCode_expandBlock() async { - await analyze(''' -f(int i, int/*?*/ j) { - if (i != null) { - j.isEven; - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true, - findNode.simple('j.isEven'): NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), ''' -f(int i, int/*?*/ j) { - j!.isEven; -} -'''); - } - - Future test_eliminateDeadIf_element_delete_drop_completely() async { - await analyze(''' -List f(int i) { - return [if (i == null) null]; -} -'''); - var previewInfo = run({ - findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false - })!; - expect(previewInfo.applyTo(code!), ''' -List f(int i) { - return []; -} -'''); - } - - Future - test_eliminateDeadIf_element_delete_drop_completely_not_in_sequence() async { - await analyze(''' -List f(int i) { - return [for (var x in [1, 2, 3]) if (i == null) null]; -} -'''); - var previewInfo = run({ - findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false - })!; - // This is a little kludgy; we could drop the `for` loop, but it's difficult - // to do so, and this is a rare enough corner case that it doesn't seem - // worth it. Replacing the `if` with `...{}` has the right effect, since - // it expands to nothing. - expect(previewInfo.applyTo(code!), ''' -List f(int i) { - return [for (var x in [1, 2, 3]) ...{}]; -} -'''); - } - - Future test_eliminateDeadIf_element_delete_keep_else() async { - await analyze(''' -List f(int i) { - return [if (i == null) null else i + 1]; -} -'''); - var previewInfo = run({ - findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = false - })!; - expect(previewInfo.applyTo(code!), ''' -List f(int i) { - return [i + 1]; -} -'''); - } - - Future test_eliminateDeadIf_element_delete_keep_then() async { - await analyze(''' -List f(int i) { - return [if (i == null) null else i + 1]; -} -'''); - var previewInfo = run({ - findNode.ifElement('=='): NodeChangeForIfElement()..conditionValue = true - })!; - expect(previewInfo.applyTo(code!), ''' -List f(int i) { - return [null]; -} -'''); - } - - Future test_eliminateDeadIf_expression_delete_keep_else() async { - await analyze(''' -int f(int i) { - return i == null ? null : i + 1; -} -'''); - var previewInfo = run({ - findNode.conditionalExpression('=='): NodeChangeForConditionalExpression() - ..conditionValue = false - })!; - expect(previewInfo.applyTo(code!), ''' -int f(int i) { - return i + 1; -} -'''); - } - - Future test_eliminateDeadIf_expression_delete_keep_then() async { - await analyze(''' -int f(int i) { - return i == null ? null : i + 1; -} -'''); - var previewInfo = run({ - findNode.conditionalExpression('=='): NodeChangeForConditionalExpression() - ..conditionValue = true - })!; - expect(previewInfo.applyTo(code!), ''' -int f(int i) { - return null; -} -'''); - } - - Future test_eliminateDeadIf_statement_comment_keep_else() async { - await analyze(''' -int f(int i) { - if (i == null) { - return null; - } else { - return i + 1; - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = false - }, removeViaComments: true)!; - expect(previewInfo.applyTo(code!), ''' -int f(int i) { - /* if (i == null) { - return null; - } else { - */ return i + 1; /* - } */ -} -'''); - } - - Future test_eliminateDeadIf_statement_comment_keep_then() async { - await analyze(''' -int f(int i) { - if (i == null) { - return null; - } else { - return i + 1; - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true - }, removeViaComments: true)!; - expect(previewInfo.applyTo(code!), ''' -int f(int i) { - /* if (i == null) { - */ return null; /* - } else { - return i + 1; - } */ -} -'''); - } - - Future - test_eliminateDeadIf_statement_delete_drop_completely_false() async { - await analyze(''' -void f(int i) { - if (i == null) { - print('null'); - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = false - })!; - expect(previewInfo.applyTo(code!), ''' -void f(int i) {} -'''); - } - - Future - test_eliminateDeadIf_statement_delete_drop_completely_not_in_block() async { - await analyze(''' -void f(int i) { - while (true) - if (i == null) { - print('null'); - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = false - })!; - // Note: formatting is a little weird here but it's such a rare case that - // we don't care. - expect(previewInfo.applyTo(code!), ''' -void f(int i) { - while (true) - {} -} -'''); - } - - Future - test_eliminateDeadIf_statement_delete_drop_completely_true() async { - await analyze(''' -void f(int i) { - if (i != null) {} else { - print('null'); - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true - })!; - expect(previewInfo.applyTo(code!), ''' -void f(int i) {} -'''); - } - - Future test_eliminateDeadIf_statement_delete_keep_else() async { - await analyze(''' -int f(int i) { - if (i == null) { - return null; - } else { - return i + 1; - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = false - })!; - expect(previewInfo.applyTo(code!), ''' -int f(int i) { - return i + 1; -} -'''); - } - - Future test_eliminateDeadIf_statement_delete_keep_then() async { - await analyze(''' -int f(int i) { - if (i != null) { - return i + 1; - } else { - return null; - } -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true - })!; - expect(previewInfo.applyTo(code!), ''' -int f(int i) { - return i + 1; -} -'''); - } - - Future - test_eliminateDeadIf_statement_delete_keep_then_declaration() async { - await analyze(''' -void f(int i, String callback()) { - if (i != null) { - var i = callback(); - } else { - return; - } - print(i); -} -'''); - // In this case we have to keep the block so that the scope of `var i` - // doesn't widen. - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true - })!; - expect(previewInfo.applyTo(code!), ''' -void f(int i, String callback()) { - { - var i = callback(); - } - print(i); -} -'''); - } - - Future test_introduceAs_distant_parens_no_longer_needed() async { - // Note: in principle it would be nice to delete the outer parens, but it's - // difficult to see that they used to be necessary and aren't anymore, so we - // leave them. - await analyze('f(a, c) => a..b = (throw c..d);'); - var cd = findNode.cascade('c..d'); - var previewInfo = run({ - cd: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false), - _MockInfo()) - })!; - expect( - previewInfo.applyTo(code!), 'f(a, c) => a..b = (throw (c..d) as int);'); - } - - Future test_introduceAs_dynamic() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.dynamicType, isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(Object o) => o as dynamic;'); - } - - Future test_introduceAs_favorPrefix() async { - await analyze(''' -import 'dart:async' as a; -import 'dart:async'; -f(Object o) => o; -'''); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.futureNullType, - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), ''' -import 'dart:async' as a; -import 'dart:async'; -f(Object o) => o as a.Future; -'''); - } - - Future test_introduceAs_functionType() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(Object o) => o as bool Function();'); - } - - Future test_introduceAs_functionType_formal_bound() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [ - TypeParameterElementImpl.synthetic('T') - ..bound = nnbdTypeProvider.numType - ], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function();'); - } - - Future test_introduceAs_functionType_formal_bound_dynamic() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [ - TypeParameterElementImpl.synthetic('T') - ..bound = nnbdTypeProvider.dynamicType - ], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect( - previewInfo.applyTo(code!), 'f(Object o) => o as bool Function();'); - } - - Future test_introduceAs_functionType_formal_bound_object() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [ - TypeParameterElementImpl.synthetic('T') - ..bound = nnbdTypeProvider.objectType - ], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function();'); - } - - Future - test_introduceAs_functionType_formal_bound_object_question() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [ - TypeParameterElementImpl.synthetic('T') - ..bound = (nnbdTypeProvider.objectType as TypeImpl) - .withNullability(NullabilitySuffix.question) - ], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect( - previewInfo.applyTo(code!), 'f(Object o) => o as bool Function();'); - } - - Future test_introduceAs_functionType_formal_bound_question() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [ - TypeParameterElementImpl.synthetic('T') - ..bound = (nnbdTypeProvider.numType as TypeImpl) - .withNullability(NullabilitySuffix.question) - ], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function();'); - } - - Future test_introduceAs_functionType_formals() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [ - TypeParameterElementImpl.synthetic('T'), - TypeParameterElementImpl.synthetic('U') - ], - parameters: [], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function();'); - } - - Future test_introduceAs_functionType_parameters() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [], - parameters: [ - ParameterElementImpl.synthetic('x', - nnbdTypeProvider.intType, ParameterKind.REQUIRED), - ParameterElementImpl.synthetic( - 'y', nnbdTypeProvider.numType, ParameterKind.REQUIRED) - ], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function(int, num);'); - } - - Future test_introduceAs_functionType_parameters_named() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [], - parameters: [ - ParameterElementImpl.synthetic( - 'x', nnbdTypeProvider.intType, ParameterKind.NAMED), - ParameterElementImpl.synthetic( - 'y', nnbdTypeProvider.numType, ParameterKind.NAMED) - ], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function({int x, num y});'); - } - - Future test_introduceAs_functionType_parameters_optional() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - FunctionTypeImpl( - returnType: nnbdTypeProvider.boolType, - typeFormals: [], - parameters: [ - ParameterElementImpl.synthetic('x', - nnbdTypeProvider.intType, ParameterKind.POSITIONAL), - ParameterElementImpl.synthetic('y', - nnbdTypeProvider.numType, ParameterKind.POSITIONAL) - ], - nullabilitySuffix: NullabilitySuffix.none), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), - 'f(Object o) => o as bool Function([int, num]);'); - } - - Future test_introduceAs_interfaceType_parameterized() async { - await analyze('f(Object o) => o;'); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange( - nnbdTypeProvider.mapType( - nnbdTypeProvider.intType, nnbdTypeProvider.boolType), - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(Object o) => o as Map;'); - } - - Future test_introduceAs_no_parens() async { - await analyze('f(a, b) => a | b;'); - var expr = findNode.binary('a | b'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a, b) => a | b as int;'); - } - - Future test_introduceAs_parens() async { - await analyze('f(a, b) => a < b;'); - var expr = findNode.binary('a < b'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.boolType, isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a, b) => (a < b) as bool;'); - } - - Future test_introduceAs_usePrefix() async { - await analyze(''' -import 'dart:async' as a; -f(Object o) => o; -'''); - var expr = findNode.simple('o;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.futureNullType, - isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), ''' -import 'dart:async' as a; -f(Object o) => o as a.Future; -'''); - } - - Future test_introduceAs_withNullCheck() async { - await analyze('f(x) => x;'); - var expr = findNode.simple('x;'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false), - _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x! as int;'); - } - - Future test_keep_redundant_parens() async { - await analyze('f(a, b, c) => a + (b * c);'); - var previewInfo = run({}); - expect(previewInfo, isEmpty); - } - - Future test_makeNullable() async { - await analyze('f(int x) {}'); - var typeName = findNode.namedType('int'); - var previewInfo = run({ - typeName: NodeChangeForTypeAnnotation() - ..recordNullability( - MockDecoratedType( - MockDartType(toStringValueWithoutNullability: 'int')), - true) - })!; - expect(previewInfo.applyTo(code!), 'f(int? x) {}'); - } - - Future test_methodName_change() async { - await analyze('f() => f();'); - var previewInfo = run({ - findNode.methodInvocation('f();').methodName: NodeChangeForMethodName() - ..replaceWith('g', null) - })!; - expect(previewInfo.applyTo(code!), 'f() => g();'); - } - - Future test_methodName_no_change() async { - await analyze('f() => f();'); - var previewInfo = run({ - findNode.methodInvocation('f();').methodName: NodeChangeForMethodName() - }); - expect(previewInfo, isNull); - } - - Future test_noChangeToTypeAnnotation() async { - await analyze('int x = 0;'); - var typeName = findNode.namedType('int'); - var previewInfo = run({ - typeName: NodeChangeForTypeAnnotation() - ..recordNullability( - MockDecoratedType( - MockDartType(toStringValueWithoutNullability: 'int')), - false) - })!; - expect(previewInfo.applyTo(code!), 'int x = 0;'); - expect(previewInfo.applyTo(code!, includeInformative: true), 'int x = 0;'); - expect(previewInfo.values.single.single.info!.description.appliedMessage, - "Type 'int' was not made nullable"); - } - - Future test_noInfoForTypeAnnotation() async { - await analyze('int x = 0;'); - var typeName = findNode.namedType('int'); - var previewInfo = run({typeName: NodeChangeForTypeAnnotation()}); - expect(previewInfo, null); - } - - Future test_noValidMigration() async { - await analyze('f(a) => null;'); - var literal = findNode.nullLiteral('null'); - var previewInfo = run({ - literal: NodeChangeForExpression() - ..addExpressionChange( - NoValidMigrationChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), code); - expect(previewInfo.applyTo(code!, includeInformative: true), - 'f(a) => null /* no valid migration */;'); - } - - Future test_nullCheck_index_cascadeResult() async { - await analyze('f(a) => a..[0].c;'); - var index = findNode.index('[0]'); - var previewInfo = run({ - index: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a) => a..[0]!.c;'); - } - - Future test_nullCheck_methodInvocation_cascadeResult() async { - await analyze('f(a) => a..b().c;'); - var method = findNode.methodInvocation('b()'); - var previewInfo = run({ - method: NodeChangeForMethodInvocation() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a) => a..b()!.c;'); - } - - Future test_nullCheck_no_parens() async { - await analyze('f(a) => a++;'); - var expr = findNode.postfix('a++'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a) => a++!;'); - } - - Future test_nullCheck_parens() async { - await analyze('f(a) => -a;'); - var expr = findNode.prefix('-a'); - var previewInfo = run({ - expr: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a) => (-a)!;'); - } - - Future test_nullCheck_propertyAccess_cascadeResult() async { - await analyze('f(a) => a..b.c;'); - var property = findNode.propertyAccess('b'); - var previewInfo = run({ - property: NodeChangeForPropertyAccess() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(a) => a..b!.c;'); - } - - Future test_parameter_addExplicitType_annotated() async { - await analyze('f({@deprecated x = 0}) {}'); - var previewInfo = run({ - findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'f({@deprecated int x = 0}) {}'); - } - - Future test_parameter_addExplicitType_declared_with_covariant() async { - await analyze(''' -class C { - m({num x}) {} -} -class D extends C { - m({covariant x = 3}) {} -} -'''); - var previewInfo = run({ - findNode.simpleParameter('x = 3'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), ''' -class C { - m({num x}) {} -} -class D extends C { - m({covariant int x = 3}) {} -} -'''); - } - - Future test_parameter_addExplicitType_declared_with_final() async { - await analyze('f({final x = 0}) {}'); - var previewInfo = run({ - findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'f({final int x = 0}) {}'); - } - - Future test_parameter_addExplicitType_declared_with_var() async { - await analyze('f({var x = 0}) {}'); - var previewInfo = run({ - findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'f({int x = 0}) {}'); - } - - Future test_parameter_addExplicitType_named() async { - await analyze('f({x = 0}) {}'); - var previewInfo = run({ - findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'f({int x = 0}) {}'); - } - - Future test_parameter_addExplicitType_no() async { - await analyze('f([x = 0]) {}'); - var previewInfo = run( - {findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter()}); - expect(previewInfo, isNull); - } - - Future test_parameter_addExplicitType_optional_insert() async { - await analyze('f([x = 0]) {}'); - var previewInfo = run({ - findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'f([int x = 0]) {}'); - } - - Future test_parameter_addExplicitType_prefixed_type() async { - await analyze(''' -import 'dart:core' as core; -f({x = 0}) {} -'''); - var previewInfo = run({ - findNode.simpleParameter('x'): NodeChangeForSimpleFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), ''' -import 'dart:core' as core; -f({core.int x = 0}) {} -'''); - } - - Future test_parameter_field_formal_addExplicitType() async { - await analyze(''' -class C { - int x; - C(this.x) {} -} -'''); - var previewInfo = run({ - findNode.fieldFormalParameter('this.x'): - NodeChangeForFieldFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), ''' -class C { - int x; - C(int this.x) {} -} -'''); - } - - Future - test_parameter_field_formal_addExplicitType_declared_with_final() async { - verifyNoTestUnitErrors = false; - await analyze(''' -class C { - int x; - C(final this.x) {} -} -'''); - var previewInfo = run({ - findNode.fieldFormalParameter('this.x'): - NodeChangeForFieldFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), ''' -class C { - int x; - C(final int this.x) {} -} -'''); - } - - Future - test_parameter_field_formal_addExplicitType_declared_with_var() async { - await analyze(''' -class C { - int x; - C(var this.x) {} -} -'''); - var previewInfo = run({ - findNode.fieldFormalParameter('this.x'): - NodeChangeForFieldFormalParameter() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), ''' -class C { - int x; - C(int this.x) {} -} -'''); - } - - Future test_parameter_field_formal_addExplicitType_no() async { - await analyze(''' -class C { - int x; - C(this.x) {} -} -'''); - var previewInfo = run({ - findNode.fieldFormalParameter('this.x'): - NodeChangeForFieldFormalParameter() - }); - expect(previewInfo, isNull); - } - - Future test_post_increment_add_null_check() async { - var content = 'f(int x) => x++;'; - await analyze(content); - var previewInfo = run({ - findNode.postfix('++'): NodeChangeForPostfixExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x) => x++!;'); - } - - Future test_post_increment_change_target() async { - var content = 'f(List x) => x[0]++;'; - await analyze(content); - var previewInfo = run({ - findNode.postfix('++'): NodeChangeForPostfixExpression(), - findNode.index('[0]').target: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(List x) => x![0]++;'); - } - - Future test_post_increment_introduce_as() async { - var content = 'f(int x) => x++;'; - await analyze(content); - var previewInfo = run({ - findNode.postfix('++'): NodeChangeForPostfixExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false), - null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x) => x++ as int;'); - } - - Future test_post_increment_with_bad_combined_type() async { - var content = 'f(int x) => x++;'; - await analyze(content); - var previewInfo = run({ - findNode.postfix('++'): NodeChangeForPostfixExpression() - ..hasBadCombinedType = true - })!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('++')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.compoundAssignmentHasBadCombinedType); - expect(edit.isInformative, isTrue); - expect(edit.length, '++'.length); - } - - Future test_post_increment_with_nullable_source() async { - var content = 'f(int x) => x++;'; - await analyze(content); - var previewInfo = run({ - findNode.postfix('++'): NodeChangeForPostfixExpression() - ..hasNullableSource = true - })!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('++')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.compoundAssignmentHasNullableSource); - expect(edit.isInformative, isTrue); - expect(edit.length, '++'.length); - } - - Future test_pre_increment_add_null_check() async { - var content = 'f(int x) => ++x;'; - await analyze(content); - var previewInfo = run({ - findNode.prefix('++'): NodeChangeForPrefixExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x) => (++x)!;'); - } - - Future test_pre_increment_change_target() async { - var content = 'f(List x) => ++x[0];'; - await analyze(content); - var previewInfo = run({ - findNode.prefix('++'): NodeChangeForPrefixExpression(), - findNode.index('[0]').target: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), null) - })!; - expect(previewInfo.applyTo(code!), 'f(List x) => ++x![0];'); - } - - Future test_pre_increment_introduce_as() async { - var content = 'f(int x) => ++x;'; - await analyze(content); - var previewInfo = run({ - findNode.prefix('++'): NodeChangeForPrefixExpression() - ..addExpressionChange( - IntroduceAsChange(nnbdTypeProvider.intType, isDowncast: false), - null) - })!; - expect(previewInfo.applyTo(code!), 'f(int x) => ++x as int;'); - } - - Future test_pre_increment_with_bad_combined_type() async { - var content = 'f(int x) => ++x;'; - await analyze(content); - var previewInfo = run({ - findNode.prefix('++'): NodeChangeForPrefixExpression() - ..hasBadCombinedType = true - })!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('++')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.compoundAssignmentHasBadCombinedType); - expect(edit.isInformative, isTrue); - expect(edit.length, '++'.length); - } - - Future test_pre_increment_with_nullable_source() async { - var content = 'f(int x) => ++x;'; - await analyze(content); - var previewInfo = run({ - findNode.prefix('++'): NodeChangeForPrefixExpression() - ..hasNullableSource = true - })!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('++')]!.single; - expect(edit.info!.description, - NullabilityFixDescription.compoundAssignmentHasNullableSource); - expect(edit.isInformative, isTrue); - expect(edit.length, '++'.length); - } - - Future - test_removeAs_in_cascade_target_no_parens_needed_cascade() async { - await analyze('f(a) => ((a..b) as dynamic)..c;'); - var cascade = findNode.cascade('a..b'); - var cast = cascade.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a) => a..b..c;'); - } - - Future - test_removeAs_in_cascade_target_no_parens_needed_conditional() async { - // TODO(paulberry): would it be better to keep the parens in this case for - // clarity, even though they're not needed? - await analyze('f(a, b, c) => ((a ? b : c) as dynamic)..d;'); - var conditional = findNode.conditionalExpression('a ? b : c'); - var cast = conditional.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, c) => a ? b : c..d;'); - } - - Future - test_removeAs_in_cascade_target_parens_needed_assignment() async { - await analyze('f(a, b) => ((a = b) as dynamic)..c;'); - var assignment = findNode.assignment('a = b'); - var cast = assignment.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b) => (a = b)..c;'); - } - - Future test_removeAs_in_cascade_target_parens_needed_throw() async { - await analyze('f(a) => ((throw a) as dynamic)..b;'); - var throw_ = findNode.throw_('throw a'); - var cast = throw_.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a) => (throw a)..b;'); - } - - Future - test_removeAs_lower_precedence_do_not_remove_inner_parens() async { - await analyze('f(a, b, c) => (a == b) as Null == c;'); - var expr = findNode.binary('a == b'); - var previewInfo = run( - {expr.parent!.parent: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, c) => (a == b) == c;'); - } - - Future test_removeAs_lower_precedence_remove_inner_parens() async { - await analyze('f(a, b) => (a == b) as Null;'); - var expr = findNode.binary('a == b'); - var previewInfo = run( - {expr.parent!.parent: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b) => a == b;'); - } - - Future test_removeAs_parens_needed_due_to_cascade() async { - // Note: parens are needed, and they could either be around `c..d` or around - // `throw c..d`. In an ideal world, we would see that we can just keep the - // parens we have, but this is difficult because we don't see that the - // parens are needed until we walk far enough up the AST to see that we're - // inside a cascade expression. So we drop the parens and then create new - // ones surrounding `throw c..d`. - // - // Strictly speaking the code we produce is correct, it's just making a - // slightly larger edit than necessary. This is presumably a really rare - // corner case so for now we're not worrying about it. - await analyze('f(a, c) => a..b = throw (c..d) as int;'); - var cd = findNode.cascade('c..d'); - var cast = cd.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, c) => a..b = (throw c..d);'); - } - - Future - test_removeAs_parens_needed_due_to_cascade_in_conditional_else() async { - await analyze('f(a, b, c) => a ? b : (c..d) as int;'); - var cd = findNode.cascade('c..d'); - var cast = cd.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, c) => a ? b : (c..d);'); - } - - Future - test_removeAs_parens_needed_due_to_cascade_in_conditional_then() async { - await analyze('f(a, b, d) => a ? (b..c) as int : d;'); - var bc = findNode.cascade('b..c'); - var cast = bc.parent!.parent; - var previewInfo = - run({cast: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, d) => a ? (b..c) : d;'); - } - - Future test_removeAs_raise_precedence_do_not_remove_parens() async { - await analyze('f(a, b, c) => a | (b | c as int);'); - var expr = findNode.binary('b | c'); - var previewInfo = - run({expr.parent: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, c) => a | (b | c);'); - } - - Future test_removeAs_raise_precedence_no_parens_to_remove() async { - await analyze('f(a, b, c) => a = b | c as int;'); - var expr = findNode.binary('b | c'); - var previewInfo = - run({expr.parent: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, c) => a = b | c;'); - } - - Future test_removeAs_raise_precedence_remove_parens() async { - await analyze('f(a, b, c) => a < (b | c as int);'); - var expr = findNode.binary('b | c'); - var previewInfo = - run({expr.parent: NodeChangeForAsExpression()..removeAs = true})!; - expect(previewInfo.applyTo(code!), 'f(a, b, c) => a < b | c;'); - } - - Future test_removeLanguageVersion() async { - await analyze(''' -// ignore: illegal_language_version_override -//@dart=2.6 -void main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..removeLanguageVersionComment = true - })!; - // TODO(mfairhurst): Remove beginning \n once it renders properly in preview - expect(previewInfo.applyTo(code!), ''' -// ignore: illegal_language_version_override - -void main() {} -'''); - } - - Future test_removeLanguageVersion_after_license() async { - await analyze(''' -// Some licensing stuff here... -// Some copyrighting stuff too... -// etc... -// ignore: illegal_language_version_override -// @dart = 2.6 -void main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..removeLanguageVersionComment = true - })!; - // TODO(mfairhurst): Remove beginning \n once it renders properly in preview - expect(previewInfo.applyTo(code!), ''' -// Some licensing stuff here... -// Some copyrighting stuff too... -// etc... -// ignore: illegal_language_version_override - -void main() {} -'''); - } - - Future test_removeLanguageVersion_spaces() async { - await analyze(''' -// ignore: illegal_language_version_override -// @dart = 2.6 -void main() {} -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..removeLanguageVersionComment = true - })!; - // TODO(mfairhurst): Remove beginning \n once it renders properly in preview - expect(previewInfo.applyTo(code!), ''' -// ignore: illegal_language_version_override - -void main() {} -'''); - } - - Future test_removeLanguageVersion_withOtherChanges() async { - await analyze(''' -// ignore: illegal_language_version_override -//@dart=2.6 -int f() => null; -'''); - var previewInfo = run({ - findNode.unit: NodeChangeForCompilationUnit() - ..removeLanguageVersionComment = true, - findNode.typeAnnotation('int'): NodeChangeForTypeAnnotation() - ..recordNullability( - MockDecoratedType( - MockDartType(toStringValueWithoutNullability: 'int')), - true) - })!; - // TODO(mfairhurst): Remove beginning \n once it renders properly in preview - expect(previewInfo.applyTo(code!), ''' -// ignore: illegal_language_version_override - -int? f() => null; -'''); - } - - Future test_removeNullAwarenessFromMethodInvocation() async { - await analyze('f(x) => x?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - var previewInfo = run({ - methodInvocation: NodeChangeForMethodInvocation() - ..nullAwarenessRemoval = NullAwarenessRemovalType.strong - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x.m();'); - } - - Future - test_removeNullAwarenessFromMethodInvocation_changeArgument() async { - await analyze('f(x) => x?.m(x);'); - var methodInvocation = findNode.methodInvocation('?.'); - var argument = findNode.simple('x);'); - var previewInfo = run({ - methodInvocation: NodeChangeForMethodInvocation() - ..nullAwarenessRemoval = NullAwarenessRemovalType.strong, - argument: NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x.m(x!);'); - } - - Future - test_removeNullAwarenessFromMethodInvocation_changeTarget() async { - await analyze('f(x) => (x as dynamic)?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - var cast = findNode.as_('as'); - var previewInfo = run({ - methodInvocation: NodeChangeForMethodInvocation() - ..nullAwarenessRemoval = NullAwarenessRemovalType.strong, - cast: NodeChangeForAsExpression()..removeAs = true - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x.m();'); - } - - Future - test_removeNullAwarenessFromMethodInvocation_changeTypeArgument() async { - await analyze('f(x) => x?.m();'); - var methodInvocation = findNode.methodInvocation('?.'); - var typeAnnotation = findNode.typeAnnotation('int'); - var previewInfo = run({ - methodInvocation: NodeChangeForMethodInvocation() - ..nullAwarenessRemoval = NullAwarenessRemovalType.strong, - typeAnnotation: NodeChangeForTypeAnnotation() - ..recordNullability( - MockDecoratedType( - MockDartType(toStringValueWithoutNullability: 'int')), - true) - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x.m();'); - } - - Future test_removeNullAwarenessFromPropertyAccess() async { - await analyze('f(x) => x?.y;'); - var propertyAccess = findNode.propertyAccess('?.'); - var previewInfo = run({ - propertyAccess: NodeChangeForPropertyAccess() - ..nullAwarenessRemoval = NullAwarenessRemovalType.strong - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x.y;'); - } - - Future test_removeNullAwarenessFromPropertyAccess_changeTarget() async { - await analyze('f(x) => (x as dynamic)?.y;'); - var propertyAccess = findNode.propertyAccess('?.'); - var cast = findNode.as_('as'); - var previewInfo = run({ - propertyAccess: NodeChangeForPropertyAccess() - ..nullAwarenessRemoval = NullAwarenessRemovalType.strong, - cast: NodeChangeForAsExpression()..removeAs = true - })!; - expect(previewInfo.applyTo(code!), 'f(x) => x.y;'); - } - - Future - test_requiredAnnotationToRequiredKeyword_leadingAnnotations() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -f({@deprecated @required int x}) {} -'''); - var annotation = findNode.annotation('required'); - var previewInfo = run({ - annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:meta/meta.dart'; -f({@deprecated required int x}) {} -'''); - expect(previewInfo.values.single.single.isDeletion, true); - } - - Future test_requiredAnnotationToRequiredKeyword_prefixed() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart' as meta; -f({@meta.required int x}) {} -'''); - var annotation = findNode.annotation('required'); - var previewInfo = run({ - annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:meta/meta.dart' as meta; -f({required int x}) {} -'''); - expect(previewInfo.values.single.single.isDeletion, true); - } - - Future test_requiredAnnotationToRequiredKeyword_renamed() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -const foo = required; -f({@foo int x}) {} -'''); - var annotation = findNode.annotation('@foo'); - var previewInfo = run({ - annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:meta/meta.dart'; -const foo = required; -f({required int x}) {} -'''); - } - - Future test_requiredAnnotationToRequiredKeyword_simple() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -f({@required int x}) {} -'''); - var annotation = findNode.annotation('required'); - var previewInfo = run({ - annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true - })!; - expect(previewInfo.applyTo(code!), ''' -import 'package:meta/meta.dart'; -f({required int x}) {} -'''); - expect(previewInfo.values.single.single.isDeletion, true); - } - - Future - test_requiredAnnotationToRequiredKeyword_simple_removeViaComment() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -f({@required int x}) {} -'''); - var annotation = findNode.annotation('required'); - var previewInfo = run( - {annotation: NodeChangeForAnnotation()..changeToRequiredKeyword = true}, - removeViaComments: true)!; - expect(previewInfo.applyTo(code!), ''' -import 'package:meta/meta.dart'; -f({required int x}) {} -'''); - expect(previewInfo.values.single.single.isDeletion, true); - } - - Future test_variableDeclarationList_addExplicitType_insert() async { - await analyze('final x = 0;'); - var previewInfo = run({ - findNode.variableDeclarationList('final'): - NodeChangeForVariableDeclarationList() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'final int x = 0;'); - } - - Future test_variableDeclarationList_addExplicitType_metadata() async { - await analyze('@deprecated var x = 0;'); - var previewInfo = run({ - findNode.variableDeclarationList('var'): - NodeChangeForVariableDeclarationList() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), '@deprecated int x = 0;'); - } - - Future test_variableDeclarationList_addExplicitType_no() async { - await analyze('var x = 0;'); - var previewInfo = run({ - findNode.variableDeclarationList('var'): - NodeChangeForVariableDeclarationList() - }); - expect(previewInfo, isNull); - } - - Future test_variableDeclarationList_addExplicitType_otherPlans() async { - await analyze('var x = 0;'); - var previewInfo = run({ - findNode.variableDeclarationList('var'): - NodeChangeForVariableDeclarationList() - ..addExplicitType = nnbdTypeProvider.intType, - findNode.integerLiteral('0'): NodeChangeForExpression() - ..addExpressionChange(NullCheckChange(MockDartType()), _MockInfo()) - })!; - expect(previewInfo.applyTo(code!), 'int x = 0!;'); - } - - Future test_variableDeclarationList_addExplicitType_prefixed() async { - await analyze(''' -import 'dart:core' as core; -final x = 0; -'''); - var previewInfo = run({ - findNode.variableDeclarationList('final'): - NodeChangeForVariableDeclarationList() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), ''' -import 'dart:core' as core; -final core.int x = 0; -'''); - } - - Future test_variableDeclarationList_addExplicitType_replaceVar() async { - await analyze('var x = 0;'); - var previewInfo = run({ - findNode.variableDeclarationList('var'): - NodeChangeForVariableDeclarationList() - ..addExplicitType = nnbdTypeProvider.intType - })!; - expect(previewInfo.applyTo(code!), 'int x = 0;'); - } - - Future test_warnOnDeadIf_false() async { - await analyze(''' -f(int i) { - if (i == null) print(i); -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = false - }, warnOnWeakCode: true)!; - expect(previewInfo.applyTo(code!, includeInformative: true), ''' -f(int i) { - if (i == null /* == false */) print(i); -} -'''); - } - - Future test_warnOnDeadIf_true() async { - await analyze(''' -f(int i) { - if (i != null) print(i); -} -'''); - var previewInfo = run({ - findNode.statement('if'): NodeChangeForIfStatement() - ..conditionValue = true - }, warnOnWeakCode: true)!; - expect(previewInfo.applyTo(code!, includeInformative: true), ''' -f(int i) { - if (i != null /* == true */) print(i); -} -'''); - } - - Future test_warnOnNullAwareAccess() async { - var content = ''' -f(int i) { - print(i?.isEven); -} -'''; - await analyze(content); - var previewInfo = run({ - findNode.propertyAccess('?.'): NodeChangeForPropertyAccess() - ..nullAwarenessRemoval = NullAwarenessRemovalType.weak - }, warnOnWeakCode: true)!; - expect(previewInfo.applyTo(code!), content); - expect(previewInfo, hasLength(1)); - var edit = previewInfo[content.indexOf('?')]!.single; - expect(edit.isInformative, isTrue); - expect(edit.length, '?'.length); - } -} - -class FixAggregatorTestBase extends AbstractSingleUnitTest { - String? code; - - Future analyze(String code) async { - this.code = code; - await resolveTestUnit(code); - } - - Map>? run(Map changes, - {bool removeViaComments = false, bool warnOnWeakCode = false}) { - return FixAggregator.run(testUnit!, testCode, changes, - removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode); - } -} - -class MockDartType implements TypeImpl { - final String? toStringValueWithNullability; - - final String? toStringValueWithoutNullability; - - const MockDartType( - {this.toStringValueWithNullability, - this.toStringValueWithoutNullability}); - - @override - String getDisplayString({ - bool skipAllDynamicArguments = false, - bool withNullability = false, - }) { - var result = withNullability - ? toStringValueWithNullability! - : toStringValueWithoutNullability!; - expect(result, isNotNull); - return result; - } - - @override - noSuchMethod(Invocation invocation) { - return super.noSuchMethod(invocation); - } -} - -class MockDecoratedType implements DecoratedType { - @override - final DartType type; - - const MockDecoratedType(this.type); - - @override - NullabilityNode get node => - NullabilityNode.forTypeAnnotation(NullabilityNodeTarget.text('test')); - - @override - noSuchMethod(Invocation invocation) { - return super.noSuchMethod(invocation); - } -} - -class _MockInfo implements AtomicEditInfo { - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart deleted file mode 100644 index ee0a689feab8..000000000000 --- a/pkg/nnbd_migration/test/fix_builder_test.dart +++ /dev/null @@ -1,3948 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/error/codes.g.dart'; -import 'package:analyzer/src/generated/element_type_provider.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/fix_aggregator.dart'; -import 'package:nnbd_migration/src/fix_builder.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(FixBuilderTest); - }); -} - -/// Information about the target of an assignment expression analyzed by -/// [FixBuilder]. -class AssignmentTargetInfo { - /// The type that the assignment target has when read. This is only relevant - /// for compound assignments (since they both read and write the assignment - /// target) - final DartType? readType; - - /// The type that the assignment target has when written to. - final DartType? writeType; - - AssignmentTargetInfo(this.readType, this.writeType); -} - -@reflectiveTest -class FixBuilderTest extends EdgeBuilderTestBase { - static final isAddRequiredKeyword = - TypeMatcher() - .having((c) => c.addRequiredKeyword, 'addRequiredKeyword', true); - - static final isMakeNullable = TypeMatcher() - .having((c) => c.makeNullable, 'makeNullable', true) - .having((c) => c.nullabilityHint, 'nullabilityHint', isNull); - - static final isMakeNullableDueToHint = - TypeMatcher() - .having((c) => c.makeNullable, 'makeNullable', true) - .having((c) => c.nullabilityHint, 'nullabilityHint', isNotNull); - - static const isEdge = TypeMatcher(); - - static final isExplainNonNullable = TypeMatcher() - .having((c) => c.makeNullable, 'makeNullable', false); - - static final isBadCombinedType = TypeMatcher() - .having((c) => c.hasBadCombinedType, 'hasBadCombinedType', true); - - static final isNullableSource = TypeMatcher() - .having((c) => c.hasNullableSource, 'hasNullableSource', true); - - static final isNodeChangeForExpression = - TypeMatcher(); - - static final isNoValidMigration = - isNodeChangeForExpression.havingNoValidMigrationWithInfo(anything); - - static final isNullCheck = - isNodeChangeForExpression.havingNullCheckWithInfo(anything); - - static final isRemoveLanguageVersion = - TypeMatcher().having( - (c) => c.removeLanguageVersionComment, - 'removeLanguageVersionComment', - true); - - static final isAddImportOfIterableExtension = - TypeMatcher() - .having((c) => c.addImports, 'addImports', { - 'package:collection/collection.dart': {'IterableExtension'} - }); - - static final isAddShowOfIterableExtension = - TypeMatcher().having((c) => c.addNames, - 'addNames', unorderedEquals(['IterableExtension'])); - - static final isRemoveNullAwareness = - TypeMatcher().having( - (c) => c.nullAwarenessRemoval != NullAwarenessRemovalType.none, - 'removeNullAwareness', - true); - - static final isRemoveAs = TypeMatcher() - .having((c) => c.removeAs, 'removeAs', true); - - static final isRequiredAnnotationToRequiredKeyword = - TypeMatcher().having( - (c) => c.changeToRequiredKeyword, 'changeToRequiredKeyword', true); - - static final isWeakNullAwareAssignment = - TypeMatcher() - .having((c) => c.isWeakNullAware, 'isWeakNullAware', true); - - @override - Future analyze(String code) async { - var unit = await super.analyze(code); - graph.propagate(); - return unit; - } - - TypeMatcher isDropArgument( - dynamic argumentsToDrop) => - TypeMatcher() - .having((c) => c.argumentsToDrop, 'argumentsToDrop', argumentsToDrop); - - TypeMatcher isInfo(description, fixReasons) => - TypeMatcher() - .having((i) => i.description, 'description', description) - .having((i) => i.fixReasons, 'fixReasons', fixReasons); - - TypeMatcher isMethodNameChange( - dynamic replacement) => - TypeMatcher() - .having((c) => c.replacement, 'replacement', replacement); - - Map scopedChanges( - FixBuilder fixBuilder, AstNode? scope) => - { - for (var entry in fixBuilder.changes.entries) - if (_isInScope(entry.key, scope) && !entry.value.isInformative) - entry.key: entry.value - }; - - Map scopedInformative( - FixBuilder fixBuilder, AstNode scope) => - { - for (var entry in fixBuilder.changes.entries) - if (_isInScope(entry.key, scope) && entry.value.isInformative) - entry.key: entry.value - }; - - Map> scopedProblems( - FixBuilder fixBuilder, AstNode? scope) => - { - for (var entry in fixBuilder.problems.entries) - if (_isInScope(entry.key, scope)) entry.key: entry.value - }; - - Future test_asExpression_keep() async { - await analyze(''' -_f(Object x) { - print((x as int) + 1); -} -'''); - var asExpression = findNode.simple('x as').parent as Expression; - visitSubexpression(asExpression, 'int'); - } - - Future test_asExpression_keep_previously_unnecessary() async { - verifyNoTestUnitErrors = false; - await analyze(''' -f(int i) { - print((i as int) + 1); -} -'''); - expect(testAnalysisResult.errors.single.errorCode, - WarningCode.UNNECESSARY_CAST); - var asExpression = findNode.simple('i as').parent as Expression; - visitSubexpression(asExpression, 'int'); - } - - Future test_asExpression_remove() async { - await analyze(''' -_f(Object x) { - if (x is! int) return; - print((x as int) + 1); -} -'''); - var asExpression = findNode.simple('x as').parent as Expression; - visitSubexpression(asExpression, 'int', - changes: {asExpression: isRemoveAs}); - } - - Future - test_assignmentExpression_compound_combined_nullable_noProblem() async { - await analyze(''' -abstract class _C { - _D/*?*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*!*/ get x; - void set x(_C/*?*/ value); - f(int/*!*/ y) => x += y; -} -'''); - visitSubexpression(findNode.assignment('+='), '_D?'); - } - - Future - test_assignmentExpression_compound_combined_nullable_noProblem_dynamic() async { - await analyze(''' -abstract class _E { - dynamic get x; - void set x(Object/*!*/ value); - f(int/*!*/ y) => x += y; -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, 'dynamic'); - } - - @FailingTest(reason: 'TODO(paulberry)') - Future - test_assignmentExpression_compound_combined_nullable_problem() async { - await analyze(''' -abstract class _C { - _D/*?*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*!*/ get x; - void set x(_C/*!*/ value); - f(int/*!*/ y) => x += y; -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, '_D', problems: { - assignment: {const CompoundAssignmentCombinedNullable()} - }); - } - - Future test_assignmentExpression_compound_dynamic() async { - // To confirm that the RHS is visited, we check that a null check was - // properly inserted into a subexpression of the RHS. - await analyze(''' -_f(dynamic x, int/*?*/ y) => x += y + 1; -'''); - visitSubexpression(findNode.assignment('+='), 'dynamic', - changes: {findNode.simple('y +'): isNullCheck}); - } - - Future test_assignmentExpression_compound_intRules() async { - await analyze(''' -_f(int x, int y) => x += y; -'''); - visitSubexpression(findNode.assignment('+='), 'int'); - } - - @FailingTest(reason: 'TODO(paulberry)') - Future test_assignmentExpression_compound_lhs_nullable_problem() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*?*/ get x; - void set x(_C/*?*/ value); - f(int/*!*/ y) => x += y; -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, '_D', problems: { - assignment: {const CompoundAssignmentReadNullable()} - }); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39641') - Future test_assignmentExpression_compound_promoted() async { - await analyze(''' -f(bool/*?*/ x, bool/*?*/ y) => x != null && (x = y); -'''); - // It is ok to assign a nullable value to `x` even though it is promoted to - // non-nullable, so `y` should not be null-checked. However, the whole - // assignment `x = y` should be null checked because the RHS of `&&` cannot - // be nullable. - visitSubexpression(findNode.binary('&&'), 'bool', - changes: {findNode.parenthesized('x = y'): isNullCheck}); - } - - Future test_assignmentExpression_compound_rhs_nonNullable() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -_f(_C/*!*/ x, int/*!*/ y) => x += y; -'''); - visitSubexpression(findNode.assignment('+='), '_D'); - } - - Future test_assignmentExpression_compound_rhs_nullable_check() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -_f(_C/*!*/ x, int/*?*/ y) => x += y; -'''); - visitSubexpression(findNode.assignment('+='), '_D', - changes: {findNode.simple('y;'): isNullCheck}); - } - - Future test_assignmentExpression_compound_rhs_nullable_noCheck() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*?*/ value); -} -abstract class _D extends _C {} -_f(_C/*!*/ x, int/*?*/ y) => x += y; -'''); - visitSubexpression(findNode.assignment('+='), '_D'); - } - - Future - test_assignmentExpression_null_aware_rhs_does_not_promote() async { - await analyze(''' -_f(bool/*?*/ b, int/*?*/ i) { - b ??= i.isEven; // 1 - b = i.isEven; // 2 - b = i.isEven; // 3 -} -'''); - // The null check inserted at 1 fails to promote i because it's inside the - // `??=`, so a null check is inserted at 2. This does promote i, so no null - // check is inserted at 3. - visitStatement(findNode.block('{'), changes: { - findNode.simple('i.isEven; // 1'): isNullCheck, - findNode.simple('i.isEven; // 2'): isNullCheck - }); - } - - Future test_assignmentExpression_null_aware_rhs_nonNullable() async { - await analyze(''' -abstract class _B {} -abstract class _C extends _B {} -abstract class _D extends _C {} -abstract class _E extends _C {} -abstract class _F { - _D/*?*/ get x; - void set x(_B/*?*/ value); - f(_E/*!*/ y) => x ??= y; -} -'''); - visitSubexpression(findNode.assignment('??='), '_C'); - } - - Future test_assignmentExpression_null_aware_rhs_nullable() async { - await analyze(''' -abstract class _B {} -abstract class _C extends _B {} -abstract class _D extends _C {} -abstract class _E extends _C {} -abstract class _F { - _D/*?*/ get x; - void set x(_B/*?*/ value); - f(_E/*?*/ y) => x ??= y; -} -'''); - visitSubexpression(findNode.assignment('??='), '_C?'); - } - - Future test_assignmentExpression_null_aware_simple_promoted() async { - await analyze(''' -_f(bool/*?*/ x, bool/*?*/ y) => x != null && (x ??= y) != null; -'''); - // On the RHS of the `&&`, `x` is promoted to non-nullable, but it is still - // considered to be a nullable assignment target, so no null check is - // generated for `y`. - visitSubexpression(findNode.binary('&&'), 'bool', - changes: {findNode.assignment('??='): isWeakNullAwareAssignment}); - } - - Future - test_assignmentExpression_simple_nonNullable_to_nonNullable() async { - await analyze(''' -_f(int/*!*/ x, int/*!*/ y) => x = y; -'''); - visitSubexpression(findNode.assignment('= '), 'int'); - } - - Future - test_assignmentExpression_simple_nonNullable_to_nullable() async { - await analyze(''' -_f(int/*?*/ x, int/*!*/ y) => x = y; -'''); - visitSubexpression(findNode.assignment('= '), 'int'); - } - - Future - test_assignmentExpression_simple_nullable_to_nonNullable() async { - await analyze(''' -_f(int/*!*/ x, int/*?*/ y) => x = y; -'''); - visitSubexpression(findNode.assignment('= '), 'int', - changes: {findNode.simple('y;'): isNullCheck}); - } - - Future test_assignmentExpression_simple_nullable_to_nullable() async { - await analyze(''' -_f(int/*?*/ x, int/*?*/ y) => x = y; -'''); - visitSubexpression(findNode.assignment('= '), 'int?'); - } - - Future test_assignmentExpression_simple_promoted() async { - await analyze(''' -_f(bool/*?*/ x, bool/*?*/ y) => x != null && (x = y) != null; -'''); - // On the RHS of the `&&`, `x` is promoted to non-nullable, but it is still - // considered to be a nullable assignment target, so no null check is - // generated for `y`. - visitSubexpression(findNode.binary('&&'), 'bool'); - } - - Future test_assignmentTarget_indexExpression_compound_dynamic() async { - await analyze(''' -_f(dynamic d, int/*?*/ i) => d[i] += 0; -'''); - visitAssignmentTarget(findNode.index('d[i]'), 'dynamic', 'dynamic'); - } - - Future test_assignmentTarget_indexExpression_compound_simple() async { - await analyze(''' -class _C { - int operator[](String s) => 1; - void operator[]=(String s, num n) {} -} -_f(_C c) => c['foo'] += 0; -'''); - visitAssignmentTarget(findNode.index('c['), 'int', 'num?'); - } - - Future - test_assignmentTarget_indexExpression_compound_simple_check_lhs() async { - await analyze(''' -class _C { - int operator[](String s) => 1; - void operator[]=(String s, num n) {} -} -_f(_C/*?*/ c) => c['foo'] += 0; -'''); - visitAssignmentTarget(findNode.index('c['), 'int', 'num?', - changes: {findNode.simple('c['): isNullCheck}); - } - - Future - test_assignmentTarget_indexExpression_compound_simple_check_rhs() async { - await analyze(''' -class _C { - int operator[](String/*!*/ s) => 1; - void operator[]=(String/*?*/ s, num n) {} -} -_f(_C c, String/*?*/ s) => c[s] += 0; -'''); - visitAssignmentTarget(findNode.index('c['), 'int', 'num?', - changes: {findNode.simple('s]'): isNullCheck}); - } - - Future - test_assignmentTarget_indexExpression_compound_substituted() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; - void operator[]=(U u, T t) {} -} -_f(_C c) => c['foo'] += 1; -'''); - visitAssignmentTarget(findNode.index('c['), 'int', 'int'); - } - - Future - test_assignmentTarget_indexExpression_compound_substituted_check_rhs() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; - void operator[]=(U/*?*/ u, T t) {} -} -_f(_C c, String/*?*/ s) => c[s] += 1; -'''); - visitAssignmentTarget(findNode.index('c['), 'int', 'int', - changes: {findNode.simple('s]'): isNullCheck}); - } - - Future - test_assignmentTarget_indexExpression_compound_substituted_no_check_rhs() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; - void operator[]=(U u, T t) {} -} -_f(_C c, String/*?*/ s) => c[s] += 0; -'''); - visitAssignmentTarget(findNode.index('c['), 'int', 'int'); - } - - Future test_assignmentTarget_indexExpression_dynamic() async { - await analyze(''' -_f(dynamic d, int/*?*/ i) => d[i] = 0; -'''); - visitAssignmentTarget(findNode.index('d[i]'), null, 'dynamic'); - } - - Future test_assignmentTarget_indexExpression_simple() async { - await analyze(''' -class _C { - int operator[](String s) => 1; - void operator[]=(String s, num n) {} -} -_f(_C c) => c['foo'] = 0; -'''); - visitAssignmentTarget(findNode.index('c['), null, 'num?'); - } - - Future test_assignmentTarget_indexExpression_simple_check_lhs() async { - await analyze(''' -class _C { - int operator[](String s) => 1; - void operator[]=(String s, num n) {} -} -_f(_C/*?*/ c) => c['foo'] = 0; -'''); - visitAssignmentTarget(findNode.index('c['), null, 'num?', - changes: {findNode.simple('c['): isNullCheck}); - } - - Future test_assignmentTarget_indexExpression_simple_check_rhs() async { - await analyze(''' -class _C { - int operator[](String/*?*/ s) => 1; - void operator[]=(String/*!*/ s, num n) {} -} -_f(_C c, String/*?*/ s) => c[s] = 0; -'''); - visitAssignmentTarget(findNode.index('c['), null, 'num?', - changes: {findNode.simple('s]'): isNullCheck}); - } - - Future test_assignmentTarget_indexExpression_substituted() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; - void operator[]=(U u, T t) {} -} -_f(_C c) => c['foo'] = 1; -'''); - visitAssignmentTarget(findNode.index('c['), null, 'int'); - } - - Future - test_assignmentTarget_indexExpression_substituted_check_rhs() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; - void operator[]=(U/*!*/ u, T t) {} -} -_f(_C c, String/*?*/ s) => c[s] = 1; -'''); - visitAssignmentTarget(findNode.index('c['), null, 'int', - changes: {findNode.simple('s]'): isNullCheck}); - } - - Future - test_assignmentTarget_indexExpression_substituted_no_check_rhs() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; - void operator[]=(U u, T t) {} -} -_f(_C c, String/*?*/ s) => c[s] = 0; -'''); - visitAssignmentTarget(findNode.index('c['), null, 'int'); - } - - Future test_assignmentTarget_prefixedIdentifier_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d) => d.x += 1; -'''); - visitAssignmentTarget(findNode.prefixed('d.x'), 'dynamic', 'dynamic'); - } - - Future test_assignmentTarget_propertyAccess_dynamic() async { - await analyze(''' -_f(dynamic d) => (d).x += 1; -'''); - visitAssignmentTarget( - findNode.propertyAccess('(d).x'), 'dynamic', 'dynamic'); - } - - Future - test_assignmentTarget_propertyAccess_dynamic_notCompound() async { - await analyze(''' -_f(dynamic d) => (d).x = 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(d).x'), null, 'dynamic'); - } - - Future test_assignmentTarget_propertyAccess_field_nonNullable() async { - await analyze(''' -class _C { - int/*!*/ x = 0; -} -_f(_C c) => (c).x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(c).x'), 'int', 'int'); - } - - Future - test_assignmentTarget_propertyAccess_field_nonNullable_notCompound() async { - await analyze(''' -class _C { - int/*!*/ x = 0; -} -_f(_C c) => (c).x = 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(c).x'), null, 'int'); - } - - Future test_assignmentTarget_propertyAccess_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ x = 0; -} -_f(_C c) => (c).x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(c).x'), 'int?', 'int?'); - } - - Future test_assignmentTarget_propertyAccess_getter_nullable() async { - await analyze(''' -abstract class _C { - int/*?*/ get x; - void set x(num/*?*/ value); -} -_f(_C c) => (c).x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(c).x'), 'int?', 'num?'); - } - - Future - test_assignmentTarget_propertyAccess_getter_setter_check_lhs() async { - await analyze(''' -abstract class _C { - int get x; - void set x(num value); -} -_f(_C/*?*/ c) => (c).x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(c).x'), 'int', 'num', - changes: {findNode.parenthesized('(c).x'): isNullCheck}); - } - - Future - test_assignmentTarget_propertyAccess_getter_setter_nonNullable() async { - await analyze(''' -abstract class _C { - int/*!*/ get x; - void set x(num/*!*/ value); -} -_f(_C c) => (c).x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('(c).x'), 'int', 'num'); - } - - Future test_assignmentTarget_propertyAccess_nullAware_dynamic() async { - await analyze(''' -_f(dynamic d) => d?.x += 1; -'''); - visitAssignmentTarget( - findNode.propertyAccess('d?.x'), 'dynamic', 'dynamic'); - } - - Future - test_assignmentTarget_propertyAccess_nullAware_field_nonNullable() async { - await analyze(''' -class _C { - int/*!*/ x = 0; -} -_f(_C/*?*/ c) => c?.x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('c?.x'), 'int', 'int'); - } - - Future - test_assignmentTarget_propertyAccess_nullAware_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ x = 0; -} -_f(_C/*?*/ c) => c?.x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('c?.x'), 'int?', 'int?'); - } - - Future - test_assignmentTarget_propertyAccess_nullAware_getter_setter_nonNullable() async { - await analyze(''' -abstract class _C { - int/*!*/ get x; - void set x(num/*!*/ value); -} -_f(_C/*?*/ c) => c?.x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('c?.x'), 'int', 'num'); - } - - Future - test_assignmentTarget_propertyAccess_nullAware_getter_setter_nullable() async { - await analyze(''' -abstract class _C { - int/*?*/ get x; - void set x(num/*?*/ value); -} -_f(_C/*?*/ c) => c?.x += 1; -'''); - visitAssignmentTarget(findNode.propertyAccess('c?.x'), 'int?', 'num?'); - } - - Future - test_assignmentTarget_propertyAccess_nullAware_substituted() async { - await analyze(''' -abstract class _C { - _E get x; - void set x(_D value); -} -class _D implements Iterable { - noSuchMethod(invocation) => super.noSuchMethod(invocation); - _D operator+(int i) => this; -} -class _E extends _D {} -_f(_C/*?*/ c) => c?.x += 1; -'''); - visitAssignmentTarget( - findNode.propertyAccess('c?.x'), '_E', '_D'); - } - - Future test_assignmentTarget_propertyAccess_substituted() async { - await analyze(''' -abstract class _C { - _E get x; - void set x(_D value); -} -class _D implements Iterable { - noSuchMethod(invocation) => super.noSuchMethod(invocation); - _D operator+(int i) => this; -} -class _E extends _D {} -_f(_C c) => (c).x += 1; -'''); - visitAssignmentTarget( - findNode.propertyAccess('(c).x'), '_E', '_D'); - } - - Future test_assignmentTarget_simpleIdentifier_field_generic() async { - await analyze(''' -abstract class _C { - _C operator+(int i); -} -class _D { - _D(this.x); - _C/*!*/ x; - _f() => x += 0; -} -'''); - visitAssignmentTarget(findNode.simple('x +='), '_C', '_C'); - } - - Future - test_assignmentTarget_simpleIdentifier_field_nonNullable() async { - await analyze(''' -class _C { - int/*!*/ x; - _f() => x += 0; -} -'''); - visitAssignmentTarget(findNode.simple('x '), 'int', 'int'); - } - - Future test_assignmentTarget_simpleIdentifier_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ x; - _f() => x += 0; -} -'''); - visitAssignmentTarget(findNode.simple('x '), 'int?', 'int?'); - } - - Future test_assignmentTarget_simpleIdentifier_getset_generic() async { - await analyze(''' -abstract class _C { - _C operator+(int i); -} -abstract class _D extends _C {} -abstract class _E { - _D/*!*/ get x; - void set x(_C/*!*/ value); - _f() => x += 0; -} -'''); - visitAssignmentTarget(findNode.simple('x +='), '_D', '_C'); - } - - Future - test_assignmentTarget_simpleIdentifier_getset_getterNullable() async { - await analyze(''' -class _C { - int/*?*/ get x => 1; - void set x(int/*!*/ value) {} - _f() => x += 0; -} -'''); - visitAssignmentTarget(findNode.simple('x +='), 'int?', 'int'); - } - - Future - test_assignmentTarget_simpleIdentifier_getset_setterNullable() async { - await analyze(''' -class _C { - int/*!*/ get x => 1; - void set x(int/*?*/ value) {} - _f() => x += 0; -} -'''); - visitAssignmentTarget(findNode.simple('x +='), 'int', 'int?'); - } - - Future - test_assignmentTarget_simpleIdentifier_localVariable_nonNullable() async { - await analyze(''' -_f(int/*!*/ x) => x += 0; -'''); - visitAssignmentTarget(findNode.simple('x '), 'int', 'int'); - } - - Future - test_assignmentTarget_simpleIdentifier_localVariable_nullable() async { - await analyze(''' -_f(int/*?*/ x) => x += 0; -'''); - visitAssignmentTarget(findNode.simple('x '), 'int?', 'int?'); - } - - Future - test_assignmentTarget_simpleIdentifier_setter_nonNullable() async { - await analyze(''' -class _C { - void set x(int/*!*/ value) {} - _f() => x = 0; -} -'''); - visitAssignmentTarget(findNode.simple('x '), null, 'int'); - } - - Future test_assignmentTarget_simpleIdentifier_setter_nullable() async { - await analyze(''' -class _C { - void set x(int/*?*/ value) {} - _f() => x = 0; -} -'''); - visitAssignmentTarget(findNode.simple('x '), null, 'int?'); - } - - Future test_binaryExpression_ampersand_ampersand() async { - await analyze(''' -_f(bool x, bool y) => x && y; -'''); - visitSubexpression(findNode.binary('&&'), 'bool'); - } - - Future test_binaryExpression_ampersand_ampersand_flow() async { - await analyze(''' -_f(bool/*?*/ x) => x != null && x; -'''); - visitSubexpression(findNode.binary('&&'), 'bool'); - } - - Future test_binaryExpression_ampersand_ampersand_nullChecked() async { - await analyze(''' -_f(bool/*?*/ x, bool/*?*/ y) => x && y; -'''); - var xRef = findNode.simple('x &&'); - var yRef = findNode.simple('y;'); - visitSubexpression(findNode.binary('&&'), 'bool', - changes: {xRef: isNullCheck, yRef: isNullCheck}); - } - - Future test_binaryExpression_bang_eq() async { - await analyze(''' -_f(Object/*?*/ x, Object/*?*/ y) => x != y; -'''); - visitSubexpression(findNode.binary('!='), 'bool'); - } - - Future test_binaryExpression_bar_bar() async { - await analyze(''' -_f(bool x, bool y) { - return x || y; -} -'''); - visitSubexpression(findNode.binary('||'), 'bool'); - } - - Future test_binaryExpression_bar_bar_flow() async { - await analyze(''' -_f(bool/*?*/ x) { - return x == null || x; -} -'''); - visitSubexpression(findNode.binary('||'), 'bool'); - } - - Future test_binaryExpression_bar_bar_nullChecked() async { - await analyze(''' -_f(bool/*?*/ x, bool/*?*/ y) { - return x || y; -} -'''); - var xRef = findNode.simple('x ||'); - var yRef = findNode.simple('y;'); - visitSubexpression(findNode.binary('||'), 'bool', - changes: {xRef: isNullCheck, yRef: isNullCheck}); - } - - Future test_binaryExpression_eq_eq() async { - await analyze(''' -_f(Object/*?*/ x, Object/*?*/ y) { - return x == y; -} -'''); - visitSubexpression(findNode.binary('=='), 'bool'); - } - - Future test_binaryExpression_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - void operator+(C/*!*/ other) {} -} -f(C/*?*/ c) => c + c; -'''); - var binaryExpression = findNode.binary('c + c'); - visitSubexpression(binaryExpression, 'void', - changes: {binaryExpression.rightOperand: isNullCheck}); - } - - Future - test_binaryExpression_extensionMember_allowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - void operator+(C/*!*/ other) {} -} -f(C/*?*/ c) => E(c) + c; -'''); - var binaryExpression = findNode.binary('E(c) + c'); - visitSubexpression(binaryExpression, 'void', - changes: {binaryExpression.rightOperand: isNullCheck}); - } - - Future test_binaryExpression_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - void operator+(C/*!*/ other) {} -} -f(C/*?*/ c) => c + c; -'''); - var binaryExpression = findNode.binary('c + c'); - visitSubexpression(binaryExpression, 'void', - changes: {binaryExpression.leftOperand: isNullCheck}); - } - - Future - test_binaryExpression_extensionMember_disallowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - void operator+(C/*!*/ other) {} -} -f(C/*?*/ c) => E(c) + c; -'''); - var binaryExpression = findNode.binary('E(c) + c'); - visitSubexpression(binaryExpression, 'void', - changes: {findNode.simple('c) +'): isNullCheck}); - } - - Future test_binaryExpression_question_question() async { - await analyze(''' -_f(int/*?*/ x, double/*?*/ y) { - return x ?? y; -} -'''); - visitSubexpression(findNode.binary('??'), 'num?'); - } - - Future test_binaryExpression_question_question_flow() async { - await analyze(''' -_f(int/*?*/ x, int/*?*/ y) => - [x ?? (y != null ? 1 : throw 'foo'), y + 1]; -'''); - // The null check on the RHS of the `??` doesn't promote, because it is not - // guaranteed to execute. - visitSubexpression(findNode.listLiteral('['), 'List', - changes: {findNode.simple('y +'): isNullCheck}); - } - - Future test_binaryExpression_question_question_nullChecked() async { - await analyze(''' -Object/*!*/ _f(int/*?*/ x, double/*?*/ y) { - return x ?? y; -} -'''); - var yRef = findNode.simple('y;'); - visitSubexpression(findNode.binary('??'), 'num', - changes: {yRef: isNullCheck}); - } - - Future test_binaryExpression_userDefinable_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d, int/*?*/ i) => d + i; -'''); - visitSubexpression(findNode.binary('+'), 'dynamic'); - } - - Future test_binaryExpression_userDefinable_intRules() async { - await analyze(''' -_f(int i, int j) => i + j; -'''); - visitSubexpression(findNode.binary('+'), 'int'); - } - - Future test_binaryExpression_userDefinable_simple() async { - await analyze(''' -class _C { - int operator+(String s) => 1; -} -_f(_C c) => c + 'foo'; -'''); - visitSubexpression(findNode.binary('c +'), 'int'); - } - - Future test_binaryExpression_userDefinable_simple_check_lhs() async { - await analyze(''' -class _C { - int operator+(String s) => 1; -} -_f(_C/*?*/ c) => c + 'foo'; -'''); - visitSubexpression(findNode.binary('c +'), 'int', - changes: {findNode.simple('c +'): isNullCheck}); - } - - Future test_binaryExpression_userDefinable_simple_check_rhs() async { - await analyze(''' -class _C { - int operator+(String/*!*/ s) => 1; -} -_f(_C c, String/*?*/ s) => c + s; -'''); - visitSubexpression(findNode.binary('c +'), 'int', - changes: {findNode.simple('s;'): isNullCheck}); - } - - Future test_binaryExpression_userDefinable_substituted() async { - await analyze(''' -class _C { - T operator+(U u) => throw 'foo'; -} -_f(_C c) => c + 'foo'; -'''); - visitSubexpression(findNode.binary('c +'), 'int'); - } - - Future - test_binaryExpression_userDefinable_substituted_check_rhs() async { - await analyze(''' -class _C { - T operator+(U/*!*/ u) => throw 'foo'; -} -_f(_C c, String/*?*/ s) => c + s; -'''); - visitSubexpression(findNode.binary('c +'), 'int', - changes: {findNode.simple('s;'): isNullCheck}); - } - - Future - test_binaryExpression_userDefinable_substituted_no_check_rhs() async { - await analyze(''' -class _C { - T operator+(U u) => throw 'foo'; -} -_f(_C c, String/*?*/ s) => c + s; -'''); - visitSubexpression(findNode.binary('c +'), 'int'); - } - - Future test_block() async { - await analyze(''' -_f(int/*?*/ x, int/*?*/ y) { - { // block - x + 1; - y + 1; - } -} -'''); - visitStatement(findNode.statement('{ // block'), changes: { - findNode.simple('x + 1'): isNullCheck, - findNode.simple('y + 1'): isNullCheck - }); - } - - Future test_booleanLiteral() async { - await analyze(''' -f() => true; -'''); - visitSubexpression(findNode.booleanLiteral('true'), 'bool'); - } - - Future test_compound_assignment_null_shorted_ok() async { - await analyze(''' -class C { - int/*!*/ x; -} -_f(C/*?*/ c) { - c?.x += 1; -} -'''); - // Even though c?.x is nullable, it should not be a problem to use it as the - // LHS of a compound assignment, because null shorting will ensure that the - // assignment only happens if c is non-null. - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, 'int?'); - } - - Future test_compound_assignment_nullable_result_bad() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -f(C c) { - c += 1; -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, 'C?', - changes: {assignment: isBadCombinedType}); - } - - Future test_compound_assignment_nullable_result_ok() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -abstract class D { - void set x(C/*?*/ value); - C/*!*/ get x; - f() { - x += 1; - } -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, 'C?'); - } - - Future test_compound_assignment_nullable_source() async { - await analyze(''' -_f(int/*?*/ x) { - x += 1; -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, 'int', - changes: {assignment: isNullableSource}); - } - - Future test_compound_assignment_potentially_nullable_source() async { - await analyze(''' -class C { - _f(T/*!*/ x) { - x += 1; - } -} -'''); - var assignment = findNode.assignment('+='); - visitSubexpression(assignment, 'num', - changes: {assignment: isNullableSource}); - } - - Future test_compound_assignment_promoted_ok() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -f(C/*?*/ x) { - if (x != null) { - x += 1; - } -} -'''); - // The compound assignment is ok, because: - // - prior to the assignment, x's value is promoted to non-nullable - // - the nullable return value of operator+ is ok to assign to x, because it - // un-does the promotion. - visitSubexpression(findNode.assignment('+='), 'C?'); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_conditionalExpression_dead_else_remove() async { - await analyze('_f(int x, int/*?*/ y) => x != null ? x + 1 : y + 1.0;'); - var expression = findNode.conditionalExpression('x != null'); - visitSubexpression(expression, 'num', - changes: {findNode.simple('y +'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_conditionalExpression_dead_else_warn() async { - await analyze('_f(int x, int/*?*/ y) => x != null ? x + 1 : y + 1.0;'); - var expression = findNode.conditionalExpression('x != null'); - visitSubexpression(expression, 'num', - warnOnWeakCode: true, changes: {findNode.simple('y +'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_conditionalExpression_dead_then_remove() async { - await analyze('_f(int x, int/*?*/ y) => x == null ? y + 1.0 : x + 1;'); - var expression = findNode.conditionalExpression('x == null'); - visitSubexpression(expression, 'num', - changes: {findNode.simple('y +'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_conditionalExpression_dead_then_warn() async { - await analyze('_f(int x, int/*?*/ y) => x == null ? y + 1.0 : x + 1;'); - var expression = findNode.conditionalExpression('x == null'); - visitSubexpression(expression, 'num', - warnOnWeakCode: true, changes: {findNode.simple('y +'): isNullCheck}); - } - - Future test_conditionalExpression_flow_as_condition() async { - await analyze(''' -_f(bool x, int/*?*/ y) => (x ? y != null : y != null) ? y + 1 : 0; -'''); - // No explicit check needs to be added to `y + 1`, because both arms of the - // conditional can only be true if `y != null`. - visitSubexpression(findNode.conditionalExpression('y + 1'), 'int'); - } - - Future test_conditionalExpression_flow_condition() async { - await analyze(''' -_f(bool/*?*/ x) => x ? (x && true) : (x && true); -'''); - // No explicit check needs to be added to either `x && true`, because there - // is already an explicit null check inserted for the condition. - visitSubexpression(findNode.conditionalExpression('x ?'), 'bool', - changes: {findNode.simple('x ?'): isNullCheck}); - } - - Future test_conditionalExpression_flow_then_else() async { - await analyze(''' -_f(bool x, bool/*?*/ y) => (x ? (y && true) : (y && true)) && y; -'''); - // No explicit check needs to be added to the final reference to `y`, - // because null checks are added to the "then" and "else" branches promoting - // y. - visitSubexpression(findNode.binary('&& y'), 'bool', changes: { - findNode.simple('y && true) '): isNullCheck, - findNode.simple('y && true))'): isNullCheck - }); - } - - Future test_conditionalExpression_lub() async { - await analyze(''' -_f(bool b) => b ? 1 : 1.0; -'''); - visitSubexpression(findNode.conditionalExpression('1.0'), 'num'); - } - - Future test_conditionalExpression_throw_promotes() async { - await analyze(''' -_f(int/*?*/ x) => - [(x != null ? 1 : throw 'foo'), x + 1]; -'''); - // No null check needs to be added to `x + 1`, because there is already an - // explicit null check. - visitSubexpression(findNode.listLiteral('['), 'List'); - } - - Future - test_defaultFormalParameter_add_required_ignore_decoy_annotation() async { - await analyze(''' -const foo = Object(); -int _f({@foo int x}) => x + 1; -'''); - visitAll( - changes: {findNode.defaultParameter('int x'): isAddRequiredKeyword}); - } - - Future - test_defaultFormalParameter_add_required_no_because_default() async { - await analyze(''' -int _f({int x = 0}) => x + 1; -'''); - visitAll(); - } - - Future - test_defaultFormalParameter_add_required_no_because_nullable() async { - await analyze(''' -int _f({int/*?*/ x}) => 1; -'''); - visitAll( - changes: {findNode.namedType('int/*?*/ x'): isMakeNullableDueToHint}); - } - - Future - test_defaultFormalParameter_add_required_no_because_positional() async { - await analyze(''' -int _f([int/*!*/ x]) => x + 1; -'''); - visitAll(problems: { - findNode.defaultParameter('int/*!*/ x'): { - const NonNullableUnnamedOptionalParameter() - } - }); - } - - Future - test_defaultFormalParameter_add_required_replace_annotation() async { - // TODO(paulberry): it would be nice to remove the import of `meta` if it's - // no longer needed after the change. - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -int _f({@required int x}) => x + 1; -'''); - visitAll(changes: { - findNode.annotation('required'): isRequiredAnnotationToRequiredKeyword - }); - } - - Future - test_defaultFormalParameter_add_required_replace_annotation_nullable() async { - // TODO(paulberry): it would be nice to remove the import of `meta` if it's - // no longer needed after the change. - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -void _f({@required int/*?*/ x}) {} -'''); - visitAll(changes: { - findNode.annotation('required'): isRequiredAnnotationToRequiredKeyword, - findNode.namedType('int'): isMakeNullableDueToHint, - }); - } - - Future test_defaultFormalParameter_add_required_yes() async { - await analyze(''' -int _f({int x}) => x + 1; -'''); - visitAll( - changes: {findNode.defaultParameter('int x'): isAddRequiredKeyword}); - } - - Future test_doubleLiteral() async { - await analyze(''' -f() => 1.0; -'''); - visitSubexpression(findNode.doubleLiteral('1.0'), 'double'); - } - - Future test_enum_ref_index() async { - await analyze(''' -enum E { V } -_f(E e) => e.index; -'''); - visitSubexpression(findNode.prefixed('e.index'), 'int'); - } - - Future test_enum_ref_value() async { - await analyze(''' -enum E { V } -_f() => E.V; -'''); - visitSubexpression(findNode.prefixed('E.V'), 'E'); - } - - Future test_enum_ref_values() async { - await analyze(''' -enum E { V } -_f() => E.values; -'''); - visitSubexpression(findNode.prefixed('E.values'), 'List'); - } - - Future test_expressionStatement() async { - await analyze(''' -_f(int/*!*/ x, int/*?*/ y) { - x = y; -} -'''); - visitStatement(findNode.statement('x = y'), - changes: {findNode.simple('y;'): isNullCheck}); - } - - Future test_firstWhere_transform() async { - await analyze(''' -_f(Iterable x) => x.firstWhere((n) => n.isEven, orElse: () => null); -'''); - var methodInvocation = findNode.methodInvocation('firstWhere'); - var functionExpression = findNode.functionExpression('() => null'); - var fixBuilder = visitSubexpression(methodInvocation, 'int?', changes: { - methodInvocation.methodName: isMethodNameChange('firstWhereOrNull'), - methodInvocation.argumentList: - isDropArgument({functionExpression.parent: anything}), - // Behavior of the null literal doesn't matter because it's being dropped. - findNode.nullLiteral('null'): anything - }); - expect(fixBuilder.neededCollectionPackageExtensions, {'IterableExtension'}); - } - - Future test_functionExpressionInvocation_dynamic() async { - await analyze(''' -_f(dynamic d) => d(); -'''); - visitSubexpression(findNode.functionExpressionInvocation('d('), 'dynamic'); - } - - Future - test_functionExpressionInvocation_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - void call() {} -} -f(C/*?*/ c) => c(); -'''); - var functionExpressionInvocation = - findNode.functionExpressionInvocation('c()'); - visitSubexpression(functionExpressionInvocation, 'void'); - } - - Future - test_functionExpressionInvocation_extensionMember_allowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - void call() {} -} -f(C/*?*/ c) => E(c)(); -'''); - var functionExpressionInvocation = - findNode.functionExpressionInvocation('E(c)()'); - visitSubexpression(functionExpressionInvocation, 'void'); - } - - Future - test_functionExpressionInvocation_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - void call() {} -} -f(C/*?*/ c) => c(); -'''); - var functionExpressionInvocation = - findNode.functionExpressionInvocation('c()'); - visitSubexpression(functionExpressionInvocation, 'void', - changes: {functionExpressionInvocation.function: isNullCheck}); - } - - Future - test_functionExpressionInvocation_extensionMember_disallowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - void call() {} -} -f(C/*?*/ c) => E(c)(); -'''); - var functionExpressionInvocation = - findNode.functionExpressionInvocation('E(c)()'); - visitSubexpression(functionExpressionInvocation, 'void', - changes: {findNode.simple('c)()'): isNullCheck}); - } - - Future test_functionExpressionInvocation_function_checked() async { - await analyze(''' -_f(Function/*?*/ func) => func(); -'''); - visitSubexpression( - findNode.functionExpressionInvocation('func('), 'dynamic', - changes: {findNode.simple('func()'): isNullCheck}); - } - - Future test_functionExpressionInvocation_getter() async { - await analyze(''' -abstract class _C { - int Function() get f; -} -_f(_C c) => (c.f)(); -'''); - visitSubexpression(findNode.functionExpressionInvocation('c.f'), 'int'); - } - - Future - test_functionExpressionInvocation_getter_looksLikeMethodCall() async { - await analyze(''' -abstract class _C { - int Function() get f; -} -_f(_C c) => c.f(); -'''); - visitSubexpression(findNode.functionExpressionInvocation('c.f'), 'int'); - } - - Future test_functionExpressionInvocation_getter_nullChecked() async { - await analyze(''' -abstract class _C { - int Function()/*?*/ get f; -} -_f(_C c) => (c.f)(); -'''); - visitSubexpression(findNode.functionExpressionInvocation('c.f'), 'int', - changes: {findNode.parenthesized('c.f'): isNullCheck}); - } - - Future - test_functionExpressionInvocation_getter_nullChecked_looksLikeMethodCall() async { - await analyze(''' -abstract class _C { - int Function()/*?*/ get f; -} -_f(_C c) => c.f(); -'''); - visitSubexpression(findNode.functionExpressionInvocation('c.f'), 'int', - changes: {findNode.propertyAccess('c.f'): isNullCheck}); - } - - Future test_genericFunctionType_nonNullable() async { - await analyze(''' -void _f() { - void Function() x = _f; -} -'''); - var genericFunctionType = findNode.genericFunctionType('Function'); - visitTypeAnnotation(genericFunctionType, 'void Function()', - informative: {genericFunctionType: isExplainNonNullable}); - } - - Future test_genericFunctionType_nonNullable_by_context() async { - await analyze(''' -typedef F = void Function(); -'''); - var genericFunctionType = findNode.genericFunctionType('Function'); - visitTypeAnnotation(genericFunctionType, 'void Function()', - informative: isEmpty); - } - - Future test_genericFunctionType_nullable() async { - await analyze(''' -void _f() { - void Function() x = null; -} -'''); - var genericFunctionTypeAnnotation = - findNode.genericFunctionType('Function'); - visitTypeAnnotation(genericFunctionTypeAnnotation, 'void Function()?', - changes: {genericFunctionTypeAnnotation: isMakeNullable}); - } - - Future test_genericFunctionType_nullable_arg() async { - await analyze(''' -void Function(int/*?*/) _f() { - void Function(int) x = _g; - return x; -} -void _g(int/*?*/ x) {} -'''); - var intTypeAnnotation = findNode.namedType('int)'); - var genericFunctionTypeAnnotation = - findNode.genericFunctionType('Function(int)'); - visitTypeAnnotation(genericFunctionTypeAnnotation, 'void Function(int?)', - changes: {intTypeAnnotation: isMakeNullable}); - } - - Future test_genericFunctionType_nullable_return() async { - await analyze(''' -void _f() { - int Function() x = _g; -} -int/*?*/ _g() => null; -'''); - var intTypeAnnotation = findNode.namedType('int Function'); - var genericFunctionTypeAnnotation = - findNode.genericFunctionType('Function'); - visitTypeAnnotation(genericFunctionTypeAnnotation, 'int? Function()', - changes: {intTypeAnnotation: isMakeNullable}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_ifStatement_dead_else() async { - await analyze(''' -_f(int x, int/*?*/ y) { - if (x != null) { - print(x + 1); - } else { - print(y + 1); - } -} -'''); - var ifStatement = findNode.statement('if'); - visitStatement(ifStatement, changes: {findNode.simple('y +'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_ifStatement_dead_then() async { - await analyze(''' -_f(int x, int/*?*/ y) { - if (x == null) { - print(y + 1); - } else { - print(x + 1); - } -} -'''); - var ifStatement = findNode.statement('if'); - visitStatement(ifStatement, changes: {findNode.simple('y +'): isNullCheck}); - } - - Future test_ifStatement_flow_promote_in_else() async { - await analyze(''' -_f(int/*?*/ x) { - if (x == null) { - x + 1; - } else { - x + 2; - } -} -'''); - visitStatement(findNode.statement('if'), - changes: {findNode.simple('x + 1'): isNullCheck}); - } - - Future test_ifStatement_flow_promote_in_then() async { - await analyze(''' -_f(int/*?*/ x) { - if (x != null) { - x + 1; - } else { - x + 2; - } -} -'''); - visitStatement(findNode.statement('if'), - changes: {findNode.simple('x + 2'): isNullCheck}); - } - - Future test_ifStatement_flow_promote_in_then_no_else() async { - await analyze(''' -_f(int/*?*/ x) { - if (x != null) { - x + 1; - } -} -'''); - visitStatement(findNode.statement('if')); - } - - Future test_implicit_downcast() async { - await analyze('int _f(num x) => x;'); - var xRef = findNode.simple('x;'); - visitSubexpression(xRef, 'int', changes: { - xRef: isNodeChangeForExpression.havingIntroduceAsWithInfo( - 'int', - isInfo(NullabilityFixDescription.downcastExpression, - {FixReasonTarget.root: isEdge})) - }); - } - - Future test_import_IterableExtension_already_imported_add_show() async { - addPackageFile('collection', 'collection.dart', 'class PriorityQueue {}'); - await analyze(''' -import 'package:collection/collection.dart' show PriorityQueue; - -main() {} -'''); - visitAll(injectNeedsIterableExtension: true, changes: { - findNode.import('package:collection').combinators[0]: - isAddShowOfIterableExtension - }); - } - - Future test_import_IterableExtension_already_imported_all() async { - addPackageFile('collection', 'collection.dart', ''); - await analyze(''' -import 'package:collection/collection.dart'; - -main() {} -'''); - visitAll(injectNeedsIterableExtension: true, changes: {}); - } - - Future - test_import_IterableExtension_already_imported_and_shown() async { - addPackageFile('collection', 'collection.dart', - 'extension IterableExtension on Iterable {}'); - await analyze(''' -import 'package:collection/collection.dart' show IterableExtension; - -main() {} -'''); - visitAll(injectNeedsIterableExtension: true, changes: {}); - } - - Future test_import_IterableExtension_already_imported_prefixed() async { - addPackageFile('collection', 'collection.dart', ''); - await analyze(''' -import 'package:collection/collection.dart' as c; - -main() {} -'''); - visitAll( - injectNeedsIterableExtension: true, - changes: {findNode.unit: isAddImportOfIterableExtension}); - } - - Future test_import_IterableExtension_other_import() async { - addPackageFile( - 'foo', 'foo.dart', 'extension IterableExtension on Iterable {}'); - await analyze(''' -import 'package:foo/foo.dart' show IterableExtension; - -main() {} -'''); - visitAll( - injectNeedsIterableExtension: true, - changes: {findNode.unit: isAddImportOfIterableExtension}); - } - - Future test_import_IterableExtension_simple() async { - await analyze(''' -main() {} -'''); - visitAll( - injectNeedsIterableExtension: true, - changes: {findNode.unit: isAddImportOfIterableExtension}); - } - - Future test_indexExpression_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d, int/*?*/ i) => d[i]; -'''); - visitSubexpression(findNode.index('d[i]'), 'dynamic'); - } - - Future test_indexExpression_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - int operator[](int index) => 0; -} -f(C/*?*/ c) => c[0]; -'''); - var indexExpression = findNode.index('c[0]'); - visitSubexpression(indexExpression, 'int'); - } - - Future - test_indexExpression_extensionMember_allowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - int operator[](int index) => 0; -} -f(C/*?*/ c) => E(c)[0]; -'''); - var indexExpression = findNode.index('E(c)[0]'); - visitSubexpression(indexExpression, 'int'); - } - - Future test_indexExpression_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - int operator[](int index) => 0; -} -f(C/*?*/ c) => c[0]; -'''); - var indexExpression = findNode.index('c[0]'); - visitSubexpression(indexExpression, 'int', - changes: {indexExpression.target: isNullCheck}); - } - - Future - test_indexExpression_extensionMember_disallowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - int operator[](int index) => 0; -} -f(C/*?*/ c) => E(c)[0]; -'''); - var indexExpression = findNode.index('E(c)[0]'); - visitSubexpression(indexExpression, 'int', - changes: {findNode.simple('c)[0]'): isNullCheck}); - } - - Future test_indexExpression_simple() async { - await analyze(''' -class _C { - int operator[](String s) => 1; -} -_f(_C c) => c['foo']; -'''); - visitSubexpression(findNode.index('c['), 'int'); - } - - Future test_indexExpression_simple_check_lhs() async { - await analyze(''' -class _C { - int operator[](String s) => 1; -} -_f(_C/*?*/ c) => c['foo']; -'''); - visitSubexpression(findNode.index('c['), 'int', - changes: {findNode.simple('c['): isNullCheck}); - } - - Future test_indexExpression_simple_check_rhs() async { - await analyze(''' -class _C { - int operator[](String/*!*/ s) => 1; -} -_f(_C c, String/*?*/ s) => c[s]; -'''); - visitSubexpression(findNode.index('c['), 'int', - changes: {findNode.simple('s]'): isNullCheck}); - } - - Future test_indexExpression_substituted() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; -} -_f(_C c) => c['foo']; -'''); - visitSubexpression(findNode.index('c['), 'int'); - } - - Future test_indexExpression_substituted_check_rhs() async { - await analyze(''' -class _C { - T operator[](U/*!*/ u) => throw 'foo'; -} -_f(_C c, String/*?*/ s) => c[s]; -'''); - visitSubexpression(findNode.index('c['), 'int', - changes: {findNode.simple('s]'): isNullCheck}); - } - - Future test_indexExpression_substituted_no_check_rhs() async { - await analyze(''' -class _C { - T operator[](U u) => throw 'foo'; -} -_f(_C c, String/*?*/ s) => c[s]; -'''); - visitSubexpression(findNode.index('c['), 'int'); - } - - Future test_integerLiteral() async { - await analyze(''' -f() => 1; -'''); - visitSubexpression(findNode.integerLiteral('1'), 'int'); - } - - Future test_list_ifElement_alive() async { - await analyze(''' -_f(int x, bool b, int/*?*/ y) => [if (b) h(y) else g(y)]; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.listLiteral('['), 'List', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)]'): isNullCheck - }); - } - - Future test_list_ifElement_alive_with_null_check() async { - await analyze(''' -_f(int x, bool/*?*/ b, int/*?*/ y) => [if (b == null) h(y) else g(y)]; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.listLiteral('['), 'List', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)]'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_list_ifElement_dead_else() async { - await analyze(''' -_f(int x, int/*?*/ y) => [if (x != null) g(y) else h(y)]; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.listLiteral('['), 'List', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)]'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_list_ifElement_dead_else_no_else() async { - await analyze(''' -_f(int x, int/*?*/ y) => [if (x != null) g(y)]; -int/*!*/ g(int/*!*/ y) => y; -'''); - visitSubexpression(findNode.listLiteral('['), 'List', - changes: {findNode.simple('y)]'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_list_ifElement_dead_then() async { - await analyze(''' -_f(int x, int/*?*/ y) => [if (x == null) h(y) else g(y)]; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.listLiteral('['), 'List', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)]'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_list_ifElement_dead_then_no_else() async { - // TODO(paulberry): rather than infer the type to be List, - // FixBuilder should add an explicit type argument to ensure that it is - // still List. - await analyze(''' -_f(int x, int/*?*/ y) => [if (x == null) h(y)]; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.listLiteral('['), 'List', - changes: {findNode.simple('y)]'): isNullCheck}); - } - - Future test_list_make_explicit_type_nullable() async { - await analyze('_f() => [null];'); - // The `null` should be analyzed with a context type of `int?`, so it should - // not be null-checked. - visitSubexpression(findNode.listLiteral('['), 'List', - changes: {findNode.typeAnnotation('int'): isMakeNullable}); - } - - Future test_list_unchanged() async { - await analyze('_f(int x) => [x];'); - visitSubexpression(findNode.listLiteral('['), 'List'); - } - - Future test_listLiteral_typed() async { - await analyze(''' -_f() => []; -'''); - visitSubexpression(findNode.listLiteral('['), 'List'); - } - - Future test_listLiteral_typed_visit_contents() async { - await analyze(''' -_f(int/*?*/ x) => [x]; -'''); - visitSubexpression(findNode.listLiteral('['), 'List', - changes: {findNode.simple('x]'): isNullCheck}); - } - - Future test_map_ifElement_alive() async { - await analyze(''' -_f(int x, bool b, int/*?*/ y) => {if (b) 0: h(y) else 0: g(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - Future test_map_ifElement_alive_with_null_check() async { - await analyze(''' -_f(int x, bool/*?*/ b, int/*?*/ y) => {if (b == null) 0: h(y) else 0: g(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_map_ifElement_dead_else() async { - await analyze(''' -_f(int x, int/*?*/ y) => {if (x != null) 0: g(y) else 0: h(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_map_ifElement_dead_else_no_else() async { - await analyze(''' -_f(int x, int/*?*/ y) => {if (x != null) 0: g(y)}; -int/*!*/ g(int/*!*/ y) => y; -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: {findNode.simple('y)}'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_map_ifElement_dead_then() async { - await analyze(''' -_f(int x, int/*?*/ y) => {if (x == null) 0: h(y) else 0: g(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_map_ifElement_dead_then_no_else() async { - // TODO(paulberry): rather than infer the type to be Map, - // FixBuilder should add an explicit type argument to ensure that it is - // still Map. - await analyze(''' -_f(int x, int/*?*/ y) => {if (x == null) 0: h(y)}; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: {findNode.simple('y)}'): isNullCheck}); - } - - Future test_map_make_explicit_key_type_nullable() async { - await analyze('_f() => {null: 0.0};'); - // The `null` should be analyzed with a context type of `int?`, so it should - // not be null-checked. - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: {findNode.typeAnnotation('int'): isMakeNullable}); - } - - Future test_map_make_explicit_value_type_nullable() async { - await analyze('_f() => {0.0: null};'); - // The `null` should be analyzed with a context type of `int?`, so it should - // not be null-checked. - visitSubexpression(findNode.setOrMapLiteral('{'), 'Map', - changes: {findNode.typeAnnotation('int'): isMakeNullable}); - } - - Future test_methodInvocation_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d) => d.f(); -'''); - visitSubexpression(findNode.methodInvocation('d.f'), 'dynamic'); - } - - Future test_methodInvocation_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - void foo() {} -} -f(C/*?*/ c) => c.foo(); -'''); - var methodInvocation = findNode.methodInvocation('c.foo'); - visitSubexpression(methodInvocation, 'void'); - } - - Future - test_methodInvocation_extensionMember_allowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - void foo() {} -} -f(C/*?*/ c) => E(c).foo(); -'''); - var methodInvocation = findNode.methodInvocation('E(c).foo'); - visitSubexpression(methodInvocation, 'void'); - } - - Future test_methodInvocation_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - void foo() {} -} -f(C/*?*/ c) => c.foo(); -'''); - var methodInvocation = findNode.methodInvocation('c.foo'); - visitSubexpression(methodInvocation, 'void', - changes: {methodInvocation.target: isNullCheck}); - } - - Future - test_methodInvocation_extensionMember_disallowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - void foo() {} -} -f(C/*?*/ c) => E(c).foo(); -'''); - var methodInvocation = findNode.methodInvocation('E(c).foo'); - visitSubexpression(methodInvocation, 'void', - changes: {findNode.simple('c).foo'): isNullCheck}); - } - - Future test_methodInvocation_function_call_nullCheck() async { - await analyze(''' -f(void Function()/*?*/ x) => x.call(); -'''); - var methodInvocation = findNode.methodInvocation('x.call'); - visitSubexpression(methodInvocation, 'void', - changes: {methodInvocation.target: isNullCheck}); - } - - Future test_methodInvocation_namedParameter() async { - await analyze(''' -abstract class _C { - int f({int/*!*/ x}); -} -_f(_C c, int/*?*/ y) => c.f(x: y); -'''); - visitSubexpression(findNode.methodInvocation('c.f'), 'int', - changes: {findNode.simple('y);'): isNullCheck}); - } - - Future test_methodInvocation_ordinaryParameter() async { - await analyze(''' -abstract class _C { - int f(int/*!*/ x); -} -_f(_C c, int/*?*/ y) => c.f(y); -'''); - visitSubexpression(findNode.methodInvocation('c.f'), 'int', - changes: {findNode.simple('y);'): isNullCheck}); - } - - Future test_methodInvocation_return_nonNullable() async { - await analyze(''' -abstract class _C { - int f(); -} -_f(_C c) => c.f(); -'''); - visitSubexpression(findNode.methodInvocation('c.f'), 'int'); - } - - Future test_methodInvocation_return_nonNullable_check_target() async { - await analyze(''' -abstract class _C { - int f(); -} -_f(_C/*?*/ c) => c.f(); -'''); - visitSubexpression(findNode.methodInvocation('c.f'), 'int', - changes: {findNode.simple('c.f'): isNullCheck}); - } - - Future test_methodInvocation_return_nonNullable_nullAware() async { - await analyze(''' -abstract class _C { - int f(); -} -_f(_C/*?*/ c) => c?.f(); -'''); - visitSubexpression(findNode.methodInvocation('c?.f'), 'int?'); - } - - Future test_methodInvocation_return_nullable() async { - await analyze(''' -abstract class _C { - int/*?*/ f(); -} -_f(_C c) => c.f(); -'''); - visitSubexpression(findNode.methodInvocation('c.f'), 'int?'); - } - - Future test_methodInvocation_static() async { - await analyze(''' -_f() => _C.g(); -class _C { - static int g() => 1; -} -'''); - visitSubexpression(findNode.methodInvocation('_C.g();'), 'int'); - } - - Future test_methodInvocation_topLevel() async { - await analyze(''' -_f() => _g(); -int _g() => 1; -'''); - visitSubexpression(findNode.methodInvocation('_g();'), 'int'); - } - - Future test_methodInvocation_toString() async { - await analyze(''' -abstract class _C {} -_f(_C/*?*/ c) => c.toString(); -'''); - visitSubexpression(findNode.methodInvocation('c.toString'), 'String'); - } - - Future test_null_aware_assignment_non_nullable_source() async { - await analyze(''' -abstract class C { - int/*!*/ f(); - g(int/*!*/ x) { - x ??= f(); - } -} -'''); - var assignment = findNode.assignment('??='); - visitSubexpression(assignment, 'int', - changes: {assignment: isWeakNullAwareAssignment}); - } - - Future test_null_aware_assignment_nullable_rhs_needs_check() async { - await analyze(''' -abstract class C { - void set x(int/*!*/ value); - int/*?*/ get x; - int/*?*/ f(); - g() { - x ??= f(); - } -} -'''); - var assignment = findNode.assignment('??='); - visitSubexpression(assignment, 'int', - changes: {assignment.rightHandSide: isNullCheck}); - } - - Future test_null_aware_assignment_nullable_rhs_ok() async { - await analyze(''' -abstract class C { - int/*?*/ f(); - g(int/*?*/ x) { - x ??= f(); - } -} -'''); - var assignment = findNode.assignment('??='); - visitSubexpression(assignment, 'int?'); - } - - Future test_nullable_value_in_null_context() async { - await analyze('int/*!*/ f(int/*?*/ i) => i;'); - var iRef = findNode.simple('i;'); - visitSubexpression(iRef, 'int', changes: { - iRef: isNodeChangeForExpression.havingNullCheckWithInfo(isInfo( - NullabilityFixDescription.checkExpression, - {FixReasonTarget.root: TypeMatcher()})) - }); - } - - Future test_nullAssertion_promotes() async { - await analyze(''' -_f(bool/*?*/ x) => x && x; -'''); - // Only the first `x` is null-checked because thereafter, the type of `x` is - // promoted to `bool`. - visitSubexpression(findNode.binary('&&'), 'bool', - changes: {findNode.simple('x &&'): isNullCheck}); - } - - Future test_nullExpression_noValidMigration() async { - await analyze(''' -int/*!*/ f() => g(); -Null g() => null; -'''); - var invocation = findNode.methodInvocation('g();'); - // Note: in spite of the fact that we leave the method invocation alone, we - // analyze it as though it has type `Never`, because it's in a context where - // `null` doesn't work. - visitSubexpression(invocation, 'Never', changes: { - invocation: isNodeChangeForExpression.havingNoValidMigrationWithInfo( - isInfo(NullabilityFixDescription.noValidMigrationForNull, - {FixReasonTarget.root: TypeMatcher()})) - }); - } - - Future test_nullLiteral() async { - await analyze(''' -f() => null; -'''); - visitSubexpression(findNode.nullLiteral('null'), 'Null'); - } - - Future test_nullLiteral_hinted() async { - await analyze(''' -int/*!*/ f() => null/*!*/; -'''); - var literal = findNode.nullLiteral('null'); - // Normally we would leave the null literal alone and add an informative - // comment saying there's no valid migration for it. But since the user - // specifically hinted that `!` should be added, we respect that. - visitSubexpression(literal, 'Never', changes: { - literal: isNodeChangeForExpression.havingNullCheckWithInfo(isInfo( - NullabilityFixDescription.checkExpressionDueToHint, - {FixReasonTarget.root: TypeMatcher()})) - }); - } - - Future test_nullLiteral_noValidMigration() async { - await analyze(''' -int/*!*/ f() => null; -'''); - var literal = findNode.nullLiteral('null'); - // Note: in spite of the fact that we leave the literal as `null`, we - // analyze it as though it has type `Never`, because it's in a context where - // `null` doesn't work. - visitSubexpression(literal, 'Never', changes: { - literal: isNodeChangeForExpression.havingNoValidMigrationWithInfo(isInfo( - NullabilityFixDescription.noValidMigrationForNull, - {FixReasonTarget.root: TypeMatcher()})) - }); - } - - Future test_parenthesizedExpression() async { - await analyze(''' -f() => (1); -'''); - visitSubexpression(findNode.integerLiteral('1'), 'int'); - } - - Future test_parenthesizedExpression_flow() async { - await analyze(''' -_f(bool/*?*/ x) => ((x) != (null)) && x; -'''); - visitSubexpression(findNode.binary('&&'), 'bool'); - } - - Future test_post_decrement_int_behavior() async { - await analyze(''' -_f(int x) => x--; -'''); - // It's not a problem that int.operator- returns `num` (which is not - // assignable to `int`) because the value implicitly passed to operator- has - // type `int`, so the static type of the result is `int`. - visitSubexpression(findNode.postfix('--'), 'int'); - } - - Future test_post_increment_int_behavior() async { - await analyze(''' -_f(int x) => x++; -'''); - // It's not a problem that int.operator+ returns `num` (which is not - // assignable to `int`) because the value implicitly passed to operator- has - // type `int`, so the static type of the result is `int`. - visitSubexpression(findNode.postfix('++'), 'int'); - } - - Future test_post_increment_null_shorted_ok() async { - await analyze(''' -class C { - int/*!*/ x; -} -_f(C/*?*/ c) { - c?.x++; -} -'''); - // Even though c?.x is nullable, it should not be a problem to use it as the - // target of a post-increment, because null shorting will ensure that the - // increment only happens if c is non-null. - var increment = findNode.postfix('++'); - visitSubexpression(increment, 'int?'); - } - - Future test_post_increment_nullable_result_bad() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -f(C c) { - c++; -} -'''); - var increment = findNode.postfix('++'); - visitSubexpression(increment, 'C', changes: {increment: isBadCombinedType}); - } - - Future test_post_increment_nullable_result_ok() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -abstract class D { - void set x(C/*?*/ value); - C/*!*/ get x; - f() { - x++; - } -} -'''); - var increment = findNode.postfix('++'); - visitSubexpression(increment, 'C'); - } - - Future test_post_increment_nullable_source() async { - await analyze(''' -_f(int/*?*/ x) { - x++; -} -'''); - var increment = findNode.postfix('++'); - visitSubexpression(increment, 'int?', - changes: {increment: isNullableSource}); - } - - Future test_post_increment_potentially_nullable_source() async { - await analyze(''' -class C { - _f(T/*!*/ x) { - x++; - } -} -'''); - var increment = findNode.postfix('++'); - visitSubexpression(increment, 'T', changes: {increment: isNullableSource}); - } - - Future test_post_increment_promoted_ok() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -f(C/*?*/ x) { - if (x != null) { - x++; - } -} -'''); - // The increment is ok, because: - // - prior to the increment, x's value is promoted to non-nullable - // - the nullable return value of operator+ is ok to assign to x, because it - // un-does the promotion. - visitSubexpression(findNode.postfix('++'), 'C'); - } - - Future test_postfixExpression_combined_nullable_noProblem() async { - await analyze(''' -abstract class _C { - _D/*?*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*!*/ get x; - void set x(_C/*?*/ value); - f() => x++; -} -'''); - visitSubexpression(findNode.postfix('++'), '_C'); - } - - Future - test_postfixExpression_combined_nullable_noProblem_dynamic() async { - await analyze(''' -abstract class _E { - dynamic get x; - void set x(Object/*!*/ value); - f() => x++; -} -'''); - visitSubexpression(findNode.postfix('++'), 'dynamic'); - } - - @FailingTest(reason: 'TODO(paulberry)') - Future test_postfixExpression_combined_nullable_problem() async { - await analyze(''' -abstract class _C { - _D/*?*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*!*/ get x; - void set x(_C/*!*/ value); - f() => x++; -} -'''); - var postfix = findNode.postfix('++'); - visitSubexpression(postfix, '_C', problems: { - postfix: {const CompoundAssignmentCombinedNullable()} - }); - } - - Future test_postfixExpression_decrement_undoes_promotion() async { - await analyze(''' -abstract class _C { - _C/*?*/ operator-(int value); -} -_f(_C/*?*/ c) { // method - if (c != null) { - c--; - _g(c); - } -} -_g(_C/*!*/ c) {} -'''); - visitStatement(findNode.block('{ // method'), - changes: {findNode.simple('c);'): isNullCheck}); - } - - Future test_postfixExpression_dynamic() async { - await analyze(''' -_f(dynamic x) => x++; -'''); - visitSubexpression(findNode.postfix('++'), 'dynamic'); - } - - Future test_postfixExpression_increment_undoes_promotion() async { - await analyze(''' -abstract class _C { - _C/*?*/ operator+(int value); -} -_f(_C/*?*/ c) { // method - if (c != null) { - c++; - _g(c); - } -} -_g(_C/*!*/ c) {} -'''); - visitStatement(findNode.block('{ // method'), - changes: {findNode.simple('c);'): isNullCheck}); - } - - @FailingTest(reason: 'TODO(paulberry)') - Future test_postfixExpression_lhs_nullable_problem() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*?*/ get x; - void set x(_C/*?*/ value); - f() => x++; -} -'''); - var postfix = findNode.postfix('++'); - visitSubexpression(postfix, '_C?', problems: { - postfix: {const CompoundAssignmentReadNullable()} - }); - } - - Future test_postfixExpression_rhs_nonNullable() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -_f(_C/*!*/ x) => x++; -'''); - visitSubexpression(findNode.postfix('++'), '_C'); - } - - Future test_pre_decrement_int_behavior() async { - await analyze(''' -_f(int x) => --x; -'''); - // It's not a problem that int.operator- returns `num` (which is not - // assignable to `int`) because the value implicitly passed to operator- has - // type `int`, so the static type of the result is `int`. - visitSubexpression(findNode.prefix('--'), 'int'); - } - - Future test_pre_increment_int_behavior() async { - await analyze(''' -_f(int x) => ++x; -'''); - // It's not a problem that int.operator+ returns `num` (which is not - // assignable to `int`) because the value implicitly passed to operator- has - // type `int`, so the static type of the result is `int`. - visitSubexpression(findNode.prefix('++'), 'int'); - } - - Future test_pre_increment_null_shorted_ok() async { - await analyze(''' -class C { - int/*!*/ x; -} -_f(C/*?*/ c) { - ++c?.x; -} -'''); - // Even though c?.x is nullable, it should not be a problem to use it as the - // target of a pre-increment, because null shorting will ensure that the - // increment only happens if c is non-null. - var increment = findNode.prefix('++'); - visitSubexpression(increment, 'int?'); - } - - Future test_pre_increment_nullable_result_bad() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -f(C c) { - ++c; -} -'''); - var increment = findNode.prefix('++'); - visitSubexpression(increment, 'C?', - changes: {increment: isBadCombinedType}); - } - - Future test_pre_increment_nullable_result_ok() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -abstract class D { - void set x(C/*?*/ value); - C/*!*/ get x; - f() { - ++x; - } -} -'''); - var increment = findNode.prefix('++'); - visitSubexpression(increment, 'C?'); - } - - Future test_pre_increment_nullable_source() async { - await analyze(''' -_f(int/*?*/ x) { - ++x; -} -'''); - var increment = findNode.prefix('++'); - visitSubexpression(increment, 'int', - changes: {increment: isNullableSource}); - } - - Future test_pre_increment_potentially_nullable_source() async { - await analyze(''' -class C { - _f(T/*!*/ x) { - ++x; - } -} -'''); - var increment = findNode.prefix('++'); - visitSubexpression(increment, 'num', - changes: {increment: isNullableSource}); - } - - Future test_pre_increment_promoted_ok() async { - await analyze(''' -abstract class C { - C/*?*/ operator+(int i); -} -f(C/*?*/ x) { - if (x != null) { - ++x; - } -} -'''); - // The increment is ok, because: - // - prior to the increment, x's value is promoted to non-nullable - // - the nullable return value of operator+ is ok to assign to x, because it - // un-does the promotion. - visitSubexpression(findNode.prefix('++'), 'C?'); - } - - Future test_prefixedIdentifier_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d) => d.x; -'''); - visitSubexpression(findNode.prefixed('d.x'), 'dynamic'); - } - - Future test_prefixedIdentifier_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - int get foo => 0; -} -f(C/*?*/ c) => c.foo; -'''); - var prefixedIdentifier = findNode.prefixed('c.foo'); - visitSubexpression(prefixedIdentifier, 'int'); - } - - Future test_prefixedIdentifier_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - int get foo => 0; -} -f(C/*?*/ c) => c.foo; -'''); - var prefixedIdentifier = findNode.prefixed('c.foo'); - visitSubexpression(prefixedIdentifier, 'int', - changes: {prefixedIdentifier.prefix: isNullCheck}); - } - - Future test_prefixedIdentifier_field_nonNullable() async { - await analyze(''' -class _C { - int/*!*/ x = 0; -} -_f(_C c) => c.x; -'''); - visitSubexpression(findNode.prefixed('c.x'), 'int'); - } - - Future test_prefixedIdentifier_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ x = 0; -} -_f(_C c) => c.x; -'''); - visitSubexpression(findNode.prefixed('c.x'), 'int?'); - } - - Future test_prefixedIdentifier_getter_check_lhs() async { - await analyze(''' -abstract class _C { - int get x; -} -_f(_C/*?*/ c) => c.x; -'''); - visitSubexpression(findNode.prefixed('c.x'), 'int', - changes: {findNode.simple('c.x'): isNullCheck}); - } - - Future test_prefixedIdentifier_getter_nonNullable() async { - await analyze(''' -abstract class _C { - int/*!*/ get x; -} -_f(_C c) => c.x; -'''); - visitSubexpression(findNode.prefixed('c.x'), 'int'); - } - - Future test_prefixedIdentifier_getter_nullable() async { - await analyze(''' -abstract class _C { - int/*?*/ get x; -} -_f(_C c) => c.x; -'''); - visitSubexpression(findNode.prefixed('c.x'), 'int?'); - } - - Future test_prefixedIdentifier_object_getter() async { - await analyze(''' -class _C {} -_f(_C/*?*/ c) => c.hashCode; -'''); - visitSubexpression(findNode.prefixed('c.hashCode'), 'int'); - } - - Future test_prefixedIdentifier_object_tearoff() async { - await analyze(''' -class _C {} -_f(_C/*?*/ c) => c.toString; -'''); - visitSubexpression(findNode.prefixed('c.toString'), 'String Function()'); - } - - Future test_prefixedIdentifier_substituted() async { - await analyze(''' -abstract class _C { - List get x; -} -_f(_C c) => c.x; -'''); - visitSubexpression(findNode.prefixed('c.x'), 'List'); - } - - Future test_prefixExpression_bang_flow() async { - await analyze(''' -_f(int/*?*/ x) { - if (!(x == null)) { - x + 1; - } -} -'''); - // No null check should be needed on `x + 1` because `!(x == null)` promotes - // x's type to `int`. - visitStatement(findNode.statement('if')); - } - - Future test_prefixExpression_bang_nonNullable() async { - await analyze(''' -_f(bool/*!*/ x) => !x; -'''); - visitSubexpression(findNode.prefix('!x'), 'bool'); - } - - Future test_prefixExpression_bang_nullable() async { - await analyze(''' -_f(bool/*?*/ x) => !x; -'''); - visitSubexpression(findNode.prefix('!x'), 'bool', - changes: {findNode.simple('x;'): isNullCheck}); - } - - Future test_prefixExpression_combined_nullable_noProblem() async { - await analyze(''' -abstract class _C { - _D/*?*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*!*/ get x; - void set x(_C/*?*/ value); - f() => ++x; -} -'''); - visitSubexpression(findNode.prefix('++'), '_D?'); - } - - Future - test_prefixExpression_combined_nullable_noProblem_dynamic() async { - await analyze(''' -abstract class _E { - dynamic get x; - void set x(Object/*!*/ value); - f() => ++x; -} -'''); - var prefix = findNode.prefix('++'); - visitSubexpression(prefix, 'dynamic'); - } - - @FailingTest(reason: 'TODO(paulberry)') - Future test_prefixExpression_combined_nullable_problem() async { - await analyze(''' -abstract class _C { - _D/*?*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*!*/ get x; - void set x(_C/*!*/ value); - f() => ++x; -} -'''); - var prefix = findNode.prefix('++'); - visitSubexpression(prefix, '_D', problems: { - prefix: {const CompoundAssignmentCombinedNullable()} - }); - } - - Future test_prefixExpression_decrement_undoes_promotion() async { - await analyze(''' -abstract class _C { - _C/*?*/ operator-(int value); -} -_f(_C/*?*/ c) { // method - if (c != null) { - --c; - _g(c); - } -} -_g(_C/*!*/ c) {} -'''); - visitStatement(findNode.block('{ // method'), - changes: {findNode.simple('c);'): isNullCheck}); - } - - Future test_prefixExpression_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - C operator-() => C(); -} -f(C/*?*/ c) => -c; -'''); - var prefixExpression = findNode.prefix('-c'); - visitSubexpression(prefixExpression, 'C'); - } - - Future - test_prefixExpression_extensionMember_allowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - C operator-() => C(); -} -f(C/*?*/ c) => -E(c); -'''); - var prefixExpression = findNode.prefix('-E(c)'); - visitSubexpression(prefixExpression, 'C'); - } - - Future test_prefixExpression_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - C operator-() => C(); -} -f(C/*?*/ c) => -c; -'''); - var prefixExpression = findNode.prefix('-c'); - visitSubexpression(prefixExpression, 'C', - changes: {prefixExpression.operand: isNullCheck}); - } - - Future - test_prefixExpression_extensionMember_disallowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - C operator-() => C(); -} -f(C/*?*/ c) => -E(c); -'''); - var prefixExpression = findNode.prefix('-E(c)'); - visitSubexpression(prefixExpression, 'C', - changes: {findNode.simple('c);'): isNullCheck}); - } - - Future test_prefixExpression_increment_undoes_promotion() async { - await analyze(''' -abstract class _C { - _C/*?*/ operator+(int value); -} -_f(_C/*?*/ c) { // method - if (c != null) { - ++c; - _g(c); - } -} -_g(_C/*!*/ c) {} -'''); - visitStatement(findNode.block('{ // method'), - changes: {findNode.simple('c);'): isNullCheck}); - } - - Future test_prefixExpression_intRules() async { - await analyze(''' -_f(int x) => ++x; -'''); - visitSubexpression(findNode.prefix('++'), 'int'); - } - - @FailingTest(reason: 'TODO(paulberry)') - Future test_prefixExpression_lhs_nullable_problem() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -abstract class _E { - _C/*?*/ get x; - void set x(_C/*?*/ value); - f() => ++x; -} -'''); - var prefix = findNode.prefix('++'); - visitSubexpression(prefix, '_D', problems: { - prefix: {const CompoundAssignmentReadNullable()} - }); - } - - Future test_prefixExpression_minus_dynamic() async { - await analyze(''' -_f(dynamic x) => -x; -'''); - visitSubexpression(findNode.prefix('-x'), 'dynamic'); - } - - Future test_prefixExpression_minus_nonNullable() async { - await analyze(''' -_f(int/*!*/ x) => -x; -'''); - visitSubexpression(findNode.prefix('-x'), 'int'); - } - - Future test_prefixExpression_minus_nullable() async { - await analyze(''' -_f(int/*?*/ x) => -x; -'''); - visitSubexpression(findNode.prefix('-x'), 'int', - changes: {findNode.simple('x;'): isNullCheck}); - } - - Future test_prefixExpression_minus_substitution() async { - await analyze(''' -abstract class _C { - List operator-(); -} -_f(_C x) => -x; -'''); - visitSubexpression(findNode.prefix('-x'), 'List'); - } - - Future test_prefixExpression_rhs_nonNullable() async { - await analyze(''' -abstract class _C { - _D/*!*/ operator+(int/*!*/ value); -} -abstract class _D extends _C {} -_f(_C/*!*/ x) => ++x; -'''); - visitSubexpression(findNode.prefix('++'), '_D'); - } - - Future test_prefixExpression_tilde_dynamic() async { - await analyze(''' -_f(dynamic x) => ~x; -'''); - visitSubexpression(findNode.prefix('~x'), 'dynamic'); - } - - Future test_prefixExpression_tilde_nonNullable() async { - await analyze(''' -_f(int/*!*/ x) => ~x; -'''); - visitSubexpression(findNode.prefix('~x'), 'int'); - } - - Future test_prefixExpression_tilde_nullable() async { - await analyze(''' -_f(int/*?*/ x) => ~x; -'''); - visitSubexpression(findNode.prefix('~x'), 'int', - changes: {findNode.simple('x;'): isNullCheck}); - } - - Future test_prefixExpression_tilde_substitution() async { - await analyze(''' -abstract class _C { - List operator~(); -} -_f(_C x) => ~x; -'''); - visitSubexpression(findNode.prefix('~x'), 'List'); - } - - Future test_propertyAccess_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d) => (d).x; -'''); - visitSubexpression(findNode.propertyAccess('(d).x'), 'dynamic'); - } - - Future test_propertyAccess_extensionMember_allowsNull() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - int get foo => 0; -} -f(C/*?*/ Function() g) => g().foo; -'''); - var propertyAccess = findNode.propertyAccess('g().foo'); - visitSubexpression(propertyAccess, 'int'); - } - - Future test_propertyAccess_extensionMember_allowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*?*/ { - int get foo => 0; -} -f(C/*?*/ Function() g) => E(g()).foo; -'''); - var propertyAccess = findNode.propertyAccess('E(g()).foo'); - visitSubexpression(propertyAccess, 'int'); - } - - Future test_propertyAccess_extensionMember_disallowsNull() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - int get foo => 0; -} -f(C/*?*/ Function() g) => g().foo; -'''); - var propertyAccess = findNode.propertyAccess('g().foo'); - visitSubexpression(propertyAccess, 'int', - changes: {propertyAccess.target: isNullCheck}); - } - - Future - test_propertyAccess_extensionMember_disallowsNull_explicit() async { - await analyze(''' -class C {} -extension E on C/*!*/ { - int get foo => 0; -} -f(C/*?*/ Function() g) => E(g()).foo; -'''); - var propertyAccess = findNode.propertyAccess('E(g()).foo'); - visitSubexpression(propertyAccess, 'int', changes: { - findNode.functionExpressionInvocation('g()).foo'): isNullCheck - }); - } - - Future test_propertyAccess_field_nonNullable() async { - await analyze(''' -class _C { - int/*!*/ x = 0; -} -_f(_C c) => (c).x; -'''); - visitSubexpression(findNode.propertyAccess('(c).x'), 'int'); - } - - Future test_propertyAccess_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ x = 0; -} -_f(_C c) => (c).x; -'''); - visitSubexpression(findNode.propertyAccess('(c).x'), 'int?'); - } - - Future test_propertyAccess_getter_check_lhs() async { - await analyze(''' -abstract class _C { - int get x; -} -_f(_C/*?*/ c) => (c).x; -'''); - visitSubexpression(findNode.propertyAccess('(c).x'), 'int', - changes: {findNode.parenthesized('(c).x'): isNullCheck}); - } - - Future test_propertyAccess_getter_nonNullable() async { - await analyze(''' -abstract class _C { - int/*!*/ get x; -} -_f(_C c) => (c).x; -'''); - visitSubexpression(findNode.propertyAccess('(c).x'), 'int'); - } - - Future test_propertyAccess_getter_nullable() async { - await analyze(''' -abstract class _C { - int/*?*/ get x; -} -_f(_C c) => (c).x; -'''); - visitSubexpression(findNode.propertyAccess('(c).x'), 'int?'); - } - - Future test_propertyAccess_nullAware_dynamic() async { - await analyze(''' -Object/*!*/ _f(dynamic d) => d?.x; -'''); - visitSubexpression(findNode.propertyAccess('d?.x'), 'dynamic'); - } - - Future test_propertyAccess_nullAware_field_nonNullable() async { - await analyze(''' -class _C { - int/*!*/ x = 0; -} -_f(_C/*?*/ c) => c?.x; -'''); - visitSubexpression(findNode.propertyAccess('c?.x'), 'int?'); - } - - Future test_propertyAccess_nullAware_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ x = 0; -} -_f(_C/*?*/ c) => c?.x; -'''); - visitSubexpression(findNode.propertyAccess('c?.x'), 'int?'); - } - - Future test_propertyAccess_nullAware_getter_nonNullable() async { - await analyze(''' -abstract class _C { - int/*!*/ get x; -} -_f(_C/*?*/ c) => c?.x; -'''); - visitSubexpression(findNode.propertyAccess('c?.x'), 'int?'); - } - - Future test_propertyAccess_nullAware_getter_nullable() async { - await analyze(''' -abstract class _C { - int/*?*/ get x; -} -_f(_C/*?*/ c) => c?.x; -'''); - visitSubexpression(findNode.propertyAccess('c?.x'), 'int?'); - } - - Future test_propertyAccess_nullAware_object_getter() async { - await analyze(''' -class _C {} -_f(_C/*?*/ c) => c?.hashCode; -'''); - visitSubexpression(findNode.propertyAccess('c?.hashCode'), 'int?'); - } - - Future test_propertyAccess_nullAware_object_tearoff() async { - await analyze(''' -class _C {} -_f(_C/*?*/ c) => c?.toString; -'''); - visitSubexpression( - findNode.propertyAccess('c?.toString'), 'String Function()?'); - } - - Future test_propertyAccess_nullAware_potentiallyNullable() async { - // In the code example below, the `?.` is not changed to `.` because `T` - // might be instantiated to `int?`, in which case the null check is still - // needed. - await analyze(''' -class C { - f(T t) => t?.isEven; -} -'''); - visitSubexpression(findNode.propertyAccess('?.'), 'bool?'); - } - - Future test_propertyAccess_nullAware_removeNullAwareness() async { - await analyze('_f(int/*!*/ i) => i?.isEven;'); - var propertyAccess = findNode.propertyAccess('?.'); - visitSubexpression(propertyAccess, 'bool', - changes: {propertyAccess: isRemoveNullAwareness}); - } - - Future - test_propertyAccess_nullAware_removeNullAwareness_nullCheck() async { - await analyze(''' -class C { - int/*?*/ i; -} -int/*!*/ f(C/*!*/ c) => c?.i; -'''); - var propertyAccess = findNode.propertyAccess('?.'); - visitSubexpression(propertyAccess, 'int', changes: { - propertyAccess: TypeMatcher() - .havingNullCheckWithInfo(isNotNull) - .having( - (c) => c.nullAwarenessRemoval != NullAwarenessRemovalType.none, - 'removeNullAwareness', - true) - }); - } - - Future test_propertyAccess_nullAware_substituted() async { - await analyze(''' -abstract class _C { - List get x; -} -_f(_C/*?*/ c) => c?.x; -'''); - visitSubexpression(findNode.propertyAccess('c?.x'), 'List?'); - } - - Future test_propertyAccess_object_getter() async { - await analyze(''' -class _C {} -_f(_C/*?*/ c) => (c).hashCode; -'''); - visitSubexpression(findNode.propertyAccess('(c).hashCode'), 'int'); - } - - Future test_propertyAccess_object_tearoff() async { - await analyze(''' -class _C {} -_f(_C/*?*/ c) => (c).toString; -'''); - visitSubexpression( - findNode.propertyAccess('(c).toString'), 'String Function()'); - } - - Future test_propertyAccess_substituted() async { - await analyze(''' -abstract class _C { - List get x; -} -_f(_C c) => (c).x; -'''); - visitSubexpression(findNode.propertyAccess('(c).x'), 'List'); - } - - Future test_removeLanguageVersionComment() async { - await analyze(''' -// ignore: illegal_language_version_override -// @dart = 2.6 -void main() {} -'''); - visitAll(changes: {findNode.unit: isRemoveLanguageVersion}); - } - - Future test_removeLanguageVersionComment_withCopyright() async { - await analyze(''' -// Some copyright notice here... -// ignore: illegal_language_version_override -// @dart = 2.6 -void main() {} -'''); - visitAll(changes: {findNode.unit: isRemoveLanguageVersion}); - } - - Future test_set_ifElement_alive() async { - await analyze(''' -_f(int x, bool b, int/*?*/ y) => {if (b) h(y) else g(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - Future test_set_ifElement_alive_with_null_check() async { - await analyze(''' -_f(int x, bool/*?*/ b, int/*?*/ y) => {if (b == null) h(y) else g(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_set_ifElement_dead_else() async { - await analyze(''' -_f(int x, int/*?*/ y) => {if (x != null) g(y) else h(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_set_ifElement_dead_else_no_else() async { - await analyze(''' -_f(int x, int/*?*/ y) => {if (x != null) g(y)}; -int/*!*/ g(int/*!*/ y) => y; -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', - changes: {findNode.simple('y)}'): isNullCheck}); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_set_ifElement_dead_then() async { - await analyze(''' -_f(int x, int/*?*/ y) => {if (x == null) h(y) else g(y)}; -int/*!*/ g(int/*!*/ y) => y; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', changes: { - findNode.simple('y) else'): isNullCheck, - findNode.simple('y)}'): isNullCheck - }); - } - - /* TODO(yanok): not dead anymore, remove? */ - Future test_set_ifElement_dead_then_no_else() async { - // TODO(paulberry): rather than infer the type to be Map, - // FixBuilder should add an explicit type argument to ensure that it is - // still Set. - await analyze(''' -_f(int x, int/*?*/ y) => {if (x == null) h(y)}; -double/*!*/ h(int/*!*/ y) => y.toDouble(); -'''); - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', - changes: {findNode.simple('y)}'): isNullCheck}); - } - - Future test_set_make_explicit_type_nullable() async { - await analyze('_f() => {null};'); - // The `null` should be analyzed with a context type of `int?`, so it should - // not be null-checked. - visitSubexpression(findNode.setOrMapLiteral('{'), 'Set', - changes: {findNode.typeAnnotation('int'): isMakeNullable}); - } - - Future test_simpleIdentifier_className() async { - await analyze(''' -_f() => int; -'''); - visitSubexpression(findNode.simple('int'), 'Type'); - } - - Future test_simpleIdentifier_field() async { - await analyze(''' -class _C { - int i = 1; - f() => i; -} -'''); - visitSubexpression(findNode.simple('i;'), 'int'); - } - - Future test_simpleIdentifier_field_generic() async { - await analyze(''' -class _C { - List x = null; - f() => x; -} -'''); - visitSubexpression(findNode.simple('x;'), 'List?'); - } - - Future test_simpleIdentifier_field_nullable() async { - await analyze(''' -class _C { - int/*?*/ i = 1; - f() => i; -} -'''); - visitSubexpression(findNode.simple('i;'), 'int?'); - } - - Future test_simpleIdentifier_getter() async { - await analyze(''' -class _C { - int get i => 1; - f() => i; -} -'''); - visitSubexpression(findNode.simple('i;'), 'int'); - } - - Future test_simpleIdentifier_getter_nullable() async { - await analyze(''' -class _C { - int/*?*/ get i => 1; - f() => i; -} -'''); - visitSubexpression(findNode.simple('i;'), 'int?'); - } - - Future test_simpleIdentifier_localVariable_nonNullable() async { - await analyze(''' -_f(int x) { - return x; -} -'''); - visitSubexpression(findNode.simple('x;'), 'int'); - } - - Future test_simpleIdentifier_localVariable_nullable() async { - await analyze(''' -_f(int/*?*/ x) { - return x; -} -'''); - visitSubexpression(findNode.simple('x;'), 'int?'); - } - - Future test_simpleIdentifier_null_check_hint() async { - await analyze('int/*?*/ _f(int/*?*/ x) => x/*!*/;'); - var xRef = findNode.simple('x/*!*/'); - visitSubexpression(xRef, 'int', changes: { - xRef: isNodeChangeForExpression.havingNullCheckWithInfo(isInfo( - NullabilityFixDescription.checkExpressionDueToHint, - {FixReasonTarget.root: TypeMatcher()})) - }); - } - - Future test_stringLiteral() async { - await analyze(''' -f() => 'foo'; -'''); - visitSubexpression(findNode.stringLiteral("'foo'"), 'String'); - } - - Future test_suspicious_cast() async { - await analyze(''' -int f(Object o) { - if (o is! String) return 0; - return o; -} -'''); - var xRef = findNode.simple('o;'); - visitSubexpression(xRef, 'int', changes: {xRef: isNoValidMigration}); - } - - Future test_symbolLiteral() async { - await analyze(''' -f() => #foo; -'''); - visitSubexpression(findNode.symbolLiteral('#foo'), 'Symbol'); - } - - Future test_throw_flow() async { - await analyze(''' -_f(int/*?*/ i) { - if (i == null) throw 'foo'; - i + 1; -} -'''); - visitStatement(findNode.block('{')); - } - - Future test_throw_nullable() async { - await analyze(''' -_f(int/*?*/ i) => throw i; -'''); - visitSubexpression(findNode.throw_('throw'), 'Never', - changes: {findNode.simple('i;'): isNullCheck}); - } - - Future test_throw_simple() async { - await analyze(''' -_f() => throw 'foo'; -'''); - visitSubexpression(findNode.throw_('throw'), 'Never'); - } - - Future test_typeName_dynamic() async { - await analyze(''' -void _f() { - dynamic d = null; -} -'''); - visitTypeAnnotation(findNode.typeAnnotation('dynamic'), 'dynamic'); - } - - Future test_typeName_futureOr_dynamic_nullable() async { - await analyze(''' -import 'dart:async'; -void _f() { - FutureOr x = null; -} -'''); - // The type of `x` should be `FutureOr?`, but this is equivalent to - // `FutureOr`, so we don't add a `?`. Note: expected type is - // still `FutureOr?`; we don't go to extra effort to remove the - // redundant `?` from the internal type representation, just from the source - // code we generate. - visitTypeAnnotation( - findNode.typeAnnotation('FutureOr x'), 'FutureOr?', - changes: {}); - } - - Future test_typeName_futureOr_inner() async { - await analyze(''' -import 'dart:async'; -void _f(FutureOr x) { - FutureOr y = x; -} -'''); - visitTypeAnnotation( - findNode.typeAnnotation('FutureOr y'), 'FutureOr', - changes: {findNode.typeAnnotation('int> y'): isMakeNullable}); - } - - Future test_typeName_futureOr_null_nullable() async { - await analyze(''' -import 'dart:async'; -void _f() { - FutureOr x = null; -} -'''); - // The type of `x` should be `FutureOr?`, but this is equivalent to - // `FutureOr`, so we don't add a `?`. Note: expected type is - // still `FutureOr?`; we don't go to extra effort to remove the - // redundant `?` from the internal type representation, just from the source - // code we generate. - visitTypeAnnotation( - findNode.typeAnnotation('FutureOr x'), 'FutureOr?', - changes: {}); - } - - Future test_typeName_futureOr_outer() async { - await analyze(''' -import 'dart:async'; -void _f(FutureOr/*?*/ x) { - FutureOr y = x; -} -'''); - var typeAnnotation = findNode.typeAnnotation('FutureOr y'); - visitTypeAnnotation(typeAnnotation, 'FutureOr?', - changes: {typeAnnotation: isMakeNullable}); - } - - Future test_typeName_futureOr_redundant() async { - await analyze(''' -import 'dart:async'; -void _f(bool b, FutureOr/*?*/ x, FutureOr y) { - FutureOr z = b ? x : y; -} -'''); - // The type of `z` should be `FutureOr?`, but this is equivalent to - // `FutureOr`, so we only add the first `?`. Note: expected type is - // still `FutureOr?`; we don't go to extra effort to remove the - // redundant `?` from the internal type representation, just from the source - // code we generate. - visitTypeAnnotation( - findNode.typeAnnotation('FutureOr z'), 'FutureOr?', - changes: {findNode.typeAnnotation('int> z'): isMakeNullable}); - } - - Future test_typeName_futureOr_void_nullable() async { - await analyze(''' -import 'dart:async'; -void _f() { - FutureOr x = null; -} -'''); - // The type of `x` should be `FutureOr?`, but this is equivalent to - // `FutureOr`, so we don't add a `?`. Note: expected type is - // still `FutureOr?`; we don't go to extra effort to remove the - // redundant `?` from the internal type representation, just from the source - // code we generate. - visitTypeAnnotation( - findNode.typeAnnotation('FutureOr x'), 'FutureOr?', - changes: {}); - } - - Future test_typeName_generic_nonNullable() async { - await analyze(''' -void _f() { - List i = [0]; -} -'''); - visitTypeAnnotation(findNode.typeAnnotation('List'), 'List'); - } - - Future test_typeName_generic_nullable() async { - await analyze(''' -void _f() { - List i = null; -} -'''); - var listIntAnnotation = findNode.typeAnnotation('List'); - visitTypeAnnotation(listIntAnnotation, 'List?', - changes: {listIntAnnotation: isMakeNullable}); - } - - Future test_typeName_generic_nullable_arg() async { - await analyze(''' -void _f() { - List i = [null]; -} -'''); - visitTypeAnnotation(findNode.typeAnnotation('List'), 'List', - changes: {findNode.typeAnnotation('int'): isMakeNullable}); - } - - Future test_typeName_generic_nullable_arg_and_outer() async { - await analyze(''' -void _f(bool b) { - List i = b ? [null] : null; -} -'''); - var listInt = findNode.typeAnnotation('List'); - visitTypeAnnotation(listInt, 'List?', changes: { - findNode.typeAnnotation('int'): isMakeNullable, - listInt: isMakeNullable - }); - } - - Future test_typeName_simple_nonNullable() async { - await analyze(''' -void _f() { - int i = 0; -} -'''); - var typeAnnotation = findNode.typeAnnotation('int'); - visitTypeAnnotation(typeAnnotation, 'int', - informative: {typeAnnotation: isExplainNonNullable}); - } - - Future test_typeName_simple_nonNullable_by_context() async { - await analyze(''' -class C extends Object {} -'''); - visitTypeAnnotation(findNode.typeAnnotation('Object'), 'Object', - informative: isEmpty); - } - - Future test_typeName_simple_nullable() async { - await analyze(''' -void _f() { - int i = null; -} -'''); - var intAnnotation = findNode.typeAnnotation('int'); - visitTypeAnnotation(intAnnotation, 'int?', - changes: {intAnnotation: isMakeNullable}); - } - - Future test_typeName_void() async { - await analyze(''' -void _f() { - return; -} -'''); - visitTypeAnnotation(findNode.typeAnnotation('void'), 'void'); - } - - Future test_use_of_dynamic() async { - // Use of `dynamic` in a context requiring non-null is not explicitly null - // checked. - await analyze(''' -bool _f(dynamic d, bool b) => d && b; -'''); - visitSubexpression(findNode.binary('&&'), 'bool'); - } - - Future test_variableDeclaration_typed_initialized_nonNullable() async { - await analyze(''' -void _f() { - int x = 0; -} -'''); - visitStatement(findNode.statement('int x')); - } - - Future test_variableDeclaration_typed_initialized_nullable() async { - await analyze(''' -void _f() { - int x = null; -} -'''); - visitStatement(findNode.statement('int x'), - changes: {findNode.typeAnnotation('int'): isMakeNullable}); - } - - Future test_variableDeclaration_typed_uninitialized() async { - await analyze(''' -void _f() { - int x; -} -'''); - visitStatement(findNode.statement('int x')); - } - - Future test_variableDeclaration_untyped_initialized() async { - await analyze(''' -void _f() { - var x = 0; -} -'''); - visitStatement(findNode.statement('var x')); - } - - Future test_variableDeclaration_untyped_uninitialized() async { - await analyze(''' -void _f() { - var x; -} -'''); - visitStatement(findNode.statement('var x')); - } - - Future test_variableDeclaration_visit_initializer() async { - await analyze(''' -void _f(bool/*?*/ x, bool/*?*/ y) { - bool z = x && y; -} -'''); - visitStatement(findNode.statement('bool z'), changes: { - findNode.simple('x &&'): isNullCheck, - findNode.simple('y;'): isNullCheck - }); - } - - Future test_where_transform() async { - await analyze(''' -Iterable _f(Iterable x) => x.where((e) => e != null); -'''); - var methodInvocation = findNode.methodInvocation('where'); - var functionExpression = findNode.functionExpression('(e) => e != null'); - var fixBuilder = - visitSubexpression(methodInvocation, 'Iterable', changes: { - methodInvocation.methodName: isMethodNameChange('whereNotNull'), - methodInvocation.argumentList: - isDropArgument({functionExpression: anything}), - }); - expect(fixBuilder.neededCollectionPackageExtensions, - {'IterableNullableExtension'}); - } - - void visitAll( - {Map changes = const {}, - Map> problems = const >{}, - bool injectNeedsIterableExtension = false}) { - var fixBuilder = _createFixBuilder(testUnit!); - if (injectNeedsIterableExtension) { - fixBuilder.neededCollectionPackageExtensions.add('IterableExtension'); - } - fixBuilder.visitAll(); - expect(scopedChanges(fixBuilder, testUnit), changes); - expect(scopedProblems(fixBuilder, testUnit), problems); - } - - void visitAssignmentTarget( - Expression node, String? expectedReadType, String expectedWriteType, - {Map changes = const {}, - Map> problems = const >{}}) { - var fixBuilder = _createFixBuilder(node); - fixBuilder.visitAll(); - var targetInfo = _computeAssignmentTargetInfo(node, fixBuilder); - if (expectedReadType == null) { - expect(targetInfo.readType, null); - } else { - expect(targetInfo.readType!.getDisplayString(withNullability: true), - expectedReadType); - } - expect(targetInfo.writeType!.getDisplayString(withNullability: true), - expectedWriteType); - expect(scopedChanges(fixBuilder, node), changes); - expect(scopedProblems(fixBuilder, node), problems); - } - - void visitStatement(Statement node, - {Map changes = const {}, - Map> problems = const >{}}) { - var fixBuilder = _createFixBuilder(node); - fixBuilder.visitAll(); - expect(scopedChanges(fixBuilder, node), changes); - expect(scopedProblems(fixBuilder, node), problems); - } - - FixBuilder visitSubexpression(Expression node, String expectedType, - {Map changes = const {}, - Map> problems = const >{}, - bool warnOnWeakCode = false}) { - var fixBuilder = _createFixBuilder(node, warnOnWeakCode: warnOnWeakCode); - fixBuilder.visitAll(); - var type = node.staticType!; - expect(type.getDisplayString(withNullability: true), expectedType); - expect(scopedChanges(fixBuilder, node), changes); - expect(scopedProblems(fixBuilder, node), problems); - return fixBuilder; - } - - void visitTypeAnnotation(TypeAnnotation node, String expectedType, - {Map changes = const {}, - Map> problems = const >{}, - dynamic informative = anything}) { - var fixBuilder = _createFixBuilder(node); - fixBuilder.visitAll(); - var type = node.type!; - expect(type.getDisplayString(withNullability: true), expectedType); - expect(scopedChanges(fixBuilder, node), changes); - expect(scopedProblems(fixBuilder, node), problems); - expect(scopedInformative(fixBuilder, node), informative); - } - - AssignmentTargetInfo _computeAssignmentTargetInfo( - Expression node, FixBuilder fixBuilder) { - try { - assert( - identical(ElementTypeProvider.current, const ElementTypeProvider())); - ElementTypeProvider.current = fixBuilder.migrationResolutionHooks; - var assignment = node.thisOrAncestorOfType()!; - var readType = assignment.readType; - var writeType = assignment.writeType; - return AssignmentTargetInfo(readType, writeType); - } finally { - ElementTypeProvider.current = const ElementTypeProvider(); - } - } - - FixBuilder _createFixBuilder(AstNode scope, {bool warnOnWeakCode = false}) { - var unit = scope.thisOrAncestorOfType()!; - var definingLibrary = unit.declaredElement!.library; - return FixBuilder( - testSource, - decoratedClassHierarchy, - typeProvider, - typeSystem, - variables, - definingLibrary as LibraryElementImpl, - null, - scope.thisOrAncestorOfType(), - warnOnWeakCode, - graph, {}); - } - - bool _isInScope(AstNode node, AstNode? scope) { - return node - .thisOrAncestorMatching((ancestor) => identical(ancestor, scope)) != - null; - } -} - -extension _NodeChangeForExpressionExtension - on TypeMatcher { - TypeMatcher havingExpressionChange( - dynamic changeMatcher, dynamic infoMatcher) => - having((c) => c.expressionChanges.single, 'expressionChanges.single', - changeMatcher) - .having((c) => c.expressionChangeInfos.single, - 'expressionChangeInfos.single', infoMatcher); - - TypeMatcher havingIntroduceAsWithInfo( - dynamic typeStringMatcher, dynamic infoMatcher) => - havingExpressionChange( - TypeMatcher().having( - (c) => c.type.toString(), 'type (string)', typeStringMatcher), - infoMatcher); - - TypeMatcher havingNoValidMigrationWithInfo(dynamic matcher) => - havingExpressionChange(TypeMatcher(), matcher); - - TypeMatcher havingNullCheckWithInfo(dynamic matcher) => - havingExpressionChange(TypeMatcher(), matcher); -} diff --git a/pkg/nnbd_migration/test/fix_reason_target_test.dart b/pkg/nnbd_migration/test/fix_reason_target_test.dart deleted file mode 100644 index c36391ff8a94..000000000000 --- a/pkg/nnbd_migration/test/fix_reason_target_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(FixReasonTargetTest); - }); -} - -@reflectiveTest -class FixReasonTargetTest { - void test_suffix_complex() { - var root = FixReasonTarget.root; - expect(root.returnType.typeArgument(0).suffix, - ' for type argument 0 of return type'); - expect(root.yieldedType.namedParameter('foo').suffix, - ' for parameter foo of yielded type'); - expect(root.namedParameter('foo').positionalParameter(0).suffix, - ' for parameter 0 of parameter foo'); - expect(root.positionalParameter(0).returnType.suffix, - ' for return type of parameter 0'); - expect(root.typeArgument(0).yieldedType.suffix, - ' for yielded type from type argument 0'); - } - - void test_suffix_simple() { - var root = FixReasonTarget.root; - expect(root.suffix, ''); - expect(root.returnType.suffix, ' for return type'); - expect(root.yieldedType.suffix, ' for yielded type'); - expect(root.namedParameter('foo').suffix, ' for parameter foo'); - expect(root.positionalParameter(0).suffix, ' for parameter 0'); - expect(root.typeArgument(0).suffix, ' for type argument 0'); - } -} diff --git a/pkg/nnbd_migration/test/front_end/analysis_abstract.dart b/pkg/nnbd_migration/test/front_end/analysis_abstract.dart deleted file mode 100644 index 1ae62ea1e969..000000000000 --- a/pkg/nnbd_migration/test/front_end/analysis_abstract.dart +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2014, 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. - -import 'package:test/test.dart'; - -import '../abstract_context.dart'; - -/// An abstract base for all 'analysis' domain tests. -class AbstractAnalysisTest extends AbstractContextTest { - String? projectPath; - String? testFolder; - String? testFile; - late String testCode; - - AbstractAnalysisTest(); - - void addAnalysisOptionsFile(String content) { - newFile( - resourceProvider.pathContext - .join(projectPath!, 'analysis_options.yaml'), - content); - } - - String? addTestFile(String content) { - newFile(testFile!, content); - testCode = content; - return testFile; - } - - /// Create an analysis options file based on the given arguments. - void createAnalysisOptionsFile({List? experiments}) { - var buffer = StringBuffer(); - if (experiments != null) { - buffer.writeln('analyzer:'); - buffer.writeln(' enable-experiment:'); - for (var experiment in experiments) { - buffer.writeln(' - $experiment'); - } - } - addAnalysisOptionsFile(buffer.toString()); - } - - /// Creates a project [projectPath]. - void createProject({Map? packageRoots}) { - newFolder(projectPath!); - } - - /// Returns the offset of [search] in the file at the given [path]. - /// Fails if not found. - int findFileOffset(String path, String search) { - var file = getFile(path); - var code = file.createSource().contents.data; - var offset = code.indexOf(search); - expect(offset, isNot(-1), reason: '"$search" in\n$code'); - return offset; - } - - /// Returns the offset of [search] in [testCode]. - /// Fails if not found. - int findOffset(String search) { - var offset = testCode.indexOf(search); - expect(offset, isNot(-1)); - return offset; - } - - String? modifyTestFile(String content) { - modifyFile(testFile!, content); - testCode = content; - return testFile; - } - - void setUp() { - super.setUp(); - projectPath = convertPath(testsPath); - testFolder = convertPath('$testsPath/bin'); - testFile = convertPath('$testsPath/bin/test.dart'); - } -} diff --git a/pkg/nnbd_migration/test/front_end/info_builder_test.dart b/pkg/nnbd_migration/test/front_end/info_builder_test.dart deleted file mode 100644 index 316567db98d9..000000000000 --- a/pkg/nnbd_migration/test/front_end/info_builder_test.dart +++ /dev/null @@ -1,1759 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/info_builder.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'analysis_abstract.dart'; -import 'nnbd_migration_test_base.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(BuildEnclosingMemberDescriptionTest); - defineReflectiveTests(InfoBuilderTest); - }); -} - -@reflectiveTest -class BuildEnclosingMemberDescriptionTest extends AbstractAnalysisTest { - Future resolveTestFile() async { - return await session.getResolvedUnit(testFile!) as ResolvedUnitResult; - } - - Future test_classConstructor_named() async { - addTestFile(r''' -class C { - C.aaa(); -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - var constructor = class_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(constructor), - equals("the constructor 'C.aaa'")); - } - - Future test_classConstructor_unnamed() async { - addTestFile(r''' -class C { - C(); -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - var constructor = class_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(constructor), - equals("the default constructor of 'C'")); - } - - Future test_classField() async { - addTestFile(r''' -class C { - int i; -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - FieldDeclaration fieldDeclaration = - class_.members.single as FieldDeclaration; - var field = fieldDeclaration.fields.variables[0]; - expect(InfoBuilder.buildEnclosingMemberDescription(field), - equals("the field 'C.i'")); - } - - Future test_classField_from_type() async { - addTestFile(r''' -class C { - int i; -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - FieldDeclaration fieldDeclaration = - class_.members.single as FieldDeclaration; - var type = fieldDeclaration.fields.type; - expect(InfoBuilder.buildEnclosingMemberDescription(type), - equals("the field 'C.i'")); - } - - Future test_classGetter() async { - addTestFile(r''' -class C { - int get aaa => 7; -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - var getter = class_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(getter), - equals("the getter 'C.aaa'")); - } - - Future test_classMethod() async { - addTestFile(r''' -class C { - int aaa() => 7; -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - var method = class_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(method), - equals("the method 'C.aaa'")); - } - - Future test_classOperator() async { - addTestFile(r''' -class C { - bool operator ==(Object other) => false; -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - var operator = class_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(operator), - equals("the operator 'C.=='")); - } - - Future test_classSetter() async { - addTestFile(r''' -class C { - void set aaa(value) {} -} -'''); - var result = await resolveTestFile(); - ClassDeclaration class_ = - result.unit.declarations.single as ClassDeclaration; - var setter = class_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(setter), - equals("the setter 'C.aaa='")); - } - - Future test_extensionMethod() async { - addTestFile(r''' -extension E on List { - int aaa() => 7; -} -'''); - var result = await resolveTestFile(); - ExtensionDeclaration extension_ = - result.unit.declarations.single as ExtensionDeclaration; - var method = extension_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(method), - equals("the method 'E.aaa'")); - } - - Future test_extensionMethod_unnamed() async { - addTestFile(r''' -extension on List { - int aaa() => 7; -} -'''); - var result = await resolveTestFile(); - ExtensionDeclaration extension_ = - result.unit.declarations.single as ExtensionDeclaration; - var method = extension_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(method), - equals("the method 'aaa' in unnamed extension on List")); - } - - Future test_mixinMethod() async { - addTestFile(r''' -mixin C { - int aaa() => 7; -} -'''); - var result = await resolveTestFile(); - MixinDeclaration mixin_ = - result.unit.declarations.single as MixinDeclaration; - var method = mixin_.members.single; - expect(InfoBuilder.buildEnclosingMemberDescription(method), - equals("the method 'C.aaa'")); - } - - Future test_topLevelFunction() async { - addTestFile(r''' -void aaa(value) {} -'''); - var result = await resolveTestFile(); - var function = result.unit.declarations.single; - expect(InfoBuilder.buildEnclosingMemberDescription(function), - equals("the function 'aaa'")); - } - - Future test_topLevelGetter() async { - addTestFile(r''' -int get aaa => 7; -'''); - var result = await resolveTestFile(); - var getter = result.unit.declarations.single; - expect(InfoBuilder.buildEnclosingMemberDescription(getter), - equals("the getter 'aaa'")); - } - - Future test_topLevelSetter() async { - addTestFile(r''' -void set aaa(value) {} -'''); - var result = await resolveTestFile(); - var setter = result.unit.declarations.single; - expect(InfoBuilder.buildEnclosingMemberDescription(setter), - equals("the setter 'aaa='")); - } - - Future test_topLevelVariable() async { - addTestFile(r''' -int i; -'''); - var result = await resolveTestFile(); - TopLevelVariableDeclaration topLevelVariableDeclaration = - result.unit.declarations.single as TopLevelVariableDeclaration; - var variable = topLevelVariableDeclaration.variables.variables[0]; - expect(InfoBuilder.buildEnclosingMemberDescription(variable), - equals("the variable 'i'")); - } - - Future test_topLevelVariable_from_type() async { - addTestFile(r''' -int i; -'''); - var result = await resolveTestFile(); - TopLevelVariableDeclaration topLevelVariableDeclaration = - result.unit.declarations.single as TopLevelVariableDeclaration; - var type = topLevelVariableDeclaration.variables.type; - expect(InfoBuilder.buildEnclosingMemberDescription(type), - equals("the variable 'i'")); - } -} - -@reflectiveTest -class InfoBuilderTest extends NnbdMigrationTestBase { - /// Assert various properties of the given [edit]. - bool assertEdit( - {required EditDetail? edit, - int? offset, - int? length, - String? replacement}) { - expect(edit, isNotNull); - if (offset != null) { - expect(edit!.offset, offset); - } - if (length != null) { - expect(edit!.length, length); - } - if (replacement != null) { - expect(edit!.replacement, replacement); - } - return true; - } - - List getNonInformativeRegions(List regions) { - return regions - .where((region) => - region.kind != NullabilityFixKind.typeNotMadeNullable && - region.kind != NullabilityFixKind.typeNotMadeNullableDueToHint) - .toList(); - } - - Future test_addLate() async { - var content = ''' -f() { - String s; - if (1 == 2) s = "Hello"; - g(s); -} -g(String /*!*/ s) {} -'''; - var migratedContent = ''' -f() { - late String s; - if (1 == 2) s = "Hello"; - g(s); -} -g(String /*!*/ s) {} -'''; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - var region = regions[0]; - assertRegion( - region: region, - offset: 8, - length: 4, - explanation: 'Added a late keyword', - kind: NullabilityFixKind.addLate); - } - - Future test_addLate_dueToHint() async { - var content = '/*late*/ int x = 0;'; - var migratedContent = '/*late*/ int x = 0;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - var textToRemove = '/*late*/ '; - assertRegionPair(regions, 0, - offset1: migratedContent.indexOf('/*'), - length1: 2, - offset2: migratedContent.indexOf('*/'), - length2: 2, - explanation: 'Added a late keyword, due to a hint', - kind: NullabilityFixKind.addLateDueToHint, - edits: (List edits) => assertEdit( - edit: edits.single, - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: '')); - } - - Future test_addLate_dueToTestSetup() async { - addTestCorePackage(); - var content = ''' -import 'package:test/test.dart'; -void main() { - int i; - setUp(() { - i = 1; - }); - test('a', () { - f(i); - }); - f(int /*?*/ i) {} -} -'''; - var migratedContent = ''' -import 'package:test/test.dart'; -void main() { - late int i; - setUp(() { - i = 1; - }); - test('a', () { - f(i); - }); - f(int /*?*/ i) {} -} -'''; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(3)); - var region = regions[0]; - assertRegion( - region: region, - offset: 49, - length: 4, - explanation: 'Added a late keyword, due to assignment in `setUp`', - kind: NullabilityFixKind.addLateDueToTestSetup); - } - - Future test_addLateFinal_dueToHint() async { - var content = '/*late final*/ int x = 0;'; - var migratedContent = '/*late final*/ int x = 0;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - var textToRemove = '/*late final*/ '; - assertRegionPair(regions, 0, - offset1: migratedContent.indexOf('/*'), - length1: 2, - offset2: migratedContent.indexOf('*/'), - length2: 2, - explanation: 'Added late and final keywords, due to a hint', - kind: NullabilityFixKind.addLateFinalDueToHint, - edits: (List edits) => assertEdit( - edit: edits.single, - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: '')); - } - - Future test_addLateHint_classMultipleTypedInstanceVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -class C { - int f, g; - C() { - f = 1; - } -} -''', migratedContent: ''' -class C { - int? f, g; - C() { - f = 1; - } -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 15, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 12, length: 0, replacement: '/*late*/'); - } - - Future test_addLateHint_classTypedFinalInstanceVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -class C { - final int f; - C({this.f}); -} -''', migratedContent: ''' -class C { - final int? f; - C({this.f}); -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 21, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 12, length: 0, replacement: '/*late*/'); - } - - Future test_addLateHint_classTypedInstanceVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -class C { - int f; - C() { - f = 1; - } -} -''', migratedContent: ''' -class C { - int? f; - C() { - f = 1; - } -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 15, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 12, length: 0, replacement: '/*late*/'); - } - - Future test_addLateHint_classTypedStaticVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -class C { - static int x; - void f() => x = null; -} -''', migratedContent: ''' -class C { - static int? x; - void f() => x = null; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 22, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 19, length: 0, replacement: '/*late*/'); - } - - Future test_addLateHint_mixinVarInstanceVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -mixin M { - int f; - void m() => f = null; -} -''', migratedContent: ''' -mixin M { - int? f; - void m() => f = null; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 15, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 12, length: 0, replacement: '/*late*/'); - } - - Future test_addLateHint_typedLocalVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -void f() { - int x; - void g() => x = null; -} -''', migratedContent: ''' -void f() { - int? x; - void g() => x = null; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 16, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 13, length: 0, replacement: '/*late*/'); - } - - Future test_addLateHint_typedTopLevelVariable() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -int x; -void f() => x = null; -''', migratedContent: ''' -int? x; -void f() => x = null; -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 3, - length: 1, - explanation: "Changed type 'int' to be nullable", - kind: NullabilityFixKind.makeTypeNullable); - assertEdit(edit: edits[2], offset: 0, length: 0, replacement: '/*late*/'); - } - - Future test_compound_assignment_nullable_result() async { - var unit = await buildInfoForSingleTestFile(''' -abstract class C { - C/*?*/ operator+(int i); -} -void f(C/*!*/ a, int b) { - a += b; -} -''', migratedContent: ''' -abstract class C { - C/*?*/ operator+(int? i); -} -void f(C/*!*/ a, int? b) { - a += b; -} -'''); - var operator = '+='; - var operatorOffset = unit.content!.indexOf(operator); - var region = - unit.regions.where((region) => region.offset == operatorOffset).single; - assertRegion( - region: region, - length: operator.length, - explanation: 'Compound assignment has bad combined type', - kind: NullabilityFixKind.compoundAssignmentHasBadCombinedType, - edits: isEmpty); - } - - Future test_compound_assignment_nullable_source() async { - var unit = await buildInfoForSingleTestFile(''' -void f(int/*?*/ a, int b) { - a += b; -} -''', migratedContent: ''' -void f(int/*?*/ a, int b) { - a += b; -} -'''); - var operator = '+='; - var operatorOffset = unit.content!.indexOf(operator); - var region = - unit.regions.where((region) => region.offset == operatorOffset).single; - assertRegion( - region: region, - length: operator.length, - explanation: 'Compound assignment has nullable source', - kind: NullabilityFixKind.compoundAssignmentHasNullableSource, - edits: isEmpty); - } - - Future test_discardCondition() async { - var unit = await buildInfoForSingleTestFile(''' -void g(int i) { - print(i.isEven); - if (i != null) print('NULL'); -} -''', migratedContent: ''' -void g(int i) { - print(i.isEven); - /* if (i != null) */ print('NULL'); -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - assertRegion( - region: regions[0], - offset: 38, - length: 3, - kind: NullabilityFixKind.removeDeadCode); - assertRegion( - region: regions[1], - offset: 56, - length: 3, - kind: NullabilityFixKind.removeDeadCode); - } - - Future test_downcast_nonNullable() async { - var content = 'int/*!*/ f(num/*!*/ n) => n;'; - var migratedContent = 'int/*!*/ f(num/*!*/ n) => n as int;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = getNonInformativeRegions(unit.regions); - expect(regions, hasLength(1)); - var region = regions.single; - var regionTarget = ' as int'; - assertRegion( - region: region, - offset: migratedContent.indexOf(regionTarget), - length: regionTarget.length, - kind: NullabilityFixKind.downcastExpression, - edits: isEmpty, - traces: isEmpty); - } - - Future test_downcast_nonNullable_to_nullable() async { - var content = 'int/*?*/ f(num/*!*/ n) => n;'; - var migratedContent = 'int/*?*/ f(num/*!*/ n) => n as int;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = getNonInformativeRegions(unit.regions); - var regionTarget = ' as int'; - var offset = migratedContent.indexOf(regionTarget); - var region = regions.where((region) => region.offset == offset).single; - assertRegion( - region: region, - offset: offset, - length: regionTarget.length, - kind: NullabilityFixKind.downcastExpression, - edits: isEmpty, - traces: isEmpty); - } - - Future test_downcast_nullable() async { - var content = 'int/*?*/ f(num/*?*/ n) => n;'; - var migratedContent = 'int/*?*/ f(num/*?*/ n) => n as int?;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = getNonInformativeRegions(unit.regions); - var regionTarget = ' as int?'; - var offset = migratedContent.indexOf(regionTarget); - var region = regions.where((region) => region.offset == offset).single; - assertRegion( - region: region, - offset: offset, - length: regionTarget.length, - kind: NullabilityFixKind.downcastExpression, - edits: isEmpty, - traces: isEmpty); - } - - Future test_downcast_nullable_to_nonNullable() async { - var content = 'int/*!*/ f(num/*?*/ n) => n;'; - var migratedContent = 'int/*!*/ f(num/*?*/ n) => n as int;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = getNonInformativeRegions(unit.regions); - var regionTarget = ' as int'; - var offset = migratedContent.indexOf(regionTarget); - var region = regions.where((region) => region.offset == offset).single; - assertRegion( - region: region, - offset: offset, - length: regionTarget.length, - kind: NullabilityFixKind.downcastExpression, - edits: isEmpty, - traces: isNotEmpty); - } - - Future test_downcast_with_traces() async { - var content = 'List/*!*/ f(List/*?*/ x) => x;'; - var migratedContent = - 'List/*!*/ f(List/*?*/ x) => x as List;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.regions.where( - (region) => region.kind == NullabilityFixKind.downcastExpression); - expect(regions, hasLength(1)); - var region = regions.single; - var regionTarget = ' as List'; - assertRegion( - region: region, - offset: migratedContent.indexOf(regionTarget), - length: regionTarget.length, - kind: NullabilityFixKind.downcastExpression, - edits: isEmpty, - traces: isNotEmpty); - var traceDescriptionToOffset = { - for (var trace in region.traces) - trace.description: trace.entries[0].target!.offset - }; - expect(traceDescriptionToOffset, { - 'Nullability reason': content.indexOf('List/*?*/'), - 'Non-nullability reason': content.indexOf('List/*!*/'), - 'Nullability reason for type argument 0': content.indexOf('int/*?*/'), - 'Non-nullability reason for type argument 0': content.indexOf('int/*!*/') - }); - } - - Future test_dynamicValueIsUsed() async { - var unit = await buildInfoForSingleTestFile(''' -bool f(int i) { - if (i == null) return true; - else return false; -} -void g() { - dynamic i = null; - f(i); -} -''', migratedContent: ''' -bool f(int? i) { - if (i == null) return true; - else return false; -} -void g() { - dynamic i = null; - f(i); -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 11, - explanation: "Changed type 'int' to be nullable"); - assertEdit(edit: edits[0], offset: 10, replacement: '/*!*/'); - assertEdit(edit: edits[1], offset: 10, replacement: '/*?*/'); - } - - Future test_expressionFunctionReturnTarget() async { - var unit = await buildInfoForSingleTestFile(''' -String g() => 1 == 2 ? "Hello" : null; -''', migratedContent: ''' -String? g() => 1 == 2 ? "Hello" : null; -'''); - assertInTargets(targets: unit.targets, offset: 7, length: 1); // "g" - var regions = unit.regions; - expect(regions, hasLength(1)); - assertRegion( - region: regions[0], - offset: 6, - explanation: "Changed type 'String' to be nullable"); - } - - Future test_function_typed_parameter_made_nullable_due_to_hint() async { - var content = 'f(void g(int i)/*?*/) {}'; - var migratedContent = 'f(void g(int i)/*?*/) {}'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - var textToRemove = '/*?*/'; - assertRegionPair(regions, 0, - offset1: migratedContent.indexOf(textToRemove), - length1: 2, - offset2: migratedContent.indexOf(textToRemove) + 3, - length2: 2, - explanation: - "Changed type 'void Function(int)' to be nullable, due to a " - 'nullability hint', - kind: NullabilityFixKind.makeTypeNullableDueToHint, - traces: isNotNull, edits: (List edits) { - expect(edits, hasLength(2)); - var editsByDescription = {for (var edit in edits) edit.description: edit}; - assertEdit( - edit: editsByDescription['Change to /*!*/ hint'], - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: '/*!*/'); - assertEdit( - edit: editsByDescription['Remove /*?*/ hint'], - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: ''); - return true; - }); - } - - Future test_increment_nullable_result() async { - var unit = await buildInfoForSingleTestFile(''' -abstract class C { - C/*?*/ operator+(int i); -} -void f(C/*!*/ a) { - a++; -} -''', migratedContent: ''' -abstract class C { - C/*?*/ operator+(int? i); -} -void f(C/*!*/ a) { - a++; -} -'''); - var operator = '++'; - var operatorOffset = unit.content!.indexOf(operator); - var region = - unit.regions.where((region) => region.offset == operatorOffset).single; - assertRegion( - region: region, - length: operator.length, - explanation: 'Compound assignment has bad combined type', - kind: NullabilityFixKind.compoundAssignmentHasBadCombinedType, - edits: isEmpty); - } - - Future test_increment_nullable_source() async { - var unit = await buildInfoForSingleTestFile(''' -void f(int/*?*/ a) { - a++; -} -''', migratedContent: ''' -void f(int/*?*/ a) { - a++; -} -'''); - var operator = '++'; - var operatorOffset = unit.content!.indexOf(operator); - var region = - unit.regions.where((region) => region.offset == operatorOffset).single; - assertRegion( - region: region, - length: operator.length, - explanation: 'Compound assignment has nullable source', - kind: NullabilityFixKind.compoundAssignmentHasNullableSource, - edits: isEmpty); - } - - Future test_insertedRequired_fieldFormal() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -import 'package:meta/meta.dart'; -class C { - int level; - int level2; - C({this.level}) : this.level2 = level + 1; -} -''', migratedContent: ''' -import 'package:meta/meta.dart'; -class C { - int level; - int level2; - C({required this.level}) : this.level2 = level + 1; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 77, - length: 9, - explanation: "Add 'required' keyword to parameter 'level' in 'C.'", - kind: NullabilityFixKind.addRequired); - assertEdit( - edit: edits[0], offset: 75, length: 0, replacement: '@required '); - } - - Future test_insertedRequired_fieldFormal_hint() async { - var unit = await buildInfoForSingleTestFile(''' -class C { - int level; - int level2; - C({this.level}) : this.level2 = level + 1; -} -''', migratedContent: ''' -class C { - int level; - int level2; - C({required this.level}) : this.level2 = level + 1; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 44, - length: 9, - explanation: "Add 'required' keyword to parameter 'level' in 'C.'", - kind: NullabilityFixKind.addRequired); - assertEdit( - edit: edits[0], offset: 42, length: 0, replacement: '/*required*/ '); - } - - Future test_insertedRequired_parameter() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -import 'package:meta/meta.dart'; -class C { - int level = 0; - bool f({int lvl}) => lvl >= level; -} -''', migratedContent: ''' -import 'package:meta/meta.dart'; -class C { - int level = 0; - bool f({required int lvl}) => lvl >= level; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 72, - length: 9, - explanation: "Add 'required' keyword to parameter 'lvl' in 'C.f'", - kind: NullabilityFixKind.addRequired); - assertEdit( - edit: edits[0], offset: 70, length: 0, replacement: '@required '); - } - - Future test_insertedRequired_parameter_hint() async { - var unit = await buildInfoForSingleTestFile(''' -class C { - int level = 0; - bool f({int lvl}) => lvl >= level; -} -''', migratedContent: ''' -class C { - int level = 0; - bool f({required int lvl}) => lvl >= level; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertRegion( - region: region, - offset: 39, - length: 9, - explanation: "Add 'required' keyword to parameter 'lvl' in 'C.f'", - kind: NullabilityFixKind.addRequired); - assertEdit( - edit: edits[0], offset: 37, length: 0, replacement: '/*required*/ '); - } - - Future test_insertedRequired_parameter_metaPrefixed() async { - addMetaPackage(); - var unit = await buildInfoForSingleTestFile(''' -import 'package:meta/meta.dart' as meta; -class C { - int level = 0; - bool f({int lvl}) => lvl >= level; -} -''', migratedContent: ''' -import 'package:meta/meta.dart' as meta; -class C { - int level = 0; - bool f({required int lvl}) => lvl >= level; -} -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(1)); - var region = regions[0]; - var edits = region.edits; - assertEdit( - edit: edits[0], offset: 78, length: 0, replacement: '@meta.required '); - } - - Future test_insertParens() async { - var originalContent = ''' -class C { - C operator+(C c) => null; -} -C/*!*/ _f(C c) => c + c; -'''; - var migratedContent = ''' -class C { - C? operator+(C? c) => null; -} -C/*!*/ _f(C c) => (c + c)!; -'''; - var unit = await buildInfoForSingleTestFile(originalContent, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(4)); - assertRegion( - region: regions[0], - offset: migratedContent.indexOf('? operator'), - length: 1, - explanation: "Changed type 'C' to be nullable"); - assertRegion( - region: regions[1], - offset: migratedContent.indexOf('? c'), - length: 1, - explanation: "Changed type 'C' to be nullable"); - assertRegion( - region: regions[2], - offset: migratedContent.indexOf('/*!*/'), - length: 5, - explanation: "Type 'C' was not made nullable due to a hint", - kind: NullabilityFixKind.typeNotMadeNullableDueToHint); - assertRegion( - region: regions[3], - offset: migratedContent.indexOf('!;'), - length: 1, - explanation: 'Added a non-null assertion to nullable expression', - kind: NullabilityFixKind.checkExpression); - } - - Future test_method_name_change() async { - addPackageFile('collection', 'collection.dart', ''); - var content = ''' -import 'package:collection/collection.dart'; - -int f(List values, int/*?*/ x) - => values.firstWhere((i) => (i + x).isEven, - orElse: () => null); -'''; - var migratedContent = ''' -import 'package:collection/collection.dart'; - -int? f(List values, int/*?*/ x) - => values.firstWherefirstWhereOrNull((i) => (i + x!).isEven, - orElse: () => null); -'''; - await buildInfoForSingleTestFile(content, - migratedContent: migratedContent, removeViaComments: false); - } - - void test_nullAwareAssignment_remove() async { - var unit = await buildInfoForSingleTestFile(''' -int f(int/*!*/ x, int y) => x ??= y; -''', migratedContent: ''' -int f(int/*!*/ x, int? y) => x ??= y; -''', warnOnWeakCode: false, removeViaComments: false); - var codeToRemove = ' ??= y'; - var removalOffset = unit.content!.indexOf(codeToRemove); - var region = - unit.regions.where((region) => region.offset == removalOffset).single; - assertRegion( - region: region, - length: codeToRemove.length, - explanation: - 'Removed a null-aware assignment, because the target cannot be ' - 'null', - kind: NullabilityFixKind.removeDeadCode, - edits: isEmpty); - } - - void test_nullAwareAssignment_unnecessaryInStrongMode() async { - var unit = await buildInfoForSingleTestFile(''' -int f(int/*!*/ x, int y) => x ??= y; -''', migratedContent: ''' -int f(int/*!*/ x, int? y) => x ??= y!; -''', warnOnWeakCode: true); - var operator = '??='; - var operatorOffset = unit.content!.indexOf(operator); - var region = - unit.regions.where((region) => region.offset == operatorOffset).single; - assertRegion( - region: region, - length: operator.length, - explanation: - 'Null-aware assignment will be unnecessary in strong checking mode', - kind: NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode, - edits: isEmpty); - } - - void test_nullAwarenessUnnecessaryInStrongMode() async { - var unit = await buildInfoForSingleTestFile(''' -int f(String/*!*/ s) => s?.length; -''', migratedContent: ''' -int f(String/*!*/ s) => s?.length; -''', warnOnWeakCode: true); - var question = '?'; - var questionOffset = unit.content!.indexOf(question); - var region = - unit.regions.where((region) => region.offset == questionOffset).single; - assertRegion( - region: region, - length: question.length, - explanation: - 'Null-aware access will be unnecessary in strong checking mode', - kind: NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode, - edits: isEmpty); - } - - Future test_nullCheck_dueToHint() async { - var content = 'int f(int/*?*/ x) => x/*!*/;'; - var migratedContent = 'int f(int/*?*/ x) => x/*!*/;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(4)); - assertRegionPair(regions, 0, - kind: NullabilityFixKind.makeTypeNullableDueToHint); - var hintText = '/*!*/'; - assertRegionPair(regions, 2, - offset1: migratedContent.indexOf(hintText), - length1: 2, - offset2: migratedContent.indexOf(hintText) + 3, - length2: 2, - explanation: 'Accepted a null check hint', - kind: NullabilityFixKind.checkExpressionDueToHint, - traces: isNotEmpty, - edits: (List edits) => assertEdit( - edit: edits.single, - offset: content.indexOf(hintText), - length: hintText.length, - replacement: '')); - } - - Future test_nullCheck_onMemberAccess() async { - var unit = await buildInfoForSingleTestFile(''' -class C { - int value; - C([this.value]); - void f() { - value.sign; - } -} -''', migratedContent: ''' -class C { - int? value; - C([this.value]); - void f() { - value!.sign; - } -} -'''); - var regions = unit.regions; - expect(regions, hasLength(2)); - // regions[0] is `int?`. - var region = regions[1]; - var edits = region.edits; - assertRegion( - region: regions[1], - offset: 65, - explanation: 'Added a non-null assertion to nullable expression', - kind: NullabilityFixKind.checkExpression); - assertEdit(edit: edits[0], offset: 64, length: 0, replacement: '/*!*/'); - } - - Future test_parameter_fromOverriddenField_explicit() async { - var unit = await buildInfoForSingleTestFile(''' -class A { - int m; -} -class B extends A { - void set m(Object p) {} -} -void f(A a) => a.m = null; -''', migratedContent: ''' -class A { - int? m; -} -class B extends A { - void set m(Object? p) {} -} -void f(A a) => a.m = null; -'''); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - assertRegion( - region: regions[0], - offset: 15, - explanation: "Changed type 'int' to be nullable"); - assertRegion( - region: regions[1], - offset: 61, - explanation: "Changed type 'Object' to be nullable"); - - expect(regions[0].traces, hasLength(1)); - var trace = regions[0].traces.first; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(2)); - assertTraceEntry(unit, entries[0], 'A.m', unit.content!.indexOf('int?'), - contains('A.m (test.dart:2:3)')); - } - - Future test_removal_handles_offsets_correctly() async { - var originalContent = ''' -void f(num n, int/*?*/ i) { - if (n is! int) return; - print((n as int).isEven); - print(i + 1); -} -'''; - // Note: even though `as int` is removed, it still shows up in the - // preview, since we show deleted text. - var migratedContent = ''' -void f(num? n, int/*?*/ i) { - if (n is! int ) return; - print((n as int).isEven); - print(i! + 1); -} -'''; - var unit = await buildInfoForSingleTestFile(originalContent, - migratedContent: migratedContent, removeViaComments: false); - var regions = unit.fixRegions; - expect(regions, hasLength(5)); - assertRegionPair(regions, 1, - kind: NullabilityFixKind.makeTypeNullableDueToHint); - assertRegion( - region: regions[3], - offset: migratedContent.indexOf(' as int'), - length: ' as int'.length, - explanation: 'Discarded a downcast that is now unnecessary', - kind: NullabilityFixKind.removeAs); - assertRegion( - region: regions[4], - offset: migratedContent.indexOf('! + 1'), - explanation: 'Added a non-null assertion to nullable expression', - kind: NullabilityFixKind.checkExpression); - } - - Future test_returnDetailTarget() async { - var unit = await buildInfoForSingleTestFile(''' -String g() { - return 1 == 2 ? "Hello" : null; -} -''', migratedContent: ''' -String? g() { - return 1 == 2 ? "Hello" : null; -} -'''); - assertInTargets(targets: unit.targets, offset: 7, length: 1); // "g" - var regions = unit.regions; - expect(regions, hasLength(1)); - assertRegion( - region: regions[0], - offset: 6, - explanation: "Changed type 'String' to be nullable"); - } - - Future test_suspicious_cast() async { - var content = ''' -int f(Object o) { - if (o is! String) return 0; - return o; -} -'''; - var migratedContent = ''' -int f(Object? o) { - if (o is! String ) return 0; - return o /* no valid migration */; -} -'''; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = getNonInformativeRegions(unit.regions); - var regionTarget = '/* no valid migration */'; - var offset = migratedContent.indexOf(regionTarget); - var region = regions.where((region) => region.offset == offset).single; - assertRegion( - region: region, - offset: offset, - length: regionTarget.length, - kind: NullabilityFixKind.noValidMigrationForNull, - edits: isEmpty); - } - - Future test_trace_constructor_named() async { - var unit = await buildInfoForSingleTestFile(''' -class C { - C.named(int i) {} -} -void f() { - C.named(null); -} -''', migratedContent: ''' -class C { - C.named(int? i) {} -} -void f() { - C.named(null); -} -'''); - var region = unit.regions - .where((info) => info.offset == unit.content!.indexOf('? i) {}')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - assertTraceEntry( - unit, - entries[0], - 'C.named', - unit.content!.indexOf('int? i) {}'), - contains('parameter 0 of C.named')); - } - - Future test_trace_constructor_unnamed() async { - var unit = await buildInfoForSingleTestFile(''' -class C { - C(int i) {} -} -void f() { - C(null); -} -''', migratedContent: ''' -class C { - C(int? i) {} -} -void f() { - C(null); -} -'''); - var region = unit.regions - .where((info) => info.offset == unit.content!.indexOf('? i) {}')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - assertTraceEntry( - unit, - entries[0], - 'C.', - unit.content!.indexOf('int? i) {}'), - contains('parameter 0 of C.')); - } - - Future test_trace_deadCode() async { - var unit = await buildInfoForSingleTestFile(''' -void f(int/*!*/ i) { - if (i == null) return; -} -''', migratedContent: ''' -void f(int/*!*/ i) { - /* if (i == null) return; */ -} -'''); - var region = unit.regions - .where( - (regionInfo) => regionInfo.offset == unit.content!.indexOf('/* if')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Non-nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(2)); - // Entry 0 is the nullability of f's argument - assertTraceEntry(unit, entries[0], 'f', unit.content!.indexOf('int'), - contains('parameter 0 of f')); - // Entry 1 is the edge from f's argument to never, due to the `/*!*/` hint. - assertTraceEntry(unit, entries[1], 'f', unit.content!.indexOf('int'), - 'explicitly hinted to be non-nullable'); - } - - Future test_trace_extension_unnamed() async { - var unit = await buildInfoForSingleTestFile(''' -extension on String { - m(int i) {} -} -void f() { - "".m(null); -} -''', migratedContent: ''' -extension on String { - m(int? i) {} -} -void f() { - "".m(null); -} -'''); - var region = unit.regions - .where((info) => info.offset == unit.content!.indexOf('? i) {}')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - assertTraceEntry( - unit, - entries[0], - '.m', - unit.content!.indexOf('int? i) {}'), - contains('parameter 0 of .m')); - } - - Future test_trace_nullableType() async { - var unit = await buildInfoForSingleTestFile(''' -void _f(int i) {} // _f -void _g(int i) { // _g - _f(i); -} -void h() { - _g(null); -} -''', migratedContent: ''' -void _f(int? i) {} // _f -void _g(int? i) { // _g - _f(i); -} -void h() { - _g(null); -} -'''); - var region = unit.regions - .where((regionInfo) => - regionInfo.offset == unit.content!.indexOf('? i) {} // _f')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(6)); - // Entry 0 is the nullability of _f's argument - assertTraceEntry( - unit, - entries[0], - '_f', - unit.content!.indexOf('int? i) {} // _f'), - contains('parameter 0 of _f'), - hintActions: { - HintActionKind.addNullableHint, - HintActionKind.addNonNullableHint - }); - - // Entry 1 is the edge from _g's argument to _f's argument, due to g's call - // to _f. - assertTraceEntry( - unit, entries[1], '_g', unit.content!.indexOf('i);'), 'data flow'); - // Entry 2 is the nullability of _g's argument - assertTraceEntry(unit, entries[2], '_g', - unit.content!.indexOf('int? i) { // _g'), contains('parameter 0 of _g'), - hintActions: { - HintActionKind.addNullableHint, - HintActionKind.addNonNullableHint - }); - // Entry 3 is the edge from null to _g's argument, due to h's call to _g. - assertTraceEntry( - unit, entries[3], 'h', unit.content!.indexOf('null'), 'data flow'); - // Entry 4 is the nullability of the null literal. - assertTraceEntry(unit, entries[4], 'h', unit.content!.indexOf('null'), - contains('null literal')); - // Entry 5 is the edge from always to null. - // TODO(paulberry): this edge provides no additional useful information and - // shouldn't be included in the trace. - assertTraceEntry(unit, entries[5], 'h', unit.content!.indexOf('null'), - 'literal expression'); - } - - Future test_trace_nullCheck() async { - var unit = await buildInfoForSingleTestFile('int f(int/*?*/ i) => i + 1;', - migratedContent: 'int f(int/*?*/ i) => i! + 1;'); - var region = unit.regions - .where( - (regionInfo) => regionInfo.offset == unit.content!.indexOf('! +')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(2)); - // Entry 0 is the nullability of the type of i. - assertTraceEntry(unit, entries[0], 'f', unit.content!.indexOf('int/*?*/'), - contains('parameter 0 of f')); - // Entry 1 is the edge from always to the type of i. - // TODO(paulberry): this edge provides no additional useful information and - // shouldn't be included in the trace. - assertTraceEntry(unit, entries[1], 'f', unit.content!.indexOf('int/*?*/'), - 'explicitly hinted to be nullable'); - } - - Future test_trace_nullCheck_notNullableReason() async { - var unit = await buildInfoForSingleTestFile(''' -void f(int i) { // f - assert(i != null); -} -void g(int i) { // g - f(i); // call f -} -void h(int/*?*/ i) { - g(i); -} -''', migratedContent: ''' -void f(int i) { // f - assert(i != null); -} -void g(int i) { // g - f(i); // call f -} -void h(int/*?*/ i) { - g(i!); -} -'''); - var region = unit.regions - .where((regionInfo) => regionInfo.offset == unit.content!.indexOf('!)')) - .single; - expect(region.traces, hasLength(2)); - // Trace 0 is the nullability reason; we don't care about that right now. - // Trace 1 is the non-nullability reason. - var trace = region.traces[1]; - expect(trace.description, 'Non-nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(4)); - // Entry 0 is the nullability of g's argument - assertTraceEntry(unit, entries[0], 'g', - unit.content!.indexOf('int i) { // g'), contains('parameter 0 of g')); - // Entry 1 is the edge from g's argument to f's argument, due to g's call to - // f. - assertTraceEntry(unit, entries[1], 'g', - unit.content!.indexOf('i); // call f'), 'data flow'); - // Entry 2 is the nullability of f's argument - assertTraceEntry(unit, entries[2], 'f', - unit.content!.indexOf('int i) { // f'), contains('parameter 0 of f')); - // Entry 3 is the edge f's argument to never, due to the assert. - assertTraceEntry(unit, entries[3], 'f', unit.content!.indexOf('assert'), - 'value asserted to be non-null'); - } - - Future test_trace_nullCheckHint() async { - var unit = await buildInfoForSingleTestFile('int f(int/*?*/ i) => i/*!*/;', - migratedContent: 'int f(int/*?*/ i) => i/*!*/;'); - var region = unit.regions - .where( - (regionInfo) => regionInfo.offset == unit.content!.indexOf('/*!*/')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Reason'); - expect(trace.entries, hasLength(1)); - assertTraceEntry(unit, trace.entries.single, 'f', - unit.content!.indexOf('i/*!*/'), 'Null check hint'); - } - - Future test_trace_refers_to_variable_initializer() async { - var unit = await buildInfoForSingleTestFile(''' -void f(int/*?*/ i) { - var x = [i]; - int y = x[0]; -} -''', migratedContent: ''' -void f(int/*?*/ i) { - var x = [i]; - int? y = x[0]; -} -'''); - var region = unit.regions - .where( - (regionInfo) => regionInfo.offset == unit.content!.indexOf('? y')) - .single; - expect(region.traces, hasLength(1)); - var trace = region.traces.single; - expect(trace.description, 'Nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(8)); - // Entry 0 is the nullability of y - assertTraceEntry(unit, entries[0], 'f.y', unit.content!.indexOf('int? y'), - contains('f.y')); - // Entry 1 is the edge from the list element type of x to y, due to array - // indexing. - assertTraceEntry(unit, entries[1], 'f.y', unit.content!.indexOf('x[0]'), - contains('data flow')); - // Entry 2 is the nullability of the implicit list element type of x - assertTraceEntry(unit, entries[2], 'f.x', unit.content!.indexOf('x ='), - contains('type argument 0 of f.x')); - // Entry 3 is the edge from the explicit list element type on the RHS of x - // to the implicit list element type on the LHS of x - assertTraceEntry(unit, entries[3], 'f.x', - unit.content!.indexOf('[i]'), contains('data flow')); - // Entry 4 is the explicit list element type on the RHS of x - assertTraceEntry(unit, entries[4], 'f.x', unit.content!.indexOf('int?>[i]'), - contains('list element type')); - // Entry 5 is the edge from the parameter i to the list literal - assertTraceEntry(unit, entries[5], 'f.x', unit.content!.indexOf('i]'), - contains('data flow')); - // Entry 6 is the nullability of the parameter i - assertTraceEntry(unit, entries[6], 'f', unit.content!.indexOf('int/*?*/'), - contains('parameter 0 of f')); - // Entry 7 is the edge due to the explicit /*?*/ hint - assertTraceEntry(unit, entries[7], 'f', unit.content!.indexOf('int/*?*/'), - contains('explicitly hinted to be nullable')); - } - - Future test_trace_substitutionNode() async { - var unit = await buildInfoForSingleTestFile(''' -class C {} - -C c; - -Map x = {}; -String/*!*/ y = x[0]; -''', migratedContent: ''' -class C {} - -C? c; - -Map x = {}; -String/*!*/ y = x[0]!; -'''); - var region = unit.regions - .where((regionInfo) => regionInfo.offset == unit.content!.indexOf('!;')) - .single; - // The "why nullable" node associated with adding the `!` is a substitution - // node, and we don't currently generate a trace for a substitution node. - // TODO(paulberry): fix this. - // We do, however, generate a trace for "why not nullable". - expect(region.traces, hasLength(1)); - expect(region.traces[0].description, 'Non-nullability reason'); - } - - Future test_type_made_nullable() async { - var unit = await buildInfoForSingleTestFile(''' -String g() => 1 == 2 ? "Hello" : null; -''', migratedContent: ''' -String? g() => 1 == 2 ? "Hello" : null; -'''); - assertInTargets(targets: unit.targets, offset: 7, length: 1); // "g" - var regions = unit.regions; - expect(regions, hasLength(1)); - assertRegion( - region: regions[0], - offset: 6, - explanation: "Changed type 'String' to be nullable"); - } - - Future test_type_made_nullable_due_to_hint() async { - var content = 'int/*?*/ x = 0;'; - var migratedContent = 'int/*?*/ x = 0;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.fixRegions; - expect(regions, hasLength(2)); - var textToRemove = '/*?*/'; - assertRegionPair(regions, 0, - offset1: migratedContent.indexOf(textToRemove), - length1: 2, - offset2: migratedContent.indexOf(textToRemove) + 3, - length2: 2, - explanation: - "Changed type 'int' to be nullable, due to a nullability hint", - kind: NullabilityFixKind.makeTypeNullableDueToHint, - traces: isNotNull, edits: (List edits) { - expect(edits, hasLength(2)); - var editsByDescription = {for (var edit in edits) edit.description: edit}; - assertEdit( - edit: editsByDescription['Change to /*!*/ hint'], - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: '/*!*/'); - assertEdit( - edit: editsByDescription['Remove /*?*/ hint'], - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: ''); - return true; - }); - } - - Future test_type_not_made_nullable() async { - var unit = await buildInfoForSingleTestFile('int i = 0;', - migratedContent: 'int i = 0;'); - var region = unit.regions - .where( - (regionInfo) => regionInfo.offset == unit.content!.indexOf(' i')) - .single; - expect(region.length, 1); - expect(region.lineNumber, 1); - expect(region.explanation, "Type 'int' was not made nullable"); - expect(region.edits.map((edit) => edit.description).toSet(), - {'Add /*?*/ hint', 'Add /*!*/ hint', 'Add late hint'}); - var trace = region.traces.first; - expect(trace.description, 'Non-nullability reason'); - var entries = trace.entries; - expect(entries, hasLength(1)); - assertTraceEntry(unit, entries[0], 'i', unit.content!.indexOf('int'), - 'No reason found to make nullable'); - expect(region.kind, NullabilityFixKind.typeNotMadeNullable); - } - - Future test_type_not_made_nullable_due_to_hint() async { - var content = 'int/*!*/ i = 0;'; - var migratedContent = 'int/*!*/ i = 0;'; - var unit = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - var regions = unit.regions; - expect(regions, hasLength(1)); - var textToRemove = '/*!*/'; - assertRegion( - region: regions[0], - offset: migratedContent.indexOf(textToRemove), - length: 5, - explanation: "Type 'int' was not made nullable due to a hint", - kind: NullabilityFixKind.typeNotMadeNullableDueToHint, - traces: isNotNull, - edits: (List edits) { - expect(edits, hasLength(2)); - var editsByDescription = { - for (var edit in edits) edit.description: edit - }; - assertEdit( - edit: editsByDescription['Change to /*?*/ hint'], - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: '/*?*/'); - assertEdit( - edit: editsByDescription['Remove /*!*/ hint'], - offset: content.indexOf(textToRemove), - length: textToRemove.length, - replacement: ''); - return true; - }); - } -} diff --git a/pkg/nnbd_migration/test/front_end/instrumentation_renderer_test.dart b/pkg/nnbd_migration/test/front_end/instrumentation_renderer_test.dart deleted file mode 100644 index 452d4d05099d..000000000000 --- a/pkg/nnbd_migration/test/front_end/instrumentation_renderer_test.dart +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/instrumentation_renderer.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'nnbd_migration_test_base.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(InstrumentationRendererTest); - }); -} - -@reflectiveTest -class InstrumentationRendererTest extends NnbdMigrationTestBase { - /// Render the instrumentation view for [files]. - Future renderViewForTestFiles( - {bool applied = false, bool needsRerun = false}) async { - var files = {convertPath('$projectPath/lib/a.dart'): 'int a = null;'}; - await buildInfoForTestFiles(files, includedRoot: projectPath); - var migrationInfo = - MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath); - var instrumentationRenderer = InstrumentationRenderer( - migrationInfo, PathMapper(resourceProvider), applied, needsRerun); - return instrumentationRenderer.render(); - } - - Future test_appliedStyle() async { - var renderedView = await renderViewForTestFiles(applied: true); - // harmless space in class list due to other potential classes here. - expect(renderedView, contains('')); - } - - Future test_navigation_containsRoot() async { - var renderedView = await renderViewForTestFiles(); - // harmless space in class list due to other potential classes here. - expect(renderedView, contains('

$projectPath

')); - } - - Future test_needsRerunStyle() async { - var renderedView = await renderViewForTestFiles(needsRerun: true); - expect(renderedView, contains('')); - } - - Future test_notAppliedStyle() async { - var renderedView = await renderViewForTestFiles(applied: false); - // harmless space in class list due to other potential classes here. - expect(renderedView, contains('')); - } -} diff --git a/pkg/nnbd_migration/test/front_end/migration_info_test.dart b/pkg/nnbd_migration/test/front_end/migration_info_test.dart deleted file mode 100644 index ad989db4d907..000000000000 --- a/pkg/nnbd_migration/test/front_end/migration_info_test.dart +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/offset_mapper.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(UnitInfoTest); - }); -} - -@reflectiveTest -class UnitInfoTest { - static bool get _areAssertsEnabled { - try { - assert(false); - return false; - } on AssertionError { - return true; - } - } - - void test_hadDiskContent_different() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.diskContent = 'abcd'; - expect(unitInfo.hadDiskContent('dcba'), false); - } - - void test_hadDiskContent_nullContentMatchesEmptyString() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.diskContent = ''; - expect(unitInfo.hadDiskContent(null), true); - expect(unitInfo.hadDiskContent(''), true); - } - - void test_hadDiskContent_nullMatchesEmptyStringContent() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.diskContent = null; - expect(unitInfo.hadDiskContent(null), true); - expect(unitInfo.hadDiskContent(''), true); - } - - void test_hadDiskContent_theSame() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.diskContent = 'abcd'; - expect(unitInfo.hadDiskContent('abcd'), true); - } - - void test_hadDiskContent_usedBeforeSet_assertsDisabled() { - if (_areAssertsEnabled) return; - - final unitInfo = UnitInfo('/foo.dart'); - expect(unitInfo.hadDiskContent(''), false); - } - - void test_hadDiskContent_usedBeforeSet_assertsEnabled() { - if (!_areAssertsEnabled) return; - - final unitInfo = UnitInfo('/foo.dart'); - expect(() => unitInfo.hadDiskContent(''), throwsA(isA())); - } - - void test_handleSourceEdit() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.content = 'int x;'; - unitInfo.migrationOffsetMapper = - OffsetMapper.forEdits([SourceEdit('int'.length, 0, ' ')]); - unitInfo.handleSourceEdit(SourceEdit('int'.length, 0, '/*?*/')); - expect(unitInfo.content, 'int/*?*/ x;'); - expect(unitInfo.offsetMapper.map('in'.length), 'in'.length); - expect(unitInfo.offsetMapper.map('int'.length), 'int/*?*/'.length); - expect(unitInfo.offsetMapper.map('int x'.length), 'int/*?*/ x'.length); - expect(unitInfo.diskChangesOffsetMapper.map('in'.length), 'in'.length); - expect( - unitInfo.diskChangesOffsetMapper.map('int'.length), 'int/*?*/'.length); - expect(unitInfo.diskChangesOffsetMapper.map('int x'.length), - 'int/*?*/ x'.length); - } - - void test_handleSourceEdit_deletion() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.content = 'int/*!*/ x = null!;'; - unitInfo.migrationOffsetMapper = - OffsetMapper.forEdits([SourceEdit('int/*!*/ x = null'.length, 0, '!')]); - unitInfo.handleSourceEdit(SourceEdit('int'.length, '/*!*/'.length, '')); - expect(unitInfo.content, 'int x = null!;'); - expect(unitInfo.offsetMapper.map('in'.length), 'in'.length); - expect(unitInfo.offsetMapper.map('int/*!*/ x'.length), 'int x'.length); - expect(unitInfo.offsetMapper.map('int/*!*/ x = null'.length), - 'int x = null'.length); - expect(unitInfo.offsetMapper.map('int/*!*/ x = null;'.length), - 'int x = null!;'.length); - expect(unitInfo.diskChangesOffsetMapper.map('in'.length), 'in'.length); - expect( - unitInfo.diskChangesOffsetMapper.map('int/*!*/'.length), 'int'.length); - expect(unitInfo.diskChangesOffsetMapper.map('int/*!*/ x'.length), - 'int x'.length); - } - - void test_handleSourceEdit_regression_41894() { - final unitInfo = UnitInfo('/foo.dart'); - unitInfo.content = 'C > > x;'; - unitInfo.migrationOffsetMapper = OffsetMapper.forEdits([ - SourceEdit('C'.length, 0, ' '), - SourceEdit('C>'.length, 0, ' '), - SourceEdit('C>>'.length, 0, ' ') - ]); - unitInfo.handleSourceEdit(SourceEdit('C'.length, 0, '/*?*/')); - unitInfo.handleSourceEdit(SourceEdit('C>'.length, 0, '/*?*/')); - unitInfo.handleSourceEdit(SourceEdit('C>>'.length, 0, '/*?*/')); - - // Before 41894 was fixed, this would produce: - // - // `C/*?*/ >/*/*?*/?*/ > x;` - // - // because the diskChangesOffsetMapper contained an insertion that was - // mapped on the post-migrated content mapper instead of the disk changes - // content mapper. - // - // This essentially meant that migrationInfo thought that the second to last - // /*?*/ hint was added 3 characters too late in the file, (that's the - // *migrated* offset of the third insertion), and so making an edit within - // those three characters was mapped five characters too early. In this - // example the fourth edit was presumed, from the offset mapper logic, to - // be an insertion before the second-to-last insertion when it is in fact - // insert after it. - expect(unitInfo.content, 'C/*?*/ >/*?*/ >/*?*/ x;'); - - // Rigorous offset mapper testing. - expect(unitInfo.offsetMapper.map('C'.length), - 'C/*?*/'.length); - expect(unitInfo.offsetMapper.map('C>'.length), - 'C/*?*/ >/*?*/'.length); - expect(unitInfo.offsetMapper.map('C>>'.length), - 'C/*?*/ >/*?*/ /*?*/>'.length); - expect(unitInfo.offsetMapper.map('C>> '.length), - 'C/*?*/ >/*?*/ /*?*/> '.length); - expect(unitInfo.offsetMapper.map('C>> x'.length), - 'C/*?*/ >/*?*/ /*?*/> x'.length); - expect( - unitInfo.diskChangesOffsetMapper.map('C'.length), - 'C/*?*/'.length); - expect(unitInfo.diskChangesOffsetMapper.map('C>'.length), - 'C/*?*/>/*?*/'.length); - expect(unitInfo.diskChangesOffsetMapper.map('C>>'.length), - 'C/*?*/>/*?*//*?*/>'.length); - expect(unitInfo.diskChangesOffsetMapper.map('C>> '.length), - 'C/*?*/>/*?*//*?*/> '.length); - expect(unitInfo.diskChangesOffsetMapper.map('C>> x'.length), - 'C/*?*/>/*?*//*?*/> x'.length); - } -} diff --git a/pkg/nnbd_migration/test/front_end/migration_summary_test.dart b/pkg/nnbd_migration/test/front_end/migration_summary_test.dart deleted file mode 100644 index 15b75bd7d474..000000000000 --- a/pkg/nnbd_migration/test/front_end/migration_summary_test.dart +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:convert'; - -import 'package:analyzer/file_system/memory_file_system.dart'; -import 'package:analyzer/src/string_source.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/front_end/migration_summary.dart'; -import 'package:path/path.dart' as path; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(MigrationSummaryTestPosix); - defineReflectiveTests(MigrationSummaryTestWindows); - }); -} - -abstract class MigrationSummaryTestBase { - MemoryResourceProvider get resourceProvider; -} - -mixin MigrationSummaryTestCases on MigrationSummaryTestBase { - test_summarize_changes_by_file() { - var summaryPath = resourceProvider.convertPath('/summary.json'); - var rootPath = resourceProvider.convertPath('/project'); - var summary = MigrationSummary(summaryPath, resourceProvider, rootPath); - summary.recordChanges( - StringSource('', resourceProvider.convertPath('/project/lib/foo.dart')), - { - 0: [ - AtomicEdit.insert('x', - info: AtomicEditInfo( - NullabilityFixDescription.makeTypeNullable('int'), - const {})) - ] - }); - summary.write(); - var json = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - var separator = resourceProvider.pathContext.separator; - expect(json['changes']['byPath'], { - 'lib${separator}foo.dart': {'makeTypeNullable': 1} - }); - } -} - -@reflectiveTest -class MigrationSummaryTestPosix extends MigrationSummaryTestBase - with MigrationSummaryTestCases { - @override - final MemoryResourceProvider resourceProvider = - MemoryResourceProvider(context: path.posix); -} - -@reflectiveTest -class MigrationSummaryTestWindows extends MigrationSummaryTestBase - with MigrationSummaryTestCases { - @override - final MemoryResourceProvider resourceProvider = - MemoryResourceProvider(context: path.windows); -} diff --git a/pkg/nnbd_migration/test/front_end/mocks.dart b/pkg/nnbd_migration/test/front_end/mocks.dart deleted file mode 100644 index d09c799baeeb..000000000000 --- a/pkg/nnbd_migration/test/front_end/mocks.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2014, 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. - -import 'package:analyzer_plugin/protocol/protocol.dart'; -import 'package:test/test.dart'; - -/// A [Matcher] that check that the given [Response] has an expected identifier -/// and no error. -Matcher isResponseSuccess(String id) => _IsResponseSuccess(id); - -/// A [Matcher] that check that there are no `error` in a given [Response]. -class _IsResponseSuccess extends Matcher { - final String _id; - - _IsResponseSuccess(this._id); - - @override - Description describe(Description description) { - return description - .addDescriptionOf('response with identifier "$_id" and without error'); - } - - @override - Description describeMismatch( - item, Description mismatchDescription, Map matchState, bool verbose) { - Response? response = item as Response?; - if (response == null) { - mismatchDescription.add('is null response'); - } else { - var id = response.id; - var error = response.error; - mismatchDescription.add('has identifier "$id"'); - if (error != null) { - mismatchDescription.add(' and has error $error'); - } - } - return mismatchDescription; - } - - @override - bool matches(item, Map matchState) { - Response? response = item as Response?; - return response != null && response.id == _id && response.error == null; - } -} diff --git a/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart b/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart deleted file mode 100644 index 536560f16136..000000000000 --- a/pkg/nnbd_migration/test/front_end/navigation_tree_renderer_test.dart +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/navigation_tree_renderer.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'nnbd_migration_test_base.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(NavigationTreeRendererTest); - }); -} - -const isNavigationTreeDirectoryNode = - TypeMatcher(); - -const isNavigationTreeFileNode = TypeMatcher(); - -@reflectiveTest -class NavigationTreeRendererTest extends NnbdMigrationTestBase { - PathMapper? pathMapper; - - /// Render the navigation tree view for [files]. - Future> renderNavigationTree( - Map files) async { - await buildInfoForTestFiles(files, includedRoot: projectPath); - var migrationInfo = - MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath); - pathMapper = PathMapper(resourceProvider); - return NavigationTreeRenderer(migrationInfo, pathMapper).render(); - } - - Future test_containsEditCounts() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = 1;', - convertPath('$projectPath/lib/b.dart'): 'int b = null;', - convertPath('$projectPath/lib/c.dart'): 'int c = null;\nint d = null;', - }); - - var libNode = response[0] as NavigationTreeDirectoryNode; - expect( - libNode, - isNavigationTreeDirectoryNode.havingSubtree([ - isNavigationTreeFileNode.havingEditCount(0), - isNavigationTreeFileNode.havingEditCount(1), - isNavigationTreeFileNode.havingEditCount(2) - ])); - } - - Future test_containsHrefs() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - convertPath('$projectPath/tool.dart'): 'int c = null;', - }); - - var libNode = response[0]; - expect( - libNode, - isNavigationTreeDirectoryNode.named('lib').havingSubtree([ - isNavigationTreeDirectoryNode.named('src').havingSubtree([ - isNavigationTreeFileNode.havingHref( - '$projectPath/lib/src/b.dart', pathMapper!) - ]), - isNavigationTreeFileNode.havingHref( - '$projectPath/lib/a.dart', pathMapper!) - ])); - - var toolNode = response[1] as NavigationTreeFileNode; - expect( - toolNode.href, pathMapper!.map(convertPath('$projectPath/tool.dart'))); - } - - Future test_containsMultipleLinks_multipleDepths() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - convertPath('$projectPath/tool.dart'): 'int c = null;', - }); - expect(response, hasLength(2)); - - var libNode = response[0]; - expect( - libNode, - isNavigationTreeDirectoryNode.named('lib').havingSubtree([ - isNavigationTreeDirectoryNode - .named('src') - .havingSubtree([isNavigationTreeFileNode.named('b.dart')]), - isNavigationTreeFileNode.named('a.dart') - ])); - - var toolNode = response[1]; - expect(toolNode.name, 'tool.dart'); - } - - Future test_containsMultipleLinks_multipleRoots() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/bin/bin.dart'): 'int c = null;', - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - }); - expect(response, hasLength(2)); - - var binNode = response[0] as NavigationTreeDirectoryNode; - expect(binNode.type, equals(NavigationTreeNodeType.directory)); - expect(binNode.name, equals('bin')); - expect(binNode.subtree, hasLength(1)); - - var libNode = response[1] as NavigationTreeDirectoryNode; - expect(libNode.type, equals(NavigationTreeNodeType.directory)); - expect(libNode.name, equals('lib')); - expect(libNode.subtree, hasLength(1)); - } - - Future test_containsMultipleLinks_sameDepth() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/b.dart'): 'int b = null;', - }); - expect(response, hasLength(1)); - - var libNode = response[0]; - expect( - libNode, - isNavigationTreeDirectoryNode.named('lib').havingSubtree([ - isNavigationTreeFileNode - .named('a.dart') - .havingPath(convertPath('lib/a.dart')) - .havingHref('$projectPath/lib/a.dart', pathMapper!), - isNavigationTreeFileNode - .named('b.dart') - .havingPath(convertPath('lib/b.dart')) - .havingHref('$projectPath/lib/b.dart', pathMapper!) - ])); - } - - Future test_containsPaths() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - convertPath('$projectPath/tool.dart'): 'int c = null;', - }); - - var libNode = response[0]; - expect( - libNode, - isNavigationTreeDirectoryNode.named('lib').havingSubtree([ - isNavigationTreeDirectoryNode.named('src').havingSubtree([ - isNavigationTreeFileNode.havingPath(convertPath('lib/src/b.dart')) - ]), - isNavigationTreeFileNode.havingPath(convertPath('lib/a.dart')) - ])); - - var toolNode = response[1]; - expect(toolNode.path, 'tool.dart'); - } - - Future test_containsSingleLink_deep() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/src/a.dart'): 'int a = null;', - }); - expect(response, hasLength(1)); - - var libNode = response[0]; - expect( - libNode, - isNavigationTreeDirectoryNode.named('lib').havingSubtree([ - isNavigationTreeDirectoryNode.named('src').havingSubtree([ - isNavigationTreeFileNode - .named('a.dart') - .havingPath(convertPath('lib/src/a.dart')) - .havingHref('$projectPath/lib/src/a.dart', pathMapper!) - ]) - ])); - } - - Future test_containsSingleLink_shallow() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/a.dart'): 'int a = null;', - }); - expect(response, hasLength(1)); - - var aNode = response[0] as NavigationTreeFileNode; - expect(aNode.name, 'a.dart'); - expect(aNode.path, 'a.dart'); - expect(aNode.href, pathMapper!.map(convertPath('$projectPath/a.dart'))); - } - - Future test_migrationStatus_directory_allMigrated() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - }); - - var libNode = response[0] as NavigationTreeDirectoryNode; - ((libNode.subtree![0] as NavigationTreeDirectoryNode).subtree![0] - as NavigationTreeFileNode) - .migrationStatus = UnitMigrationStatus.alreadyMigrated; - (libNode.subtree![1] as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.alreadyMigrated; - expect( - libNode, - isNavigationTreeDirectoryNode - .named('lib') - .havingMigrationStatus(UnitMigrationStatus.alreadyMigrated) - .havingSubtree([ - isNavigationTreeDirectoryNode - .named('src') - .havingMigrationStatus(UnitMigrationStatus.alreadyMigrated), - isNavigationTreeFileNode - ])); - } - - Future test_migrationStatus_directory_allMigrating() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - }); - - var libNode = response[0]; - expect( - libNode, - isNavigationTreeDirectoryNode - .named('lib') - .havingMigrationStatus(UnitMigrationStatus.migrating) - .havingSubtree([ - isNavigationTreeDirectoryNode - .named('src') - .havingMigrationStatus(UnitMigrationStatus.migrating), - isNavigationTreeFileNode - ])); - } - - Future test_migrationStatus_directory_allOptingOut() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - }); - - var libNode = response[0] as NavigationTreeDirectoryNode; - ((libNode.subtree![0] as NavigationTreeDirectoryNode).subtree![0] - as NavigationTreeFileNode) - .migrationStatus = UnitMigrationStatus.optingOut; - (libNode.subtree![1] as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - expect( - libNode, - isNavigationTreeDirectoryNode - .named('lib') - .havingMigrationStatus(UnitMigrationStatus.optingOut) - .havingSubtree([ - isNavigationTreeDirectoryNode - .named('src') - .havingMigrationStatus(UnitMigrationStatus.optingOut), - isNavigationTreeFileNode - ])); - } - - Future test_migrationStatus_directory_mixedInAndOut() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - convertPath('$projectPath/lib/src/c.dart'): 'int b = null;', - }); - - var libNode = response[0] as NavigationTreeDirectoryNode; - var libSrcNode = libNode.subtree![0] as NavigationTreeDirectoryNode; - (libSrcNode.subtree![0] as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - expect( - libNode, - isNavigationTreeDirectoryNode - .named('lib') - .havingMigrationStatus(UnitMigrationStatus.indeterminate) - .havingSubtree([ - isNavigationTreeDirectoryNode - .named('src') - .havingMigrationStatus(UnitMigrationStatus.indeterminate), - isNavigationTreeFileNode - ])); - } - - Future test_migrationStatus_directory_mixedInAndOutAndMigrated() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = null;', - convertPath('$projectPath/lib/src/b.dart'): 'int b = null;', - convertPath('$projectPath/lib/src/c.dart'): 'int b = null;', - }); - - var libNode = response[0] as NavigationTreeDirectoryNode; - var libSrcNode = libNode.subtree![0] as NavigationTreeDirectoryNode; - (libSrcNode.subtree![0] as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - (libSrcNode.subtree![1] as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.alreadyMigrated; - expect( - libNode, - isNavigationTreeDirectoryNode - .named('lib') - .havingMigrationStatus(UnitMigrationStatus.indeterminate) - .havingSubtree([ - isNavigationTreeDirectoryNode - .named('src') - .havingMigrationStatus(UnitMigrationStatus.optingOut), - isNavigationTreeFileNode - ])); - } - - Future test_migrationStatus_file() async { - var response = await renderNavigationTree({ - convertPath('$projectPath/lib/a.dart'): 'int a = 1;', - convertPath('$projectPath/lib/b.dart'): '// @dart=2.9\nint b = null;', - }); - - var libNode = response[0] as NavigationTreeDirectoryNode; - expect( - libNode, - isNavigationTreeDirectoryNode.havingSubtree([ - isNavigationTreeFileNode - .havingMigrationStatus(UnitMigrationStatus.migrating), - isNavigationTreeFileNode - .havingMigrationStatus(UnitMigrationStatus.migrating), - ])); - } -} - -extension on TypeMatcher { - TypeMatcher havingMigrationStatus(dynamic matcher) => - having((node) => node.migrationStatus, 'migrationStatus', matcher); - - TypeMatcher named(dynamic matcher) => - having((node) => node.name, 'name', matcher); -} - -extension on TypeMatcher { - TypeMatcher havingSubtree(dynamic matcher) => - having((node) => node.subtree, 'subtree', matcher); -} - -extension on TypeMatcher { - TypeMatcher havingEditCount(dynamic matcher) => - having((node) => node.editCount, 'editCount', matcher); - - //TypeMatcher named(dynamic matcher) => - // having((node) => node.name, 'name', matcher); - - TypeMatcher havingHref( - String path, PathMapper pathMapper) => - having((node) => node.href, 'href', pathMapper.map(path)); - - TypeMatcher havingPath(dynamic matcher) => - having((node) => node.path, 'path', matcher); -} diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart deleted file mode 100644 index 6df8f38e314f..000000000000 --- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/source/line_info.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart'; -import 'package:nnbd_migration/src/front_end/info_builder.dart'; -import 'package:nnbd_migration/src/front_end/instrumentation_listener.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/non_nullable_fix.dart'; -import 'package:nnbd_migration/src/front_end/offset_mapper.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import '../utilities/test_logger.dart'; -import 'analysis_abstract.dart'; - -class ListenerClient implements DartFixListenerClient { - @override - void onException(String detail) { - fail('Unexpected call to onException($detail)'); - } - - @override - void onFatalError(String detail) { - fail('Unexpected call to onFatalError($detail)'); - } - - @override - void onMessage(String detail) { - fail('Unexpected call to onMessage($detail)'); - } -} - -@reflectiveTest -class NnbdMigrationTestBase extends AbstractAnalysisTest { - /// The information produced by the InfoBuilder, or `null` if [buildInfo] has - /// not yet completed. - Set? infos; - NodeMapper? nodeMapper; - - /// Assert that some target in [targets] has various properties. - void assertInTargets( - {required Iterable targets, - int? offset, - int? length, - OffsetMapper? offsetMapper}) { - var failureReasons = [ - if (offset != null) 'offset: $offset', - if (length != null) 'length: $length', - if (offsetMapper != null) 'match a custom offset mapper', - ].join(' and '); - offsetMapper ??= OffsetMapper.identity; - expect(targets.any((t) { - return (offset == null || offset == offsetMapper!.map(t.offset)) && - (length == null || length == t.length); - }), isTrue, reason: 'Expected one of $targets to contain $failureReasons'); - } - - /// Assert various properties of the given [region]. If an [offset] is - /// provided but no [length] is provided, a default length of `1` will be - /// used. - void assertRegion( - {required RegionInfo region, - int? offset, - int? length, - Object explanation = anything, - Object edits = anything, - Object traces = anything, - Object kind = NullabilityFixKind.makeTypeNullable}) { - if (offset != null) { - expect(region.offset, offset); - expect(region.length, length ?? 1); - } else if (length != null) { - expect(region.length, length); - } - expect(region.kind, kind); - expect(region.edits, edits); - expect(region.explanation, explanation); - expect(region.traces, traces); - } - - /// Asserts various properties of the pair of [regions], `regions[index]` and - /// `regions[index + 1]`. The expected offsets and lengths are specified - /// separately; everything else is asserted using the same matcher. - void assertRegionPair(List regions, int index, - {int? offset1, - int? length1, - int? offset2, - int? length2, - Object explanation = anything, - Object edits = anything, - Object traces = anything, - Object kind = anything}) { - assertRegion( - region: regions[index], - offset: offset1, - length: length1, - explanation: explanation, - edits: edits, - traces: traces, - kind: kind); - assertRegion( - region: regions[index + 1], - offset: offset2, - length: length2, - explanation: explanation, - edits: edits, - traces: traces, - kind: kind); - } - - void assertTraceEntry(UnitInfo unit, TraceEntryInfo entryInfo, - String function, int offset, Object descriptionMatcher, - {Set? hintActions}) { - assert(offset >= 0); - var lineInfo = LineInfo.fromContent(unit.content!); - var expectedLocation = lineInfo.getLocation(offset); - expect(entryInfo.target!.filePath, unit.path); - expect(entryInfo.target!.line, expectedLocation.lineNumber); - expect(unit.offsetMapper.map(entryInfo.target!.offset), offset); - expect(entryInfo.function, function); - expect(entryInfo.description, descriptionMatcher); - if (hintActions != null) { - assertTraceHintActions(unit, entryInfo, hintActions, offset); - } - } - - void assertTraceHintActions(UnitInfo unit, TraceEntryInfo traceEntry, - Set expectedHints, int nodeOffset) { - final actionsByKind = Map.fromIterables( - traceEntry.hintActions.map((action) => action.kind), - traceEntry.hintActions); - expect(actionsByKind, hasLength(expectedHints.length)); - for (final expectedHint in expectedHints) { - final action = actionsByKind[expectedHint]!; - expect(action, isNotNull); - final node = nodeMapper!.nodeForId(action.nodeId)!; - expect(node, isNotNull); - expect(unit.offsetMapper.map(node.codeReference!.offset), nodeOffset); - } - } - - /// Uses the InfoBuilder to build information for [testFile]. - /// - /// The information is stored in [infos]. - Future buildInfo( - {bool removeViaComments = true, bool warnOnWeakCode = false}) async { - var includedRoot = resourceProvider.pathContext.dirname(testFile!); - await _buildMigrationInfo([testFile], - includedRoot: includedRoot, - shouldBeMigratedFunction: (String? path) => true, - pathsToProcess: [testFile], - removeViaComments: removeViaComments, - warnOnWeakCode: warnOnWeakCode); - } - - /// Uses the InfoBuilder to build information for a single test file. - /// - /// Asserts that [originalContent] is migrated to [migratedContent]. Returns - /// the singular UnitInfo which was built. - Future buildInfoForSingleTestFile(String originalContent, - {required String migratedContent, - bool removeViaComments = true, - bool warnOnWeakCode = false}) async { - addTestFile(originalContent); - await buildInfo( - removeViaComments: removeViaComments, warnOnWeakCode: warnOnWeakCode); - // Ignore info for dart:core. - var filteredInfos = [ - for (var info in infos!) - if (!info.path!.contains('core.dart')) info - ]; - expect(filteredInfos, hasLength(1)); - var unit = filteredInfos[0]; - expect(unit.path, testFile); - expect(unit.content, migratedContent); - return unit; - } - - /// Uses the [InfoBuilder] to build information for test files. - /// - /// Returns the singular [UnitInfo] which was built. - Future> buildInfoForTestFiles(Map files, - {required String? includedRoot, - bool Function(String?)? shouldBeMigratedFunction, - Iterable? pathsToProcess}) async { - shouldBeMigratedFunction ??= (String? path) => true; - var testPaths = []; - files.forEach((String path, String content) { - newFile(path, content); - testPaths.add(path); - }); - pathsToProcess ??= testPaths; - await _buildMigrationInfo(testPaths, - includedRoot: includedRoot, - shouldBeMigratedFunction: shouldBeMigratedFunction, - pathsToProcess: pathsToProcess); - // Ignore info for dart:core. - var filteredInfos = [ - for (var info in infos!) - if (!info.path!.contains('core.dart')) info - ]; - return filteredInfos; - } - - void setUp() { - super.setUp(); - nodeMapper = SimpleNodeMapper(); - } - - /// Uses the InfoBuilder to build information for files at [testPaths], which - /// should all share a common parent directory, [includedRoot]. - Future _buildMigrationInfo(Iterable testPaths, - {required String? includedRoot, - required bool Function(String?) shouldBeMigratedFunction, - required Iterable pathsToProcess, - bool removeViaComments = true, - bool warnOnWeakCode = false}) async { - // Compute the analysis results. - var server = DriverProviderImpl(resourceProvider, driver!.analysisContext); - // Run the migration engine. - var listener = DartFixListener(server, ListenerClient()); - var instrumentationListener = InstrumentationListener(); - var adapter = NullabilityMigrationAdapter(listener); - var migration = NullabilityMigration(adapter, - permissive: false, - instrumentation: instrumentationListener, - removeViaComments: removeViaComments, - warnOnWeakCode: warnOnWeakCode); - Future forEachPath(void Function(ResolvedUnitResult) callback) async { - for (var testPath in testPaths) { - var result = await driver!.currentSession.getResolvedUnit(testPath!) - as ResolvedUnitResult; - callback(result); - } - } - - await forEachPath(migration.prepareInput); - expect(migration.unmigratedDependencies, isEmpty); - await forEachPath(migration.processInput); - await forEachPath(migration.finalizeInput); - migration.finish(); - // Build the migration info. - var info = instrumentationListener.data; - var logger = TestLogger(false); - var builder = InfoBuilder( - resourceProvider, - includedRoot, - info, - listener, - migration, - nodeMapper, - logger, - shouldBeMigratedFunction, - pathsToProcess); - infos = await builder.explainMigration(); - } -} diff --git a/pkg/nnbd_migration/test/front_end/offset_mapper_test.dart b/pkg/nnbd_migration/test/front_end/offset_mapper_test.dart deleted file mode 100644 index 011210829b32..000000000000 --- a/pkg/nnbd_migration/test/front_end/offset_mapper_test.dart +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:nnbd_migration/src/front_end/offset_mapper.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'analysis_abstract.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(OffsetMapperTest); - }); -} - -@reflectiveTest -class OffsetMapperTest extends AbstractAnalysisTest { - void test_identity() { - var mapper = OffsetMapper.identity; - expect(mapper.map(0), 0); - expect(mapper.map(20), 20); - expect(mapper.map(0xFFFFFF), 0xFFFFFF); - } - - void test_insertMapper() { - var mapper = OffsetMapper.forInsertion(10, 5); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), 15); - expect(mapper.map(11), 16); - expect(mapper.map(20), 25); - } - - void test_multipleEdits() { - var mapper = OffsetMapper.forEdits([ - SourceEdit(13, 0, '?'), - SourceEdit(21, 0, '!'), - SourceEdit(32, 0, '?'), - ]); - expect(mapper.map(0), 0); - expect(mapper.map(13), 13); - expect(mapper.map(14), 15); - expect(mapper.map(21), 22); - expect(mapper.map(22), 24); - expect(mapper.map(32), 34); - expect(mapper.map(33), 36); - expect(mapper.map(55), 58); - } - - void test_rebase_deletionRebasingInsertion() { - var mapper = OffsetMapper.rebase(OffsetMapper.forReplacement(5, 2, ''), - OffsetMapper.forInsertion(10, 5)); - expect(mapper.map(0), 0); - expect(mapper.map(4), 4); - expect(mapper.map(5), null); - expect(mapper.map(6), null); - expect(mapper.map(7), 5); - expect(mapper.map(8), 6); - expect(mapper.map(11), 14); - expect(mapper.map(12), 15); - } - - void test_rebase_insertionRebasingDeletion() { - var mapper = OffsetMapper.rebase(OffsetMapper.forInsertion(5, 5), - OffsetMapper.forReplacement(10, 5, '')); - expect(mapper.map(0), 0); - expect(mapper.map(4), 4); - expect(mapper.map(5), 10); - expect(mapper.map(6), 11); - expect(mapper.map(9), 14); - expect(mapper.map(10), null); - expect(mapper.map(14), null); - expect(mapper.map(15), 15); - expect(mapper.map(16), 16); - } - - void test_rebase_insertMapper() { - var mapper = OffsetMapper.rebase( - OffsetMapper.forInsertion(5, 5), OffsetMapper.forInsertion(10, 5)); - expect(mapper.map(0), 0); - expect(mapper.map(4), 4); - expect(mapper.map(5), 10); - expect(mapper.map(6), 11); - expect(mapper.map(9), 14); - expect(mapper.map(10), 20); - expect(mapper.map(11), 21); - expect(mapper.map(12), 22); - } - - void test_replacementMapper_effectivelyDeletes() { - var mapper = OffsetMapper.forReplacement(10, 5, ''); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(14), null); - expect(mapper.map(15), 10); - } - - void test_replacementMapper_effectivelyDeletesAndInserts() { - var mapper = OffsetMapper.forReplacement(10, 5, 'foo'); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(14), null); - expect(mapper.map(15), 13); - expect(mapper.map(16), 14); - } - - void test_replacementMapper_effectivelyInserts() { - var mapper = OffsetMapper.forReplacement(10, 0, 'five '); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), 15); - expect(mapper.map(11), 16); - expect(mapper.map(20), 25); - } - - void test_sequence_insertMappers() { - var mapper = OffsetMapper.sequence( - OffsetMapper.forInsertion(30, 10), OffsetMapper.forInsertion(10, 10)); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), 20); - expect(mapper.map(11), 21); - expect(mapper.map(29), 39); - expect(mapper.map(30), 50); - expect(mapper.map(31), 51); - } - - void test_sequence_insertMappers_firstBeforeSecond() { - var mapper = OffsetMapper.sequence( - OffsetMapper.forInsertion(10, 10), OffsetMapper.forInsertion(30, 10)); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), 20); - expect(mapper.map(11), 21); - expect(mapper.map(19), 29); - expect(mapper.map(20), 40); - expect(mapper.map(21), 41); - } - - void test_sequence_insertMappers_overlapping() { - // by inserting into the middle of a previous insertion, we just effectively - // make the first insertion longer. - var mapper = OffsetMapper.sequence( - OffsetMapper.forInsertion(10, 10), OffsetMapper.forInsertion(15, 5)); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), 25); - expect(mapper.map(11), 26); - expect(mapper.map(20), 35); - } - - void test_sequence_replacementMappers_deleteAmidInsert() { - // Deleting in the middle of an insertion is equivalent to originally making - // a smaller insertion. - var mapper = OffsetMapper.sequence( - OffsetMapper.forReplacement(10, 0, 'five '), - OffsetMapper.forReplacement(12, 3, '')); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), 12); - expect(mapper.map(11), 13); - } - - void test_sequence_replacementMappers_deleteAroundInsert() { - // Deleting around an insertion is equivalent to originally making a smaller - // deletion. - var mapper = OffsetMapper.sequence( - OffsetMapper.forReplacement(15, 0, 'five '), - OffsetMapper.forReplacement(10, 15, '')); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(19), null); - expect(mapper.map(20), 10); - expect(mapper.map(21), 11); - } - - void test_sequence_replacementMappers_deleteIntoInsert() { - // A deletion starting before an insertion that goes into the middle of an - // insertion is equivalent to a smaller deletion + a smaller insertion. - var mapper = OffsetMapper.sequence( - OffsetMapper.forReplacement(10, 0, 'five '), - OffsetMapper.forReplacement(5, 7, '')); - expect(mapper.map(0), 0); - expect(mapper.map(4), 4); - expect(mapper.map(5), null); - expect(mapper.map(6), null); - expect(mapper.map(10), 8); - expect(mapper.map(11), 9); - } - - void test_sequence_replacementMappers_deletePastInsert() { - // A deletion starting in the middle of an insertion that goes past the end - // is equivalent to a smaller insertion + a smaller deletion. - var mapper = OffsetMapper.sequence( - OffsetMapper.forReplacement(10, 0, 'five '), - OffsetMapper.forReplacement(12, 10, '')); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(16), null); - expect(mapper.map(17), 12); - expect(mapper.map(18), 13); - } - - void test_sequence_replacementMappers_insertAmidDeletion() { - // Inserting at a deletion offset is equivalent to inserting after the - // deletion. - var mapper = OffsetMapper.sequence(OffsetMapper.forReplacement(10, 5, ''), - OffsetMapper.forReplacement(10, 0, 'foo')); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(14), null); - expect(mapper.map(15), 13); - expect(mapper.map(16), 14); - } - - void test_sequence_replacementMappers_twoDeletions_consecutive() { - var mapper = OffsetMapper.sequence(OffsetMapper.forReplacement(10, 5, ''), - OffsetMapper.forReplacement(10, 5, '')); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(19), null); - expect(mapper.map(20), 10); - expect(mapper.map(21), 11); - } - - void test_sequence_replacementMappers_twoDeletions_separate() { - var mapper = OffsetMapper.sequence(OffsetMapper.forReplacement(10, 5, ''), - OffsetMapper.forReplacement(15, 5, '')); - expect(mapper.map(0), 0); - expect(mapper.map(9), 9); - expect(mapper.map(10), null); - expect(mapper.map(11), null); - expect(mapper.map(14), null); - expect(mapper.map(15), 10); - expect(mapper.map(16), 11); - expect(mapper.map(19), 14); - expect(mapper.map(20), null); - expect(mapper.map(21), null); - expect(mapper.map(24), null); - expect(mapper.map(25), 15); - expect(mapper.map(26), 16); - } - - void test_singleEdit() { - var mapper = OffsetMapper.forEdits([ - SourceEdit(13, 0, '?'), - ]); - expect(mapper.map(0), 0); - expect(mapper.map(13), 13); - expect(mapper.map(14), 15); - } -} diff --git a/pkg/nnbd_migration/test/front_end/region_renderer_test.dart b/pkg/nnbd_migration/test/front_end/region_renderer_test.dart deleted file mode 100644 index 8fb31f64d83b..000000000000 --- a/pkg/nnbd_migration/test/front_end/region_renderer_test.dart +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/region_renderer.dart'; -import 'package:nnbd_migration/src/front_end/web/edit_details.dart'; -import 'package:path/path.dart' as p; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'nnbd_migration_test_base.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(RegionRendererTest); - defineReflectiveTests(RegionRendererTestDriveD); - }); -} - -@reflectiveTest -class RegionRendererTest extends RegionRendererTestBase { - /// Returns the basename of [testFile], used in traces. - String get _testFileBasename => - resourceProvider.pathContext.basename(testFile!); - - Future test_informationalRegion_containsTrace() async { - await buildInfoForSingleTestFile('f(int a) => a.isEven;', - migratedContent: 'f(int a) => a.isEven;'); - var response = renderRegion(5); - expect(response.traces, hasLength(1)); - var trace = response.traces![0]; - expect(trace.description, equals('Non-nullability reason')); - } - - Future test_informationalRegion_containsTraceEntryDescriptions() async { - await buildInfoForSingleTestFile('f(int a) => a.isEven;', - migratedContent: 'f(int a) => a.isEven;'); - var response = renderRegion(5); - expect(response.traces, hasLength(1)); - var trace = response.traces![0]; - expect(trace.entries, hasLength(2)); - expect(trace.entries[0].description, - equals('parameter 0 of f ($_testFileBasename:1:3)')); - expect(trace.entries[1].description, equals('data flow')); - } - - Future test_informationalRegion_containsTraceLinks() async { - await buildInfoForSingleTestFile('f(int a) => a.isEven;', - migratedContent: 'f(int a) => a.isEven;'); - var response = renderRegion(5); - expect(response.traces, hasLength(1)); - var trace = response.traces![0]; - var entry = trace.entries[0]; - expect(entry.link, isNotNull); - var testFileUriPath = resourceProvider.pathContext.toUri(testFile!).path; - expect(entry.link!.href, - equals('$testFileUriPath?offset=2&line=1&authToken=AUTH_TOKEN')); - expect(entry.link!.path, - equals(resourceProvider.pathContext.toUri(_testFileBasename).path)); - } - - Future test_modifiedOutput_containsExplanation() async { - await buildInfoForSingleTestFile('int a = null;', - migratedContent: 'int? a = null;'); - var response = renderRegion(3); - expect(response.explanation, equals("Changed type 'int' to be nullable")); - } - - Future test_modifiedOutput_containsPath() async { - await buildInfoForSingleTestFile('int a = null;', - migratedContent: 'int? a = null;'); - var response = renderRegion(3); - expect(response.displayPath, equals(testFile)); - expect(response.uriPath, equals(pathMapper!.map(testFile!))); - expect(response.line, equals(1)); - } - - Future test_modifiedOutput_containsTraceForNullabilityReason() async { - await buildInfoForSingleTestFile('int a = null;', - migratedContent: 'int? a = null;'); - var response = renderRegion(3); - expect(response.traces, hasLength(1)); - var trace = response.traces![0]; - expect(trace.description, equals('Nullability reason')); - expect(trace.entries, hasLength(4)); - expect(trace.entries[0].description, equals('a ($_testFileBasename:1:1)')); - expect(trace.entries[1].description, equals('data flow')); - expect(trace.entries[2].description, - equals('null literal ($_testFileBasename:1:9)')); - expect(trace.entries[3].description, equals('literal expression')); - } - - Future test_unmodifiedOutput_containsExplanation() async { - await buildInfoForSingleTestFile('f(int a) => a.isEven;', - migratedContent: 'f(int a) => a.isEven;'); - var response = renderRegion(5); - expect(response.explanation, equals("Type 'int' was not made nullable")); - } - - Future test_unmodifiedOutput_containsPath() async { - await buildInfoForSingleTestFile('f(int a) => a.isEven;', - migratedContent: 'f(int a) => a.isEven;'); - var response = renderRegion(5); - expect(response.displayPath, equals(testFile)); - expect(response.uriPath, equals(pathMapper!.map(testFile!))); - expect(response.line, equals(1)); - } -} - -class RegionRendererTestBase extends NnbdMigrationTestBase { - PathMapper? pathMapper; - - /// Render the region at [offset], using a [MigrationInfo] which knows only - /// about the library at `infos.single`. - EditDetails renderRegion(int offset) { - var migrationInfo = - MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath); - var unitInfo = infos!.single; - var region = unitInfo.regionAt(offset); - pathMapper = PathMapper(resourceProvider); - return RegionRenderer( - region, unitInfo, migrationInfo, pathMapper, 'AUTH_TOKEN') - .render(); - } -} - -@reflectiveTest -class RegionRendererTestDriveD extends RegionRendererTestBase { - @override - String get homePath => _switchToDriveD(super.homePath); - - Future - test_informationalRegion_containsTraceLinks_separateDrive() async { - // See https://github.com/dart-lang/sdk/issues/43178. Linking from a file on - // one drive to a file on another drive can cause problems. - await buildInfoForSingleTestFile(r''' -f(List a) { - if (1 == 2) List.from(a); -} -g() { - f(null); -} -''', migratedContent: r''' -f(List? a) { - if (1 == 2) List.from(a!); -} -g() { - f(null); -} -'''); - var response = renderRegion(44); // The inserted null-check. - expect(response.displayPath, - equals(_switchToDriveD(convertPath('/home/tests/bin/test.dart')))); - expect(response.traces, hasLength(2)); - var trace = response.traces![1]; - expect(trace.description, equals('Non-nullability reason')); - expect(trace.entries, hasLength(1)); - var entry = trace.entries[0]; - expect(entry.link, isNotNull); - var sdkCoreLib = convertPath('/sdk/lib/core/core.dart'); - var sdkCoreLibUriPath = resourceProvider.pathContext.toUri(sdkCoreLib).path; - var coreLibText = resourceProvider.getFile(sdkCoreLib).readAsStringSync(); - var expectedOffset = - 'List.from'.allMatches(coreLibText).single.start + 'List.'.length; - var expectedLine = - '\n'.allMatches(coreLibText.substring(0, expectedOffset)).length + 1; - expect( - entry.link!.href, - equals('$sdkCoreLibUriPath?' - 'offset=$expectedOffset&' - 'line=$expectedLine&' - 'authToken=AUTH_TOKEN')); - // On Windows, the path will simply be the absolute path to the core - // library, because there is no relative route from C:\ to D:\. On Posix, - // the path is relative. - var expectedLinkPath = resourceProvider.pathContext.style == p.Style.windows - ? sdkCoreLibUriPath - : '../../..$sdkCoreLibUriPath'; - expect(entry.link!.path, equals(expectedLinkPath)); - } - - /// On Windows, replace the C:\ relative root in [path] with the D:\ relative - /// root. - /// - /// On Posix, nothing is be replaced. - String _switchToDriveD(String path) { - assert(resourceProvider.pathContext.isAbsolute(path)); - return resourceProvider - .convertPath(path) - .replaceFirst(RegExp('^C:\\\\'), 'D:\\'); - } -} diff --git a/pkg/nnbd_migration/test/front_end/test_all.dart b/pkg/nnbd_migration/test/front_end/test_all.dart deleted file mode 100644 index 7c88db822779..000000000000 --- a/pkg/nnbd_migration/test/front_end/test_all.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'info_builder_test.dart' as info_builder; -import 'instrumentation_renderer_test.dart' as instrumentation_renderer; -import 'migration_info_test.dart' as migration_info; -import 'migration_summary_test.dart' as migration_summary; -import 'navigation_tree_renderer_test.dart' as navigation_tree_renderer; -import 'offset_mapper_test.dart' as offset_mapper; -import 'region_renderer_test.dart' as region_renderer; -import 'unit_renderer_test.dart' as unit_renderer; - -main() { - defineReflectiveSuite(() { - info_builder.main(); - instrumentation_renderer.main(); - migration_info.main(); - migration_summary.main(); - navigation_tree_renderer.main(); - offset_mapper.main(); - region_renderer.main(); - unit_renderer.main(); - }, name: 'front_end'); -} diff --git a/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart b/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart deleted file mode 100644 index 695916df81c5..000000000000 --- a/pkg/nnbd_migration/test/front_end/unit_renderer_test.dart +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/unit_renderer.dart'; -import 'package:nnbd_migration/src/front_end/web/file_details.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'nnbd_migration_test_base.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(UnitRendererTest); - }); -} - -@reflectiveTest -class UnitRendererTest extends NnbdMigrationTestBase { - /// Render [libraryInfo], using a [MigrationInfo] which knows only about this - /// library. - List renderUnits() { - var packageRoot = convertPath('/package'); - var migrationInfo = - MigrationInfo(infos, {}, resourceProvider.pathContext, packageRoot); - - var contents = []; - for (var unitInfo in infos!) { - contents.add(UnitRenderer(unitInfo, migrationInfo, - PathMapper(resourceProvider), 'AUTH_TOKEN') - .render()); - } - return contents; - } - - Future test_editList_containsCount() async { - await buildInfoForSingleTestFile(''' -int a = null; -bool b = a.isEven; -''', migratedContent: ''' -int? a = null; -bool b = a!.isEven; -'''); - var output = renderUnits()[0]; - var editList = output.edits; - expect(editList, hasLength(2)); - } - - Future test_editList_containsEdits() async { - await buildInfoForSingleTestFile(''' -int a = null; -bool b = a.isEven; -''', migratedContent: ''' -int? a = null; -bool b = a!.isEven; -'''); - var output = renderUnits()[0]; - // The null checks are higher priority than the assertions. - expect(output.edits.keys, - orderedEquals(['1 null check added', '1 type made nullable'])); - var typesMadeNullable = output.edits['1 type made nullable']!; - expect(typesMadeNullable, hasLength(1)); - var typeMadeNullable = typesMadeNullable.single; - expect(typeMadeNullable.line, equals(1)); - expect(typeMadeNullable.offset, equals(3)); - expect(typeMadeNullable.explanation, - equals("Changed type 'int' to be nullable")); - var nullChecks = output.edits['1 null check added']!; - expect(nullChecks, hasLength(1)); - var nullCheck = nullChecks.single; - expect(nullCheck.line, equals(2)); - expect(nullCheck.offset, equals(26)); - expect(nullCheck.explanation, - equals('Added a non-null assertion to nullable expression')); - } - - Future test_editList_countsHintAcceptanceSingly() async { - await buildInfoForSingleTestFile('int f(int/*?*/ x) => x/*!*/;', - migratedContent: 'int f(int/*?*/ x) => x/*!*/;'); - var output = renderUnits()[0]; - expect( - output.edits.keys, - unorderedEquals([ - '1 null check hint converted to null check', - '1 nullability hint converted to ?' - ])); - } - - Future test_editList_countsHintAcceptanceSingly_late() async { - await buildInfoForSingleTestFile('/*late*/ int x = 0;', - migratedContent: '/*late*/ int x = 0;'); - var output = renderUnits()[0]; - expect(output.edits.keys, - unorderedEquals(['1 late hint converted to late keyword'])); - } - - Future test_editList_countsHintAcceptanceSingly_lateFinal() async { - await buildInfoForSingleTestFile('/*late final*/ int x = 0;', - migratedContent: '/*late final*/ int x = 0;'); - var output = renderUnits()[0]; - expect( - output.edits.keys, - unorderedEquals( - ['1 late final hint converted to late and final keywords'])); - } - - Future test_editList_countsWhereOrNullSingly() async { - await buildInfoForSingleTestFile( - ''' -import 'package:collection/collection.dart'; - -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -''', - removeViaComments: false, - migratedContent: ''' -import 'package:collection/collection.dart'; - -int? firstEven(Iterable x) - => x.firstWherefirstWhereOrNull((x) => x.isEven, orElse: () => null); -'''); - var output = renderUnits()[0]; - expect(output.edits.keys, - unorderedEquals(['1 method name changed', '1 type made nullable'])); - } - - Future test_editList_pluralHeader() async { - await buildInfoForSingleTestFile(''' -int a = null; -int b = null; -''', migratedContent: ''' -int? a = null; -int? b = null; -'''); - var output = renderUnits()[0]; - expect(output.edits.keys.toList(), ['2 types made nullable']); - } - - Future test_handle_large_deleted_region_near_top_of_file() async { - await buildInfoForSingleTestFile(''' -class C { - int hash(Iterable elements) { - if (elements == null) { - return null.hashCode; - } - return 0; - } -} - -List x = [null]; -''', migratedContent: ''' -class C { - int hash(Iterable? elements) { - if (elements == null) { - return null.hashCode; - } - return 0; - } -} - -List x = [null]; -''', removeViaComments: false); - renderUnits(); - // No assertions necessary; we are checking to make sure there is no crash. - } - - Future test_info_within_deleted_code() async { - await buildInfoForSingleTestFile(''' -class C { - int hash(Iterable elements) { - if (elements == null) { - return null.hashCode; - } - return 0; - } -} - -List x = [null]; -''', migratedContent: ''' -class C { - int hash(Iterable? elements) { - if (elements == null) { - return null.hashCode; - } - return 0; - } -} - -List x = [null]; -''', removeViaComments: false); - var output = renderUnits()[0]; - // Strip out URLs and span IDs; they're not being tested here. - var navContent = output.navigationContent! - .replaceAll(RegExp('href="[^"]*"'), 'href="..."') - .replaceAll(RegExp('id="[^"]*"'), 'id="..."'); - expect(navContent, ''' -class C { - int hash(Iterable<int >? elements) { - if (elements == null) { - return null.hashCode; - } - return 0; - } -} - -List<int?> x = [null]; -'''); - } - - Future test_inserted_lines() async { - await buildInfoForSingleTestFile(''' -int f(List values) - => values.firstWhere((i) => i.isEven, orElse: () => null); -''', migratedContent: ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? f(List values) - => values.firstWherefirstWhereOrNull((i) => i.isEven/* , orElse: () => null */); -'''); - var output = renderUnits()[0]; - var regions = output.regions!; - // We're not testing the correctness of the data path, so drop it. - regions = regions.replaceAll(RegExp(' data-path="[^"]*"'), ''); - // Split the output into table rows. - var rows = regions.split(RegExp('(?='); - expect( - rows[1], - '1' - '' - '' - "import 'package:collection/collection.dart' show IterableExtension;" - ''); - expect(rows[2], '(new)'); - expect(rows[3], startsWith('(new)int')); - expect(rows[4], - startsWith('2')); - } - - void test_kindPriorityOrder() { - var nonDisplayedKinds = NullabilityFixKind.values.toSet(); - for (var kind in UnitRenderer.kindPriorityOrder) { - expect(nonDisplayedKinds.remove(kind), isTrue); - } - // The only kinds that should not be displayed are those associated with a - // place where nothing interesting occurred. - expect(nonDisplayedKinds, { - NullabilityFixKind.typeNotMadeNullable, - NullabilityFixKind.typeNotMadeNullableDueToHint - }); - } - - Future test_method_name_change() async { - addPackageFile('collection', 'collection.dart', ''); - await buildInfoForSingleTestFile( - ''' -import 'package:collection/collection.dart'; - -int f(List values) - => values.firstWhere((i) => i.isEven, orElse: () => null); -''', - removeViaComments: false, - migratedContent: ''' -import 'package:collection/collection.dart'; - -int? f(List values) - => values.firstWherefirstWhereOrNull((i) => i.isEven, orElse: () => null); -'''); - var output = renderUnits()[0]; - var regions = output.regions!; - // We aren't testing data-offset or data-line behaviors. - regions = regions.replaceAll(RegExp(' data-offset="[^"]*"'), ''); - regions = regions.replaceAll(RegExp(' data-line="[^"]*"'), ''); - expect( - regions, - contains('firstWhere' - 'firstWhereOrNull(')); - } - - Future test_navContentContainsEscapedHtml() async { - await buildInfoForSingleTestFile('List a = null;', - migratedContent: 'List? a = null;'); - var output = renderUnits()[0]; - // Strip out URLs which will change; not being tested here. - var navContent = output.navigationContent! - .replaceAll(RegExp('href=".*?"'), 'href="..."'); - expect( - navContent, - contains(r'List' - r'<String >? ' - r'a = null;')); - } - - void test_nullAwarenessUnnecessaryInStrongMode() async { - await buildInfoForSingleTestFile(''' -int f(String/*!*/ s) => s?.length; -''', migratedContent: ''' -int f(String/*!*/ s) => s?.length; -''', warnOnWeakCode: true); - var output = renderUnits()[0]; - expect(_stripDataAttributes(output.regions!), - contains('s?.length')); - expect( - output.edits.keys, - contains( - '1 null-aware access will be unnecessary in strong checking mode')); - } - - Future test_outputContains_addedType() async { - await buildInfoForSingleTestFile(''' -void f() { - final a = >[]; - a.add([null]); -} -''', migratedContent: ''' -void f() { - final List> a = >[]; - a.add([null]); -} -'''); - var output = renderUnits()[0]; - var regions = _stripDataAttributes(output.regions!); - expect( - regions, - contains('final ' - 'List<List<int?>>' - ' a = <List<int' - ' ' - '>' - ' ' - '>[];')); - } - - Future test_outputContains_replacedVar() async { - await buildInfoForSingleTestFile(''' -void f() { - var a = >[]; - a.add([null]); -} -''', migratedContent: ''' -void f() { - varList> a = >[]; - a.add([null]); -} -'''); - var output = renderUnits()[0]; - var regions = _stripDataAttributes(output.regions!); - expect( - regions, - contains('var' - 'List<List<int?>>' - ' a = <List<int' - ' ' - '>' - ' ' - '>[];')); - } - - Future test_outputContainsModifiedAndUnmodifiedRegions() async { - await buildInfoForSingleTestFile('int a = null;', - migratedContent: 'int? a = null;'); - var output = renderUnits()[0]; - var regions = _stripDataAttributes(output.regions!); - expect(regions, - contains('int? a = null;')); - } - - Future test_project_with_parts() async { - // In this test, we migrate a library and its part file. Both files require - // addition of a `?`, but at different offsets. We make sure the `?`s get - // added at the correct locations in each file. - var files = { - convertPath('$projectPath/lib/a.dart'): ''' -part 'b.dart'; - -int f() => null; -''', - convertPath('$projectPath/lib/b.dart'): ''' -part of 'a.dart'; - -int g() => null; -''', - }; - await buildInfoForTestFiles(files, includedRoot: projectPath); - var output = renderUnits(); - expect(output[0].sourceCode, contains('int?')); - expect(output[1].sourceCode, contains('int?')); - } - - Future test_reference_to_sdk_file_with_parts() async { - await buildInfoForSingleTestFile(''' -import 'dart:async'; -Future f(Future x) { - return x.whenComplete(() {}); -} -''', migratedContent: ''' -import 'dart:async'; -Future f(Future x) { - return x.whenComplete(() {}); -} -'''); - renderUnits(); - // No assertions; we're just making sure there's no crash. - } - - Future test_regionsContainsEscapedHtml_ampersand() async { - await buildInfoForSingleTestFile('bool a = true && false;', - migratedContent: 'bool a = true && false;'); - var output = renderUnits()[0]; - expect( - output.regions, - contains('bool a = true && false;')); - } - - Future test_regionsContainsEscapedHtml_betweenRegions() async { - await buildInfoForSingleTestFile('List a = null;', - migratedContent: 'List? a = null;'); - var output = renderUnits()[0]; - var regions = _stripDataAttributes(output.regions!); - expect( - regions, - contains( - 'List<String >' - '? a = null;')); - } - - Future test_regionsContainsEscapedHtml_region() async { - await buildInfoForSingleTestFile('f(List a) => a.join(",");', - migratedContent: 'f(List a) => a.join(",");'); - var output = renderUnits()[0]; - var regions = _stripDataAttributes(output.regions!); - expect( - regions, - contains( - 'List<String ' - '> ')); - } - - /// Strip out data attributes which are not being tested here. - String _stripDataAttributes(String html) => - html.replaceAll(RegExp(' data-[^=]+="[^"]+"'), ''); -} diff --git a/pkg/nnbd_migration/test/instrumentation_test.dart b/pkg/nnbd_migration/test/instrumentation_test.dart deleted file mode 100644 index adc6cf3d4882..000000000000 --- a/pkg/nnbd_migration/test/instrumentation_test.dart +++ /dev/null @@ -1,1225 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/analysis/results.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/test_utilities/find_node.dart'; -import 'package:nnbd_migration/fix_reason_target.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'abstract_context.dart'; -import 'api_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(_InstrumentationTest); - }); -} - -class _InstrumentationClient implements NullabilityMigrationInstrumentation { - final _InstrumentationTestBase test; - - _InstrumentationClient(this.test); - - @override - void changes(Source source, Map> changes) { - expect(test.changes, isNull); - test.changes = { - for (var entry in changes.entries) - if (entry.value.any((edit) => !edit.isInformative)) - entry.key: entry.value - }; - } - - @override - void explicitTypeNullability(Source? source, TypeAnnotation typeAnnotation, - NullabilityNodeInfo? node) { - expect(source, test.source); - expect(test.explicitTypeNullability, isNot(contains(typeAnnotation))); - test.explicitTypeNullability[typeAnnotation] = node; - } - - @override - void externalDecoratedType(Element element, DecoratedTypeInfo decoratedType) { - expect(test.externalDecoratedType, isNot(contains(element))); - test.externalDecoratedType[element] = decoratedType; - } - - @override - void externalDecoratedTypeParameterBound( - TypeParameterElement typeParameter, DecoratedTypeInfo decoratedType) { - expect(test.externalDecoratedTypeParameterBound, - isNot(contains(typeParameter))); - test.externalDecoratedTypeParameterBound[typeParameter] = decoratedType; - } - - @override - void finished() {} - - @override - void graphEdge(EdgeInfo edge, EdgeOriginInfo originInfo) { - if (edge.destinationNode != test.always) { - expect(test.edgeOrigin, isNot(contains(edge))); - test.edges.add(edge); - test.edgeOrigin[edge] = originInfo; - } - } - - @override - void immutableNodes(NullabilityNodeInfo never, NullabilityNodeInfo always) { - test.never = never; - test.always = always; - } - - @override - void implicitReturnType( - Source? source, AstNode node, DecoratedTypeInfo? decoratedReturnType) { - expect(source, test.source); - expect(test.implicitReturnType, isNot(contains(node))); - test.implicitReturnType[node] = decoratedReturnType; - } - - @override - void implicitType( - Source? source, AstNode? node, DecoratedTypeInfo decoratedType) { - expect(source, test.source); - expect(test.implicitType, isNot(contains(node))); - test.implicitType[node] = decoratedType; - } - - @override - void implicitTypeArguments( - Source? source, AstNode node, Iterable types) { - expect(source, test.source); - expect(test.implicitTypeArguments, isNot(contains(node))); - test.implicitTypeArguments[node] = types.toList(); - } - - @override - void prepareForUpdate() { - test.changes = null; - } -} - -@reflectiveTest -class _InstrumentationTest extends _InstrumentationTestBase {} - -abstract class _InstrumentationTestBase extends AbstractContextTest { - NullabilityNodeInfo? always; - - final Map explicitTypeNullability = {}; - - final Map externalDecoratedType = {}; - - final Map - externalDecoratedTypeParameterBound = {}; - - final List edges = []; - - Map>? changes; - - final Map implicitReturnType = {}; - - final Map implicitType = {}; - - final Map> implicitTypeArguments = {}; - - NullabilityNodeInfo? never; - - final Map edgeOrigin = {}; - - late FindNode findNode; - - Source? source; - - Future analyze(String content, - {bool removeViaComments = false, bool warnOnWeakCode = true}) async { - var sourcePath = convertPath('$testsPath/lib/test.dart'); - newFile(sourcePath, content); - var listener = TestMigrationListener(); - var migration = NullabilityMigration(listener, - instrumentation: _InstrumentationClient(this), - removeViaComments: removeViaComments, - warnOnWeakCode: warnOnWeakCode); - var result = - await session.getResolvedUnit(sourcePath) as ResolvedUnitResult; - source = result.unit.declaredElement!.source; - findNode = FindNode(content, result.unit); - migration.prepareInput(result); - expect(migration.unmigratedDependencies, isEmpty); - migration.processInput(result); - migration.finalizeInput(result); - migration.finish(); - } - - void assertEdit(AtomicEdit edit, - {dynamic description = anything, dynamic fixReasons = anything}) { - var info = edit.info!; - expect(info.description, description); - expect(info.fixReasons, fixReasons); - } - - Future test_explicitTypeNullability() async { - var content = ''' -int x = 1; -int y = null; -'''; - await analyze(content); - expect( - explicitTypeNullability[findNode.typeAnnotation('int x')]!.isNullable, - false); - expect( - explicitTypeNullability[findNode.typeAnnotation('int y')]!.isNullable, - true); - } - - Future test_externalDecoratedType() async { - await analyze(''' -main() { - print(1); -} -'''); - expect( - externalDecoratedType[findNode.simple('print').staticElement!]! - .type! - .getDisplayString(withNullability: false), - 'void Function(Object)'); - } - - Future test_externalDecoratedTypeParameterBound() async { - await analyze(''' -import 'dart:math'; -f(Point x) {} -'''); - var pointElement = findNode.namedType('Point').element as ClassElement; - var pointElementTypeParameter = pointElement.typeParameters[0]; - expect( - externalDecoratedTypeParameterBound[pointElementTypeParameter]! - .type! - .getDisplayString(withNullability: false), - 'num'); - } - - Future test_externalType_nullability_dynamic_edge() async { - await analyze(''' -f(List x) {} -'''); - var listElement = findNode.namedType('List').element as ClassElement; - var listElementTypeParameter = listElement.typeParameters[0]; - var typeParameterBoundNode = - externalDecoratedTypeParameterBound[listElementTypeParameter]!.node; - var edge = edges - .where((e) => - e.sourceNode == always && - e.destinationNode == typeParameterBoundNode) - .single; - var origin = edgeOrigin[edge]!; - expect(origin.kind, EdgeOriginKind.alwaysNullableType); - expect(origin.element, same(listElementTypeParameter)); - expect(origin.source, null); - expect(origin.node, null); - } - - Future test_fix_reason_add_required_function() async { - var content = '_f({int/*!*/ i) {}'; - await analyze(content); - var intAnnotation = findNode.typeAnnotation('int'); - var intPos = content.indexOf('int'); - var commentPos = content.indexOf('/*'); - expect(changes!.keys, unorderedEquals([intPos, commentPos])); - assertEdit(changes![intPos]!.single, - description: NullabilityFixDescription.addRequired(null, '_f', 'i'), - fixReasons: { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }); - } - - Future test_fix_reason_add_required_method() async { - var content = 'class C { _f({int/*!*/ i) {} }'; - await analyze(content); - var intAnnotation = findNode.typeAnnotation('int'); - var intPos = content.indexOf('int'); - var commentPos = content.indexOf('/*'); - expect(changes!.keys, unorderedEquals([intPos, commentPos])); - assertEdit(changes![intPos]!.single, - description: NullabilityFixDescription.addRequired('C', '_f', 'i'), - fixReasons: { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }); - } - - Future test_fix_reason_discard_condition() async { - var content = ''' -_f(int/*!*/ i) { - if (i != null) { - return i; - } -} -'''; - await analyze(content, warnOnWeakCode: false); - var intAnnotation = findNode.typeAnnotation('int'); - var commentPos = content.indexOf('/*'); - var ifPos = content.indexOf('if'); - var afterReturnPos = content.indexOf('i;') + 2; - expect(changes!.keys, unorderedEquals([commentPos, ifPos, afterReturnPos])); - var expectedFixReasons = { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }; - assertEdit(changes![ifPos]!.single, - description: NullabilityFixDescription.discardCondition, - fixReasons: expectedFixReasons); - assertEdit(changes![afterReturnPos]!.single, - description: NullabilityFixDescription.discardCondition, - fixReasons: expectedFixReasons); - } - - Future test_fix_reason_discard_condition_no_block() async { - var content = ''' -_f(int/*!*/ i) { - if (i != null) return i; -} -'''; - await analyze(content, warnOnWeakCode: false); - var intAnnotation = findNode.typeAnnotation('int'); - var commentPos = content.indexOf('/*'); - var ifPos = content.indexOf('if'); - expect(changes!.keys, unorderedEquals([commentPos, ifPos])); - assertEdit(changes![ifPos]!.single, - description: NullabilityFixDescription.discardCondition, - fixReasons: { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }); - } - - Future test_fix_reason_discard_else() async { - var content = ''' -_f(int/*!*/ i) { - if (i != null) { - return i; - } else { - return 'null'; - } -} -'''; - await analyze(content, warnOnWeakCode: false); - var intAnnotation = findNode.typeAnnotation('int'); - var commentPos = content.indexOf('/*'); - var ifPos = content.indexOf('if'); - var afterReturnPos = content.indexOf('i;') + 2; - expect(changes!.keys, unorderedEquals([commentPos, ifPos, afterReturnPos])); - var expectedFixReasons = { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }; - assertEdit(changes![ifPos]!.single, - description: NullabilityFixDescription.discardCondition, - fixReasons: expectedFixReasons); - assertEdit(changes![afterReturnPos]!.single, - description: NullabilityFixDescription.discardElse, - fixReasons: expectedFixReasons); - } - - Future test_fix_reason_discard_else_empty_then() async { - var content = ''' -_f(int/*!*/ i) { - if (i != null) {} else { - return 'null'; - } -} -'''; - await analyze(content, warnOnWeakCode: false); - var intAnnotation = findNode.typeAnnotation('int'); - var commentPos = content.indexOf('/*'); - var bodyPos = content.indexOf('i) {') + 4; - expect(changes!.keys, unorderedEquals([commentPos, bodyPos])); - assertEdit(changes![bodyPos]!.single, - description: NullabilityFixDescription.discardIf, - fixReasons: { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }); - } - - Future test_fix_reason_discard_then() async { - var content = ''' -_f(int/*!*/ i) { - if (i == null) { - return 'null'; - } else { - return i; - } -} -'''; - await analyze(content, warnOnWeakCode: false); - var intAnnotation = findNode.typeAnnotation('int'); - var commentPos = content.indexOf('/*'); - var ifPos = content.indexOf('if'); - var afterReturnPos = content.indexOf('i;') + 2; - expect(changes!.keys, unorderedEquals([commentPos, ifPos, afterReturnPos])); - var expectedFixReasons = { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }; - assertEdit(changes![ifPos]!.single, - description: NullabilityFixDescription.discardThen, - fixReasons: expectedFixReasons); - assertEdit(changes![afterReturnPos]!.single, - description: NullabilityFixDescription.discardThen, - fixReasons: expectedFixReasons); - } - - Future test_fix_reason_discard_then_no_else() async { - var content = ''' -_f(int/*!*/ i) { - if (i == null) { - return 'null'; - } -} -'''; - await analyze(content, warnOnWeakCode: false); - var intAnnotation = findNode.typeAnnotation('int'); - var commentPos = content.indexOf('/*'); - var bodyPos = content.indexOf('i) {') + 4; - expect(changes!.keys, unorderedEquals([commentPos, bodyPos])); - assertEdit(changes![bodyPos]!.single, - description: NullabilityFixDescription.discardIf, - fixReasons: { - FixReasonTarget.root: same(explicitTypeNullability[intAnnotation]) - }); - } - - Future test_fix_reason_edge() async { - await analyze(''' -void f(int x) { - print(x.isEven); -} -void g(int y, bool b) { - if (b) { - f(y); - } -} -main() { - g(null, false); -} -'''); - var yUsage = findNode.simple('y);'); - var edit = changes![yUsage.end]!.single; - expect(edit.isInsertion, true); - expect(edit.replacement, '!'); - var info = edit.info!; - expect(info.description, NullabilityFixDescription.checkExpression); - var reasons = info.fixReasons; - expect(reasons, hasLength(1)); - var edge = reasons[FixReasonTarget.root] as EdgeInfo; - expect(edge.sourceNode, - same(explicitTypeNullability[findNode.typeAnnotation('int y')])); - expect(edge.destinationNode, - same(explicitTypeNullability[findNode.typeAnnotation('int x')])); - expect(edge.isSatisfied, false); - expect(edgeOrigin[edge]!.node, same(yUsage)); - } - - Future test_fix_reason_node() async { - await analyze(''' -int x = null; -'''); - var intAnnotation = findNode.typeAnnotation('int'); - var entries = changes!.entries.toList(); - expect(entries, hasLength(1)); - expect(entries.single.key, intAnnotation.end); - var edit = entries.single.value.single; - expect(edit.isInsertion, true); - expect(edit.replacement, '?'); - var info = edit.info!; - expect(info.description, NullabilityFixDescription.makeTypeNullable('int')); - var reasons = info.fixReasons; - expect(reasons, hasLength(1)); - expect(reasons[FixReasonTarget.root], - same(explicitTypeNullability[intAnnotation])); - } - - Future test_fix_reason_remove_question_from_question_dot() async { - var content = '_f(int/*!*/ i) => i?.isEven;'; - await analyze(content, warnOnWeakCode: false); - var commentPos = content.indexOf('/*'); - var questionDotPos = content.indexOf('?.'); - expect(changes!.keys, unorderedEquals([commentPos, questionDotPos])); - assertEdit(changes![questionDotPos]!.single, - description: NullabilityFixDescription.removeNullAwareness, - fixReasons: isEmpty); - } - - Future - test_fix_reason_remove_question_from_question_dot_method() async { - var content = '_f(int/*!*/ i) => i?.abs();'; - await analyze(content, warnOnWeakCode: false); - var commentPos = content.indexOf('/*'); - var questionDotPos = content.indexOf('?.'); - expect(changes!.keys, unorderedEquals([commentPos, questionDotPos])); - assertEdit(changes![questionDotPos]!.single, - description: NullabilityFixDescription.removeNullAwareness, - fixReasons: isEmpty); - } - - Future test_fix_reason_remove_unnecessary_cast() async { - await analyze(''' -_f(Object x) { - if (x is! int) return; - print((x as int) + 1); -} -'''); - var xRef = findNode.simple('x as'); - var asExpression = xRef.parent as Expression; - expect(changes, hasLength(3)); - // Change #1: drop the `(` before the cast - var dropLeadingParen = changes![asExpression.offset - 1]!.single; - expect(dropLeadingParen.isDeletion, true); - expect(dropLeadingParen.length, 1); - expect(dropLeadingParen.info, null); - // Change #2: drop the text ` as int` - var dropAsInt = changes![xRef.end]!.single; - expect(dropAsInt.isDeletion, true); - expect(dropAsInt.length, 7); - expect(dropAsInt.info!.description, NullabilityFixDescription.removeAs); - expect(dropAsInt.info!.fixReasons, isEmpty); - // Change #3: drop the `)` after the cast - var dropTrailingParen = changes![asExpression.end]!.single; - expect(dropTrailingParen.isDeletion, true); - expect(dropTrailingParen.length, 1); - expect(dropTrailingParen.info, null); - } - - Future test_fix_reason_rewrite_required() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -_f({@required int i}) {} -'''); - var intAnnotation = findNode.typeAnnotation('int'); - expect(changes, isNotEmpty); - for (var change in changes!.values) { - expect(change, isNotEmpty); - for (var edit in change) { - var info = edit.info!; - expect(info.description, - NullabilityFixDescription.addRequired(null, '_f', 'i')); - expect(info.fixReasons[FixReasonTarget.root], - same(explicitTypeNullability[intAnnotation])); - } - } - } - - Future test_graphEdge() async { - await analyze(''' -int f(int x) => x; -'''); - var xNode = explicitTypeNullability[findNode.typeAnnotation('int x')]; - var returnNode = explicitTypeNullability[findNode.typeAnnotation('int f')]; - expect( - edges.where( - (e) => e.sourceNode == xNode && e.destinationNode == returnNode), - hasLength(1)); - } - - Future test_graphEdge_guards() async { - await analyze(''' -int f(int i, int j) { - if (i == null) { - return j; - } - return 1; -} -'''); - var iNode = explicitTypeNullability[findNode.typeAnnotation('int i')]; - var jNode = explicitTypeNullability[findNode.typeAnnotation('int j')]; - var returnNode = explicitTypeNullability[findNode.typeAnnotation('int f')]; - var matchingEdges = edges - .where((e) => e.sourceNode == jNode && e.destinationNode == returnNode) - .toList(); - expect(matchingEdges, hasLength(1)); - expect(matchingEdges.single.guards, hasLength(1)); - expect(matchingEdges.single.guards.single, iNode); - } - - Future test_graphEdge_hard() async { - await analyze(''' -int f(int x) => x; -'''); - var xNode = explicitTypeNullability[findNode.typeAnnotation('int x')]; - var returnNode = explicitTypeNullability[findNode.typeAnnotation('int f')]; - var matchingEdges = edges - .where((e) => e.sourceNode == xNode && e.destinationNode == returnNode) - .toList(); - expect(matchingEdges, hasLength(1)); - expect(matchingEdges.single.isUnion, false); - expect(matchingEdges.single.isHard, true); - } - - Future test_graphEdge_isSatisfied() async { - await analyze(''' -void f1(int i, bool b) { - f2(i, b); -} -void f2(int j, bool b) { - if (b) { - f3(j); - } -} -void f3(int k) { - f4(k); -} -void f4(int l) { - print(l.isEven); -} -main() { - f1(null, false); -} -'''); - var iNode = explicitTypeNullability[findNode.typeAnnotation('int i')]!; - var jNode = explicitTypeNullability[findNode.typeAnnotation('int j')]!; - var kNode = explicitTypeNullability[findNode.typeAnnotation('int k')]!; - var lNode = explicitTypeNullability[findNode.typeAnnotation('int l')]!; - var iToJ = edges - .where((e) => e.sourceNode == iNode && e.destinationNode == jNode) - .single; - var jToK = edges - .where((e) => e.sourceNode == jNode && e.destinationNode == kNode) - .single; - var kToL = edges - .where((e) => e.sourceNode == kNode && e.destinationNode == lNode) - .single; - expect(iNode.isNullable, true); - expect(jNode.isNullable, true); - expect(kNode.isNullable, false); - expect(lNode.isNullable, false); - expect(iToJ.isSatisfied, true); - expect(jToK.isSatisfied, false); - expect(kToL.isSatisfied, true); - } - - Future test_graphEdge_isUpstreamTriggered() async { - await analyze(''' -void f(int i, bool b) { - assert(i != null); - i.isEven; // unconditional - g(i); - h(i); - if (b) { - i.isEven; // conditional - } -} -void g(int/*?*/ j) {} -void h(int k) {} -'''); - var iNode = explicitTypeNullability[findNode.typeAnnotation('int i')]; - var jNode = explicitTypeNullability[findNode.typeAnnotation('int/*?*/ j')]; - var kNode = explicitTypeNullability[findNode.typeAnnotation('int k')]; - var assertNode = findNode.statement('assert'); - var unconditionalUsageNode = findNode.simple('i.isEven; // unconditional'); - var conditionalUsageNode = findNode.simple('i.isEven; // conditional'); - var nonNullEdges = edgeOrigin.entries - .where((entry) => - entry.key.sourceNode == iNode && entry.key.destinationNode == never) - .toList(); - var assertEdge = nonNullEdges - .where((entry) => entry.value.node == assertNode) - .single - .key; - var unconditionalUsageEdge = edgeOrigin.entries - .where((entry) => entry.value.node == unconditionalUsageNode) - .single - .key; - var gCallEdge = edges - .where((e) => e.sourceNode == iNode && e.destinationNode == jNode) - .single; - var hCallEdge = edges - .where((e) => e.sourceNode == iNode && e.destinationNode == kNode) - .single; - var conditionalUsageEdge = edgeOrigin.entries - .where((entry) => entry.value.node == conditionalUsageNode) - .single - .key; - // Both assertEdge and unconditionalUsageEdge are upstream triggered because - // either of them would have been sufficient to cause i to be marked as - // non-nullable. - expect(assertEdge.isUpstreamTriggered, true); - expect(unconditionalUsageEdge.isUpstreamTriggered, true); - // conditionalUsageEdge is not upstream triggered because it is a soft edge, - // so it would not have caused i to be marked as non-nullable. - expect(conditionalUsageEdge.isUpstreamTriggered, false); - // Even though gCallEdge is a hard edge, it is not upstream triggered - // because its destination node is nullable. - expect(gCallEdge.isHard, true); - expect(gCallEdge.isUpstreamTriggered, false); - // Even though hCallEdge is a hard edge and its destination node is - // non-nullable, it is not upstream triggered because k could have been made - // nullable without causing any problems, so the presence of this edge would - // not have caused i to be marked as non-nullable. - expect(hCallEdge.isHard, true); - expect(hCallEdge.isUpstreamTriggered, false); - } - - Future test_graphEdge_origin() async { - await analyze(''' -int f(int x) => x; -'''); - var xNode = explicitTypeNullability[findNode.typeAnnotation('int x')]; - var returnNode = explicitTypeNullability[findNode.typeAnnotation('int f')]; - var matchingEdges = edges - .where((e) => e.sourceNode == xNode && e.destinationNode == returnNode) - .toList(); - var origin = edgeOrigin[matchingEdges.single]!; - expect(origin.source, source); - expect(origin.node, findNode.simple('x;')); - } - - Future test_graphEdge_origin_dynamic_assignment() async { - await analyze(''' -int f(dynamic x) => x; -'''); - var xNode = explicitTypeNullability[findNode.typeAnnotation('dynamic x')]; - var returnNode = explicitTypeNullability[findNode.typeAnnotation('int f')]; - var matchingEdges = edges - .where((e) => e.sourceNode == xNode && e.destinationNode == returnNode) - .toList(); - var origin = edgeOrigin[matchingEdges.single]!; - expect(origin.kind, EdgeOriginKind.dynamicAssignment); - expect(origin.source, source); - expect(origin.node, findNode.simple('x;')); - } - - Future test_graphEdge_soft() async { - await analyze(''' -int f(int x, bool b) { - if (b) return x; - return 0; -} -'''); - var xNode = explicitTypeNullability[findNode.typeAnnotation('int x')]; - var returnNode = explicitTypeNullability[findNode.typeAnnotation('int f')]; - var matchingEdges = edges - .where((e) => e.sourceNode == xNode && e.destinationNode == returnNode) - .toList(); - expect(matchingEdges, hasLength(1)); - expect(matchingEdges.single.isUnion, false); - expect(matchingEdges.single.isHard, false); - } - - Future test_immutableNode_always() async { - await analyze(''' -int x = null; -'''); - expect(always!.isImmutable, true); - expect(always!.isNullable, true); - var xNode = explicitTypeNullability[findNode.typeAnnotation('int')]; - var edge = edges.where((e) => e.destinationNode == xNode).single; - var edgeSource = edge.sourceNode; - var upstreamEdge = - edges.where((e) => e.destinationNode == edgeSource).single; - expect(upstreamEdge.sourceNode, always); - } - - Future test_immutableNode_never() async { - await analyze(''' -bool f(int x) => x.isEven; -'''); - expect(never!.isImmutable, true); - expect(never!.isNullable, false); - var xNode = explicitTypeNullability[findNode.typeAnnotation('int')]; - var edge = edges.where((e) => e.sourceNode == xNode).single; - expect(edge.destinationNode, never); - } - - Future test_implicitReturnType_constructor() async { - await analyze(''' -class C { - factory C() => f(true); - C.named(); -} -C f(bool b) => b ? C.named() : null; -'''); - var factoryReturnNode = - implicitReturnType[findNode.constructor('C(')]!.node; - var fReturnNode = explicitTypeNullability[findNode.typeAnnotation('C f')]; - expect( - edges.where((e) => - e.sourceNode == fReturnNode && - e.destinationNode == factoryReturnNode), - hasLength(1)); - } - - Future test_implicitReturnType_formalParameter() async { - await analyze(''' -Object f(callback()) => callback(); -'''); - var paramReturnNode = implicitReturnType[ - findNode.functionTypedFormalParameter('callback())')]! - .node; - var fReturnNode = - explicitTypeNullability[findNode.typeAnnotation('Object')]; - expect( - edges.where((e) => - e.sourceNode == paramReturnNode && - e.destinationNode == fReturnNode), - hasLength(1)); - } - - Future test_implicitReturnType_function() async { - await analyze(''' -f() => 1; -Object g() => f(); -'''); - var fReturnNode = - implicitReturnType[findNode.functionDeclaration('f() =>')]!.node; - var gReturnNode = - explicitTypeNullability[findNode.typeAnnotation('Object')]; - expect( - edges.where((e) => - e.sourceNode == fReturnNode && e.destinationNode == gReturnNode), - hasLength(1)); - } - - Future test_implicitReturnType_functionExpression() async { - await analyze(''' -main() { - int Function() f = () => g(); -} -int g() => 1; -'''); - var fReturnNode = - explicitTypeNullability[findNode.typeAnnotation('int Function')]; - var functionExpressionReturnNode = - implicitReturnType[findNode.functionExpression('() => g()')]!.node; - var gReturnNode = explicitTypeNullability[findNode.typeAnnotation('int g')]; - expect( - edges.where((e) => - e.sourceNode == gReturnNode && - e.destinationNode == functionExpressionReturnNode), - hasLength(1)); - expect( - edges.where((e) => - e.sourceNode == functionExpressionReturnNode && - e.destinationNode == fReturnNode), - hasLength(1)); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/39370') - Future test_implicitReturnType_functionTypeAlias() async { - await analyze(''' -typedef F(); -Object f(F callback) => callback(); -'''); - var typedefReturnNode = - implicitReturnType[findNode.functionTypeAlias('F()')]!.node; - var fReturnNode = - explicitTypeNullability[findNode.typeAnnotation('Object')]; - expect( - edges.where((e) => - e.sourceNode == typedefReturnNode && - e.destinationNode == fReturnNode), - hasLength(1)); - } - - Future test_implicitReturnType_genericFunctionType() async { - await analyze(''' -Object f(Function() callback) => callback(); -'''); - var callbackReturnNode = - implicitReturnType[findNode.genericFunctionType('Function()')]!.node; - var fReturnNode = - explicitTypeNullability[findNode.typeAnnotation('Object')]; - expect( - edges.where((e) => - e.sourceNode == callbackReturnNode && - e.destinationNode == fReturnNode), - hasLength(1)); - } - - Future test_implicitReturnType_method() async { - await analyze(''' -abstract class Base { - int f(); -} -abstract class Derived extends Base { - f /*derived*/(); -} -'''); - var baseReturnNode = - explicitTypeNullability[findNode.typeAnnotation('int')]; - var derivedReturnNode = - implicitReturnType[findNode.methodDeclaration('f /*derived*/')]!.node; - expect( - edges.where((e) => - e.sourceNode == derivedReturnNode && - e.destinationNode == baseReturnNode), - hasLength(1)); - } - - Future test_implicitType_catch_exception() async { - await analyze(''' -void f() { - try {} catch (e) { - Object o = e; - } -} -'''); - var oNode = explicitTypeNullability[findNode.typeAnnotation('Object')]; - var eNode = implicitType[findNode.catchClauseParameter('e)')]!.node; - expect( - edges.where((e) => e.sourceNode == eNode && e.destinationNode == oNode), - hasLength(1)); - } - - Future test_implicitType_catch_stackTrace() async { - await analyze(''' -void f() { - try {} catch (e, st) { - Object o = st; - } -} -'''); - var oNode = explicitTypeNullability[findNode.typeAnnotation('Object')]; - var stNode = implicitType[findNode.catchClauseParameter('st)')]!.node; - expect( - edges - .where((e) => e.sourceNode == stNode && e.destinationNode == oNode), - hasLength(1)); - } - - Future - test_implicitType_declaredIdentifier_forEachPartsWithDeclaration() async { - await analyze(''' -void f(List l) { - for (var x in l) { - int y = x; - } -} -'''); - var xNode = implicitType[(findNode.forStatement('for').forLoopParts - as ForEachPartsWithDeclaration) - .loopVariable]! - .node; - var yNode = explicitTypeNullability[findNode.typeAnnotation('int y')]; - expect( - edges.where((e) => e.sourceNode == xNode && e.destinationNode == yNode), - hasLength(1)); - } - - Future test_implicitType_formalParameter() async { - await analyze(''' -abstract class Base { - void f(int i); -} -abstract class Derived extends Base { - void f(i); /*derived*/ -} -'''); - var baseParamNode = - explicitTypeNullability[findNode.typeAnnotation('int i')]; - var derivedParamNode = - implicitType[findNode.simpleParameter('i); /*derived*/')]!.node; - expect( - edges.where((e) => - e.sourceNode == baseParamNode && - e.destinationNode == derivedParamNode), - hasLength(1)); - } - - Future test_implicitType_namedParameter() async { - await analyze(''' -abstract class Base { - void f(void callback({int i})); -} -abstract class Derived extends Base { - void f(callback); -} -'''); - var baseParamParamNode = - explicitTypeNullability[findNode.typeAnnotation('int i')]; - var derivedParamParamNode = - implicitType[findNode.simpleParameter('callback)')]! - .namedParameter('i')! - .node; - expect( - edges.where((e) => - e.sourceNode == baseParamParamNode && - e.destinationNode == derivedParamParamNode), - hasLength(1)); - } - - Future test_implicitType_positionalParameter() async { - await analyze(''' -abstract class Base { - void f(void callback(int i)); -} -abstract class Derived extends Base { - void f(callback); -} -'''); - var baseParamParamNode = - explicitTypeNullability[findNode.typeAnnotation('int i')]; - var derivedParamParamNode = - implicitType[findNode.simpleParameter('callback)')]! - .positionalParameter(0)! - .node; - expect( - edges.where((e) => - e.sourceNode == baseParamParamNode && - e.destinationNode == derivedParamParamNode), - hasLength(1)); - } - - Future test_implicitType_returnType() async { - await analyze(''' -abstract class Base { - void f(int callback()); -} -abstract class Derived extends Base { - void f(callback); -} -'''); - var baseParamReturnNode = - explicitTypeNullability[findNode.typeAnnotation('int callback')]; - var derivedParamReturnNode = - implicitType[findNode.simpleParameter('callback)')]!.returnType!.node; - expect( - edges.where((e) => - e.sourceNode == baseParamReturnNode && - e.destinationNode == derivedParamReturnNode), - hasLength(1)); - } - - Future test_implicitType_typeArgument() async { - await analyze(''' -abstract class Base { - void f(List x); -} -abstract class Derived extends Base { - void f(x); /*derived*/ -} -'''); - var baseParamArgNode = - explicitTypeNullability[findNode.typeAnnotation('int>')]; - var derivedParamArgNode = - implicitType[findNode.simpleParameter('x); /*derived*/')]! - .typeArgument(0)! - .node; - expect( - edges.where((e) => - e.sourceNode == baseParamArgNode && - e.destinationNode == derivedParamArgNode), - hasLength(1)); - } - - Future test_implicitType_variableDeclarationList() async { - await analyze(''' -void f(int i) { - var j = i; -} -'''); - var iNode = explicitTypeNullability[findNode.typeAnnotation('int')]; - var jNode = implicitType[findNode.variableDeclarationList('j')]!.node; - expect( - edges.where((e) => e.sourceNode == iNode && e.destinationNode == jNode), - hasLength(1)); - } - - Future test_implicitTypeArguments_genericFunctionCall() async { - await analyze(''' -List g(T t) {} -List f() => g(null); -'''); - var implicitInvocationTypeArgumentNode = - implicitTypeArguments[findNode.methodInvocation('g(null)')]! - .single - .node; - var returnElementNode = - explicitTypeNullability[findNode.typeAnnotation('int')]; - expect(edges.where((e) { - var destination = e.destinationNode; - return _isPointedToByAlways(e.sourceNode) && - destination is SubstitutionNodeInfo && - destination.innerNode == implicitInvocationTypeArgumentNode; - }), hasLength(1)); - expect(edges.where((e) { - var source = e.sourceNode; - return source is SubstitutionNodeInfo && - source.innerNode == implicitInvocationTypeArgumentNode && - e.destinationNode == returnElementNode; - }), hasLength(1)); - } - - Future test_implicitTypeArguments_genericMethodCall() async { - await analyze(''' -class C { - List g(T t) {} -} -List f(C c) => c.g(null); -'''); - var implicitInvocationTypeArgumentNode = - implicitTypeArguments[findNode.methodInvocation('c.g(null)')]! - .single - .node; - var returnElementNode = - explicitTypeNullability[findNode.typeAnnotation('int')]; - expect(edges.where((e) { - var destination = e.destinationNode; - return _isPointedToByAlways(e.sourceNode) && - destination is SubstitutionNodeInfo && - destination.innerNode == implicitInvocationTypeArgumentNode; - }), hasLength(1)); - expect(edges.where((e) { - var source = e.sourceNode; - return source is SubstitutionNodeInfo && - source.innerNode == implicitInvocationTypeArgumentNode && - e.destinationNode == returnElementNode; - }), hasLength(1)); - } - - Future test_implicitTypeArguments_instanceCreationExpression() async { - await analyze(''' -class C { - C(T t); -} -C f() => C(null); -'''); - var implicitInvocationTypeArgumentNode = - implicitTypeArguments[findNode.instanceCreation('C(null)')]! - .single - .node; - var returnElementNode = - explicitTypeNullability[findNode.typeAnnotation('int')]; - expect(edges.where((e) { - var destination = e.destinationNode; - return _isPointedToByAlways(e.sourceNode) && - destination is SubstitutionNodeInfo && - destination.innerNode == implicitInvocationTypeArgumentNode; - }), hasLength(1)); - expect( - edges.where((e) => - e.sourceNode == implicitInvocationTypeArgumentNode && - e.destinationNode == returnElementNode), - hasLength(1)); - } - - Future test_implicitTypeArguments_listLiteral() async { - await analyze(''' -List f() => [null]; -'''); - var implicitListLiteralElementNode = - implicitTypeArguments[findNode.listLiteral('[null]')]!.single.node; - var returnElementNode = - explicitTypeNullability[findNode.typeAnnotation('int')]; - expect( - edges.where((e) => - _isPointedToByAlways(e.sourceNode) && - e.destinationNode == implicitListLiteralElementNode), - hasLength(1)); - expect( - edges.where((e) => - e.sourceNode == implicitListLiteralElementNode && - e.destinationNode == returnElementNode), - hasLength(1)); - } - - Future test_implicitTypeArguments_mapLiteral() async { - await analyze(''' -Map f() => {1: null}; -'''); - var implicitMapLiteralTypeArguments = - implicitTypeArguments[findNode.setOrMapLiteral('{1: null}')]!; - expect(implicitMapLiteralTypeArguments, hasLength(2)); - var implicitMapLiteralKeyNode = implicitMapLiteralTypeArguments[0].node; - var implicitMapLiteralValueNode = implicitMapLiteralTypeArguments[1].node; - var returnKeyNode = explicitTypeNullability[findNode.typeAnnotation('int')]; - var returnValueNode = - explicitTypeNullability[findNode.typeAnnotation('String')]; - expect( - edges.where((e) => - _pointsToNeverHard(e.sourceNode) && - e.destinationNode == implicitMapLiteralKeyNode), - hasLength(1)); - expect( - edges.where((e) => - e.sourceNode == implicitMapLiteralKeyNode && - e.destinationNode == returnKeyNode), - hasLength(1)); - expect( - edges.where((e) => - _isPointedToByAlways(e.sourceNode) && - e.destinationNode == implicitMapLiteralValueNode), - hasLength(1)); - expect( - edges.where((e) => - e.sourceNode == implicitMapLiteralValueNode && - e.destinationNode == returnValueNode), - hasLength(1)); - } - - Future test_implicitTypeArguments_setLiteral() async { - await analyze(''' -Set f() => {null}; -'''); - var implicitSetLiteralElementNode = - implicitTypeArguments[findNode.setOrMapLiteral('{null}')]!.single.node; - var returnElementNode = - explicitTypeNullability[findNode.typeAnnotation('int')]; - expect( - edges.where((e) => - _isPointedToByAlways(e.sourceNode) && - e.destinationNode == implicitSetLiteralElementNode), - hasLength(1)); - expect( - edges.where((e) => - e.sourceNode == implicitSetLiteralElementNode && - e.destinationNode == returnElementNode), - hasLength(1)); - } - - Future test_implicitTypeArguments_typeAnnotation() async { - await analyze(''' -List f(List l) => l; -'''); - var implicitListElementType = - implicitTypeArguments[findNode.typeAnnotation('List l')]!.single.node; - var implicitReturnElementType = - explicitTypeNullability[findNode.typeAnnotation('Object')]; - expect( - edges.where((e) => - e.sourceNode == implicitListElementType && - e.destinationNode == implicitReturnElementType), - hasLength(1)); - } - - Future test_substitutionNode() async { - await analyze(''' -class C { - void f(T t) {} -} -void g(C x, int y) { - x.f(y); -} -'''); - var yNode = explicitTypeNullability[findNode.typeAnnotation('int y')]; - var edge = edges.where((e) => e.sourceNode == yNode).single; - var sNode = edge.destinationNode as SubstitutionNodeInfo; - expect(sNode.innerNode, - explicitTypeNullability[findNode.typeAnnotation('int>')]); - expect(sNode.outerNode, - explicitTypeNullability[findNode.typeAnnotation('T t')]); - } - - bool _isPointedToByAlways(NullabilityNodeInfo? node) { - return edges - .any((e) => e.sourceNode == always && e.destinationNode == node); - } - - bool _pointsToNeverHard(NullabilityNodeInfo? node) { - return edges.any( - (e) => e.sourceNode == node && e.destinationNode == never && e.isHard); - } -} diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart deleted file mode 100644 index 4090164a61a5..000000000000 --- a/pkg/nnbd_migration/test/migration_cli_test.dart +++ /dev/null @@ -1,2292 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; -import 'package:analyzer/file_system/memory_file_system.dart'; -import 'package:analyzer/file_system/physical_file_system.dart'; -import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; -import 'package:analyzer/src/test_utilities/mock_sdk.dart' as mock_sdk; -import 'package:args/args.dart'; -import 'package:cli_util/cli_logging.dart'; -import 'package:http/http.dart' as http; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/migration_cli.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/instrumentation_listener.dart'; -import 'package:nnbd_migration/src/front_end/migration_summary.dart'; -import 'package:nnbd_migration/src/front_end/non_nullable_fix.dart'; -import 'package:nnbd_migration/src/front_end/web/edit_details.dart'; -import 'package:nnbd_migration/src/front_end/web/file_details.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/messages.dart' as messages; -import 'package:nnbd_migration/src/preview/preview_site.dart'; -import 'package:path/path.dart' as path; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'utilities/test_logger.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(_MigrationCliTestPosix); - defineReflectiveTests(_MigrationCliTestWindows); - }); -} - -const sdkRootPathPosix = '/sdk'; - -/// Specialization of [InstrumentationListener] that generates artificial -/// exceptions, so that we can test they are properly propagated to top level. -class _ExceptionGeneratingInstrumentationListener - extends InstrumentationListener { - _ExceptionGeneratingInstrumentationListener({super.migrationSummary}); - - @override - void externalDecoratedType(Element element, DecoratedTypeInfo decoratedType) { - if (element.name == 'print') { - throw StateError('Artificial exception triggered'); - } - super.externalDecoratedType(element, decoratedType); - } -} - -/// Specialization of [NonNullableFix] that generates artificial exceptions, so -/// that we can test they are properly propagated to top level. -class _ExceptionGeneratingNonNullableFix extends NonNullableFix { - _ExceptionGeneratingNonNullableFix(DartFixListener listener, - ResourceProvider resourceProvider, Object? bindAddress, Logger logger, - {List included = const [], - int? preferredPort, - String? summaryPath, - required String sdkPath}) - : super(listener, resourceProvider, bindAddress, logger, - (String? path) => true, - included: included, - preferredPort: preferredPort, - summaryPath: summaryPath, - sdkPath: sdkPath); - - @override - InstrumentationListener createInstrumentationListener( - {MigrationSummary? migrationSummary}) => - _ExceptionGeneratingInstrumentationListener( - migrationSummary: migrationSummary); -} - -class _MigrationCli extends MigrationCli { - final _MigrationCliTestBase _test; - - /// If non-null, callback function that will be invoked by the `applyHook` - /// override. - void Function()? _onApplyHook; - - _MigrationCli(this._test) - : super( - binaryName: 'nnbd_migration', - loggerFactory: (isVerbose) => _test.logger = TestLogger(isVerbose), - defaultSdkPathOverride: - _test.resourceProvider.convertPath(sdkRootPathPosix), - resourceProvider: _test.resourceProvider, - environmentVariables: _test.environmentVariables); - - _MigrationCliRunner? decodeCommandLineArgs(ArgResults argResults, - {bool? isVerbose}) { - var runner = super.decodeCommandLineArgs(argResults, isVerbose: isVerbose); - if (runner == null) return null; - return _MigrationCliRunner(this, runner.options); - } -} - -class _MigrationCliRunner extends MigrationCliRunner { - Future Function()? _runWhilePreviewServerActive; - - _MigrationCliRunner(_MigrationCli super.cli, super.options); - - _MigrationCli get cli => super.cli as _MigrationCli; - - @override - void applyHook() { - super.applyHook(); - cli._onApplyHook?.call(); - } - - @override - Object? computeBindAddress() { - var address = super.computeBindAddress(); - if (Platform.environment.containsKey('FORCE_IPV6') && - address == InternetAddress.loopbackIPv4) { - return InternetAddress.loopbackIPv6; - } - return address; - } - - @override - Set computePathsToProcess(DriverBasedAnalysisContext context) => - cli._test.overridePathsToProcess ?? - _sortPaths(super.computePathsToProcess(context)); - - @override - NonNullableFix createNonNullableFix(DartFixListener listener, - ResourceProvider resourceProvider, Object? bindAddress, - {List included = const [], - int? preferredPort, - String? summaryPath, - required String sdkPath}) { - if (cli._test.injectArtificialException) { - return _ExceptionGeneratingNonNullableFix( - listener, resourceProvider, bindAddress, logger, - included: included, - preferredPort: preferredPort, - summaryPath: summaryPath, - sdkPath: sdkPath); - } else { - return super.createNonNullableFix(listener, resourceProvider, bindAddress, - included: included, - preferredPort: preferredPort, - summaryPath: summaryPath, - sdkPath: sdkPath); - } - } - - @override - void listenForSignalInterrupt() { - if (_runWhilePreviewServerActive == null) { - fail('Preview server not expected to have been started'); - } - sigIntSignalled = Completer(); - _runWhilePreviewServerActive! - .call() - .then((_) => sigIntSignalled.complete()); - _runWhilePreviewServerActive = null; - } - - Future runWithPreviewServer(Future Function() callback) async { - _runWhilePreviewServerActive = callback; - await run(); - if (_runWhilePreviewServerActive != null) { - fail('Preview server never started'); - } - } - - @override - bool shouldBeMigrated(String path) => - cli._test.overrideShouldBeMigrated?.call(path) ?? - super.shouldBeMigrated(path); - - /// Sorts the paths in [paths] for repeatability of migration tests. - Set _sortPaths(Set paths) { - var pathList = paths.toList(); - pathList.sort(); - return pathList.toSet(); - } -} - -abstract class _MigrationCliTestBase { - Map environmentVariables = {}; - - /// If `true`, then an artificial exception should be generated when migration - /// encounters a reference to the `print` function. - bool injectArtificialException = false; - - /// If non-null, this is injected as the return value for - /// [_MigrationCliRunner.computePathsToProcess]. - Set? overridePathsToProcess; - - bool Function(String)? overrideShouldBeMigrated; - - set logger(TestLogger logger); - - MemoryResourceProvider get resourceProvider; -} - -mixin _MigrationCliTestMethods on _MigrationCliTestBase { - @override - late TestLogger logger; - - final hasVerboseHelpMessage = contains('for verbose help output'); - - final hasUsageText = contains('Usage: nnbd_migration'); - - final urlStartRegexp = RegExp('https?:'); - - final dartVersionIsNullSafeByDefault = - Feature.non_nullable.releaseVersion != null; - - String assertDecodeArgsFailure(List args) { - var cli = _createCli(); - try { - cli.decodeCommandLineArgs(MigrationCli.createParser().parse(args)); - fail('Migration succeeded; expected it to abort with an error'); - } on MigrationExit catch (migrationExit) { - expect(migrationExit.exitCode, isNotNull); - expect(migrationExit.exitCode, isNot(0)); - } - var stderrText = logger.stderrBuffer.toString(); - expect(stderrText, hasUsageText); - expect(stderrText, hasVerboseHelpMessage); - return stderrText; - } - - Future assertErrorExit( - MigrationCliRunner cliRunner, FutureOr Function() callback, - {required bool withUsage, dynamic expectedExitCode}) async { - expectedExitCode ??= isNot(0); - try { - await callback(); - fail('Migration succeeded; expected it to abort with an error'); - } on MigrationExit catch (migrationExit) { - expect(migrationExit.exitCode, isNotNull); - expect(migrationExit.exitCode, expectedExitCode); - } - expect(cliRunner.isPreviewServerRunning, isFalse); - return assertStderr(withUsage: withUsage); - } - - void assertHttpSuccess(http.Response response) { - if (response.statusCode == 500) { - try { - var decodedResponse = jsonDecode(response.body); - print('Exception: ${decodedResponse['exception']}'); - print('Stack trace:'); - print(decodedResponse['stackTrace']); - } catch (_) { - print(response.body); - } - fail('HTTP request failed'); - } - expect(response.statusCode, 200); - } - - void assertNormalExit(MigrationCliRunner cliRunner) { - expect(cliRunner.isPreviewServerRunning, isFalse); - } - - Future assertParseArgsFailure(List args) async { - try { - MigrationCli.createParser().parse(args); - } on FormatException catch (e) { - // Parsing failed, which was expected. - return e.message; - } - fail('Parsing was expected to fail, but did not'); - } - - CommandLineOptions assertParseArgsSuccess(List args) { - var cliRunner = _createCli() - .decodeCommandLineArgs(MigrationCli.createParser().parse(args))!; - assertNormalExit(cliRunner); - var options = cliRunner.options; - expect(options, isNotNull); - return options; - } - - Future assertPreviewServerResponsive(String url) async { - var response = await httpGet(Uri.parse(url)); - assertHttpSuccess(response); - } - - void assertProjectContents(String projectDir, Map expected) { - for (var entry in expected.entries) { - var relativePathPosix = entry.key; - assert(!path.posix.isAbsolute(relativePathPosix)); - var filePath = resourceProvider.pathContext - .join(projectDir, resourceProvider.convertPath(relativePathPosix)); - expect( - resourceProvider.getFile(filePath).readAsStringSync(), entry.value); - } - } - - Future assertRunFailure(List args, - {MigrationCli? cli, - bool withUsage = false, - dynamic expectedExitCode}) async { - expectedExitCode ??= isNot(0); - cli ??= _createCli(); - MigrationCliRunner? cliRunner; - try { - cliRunner = - cli.decodeCommandLineArgs(MigrationCli.createParser().parse(args)); - } on MigrationExit catch (e) { - expect(e.exitCode, isNotNull); - expect(e.exitCode, expectedExitCode); - return assertStderr(withUsage: withUsage); - } - return await assertErrorExit(cliRunner!, () => cliRunner!.run(), - withUsage: withUsage, expectedExitCode: expectedExitCode); - } - - String assertStderr({required bool withUsage}) { - var stderrText = logger.stderrBuffer.toString(); - expect(stderrText, withUsage ? hasUsageText : isNot(hasUsageText)); - expect(stderrText, - withUsage ? hasVerboseHelpMessage : isNot(hasVerboseHelpMessage)); - return stderrText; - } - - /// Wraps a future containing an HTTP response so that when that response is - /// received, we will verify that it is reasonable. - Future checkHttpResponse( - Future futureResponse) async { - var response = await futureResponse; - // Check that all "http:" and "https:" URLs in the given HTTP response are - // absolute (guards against https://github.com/dart-lang/sdk/issues/43545). - for (var match in urlStartRegexp.allMatches(response.body)) { - expect(response.body.substring(match.end), startsWith('//')); - } - return response; - } - - String createProjectDir(Map contents, - {String posixPath = '/test_project'}) { - for (var entry in contents.entries) { - var relativePathPosix = entry.key; - assert(!path.posix.isAbsolute(relativePathPosix)); - var filePathPosix = path.posix.join(posixPath, relativePathPosix); - resourceProvider.newFile( - resourceProvider.convertPath(filePathPosix), entry.value!); - } - return resourceProvider.convertPath(posixPath); - } - - Future getSourceFromServer(Uri uri, String path) async { - http.Response response = await tryGetSourceFromServer(uri, path); - assertHttpSuccess(response); - return jsonDecode(response.body)['sourceCode'] as String?; - } - - /// Performs an HTTP get, verifying that the response received (if any) is - /// reasonable. - Future httpGet(Uri url, {Map? headers}) { - return checkHttpResponse(http.get(url, headers: headers)); - } - - /// Performs an HTTP post, verifying that the response received (if any) is - /// reasonable. - Future httpPost(Uri url, - {Map? headers, dynamic body, Encoding? encoding}) { - return checkHttpResponse( - http.post(url, headers: headers, body: body, encoding: encoding)); - } - - String packagePath(String path) => - resourceProvider.convertPath('/.pub-cache/$path'); - - Future runWithPreviewServer(_MigrationCli cli, List args, - Future Function(String?) callback) async { - String? url; - var cliRunner = cli.decodeCommandLineArgs(_parseArgs(args)); - if (cliRunner != null) { - await cliRunner.runWithPreviewServer(() async { - // Server should be running now - url = RegExp('http://.*', multiLine: true) - .stringMatch(logger.stdoutBuffer.toString()); - await callback(url); - }); - // Server should be stopped now - expect(httpGet(Uri.parse(url!)), throwsA(anything)); - assertNormalExit(cliRunner); - } - } - - void setUp() { - resourceProvider.newFolder(resourceProvider.pathContext.current); - environmentVariables.clear(); - } - - Map simpleProject( - {bool migrated = false, - String? sourceText, - String? pubspecText, - String? packageConfigText, - String? analysisOptionsText}) { - return { - 'pubspec.yaml': pubspecText ?? - ''' -name: test -environment: - sdk: '${migrated ? '>=2.12.0 <3.0.0' : '>=2.6.0 <3.0.0'}' -''', - '.dart_tool/package_config.json': - packageConfigText ?? _getPackageConfigText(migrated: migrated), - 'lib/test.dart': sourceText ?? - ''' -int${migrated ? '?' : ''} f() => null; -''', - if (analysisOptionsText != null) - 'analysis_options.yaml': analysisOptionsText, - }; - } - - void tearDown() { - NonNullableFix.shutdownAllServers(); - } - - test_default_logger() { - // When running normally, we don't override the logger; make sure it has a - // non-null default so that there won't be a crash. - expect(MigrationCli(binaryName: 'nnbd_migration').logger, isNotNull); - } - - test_detect_old_sdk() async { - var cli = _createCli(); - // Alter the mock SDK, changing the signature of Object.operator== to match - // the signature that was present prior to NNBD. (This is what the - // migration tool uses to detect an old SDK). - var coreLib = resourceProvider.getFile( - resourceProvider.convertPath('$sdkRootPathPosix/lib/core/core.dart')); - var oldCoreLibText = coreLib.readAsStringSync(); - var newCoreLibText = oldCoreLibText.replaceAll( - 'external bool operator ==(Object other)', - 'external bool operator ==(dynamic other)'); - expect(newCoreLibText, isNot(oldCoreLibText)); - coreLib.writeAsStringSync(newCoreLibText); - var projectDir = createProjectDir(simpleProject()); - await assertRunFailure([projectDir], cli: cli); - var output = logger.stdoutBuffer.toString(); - expect(output, contains(messages.sdkNnbdOff)); - } - - test_detect_old_sdk_environment_variable() async { - environmentVariables['SDK_PATH'] = '/fake-old-sdk-path'; - var cli = _createCli(); // Creates the mock SDK as a side effect - // Alter the mock SDK, changing the signature of Object.operator== to match - // the signature that was present prior to NNBD. (This is what the - // migration tool uses to detect an old SDK). - var coreLib = resourceProvider.getFile( - resourceProvider.convertPath('$sdkRootPathPosix/lib/core/core.dart')); - var oldCoreLibText = coreLib.readAsStringSync(); - var newCoreLibText = oldCoreLibText.replaceAll( - 'external bool operator ==(Object other)', - 'external bool operator ==(dynamic other)'); - expect(newCoreLibText, isNot(oldCoreLibText)); - coreLib.writeAsStringSync(newCoreLibText); - var projectDir = createProjectDir(simpleProject()); - await assertRunFailure([projectDir], cli: cli); - var output = logger.stdoutBuffer.toString(); - expect(output, contains(messages.sdkNnbdOff)); - expect(output, contains(messages.sdkPathEnvironmentVariableSet)); - expect(output, contains(environmentVariables['SDK_PATH'])); - } - - test_flag_apply_changes_default() { - expect(assertParseArgsSuccess([]).applyChanges, isFalse); - expect(assertParseArgsSuccess([]).webPreview, isTrue); - } - - test_flag_apply_changes_disable() async { - // "--no-apply-changes" is not an option. - await assertParseArgsFailure(['--no-apply-changes']); - } - - test_flag_apply_changes_enable() { - var options = assertParseArgsSuccess(['--apply-changes']); - expect(options.applyChanges, isTrue); - expect(options.webPreview, isFalse); - } - - test_flag_apply_changes_enable_with_no_web_preview() { - expect( - assertParseArgsSuccess(['--no-web-preview', '--apply-changes']) - .applyChanges, - isTrue); - } - - test_flag_apply_changes_incompatible_with_web_preview() { - expect(assertDecodeArgsFailure(['--web-preview', '--apply-changes']), - contains('--apply-changes requires --no-web-preview')); - } - - test_flag_help() { - var helpText = _getHelpText(verbose: false); - expect(helpText, hasUsageText); - expect(helpText, hasVerboseHelpMessage); - } - - test_flag_help_verbose() { - var helpText = _getHelpText(verbose: true); - expect(helpText, hasUsageText); - expect(helpText, isNot(hasVerboseHelpMessage)); - } - - test_flag_ignore_errors_default() { - expect(assertParseArgsSuccess([]).ignoreErrors, isFalse); - } - - test_flag_ignore_errors_disable() async { - await assertParseArgsFailure(['--no-ignore-errors']); - } - - test_flag_ignore_errors_enable() { - expect(assertParseArgsSuccess(['--ignore-errors']).ignoreErrors, isTrue); - } - - test_flag_ignore_exceptions_default() { - expect(assertParseArgsSuccess([]).ignoreExceptions, isFalse); - } - - test_flag_ignore_exceptions_disable() async { - await assertParseArgsFailure(['--no-ignore-exceptions']); - } - - test_flag_ignore_exceptions_enable() { - expect(assertParseArgsSuccess(['--ignore-exceptions']).ignoreExceptions, - isTrue); - } - - test_flag_ignore_exceptions_hidden() { - var flagName = '--ignore-exceptions'; - expect(_getHelpText(verbose: false), isNot(contains(flagName))); - expect(_getHelpText(verbose: true), contains(flagName)); - } - - test_flag_skip_import_check_default() { - expect(assertParseArgsSuccess([]).skipImportCheck, isFalse); - } - - test_flag_skip_import_check_disable() async { - // "--no-skip-import-check" is not an option. - await assertParseArgsFailure(['--no-skip-import_check']); - } - - test_flag_skip_import_check_enable() { - expect(assertParseArgsSuccess(['--skip-import-check']).skipImportCheck, - isTrue); - } - - test_flag_web_preview_default() { - expect(assertParseArgsSuccess([]).webPreview, isTrue); - } - - test_flag_web_preview_disable() { - expect(assertParseArgsSuccess(['--no-web-preview']).webPreview, isFalse); - } - - test_flag_web_preview_enable() { - expect(assertParseArgsSuccess(['--web-preview']).webPreview, isTrue); - } - - test_lifecycle_already_migrated_file() async { - Map createProject({bool migrated = false}) { - var projectContents = simpleProject(sourceText: ''' -${migrated ? ''' -// ignore: illegal_language_version_override -''' : ''' -// ignore: illegal_language_version_override -// @dart = 2.6'''} -import 'already_migrated.dart'; -int${migrated ? '?' : ''} x = y; -''', migrated: true); - projectContents['lib/already_migrated.dart'] = ''' -int? y = 0; -'''; - return projectContents; - } - - var projectContents = createProject(); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - assertNormalExit(cliRunner); - // Check that a summary was printed - expect(logger.stdoutBuffer.toString(), contains('Applying changes')); - // And that it refers to test.dart, but not pubspec.yaml or - // already_migrated.dart. - expect(logger.stdoutBuffer.toString(), contains('test.dart')); - expect(logger.stdoutBuffer.toString(), isNot(contains('pubspec.yaml'))); - expect(logger.stdoutBuffer.toString(), - isNot(contains('already_migrated.dart'))); - // And that it does not tell the user they can rerun with `--apply-changes` - expect(logger.stdoutBuffer.toString(), isNot(contains('--apply-changes'))); - // Check that the non-migrated library was changed but not the migrated one - assertProjectContents(projectDir, createProject(migrated: true)); - } - - test_lifecycle_apply_changes() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - var cliRunner = - cli.decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - bool applyHookCalled = false; - cli._onApplyHook = () { - expect(applyHookCalled, false); - applyHookCalled = true; - // Changes should have been made - assertProjectContents(projectDir, simpleProject(migrated: true)); - }; - await cliRunner.run(); - assertNormalExit(cliRunner); - expect(applyHookCalled, true); - // Check that a summary was printed - expect(logger.stdoutBuffer.toString(), contains('Applying changes')); - // And that it refers to test.dart and pubspec.yaml - expect(logger.stdoutBuffer.toString(), contains('test.dart')); - expect(logger.stdoutBuffer.toString(), contains('pubspec.yaml')); - // And that it does not tell the user they can rerun with `--apply-changes` - expect(logger.stdoutBuffer.toString(), isNot(contains('--apply-changes'))); - } - - test_lifecycle_contextdiscovery_handles_multiple() async { - var projectContents = simpleProject(); - var subProject = simpleProject(); - for (var filePath in subProject.keys) { - projectContents['example/$filePath'] = subProject[filePath]; - } - projectContents['example/analysis_options.yaml'] = ''' -linter: - rules: - - empty_constructor_bodies -'''; - - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]))!; - await cliRunner.run(); - assertNormalExit(cliRunner); - expect(cliRunner.hasMultipleAnalysisContext, true); - expect(cliRunner.analysisContext, isNotNull); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('more than one project found')); - } - - test_lifecycle_contextdiscovery_handles_single() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]))!; - await cliRunner.run(); - assertNormalExit(cliRunner); - expect(cliRunner.hasMultipleAnalysisContext, false); - expect(cliRunner.analysisContext, isNotNull); - } - - test_lifecycle_exception_handling() async { - var projectContents = simpleProject(sourceText: 'main() { print(0); }'); - var projectDir = createProjectDir(projectContents); - injectArtificialException = true; - await assertRunFailure([projectDir]); - var errorOutput = logger.stderrBuffer.toString(); - expect(errorOutput, contains('Artificial exception triggered')); - expect( - errorOutput, isNot(contains('try to fix errors in the source code'))); - expect(errorOutput, contains('re-run with\n--ignore-exceptions')); - expect(errorOutput, contains('consider filing a bug report')); - expect( - errorOutput, - contains( - RegExp(r'Please include the SDK version \([0-9]+\.[0-9]+\..*\)'))); - } - - test_lifecycle_exception_handling_ignore() async { - var projectContents = simpleProject(sourceText: ''' -main() { - print(0); - int x = null; -} -'''); - var projectDir = createProjectDir(projectContents); - injectArtificialException = true; - var cli = _createCli(); - await runWithPreviewServer(cli, ['--ignore-exceptions', projectDir], - (url) async { - var output = logger.stdoutBuffer.toString(); - expect(output, contains('No analysis issues found')); - expect(output, isNot(contains('Artificial exception triggered'))); - expect( - output, - contains('Attempting to perform\nmigration anyway due to the use' - ' of --ignore-exceptions.')); - expect(output, contains('re-run without --ignore-exceptions')); - await assertPreviewServerResponsive(url!); - await _tellPreviewToApplyChanges(url); - assertProjectContents( - projectDir, simpleProject(migrated: true, sourceText: ''' -main() { - print(0); - int? x = null; -} -''')); - }); - expect(logger.stderrBuffer.toString(), isEmpty); - } - - test_lifecycle_exception_handling_multiple() async { - var projectContents = - simpleProject(sourceText: 'main() { print(0); print(1); }'); - var projectDir = createProjectDir(projectContents); - injectArtificialException = true; - await assertRunFailure([projectDir]); - var errorOutput = logger.stderrBuffer.toString(); - expect( - 'Artificial exception triggered'.allMatches(errorOutput), hasLength(1)); - expect( - errorOutput, isNot(contains('try to fix errors in the source code'))); - expect(errorOutput, contains('re-run with\n--ignore-exceptions')); - } - - test_lifecycle_exception_handling_with_error() async { - var projectContents = - simpleProject(sourceText: 'main() { print(0); unresolved; }'); - var projectDir = createProjectDir(projectContents); - injectArtificialException = true; - await assertRunFailure(['--ignore-errors', projectDir]); - var errorOutput = logger.stderrBuffer.toString(); - expect(errorOutput, contains('Artificial exception triggered')); - expect(errorOutput, contains('try to fix errors in the source code')); - expect(errorOutput, contains('re-run with\n--ignore-exceptions')); - } - - test_lifecycle_ignore_errors_disable() async { - var projectContents = simpleProject(sourceText: ''' -int f() => null -'''); - var projectDir = createProjectDir(projectContents); - await assertRunFailure([projectDir]); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('1 analysis issue found')); - var sep = resourceProvider.pathContext.separator; - expect( - output, - contains("error • Expected to find ';' at lib${sep}test.dart:1:12 • " - '(expected_token)')); - expect(output, contains('erroneous migration suggestions')); - expect(output, contains('We recommend fixing the analysis issues')); - expect(output, isNot(contains('Set the lower SDK constraint'))); - } - - test_lifecycle_ignore_errors_enable() async { - var projectContents = simpleProject(sourceText: ''' -int? f() => null -'''); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, ['--ignore-errors', projectDir], - (url) async { - var output = logger.stdoutBuffer.toString(); - expect(output, isNot(contains('No analysis issues found'))); - expect( - output, - contains('Continuing with migration suggestions due to the use of ' - '--ignore-errors.')); - await assertPreviewServerResponsive(url!); - }); - } - - test_lifecycle_import_check_handle_improper_lib_import() async { - Map computeProjectContents({required bool migrated}) => { - 'pubspec.yaml': ''' -name: test -environment: - sdk: '${migrated ? '>=2.12.0 <3.0.0' : '>=2.6.0 <3.0.0'}' -''', - '.dart_tool/package_config.json': - _getPackageConfigText(migrated: migrated), - 'lib/foo.dart': ''' -int${migrated ? '?' : ''} f() => null; -''', - 'test/foo_test.dart': ''' -import '../lib/foo.dart'; -int${migrated ? '?' : ''} g() => f(); -''', - }; - var projectContents = computeProjectContents(migrated: false); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - bool applyHookCalled = false; - cli._onApplyHook = () { - expect(applyHookCalled, false); - applyHookCalled = true; - // Changes should have been made - assertProjectContents(projectDir, computeProjectContents(migrated: true)); - }; - await runWithPreviewServer(cli, ['--skip-import-check', projectDir], - (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - await _tellPreviewToApplyChanges(url); - expect(applyHookCalled, true); - var output = logger.stdoutBuffer.toString(); - expect(output, - isNot(contains('Warning: package has unmigrated dependencies'))); - // Output should not mention that the user can rerun without - // `--skip-import-check`. - expect(output, - isNot(contains('`--${CommandLineOptions.skipImportCheckFlag}`'))); - }); - } - - test_lifecycle_import_check_via_export() async { - // If the user's code exports a library that imports a non-migrated library, - // that's a problem too. - var projectContents = simpleProject( - sourceText: "export 'package:foo/foo.dart';", - packageConfigText: _getPackageConfigText( - migrated: false, packagesMigrated: {'foo': true, 'bar': false})); - var projectDir = createProjectDir(projectContents); - resourceProvider.newFile( - packagePath('foo/lib/foo.dart'), "import 'package:bar/bar.dart';"); - resourceProvider.newFile(packagePath('bar/lib/bar.dart'), ''); - await assertRunFailure([projectDir], expectedExitCode: 1); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('Error: package has unmigrated dependencies')); - // Output should mention bar.dart, since it's unmigrated - expect(output, contains('package:bar/bar.dart')); - // But it should not mention foo.dart, which is migrated - expect(output, isNot(contains('package:foo/foo.dart'))); - } - - test_lifecycle_import_check_via_indirect_export() async { - // If the user's code imports a library that exports a library that imports - // a non-migrated library, that's a problem too. - var projectContents = simpleProject( - sourceText: "import 'package:foo/foo.dart';", - packageConfigText: _getPackageConfigText( - migrated: false, - packagesMigrated: {'foo': true, 'bar': true, 'baz': false})); - var projectDir = createProjectDir(projectContents); - resourceProvider.newFile( - packagePath('foo/lib/foo.dart'), "export 'package:bar/bar.dart';"); - resourceProvider.newFile( - packagePath('bar/lib/bar.dart'), "import 'package:baz/baz.dart';"); - resourceProvider.newFile(packagePath('baz/lib/baz.dart'), ''); - await assertRunFailure([projectDir], expectedExitCode: 1); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('Error: package has unmigrated dependencies')); - // Output should mention baz.dart, since it's unmigrated - expect(output, contains('package:baz/baz.dart')); - // But it should not mention foo.dart or bar.dart, which are migrated - expect(output, isNot(contains('package:foo/foo.dart'))); - expect(output, isNot(contains('package:bar/bar.dart'))); - } - - test_lifecycle_import_lib_from_test() async { - Map makeProject({bool migrated = false}) { - return simpleProject(migrated: migrated) - ..['test/foo.dart'] = ''' -import '../lib/test.dart'; -'''; - } - - var projectContents = makeProject(); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - var cliRunner = - cli.decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - bool applyHookCalled = false; - cli._onApplyHook = () { - expect(applyHookCalled, false); - applyHookCalled = true; - // Changes should have been made - assertProjectContents(projectDir, makeProject(migrated: true)); - }; - await cliRunner.run(); - assertNormalExit(cliRunner); - expect(applyHookCalled, true); - } - - @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/44118') - test_lifecycle_issue_44118() async { - var projectContents = simpleProject(sourceText: ''' -int f() => null -'''); - projectContents['lib/foo.dart'] = ''' -import 'test.dart'; -'''; - var projectDir = createProjectDir(projectContents); - await assertRunFailure([projectDir]); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('1 analysis issue found')); - var sep = resourceProvider.pathContext.separator; - expect( - output, - contains("error • Expected to find ';' at lib${sep}test.dart:1:12 • " - '(expected_token)')); - expect( - output, - contains( - 'analysis errors will result in erroneous migration suggestions')); - expect(output, contains('Please fix the analysis issues')); - } - - test_lifecycle_migration_already_performed() async { - var projectContents = simpleProject(migrated: true); - var projectDir = createProjectDir(projectContents); - await assertRunFailure([projectDir], expectedExitCode: 0); - var output = logger.stdoutBuffer.toString(); - expect(output, - contains('All sources appear to be already migrated. Nothing to do.')); - } - - test_lifecycle_no_preview() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--no-web-preview', projectDir]))!; - await cliRunner.run(); - assertNormalExit(cliRunner); - // Check that a summary was printed - var output = logger.stdoutBuffer.toString(); - expect(output, contains('Diff of changes')); - // And that it refers to test.dart and pubspec.yaml - expect(output, contains('test.dart')); - expect(output, contains('pubspec.yaml')); - // And that it contains text from a changed line - expect(output, contains('f() => null')); - // And that it tells the user they can rerun with `--apply-changes` - expect(output, contains('--apply-changes')); - // No changes should have been made - assertProjectContents(projectDir, projectContents); - } - - test_lifecycle_override_paths() async { - Map makeProject({bool migrated = false}) { - var projectContents = simpleProject(migrated: migrated); - projectContents['lib/test.dart'] = ''' -import 'skip.dart'; -import 'analyze_but_do_not_migrate.dart'; -int f() => 1; -int${migrated ? '?' : ''} g() => 1; -int${migrated ? '?' : ''} h() => 1; -bool call_h() => h() == null; -'''; - projectContents['lib/skip.dart'] = ''' -import 'test.dart'; -bool call_f() => f() == null; -'''; - projectContents['lib/analyze_but_do_not_migrate.dart'] = ''' -import 'test.dart'; -bool call_g() => g() == null; -'''; - return projectContents; - } - - var projectContents = makeProject(); - var projectDir = createProjectDir(projectContents); - var testPath = - resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart'); - var analyzeButDoNotMigratePath = resourceProvider.pathContext - .join(projectDir, 'lib', 'analyze_but_do_not_migrate.dart'); - overridePathsToProcess = {testPath, analyzeButDoNotMigratePath}; - overrideShouldBeMigrated = (path) => path == testPath; - var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs([ - '--no-web-preview', - '--apply-changes', - '--skip-import-check', - projectDir - ]))!; - await cliRunner.run(); - assertNormalExit(cliRunner); - // Check that a summary was printed - expect(logger.stdoutBuffer.toString(), contains('Applying changes')); - // And that it refers to test.dart and pubspec.yaml - expect(logger.stdoutBuffer.toString(), contains('test.dart')); - expect(logger.stdoutBuffer.toString(), contains('pubspec.yaml')); - // And that it does not tell the user they can rerun with `--apply-changes` - expect(logger.stdoutBuffer.toString(), isNot(contains('--apply-changes'))); - // Changes should have been made only to test.dart, and only accounting for - // the calls coming from analyze_but_do_not_migrate.dart and test.dart - assertProjectContents(projectDir, makeProject(migrated: true)); - } - - test_lifecycle_preview() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - var localhostAddressText = Platform.environment.containsKey('FORCE_IPV6') - ? '[::1]' - : '127.0.0.1'; - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - expect(url, startsWith('http://$localhostAddressText:')); - await assertPreviewServerResponsive(url!); - }); - // No changes should have been made. - assertProjectContents(projectDir, projectContents); - } - - test_lifecycle_preview_add_hint() async { - var projectContents = simpleProject(sourceText: 'int x;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var authToken = uri.queryParameters['authToken']; - var response = await httpPost( - uri.replace( - path: resourceProvider.pathContext - .toUri(resourceProvider.pathContext - .join(projectDir, 'lib', 'test.dart')) - .path, - queryParameters: { - 'offset': '3', - 'end': '3', - 'replacement': '/*!*/', - 'authToken': authToken - }), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - assertProjectContents( - projectDir, simpleProject(sourceText: 'int/*!*/ x;')); - }); - } - - test_lifecycle_preview_apply_changes() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - bool applyHookCalled = false; - cli._onApplyHook = () { - expect(applyHookCalled, false); - applyHookCalled = true; - // Changes should have been made - assertProjectContents(projectDir, simpleProject(migrated: true)); - }; - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - await _tellPreviewToApplyChanges(url); - expect(applyHookCalled, true); - }); - } - - test_lifecycle_preview_apply_changes_unreferenced_part() async { - var projectContents = simpleProject() - ..['lib/unreferenced_part.dart'] = ''' -part of foo; - -int f() => null; -'''; - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - bool applyHookCalled = false; - cli._onApplyHook = () { - expect(applyHookCalled, false); - applyHookCalled = true; - // Changes should have been made - assertProjectContents(projectDir, simpleProject(migrated: true)); - }; - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - await _tellPreviewToApplyChanges(url); - expect(applyHookCalled, true); - }); - } - - test_lifecycle_preview_extra_forward_slash() async { - var projectDir = createProjectDir(simpleProject()); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - var uri = Uri.parse(url!); - await assertPreviewServerResponsive( - uri.replace(path: '${uri.path}/').toString()); - }); - } - - test_lifecycle_preview_navigation_links() async { - var projectContents = simpleProject(sourceText: 'int x;'); - projectContents['lib/src/test.dart'] = 'import "../test.dart"; int y = x;'; - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - final uri = Uri.parse(url); - final authToken = uri.queryParameters['authToken']; - final fileResponse = await httpGet( - uri.replace( - path: resourceProvider.pathContext - .toUri(resourceProvider.pathContext - .join(projectDir, 'lib', 'src', 'test.dart')) - .path, - queryParameters: {'inline': 'true', 'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - final fileJson = FileDetails.fromJson(jsonDecode(fileResponse.body)); - final navigation = fileJson.navigationContent!; - final aLink = RegExp(r''); - for (final match in aLink.allMatches(navigation)) { - var href = match.group(1)!; - final contentsResponse = await httpGet( - uri.replace( - path: Uri.parse(href).path, - queryParameters: {'inline': 'true', 'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(contentsResponse); - } - }); - } - - test_lifecycle_preview_navigation_tree() async { - var projectContents = simpleProject(sourceText: 'int x;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var authToken = uri.queryParameters['authToken']; - var treeResponse = await httpGet( - uri.replace( - path: '/_preview/navigationTree.json', - queryParameters: {'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - var navRoots = jsonDecode(treeResponse.body) as List; - for (final root in navRoots) { - var navTree = - NavigationTreeNode.fromJson(root) as NavigationTreeDirectoryNode; - for (final file in navTree.subtree!) { - if (file is NavigationTreeFileNode) { - final contentsResponse = await httpGet( - uri - .resolve(file.href!) - .replace(queryParameters: {'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(contentsResponse); - } - } - } - }); - } - - test_lifecycle_preview_on_host_any() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cli = _createCli() - ..decodeCommandLineArgs(_parseArgs(['--preview-hostname=any'])); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect(url, isNot(contains('localhost'))); - await assertPreviewServerResponsive(url!); - }); - // No changes should have been made. - assertProjectContents(projectDir, projectContents); - } - - test_lifecycle_preview_region_link() async { - var projectContents = simpleProject(sourceText: 'int x;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var authToken = uri.queryParameters['authToken']; - var regionResponse = await httpGet( - uri.replace( - path: resourceProvider.pathContext - .toUri(resourceProvider.pathContext - .join(projectDir, 'lib', 'test.dart')) - .path, - queryParameters: { - 'region': 'region', - 'offset': '3', - 'authToken': authToken - }), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - var regionJson = EditDetails.fromJson(jsonDecode(regionResponse.body)); - final displayPath = regionJson.displayPath!; - final uriPath = regionJson.uriPath; - // uriPath should be a working URI - final contentsResponse = await httpGet( - uri.replace( - path: uriPath, - queryParameters: {'inline': 'true', 'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(contentsResponse); - - // Display path should be the actual windows path - final file = resourceProvider - .getFolder(projectDir) - .getChildAssumingFile(displayPath); - expect(file.exists, isTrue); - }); - } - - test_lifecycle_preview_region_table_path() async { - var projectContents = simpleProject(sourceText: 'int x;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - final uri = Uri.parse(url); - final authToken = uri.queryParameters['authToken']; - final fileResponse = await httpGet( - uri.replace( - path: resourceProvider.pathContext - .toUri(resourceProvider.pathContext - .join(projectDir, 'lib', 'test.dart')) - .path, - queryParameters: {'inline': 'true', 'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - final fileJson = FileDetails.fromJson(jsonDecode(fileResponse.body)); - final regions = fileJson.regions!; - final regionsPathRegex = RegExp(r''); - expect(regionsPathRegex.hasMatch(regions), true); - final regionsPath = regionsPathRegex.matchAsPrefix(regions)!.group(1)!; - final contentsResponse = await httpGet( - uri.replace( - path: Uri.parse(regionsPath).path, - queryParameters: {'inline': 'true', 'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(contentsResponse); - }); - } - - test_lifecycle_preview_rerun() async { - var origSourceText = 'void f() {}'; - var projectContents = simpleProject(sourceText: origSourceText); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var testPath = - resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart'); - var newSourceText = 'void g() {}'; - resourceProvider.getFile(testPath).writeAsStringSync(newSourceText); - // We haven't rerun, so getting the file details from the server should - // still yield the original source text - expect(await getSourceFromServer(uri, testPath), origSourceText); - var response = await httpPost(uri.replace(path: 'rerun-migration'), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - // Now that we've rerun, the server should yield the new source text - expect(await getSourceFromServer(uri, testPath), newSourceText); - }); - } - - test_lifecycle_preview_rerun_added_file() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var test2Path = - resourceProvider.pathContext.join(projectDir, 'lib', 'test2.dart'); - var newSourceText = 'void g() {}'; - resourceProvider.getFile(test2Path).writeAsStringSync(newSourceText); - // We haven't rerun, so getting the file details from the server should - // fail - var response = await tryGetSourceFromServer(uri, test2Path); - expect(response.statusCode, 404); - response = await httpPost(uri.replace(path: 'rerun-migration'), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - // Now that we've rerun, the server should yield the new source text - expect(await getSourceFromServer(uri, test2Path), newSourceText); - }); - } - - test_lifecycle_preview_rerun_deleted_file() async { - var projectContents = {...simpleProject(), 'lib/other.dart': ''}; - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - // Note: we use the summary to verify that the deletion was noticed - var summaryPath = resourceProvider.convertPath('/summary.json'); - await runWithPreviewServer(cli, ['--summary', summaryPath, projectDir], - (url) async { - await assertPreviewServerResponsive(url!); - // lib/test.dart should be readable from the server and appear in the - // summary - var uri = Uri.parse(url); - var testPath = - resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart'); - await getSourceFromServer(uri, testPath); - var summaryData = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - var separator = resourceProvider.pathContext.separator; - expect(summaryData['changes']['byPath'], - contains('lib${separator}test.dart')); - // Now delete the lib file and rerun - resourceProvider.deleteFile(testPath); - var response = await httpPost(uri.replace(path: 'rerun-migration'), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - // lib/test.dart should no longer be readable from the server and - // should no longer appear in the summary - response = await tryGetSourceFromServer(uri, testPath); - expect(response.statusCode, 404); - summaryData = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - expect(summaryData['changes']['byPath'], - isNot(contains('lib${separator}test.dart'))); - }); - } - - test_lifecycle_preview_rerun_with_ignore_errors() async { - var origSourceText = 'void _f(int i) {}'; - var projectContents = simpleProject(sourceText: origSourceText); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, ['--ignore-errors', projectDir], - (url) async { - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var testPath = - resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart'); - resourceProvider - .getFile(testPath) - .writeAsStringSync('void _f(int? i) {}'); - // We haven't rerun, so getting the file details from the server should - // still yield the original source text, with informational space. - expect(await getSourceFromServer(uri, testPath), 'void _f(int i) {}'); - var response = await httpPost(uri.replace(path: 'rerun-migration'), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - var body = jsonDecode(response.body); - expect(body['success'], isTrue); - expect(body['errors'], isNull); - // Now that we've rerun, the server should yield the new source text - expect(await getSourceFromServer(uri, testPath), 'void _f(int? i) {}'); - }); - } - - test_lifecycle_preview_rerun_with_new_analysis_errors() async { - var origSourceText = 'void _f(int i) {}'; - var projectContents = simpleProject(sourceText: origSourceText); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var testPath = - resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart'); - var newSourceText = 'void _f(int? i) {}'; - resourceProvider.getFile(testPath).writeAsStringSync(newSourceText); - // We haven't rerun, so getting the file details from the server should - // still yield the original source text, with informational space. - expect(await getSourceFromServer(uri, testPath), 'void _f(int i) {}'); - var response = await httpPost(uri.replace(path: 'rerun-migration'), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - var body = jsonDecode(response.body); - expect(body['success'], isFalse); - expect(body['errors'], hasLength(1)); - var error = body['errors'].single; - expect(error['severity'], equals('error')); - expect( - error['message'], - equals( - "This requires the 'non-nullable' language feature to be enabled")); - expect(error['location'], - equals(resourceProvider.pathContext.join('lib', 'test.dart:1:12'))); - expect(error['code'], equals('experiment_not_enabled')); - }); - } - - test_lifecycle_preview_serves_only_from_project_dir() async { - var crazyFunctionName = 'crazyFunctionNameThatHasNeverBeenSeenBefore'; - var projectContents = - simpleProject(sourceText: 'void $crazyFunctionName() {}'); - var mainProjectDir = createProjectDir(projectContents); - var otherProjectDir = - createProjectDir(projectContents, posixPath: '/other_project_dir'); - var cli = _createCli(); - await runWithPreviewServer(cli, [mainProjectDir], (url) async { - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - - Future tryGetSourceFromProject(String projectDir) => - tryGetSourceFromServer( - uri, - resourceProvider.pathContext - .join(projectDir, 'lib', 'test.dart')); - - // To verify that we're forming the request correctly, make sure that we - // can read a file from mainProjectDir. - var response = await tryGetSourceFromProject(mainProjectDir); - assertHttpSuccess(response); - // And that crazyFunctionName appears in the response - expect(response.body, contains(crazyFunctionName)); - // Now verify that making the exact same request from otherProjectDir - // fails. - response = await tryGetSourceFromProject(otherProjectDir); - expect(response.statusCode, 404); - // And check that we didn't leak any info through the 404 response. - expect(response.body, isNot(contains(crazyFunctionName))); - }); - } - - test_lifecycle_preview_stack_hint_action() async { - var projectContents = simpleProject(sourceText: 'int x;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var authToken = uri.queryParameters['authToken']; - var regionResponse = await httpGet( - uri.replace( - path: resourceProvider.pathContext - .toUri(resourceProvider.pathContext - .join(projectDir, 'lib', 'test.dart')) - .path, - queryParameters: { - 'region': 'region', - 'offset': '3', - 'authToken': authToken - }), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - var regionJson = jsonDecode(regionResponse.body); - var response = await httpPost( - uri.replace( - path: 'apply-hint', queryParameters: {'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}, - body: jsonEncode( - regionJson['traces'][0]['entries'][0]['hintActions'][0])); - assertHttpSuccess(response); - assertProjectContents( - projectDir, simpleProject(sourceText: 'int/*?*/ x;')); - }); - } - - test_lifecycle_preview_stacktrace_link() async { - var projectContents = simpleProject(sourceText: 'int x;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - await runWithPreviewServer(cli, [projectDir], (url) async { - expect( - logger.stdoutBuffer.toString(), contains('No analysis issues found')); - await assertPreviewServerResponsive(url!); - var uri = Uri.parse(url); - var authToken = uri.queryParameters['authToken']; - var regionUri = uri.replace( - path: resourceProvider.pathContext - .toUri(resourceProvider.pathContext - .join(projectDir, 'lib', 'test.dart')) - .path, - queryParameters: { - 'region': 'region', - 'offset': '3', - 'authToken': authToken - }); - var regionResponse = await httpGet(regionUri, - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - var regionJson = EditDetails.fromJson(jsonDecode(regionResponse.body)); - final traceEntry = regionJson.traces![0].entries[0]; - final uriPath = traceEntry.link!.href!; - // uriPath should be a working URI - final contentsResponse = await httpGet( - regionUri - .resolve(uriPath) - .replace(queryParameters: {'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(contentsResponse); - }); - } - - test_lifecycle_skip_import_check_disable() async { - var projectContents = simpleProject( - sourceText: ''' -import 'package:foo/foo.dart'; -import 'package:foo/bar.dart'; - -int f() => null; -''', - packageConfigText: _getPackageConfigText( - migrated: false, packagesMigrated: {'foo': false})); - var projectDir = createProjectDir(projectContents); - resourceProvider.newFile(packagePath('foo/lib/foo.dart'), ''); - resourceProvider.newFile(packagePath('foo/lib/bar.dart'), ''); - await assertRunFailure([projectDir], expectedExitCode: 1); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('Error: package has unmigrated dependencies')); - // Output should contain an indented, sorted list of all unmigrated - // dependencies. - expect( - output, contains('\n package:foo/bar.dart\n package:foo/foo.dart')); - // Output should mention that the user can rerun with `--skip-import-check`. - expect(output, contains('`--${CommandLineOptions.skipImportCheckFlag}`')); - } - - test_lifecycle_skip_import_check_enable() async { - var projectContents = simpleProject( - sourceText: ''' -import 'package:foo/foo.dart'; -import 'package:foo/bar.dart'; - -int f() => null; -''', - packageConfigText: _getPackageConfigText( - migrated: false, packagesMigrated: {'foo': false})); - var projectDir = createProjectDir(projectContents); - resourceProvider.newFile(packagePath('foo/lib/foo.dart'), ''); - resourceProvider.newFile(packagePath('foo/lib/bar.dart'), ''); - var cli = _createCli(); - await runWithPreviewServer(cli, ['--skip-import-check', projectDir], - (url) async { - await assertPreviewServerResponsive(url!); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('Warning: package has unmigrated dependencies')); - // Output should not mention the particular unmigrated dependencies. - expect(output, isNot(contains('package:foo'))); - // Output should mention that the user can rerun without - // `--skip-import-check`. - expect(output, contains('`--${CommandLineOptions.skipImportCheckFlag}`')); - }); - } - - test_lifecycle_summary() async { - var projectContents = simpleProject(); - var projectDir = createProjectDir(projectContents); - var summaryPath = resourceProvider.convertPath('/summary.json'); - var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs( - ['--no-web-preview', '--summary', summaryPath, projectDir]))!; - await cliRunner.run(); - var summaryData = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - expect(summaryData, TypeMatcher()); - expect(summaryData, contains('changes')); - assertNormalExit(cliRunner); - } - - test_lifecycle_summary_does_not_double_count_hint_removals() async { - var projectContents = simpleProject(sourceText: 'int/*?*/ x;'); - var projectDir = createProjectDir(projectContents); - var summaryPath = resourceProvider.convertPath('/summary.json'); - var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs( - ['--no-web-preview', '--summary', summaryPath, projectDir]))!; - await cliRunner.run(); - assertNormalExit(cliRunner); - var summaryData = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - var separator = resourceProvider.pathContext.separator; - expect(summaryData['changes']['byPath']['lib${separator}test.dart'], - {'makeTypeNullableDueToHint': 1}); - } - - test_lifecycle_summary_rewritten_upon_rerun() async { - var projectContents = simpleProject(sourceText: 'int f(int/*?*/ i) => i;'); - var projectDir = createProjectDir(projectContents); - var cli = _createCli(); - var summaryPath = resourceProvider.convertPath('/summary.json'); - await runWithPreviewServer(cli, ['--summary', summaryPath, projectDir], - (url) async { - await assertPreviewServerResponsive(url!); - var summaryData = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - var separator = resourceProvider.pathContext.separator; - expect(summaryData['changes']['byPath']['lib${separator}test.dart'], - {'makeTypeNullableDueToHint': 1, 'makeTypeNullable': 1}); - var testPath = - resourceProvider.pathContext.join(projectDir, 'lib', 'test.dart'); - var newSourceText = 'int f(int/*?*/ i) => i + 1;'; - resourceProvider.getFile(testPath).writeAsStringSync(newSourceText); - // Rerunning should create a new summary - var uri = Uri.parse(url); - var response = await httpPost(uri.replace(path: 'rerun-migration'), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - assertHttpSuccess(response); - summaryData = - jsonDecode(resourceProvider.getFile(summaryPath).readAsStringSync()); - expect(summaryData['changes']['byPath']['lib${separator}test.dart'], { - 'typeNotMadeNullable': 1, - 'makeTypeNullableDueToHint': 1, - 'checkExpression': 1 - }); - }); - } - - test_lifecycle_uri_error() async { - var projectContents = simpleProject(sourceText: ''' -import 'package:does_not/exist.dart'; -int f() => null; -'''); - var projectDir = createProjectDir(projectContents); - await assertRunFailure([projectDir]); - var output = logger.stdoutBuffer.toString(); - expect(output, contains('1 analysis issue found')); - expect(output, contains('uri_does_not_exist')); - expect(output, isNot(contains('erroneous migration suggestions'))); - expect(output, contains('Run `dart pub get`')); - expect(output, contains('Try running `dart migrate` again')); - expect(output, isNot(contains('Set the lower SDK constraint'))); - } - - test_migrate_path_absolute() { - resourceProvider.newFolder(resourceProvider.pathContext - .join(resourceProvider.pathContext.current, 'foo')); - expect( - resourceProvider.pathContext - .isAbsolute(assertParseArgsSuccess(['foo']).directory), - isTrue); - } - - test_migrate_path_file() { - resourceProvider.newFile(resourceProvider.pathContext.absolute('foo'), ''); - expect(assertDecodeArgsFailure(['foo']), contains('foo is a file')); - } - - test_migrate_path_non_existent() { - expect(assertDecodeArgsFailure(['foo']), contains('foo does not exist')); - } - - test_migrate_path_none() { - expect(assertParseArgsSuccess([]).directory, - resourceProvider.pathContext.current); - } - - test_migrate_path_normalized() { - expect(assertParseArgsSuccess(['foo/..']).directory, isNot(contains('..'))); - } - - test_migrate_path_one() { - resourceProvider.newFolder(resourceProvider.pathContext - .join(resourceProvider.pathContext.current, 'foo')); - expect( - assertParseArgsSuccess(['foo']).directory, - resourceProvider.pathContext - .join(resourceProvider.pathContext.current, 'foo')); - } - - test_migrate_path_two() async { - var stderrText = await assertRunFailure(['foo', 'bar'], withUsage: true); - expect(stderrText, contains('No more than one path may be specified')); - } - - test_option_preview_hostname() { - expect( - assertParseArgsSuccess(['--preview-hostname', 'any']).previewHostname, - 'any'); - } - - test_option_preview_hostname_default() { - expect(assertParseArgsSuccess([]).previewHostname, 'localhost'); - } - - test_option_preview_port() { - expect( - assertParseArgsSuccess(['--preview-port', '4040']).previewPort, 4040); - } - - test_option_preview_port_default() { - expect(assertParseArgsSuccess([]).previewPort, isNull); - } - - test_option_preview_port_format_error() { - expect(assertDecodeArgsFailure(['--preview-port', 'abc']), - contains('Invalid value for --preview-port')); - } - - test_option_sdk() { - var path = Uri.parse('file:///foo/bar/baz').toFilePath(); - expect(assertParseArgsSuccess(['--sdk-path', path]).sdkPath, same(path)); - } - - test_option_sdk_default() { - var cli = _createCli(); - var cliRunner = cli.decodeCommandLineArgs(_parseArgs([]))!; - expect(cliRunner.options.sdkPath, - cli._test.resourceProvider.convertPath(sdkRootPathPosix)); - } - - test_option_sdk_hidden() { - var optionName = '--sdk-path'; - expect(_getHelpText(verbose: false), isNot(contains(optionName))); - expect(_getHelpText(verbose: true), contains(optionName)); - } - - test_option_summary() { - var summaryPath = resourceProvider.convertPath('/summary.json'); - expect(assertParseArgsSuccess(['--summary', summaryPath]).summary, - summaryPath); - } - - test_option_unrecognized() async { - expect( - await assertParseArgsFailure(['--this-option-does-not-exist']), - contains( - 'Could not find an option named "this-option-does-not-exist"')); - } - - test_package_config_does_not_exist() async { - var projectContents = simpleProject() - ..remove('.dart_tool/package_config.json'); - var projectDir = createProjectDir(projectContents); - - if (dartVersionIsNullSafeByDefault) { - // The lack of a package config file means the test file is already opted - // in. - await assertRunFailure(['--apply-changes', projectDir]); - _expectErrorIndicatingCodeIsAlreadyOptedIn(); - } else { - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, - simpleProject(migrated: true) - ..remove('.dart_tool/package_config.json')); - } - } - - test_package_config_is_missing_languageVersion() async { - var packageConfigText = ''' -{ - "configVersion": 2, - "packages": [ - { - "name": "test", - "rootUri": "../", - "packageUri": "lib/" - } - ] -} -'''; - var projectContents = simpleProject(packageConfigText: packageConfigText); - var projectDir = createProjectDir(projectContents); - - if (dartVersionIsNullSafeByDefault) { - // An omitted languageVersion field means the code is opted in to null - // safety. - await assertRunFailure(['--apply-changes', projectDir]); - _expectErrorIndicatingCodeIsAlreadyOptedIn(); - } else { - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents(projectDir, - simpleProject(migrated: true, packageConfigText: packageConfigText)); - } - } - - test_package_config_is_missing_this_package() async { - var packageConfigText = ''' -{ - "configVersion": 2, - "packages": [ - ] -} -'''; - var projectContents = simpleProject(packageConfigText: packageConfigText); - var projectDir = createProjectDir(projectContents); - - if (dartVersionIsNullSafeByDefault) { - // An omitted entry in the package config means the code is opted in to null - // safety. - await assertRunFailure(['--apply-changes', projectDir]); - _expectErrorIndicatingCodeIsAlreadyOptedIn(); - } else { - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents(projectDir, - simpleProject(migrated: true, packageConfigText: packageConfigText)); - } - } - - test_package_config_is_wrong_version() async { - var packageConfigText = ''' -{ - "configVersion": 3, - "packages": [ - { - "name": "test", - "rootUri": "../", - "packageUri": "lib/", - "languageVersion": "2.6" - } - ] -} -'''; - var projectContents = simpleProject(packageConfigText: packageConfigText); - var projectDir = createProjectDir(projectContents); - - if (dartVersionIsNullSafeByDefault) { - // An unreadable package config means the code is opted in to null safety. - await assertRunFailure(['--apply-changes', projectDir]); - _expectErrorIndicatingCodeIsAlreadyOptedIn(); - } else { - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents(projectDir, - simpleProject(migrated: true, packageConfigText: packageConfigText)); - } - } - - test_pubspec_add_collection_dependency() async { - var projectContents = simpleProject(sourceText: ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -''', pubspecText: ''' -name: test -environment: - sdk: '>=2.6.0 <3.0.0' -dependencies: - foo: ^1.2.3 -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - expect( - logger.stdoutBuffer.toString(), contains('Please run `dart pub get`')); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, simpleProject(migrated: true, sourceText: ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven); -''', pubspecText: ''' -name: test -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - foo: ^1.2.3 - collection: ^1.15.0-nullsafety.4 -''')); - } - - test_pubspec_add_dependency_and_environment_sections() async { - var projectContents = simpleProject(sourceText: ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -''', pubspecText: ''' -name: test -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - expect( - logger.stdoutBuffer.toString(), contains('Please run `dart pub get`')); - // The Dart source code should still be migrated. - assertProjectContents(projectDir, simpleProject( - migrated: true, - sourceText: ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven); -''', - // Note: section order is weird, but it's valid and this is a rare use - // case. - pubspecText: ''' -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - collection: ^1.15.0-nullsafety.4 -name: test -''')); - } - - test_pubspec_add_dependency_section() async { - var projectContents = simpleProject(sourceText: ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - expect( - logger.stdoutBuffer.toString(), contains('Please run `dart pub get`')); - // The Dart source code should still be migrated. - assertProjectContents(projectDir, simpleProject( - migrated: true, - sourceText: ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven); -''', - // Note: `dependencies` section is in a weird place, but it's valid and - // this is a rare use case. - pubspecText: ''' -dependencies: - collection: ^1.15.0-nullsafety.4 -name: test -environment: - sdk: '>=2.12.0 <3.0.0' -''')); - } - - test_pubspec_does_not_exist() async { - var projectContents = simpleProject()..remove('pubspec.yaml'); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, - simpleProject( - migrated: true, - // The package config file should not have been touched. - packageConfigText: _getPackageConfigText(migrated: false)) - ..remove('pubspec.yaml')); - } - - test_pubspec_environment_is_missing_sdk() async { - var projectContents = simpleProject(pubspecText: ''' -name: test -environment: - foo: 1 -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, simpleProject(migrated: true, pubspecText: ''' -name: test -environment: - foo: 1 - sdk: '>=2.12.0 <3.0.0' -''')); - } - - test_pubspec_environment_is_not_a_map() async { - var pubspecText = ''' -name: test -environment: 1 -'''; - var projectContents = simpleProject(pubspecText: pubspecText); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, simpleProject(migrated: true, pubspecText: pubspecText)); - } - - test_pubspec_environment_sdk_is_exact_version() async { - var pubspecText = ''' -name: test -environment: - sdk: '2.0.0' -'''; - var projectContents = simpleProject(pubspecText: pubspecText); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, - simpleProject( - migrated: true, - pubspecText: pubspecText, - // The package config file should not have been touched. - packageConfigText: _getPackageConfigText(migrated: false))); - } - - test_pubspec_environment_sdk_is_missing_min() async { - var pubspecText = ''' -name: test -environment: - sdk: '<3.0.0' -'''; - var projectContents = simpleProject(pubspecText: pubspecText); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, - simpleProject( - migrated: true, - pubspecText: pubspecText, - // The package config file should not have been touched. - packageConfigText: _getPackageConfigText(migrated: false))); - } - - test_pubspec_environment_sdk_is_not_string() async { - var pubspecText = ''' -name: test -environment: - sdk: 1 -'''; - var projectContents = simpleProject(pubspecText: pubspecText); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, - simpleProject( - migrated: true, - pubspecText: pubspecText, - // The package config file should not have been touched. - packageConfigText: _getPackageConfigText(migrated: false))); - } - - test_pubspec_environment_sdk_is_union() async { - var pubspecText = ''' -name: test -environment: - sdk: '>=2.0.0 <2.1.0 >=2.2.0 <3.0.0' -'''; - var projectContents = simpleProject(pubspecText: pubspecText); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, - simpleProject( - migrated: true, - pubspecText: pubspecText, - // The package config file should not have been touched. - packageConfigText: _getPackageConfigText(migrated: false))); - } - - test_pubspec_has_unusual_max_sdk_constraint() async { - // No one should be using a weird max SDK constraint like this. If they are - // doing so, we'll fix it to 3.0.0. - var projectContents = simpleProject(pubspecText: ''' -name: test -environment: - sdk: '>=2.6.0 <2.17.4' -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, simpleProject(migrated: true, pubspecText: ''' -name: test -environment: - sdk: '>=2.12.0 <3.0.0' -''')); - } - - test_pubspec_is_missing_environment() async { - var projectContents = simpleProject(pubspecText: ''' -name: test -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - // The Dart source code should still be migrated. - assertProjectContents(projectDir, simpleProject(migrated: true, pubspecText: - // This is strange-looking, but valid. - ''' -environment: - sdk: '>=2.12.0 <3.0.0' -name: test -''')); - } - - test_pubspec_is_not_a_map() async { - var projectContents = simpleProject(pubspecText: 'not-a-map'); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - var message = await assertErrorExit( - cliRunner, () async => await cliRunner.run(), - withUsage: false); - expect(message, contains('Failed to parse pubspec file')); - } - - test_pubspec_preserve_collection_dependency() async { - var projectContents = simpleProject(sourceText: ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -''', pubspecText: ''' -name: test -environment: - sdk: '>=2.6.0 <3.0.0' -dependencies: - collection: ^1.16.0 -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - expect(logger.stdoutBuffer.toString(), - isNot(contains('Please run `dart pub get`'))); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, simpleProject(migrated: true, sourceText: ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven); -''', pubspecText: ''' -name: test -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - collection: ^1.16.0 -''')); - } - - test_pubspec_update_collection_dependency() async { - var projectContents = simpleProject(sourceText: ''' -int firstEven(Iterable x) - => x.firstWhere((x) => x.isEven, orElse: () => null); -''', pubspecText: ''' -name: test -environment: - sdk: '>=2.6.0 <3.0.0' -dependencies: - collection: ^1.14.0 -'''); - var projectDir = createProjectDir(projectContents); - var cliRunner = _createCli() - .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]))!; - await cliRunner.run(); - expect( - logger.stdoutBuffer.toString(), contains('Please run `dart pub get`')); - // The Dart source code should still be migrated. - assertProjectContents( - projectDir, simpleProject(migrated: true, sourceText: ''' -import 'package:collection/collection.dart' show IterableExtension; - -int? firstEven(Iterable x) - => x.firstWhereOrNull((x) => x.isEven); -''', pubspecText: ''' -name: test -environment: - sdk: '>=2.12.0 <3.0.0' -dependencies: - collection: ^1.15.0-nullsafety.4 -''')); - } - - test_uses_physical_resource_provider_by_default() { - var cli = MigrationCli(binaryName: 'nnbd_migration'); - expect(cli.resourceProvider, same(PhysicalResourceProvider.INSTANCE)); - } - - Future tryGetSourceFromServer(Uri uri, String path) async { - var authToken = uri.queryParameters['authToken']; - return await httpGet( - uri.replace( - path: resourceProvider.pathContext.toUri(path).path, - queryParameters: {'inline': 'true', 'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}); - } - - _MigrationCli _createCli() { - mock_sdk.createMockSdk( - resourceProvider: resourceProvider, - root: resourceProvider.newFolder( - resourceProvider.convertPath(sdkRootPathPosix), - ), - ); - return _MigrationCli(this); - } - - void _expectErrorIndicatingCodeIsAlreadyOptedIn() { - var errorOutput = logger.stdoutBuffer.toString(); - expect(errorOutput, contains('1 analysis issue found:')); - expect( - errorOutput, - contains("A value of type 'Null' can't be returned from the function " - "'f' because it has a return type of 'int'")); - expect(errorOutput, contains('Set the lower SDK constraint')); - } - - String _getHelpText({required bool verbose}) { - var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs( - ['--${CommandLineOptions.helpFlag}', if (verbose) '--verbose'])); - expect(cliRunner, isNull); - var helpText = logger.stderrBuffer.toString(); - return helpText; - } - - String _getPackageConfigText( - {required bool migrated, Map packagesMigrated = const {}}) { - Object makePackageEntry(String name, bool migrated, {String? rootUri}) { - rootUri ??= - resourceProvider.pathContext.toUri(packagePath(name)).toString(); - return { - 'name': name, - 'rootUri': rootUri, - 'packageUri': 'lib/', - 'languageVersion': migrated ? '2.12' : '2.6' - }; - } - - var json = { - 'configVersion': 2, - 'packages': [ - makePackageEntry('test', migrated, rootUri: '../'), - for (var entry in packagesMigrated.entries) - makePackageEntry(entry.key, entry.value) - ] - }; - return '${JsonEncoder.withIndent(' ').convert(json)}\n'; - } - - ArgResults _parseArgs(List args) { - return MigrationCli.createParser().parse(args); - } - - Future _tellPreviewToApplyChanges(String url) async { - var uri = Uri.parse(url); - var authToken = uri.queryParameters['authToken']; - var response = await httpPost( - uri.replace( - path: PreviewSite.applyMigrationPath, - queryParameters: {'authToken': authToken}), - headers: {'Content-Type': 'application/json; charset=UTF-8'}, - body: json.encode({'navigationTree': []})); - assertHttpSuccess(response); - } -} - -@reflectiveTest -class _MigrationCliTestPosix extends _MigrationCliTestBase - with _MigrationCliTestMethods { - @override - final MemoryResourceProvider resourceProvider; - - _MigrationCliTestPosix() - : resourceProvider = MemoryResourceProvider( - context: path.style == path.Style.posix - ? null - : path.Context( - style: path.Style.posix, current: '/working_dir')); -} - -@reflectiveTest -class _MigrationCliTestWindows extends _MigrationCliTestBase - with _MigrationCliTestMethods { - @override - final MemoryResourceProvider resourceProvider; - - _MigrationCliTestWindows() - : resourceProvider = MemoryResourceProvider( - context: path.style == path.Style.windows - ? null - : path.Context( - style: path.Style.windows, current: 'C:\\working_dir')); -} diff --git a/pkg/nnbd_migration/test/migration_visitor_test_base.dart b/pkg/nnbd_migration/test/migration_visitor_test_base.dart deleted file mode 100644 index ae06d266100b..000000000000 --- a/pkg/nnbd_migration/test/migration_visitor_test_base.dart +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/source/source.dart'; -import 'package:analyzer/src/dart/element/element.dart'; -import 'package:analyzer/src/dart/element/type.dart'; -import 'package:analyzer/src/dart/element/type_system.dart'; -import 'package:analyzer/src/generated/utilities_dart.dart'; -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/conditional_discard.dart'; -import 'package:nnbd_migration/src/decorated_class_hierarchy.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edge_builder.dart'; -import 'package:nnbd_migration/src/expression_checks.dart'; -import 'package:nnbd_migration/src/node_builder.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:nnbd_migration/src/variables.dart'; -import 'package:test/test.dart'; - -import 'abstract_single_unit.dart'; - -/// A [NodeMatcher] that matches any node, and records what node it matched to. -class AnyNodeMatcher extends _RecordingNodeMatcher { - @override - bool matches(NullabilityNode? node) { - return true; - } -} - -/// Mixin allowing unit tests to create decorated types easily. -mixin DecoratedTypeTester implements DecoratedTypeTesterBase { - int nodeId = 0; - - NullabilityNode get always => graph.always; - - DecoratedType get bottom => DecoratedType(typeProvider.bottomType, never); - - DecoratedType get dynamic_ => DecoratedType(typeProvider.dynamicType, always); - - NullabilityNode get never => graph.never; - - DecoratedType get null_ => DecoratedType(typeProvider.nullType, always); - - DecoratedType get void_ => DecoratedType(typeProvider.voidType, always); - - DecoratedType function(DecoratedType returnType, - {List required = const [], - List positional = const [], - Map named = const {}, - List typeFormals = const [], - NullabilityNode? node}) { - int i = 0; - var parameters = required - .map((t) => ParameterElementImpl.synthetic( - 'p${i++}', t.type!, ParameterKind.REQUIRED)) - .toList(); - parameters.addAll(positional.map((t) => ParameterElementImpl.synthetic( - 'p${i++}', t.type!, ParameterKind.POSITIONAL))); - parameters.addAll(named.entries.map((e) => ParameterElementImpl.synthetic( - e.key, e.value.type!, ParameterKind.NAMED))); - return DecoratedType( - FunctionTypeImpl( - typeFormals: typeFormals, - parameters: parameters, - returnType: returnType.type!, - nullabilitySuffix: NullabilitySuffix.star, - ), - node ?? newNode(), - returnType: returnType, - positionalParameters: required.toList()..addAll(positional), - namedParameters: named); - } - - DecoratedType future(DecoratedType parameter, {NullabilityNode? node}) { - return DecoratedType( - typeProvider.futureType(parameter.type!), node ?? newNode(), - typeArguments: [parameter]); - } - - DecoratedType futureOr(DecoratedType parameter, {NullabilityNode? node}) { - return DecoratedType( - typeProvider.futureOrType(parameter.type!), node ?? newNode(), - typeArguments: [parameter]); - } - - DecoratedType int_({NullabilityNode? node}) => - DecoratedType(typeProvider.intType, node ?? newNode()); - - DecoratedType iterable(DecoratedType elementType, {NullabilityNode? node}) => - DecoratedType( - typeProvider.iterableType(elementType.type!), node ?? newNode(), - typeArguments: [elementType]); - - DecoratedType list(DecoratedType elementType, {NullabilityNode? node}) => - DecoratedType(typeProvider.listType(elementType.type!), node ?? newNode(), - typeArguments: [elementType]); - - NullabilityNode newNode() => NullabilityNode.forTypeAnnotation( - NullabilityNodeTarget.text('node ${nodeId++}')); - - DecoratedType num_({NullabilityNode? node}) => - DecoratedType(typeProvider.numType, node ?? newNode()); - - DecoratedType object({NullabilityNode? node}) => - DecoratedType(typeProvider.objectType, node ?? newNode()); - - TypeParameterElement typeParameter(String name, DecoratedType bound) { - var element = TypeParameterElementImpl.synthetic(name); - element.bound = bound.type; - decoratedTypeParameterBounds.put(element, bound); - return element; - } - - DecoratedType typeParameterType(TypeParameterElement typeParameter, - {NullabilityNode? node}) { - return DecoratedType( - typeParameter.instantiate( - nullabilitySuffix: NullabilitySuffix.star, - ), - node ?? newNode(), - ); - } -} - -/// Base functionality that must be implemented by classes mixing in -/// [DecoratedTypeTester]. -abstract class DecoratedTypeTesterBase { - DecoratedTypeParameterBounds get decoratedTypeParameterBounds; - - NullabilityGraph get graph; - - TypeProvider get typeProvider; -} - -class EdgeBuilderTestBase extends MigrationVisitorTestBase { - DecoratedClassHierarchy? decoratedClassHierarchy; - - /// Analyzes the given source code, producing constraint variables and - /// constraints for it. - @override - Future analyze(String code) async { - var unit = await super.analyze(code); - decoratedClassHierarchy = DecoratedClassHierarchy(variables, graph); - unit.accept(EdgeBuilder( - typeProvider, - typeSystem, - variables, - graph, - testSource, - null, - decoratedClassHierarchy, - unit.declaredElement!.library)); - return unit; - } -} - -/// Mixin allowing unit tests to check for the presence of graph edges. -mixin EdgeTester { - /// Gets the set of all nodes pointed to by always, plus always itself. - Set get alwaysPlus { - var result = {graph.always}; - for (var edge in getEdges(graph.always, anyNode)) { - if (edge.guards.isEmpty) { - result.add(edge.destinationNode); - } - } - return result; - } - - /// Returns a [NodeMatcher] that matches any node whatsoever. - AnyNodeMatcher get anyNode => AnyNodeMatcher(); - - NullabilityGraphForTesting get graph; - - /// Gets the transitive closure of all nodes with hard edges pointing to - /// never, plus never itself. - Set get neverClosure { - var result = {}; - var pending = [graph.never]; - while (pending.isNotEmpty) { - var node = pending.removeLast(); - if (result.add(node)) { - for (var edge in getEdges(anyNode, node)) { - pending.add(edge.sourceNode); - } - } - } - return result; - } - - /// Gets the set of nodes with hard edges pointing to never. - Set get pointsToNever { - return {for (var edge in getEdges(anyNode, graph.never)) edge.sourceNode}; - } - - /// Asserts that a dummy edge exists from [source] to always. - NullabilityEdge assertDummyEdge(Object? source) => - assertEdge(source, graph.always, hard: false, checkable: false); - - /// Asserts that an edge exists with a node matching [source] and a node - /// matching [destination], and with the given [hard]ness and [guards]. - /// - /// [source] and [destination] are converted to [NodeMatcher] objects if they - /// aren't already. In practice this means that the caller can pass in either - /// a [NodeMatcher] or a [NullabilityNode]. - NullabilityEdge assertEdge(Object? source, Object? destination, - {required bool hard, - bool checkable = true, - bool isSetupAssignment = false, - Object guards = isEmpty, - Object? codeReference}) { - var edges = getEdges(source, destination); - if (edges.isEmpty) { - fail('Expected edge $source -> $destination, found none'); - } else if (edges.length != 1) { - fail('Found multiple edges $source -> $destination'); - } else { - var edge = edges[0]; - expect(edge.isHard, hard); - expect(edge.isCheckable, checkable); - expect(edge.isSetupAssignment, isSetupAssignment); - expect(edge.guards, guards); - if (codeReference != null) { - expect(edge.codeReference, codeReference); - } - return edge; - } - } - - /// Asserts that no edge exists with a node matching [source] and a node - /// matching [destination]. - /// - /// [source] and [destination] are converted to [NodeMatcher] objects if they - /// aren't already. In practice this means that the caller can pass in either - /// a [NodeMatcher] or a [NullabilityNode]. - void assertNoEdge(Object? source, Object? destination) { - var edges = getEdges(source, destination); - if (edges.isNotEmpty) { - fail('Expected no edge $source -> $destination, found $edges'); - } - } - - /// Asserts that a union-type edge exists between nodes [x] and [y]. - /// - /// [x] and [y] are converted to [NodeMatcher] objects if they aren't already. - /// In practice this means that the caller can pass in either a [NodeMatcher] - /// or a [NullabilityNode]. - void assertUnion(Object? x, Object? y) { - var edges = getEdges(x, y); - for (var edge in edges) { - if (edge.isUnion) { - expect(edge.upstreamNodes, hasLength(1)); - return; - } - } - fail('Expected union between $x and $y, not found'); - } - - /// Gets a list of all edges whose source matches [source] and whose - /// destination matches [destination]. - /// - /// [source] and [destination] are converted to [NodeMatcher] objects if they - /// aren't already. In practice this means that the caller can pass in either - /// a [NodeMatcher] or a [NullabilityNode]. - List getEdges(Object? source, Object? destination) { - var sourceMatcher = NodeMatcher(source); - var destinationMatcher = NodeMatcher(destination); - var result = []; - for (var edge in graph.getAllEdges()) { - if (sourceMatcher.matches(edge.sourceNode) && - destinationMatcher.matches(edge.destinationNode)) { - sourceMatcher.matched(edge.sourceNode); - destinationMatcher.matched(edge.destinationNode); - result.add(edge); - } - } - return result; - } - - /// Returns a [NodeMatcher] that matches any node in the given set. - NodeSetMatcher inSet(Set nodes) => NodeSetMatcher(nodes); - - /// Creates a [NodeMatcher] matching a substitution node whose inner and outer - /// nodes match [inner] and [outer]. - /// - /// [inner] and [outer] are converted to [NodeMatcher] objects if they aren't - /// already. In practice this means that the caller can pass in either a - /// [NodeMatcher] or a [NullabilityNode]. - NodeMatcher substitutionNode(Object? inner, Object? outer) => - _SubstitutionNodeMatcher(NodeMatcher(inner), NodeMatcher(outer)); -} - -/// Mock representation of constraint variables. -class InstrumentedVariables extends Variables { - final _conditionalDiscard = {}; - - final _decoratedExpressionTypes = {}; - - final _expressionChecks = {}; - - InstrumentedVariables(super.graph, super.typeProvider); - - /// Gets the [ExpressionChecks] associated with the given [expression]. - ExpressionChecksOrigin? checkExpression(Expression expression) => - _expressionChecks[_normalizeExpression(expression)]; - - /// Gets the [conditionalDiscard] associated with the given [expression]. - ConditionalDiscard? conditionalDiscard(AstNode node) => - _conditionalDiscard[node]; - - /// Gets the [DecoratedType] associated with the given [expression]. - DecoratedType? decoratedExpressionType(Expression expression) => - _decoratedExpressionTypes[_normalizeExpression(expression)]; - - @override - void recordConditionalDiscard( - Source? source, AstNode node, ConditionalDiscard conditionalDiscard) { - _conditionalDiscard[node] = conditionalDiscard; - super.recordConditionalDiscard(source, node, conditionalDiscard); - } - - void recordDecoratedExpressionType(Expression node, DecoratedType? type) { - super.recordDecoratedExpressionType(node, type); - _decoratedExpressionTypes[_normalizeExpression(node)] = type; - } - - @override - void recordExpressionChecks( - Source? source, Expression expression, ExpressionChecksOrigin origin) { - super.recordExpressionChecks(source, expression, origin); - _expressionChecks[_normalizeExpression(expression)] = origin; - } - - /// Unwraps any parentheses surrounding [expression]. - Expression _normalizeExpression(Expression expression) { - while (expression is ParenthesizedExpression) { - expression = expression.expression; - } - return expression; - } -} - -class MigrationVisitorTestBase extends AbstractSingleUnitTest with EdgeTester { - late InstrumentedVariables variables; - - final NullabilityGraphForTesting graph; - - final decoratedTypeParameterBounds = DecoratedTypeParameterBounds(); - - MigrationVisitorTestBase() : this._(NullabilityGraphForTesting()); - - MigrationVisitorTestBase._(this.graph); - - NullabilityNode get always => graph.always; - - NullabilityNode get never => graph.never; - - TypeProvider get typeProvider => testAnalysisResult.typeProvider; - - TypeSystemImpl get typeSystem => - testAnalysisResult.typeSystem as TypeSystemImpl; - - Future analyze(String code) async { - await resolveTestUnit(code); - variables = InstrumentedVariables(graph, typeProvider); - testUnit! - .accept(NodeBuilder(variables, testSource, null, graph, typeProvider)); - return testUnit!; - } - - /// Gets the [DecoratedType] associated with the constructor declaration whose - /// name matches [search]. - DecoratedType decoratedConstructorDeclaration(String search) => variables - .decoratedElementType(findNode.constructor(search).declaredElement!); - - Map decoratedDirectSupertypes(String name) { - return variables.decoratedDirectSupertypes(findElement.classOrMixin(name)); - } - - /// Gets the [DecoratedType] associated with the generic function type - /// annotation whose text is [text]. - DecoratedType decoratedGenericFunctionTypeAnnotation(String text) { - return variables.decoratedTypeAnnotation( - testSource, findNode.genericFunctionType(text)); - } - - /// Gets the [DecoratedType] associated with the method declaration whose - /// name matches [search]. - DecoratedType decoratedMethodType(String search) => - variables.decoratedElementType( - findNode.methodDeclaration(search).declaredElement!); - - /// Gets the [DecoratedType] associated with the type annotation whose text - /// is [text]. - DecoratedType decoratedTypeAnnotation(String text) { - return variables.decoratedTypeAnnotation( - testSource, findNode.typeAnnotation(text)); - } - - /// Gets the [ConditionalDiscard] information associated with the collection - /// element whose text is [text]. - ConditionalDiscard? elementDiscard(String text) { - return variables.conditionalDiscard(findNode.collectionElement(text)); - } - - /// Returns a [Matcher] that matches a [CodeReference] pointing to the given - /// file [offset], with the given [function] name. - TypeMatcher matchCodeRef( - {required int offset, required String function}) { - var location = testUnit!.lineInfo.getLocation(offset); - return TypeMatcher() - .having((cr) => cr.line, 'line', location.lineNumber) - .having((cr) => cr.column, 'column', location.columnNumber) - .having((cr) => cr.function, 'function', function); - } - - void setUp() { - DecoratedTypeParameterBounds.current = decoratedTypeParameterBounds; - super.setUp(); - } - - /// Gets the [ConditionalDiscard] information associated with the statement - /// whose text is [text]. - ConditionalDiscard? statementDiscard(String text) { - return variables.conditionalDiscard(findNode.statement(text)); - } - - void tearDown() { - DecoratedTypeParameterBounds.current = null; - super.tearDown(); - } -} - -/// Abstract base class representing a thing that can be matched against -/// nullability nodes. -abstract class NodeMatcher { - factory NodeMatcher(Object? expectation) { - if (expectation is NodeMatcher) return expectation; - if (expectation is NullabilityNode) return _ExactNodeMatcher(expectation); - fail( - 'Unclear how to match node expectation of type ${expectation.runtimeType}'); - } - - void matched(NullabilityNode? node); - - bool matches(NullabilityNode? node); -} - -/// A [NodeMatcher] that matches any node contained in the given set. -class NodeSetMatcher extends _RecordingNodeMatcher { - final Set _targetSet; - - NodeSetMatcher(this._targetSet); - - @override - bool matches(NullabilityNode? node) => _targetSet.contains(node); -} - -/// A [NodeMatcher] that matches exactly one node. -class _ExactNodeMatcher implements NodeMatcher { - final NullabilityNode _expectation; - - _ExactNodeMatcher(this._expectation); - - @override - void matched(NullabilityNode? node) {} - - @override - bool matches(NullabilityNode? node) => node == _expectation; -} - -/// Base class for [NodeMatcher]s that remember which nodes were matched. -abstract class _RecordingNodeMatcher implements NodeMatcher { - final List _matchingNodes = []; - - NullabilityNode? get matchingNode => _matchingNodes.single; - - @override - void matched(NullabilityNode? node) { - _matchingNodes.add(node); - } -} - -/// A [NodeMatcher] that matches a substitution node with the given inner and -/// outer nodes. -class _SubstitutionNodeMatcher implements NodeMatcher { - final NodeMatcher inner; - final NodeMatcher outer; - - _SubstitutionNodeMatcher(this.inner, this.outer); - - @override - void matched(NullabilityNode? node) { - if (node is NullabilityNodeForSubstitution) { - inner.matched(node.innerNode); - outer.matched(node.outerNode); - } else { - throw StateError( - 'matched should only be called on nodes for which matches returned ' - 'true'); - } - } - - @override - bool matches(NullabilityNode? node) { - return node is NullabilityNodeForSubstitution && - inner.matches(node.innerNode) && - outer.matches(node.outerNode); - } -} diff --git a/pkg/nnbd_migration/test/node_builder_test.dart b/pkg/nnbd_migration/test/node_builder_test.dart deleted file mode 100644 index 12f2090865d6..000000000000 --- a/pkg/nnbd_migration/test/node_builder_test.dart +++ /dev/null @@ -1,2264 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:analyzer/dart/element/type.dart'; -import 'package:nnbd_migration/src/decorated_type.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/hint_action.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'migration_visitor_test_base.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(NodeBuilderTest); - }); -} - -@reflectiveTest -class NodeBuilderTest extends MigrationVisitorTestBase { - /// Gets the [DecoratedType] associated with the function declaration whose - /// name matches [search]. - DecoratedType decoratedFunctionType(String search) => - variables.decoratedElementType( - findNode.functionDeclaration(search).declaredElement!); - - DecoratedType? decoratedTypeParameterBound(String search) => - variables.decoratedTypeParameterBound( - findNode.typeParameter(search).declaredElement!); - - Future test_catch_clause_with_stacktrace_with_on() async { - await analyze(''' -void f() { - try {} on String catch (ex, st) {} -} -'''); - var exceptionType = variables.decoratedElementType( - findNode.catchClauseParameter('ex').declaredElement!); - expect(exceptionType.node, TypeMatcher()); - var stackTraceType = variables.decoratedElementType( - findNode.catchClauseParameter('st').declaredElement!); - assertEdge(stackTraceType.node, never, hard: true, checkable: false); - } - - Future test_catch_clause_with_stacktrace_without_on() async { - await analyze(''' -void f() { - try {} catch (ex, st) {} -} -'''); - var exceptionType = variables.decoratedElementType( - findNode.catchClauseParameter('ex').declaredElement!); - expect(exceptionType.node.isImmutable, false); - var stackTraceType = variables.decoratedElementType( - findNode.catchClauseParameter('st').declaredElement!); - assertEdge(stackTraceType.node, never, hard: true, checkable: false); - } - - Future test_catch_clause_without_catch() async { - await analyze(''' -void f() { - try {} on String {} -} -'''); - // No assertions, since no variables are declared; we just want to make sure - // we don't crash. - } - - Future test_catch_clause_without_stacktrace_with_on() async { - await analyze(''' -void f() { - try {} on String catch (ex) {} -} -'''); - var exceptionType = variables.decoratedElementType( - findNode.catchClauseParameter('ex').declaredElement!); - expect(exceptionType.node, TypeMatcher()); - } - - Future test_catch_clause_without_stacktrace_without_on() async { - await analyze(''' -void f() { - try {} catch (ex) {} -} -'''); - var exceptionType = variables.decoratedElementType( - findNode.catchClauseParameter('ex').declaredElement!); - expect(exceptionType.node.isImmutable, false); - } - - Future test_class_alias_synthetic_constructors_no_parameters() async { - await analyze(''' -class C { - C.a(); - C.b(); -} -mixin M {} -class D = C with M; -'''); - var constructors = findElement.class_('D').constructors; - expect(constructors, hasLength(2)); - var a = findElement.constructor('a', of: 'D'); - var aType = variables.decoratedElementType(a); - _assertType(aType.type!, 'D Function()'); - expect(aType.node, same(never)); - expect(aType.typeArguments, isEmpty); - _assertType(aType.returnType!.type!, 'D'); - expect(aType.returnType!.node, same(never)); - var b = findElement.constructor('b', of: 'D'); - var bType = variables.decoratedElementType(b); - _assertType(bType.type!, 'D Function()'); - expect(bType.node, same(never)); - expect(bType.typeArguments, isEmpty); - _assertType(bType.returnType!.type!, 'D'); - expect(bType.returnType!.node, same(never)); - } - - Future test_class_alias_synthetic_constructors_with_parameters() async { - await analyze(''' -class C { - C.a(int i); - C.b([int i]); - C.c({int i}); - C.d(List x); -} -mixin M {} -class D = C with M; -'''); - var constructors = findElement.class_('D').constructors; - expect(constructors, hasLength(4)); - var a = findElement.constructor('a', of: 'D'); - var aType = variables.decoratedElementType(a); - _assertType(aType.type!, 'D Function(int)'); - expect(aType.node, same(never)); - expect(aType.typeArguments, isEmpty); - _assertType(aType.returnType!.type!, 'D'); - expect(aType.returnType!.node, same(never)); - expect(aType.positionalParameters, hasLength(1)); - _assertType(aType.positionalParameters![0].type!, 'int'); - expect(aType.positionalParameters![0].node, - TypeMatcher()); - expect(aType.namedParameters, isEmpty); - var b = findElement.constructor('b', of: 'D'); - var bType = variables.decoratedElementType(b); - _assertType(bType.type!, 'D Function([int])'); - expect(bType.node, same(never)); - expect(bType.typeArguments, isEmpty); - _assertType(bType.returnType!.type!, 'D'); - expect(bType.returnType!.node, same(never)); - expect(bType.positionalParameters, hasLength(1)); - _assertType(bType.positionalParameters![0].type!, 'int'); - expect(bType.positionalParameters![0].node, - TypeMatcher()); - expect(bType.namedParameters, isEmpty); - var c = findElement.constructor('c', of: 'D'); - var cType = variables.decoratedElementType(c); - _assertType(cType.type!, 'D Function({int i})'); - expect(cType.node, same(never)); - expect(cType.typeArguments, isEmpty); - _assertType(cType.returnType!.type!, 'D'); - expect(cType.returnType!.node, same(never)); - expect(cType.positionalParameters, isEmpty); - expect(cType.namedParameters, hasLength(1)); - expect(cType.namedParameters, contains('i')); - _assertType(cType.namedParameters!['i']!.type!, 'int'); - expect(cType.namedParameters!['i']!.node, - TypeMatcher()); - var d = findElement.constructor('d', of: 'D'); - var dType = variables.decoratedElementType(d); - _assertType(dType.type!, 'D Function(List)'); - expect(dType.node, same(never)); - expect(dType.typeArguments, isEmpty); - _assertType(dType.returnType!.type!, 'D'); - expect(dType.returnType!.node, same(never)); - expect(dType.positionalParameters, hasLength(1)); - _assertType(dType.positionalParameters![0].type!, 'List'); - expect(dType.positionalParameters![0].node, - TypeMatcher()); - expect(dType.positionalParameters![0].typeArguments, hasLength(1)); - _assertType(dType.positionalParameters![0].typeArguments[0]!.type!, 'int'); - expect(dType.positionalParameters![0].typeArguments[0]!.node, - TypeMatcher()); - expect(dType.namedParameters, isEmpty); - } - - Future - test_class_alias_synthetic_constructors_with_parameters_generic() async { - await analyze(''' -class C { - C(T t); -} -mixin M {} -class D = C with M; -'''); - var dConstructor = findElement.unnamedConstructor('D'); - var dConstructorType = variables.decoratedElementType(dConstructor); - _assertType(dConstructorType.type!, 'D Function(U)'); - expect(dConstructorType.node, same(never)); - expect(dConstructorType.typeFormals, isEmpty); - _assertType(dConstructorType.returnType!.type!, 'D'); - expect(dConstructorType.returnType!.node, same(never)); - var typeArguments = dConstructorType.returnType!.typeArguments; - expect(typeArguments, hasLength(1)); - _assertType(typeArguments[0]!.type!, 'U'); - expect(typeArguments[0]!.node, same(never)); - var dParams = dConstructorType.positionalParameters!; - expect(dParams, hasLength(1)); - _assertType(dParams[0].type!, 'U'); - expect(dParams[0].node, TypeMatcher()); - } - - Future test_class_with_default_constructor() async { - await analyze(''' -class C {} -'''); - var defaultConstructor = findElement.class_('C').constructors.single; - var decoratedConstructorType = - variables.decoratedElementType(defaultConstructor); - _assertType(decoratedConstructorType.type!, 'C Function()'); - expect(decoratedConstructorType.node, same(never)); - _assertType(decoratedConstructorType.returnType!.type!, 'C'); - expect(decoratedConstructorType.returnType!.node, same(never)); - } - - Future test_class_with_default_constructor_generic() async { - await analyze(''' -class C {} -'''); - var defaultConstructor = findElement.class_('C').constructors.single; - var decoratedConstructorType = - variables.decoratedElementType(defaultConstructor); - _assertType(decoratedConstructorType.type!, 'C Function()'); - expect(decoratedConstructorType.node, same(never)); - expect(decoratedConstructorType.typeArguments, isEmpty); - var returnType = decoratedConstructorType.returnType!; - _assertType(returnType.type!, 'C'); - expect(returnType.node, same(never)); - expect(returnType.typeArguments, hasLength(2)); - _assertType(returnType.typeArguments[0]!.type!, 'T'); - expect(returnType.typeArguments[0]!.node, same(never)); - _assertType(returnType.typeArguments[1]!.type!, 'U'); - expect(returnType.typeArguments[1]!.node, same(never)); - } - - Future test_constructor_factory() async { - await analyze(''' -class C { - C._(); - factory C() => C._(); -} -'''); - var decoratedType = decoratedConstructorDeclaration('C(').returnType!; - expect(decoratedType.node, same(never)); - } - - Future test_constructor_metadata() async { - await analyze(''' -class A { - final Object x; - const A(this.x); -} -class C { - @A([]) - C(); -} -'''); - var node = decoratedTypeAnnotation('int').node; - expect(node, TypeMatcher()); - } - - Future test_constructor_returnType_implicit_dynamic() async { - await analyze(''' -class C { - C(); -} -'''); - var decoratedType = decoratedConstructorDeclaration('C(').returnType!; - expect(decoratedType.node, same(never)); - } - - Future test_constructorFieldInitializer_visit_expression() async { - await analyze(''' -class C { - C() : f = []; - Object f; -} -'''); - var node = decoratedTypeAnnotation('int').node; - expect(node, TypeMatcher()); - } - - Future test_directSupertypes_class_extends() async { - await analyze(''' -class C {} -class D extends C {} -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V> {').node)); - } - - Future test_directSupertypes_class_extends_default() async { - await analyze(''' -class C {} -'''); - var types = decoratedDirectSupertypes('C'); - var decorated = types[typeProvider.objectType.element]!; - _assertType(decorated.type!, 'Object'); - assertEdge(decorated.node, never, hard: true, checkable: false); - expect(decorated.typeArguments, isEmpty); - } - - Future test_directSupertypes_class_implements() async { - await analyze(''' -class C {} -class D implements C {} -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V> {').node)); - } - - Future test_directSupertypes_class_with() async { - await analyze(''' -class C {} -class D extends Object with C {} -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V> {').node)); - } - - Future test_directSupertypes_classAlias_extends() async { - await analyze(''' -class M {} -class C {} -class D = C with M; -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V> w').node)); - } - - Future test_directSupertypes_classAlias_implements() async { - await analyze(''' -class M {} -class C {} -class D = Object with M implements C; -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V>;').node)); - } - - Future test_directSupertypes_classAlias_with() async { - await analyze(''' -class C {} -class D = Object with C; -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V>;').node)); - } - - Future test_directSupertypes_dartCoreClass() async { - await analyze(''' -abstract class D extends Iterable {} -'''); - var types = decoratedDirectSupertypes('D'); - var super_ = types.values.single!; - _assertType(super_.type!, 'Iterable'); - expect(super_.node, same(never)); - expect(super_.typeArguments, hasLength(1)); - expect(super_.typeArguments[0]!.node, - same(decoratedTypeAnnotation('V> {').node)); - } - - Future test_directSupertypes_mixin_extends_default() async { - await analyze(''' -mixin C {} -'''); - var types = decoratedDirectSupertypes('C'); - var decorated = types[typeProvider.objectType.element]!; - _assertType(decorated.type!, 'Object'); - assertEdge(decorated.node, never, hard: true, checkable: false); - expect(decorated.typeArguments, isEmpty); - } - - Future test_directSupertypes_mixin_implements() async { - await analyze(''' -class C {} -mixin D implements C {} -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V> {').node)); - } - - Future test_directSupertypes_mixin_on() async { - await analyze(''' -class C {} -mixin D on C {} -'''); - var types = decoratedDirectSupertypes('D'); - var decorated = types[findElement.class_('C')]!; - _assertType(decorated.type!, 'C'); - expect(decorated.node, same(never)); - expect(decorated.typeArguments, hasLength(2)); - expect(decorated.typeArguments[0]!.node, - same(decoratedTypeAnnotation('int').node)); - expect(decorated.typeArguments[1]!.node, - same(decoratedTypeAnnotation('V> {').node)); - } - - Future test_displayName_castType() async { - await analyze('f(x) => x as int;'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'cast type (test.dart:1:14)'); - } - - Future test_displayName_constructedType() async { - await analyze(''' -class C { - factory C() = D; -} -class D implements C {} -'''); - expect(decoratedTypeAnnotation('int').node.displayName, - 'type argument 0 of constructed type (test.dart:2:19)'); - } - - Future test_displayName_exceptionType_implicit() async { - await analyze(''' -f(void Function() g) { - try { - g(); - } catch (e) {} -} -'''); - expect( - variables - .decoratedElementType( - findNode.catchClauseParameter('e)').declaredElement!) - .node - .displayName, - 'f.e (test.dart:4:5)'); - } - - Future test_displayName_exceptionType_no_variable() async { - await analyze(''' -f(void Function() g) { - try { - g(); - } on String {} -} -'''); - expect(decoratedTypeAnnotation('String').node.displayName, - 'exception type (test.dart:4:8)'); - } - - Future test_displayName_exceptionType_variable() async { - await analyze(''' -f(void Function() g) { - try { - g(); - } on String catch (s) {} -} -'''); - expect(decoratedTypeAnnotation('String').node.displayName, - 'f.s (test.dart:4:8)'); - } - - Future test_displayName_explicitParameterType_named() async { - await analyze('void f({int x, int y}) {}'); - expect(decoratedTypeAnnotation('int x').node.displayName, - 'parameter x of f (test.dart:1:9)'); - expect(decoratedTypeAnnotation('int y').node.displayName, - 'parameter y of f (test.dart:1:16)'); - } - - Future test_displayName_explicitParameterType_positional() async { - await analyze('void f(int x, int y, [int z]) {}'); - expect(decoratedTypeAnnotation('int x').node.displayName, - 'parameter 0 of f (test.dart:1:8)'); - expect(decoratedTypeAnnotation('int y').node.displayName, - 'parameter 1 of f (test.dart:1:15)'); - expect(decoratedTypeAnnotation('int z').node.displayName, - 'parameter 2 of f (test.dart:1:23)'); - } - - Future test_displayName_extendedType() async { - await analyze('extension E on int {}'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'extended type (test.dart:1:16)'); - } - - Future test_displayName_field() async { - await analyze('class C { int x; }'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'C.x (test.dart:1:11)'); - } - - Future test_displayName_for_loop_variable() async { - await analyze('f(List x) { for (int y in x) {} }'); - expect(decoratedTypeAnnotation('int y').node.displayName, - 'f.y (test.dart:1:23)'); - } - - Future - test_displayName_functionExpressionInvocation_type_argument() async { - await analyze('f(g) => g();'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'type argument (test.dart:1:11)'); - } - - Future test_displayName_listElementType() async { - await analyze('f() => [];'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'list element type (test.dart:1:9)'); - } - - Future test_displayName_mapKeyType() async { - await analyze('f() => {};'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'map key type (test.dart:1:9)'); - } - - Future test_displayName_mapValueType() async { - await analyze('f() => {};'); - expect(decoratedTypeAnnotation('String').node.displayName, - 'map value type (test.dart:1:14)'); - } - - Future test_displayName_methodInvocation_type_argument() async { - await analyze('f(x) => x.g();'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'type argument (test.dart:1:13)'); - } - - Future test_displayName_setElementType() async { - await analyze('f() => {};'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'set element type (test.dart:1:9)'); - } - - Future test_displayName_supertype() async { - await analyze(''' -class C {} -class D extends C {} -'''); - expect(decoratedTypeAnnotation('int').node.displayName, - 'type argument 0 of supertype of D (test.dart:2:19)'); - } - - Future test_displayName_testedType() async { - await analyze('f(x) => x is int;'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'tested type (test.dart:1:14)'); - } - - Future test_displayName_typeArgument() async { - await analyze('var x = >[];'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'type argument 0 of list element type (test.dart:1:15)'); - } - - Future test_displayName_typedef_new_parameter() async { - await analyze('typedef F = void Function(int x);'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'parameter 0 of F (test.dart:1:27)'); - } - - Future test_displayName_typedef_new_returnType() async { - await analyze('typedef F = int Function();'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'return type of F (test.dart:1:13)'); - } - - Future test_displayName_typedef_old_parameter() async { - await analyze('typedef void F(int x);'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'parameter 0 of F (test.dart:1:16)'); - } - - Future test_displayName_typedef_old_returnType() async { - await analyze('typedef int F();'); - expect(decoratedTypeAnnotation('int').node.displayName, - 'return type of F (test.dart:1:9)'); - } - - Future test_displayName_typeParameterBound() async { - await analyze('class C {}'); - expect(decoratedTypeAnnotation('num').node.displayName, - 'bound of C.T (test.dart:1:19)'); - } - - Future test_displayName_typeParameterBound_implicit() async { - await analyze('class C {}'); - expect( - variables - .decoratedTypeParameterBound( - findElement.class_('C').typeParameters[0])! - .node - .displayName, - 'bound of C.T (test.dart:1:19)'); - } - - Future test_displayName_variable_local() async { - await analyze('f() { int x; }'); - expect( - decoratedTypeAnnotation('int').node.displayName, 'f.x (test.dart:1:7)'); - } - - Future test_displayName_variable_top_level() async { - await analyze('int x;'); - expect( - decoratedTypeAnnotation('int').node.displayName, 'x (test.dart:1:1)'); - } - - Future test_dynamic_type() async { - await analyze(''' -dynamic f() {} -'''); - var decoratedType = decoratedTypeAnnotation('dynamic'); - expect(decoratedFunctionType('f').returnType, same(decoratedType)); - assertNoEdge(always, decoratedType.node); - } - - Future test_extended_type_no_add_hint_actions() async { - await analyze(''' -class A extends Object {} -'''); - final node = decoratedTypeAnnotation('Object').node; - expect(node.hintActions, isEmpty); - } - - Future test_field_type_implicit_dynamic() async { - await analyze(''' -class C { - var x; -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node.isImmutable, false); - } - - Future test_field_type_inferred() async { - await analyze(''' -class C { - var x = 1; -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node, TypeMatcher()); - } - - Future test_field_type_inferred_dynamic() async { - await analyze(''' -dynamic f() {} -class C { - var x = f(); -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node.isImmutable, false); - } - - Future test_field_type_simple() async { - await analyze(''' -class C { - int f = 0; -} -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect(decoratedType.node, TypeMatcher()); - expect( - variables.decoratedElementType(findNode - .fieldDeclaration('f') - .fields - .variables[0] - .declaredElement!), - same(decoratedType)); - } - - Future test_fieldFormalParameter_function_namedParameter_typed() async { - await analyze(''' -class C { - Object f; - C(void this.f({int i})); -} -'''); - var ctor = findElement.unnamedConstructor('C'); - var ctorParam = ctor.parameters[0]; - var ctorType = variables.decoratedElementType(ctor); - var ctorParamType = variables.decoratedElementType(ctorParam); - expect(ctorType.positionalParameters![0], same(ctorParamType)); - expect(ctorParamType.node, TypeMatcher()); - expect(ctorParamType.namedParameters!['i'], - same(decoratedTypeAnnotation('int'))); - } - - Future - test_fieldFormalParameter_function_namedParameter_untyped() async { - await analyze(''' -class C { - Object f; - C(void this.f({i})); -} -'''); - var ctor = findElement.unnamedConstructor('C'); - var ctorParam = ctor.parameters[0]; - var ctorType = variables.decoratedElementType(ctor); - var ctorParamType = variables.decoratedElementType(ctorParam); - expect(ctorType.positionalParameters![0], same(ctorParamType)); - expect(ctorParamType.node, TypeMatcher()); - _assertType(ctorParamType.namedParameters!['i']!.type!, 'dynamic'); - expect(ctorParamType.namedParameters!['i']!.node.isImmutable, false); - } - - Future - test_fieldFormalParameter_function_positionalParameter_typed() async { - await analyze(''' -class C { - Object f; - C(void this.f(int i)); -} -'''); - var ctor = findElement.unnamedConstructor('C'); - var ctorParam = ctor.parameters[0]; - var ctorType = variables.decoratedElementType(ctor); - var ctorParamType = variables.decoratedElementType(ctorParam); - expect(ctorType.positionalParameters![0], same(ctorParamType)); - expect(ctorParamType.node, TypeMatcher()); - expect(ctorParamType.positionalParameters![0], - same(decoratedTypeAnnotation('int'))); - } - - Future - test_fieldFormalParameter_function_positionalParameter_untyped() async { - await analyze(''' -class C { - Object f; - C(void this.f(i)); -} -'''); - var ctor = findElement.unnamedConstructor('C'); - var ctorParam = ctor.parameters[0]; - var ctorType = variables.decoratedElementType(ctor); - var ctorParamType = variables.decoratedElementType(ctorParam); - expect(ctorType.positionalParameters![0], same(ctorParamType)); - expect(ctorParamType.node, TypeMatcher()); - _assertType(ctorParamType.positionalParameters![0].type!, 'dynamic'); - expect(ctorParamType.positionalParameters![0].node.isImmutable, false); - } - - Future test_fieldFormalParameter_function_return_typed() async { - await analyze(''' -class C { - Object f; - C(int this.f()); -} -'''); - var ctor = findElement.unnamedConstructor('C'); - var ctorParam = ctor.parameters[0]; - var ctorType = variables.decoratedElementType(ctor); - var ctorParamType = variables.decoratedElementType(ctorParam); - expect(ctorType.positionalParameters![0], same(ctorParamType)); - expect(ctorParamType.node, TypeMatcher()); - expect(ctorParamType.returnType, same(decoratedTypeAnnotation('int'))); - } - - Future test_fieldFormalParameter_function_return_untyped() async { - await analyze(''' -class C { - Object f; - C(this.f()) {} -} -'''); - var ctor = findElement.unnamedConstructor('C'); - var ctorParam = ctor.parameters[0]; - var ctorType = variables.decoratedElementType(ctor); - var ctorParamType = variables.decoratedElementType(ctorParam); - expect(ctorType.positionalParameters![0], same(ctorParamType)); - expect(ctorParamType.node, TypeMatcher()); - _assertType(ctorParamType.returnType!.type!, 'dynamic'); - expect(ctorParamType.returnType!.node.isImmutable, false); - } - - Future - test_fieldFormalParameter_named_no_default_required_hint() async { - await analyze(''' -class C { - String s; - C({/*required*/ this.s}); -} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.fieldFormalParameter('this.s')), - isNotNull); - } - - Future - test_fieldFormalParameter_named_no_default_required_hint_annotation() async { - await analyze(''' -class C { - String s; - C({@deprecated /*required*/ this.s}); -} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.fieldFormalParameter('this.s')), - isNotNull); - } - - Future - test_fieldFormalParameter_named_no_default_required_hint_function_typed() async { - await analyze(''' -class C { - String Function() s; - C({/*required*/ String this.s()}); -} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.fieldFormalParameter('this.s')), - isNotNull); - } - - Future - test_fieldFormalParameter_named_no_default_required_hint_type() async { - await analyze(''' -class C { - String s; - C({/*required*/ String this.s}); -} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.fieldFormalParameter('this.s')), - isNotNull); - } - - Future test_fieldFormalParameter_typed() async { - await analyze(''' -class C { - int i; - C.named(int this.i); -} -'''); - var decoratedConstructorParamType = - decoratedConstructorDeclaration('named').positionalParameters![0]; - expect(decoratedTypeAnnotation('int this'), - same(decoratedConstructorParamType)); - _assertType(decoratedConstructorParamType.type!, 'int'); - expect(decoratedConstructorParamType.node, - TypeMatcher()); - // Note: the edge builder will connect this node to the node for the type of - // the field. - } - - Future test_fieldFormalParameter_untyped() async { - await analyze(''' -class C { - int i; - C.named(this.i); -} -'''); - var decoratedConstructorParamType = - decoratedConstructorDeclaration('named').positionalParameters![0]; - _assertType(decoratedConstructorParamType.type!, 'int'); - expect(decoratedConstructorParamType.node, - TypeMatcher()); - // Note: the edge builder will unify this implicit type with the type of the - // field. - } - - Future test_formalParameter_named_no_default_required_hint() async { - await analyze(''' -void f({/*required*/ String s}) {} -'''); - expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')), - isNotNull); - } - - Future - test_formalParameter_named_no_default_required_hint_annotation() async { - await analyze(''' -void f({@deprecated /*required*/ String s}) {} -'''); - expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')), - isNotNull); - } - - Future - test_formalParameter_named_no_default_required_hint_covariant() async { - await analyze(''' -class C { - void f({/*required*/ covariant String s}) {} -} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.simpleParameter('String s')), - isNotNull); - } - - Future - test_formalParameter_named_no_default_required_hint_final_type() async { - await analyze(''' -void f({/*required*/ final String s}) {} -'''); - expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')), - isNotNull); - } - - Future - test_formalParameter_named_no_default_required_hint_no_type() async { - await analyze(''' -void f({/*required*/ s}) {} -'''); - expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')), - isNotNull); - } - - Future test_formalParameter_named_no_default_required_hint_var() async { - await analyze(''' -void f({/*required*/ var s}) {} -'''); - expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')), - isNotNull); - } - - Future test_function_explicit_returnType() async { - await analyze(''' -class C { - int f() => null; -} -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect( - decoratedType.node.displayName, 'return type of C.f (test.dart:2:3)'); - } - - Future test_function_generic_bounded() async { - await analyze(''' -T f(T t) => t; -'''); - var bound = decoratedTypeParameterBound('T extends')!; - expect(decoratedTypeAnnotation('Object'), same(bound)); - expect(bound.node, isNot(always)); - expect(bound.type, typeProvider.objectType); - } - - Future test_function_generic_implicit_bound() async { - await analyze(''' -T f(T t) => t; -'''); - var bound = decoratedTypeParameterBound('T>')!; - assertEdge(always, bound.node, hard: false); - expect(bound.type, same(typeProvider.objectType)); - } - - Future test_function_metadata() async { - await analyze(''' -class A { - final Object x; - const A(this.x); -} -@A([]) -f() {} -'''); - var node = decoratedTypeAnnotation('int').node; - expect(node, TypeMatcher()); - } - - Future test_functionExpression() async { - await analyze(''' -void f() { - var x = (int i) => 1; -} -'''); - var functionExpressionElement = - findNode.simpleParameter('int i').declaredElement!.enclosingElement!; - var decoratedType = - variables.decoratedElementType(functionExpressionElement); - expect(decoratedType.positionalParameters![0], - same(decoratedTypeAnnotation('int i'))); - expect(decoratedType.node, same(never)); - expect( - decoratedType.returnType!.node, TypeMatcher()); - } - - Future test_functionExpression_returns_bottom() async { - await analyze(''' -void f() { - var x = (int i) => throw 'foo'; -} -'''); - var functionExpressionElement = - findNode.simpleParameter('int i').declaredElement!.enclosingElement!; - var decoratedType = - variables.decoratedElementType(functionExpressionElement); - expect( - decoratedType.returnType!.node, TypeMatcher()); - } - - Future test_functionTypeAlias_generic() async { - await analyze(''' -typedef T F(U u); -'''); - var element = findElement.typeAlias('F'); - var decoratedType = variables.decoratedElementType(element.aliasedElement!); - var t = element.typeParameters[0]; - var u = element.typeParameters[1]; - // typeFormals should be empty because this is not a generic function type, - // it's a generic typedef that defines an ordinary (non-generic) function - // type. - expect(decoratedType.typeFormals, isEmpty); - expect(decoratedType.returnType, same(decoratedTypeAnnotation('T F'))); - expect( - (decoratedType.returnType!.type as TypeParameterType).element, same(t)); - expect( - decoratedType.returnType!.node, TypeMatcher()); - expect( - (decoratedType.positionalParameters![0].type as TypeParameterType) - .element, - same(u)); - expect(decoratedType.positionalParameters![0].node, - TypeMatcher()); - } - - Future test_functionTypeAlias_implicit_return_type() async { - await analyze(''' -typedef F(); -'''); - var decoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - expect(decoratedType.returnType!.type is DynamicType, isTrue); - expect(decoratedType.returnType!.node.isImmutable, false); - expect(decoratedType.typeFormals, isEmpty); - } - - Future test_functionTypeAlias_simple() async { - await analyze(''' -typedef int F(String s); -'''); - var decoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - expect(decoratedType.returnType, same(decoratedTypeAnnotation('int'))); - expect(decoratedType.typeFormals, isEmpty); - expect(decoratedType.positionalParameters![0], - same(decoratedTypeAnnotation('String'))); - } - - Future - test_functionTypedFormalParameter_named_no_default_required_hint() async { - await analyze(''' -void f({/*required*/ void s()}) {} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.functionTypedFormalParameter('s')), - isNotNull); - } - - Future - test_functionTypedFormalParameter_named_no_default_required_hint_no_return() async { - await analyze(''' -void f({/*required*/ s()}) {} -'''); - expect( - variables.getRequiredHint( - testSource, findNode.functionTypedFormalParameter('s')), - isNotNull); - } - - Future test_functionTypedFormalParameter_namedParameter_typed() async { - await analyze(''' -void f(void g({int i})) {} -'''); - var f = findElement.function('f'); - var g = f.parameters[0]; - var fType = variables.decoratedElementType(f); - var gType = variables.decoratedElementType(g); - expect(fType.positionalParameters![0], same(gType)); - expect(gType.node, TypeMatcher()); - expect(gType.namedParameters!['i'], same(decoratedTypeAnnotation('int'))); - } - - Future - test_functionTypedFormalParameter_namedParameter_untyped() async { - await analyze(''' -void f(void g({i})) {} -'''); - var f = findElement.function('f'); - var g = f.parameters[0]; - var fType = variables.decoratedElementType(f); - var gType = variables.decoratedElementType(g); - expect(fType.positionalParameters![0], same(gType)); - expect(gType.node, TypeMatcher()); - _assertType(gType.namedParameters!['i']!.type!, 'dynamic'); - expect(gType.namedParameters!['i']!.node.isImmutable, false); - } - - Future - test_functionTypedFormalParameter_positionalParameter_typed() async { - await analyze(''' -void f(void g(int i)) {} -'''); - var f = findElement.function('f'); - var g = f.parameters[0]; - var fType = variables.decoratedElementType(f); - var gType = variables.decoratedElementType(g); - expect(fType.positionalParameters![0], same(gType)); - expect(gType.node, TypeMatcher()); - expect( - gType.positionalParameters![0], same(decoratedTypeAnnotation('int'))); - } - - Future - test_functionTypedFormalParameter_positionalParameter_untyped() async { - await analyze(''' -void f(void g(i)) {} -'''); - var f = findElement.function('f'); - var g = f.parameters[0]; - var fType = variables.decoratedElementType(f); - var gType = variables.decoratedElementType(g); - expect(fType.positionalParameters![0], same(gType)); - expect(gType.node, TypeMatcher()); - _assertType(gType.positionalParameters![0].type!, 'dynamic'); - expect(gType.positionalParameters![0].node.isImmutable, false); - } - - Future test_functionTypedFormalParameter_return_typed() async { - await analyze(''' -void f(int g()) {} -'''); - var f = findElement.function('f'); - var g = f.parameters[0]; - var fType = variables.decoratedElementType(f); - var gType = variables.decoratedElementType(g); - expect(fType.positionalParameters![0], same(gType)); - expect(gType.node, TypeMatcher()); - expect(gType.returnType, same(decoratedTypeAnnotation('int'))); - } - - Future test_functionTypedFormalParameter_return_untyped() async { - await analyze(''' -void f(g()) {} -'''); - var f = findElement.function('f'); - var g = f.parameters[0]; - var fType = variables.decoratedElementType(f); - var gType = variables.decoratedElementType(g); - expect(fType.positionalParameters![0], same(gType)); - expect(gType.node, TypeMatcher()); - _assertType(gType.returnType!.type!, 'dynamic'); - expect(gType.returnType!.node.isImmutable, false); - } - - Future test_genericFunctionType_formals() async { - await analyze(''' -void f(T Function(U) x) {} -'''); - var decoratedType = decoratedGenericFunctionTypeAnnotation('T Function'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.node, TypeMatcher()); - _assertType(decoratedType.type!, 'T Function(U)'); - expect(decoratedType.typeFormals, hasLength(2)); - var t = decoratedType.typeFormals![0]; - var u = decoratedType.typeFormals![1]; - expect( - (decoratedType.returnType!.type as TypeParameterType).element, same(t)); - expect( - (decoratedType.positionalParameters![0].type as TypeParameterType) - .element, - same(u)); - } - - Future test_genericFunctionType_namedParameterType() async { - await analyze(''' -void f(void Function({int y}) x) {} -'''); - var decoratedType = - decoratedGenericFunctionTypeAnnotation('void Function({int y})'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.node, TypeMatcher()); - var decoratedIntType = decoratedTypeAnnotation('int'); - expect(decoratedType.namedParameters!['y'], same(decoratedIntType)); - expect(decoratedIntType.node, isNotNull); - expect(decoratedIntType.node, isNot(never)); - } - - Future test_genericFunctionType_returnType() async { - await analyze(''' -void f(int Function() x) {} -'''); - var decoratedType = - decoratedGenericFunctionTypeAnnotation('int Function()'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.node, TypeMatcher()); - var decoratedIntType = decoratedTypeAnnotation('int'); - expect(decoratedType.returnType, same(decoratedIntType)); - expect(decoratedIntType.node, isNotNull); - expect(decoratedIntType.node, isNot(never)); - expect(decoratedType.returnType!.node.displayName, - 'return type of parameter 0 of f (test.dart:1:8)'); - } - - Future test_genericFunctionType_syntax_inferred_dynamic_return() async { - await analyze(''' -abstract class C { - Function() f(); -} -'''); - var decoratedFType = decoratedMethodType('f'); - var decoratedFReturnType = decoratedFType.returnType!; - var decoratedFReturnReturnType = decoratedFReturnType.returnType!; - _assertType(decoratedFReturnReturnType.type!, 'dynamic'); - expect(decoratedFReturnReturnType.node.isImmutable, false); - } - - Future test_genericFunctionType_unnamedParameterType() async { - await analyze(''' -void f(void Function(int) x) {} -'''); - var decoratedType = - decoratedGenericFunctionTypeAnnotation('void Function(int)'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.node, TypeMatcher()); - var decoratedIntType = decoratedTypeAnnotation('int'); - expect(decoratedType.positionalParameters![0], same(decoratedIntType)); - expect(decoratedIntType.node, isNotNull); - expect(decoratedIntType.node, isNot(never)); - } - - Future test_genericTypeAlias_generic_inner() async { - await analyze(''' -typedef F = T Function(U u); -'''); - var element = findElement.typeAlias('F'); - var decoratedType = variables.decoratedElementType(element.aliasedElement!); - expect(decoratedType, - same(decoratedGenericFunctionTypeAnnotation('T Function'))); - expect(decoratedType.typeFormals, hasLength(2)); - var t = decoratedType.typeFormals![0]; - var u = decoratedType.typeFormals![1]; - expect(decoratedType.returnType, same(decoratedTypeAnnotation('T F'))); - expect( - (decoratedType.returnType!.type as TypeParameterType).element, same(t)); - expect( - decoratedType.returnType!.node, TypeMatcher()); - expect( - (decoratedType.positionalParameters![0].type as TypeParameterType) - .element, - same(u)); - expect(decoratedType.positionalParameters![0].node, - TypeMatcher()); - } - - Future test_genericTypeAlias_generic_outer() async { - await analyze(''' -typedef F = T Function(U u); -'''); - var element = findElement.typeAlias('F'); - var decoratedType = variables.decoratedElementType(element.aliasedElement!); - expect(decoratedType, - same(decoratedGenericFunctionTypeAnnotation('T Function'))); - var t = element.typeParameters[0]; - var u = element.typeParameters[1]; - // typeFormals should be empty because this is not a generic function type, - // it's a generic typedef that defines an ordinary (non-generic) function - // type. - expect(decoratedType.typeFormals, isEmpty); - expect(decoratedType.returnType, same(decoratedTypeAnnotation('T F'))); - expect( - (decoratedType.returnType!.type as TypeParameterType).element, same(t)); - expect( - decoratedType.returnType!.node, TypeMatcher()); - expect( - (decoratedType.positionalParameters![0].type as TypeParameterType) - .element, - same(u)); - expect(decoratedType.positionalParameters![0].node, - TypeMatcher()); - } - - Future test_genericTypeAlias_implicit_return_type() async { - await analyze(''' -typedef F = Function(); -'''); - var decoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - expect(decoratedType, - same(decoratedGenericFunctionTypeAnnotation('Function'))); - expect(decoratedType.returnType!.type is DynamicType, isTrue); - expect(decoratedType.returnType!.node.isImmutable, false); - expect(decoratedType.typeFormals, isEmpty); - } - - Future test_genericTypeAlias_simple() async { - await analyze(''' -typedef F = int Function(String s); -'''); - var decoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - expect(decoratedType, - same(decoratedGenericFunctionTypeAnnotation('int Function'))); - expect(decoratedType.returnType, same(decoratedTypeAnnotation('int'))); - expect(decoratedType.typeFormals, isEmpty); - expect(decoratedType.positionalParameters![0], - same(decoratedTypeAnnotation('String'))); - expect(decoratedType.returnType!.node.displayName, - 'return type of F (test.dart:1:13)'); - } - - Future test_implicit_type_nested_no_add_hint_actions() async { - await analyze(''' -var x = [1]; -'''); - final node = variables - .decoratedElementType(findNode - .topLevelVariableDeclaration('x') - .variables - .variables[0] - .declaredElement!) - .typeArguments[0]! - .node; - expect(node.hintActions, isEmpty); - } - - Future test_implicit_type_no_add_hint_actions() async { - await analyze(''' -var x = 1; -'''); - final node = variables - .decoratedElementType(findNode - .topLevelVariableDeclaration('x') - .variables - .variables[0] - .declaredElement!) - .node; - expect(node.hintActions, isEmpty); - } - - Future test_interfaceType_generic_instantiate_to_dynamic() async { - await analyze(''' -void f(List x) {} -'''); - var decoratedListType = decoratedTypeAnnotation('List'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedListType)); - expect(decoratedListType.node, isNotNull); - expect(decoratedListType.node, isNot(never)); - var decoratedArgType = decoratedListType.typeArguments[0]!; - expect(decoratedArgType.node.isImmutable, false); - } - - Future test_interfaceType_generic_instantiate_to_function_type() async { - await analyze(''' -class C {} -void f(C x) {} -'''); - var decoratedCType = decoratedTypeAnnotation('C x'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedCType)); - expect(decoratedCType.node, TypeMatcher()); - expect(decoratedCType.typeArguments, hasLength(1)); - var decoratedArgType = decoratedCType.typeArguments[0]!; - expect(decoratedArgType.node, TypeMatcher()); - expect(decoratedArgType.typeArguments, isEmpty); - var decoratedArgReturnType = decoratedArgType.returnType!; - expect(decoratedArgReturnType.node, TypeMatcher()); - expect(decoratedArgReturnType.typeArguments, isEmpty); - } - - Future - test_interfaceType_generic_instantiate_to_function_type_void() async { - await analyze(''' -class C {} -void f(C x) {} -'''); - var decoratedCType = decoratedTypeAnnotation('C x'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedCType)); - expect(decoratedCType.node, TypeMatcher()); - expect(decoratedCType.typeArguments, hasLength(1)); - var decoratedArgType = decoratedCType.typeArguments[0]!; - expect(decoratedArgType.node, TypeMatcher()); - expect(decoratedArgType.typeArguments, isEmpty); - var decoratedArgReturnType = decoratedArgType.returnType!; - expect(decoratedArgReturnType.node.isImmutable, false); - expect(decoratedArgReturnType.typeArguments, isEmpty); - } - - Future test_interfaceType_generic_instantiate_to_generic_type() async { - await analyze(''' -class C {} -class D> {} -void f(D x) {} -'''); - var decoratedDType = decoratedTypeAnnotation('D x'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedDType)); - expect(decoratedDType.node, TypeMatcher()); - expect(decoratedDType.typeArguments, hasLength(1)); - var decoratedArgType = decoratedDType.typeArguments[0]!; - expect(decoratedArgType.node, TypeMatcher()); - expect(decoratedArgType.typeArguments, hasLength(1)); - var decoratedArgArgType = decoratedArgType.typeArguments[0]!; - expect(decoratedArgArgType.node, TypeMatcher()); - expect(decoratedArgArgType.typeArguments, isEmpty); - } - - Future - test_interfaceType_generic_instantiate_to_generic_type_2() async { - await analyze(''' -class C {} -class D, U extends C> {} -void f(D x) {} -'''); - var decoratedDType = decoratedTypeAnnotation('D x'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedDType)); - expect(decoratedDType.node, TypeMatcher()); - expect(decoratedDType.typeArguments, hasLength(2)); - var decoratedArg0Type = decoratedDType.typeArguments[0]!; - expect(decoratedArg0Type.node, TypeMatcher()); - expect(decoratedArg0Type.typeArguments, hasLength(2)); - var decoratedArg0Arg0Type = decoratedArg0Type.typeArguments[0]!; - expect(decoratedArg0Arg0Type.node, TypeMatcher()); - expect(decoratedArg0Arg0Type.typeArguments, isEmpty); - var decoratedArg0Arg1Type = decoratedArg0Type.typeArguments[1]!; - expect(decoratedArg0Arg1Type.node, TypeMatcher()); - expect(decoratedArg0Arg1Type.typeArguments, isEmpty); - var decoratedArg1Type = decoratedDType.typeArguments[1]!; - expect(decoratedArg1Type.node, TypeMatcher()); - expect(decoratedArg1Type.typeArguments, hasLength(2)); - var decoratedArg1Arg0Type = decoratedArg1Type.typeArguments[0]!; - expect(decoratedArg1Arg0Type.node, TypeMatcher()); - expect(decoratedArg1Arg0Type.typeArguments, isEmpty); - var decoratedArg1Arg1Type = decoratedArg1Type.typeArguments[1]!; - expect(decoratedArg1Arg1Type.node, TypeMatcher()); - expect(decoratedArg1Arg1Type.typeArguments, isEmpty); - } - - Future test_interfaceType_generic_instantiate_to_object() async { - await analyze(''' -class C {} -void f(C x) {} -'''); - var decoratedListType = decoratedTypeAnnotation('C x'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedListType)); - expect(decoratedListType.node, TypeMatcher()); - expect(decoratedListType.typeArguments, hasLength(1)); - var decoratedArgType = decoratedListType.typeArguments[0]!; - expect(decoratedArgType.node, TypeMatcher()); - expect(decoratedArgType.typeArguments, isEmpty); - } - - Future test_interfaceType_typeParameter() async { - await analyze(''' -void f(List x) {} -'''); - var decoratedListType = decoratedTypeAnnotation('List'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedListType)); - expect(decoratedListType.node, isNotNull); - expect(decoratedListType.node, isNot(never)); - var decoratedIntType = decoratedTypeAnnotation('int'); - expect(decoratedListType.typeArguments[0], same(decoratedIntType)); - expect(decoratedIntType.node, isNotNull); - expect(decoratedIntType.node, isNot(never)); - } - - Future test_local_function() async { - await analyze(''' -void f() { - int g(int i) => 1; -} -'''); - var decoratedType = decoratedFunctionType('g'); - expect(decoratedType.returnType, same(decoratedTypeAnnotation('int g'))); - expect(decoratedType.positionalParameters![0], - same(decoratedTypeAnnotation('int i'))); - expect(decoratedType.node, same(never)); - } - - Future test_localVariable_type_implicit_dynamic() async { - await analyze(''' -main() { - var x; -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node.isImmutable, false); - } - - Future test_localVariable_type_inferred() async { - await analyze(''' -main() { - var x = 1; -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node, TypeMatcher()); - } - - Future test_localVariable_type_inferred_dynamic() async { - await analyze(''' -dynamic f() {} -main() { - var x = f(); -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node.isImmutable, false); - } - - Future test_localVariable_type_inferred_function() async { - await analyze(''' -main() { - var x = () => 1; -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.returnType!.node.displayName, - 'return type of main.x (test.dart:2:7)'); - } - - Future test_localVariable_type_inferred_generic() async { - await analyze(''' -main() { - var x = {1: 2}; -} -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.typeArguments[0]!.node.displayName, - 'type argument 0 of main.x (test.dart:2:7)'); - expect(decoratedType.typeArguments[1]!.node.displayName, - 'type argument 1 of main.x (test.dart:2:7)'); - } - - Future test_method_generic_bounded() async { - await analyze(''' -class C { - T f(T t) => t; -} -'''); - var bound = decoratedTypeParameterBound('T extends')!; - expect(decoratedTypeAnnotation('Object'), same(bound)); - expect(bound.node, isNot(always)); - expect(bound.type, typeProvider.objectType); - } - - Future test_method_generic_implicit_bound() async { - await analyze(''' -class C { - T f(T t) => t; -} -'''); - var bound = decoratedTypeParameterBound('T>')!; - assertEdge(always, bound.node, hard: false); - expect(bound.type, same(typeProvider.objectType)); - } - - Future test_method_metadata() async { - await analyze(''' -class A { - final Object x; - const A(this.x); -} -class C { - @A([]) - f() {} -} -'''); - var node = decoratedTypeAnnotation('int').node; - expect(node, TypeMatcher()); - } - - Future test_method_parameterType_implicit_dynamic() async { - await analyze(''' -class C { - void f(x) {} -} -'''); - var decoratedType = decoratedMethodType('f').positionalParameters![0]; - expect(decoratedType.node.isImmutable, false); - } - - Future test_method_parameterType_implicit_dynamic_named() async { - await analyze(''' -class C { - void f({x}) {} -} -'''); - var decoratedType = decoratedMethodType('f').namedParameters!['x']!; - expect(decoratedType.node.isImmutable, false); - } - - Future test_method_parameterType_inferred() async { - await analyze(''' -class B { - void f(int x) {} -} -class C extends B { - void f/*C*/(x) {} -} -'''); - var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]; - expect(decoratedType.node, TypeMatcher()); - } - - Future test_method_parameterType_inferred_dynamic() async { - await analyze(''' -class B { - void f(dynamic x) {} -} -class C extends B { - void f/*C*/(x) {} -} -'''); - var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]; - expect(decoratedType.node.isImmutable, false); - } - - Future test_method_parameterType_inferred_dynamic_named() async { - await analyze(''' -class B { - void f({dynamic x = 0}) {} -} -class C extends B { - void f/*C*/({x = 0}) {} -} -'''); - var decoratedType = decoratedMethodType('f/*C*/').namedParameters!['x']!; - expect(decoratedType.node.isImmutable, false); - } - - Future - test_method_parameterType_inferred_generic_function_typed_no_bound() async { - await analyze(''' -class B { - void f/*B*/(T Function() x) {} -} -class C extends B { - void f/*C*/(x) {} -} -'''); - var decoratedBaseType = - decoratedMethodType('f/*B*/').positionalParameters![0]; - var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]; - var decoratedTypeFormalBound = decoratedTypeParameterBounds - .get((decoratedType.type as FunctionType).typeFormals[0])!; - _assertType(decoratedTypeFormalBound.type!, 'Object'); - var decoratedBaseTypeFormalBound = decoratedTypeParameterBounds - .get((decoratedBaseType.type as FunctionType).typeFormals[0])!; - expect(decoratedTypeFormalBound.node, - isNot(same(decoratedBaseTypeFormalBound.node))); - } - - Future - test_method_parameterType_inferred_generic_function_typed_with_bound() async { - await analyze(''' -class B { - void f/*B*/(T Function() x) {} -} -class C extends B { - void f/*C*/(x) {} -} -'''); - var decoratedBaseType = - decoratedMethodType('f/*B*/').positionalParameters![0]; - var decoratedType = decoratedMethodType('f/*C*/').positionalParameters![0]; - var decoratedTypeFormalBound = decoratedTypeParameterBounds - .get((decoratedType.type as FunctionType).typeFormals[0])!; - _assertType(decoratedTypeFormalBound.type!, 'num'); - var decoratedBaseTypeFormalBound = decoratedTypeParameterBounds - .get((decoratedBaseType.type as FunctionType).typeFormals[0])!; - expect(decoratedTypeFormalBound.node, - isNot(same(decoratedBaseTypeFormalBound.node))); - } - - Future test_method_parameterType_inferred_named() async { - await analyze(''' -class B { - void f({int x = 0}) {} -} -class C extends B { - void f/*C*/({x = 0}) {} -} -'''); - var decoratedType = decoratedMethodType('f/*C*/').namedParameters!['x']!; - expect(decoratedType.node, TypeMatcher()); - } - - Future test_method_returnType_implicit_dynamic() async { - await analyze(''' -class C { - f() => 1; -} -'''); - var decoratedType = decoratedMethodType('f').returnType!; - expect(decoratedType.node.isImmutable, false); - } - - Future test_method_returnType_inferred() async { - await analyze(''' -class B { - int f() => 1; -} -class C extends B { - f/*C*/() => 1; -} -'''); - var decoratedType = decoratedMethodType('f/*C*/').returnType!; - expect(decoratedType.node, TypeMatcher()); - } - - Future test_method_returnType_inferred_dynamic() async { - await analyze(''' -class B { - dynamic f() => 1; -} -class C extends B { - f/*C*/() => 1; -} -'''); - var decoratedType = decoratedMethodType('f/*C*/').returnType!; - expect(decoratedType.node.isImmutable, false); - } - - Future test_parameters() async { - await analyze(''' -void foo({List values}) { - values.where((i) => true); -} -'''); - // No assertions; just checking that it doesn't crash. - } - - Future test_topLevelFunction_parameterType_implicit_dynamic() async { - await analyze(''' -void f(x) {} -'''); - var decoratedType = variables.decoratedElementType( - findNode.simpleFormalParameter('x').declaredElement!); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.type is DynamicType, isTrue); - } - - Future test_topLevelFunction_parameterType_named_no_default() async { - await analyze(''' -void f({String s}) {} -'''); - var decoratedType = decoratedTypeAnnotation('String'); - var functionType = decoratedFunctionType('f'); - expect(functionType.namedParameters!['s'], same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - expect(decoratedType.node, isNot(always)); - expect(functionType.namedParameters!['s']!.node.isPossiblyOptional, true); - } - - Future - test_topLevelFunction_parameterType_named_no_default_required() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -void f({@required String s}) {} -'''); - var decoratedType = decoratedTypeAnnotation('String'); - var functionType = decoratedFunctionType('f'); - expect(functionType.namedParameters!['s'], same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - expect(decoratedType.node, isNot(always)); - expect(functionType.namedParameters!['s']!.node.isPossiblyOptional, false); - } - - Future - test_topLevelFunction_parameterType_named_no_default_required_hint() async { - addMetaPackage(); - await analyze(''' -import 'package:meta/meta.dart'; -void f({/*required*/ String s}) {} -'''); - var decoratedType = decoratedTypeAnnotation('String'); - var functionType = decoratedFunctionType('f'); - expect(functionType.namedParameters!['s'], same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - expect(decoratedType.node, isNot(always)); - expect(functionType.namedParameters!['s']!.node.isPossiblyOptional, false); - expect(variables.getRequiredHint(testSource, findNode.simpleParameter('s')), - isNotNull); - } - - Future test_topLevelFunction_parameterType_named_with_default() async { - await analyze(''' -void f({String s = 'x'}) {} -'''); - var decoratedType = decoratedTypeAnnotation('String'); - var functionType = decoratedFunctionType('f'); - expect(functionType.namedParameters!['s'], same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - expect(functionType.namedParameters!['s']!.node.isPossiblyOptional, false); - } - - Future test_topLevelFunction_parameterType_positionalOptional() async { - await analyze(''' -void f([int i]) {} -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - } - - Future test_topLevelFunction_parameterType_simple() async { - await analyze(''' -void f(int i) {} -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect(decoratedFunctionType('f').positionalParameters![0], - same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - } - - Future test_topLevelFunction_returnType_implicit_dynamic() async { - await analyze(''' -f() {} -'''); - var decoratedType = decoratedFunctionType('f').returnType!; - expect(decoratedType.type is DynamicType, isTrue); - } - - Future test_topLevelFunction_returnType_simple() async { - await analyze(''' -int f() => 0; -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect(decoratedFunctionType('f').returnType, same(decoratedType)); - expect(decoratedType.node, isNotNull); - expect(decoratedType.node, isNot(never)); - } - - Future test_topLevelVariable_type_implicit_dynamic() async { - await analyze(''' -var x; -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node.isImmutable, false); - } - - Future test_topLevelVariable_type_inferred() async { - await analyze(''' -var x = 1; -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node, TypeMatcher()); - } - - Future test_topLevelVariable_type_inferred_dynamic() async { - await analyze(''' -dynamic f() {} -var x = f(); -'''); - var decoratedType = variables.decoratedElementType( - findNode.variableDeclaration('x').declaredElement!); - expect(decoratedType.node.isImmutable, false); - } - - Future test_type_add_non_null_hint() async { - await analyze(''' -void f(int i) {} -'''); - final node = decoratedTypeAnnotation('int').node; - expect(node.hintActions, contains(HintActionKind.addNonNullableHint)); - expect( - node.hintActions[HintActionKind.addNonNullableHint] - .applyTo(super.testCode!), - ''' -void f(int/*!*/ i) {} -'''); - } - - Future test_type_add_null_hint() async { - await analyze(''' -void f(int i) {} -'''); - final node = decoratedTypeAnnotation('int').node; - expect(node.hintActions, contains(HintActionKind.addNullableHint)); - expect( - node.hintActions[HintActionKind.addNullableHint] - .applyTo(super.testCode!), - ''' -void f(int/*?*/ i) {} -'''); - } - - Future test_type_comment_bang() async { - await analyze(''' -void f(int/*!*/ i) {} -'''); - final node = decoratedTypeAnnotation('int').node; - assertEdge(node, never, hard: true, checkable: false); - expect( - node.hintActions, isNot(contains(HintActionKind.addNonNullableHint))); - expect(node.hintActions, isNot(contains(HintActionKind.addNullableHint))); - expect(node.hintActions, - isNot(contains(HintActionKind.changeToNonNullableHint))); - expect( - node.hintActions[HintActionKind.removeNonNullableHint] - .applyTo(super.testCode!), - ''' -void f(int i) {} -'''); - expect( - node.hintActions[HintActionKind.changeToNullableHint] - .applyTo(super.testCode!), - ''' -void f(int/*?*/ i) {} -'''); - } - - Future test_type_comment_question() async { - await analyze(''' -void f(int/*?*/ i) {} -'''); - final node = decoratedTypeAnnotation('int').node; - assertUnion(always, node); - expect( - node.hintActions, isNot(contains(HintActionKind.addNonNullableHint))); - expect(node.hintActions, isNot(contains(HintActionKind.addNullableHint))); - expect( - node.hintActions, isNot(contains(HintActionKind.changeToNullableHint))); - expect( - node.hintActions[HintActionKind.removeNullableHint] - .applyTo(super.testCode!), - ''' -void f(int i) {} -'''); - expect( - node.hintActions[HintActionKind.changeToNonNullableHint] - .applyTo(super.testCode!), - ''' -void f(int/*!*/ i) {} -'''); - } - - Future test_type_nested_add_non_null_hint() async { - await analyze(''' -void f(List i) {} -'''); - final node = decoratedTypeAnnotation('int').node; - expect(node.hintActions, contains(HintActionKind.addNonNullableHint)); - expect( - node.hintActions[HintActionKind.addNonNullableHint] - .applyTo(super.testCode!), - ''' -void f(List i) {} -'''); - } - - Future test_type_nested_add_null_hint() async { - await analyze(''' -void f(List i) {} -'''); - final node = decoratedTypeAnnotation('int').node; - expect(node.hintActions, contains(HintActionKind.addNullableHint)); - expect( - node.hintActions[HintActionKind.addNullableHint] - .applyTo(super.testCode!), - ''' -void f(List i) {} -'''); - } - - Future test_type_parameter_explicit_bound() async { - await analyze(''' -class C {} -'''); - var bound = decoratedTypeParameterBound('T')!; - expect(decoratedTypeAnnotation('Object'), same(bound)); - expect(bound.node, isNot(always)); - expect(bound.type, typeProvider.objectType); - } - - Future test_type_parameter_implicit_bound() async { - // The implicit bound of `T` is automatically `Object?`. TODO(paulberry): - // consider making it possible for type inference to infer an explicit bound - // of `Object`. - await analyze(''' -class C {} -'''); - var bound = decoratedTypeParameterBound('T')!; - assertEdge(always, bound.node, hard: false); - expect(bound.type, same(typeProvider.objectType)); - } - - Future test_typedef_reference_generic_instantiated() async { - await analyze(''' -typedef F = T Function(); -F f; -'''); - // The instantiation of F should produce fresh nullability nodes, distinct - // from the ones in the typedef (they will be unified by the edge builder). - // This is necessary because there is no guarantee of whether the typedef or - // its usage will be visited first. - var typedefDecoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - var decoratedType = decoratedTypeAnnotation('F'); - expect(decoratedType.node, TypeMatcher()); - expect(decoratedType.node, isNot(same(typedefDecoratedType.node))); - _assertType(decoratedType.returnType!.type!, 'int'); - expect( - decoratedType.returnType!.node, TypeMatcher()); - expect(decoratedType.returnType!.node, - isNot(same(typedefDecoratedType.returnType!.node))); - } - - Future test_typedef_reference_generic_uninstantiated() async { - await analyze(''' -typedef F = T Function(); -F f; -'''); - // The instantiation of F should produce fresh nullability nodes, distinct - // from the ones in the typedef (they will be unified by the edge builder). - // This is necessary because there is no guarantee of whether the typedef or - // its usage will be visited first. - var typedefDecoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - var decoratedType = decoratedTypeAnnotation('F f'); - expect(decoratedType.node, TypeMatcher()); - expect(decoratedType.node, isNot(same(typedefDecoratedType.node))); - _assertType(decoratedType.returnType!.type!, 'T'); - expect( - decoratedType.returnType!.node, TypeMatcher()); - expect(decoratedType.returnType!.node, - isNot(same(typedefDecoratedType.returnType!.node))); - var decoratedTypeFormalBound = decoratedTypeParameterBounds - .get((decoratedType.type as FunctionType).typeFormals[0])!; - _assertType(decoratedTypeFormalBound.type!, 'num'); - expect(decoratedTypeFormalBound.node.displayName, - 'bound of type formal T of f (test.dart:2:1)'); - var decoratedTypedefTypeFormalBound = decoratedTypeParameterBounds - .get((typedefDecoratedType.type as FunctionType).typeFormals[0])!; - expect(decoratedTypeFormalBound.node, - isNot(same(decoratedTypedefTypeFormalBound.node))); - } - - Future test_typedef_reference_simple() async { - await analyze(''' -typedef int F(String s); -F f; -'''); - // The instantiation of F should produce fresh nullability nodes, distinct - // from the ones in the typedef (they will be unified by the edge builder). - // This is necessary because there is no guarantee of whether the typedef or - // its usage will be visited first. - var typedefDecoratedType = variables - .decoratedElementType(findElement.typeAlias('F').aliasedElement!); - var decoratedType = decoratedTypeAnnotation('F f'); - expect(decoratedType.node, TypeMatcher()); - expect(decoratedType.node, isNot(same(typedefDecoratedType.node))); - _assertType(decoratedType.returnType!.type!, 'int'); - expect( - decoratedType.returnType!.node, TypeMatcher()); - expect(decoratedType.returnType!.node, - isNot(same(typedefDecoratedType.returnType!.node))); - expect(typedefDecoratedType.returnType!.node.displayName, - 'return type of F (test.dart:1:9)'); - expect(decoratedType.returnType!.node.displayName, - 'return type of f (test.dart:2:1)'); - _assertType(decoratedType.positionalParameters![0].type!, 'String'); - expect(decoratedType.positionalParameters![0].node, - TypeMatcher()); - expect(decoratedType.positionalParameters![0].node, - isNot(same(typedefDecoratedType.positionalParameters![0].node))); - expect(decoratedType.positionalParameters![0].node.displayName, - 'parameter 0 of f (test.dart:2:1)'); - } - - Future test_typedef_reference_simple_named_parameter() async { - await analyze(''' -typedef int F({String s}); -F f; -'''); - // The instantiation of F should produce fresh nullability nodes, distinct - // from the ones in the typedef (they will be unified by the edge builder). - // This is necessary because there is no guarantee of whether the typedef or - // its usage will be visited first. - var decoratedType = decoratedTypeAnnotation('F f'); - expect(decoratedType.namedParameters!['s']!.node.displayName, - 'parameter s of f (test.dart:2:1)'); - } - - Future test_typedef_reference_simple_two_parameters() async { - await analyze(''' -typedef int F(String s, int i); -F f; -'''); - // The instantiation of F should produce fresh nullability nodes, distinct - // from the ones in the typedef (they will be unified by the edge builder). - // This is necessary because there is no guarantee of whether the typedef or - // its usage will be visited first. - var decoratedType = decoratedTypeAnnotation('F f'); - expect(decoratedType.positionalParameters![0].node.displayName, - 'parameter 0 of f (test.dart:2:1)'); - expect(decoratedType.positionalParameters![1].node.displayName, - 'parameter 1 of f (test.dart:2:1)'); - } - - Future test_typedef_rhs_nullability() async { - await analyze(''' -typedef F = void Function(); -'''); - var decorated = decoratedGenericFunctionTypeAnnotation('void Function()'); - expect(decorated.node, same(never)); - } - - Future test_variableDeclaration_late_final_hint_simple() async { - await analyze('/*late final*/ int i;'); - expect( - variables.getLateHint( - testSource, findNode.variableDeclarationList('int i')), - isNotNull); - } - - Future test_variableDeclaration_late_hint_after_metadata() async { - await analyze('@deprecated /*late*/ int i;'); - expect( - variables.getLateHint( - testSource, findNode.variableDeclarationList('int i')), - isNotNull); - } - - Future test_variableDeclaration_late_hint_multiple_comments() async { - await analyze('/*other*/ /*late*/ int i;'); - expect( - variables.getLateHint( - testSource, findNode.variableDeclarationList('int i')), - isNotNull); - } - - Future test_variableDeclaration_late_hint_simple() async { - await analyze('/*late*/ int i;'); - expect( - variables.getLateHint( - testSource, findNode.variableDeclarationList('int i')), - isNotNull); - } - - Future test_variableDeclaration_late_hint_with_spaces() async { - await analyze('/* late */ int i;'); - expect( - variables.getLateHint( - testSource, findNode.variableDeclarationList('int i')), - isNotNull); - } - - Future test_variableDeclaration_type_simple() async { - await analyze(''' -main() { - int i; -} -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect(decoratedType.node, TypeMatcher()); - } - - Future test_variableDeclaration_visit_initializer() async { - await analyze(''' -class C {} -void f(C c) { - var x = c as C; -} -'''); - var decoratedType = decoratedTypeAnnotation('int'); - expect(decoratedType.node, TypeMatcher()); - } - - Future test_void_type() async { - await analyze(''' -void f() {} -'''); - var decoratedType = decoratedTypeAnnotation('void'); - expect(decoratedFunctionType('f').returnType, same(decoratedType)); - assertNoEdge(always, decoratedType.node); - } - - void _assertType(DartType type, String expected) { - var typeStr = type.getDisplayString(withNullability: false); - expect(typeStr, expected); - } -} diff --git a/pkg/nnbd_migration/test/nullability_node_test.dart b/pkg/nnbd_migration/test/nullability_node_test.dart deleted file mode 100644 index 88893b807918..000000000000 --- a/pkg/nnbd_migration/test/nullability_node_test.dart +++ /dev/null @@ -1,772 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/instrumentation.dart'; -import 'package:nnbd_migration/src/edge_origin.dart'; -import 'package:nnbd_migration/src/nullability_node.dart'; -import 'package:nnbd_migration/src/nullability_node_target.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(NullabilityNodeTest); - }); -} - -@reflectiveTest -class NullabilityNodeTest { - final graph = NullabilityGraphForTesting(); - - /// A list of all edges that couldn't be satisfied. May contain duplicates. - late List unsatisfiedEdges; - - List? unsatisfiedSubstitutions; - - NullabilityNode get always => graph.always; - - NullabilityNode get never => graph.never; - - void assertUnsatisfied(List expectedUnsatisfiedEdges) { - expect(unsatisfiedEdges.toSet(), expectedUnsatisfiedEdges.toSet()); - } - - NullabilityEdge connect(NullabilityNode source, NullabilityNode destination, - {bool hard = false, - bool checkable = true, - List guards = const []}) { - return graph.connect(source, destination, _TestEdgeOrigin(), - hard: hard, checkable: checkable, guards: guards); - } - - NullabilityNode lub(NullabilityNode left, NullabilityNode right) { - return NullabilityNode.forLUB(left, right); - } - - NullabilityNode newNode(int id) => - NullabilityNode.forTypeAnnotation(NullabilityNodeTarget.text('node $id')); - - void propagate() { - var propagationResult = graph.propagate(); - unsatisfiedEdges = propagationResult.unsatisfiedEdges; - unsatisfiedSubstitutions = propagationResult.unsatisfiedSubstitutions; - } - - NullabilityNode subst(NullabilityNode? inner, NullabilityNode? outer) { - return NullabilityNode.forSubstitution(inner, outer); - } - - void test_always_and_never_state() { - propagate(); - expect(always.isNullable, isTrue); - expect(never.isNullable, isFalse); - assertUnsatisfied([]); - } - - void test_always_and_never_unaffected_by_hard_edges() { - var edge = connect(always, never, hard: true); - propagate(); - expect(always.isNullable, isTrue); - expect(never.isNullable, isFalse); - assertUnsatisfied([edge]); - } - - void test_always_and_never_unaffected_by_soft_edges() { - var edge = connect(always, never); - propagate(); - expect(always.isNullable, isTrue); - expect(never.isNullable, isFalse); - assertUnsatisfied([edge]); - } - - void test_always_destination() { - // always -> 1 -(hard)-> always - var n1 = newNode(1); - connect(always, n1); - connect(n1, always, hard: true); - propagate(); - // Upstream propagation of non-nullability ignores edges terminating at - // `always`, so n1 should be nullable. - expect(n1.isNullable, true); - assertUnsatisfied([]); - } - - void test_edge_satisfied_due_to_guard() { - // always -> 1 - // 1 -(2) -> 3 - // 3 -(hard)-> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(n1, n3, guards: [n2]); - connect(n3, never, hard: true); - propagate(); - expect(n1.isNullable, true); - expect(n2.isNullable, false); - expect(n3.isNullable, false); - // Although n1 is nullable and n3 is non-nullable, the edge from 1 to 3 is - // considered satisfied because the guard (n2) is non-nullable. - assertUnsatisfied([]); - } - - void test_lubNode_relatesInBothDirections() { - final nodeA = newNode(1); - final nodeB = newNode(2); - final lubNode = lub(nodeA, nodeB); - - expect(nodeA.outerCompoundNodes, [lubNode]); - expect(nodeB.outerCompoundNodes, [lubNode]); - } - - void test_never_source() { - // never -> 1 - var n1 = newNode(1); - connect(never, n1); - propagate(); - // Downstream propagation of nullability ignores edges originating at - // `never`, so n1 should be non-nullable. - expect(n1.isNullable, false); - assertUnsatisfied([]); - } - - void test_propagation_always_union() { - // always == 1 - // 1 -(hard)-> never - // 1 -> 2 - // 1 -> 3 - // 3 -(hard)-> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - union(always, n1); - var edge_1_never = connect(n1, never, hard: true); - connect(n1, n2); - var edge_1_3 = connect(n1, n3); - connect(n3, never, hard: true); - propagate(); - // Union edges take precedence over hard ones, so n1 should be nullable. - expect(n1.isNullable, true); - // And nullability should be propagated to n2. - expect(n2.isNullable, true); - // But it should not be propagated to n3 because non-nullability propagation - // takes precedence over ordinary nullability propagation. - expect(n3.isNullable, false); - assertUnsatisfied([edge_1_never, edge_1_3]); - } - - void test_propagation_always_union_reversed() { - // always == 1 - // 1 -(hard)-> never - // 1 -> 2 - // 1 -> 3 - // 3 -(hard)-> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - union(n1, always); - var edge_1_never = connect(n1, never, hard: true); - connect(n1, n2); - var edge_1_3 = connect(n1, n3); - connect(n3, never, hard: true); - propagate(); - // Union edges take precedence over hard ones, so n1 should be nullable. - expect(n1.isNullable, true); - // And nullability should be propagated to n2. - expect(n2.isNullable, true); - // But it should not be propagated to n3 because non-nullability propagation - // takes precedence over ordinary nullability propagation. - expect(n3.isNullable, false); - assertUnsatisfied([edge_1_never, edge_1_3]); - } - - void test_propagation_downstream_breadth_first() { - // always -> 1 -> 2 - // 1 -> 3 -> 4 - // always -> 5 -> 4 - // 5 -> 6 -> 2 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(4); - var n5 = newNode(5); - var n6 = newNode(6); - connect(always, n1); - connect(n1, n2); - connect(n1, n3); - connect(n3, n4); - connect(always, n5); - connect(n5, n4); - connect(n5, n6); - connect(n6, n2); - propagate(); - // Node 2 should be caused by node 1, since that's the shortest path back to - // "always". Similarly, node 4 should be caused by node 5. - expect(_downstreamCauseNode(n2), same(n1)); - expect(_downstreamCauseNode(n4), same(n5)); - } - - void test_propagation_downstream_guarded_multiple_guards_all_satisfied() { - // always -> 1 - // always -> 2 - // always -> 3 - // 1 -(2,3)-> 4 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(3); - connect(always, n1); - connect(always, n2); - connect(always, n3); - connect(n1, n4, guards: [n2, n3]); - propagate(); - expect(n4.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_guarded_multiple_guards_not_all_satisfied() { - // always -> 1 - // always -> 2 - // 1 -(2,3)-> 4 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(3); - connect(always, n1); - connect(always, n2); - connect(n1, n4, guards: [n2, n3]); - propagate(); - expect(n4.isNullable, false); - assertUnsatisfied([]); - } - - void test_propagation_downstream_guarded_satisfy_guard_first() { - // always -> 1 - // always -> 2 - // 2 -(1)-> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(always, n2); - connect(n2, n3, guards: [n1]); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_guarded_satisfy_source_first() { - // always -> 1 - // always -> 2 - // 1 -(2)-> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(always, n2); - connect(n1, n3, guards: [n2]); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_guarded_unsatisfied_guard() { - // always -> 1 - // 1 -(2)-> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(n1, n3, guards: [n2]); - propagate(); - expect(n3.isNullable, false); - assertUnsatisfied([]); - } - - void test_propagation_downstream_guarded_unsatisfied_source() { - // always -> 1 - // 2 -(1)-> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(n2, n3, guards: [n1]); - propagate(); - expect(n3.isNullable, false); - assertUnsatisfied([]); - } - - void test_propagation_downstream_reverse_substitution_exact() { - // always -> subst(1, 2) - // 3 -(uncheckable)-> 1 - // 4 -(uncheckable)-> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(4); - connect(always, subst(n1, n2)); - connect(n3, n1, checkable: false); - connect(n4, n3, checkable: false); - propagate(); - expect(n1.isNullable, true); - expect(n1.isExactNullable, true); - expect(n2.isNullable, false); - expect(n3.isNullable, true); - expect(n3.isExactNullable, true); - expect(n4.isNullable, true); - expect(n4.isExactNullable, true); - } - - void test_propagation_downstream_reverse_substitution_exact_checkable() { - // always -> subst(1, 2) - // 3 -(uncheckable)-> 1 - // 4 -(checkable)-> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(4); - connect(always, subst(n1, n2)); - connect(n3, n1, checkable: false); - connect(n4, n3, checkable: true); - propagate(); - expect(n1.isNullable, true); - expect(n1.isExactNullable, true); - expect(n2.isNullable, false); - expect(n3.isNullable, true); - expect(n3.isExactNullable, true); - expect(n4.isNullable, false); - expect(n4.isExactNullable, false); - } - - void test_propagation_downstream_reverse_substitution_inner_non_nullable() { - // 1 -> never (hard) - // always -> subst(1, 2) - // 3 -> 2 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(n1, never, hard: true); - connect(always, subst(n1, n2)); - connect(n3, n2); - propagate(); - expect(n1.isNullable, false); - expect(n2.isNullable, true); - expect(n2.isExactNullable, false); - expect(n3.isNullable, false); - } - - void test_propagation_downstream_reverse_substitution_non_null_intent() { - // always -> subst(1, 2) - // 3 -(uncheckable)-> 1 - // 4 -(checkable)-> 3 - // 4 -(hard) -> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(4); - connect(always, subst(n1, n2)); - connect(n3, n1, checkable: false); - connect(n4, n3, checkable: false); - connect(n4, never, hard: true); - propagate(); - expect(n1.isNullable, true); - expect(n1.isExactNullable, true); - expect(n2.isNullable, false); - expect(n3.isNullable, true); - expect(n3.isExactNullable, true); - expect(n4.isNullable, false); - expect(n4.isExactNullable, false); - } - - void - test_propagation_downstream_reverse_substitution_outer_already_nullable() { - // always -> 2 - // always -> subst(1, 2) - // 3 -> 2 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n2); - connect(always, subst(n1, n2)); - connect(n3, n2); - propagate(); - expect(n1.isNullable, false); - expect(n2.isNullable, true); - expect(n2.isExactNullable, false); - expect(n3.isNullable, false); - } - - void test_propagation_downstream_reverse_substitution_unsatisfiable() { - // 1 -> never (hard) - // 2 -> never (hard) - // always -> subst(1, 2) - var n1 = newNode(1); - var n2 = newNode(2); - connect(n1, never, hard: true); - connect(n2, never, hard: true); - var substitutionNode = subst(n1, n2); - connect(always, substitutionNode); - propagate(); - expect(n1.isNullable, false); - expect(n2.isNullable, false); - expect(substitutionNode.isNullable, false); - expect(unsatisfiedSubstitutions, isEmpty); - } - - void test_propagation_downstream_through_lub_both() { - // always -> 1 - // always -> 2 - // LUB(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(always, n2); - connect(lub(n1, n2), n3); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_lub_cascaded() { - // always -> 1 - // LUB(LUB(1, 2), 3) -> 4 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(3); - connect(always, n1); - connect(lub(lub(n1, n2), n3), n4); - propagate(); - expect(n4.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_lub_left() { - // always -> 1 - // LUB(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(lub(n1, n2), n3); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_lub_neither() { - // LUB(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(lub(n1, n2), n3); - propagate(); - expect(n3.isNullable, false); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_lub_right() { - // always -> 2 - // LUB(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n2); - connect(lub(n1, n2), n3); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_substitution_both() { - // always -> 1 - // always -> 2 - // subst(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(always, n2); - connect(subst(n1, n2), n3); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_substitution_cascaded() { - // always -> 1 - // subst(subst(1, 2), 3) -> 4 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(3); - connect(always, n1); - connect(subst(subst(n1, n2), n3), n4); - propagate(); - expect(n4.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_substitution_inner() { - // always -> 1 - // subst(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - connect(subst(n1, n2), n3); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_substitution_neither() { - // subst(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(subst(n1, n2), n3); - propagate(); - expect(n3.isNullable, false); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_substitution_outer() { - // always -> 2 - // subst(1, 2) -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n2); - connect(subst(n1, n2), n3); - propagate(); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_union() { - // always -> 1 - // 1 == 2 - // 2 -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - union(n1, n2); - connect(n2, n3); - propagate(); - expect(n1.isNullable, true); - expect(n2.isNullable, true); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_downstream_through_union_reversed() { - // always -> 1 - // 2 == 1 - // 2 -> 3 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - union(n2, n1); - connect(n2, n3); - propagate(); - expect(n1.isNullable, true); - expect(n2.isNullable, true); - expect(n3.isNullable, true); - assertUnsatisfied([]); - } - - void test_propagation_simple() { - // always -(soft)-> 1 -(soft)-> 2 -(hard) -> 3 -(hard)-> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - connect(always, n1); - var edge_1_2 = connect(n1, n2); - connect(n2, n3, hard: true); - connect(n3, never, hard: true); - propagate(); - expect(n1.isNullable, true); - expect(n2.isNullable, false); - expect(n3.isNullable, false); - assertUnsatisfied([edge_1_2]); - } - - void test_propagation_upstream_breadth_first() { - // never <- 1 <- 2 - // 1 <- 3 <- 4 - // never <- 5 <- 4 - // 5 <- 6 <- 2 - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var n4 = newNode(4); - var n5 = newNode(5); - var n6 = newNode(6); - connect(n1, never, hard: true); - connect(n2, n1, hard: true); - connect(n3, n1, hard: true); - connect(n4, n3, hard: true); - connect(n5, never, hard: true); - connect(n4, n5, hard: true); - connect(n6, n5, hard: true); - connect(n2, n6, hard: true); - propagate(); - // Node 2 should be caused by node 1, since that's the shortest path back to - // "always". Similarly, node 4 should be caused by node 5. - expect(_upstreamCauseNode(n2), same(n1)); - expect(_upstreamCauseNode(n4), same(n5)); - } - - void test_propagation_upstream_through_union() { - // always -> 1 - // always -> 2 - // always -> 3 - // 1 -(hard)-> 2 - // 2 == 3 - // 3 -(hard)-> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var edge_always_1 = connect(always, n1); - var edge_always_2 = connect(always, n2); - var edge_always_3 = connect(always, n3); - connect(n1, n2, hard: true); - union(n2, n3); - connect(n3, never, hard: true); - propagate(); - expect(n1.isNullable, false); - expect(n2.isNullable, false); - expect(n3.isNullable, false); - assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]); - } - - void test_propagation_upstream_through_union_reversed() { - // always -> 1 - // always -> 2 - // always -> 3 - // 1 -(hard)-> 2 - // 3 == 2 - // 3 -(hard)-> never - var n1 = newNode(1); - var n2 = newNode(2); - var n3 = newNode(3); - var edge_always_1 = connect(always, n1); - var edge_always_2 = connect(always, n2); - var edge_always_3 = connect(always, n3); - connect(n1, n2, hard: true); - union(n3, n2); - connect(n3, never, hard: true); - propagate(); - expect(n1.isNullable, false); - expect(n2.isNullable, false); - expect(n3.isNullable, false); - assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]); - } - - void test_satisfied_edge_destination_nullable() { - var n1 = newNode(1); - var edge = connect(always, n1); - propagate(); - assertUnsatisfied([]); - expect(edge.isSatisfied, true); - } - - void test_satisfied_edge_source_non_nullable() { - var n1 = newNode(1); - var n2 = newNode(1); - var edge = connect(n1, n2); - propagate(); - assertUnsatisfied([]); - expect(edge.isSatisfied, true); - } - - void test_satisfied_edge_two_sources_first_non_nullable() { - var n1 = newNode(1); - var n2 = newNode(1); - connect(always, n2); - var edge = connect(n1, never, guards: [n2]); - propagate(); - assertUnsatisfied([]); - expect(edge.isSatisfied, true); - } - - void test_satisfied_edge_two_sources_second_non_nullable() { - var n1 = newNode(1); - var n2 = newNode(1); - connect(always, n1); - var edge = connect(n1, never, guards: [n2]); - propagate(); - assertUnsatisfied([]); - expect(edge.isSatisfied, true); - } - - void test_substitution_simplify_null() { - var n1 = newNode(1); - expect(subst(null, n1), same(n1)); - expect(subst(n1, null), same(n1)); - } - - void test_substitutionNode_relatesInBothDirections() { - final nodeA = newNode(1); - final nodeB = newNode(2); - final substNode = subst(nodeA, nodeB); - - expect(nodeA.outerCompoundNodes, [substNode]); - expect(nodeB.outerCompoundNodes, [substNode]); - } - - void test_unconstrained_node_non_nullable() { - var n1 = newNode(1); - propagate(); - expect(n1.isNullable, false); - assertUnsatisfied([]); - } - - void test_unsatisfied_edge_multiple_sources() { - var n1 = newNode(1); - connect(always, n1); - var edge = connect(always, never, guards: [n1]); - propagate(); - assertUnsatisfied([edge]); - expect(edge.isSatisfied, false); - } - - void test_unsatisfied_edge_single_source() { - var edge = connect(always, never); - propagate(); - assertUnsatisfied([edge]); - expect(edge.isSatisfied, false); - } - - void union(NullabilityNode x, NullabilityNode y) { - graph.union(x, y, _TestEdgeOrigin()); - } - - NullabilityNode? _downstreamCauseNode(NullabilityNode node) => - (node.whyNullable as SimpleDownstreamPropagationStep).edge.sourceNode; - - NullabilityNode _upstreamCauseNode(NullabilityNode node) => - node.whyNotNullable!.principalCause!.node; -} - -class _TestEdgeOrigin implements EdgeOrigin { - @override - CodeReference? get codeReference => null; - - @override - String get description => 'Test edge'; - - @override - bool get isSetupAssignment => false; - - @override - EdgeOriginKind? get kind => null; - - @override - noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); -} diff --git a/pkg/nnbd_migration/test/preview/preview_site_test.dart b/pkg/nnbd_migration/test/preview/preview_site_test.dart deleted file mode 100644 index 01bdcfb556aa..000000000000 --- a/pkg/nnbd_migration/test/preview/preview_site_test.dart +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; -import 'package:analyzer_plugin/protocol/protocol_common.dart' - hide NavigationTarget; -import 'package:nnbd_migration/nnbd_migration.dart'; -import 'package:nnbd_migration/src/front_end/dartfix_listener.dart'; -import 'package:nnbd_migration/src/front_end/migration_info.dart'; -import 'package:nnbd_migration/src/front_end/migration_state.dart'; -import 'package:nnbd_migration/src/front_end/navigation_tree_renderer.dart'; -import 'package:nnbd_migration/src/front_end/offset_mapper.dart'; -import 'package:nnbd_migration/src/front_end/path_mapper.dart'; -import 'package:nnbd_migration/src/front_end/web/navigation_tree.dart'; -import 'package:nnbd_migration/src/preview/preview_site.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import '../front_end/nnbd_migration_test_base.dart'; -import '../utilities/test_logger.dart'; - -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(PreviewSiteTest); - defineReflectiveTests(PreviewSiteWithEngineTest); - }); -} - -@reflectiveTest -class PreviewSiteTest with ResourceProviderMixin, PreviewSiteTestMixin { - @override - Future performEdit(String path, int offset, String replacement) { - final pathUri = Uri.file(path).path; - return site - .performEdit(Uri.parse('localhost://$pathUri?offset=$offset&end=$offset' - '&replacement=${Uri.encodeComponent(replacement)}')); - } - - void setUp() { - dartfixListener = DartFixListener(null, ListenerClient()); - final migrationInfo = - MigrationInfo({}, {}, resourceProvider.pathContext, null); - state = MigrationState( - null, null, dartfixListener, null, {}, (String? path) => true); - state!.pathMapper = PathMapper(resourceProvider); - state!.migrationInfo = migrationInfo; - logger = TestLogger(false /*isVerbose*/); - site = PreviewSite(state, () async { - return state; - }, () {}, logger); - } - - void test_apply_regress41391() async { - final path = convertPath('/test.dart'); - final file = getFile(path); - final analysisOptionsPath = convertPath('/analysis_options.yaml'); - final analysisOptions = getFile(analysisOptionsPath); - analysisOptions.writeAsStringSync('analyzer:'); - final content = 'void main() {}'; - file.writeAsStringSync(content); - site.unitInfoMap[path] = UnitInfo(path)..diskContent = content; - // Add a source change for analysis_options, which has no UnitInfo. - dartfixListener!.addSourceFileEdit( - 'enable experiment', - Location(analysisOptionsPath, 9, 0, 1, 9, endLine: 1, endColumn: 9), - SourceFileEdit(analysisOptionsPath, 0, edits: [ - SourceEdit(9, 0, '\n enable-experiment:\n - non-nullable') - ])); - // This should not crash. - site.performApply([]); - expect(analysisOptions.readAsStringSync(), ''' -analyzer: - enable-experiment: - - non-nullable'''); - expect(state!.hasBeenApplied, true); - } - - void test_applyChangesEmpty() { - final file = getFile('/test.dart'); - file.writeAsStringSync('void main() {}'); - site.performApply([]); - expect(file.readAsStringSync(), 'void main() {}'); - expect(state!.hasBeenApplied, true); - } - - void test_applyChangesTwiceThrows() { - site.performApply([]); - expect(() => site.performApply([]), throwsA(isA())); - } - - void test_applyMigration_sanityCheck_dontApply() async { - final path = convertPath('/test.dart'); - final file = getFile(path); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = '// different content'; - final currentContent = 'void main() {}'; - file.writeAsStringSync(currentContent); - dartfixListener!.addSourceFileEdit( - 'test change', - Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10), - SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')])); - expect(() => site.performApply([]), throwsA(isA())); - expect(file.readAsStringSync(), currentContent); - expect(state!.hasBeenApplied, false); - } - - void test_applyMultipleChanges() { - final path = convertPath('/test.dart'); - final file = getFile(path); - final content = 'void main() {}'; - file.writeAsStringSync(content); - site.unitInfoMap[path] = UnitInfo(path)..diskContent = content; - dartfixListener!.addSourceFileEdit( - 'test change', - Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10), - SourceFileEdit(path, 0, edits: [ - SourceEdit(10, 0, 'List args'), - SourceEdit(13, 0, '\n print(args);\n') - ])); - site.performApply([]); - expect(file.readAsStringSync(), ''' -void main(List args) { - print(args); -}'''); - expect(state!.hasBeenApplied, true); - } - - void test_applySingleChange() { - final path = convertPath('/test.dart'); - final file = getFile(path); - final content = 'void main() {}'; - file.writeAsStringSync(content); - site.unitInfoMap[path] = UnitInfo(path)..diskContent = content; - dartfixListener!.addSourceFileEdit( - 'test change', - Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10), - SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')])); - site.performApply([]); - expect(file.readAsStringSync(), 'void main(List args) {}'); - expect(state!.hasBeenApplied, true); - } - - void test_optOutOfNullSafety_blankLines() { - expect(IncrementalPlan.optCodeOutOfNullSafety(' \n \n'), - equals('// @dart=2.9\n\n \n \n')); - } - - void test_optOutOfNullSafety_blankLines_windows() { - expect(IncrementalPlan.optCodeOutOfNullSafety(' \r\n \r\n'), - equals('// @dart=2.9\r\n\r\n \r\n \r\n')); - } - - void test_optOutOfNullSafety_commentThenCode() { - expect( - IncrementalPlan.optCodeOutOfNullSafety('// comment\n\nvoid main() {}'), - equals('// comment\n\n// @dart=2.9\n\nvoid main() {}')); - } - - void test_optOutOfNullSafety_commentThenCode_windows() { - expect( - IncrementalPlan.optCodeOutOfNullSafety( - '// comment\r\n\r\nvoid main() {}'), - equals('// comment\r\n\r\n// @dart=2.9\r\n\r\nvoid main() {}')); - } - - void test_optOutOfNullSafety_commentThenDirective() { - expect( - IncrementalPlan.optCodeOutOfNullSafety( - '// comment\nimport "dart:core";'), - equals('// comment\n\n// @dart=2.9\n\nimport "dart:core";')); - } - - void test_optOutOfNullSafety_commentThenDirective_multiLine() { - expect( - IncrementalPlan.optCodeOutOfNullSafety( - '// comment\n// comment\nimport "dart:core";'), - equals( - '// comment\n// comment\n\n// @dart=2.9\n\nimport "dart:core";')); - } - - void test_optOutOfNullSafety_commentThenSemicolon() { - expect( - IncrementalPlan.optCodeOutOfNullSafety('// comment\n;\nvoid main() {}'), - equals('// comment\n\n// @dart=2.9\n\n;\nvoid main() {}')); - } - - void test_optOutOfNullSafety_empty() { - expect(IncrementalPlan.optCodeOutOfNullSafety(''), equals('// @dart=2.9')); - } - - void test_optOutOfNullSafety_singleComment_multiLine() { - expect(IncrementalPlan.optCodeOutOfNullSafety('// line 1\n// line 2'), - equals('// line 1\n// line 2\n\n// @dart=2.9\n')); - } - - void test_optOutOfNullSafety_singleComment_multiLine_indented() { - expect(IncrementalPlan.optCodeOutOfNullSafety(' // line 1\n // line 2'), - equals(' // line 1\n // line 2\n\n// @dart=2.9\n')); - } - - void test_optOutOfNullSafety_singleComment_singleLine() { - expect(IncrementalPlan.optCodeOutOfNullSafety('// comment'), - equals('// comment\n\n// @dart=2.9\n')); - } - - void test_optOutOfNullSafety_singleComment_singleLine_trailingNewline() { - expect(IncrementalPlan.optCodeOutOfNullSafety('// comment\n'), - equals('// comment\n\n\n// @dart=2.9\n')); - } - - void test_optOutOfNullSafety_singleLine() { - expect(IncrementalPlan.optCodeOutOfNullSafety('void main() {}'), - equals('// @dart=2.9\n\nvoid main() {}')); - } - - void test_optOutOfNullSafety_singleLine_afterBlankLines() { - expect(IncrementalPlan.optCodeOutOfNullSafety('\n\nvoid main() {}'), - equals('// @dart=2.9\n\n\n\nvoid main() {}')); - } - - void test_optOutOfNullSafety_singleLine_windows() { - expect(IncrementalPlan.optCodeOutOfNullSafety('void main() {}\r\n'), - equals('// @dart=2.9\r\n\r\nvoid main() {}\r\n')); - } - - void test_performEdit() { - final path = convertPath('/test.dart'); - final file = getFile(path); - final content = 'int foo() {}'; - final unitInfo = UnitInfo(path) - ..diskContent = content - ..content = content - ..diskChangesOffsetMapper = OffsetMapper.identity; - site.unitInfoMap[path] = unitInfo; - file.writeAsStringSync(content); - performEdit(path, 3, '/*?*/'); - expect(file.readAsStringSync(), 'int/*?*/ foo() {}'); - expect(state!.hasBeenApplied, false); - expect(state!.needsRerun, true); - expect(unitInfo.content, 'int/*?*/ foo() {}'); - } - - void test_performEdit_sanityCheck_dontApply() { - final path = convertPath('/test.dart'); - final file = getFile(path); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = '// different content'; - final currentContent = 'void main() {}'; - file.writeAsStringSync(currentContent); - expect(() => performEdit(path, 0, 'foo'), throwsA(isA())); - expect(file.readAsStringSync(), currentContent); - expect(state!.hasBeenApplied, false); - } -} - -mixin PreviewSiteTestMixin { - late PreviewSite site; - DartFixListener? dartfixListener; - MigrationState? state; - TestLogger? logger; - - Future performEdit(String path, int offset, String replacement) { - final pathUri = Uri.file(path).path; - return site - .performEdit(Uri.parse('localhost://$pathUri?offset=$offset&end=$offset' - '&replacement=${Uri.encodeComponent(replacement)}')); - } -} - -@reflectiveTest -class PreviewSiteWithEngineTest extends NnbdMigrationTestBase - with PreviewSiteTestMixin { - MigrationInfo? migrationInfo; - - Future setUpMigrationInfo(Map files, - {bool Function(String?)? shouldBeMigratedFunction, - Iterable? pathsToProcess}) async { - shouldBeMigratedFunction ??= (String? path) => true; - pathsToProcess ??= files.keys; - await buildInfoForTestFiles(files, - includedRoot: projectPath, - shouldBeMigratedFunction: shouldBeMigratedFunction, - pathsToProcess: pathsToProcess); - dartfixListener = DartFixListener(null, ListenerClient()); - migrationInfo = - MigrationInfo(infos, {}, resourceProvider.pathContext, projectPath); - state = MigrationState( - null, null, dartfixListener, null, {}, shouldBeMigratedFunction); - nodeMapper = state!.nodeMapper; - state!.pathMapper = PathMapper(resourceProvider); - state!.migrationInfo = migrationInfo; - logger = TestLogger(false /*isVerbose*/); - site = PreviewSite(state, () async { - return state; - }, () {}, logger); - } - - void test_applyHintAction() async { - await setUpMigrationInfo({}); - final path = convertPath('$projectPath/bin/test.dart'); - final file = getFile(path); - final content = r''' -int x; -int y = x; -'''; - file.writeAsStringSync(content); - final migratedContent = ''' -int? x; -int? y = x; -'''; - final unitInfo = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - site.unitInfoMap[path] = unitInfo; - await site.performHintAction( - unitInfo.regions[1].traces[0].entries[0].hintActions[0]); - await site.performHintAction( - unitInfo.regions[1].traces[0].entries[2].hintActions[0]); - expect(file.readAsStringSync(), ''' -int/*?*/ x; -int/*?*/ y = x; -'''); - expect(unitInfo.content, ''' -int/*?*/? x; -int/*?*/? y = x; -'''); - assertRegion( - region: unitInfo.regions[0], offset: unitInfo.content!.indexOf('? x')); - assertRegion( - region: unitInfo.regions[1], offset: unitInfo.content!.indexOf('? y')); - final targets = List.from(unitInfo.targets); - assertInTargets( - targets: targets, - offset: unitInfo.content!.indexOf('x'), - offsetMapper: unitInfo.offsetMapper); - assertInTargets( - targets: targets, - offset: unitInfo.content!.indexOf('y'), - offsetMapper: unitInfo.offsetMapper); - var trace = unitInfo.regions[1].traces[0]; - assertTraceEntry( - unitInfo, - trace.entries[0], - 'y', - unitInfo.content!.indexOf('int/*?*/? y'), - contains('y (test.dart:2:1)')); - assertTraceEntry(unitInfo, trace.entries[1], 'y', - unitInfo.content!.indexOf('= x;') + '= '.length, contains('data flow')); - expect(state!.hasBeenApplied, false); - expect(state!.needsRerun, true); - } - - void test_applyHintAction_removeHint() async { - await setUpMigrationInfo({}); - final path = convertPath('$projectPath/bin/test.dart'); - final file = getFile(path); - final content = r''' -int/*!*/ x; -int y = x; -'''; - file.writeAsStringSync(content); - final migratedContent = ''' -int/*!*/ x; -int y = x; -'''; - final unitInfo = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - site.unitInfoMap[path] = unitInfo; - await site.performHintAction( - unitInfo.regions[0].traces[0].entries[0].hintActions[0]); - expect(file.readAsStringSync(), ''' -int x; -int y = x; -'''); - expect(unitInfo.content, ''' -int x; -int y = x; -'''); - expect(unitInfo.regions, hasLength(1)); - assertRegion( - kind: NullabilityFixKind.typeNotMadeNullable, - region: unitInfo.regions[0], - offset: unitInfo.content!.indexOf(' y')); - final targets = List.from(unitInfo.targets); - assertInTargets( - targets: targets, - offset: unitInfo.content!.indexOf('x'), - offsetMapper: unitInfo.offsetMapper); - expect(state!.hasBeenApplied, false); - expect(state!.needsRerun, true); - } - - void test_applyMigration_doNotOptOutFileNotInPathsToProcess() async { - final pathA = convertPath('$projectPath/lib/a.dart'); - final pathB = convertPath('$projectPath/lib/b.dart'); - final content = 'void main() {}'; - await setUpMigrationInfo({pathA: content, pathB: content}, - // Neither [pathA] nor [[pathB] should be migrated. - shouldBeMigratedFunction: (String? path) => false, - pathsToProcess: [pathA]); - site.unitInfoMap[pathA] = UnitInfo(pathA) - ..diskContent = content - ..wasExplicitlyOptedOut = false - ..migrationStatus = UnitMigrationStatus.optingOut; - site.unitInfoMap[pathB] = UnitInfo(pathB) - ..diskContent = content - ..wasExplicitlyOptedOut = false - ..migrationStatus = UnitMigrationStatus.optingOut; - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - site.performApply(navigationTree); - expect(getFile(pathA).readAsStringSync(), ''' -// @dart=2.9 - -void main() {}'''); - expect(getFile(pathB).readAsStringSync(), 'void main() {}'); - } - - void test_applyMigration_migratePreviouslyOptedOutFile() async { - final path = convertPath('$projectPath/lib/a.dart'); - final content = ''' -// @dart=2.9 - -void main() {}'''; - await setUpMigrationInfo({path: content}); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = content - ..wasExplicitlyOptedOut = true; - dartfixListener!.addSourceFileEdit( - 'remove DLV comment', - Location(path, 0, 14, 1, 1, endLine: 3, endColumn: 1), - SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')])); - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - site.performApply(navigationTree); - expect(getFile(path).readAsStringSync(), 'void main() {}'); - expect(logger!.stdoutBuffer.toString(), contains(''' -Migrated 1 file: - ${convertPath('lib/a.dart')} -''')); - } - - void test_applyMigration_optOutEmptyFile() async { - final path = convertPath('$projectPath/lib/a.dart'); - final content = ''; - await setUpMigrationInfo({path: content}); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = content - ..wasExplicitlyOptedOut = false; - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - var libDir = navigationTree.single as NavigationTreeDirectoryNode; - (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - site.performApply(navigationTree); - expect(getFile(path).readAsStringSync(), '// @dart=2.9'); - expect(logger!.stdoutBuffer.toString(), contains(''' -Opted 1 file out of null safety with a new Dart language version comment: - ${convertPath('lib/a.dart')} -''')); - } - - void test_applyMigration_optOutFileWithEdits() async { - final path = convertPath('$projectPath/lib/a.dart'); - final content = 'void main() {}'; - await setUpMigrationInfo({path: content}); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = content - ..wasExplicitlyOptedOut = false; - dartfixListener!.addSourceFileEdit( - 'test change', - Location(path, 10, 0, 1, 10, endLine: 1, endColumn: 10), - SourceFileEdit(path, 0, edits: [SourceEdit(10, 0, 'List args')])); - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - var libDir = navigationTree.single as NavigationTreeDirectoryNode; - (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - site.performApply(navigationTree); - expect(getFile(path).readAsStringSync(), ''' -// @dart=2.9 - -void main() {}'''); - expect(logger!.stdoutBuffer.toString(), contains(''' -Opted 1 file out of null safety with a new Dart language version comment: - ${convertPath('lib/a.dart')} -''')); - } - - void test_applyMigration_optOutFileWithoutEdits() async { - final path = convertPath('$projectPath/lib/a.dart'); - final content = 'void main() {}'; - await setUpMigrationInfo({path: content}); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = content - ..wasExplicitlyOptedOut = false; - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - var libDir = navigationTree.single as NavigationTreeDirectoryNode; - (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - site.performApply(navigationTree); - expect(getFile(path).readAsStringSync(), ''' -// @dart=2.9 - -void main() {}'''); - expect(logger!.stdoutBuffer.toString(), contains(''' -Opted 1 file out of null safety with a new Dart language version comment: - ${convertPath('lib/a.dart')} -''')); - } - - void test_applyMigration_optOutOne_migrateAnother() async { - final pathA = convertPath('$projectPath/lib/a.dart'); - final pathB = convertPath('$projectPath/lib/b.dart'); - final content = 'void main() {}'; - await setUpMigrationInfo({pathA: content, pathB: content}); - site.unitInfoMap[pathA] = UnitInfo(pathA) - ..diskContent = content - ..wasExplicitlyOptedOut = false; - site.unitInfoMap[pathB] = UnitInfo(pathB) - ..diskContent = content - ..wasExplicitlyOptedOut = false; - dartfixListener!.addSourceFileEdit( - 'test change', - Location(pathA, 10, 0, 1, 10, endLine: 1, endColumn: 10), - SourceFileEdit(pathA, 0, edits: [SourceEdit(10, 0, 'List args')])); - dartfixListener!.addSourceFileEdit( - 'test change', - Location(pathB, 10, 0, 1, 10, endLine: 1, endColumn: 10), - SourceFileEdit(pathB, 0, edits: [SourceEdit(10, 0, 'List args')])); - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - var libDir = navigationTree.single as NavigationTreeDirectoryNode; - (libDir.subtree![0] as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - site.performApply(navigationTree); - expect(getFile(pathA).readAsStringSync(), ''' -// @dart=2.9 - -void main() {}'''); - expect(getFile(pathB).readAsStringSync(), ''' -void main(List args) {}'''); - expect(logger!.stdoutBuffer.toString(), contains(''' -Migrated 1 file: - ${convertPath('lib/b.dart')} -Opted 1 file out of null safety with a new Dart language version comment: - ${convertPath('lib/a.dart')} -''')); - } - - void test_applyMigration_optOutPreviouslyOptedOutFile() async { - final path = convertPath('$projectPath/lib/a.dart'); - final content = ''' -// @dart=2.9 - -int a;'''; - await setUpMigrationInfo({path: content}); - site.unitInfoMap[path] = UnitInfo(path) - ..diskContent = content - ..wasExplicitlyOptedOut = true; - dartfixListener!.addSourceFileEdit( - 'remove DLV comment', - Location(path, 0, 14, 1, 1, endLine: 3, endColumn: 1), - SourceFileEdit(path, 0, edits: [SourceEdit(0, 14, '')])); - var navigationTree = - NavigationTreeRenderer(migrationInfo, state!.pathMapper).render(); - var libDir = navigationTree.single as NavigationTreeDirectoryNode; - (libDir.subtree!.single as NavigationTreeFileNode).migrationStatus = - UnitMigrationStatus.optingOut; - site.performApply(navigationTree); - expect(getFile(path).readAsStringSync(), content); - expect(logger!.stdoutBuffer.toString(), contains(''' -Kept 1 file opted out of null safety: - ${convertPath('lib/a.dart')} -''')); - } - - void test_performEdit_multiple() async { - await setUpMigrationInfo({}); - final path = convertPath('/test.dart'); - final file = getFile(path); - final content = r''' -int x; -int y = x; -'''; - file.writeAsStringSync(content); - final migratedContent = ''' -int? x; -int? y = x; -'''; - final unitInfo = await buildInfoForSingleTestFile(content, - migratedContent: migratedContent); - site.unitInfoMap[path] = unitInfo; - final firstEditOffset = unitInfo.regions[0].edits[0].offset; - performEdit(path, firstEditOffset, '/*?*/'); - final secondEditOffset = unitInfo.regions[1].edits[0].offset; - performEdit(path, secondEditOffset, '/*?*/'); - expect(file.readAsStringSync(), ''' -int/*?*/ x; -int/*?*/ y = x; -'''); - expect(unitInfo.content, ''' -int/*?*/? x; -int/*?*/? y = x; -'''); - assertRegion( - region: unitInfo.regions[0], offset: unitInfo.content!.indexOf('? x')); - assertRegion( - region: unitInfo.regions[1], offset: unitInfo.content!.indexOf('? y')); - final targets = List.from(unitInfo.targets); - assertInTargets( - targets: targets, - offset: unitInfo.content!.indexOf('x'), - offsetMapper: unitInfo.offsetMapper); - assertInTargets( - targets: targets, - offset: unitInfo.content!.indexOf('y'), - offsetMapper: unitInfo.offsetMapper); - var trace = unitInfo.regions[1].traces[0]; - assertTraceEntry( - unitInfo, - trace.entries[0], - 'y', - unitInfo.content!.indexOf('int/*?*/? y'), - contains('y (test.dart:2:1)')); - assertTraceEntry(unitInfo, trace.entries[1], 'y', - unitInfo.content!.indexOf('= x;') + '= '.length, contains('data flow')); - expect(state!.hasBeenApplied, false); - expect(state!.needsRerun, true); - } -} diff --git a/pkg/nnbd_migration/test/preview/test_all.dart b/pkg/nnbd_migration/test/preview/test_all.dart deleted file mode 100644 index e357e6d3d262..000000000000 --- a/pkg/nnbd_migration/test/preview/test_all.dart +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'preview_site_test.dart' as preview_site; - -void main() { - defineReflectiveSuite(() { - preview_site.main(); - }, name: 'preview'); -} diff --git a/pkg/nnbd_migration/test/test_all.dart b/pkg/nnbd_migration/test/test_all.dart deleted file mode 100644 index c2f9b7fcba80..000000000000 --- a/pkg/nnbd_migration/test/test_all.dart +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'already_migrated_code_decorator_test.dart' - as already_migrated_code_decorator_test; -import 'api_test.dart' as api_test; -import 'decorated_class_hierarchy_test.dart' as decorated_class_hierarchy_test; -import 'decorated_type_test.dart' as decorated_type_test; -import 'edge_builder_flow_analysis_test.dart' - as edge_builder_flow_analysis_test; -import 'edge_builder_test.dart' as edge_builder_test; -import 'edit_plan_test.dart' as edit_plan_test; -import 'edit_planner_pass_through_merging_test.dart' - as edit_planner_pass_through_merging_test; -import 'fix_aggregator_test.dart' as fix_aggregator_test; -import 'fix_builder_test.dart' as fix_builder_test; -import 'fix_reason_target_test.dart' as fix_reason_target_test; -import 'front_end/test_all.dart' as front_end; -import 'instrumentation_test.dart' as instrumentation_test; -import 'migration_cli_test.dart' as migration_cli_test; -import 'node_builder_test.dart' as node_builder_test; -import 'nullability_node_test.dart' as nullability_node_test; -import 'preview/test_all.dart' as preview; -import 'utilities/test_all.dart' as utilities; -import 'variables_test.dart' as variables; -import 'verify_tests_test.dart' as verify_tests; - -main() { - defineReflectiveSuite(() { - already_migrated_code_decorator_test.main(); - api_test.main(); - decorated_class_hierarchy_test.main(); - decorated_type_test.main(); - edge_builder_flow_analysis_test.main(); - edge_builder_test.main(); - edit_plan_test.main(); - edit_planner_pass_through_merging_test.main(); - fix_aggregator_test.main(); - fix_builder_test.main(); - fix_reason_target_test.main(); - front_end.main(); - instrumentation_test.main(); - migration_cli_test.main(); - node_builder_test.main(); - nullability_node_test.main(); - preview.main(); - utilities.main(); - variables.main(); - verify_tests.main(); - }); -} diff --git a/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart b/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart deleted file mode 100644 index 18d7c67ca9f1..000000000000 --- a/pkg/nnbd_migration/test/utilities/multi_future_tracker_test.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'dart:async'; - -import 'package:nnbd_migration/src/utilities/multi_future_tracker.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(MultiFutureTrackerTest); - }); -} - -@reflectiveTest -class MultiFutureTrackerTest { - MultiFutureTracker? testTracker; - - tearDown() { - // This will leak futures on certain kinds of test failures. But it is - // a test, so we don't care. - testTracker = null; - } - - Future test_doesNotBlockWithoutLimit() async { - var completer1 = Completer(); - - // Limit is set above the number of futures we are adding. - testTracker = MultiFutureTracker(10); - await testTracker!.addFutureFromClosure(() => completer1.future); - // The second future added should be executing even though the first - // future is not complete. A test failure will time out. - await testTracker!.addFutureFromClosure(() async { - expect(completer1.isCompleted, isFalse); - completer1.complete(); - }); - - return await testTracker!.wait(); - } - - Future test_multiFutureBlocksOnLimit() async { - var completer1 = Completer(); - - testTracker = MultiFutureTracker(1); - await testTracker!.addFutureFromClosure(() => completer1.future); - // The second future added shouldn't be executing until the first - // future is complete. - var secondInQueue = testTracker!.addFutureFromClosure(() async { - expect(completer1.isCompleted, isTrue); - }); - - completer1.complete(); - await secondInQueue; - return await testTracker!.wait(); - } - - Future test_returnsValueFromRun() async { - testTracker = MultiFutureTracker(1); - await expectLater(await testTracker!.runFutureFromClosure(() async => true), - equals(true)); - await expectLater( - await testTracker!.runFutureFromClosure(() => true), equals(true)); - } - - Future test_runsSeriallyAtLowLimit() async { - var completer1 = Completer(); - - testTracker = MultiFutureTracker(1); - var runFuture1 = testTracker!.runFutureFromClosure(() => completer1.future); - var runFuture2 = testTracker!.runFutureFromClosure(() => null); - - // Both futures _should_ timeout. - await expectLater(runFuture1.timeout(Duration(milliseconds: 1)), - throwsA(TypeMatcher())); - await expectLater(runFuture2.timeout(Duration(milliseconds: 1)), - throwsA(TypeMatcher())); - expect(completer1.isCompleted, isFalse); - - completer1.complete(); - - // Now, these should complete normally. - await runFuture1; - await runFuture2; - } -} diff --git a/pkg/nnbd_migration/test/utilities/scoped_set_test.dart b/pkg/nnbd_migration/test/utilities/scoped_set_test.dart deleted file mode 100644 index edaab6b96879..000000000000 --- a/pkg/nnbd_migration/test/utilities/scoped_set_test.dart +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/utilities/scoped_set.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(ScopedSetTest); - }); -} - -@reflectiveTest -class ScopedSetTest { - test_clearEachScope() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.pushScope(copyCurrent: true); - set.clearEachScope(); - expect(set.isInScope(0), false); - set.popScope(); - expect(set.isInScope(0), false); - } - - test_doScoped_actionPerformed() { - final set = ScopedSet(); - bool ran = false; - set.doScoped(action: () { - ran = true; - }); - expect(ran, true); - } - - test_doScoped_actionThrows() { - final set = ScopedSet(); - bool? threw; - try { - set.doScoped(action: () { - set.add(0); - throw ''; - }); - } catch (_) { - threw = true; - } - - expect(threw, true); - expect(set.isInScope(0), false); - } - - test_doScoped_copyCurrent() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.doScoped( - copyCurrent: true, - action: () { - set.add(1); - expect(set.isInScope(0), true); - expect(set.isInScope(1), true); - }); - expect(set.isInScope(0), true); - expect(set.isInScope(1), false); - } - - test_doScoped_elements() { - final set = ScopedSet(); - set.pushScope(); - set.doScoped( - elements: [0, 1], - action: () { - expect(set.isInScope(0), true); - expect(set.isInScope(1), true); - }); - expect(set.isInScope(0), false); - expect(set.isInScope(1), false); - } - - test_doScoped_newScope() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.doScoped(action: () { - set.add(1); - expect(set.isInScope(0), false); - expect(set.isInScope(1), true); - }); - expect(set.isInScope(0), true); - expect(set.isInScope(1), false); - } - - test_initiallyEmpty() { - final set = ScopedSet(); - expect(set.isInScope(0), false); - expect(set.isInScope(1), false); - } - - test_popScope_copyCurrent() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.pushScope(copyCurrent: true); - set.popScope(); - expect(set.isInScope(0), true); - } - - test_popScope_element() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.pushScope(); - set.popScope(); - expect(set.isInScope(0), true); - } - - test_popScope_empty() { - final set = ScopedSet(); - set.pushScope(); - set.pushScope(); - set.add(0); - set.popScope(); - expect(set.isInScope(0), false); - } - - test_pushScope_add() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - expect(set.isInScope(0), true); - } - - test_pushScope_copyCurrent() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.pushScope(copyCurrent: true); - expect(set.isInScope(0), true); - } - - test_pushScope_empty() { - final set = ScopedSet(); - set.pushScope(); - expect(set.isInScope(0), false); - } - - test_pushScope_empty2() { - final set = ScopedSet(); - set.pushScope(); - set.add(0); - set.pushScope(); - expect(set.isInScope(0), false); - } - - test_pushScope_withElements() { - final set = ScopedSet(); - set.pushScope(elements: [0, 1]); - expect(set.isInScope(0), true); - expect(set.isInScope(1), true); - } - - test_removeFromAllScopes() { - final set = ScopedSet(); - set.pushScope(elements: [0, 1]); - set.pushScope(copyCurrent: true); - set.removeFromAllScopes(0); - expect(set.isInScope(0), false); - expect(set.isInScope(1), true); - set.popScope(); - expect(set.isInScope(0), false); - expect(set.isInScope(1), true); - } -} diff --git a/pkg/nnbd_migration/test/utilities/source_edit_diff_formatter_test.dart b/pkg/nnbd_migration/test/utilities/source_edit_diff_formatter_test.dart deleted file mode 100644 index 39cf3142074d..000000000000 --- a/pkg/nnbd_migration/test/utilities/source_edit_diff_formatter_test.dart +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:cli_util/cli_logging.dart'; -import 'package:nnbd_migration/src/edit_plan.dart'; -import 'package:nnbd_migration/src/utilities/source_edit_diff_formatter.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(SourceEditDiffFormatterTest); - }); -} - -@reflectiveTest -class SourceEditDiffFormatterTest { - DiffStyle get compactStyle => DiffStyle.forTesting(true); - - DiffStyle get traditionalStyle => DiffStyle.forTesting(false); - - test_ansi_supported() { - var ansi = Ansi(true); - expect( - DiffStyle(ansi).formatDiff('ab', { - 0: [AtomicEdit.delete(1)], - 2: [AtomicEdit.insert('c')] - }), - [ - 'line 1 • ${ansi.red}${ansi.reversed}a${ansi.none}' - 'b${ansi.green}${ansi.reversed}c${ansi.none}' - ]); - } - - test_ansi_unsupported() { - var ansi = Ansi(false); - expect( - DiffStyle(ansi).formatDiff('ab', { - 0: [AtomicEdit.delete(1)], - 2: [AtomicEdit.insert('c')] - }), - ['line 1 -ab', ' +bc']); - } - - test_compact_consecutiveEdits() { - expect( - compactStyle.formatDiff('a', { - 1: [AtomicEdit.insert('b'), AtomicEdit.insert('c')] - }), - ['line 1 * a{+b+}{+c+}']); - } - - test_compact_deletion() { - expect( - compactStyle.formatDiff('abc', { - 1: [AtomicEdit.delete(1)] - }), - ['line 1 * a{-b-}c']); - } - - test_compact_deletion_ending_in_newline_at_line_start() { - expect( - compactStyle.formatDiff('abc\ndef', { - 0: [AtomicEdit.delete(4)] - }), - ['line 1 * {-abc-}']); - } - - test_compact_deletion_ending_in_newline_not_at_line_start() { - expect( - compactStyle.formatDiff('abc\ndef', { - 1: [AtomicEdit.delete(3)] - }), - ['line 1 * a{-bc-}']); - } - - test_compact_deletion_multiline() { - expect( - compactStyle.formatDiff('abc\ndef', { - 1: [AtomicEdit.delete(5)] - }), - ['line 1 * a{-bc-}', 'line 2 * {-de-}f']); - } - - test_compact_initialText() { - expect( - compactStyle.formatDiff('abc', { - 3: [AtomicEdit.insert('d')] - }), - ['line 1 * abc{+d+}']); - } - - test_compact_insertion() { - expect( - compactStyle.formatDiff('ac', { - 1: [AtomicEdit.insert('b')] - }), - ['line 1 * a{+b+}c']); - } - - test_compact_insertion_ending_in_newline() { - expect( - compactStyle.formatDiff('def', { - 0: [AtomicEdit.insert('abc\n')] - }), - ['line 1 * {+abc+}']); - } - - test_compact_insertion_multiline() { - expect( - compactStyle.formatDiff('af', { - 1: [AtomicEdit.insert('bc\nde')] - }), - ['line 1 * a{+bc+}', ' * {+de+}f']); - } - - test_compact_sort() { - expect( - compactStyle.formatDiff('b', { - 1: [AtomicEdit.insert('c')], - 0: [AtomicEdit.insert('a')] - }), - ['line 1 * {+a+}b{+c+}']); - } - - test_compact_startsWithUnchangedLine() { - expect( - compactStyle.formatDiff('a\nb', { - 3: [AtomicEdit.insert('c')] - }), - ['line 2 * b{+c+}']); - } - - test_compact_trailingTextOnEditedLine_withFinalNewline() { - expect( - compactStyle.formatDiff('ac\n', { - 1: [AtomicEdit.insert('b')] - }), - ['line 1 * a{+b+}c']); - } - - test_compact_trailingTextOnEditedLine_withoutFinalNewline() { - expect( - compactStyle.formatDiff('ac', { - 1: [AtomicEdit.insert('b')] - }), - ['line 1 * a{+b+}c']); - } - - test_traditional_consecutiveEdits() { - expect( - traditionalStyle.formatDiff('a', { - 1: [AtomicEdit.insert('b'), AtomicEdit.insert('c')] - }), - ['line 1 -a', ' +a{+b+}{+c+}']); - } - - test_traditional_deletion() { - expect( - traditionalStyle.formatDiff('abc', { - 1: [AtomicEdit.delete(1)] - }), - ['line 1 -a{-b-}c', ' +ac']); - } - - test_traditional_deletion_ending_in_newline_at_line_start() { - expect( - traditionalStyle.formatDiff('abc\ndef', { - 0: [AtomicEdit.delete(4)] - }), - ['line 1 -{-abc-}']); - } - - test_traditional_deletion_ending_in_newline_not_at_line_start() { - expect( - traditionalStyle.formatDiff('abc\ndef', { - 1: [AtomicEdit.delete(3)] - }), - ['line 1 -a{-bc-}', 'line 2 -def', ' +adef']); - } - - test_traditional_deletion_multiline() { - expect( - traditionalStyle.formatDiff('abc\ndef', { - 1: [AtomicEdit.delete(5)] - }), - ['line 1 -a{-bc-}', 'line 2 -{-de-}f', ' +af']); - } - - test_traditional_initialText() { - expect( - traditionalStyle.formatDiff('abc', { - 3: [AtomicEdit.insert('d')] - }), - ['line 1 -abc', ' +abc{+d+}']); - } - - test_traditional_insertion() { - expect( - traditionalStyle.formatDiff('ac', { - 1: [AtomicEdit.insert('b')] - }), - ['line 1 -ac', ' +a{+b+}c']); - } - - test_traditional_insertion_ending_in_newline() { - expect( - traditionalStyle.formatDiff('def', { - 0: [AtomicEdit.insert('abc\n')] - }), - ['line 1 +{+abc+}']); - } - - test_traditional_insertion_multiline() { - expect( - traditionalStyle.formatDiff('af', { - 1: [AtomicEdit.insert('bc\nde')] - }), - ['line 1 -af', ' +a{+bc+}', ' +{+de+}f']); - } - - test_traditional_sort() { - expect( - traditionalStyle.formatDiff('b', { - 1: [AtomicEdit.insert('c')], - 0: [AtomicEdit.insert('a')] - }), - ['line 1 -b', ' +{+a+}b{+c+}']); - } - - test_traditional_startsWithUnchangedLine() { - expect( - traditionalStyle.formatDiff('a\nb', { - 3: [AtomicEdit.insert('c')] - }), - ['line 2 -b', ' +b{+c+}']); - } - - test_traditional_trailingTextOnEditedLine_withFinalNewline() { - expect( - traditionalStyle.formatDiff('ac\n', { - 1: [AtomicEdit.insert('b')] - }), - ['line 1 -ac', ' +a{+b+}c']); - } - - test_traditional_trailingTextOnEditedLine_withoutFinalNewline() { - expect( - traditionalStyle.formatDiff('ac', { - 1: [AtomicEdit.insert('b')] - }), - ['line 1 -ac', ' +a{+b+}c']); - } -} diff --git a/pkg/nnbd_migration/test/utilities/test_all.dart b/pkg/nnbd_migration/test/utilities/test_all.dart deleted file mode 100644 index dc5758e38edc..000000000000 --- a/pkg/nnbd_migration/test/utilities/test_all.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import 'multi_future_tracker_test.dart' as multi_future_tracker_test; -import 'scoped_set_test.dart' as scoped_set_test; -import 'source_edit_diff_formatter_test.dart' - as source_edit_diff_formatter_test; -import 'where_not_null_transformer_test.dart' - as where_not_null_transformer_test; -import 'where_or_null_transformer_test.dart' as where_or_null_transformer_test; - -main() { - defineReflectiveSuite(() { - multi_future_tracker_test.main(); - scoped_set_test.main(); - source_edit_diff_formatter_test.main(); - where_not_null_transformer_test.main(); - where_or_null_transformer_test.main(); - }); -} diff --git a/pkg/nnbd_migration/test/utilities/test_logger.dart b/pkg/nnbd_migration/test/utilities/test_logger.dart deleted file mode 100644 index 67f73f9aa336..000000000000 --- a/pkg/nnbd_migration/test/utilities/test_logger.dart +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:cli_util/cli_logging.dart'; - -/// TODO(paulberry): move into cli_util -class TestLogger implements Logger { - final stderrBuffer = StringBuffer(); - - final stdoutBuffer = StringBuffer(); - - final bool isVerbose; - - TestLogger(this.isVerbose); - - @override - Ansi get ansi => Ansi(false); - - @override - void flush() {} - - @override - Progress progress(String message) { - return SimpleProgress(this, message); - } - - @override - void stderr(String message) { - stderrBuffer.writeln(message); - } - - @override - void stdout(String message) { - stdoutBuffer.writeln(message); - } - - @override - void trace(String message) { - if (isVerbose) { - stdoutBuffer.writeln(message); - } - } - - @override - void write(String message) { - stdoutBuffer.write(message); - } - - @override - void writeCharCode(int charCode) { - stdoutBuffer.writeCharCode(charCode); - } -} diff --git a/pkg/nnbd_migration/test/utilities/where_not_null_transformer_test.dart b/pkg/nnbd_migration/test/utilities/where_not_null_transformer_test.dart deleted file mode 100644 index a4e9f47fd793..000000000000 --- a/pkg/nnbd_migration/test/utilities/where_not_null_transformer_test.dart +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) 2022, 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. - -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; -import 'package:nnbd_migration/src/utilities/where_not_null_transformer.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import '../abstract_single_unit.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(WhereNotNullTransformerTest); - }); -} - -@reflectiveTest -class WhereNotNullTransformerTest extends AbstractSingleUnitTest { - late WhereNotNullTransformer transformer; - - TypeProvider get typeProvider => testAnalysisResult.typeProvider; - - TypeSystem get typeSystem => testAnalysisResult.typeSystem; - - Future analyze(String code) async { - await resolveTestUnit(code); - transformer = WhereNotNullTransformer(typeProvider, typeSystem); - } - - Future test_match() async { - await analyze(''' -f(List x) => x.where((i) => i != null); -'''); - var methodInvocation = findNode.methodInvocation('.where'); - var transformationInfo = - transformer.tryTransformMethodInvocation(methodInvocation)!; - expect(transformationInfo, isNotNull); - expect(transformationInfo.methodInvocation, same(methodInvocation)); - expect(transformationInfo.argument, - same(findNode.functionExpression('(i) => i != null'))); - expect(transformationInfo.originalName, 'where'); - expect(transformationInfo.replacementName, 'whereNotNull'); - } - - Future test_match_extended() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => i != null); -'''); - var methodInvocation = findNode.methodInvocation('.where'); - var transformationInfo = - transformer.tryTransformMethodInvocation(methodInvocation)!; - expect(transformationInfo, isNotNull); - expect(transformationInfo.methodInvocation, same(methodInvocation)); - expect(transformationInfo.argument, - same(findNode.functionExpression('(i) => i != null'))); - expect(transformationInfo.originalName, 'where'); - expect(transformationInfo.replacementName, 'whereNotNull'); - } - - Future test_match_returns_subtype() async { - await analyze(''' -abstract class C implements Iterable { - List where(bool test(int element)) => null; -} -f(C c) => c.where((i) => i != null); -'''); - var methodInvocation = findNode.methodInvocation('.where'); - var transformationInfo = - transformer.tryTransformMethodInvocation(methodInvocation)!; - expect(transformationInfo, isNotNull); - expect(transformationInfo.methodInvocation, same(methodInvocation)); - expect(transformationInfo.argument, - same(findNode.functionExpression('(i) => i != null'))); - expect(transformationInfo.originalName, 'where'); - expect(transformationInfo.replacementName, 'whereNotNull'); - } - - Future test_mismatch_closure_block_typed() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) { return i != null; }); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_closure_lhs_not_identifier() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => 2*i != null); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_closure_lhs_wrong_element() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => c != null); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_closure_non_binary_expression() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => true); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_closure_wrong_operator() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => i == null); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_extension_method() async { - await analyze(''' -extension on String { - Iterable where(bool test(int element)) => null; -} -f(String s) => s.where((i) => i != null); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_misnamed_method() async { - await analyze(''' -abstract class C implements Iterable { - Iterable fooBar(bool test(int element)) => null; -} -f(C c) => c.fooBar((i) => i != null); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.fooBar')), - isNull); - } - - Future test_mismatch_not_a_subtype_of_iterable() async { - await analyze(''' -abstract class C { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => i != null); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_rhs_not_null() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element)) => null; -} -f(C c) => c.where((i) => i != 0); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } - - Future test_mismatch_too_many_arguments() async { - await analyze(''' -abstract class C implements Iterable { - Iterable where(bool test(int element), {int x}) => null; -} -f(C c) => c.where((i) => i != null, x: 0); -'''); - expect( - transformer - .tryTransformMethodInvocation(findNode.methodInvocation('.where')), - isNull); - } -} diff --git a/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart b/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart deleted file mode 100644 index 748d373da887..000000000000 --- a/pkg/nnbd_migration/test/utilities/where_or_null_transformer_test.dart +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/dart/element/type_provider.dart'; -import 'package:analyzer/dart/element/type_system.dart'; -import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -import '../abstract_single_unit.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(WhereOrNullTransformerTest); - }); -} - -@reflectiveTest -class WhereOrNullTransformerTest extends AbstractSingleUnitTest { - late WhereOrNullTransformer transformer; - - TypeProvider get typeProvider => testAnalysisResult.typeProvider; - - TypeSystem get typeSystem => testAnalysisResult.typeSystem; - - Future analyze(String code) async { - await resolveTestUnit(code); - transformer = WhereOrNullTransformer(typeProvider, typeSystem); - } - - Future test_match() async { - await analyze(''' -f(List x) => x.firstWhere((i) => i.isEven, orElse: () => null); -'''); - var orElseExpression = findNode.functionExpression('() => null'); - var transformationInfo = - transformer.tryTransformOrElseArgument(orElseExpression)!; - expect(transformationInfo, isNotNull); - expect(transformationInfo.methodInvocation, - same(findNode.methodInvocation('firstWhere'))); - expect(transformationInfo.orElseArgument, same(orElseExpression.parent)); - expect(transformationInfo.originalName, 'firstWhere'); - expect(transformationInfo.replacementName, 'firstWhereOrNull'); - } - - Future test_match_extended() async { - await analyze(''' -abstract class C implements Iterable { - int firstWhere(bool test(int element), {int orElse()}) => null; -} -f(C c) => c.firstWhere((i) => i.isEven, orElse: () => null); -'''); - var orElseExpression = findNode.functionExpression('() => null'); - var transformationInfo = - transformer.tryTransformOrElseArgument(orElseExpression)!; - expect(transformationInfo, isNotNull); - expect(transformationInfo.methodInvocation, - same(findNode.methodInvocation('firstWhere(('))); - expect(transformationInfo.orElseArgument, same(orElseExpression.parent)); - expect(transformationInfo.originalName, 'firstWhere'); - expect(transformationInfo.replacementName, 'firstWhereOrNull'); - } - - Future test_mismatch_misnamed_method() async { - await analyze(''' -abstract class C extends Iterable { - int fooBar(bool test(int element), {int orElse()}); -} -f(C c) => c.fooBar((i) => i.isEven, orElse: () => null); -'''); - expect( - transformer.tryTransformOrElseArgument( - findNode.functionExpression('() => null')), - isNull); - } - - Future test_mismatch_orElse_expression() async { - await analyze(''' -f(List x) => x.firstWhere((i) => i.isEven, orElse: () => 0); -'''); - expect( - transformer - .tryTransformOrElseArgument(findNode.functionExpression('() => 0')), - isNull); - } - - Future test_mismatch_orElse_name() async { - await analyze(''' -abstract class C extends Iterable { - @override - int firstWhere(bool test(int element), {int orElse(), int ifSo()}); -} -f(C c) => c.firstWhere((i) => i.isEven, ifSo: () => null); -'''); - expect( - transformer.tryTransformOrElseArgument( - findNode.functionExpression('() => null')), - isNull); - } - - Future test_mismatch_orElse_named_parameter() async { - await analyze(''' -f(List x) => x.firstWhere((i) => i.isEven, orElse: ({int x}) => null); -'''); - expect( - transformer.tryTransformOrElseArgument( - findNode.functionExpression(') => null')), - isNull); - } - - Future test_mismatch_orElse_optional_parameter() async { - await analyze(''' -f(List x) => x.firstWhere((i) => i.isEven, orElse: ([int x]) => null); -'''); - expect( - transformer.tryTransformOrElseArgument( - findNode.functionExpression(') => null')), - isNull); - } - - Future test_mismatch_orElse_presence_of_other_arg() async { - await analyze(''' -abstract class C extends Iterable { - @override - int firstWhere(bool test(int element), {int orElse(), int ifSo()}); -} -f(C c) => c.firstWhere((i) => i.isEven, orElse: () => null, ifSo: () => null); -'''); - expect( - transformer.tryTransformOrElseArgument( - findNode.functionExpression('() => null,')), - isNull); - } - - Future test_mismatch_other_subexpression() async { - await analyze(''' -List f(List x) => x; -g(List x) => f(x).firstWhere((i) => i.isEven, orElse: () => null); -'''); - var xExpression = findNode.simple('x).firstWhere'); - expect(transformer.tryTransformOrElseArgument(xExpression), isNull); - } - - Future test_mismatch_unrelated_type() async { - await analyze(''' -abstract class C { - int firstWhere(bool test(int element), {int orElse()}); -} -f(C c) => c.firstWhere((i) => i.isEven, orElse: () => null); -'''); - expect( - transformer.tryTransformOrElseArgument( - findNode.functionExpression('() => null')), - isNull); - } -} diff --git a/pkg/nnbd_migration/test/variables_test.dart b/pkg/nnbd_migration/test/variables_test.dart deleted file mode 100644 index fd1e5103f729..000000000000 --- a/pkg/nnbd_migration/test/variables_test.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2019, 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. - -import 'package:nnbd_migration/src/variables.dart'; -import 'package:test/test.dart'; -import 'package:test_reflective_loader/test_reflective_loader.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(_UniqueIdentifierForSpanTest); - }); -} - -@reflectiveTest -class _UniqueIdentifierForSpanTest { - void test_inverse() { - const maxEnd = 1000; - for (int offset = 0; offset <= maxEnd; offset++) { - for (int end = offset; end <= maxEnd; end++) { - var uniqueId = Variables.uniqueIdentifierForSpan(offset, end); - var decoded = Variables.spanForUniqueIdentifier(uniqueId); - expect(decoded.offset, offset); - expect(decoded.end, end); - } - } - } - - void test_uniqueness() { - const maxEnd = 1000; - const maxExpectedId = maxEnd * maxEnd; - var idsSeen = {}; - for (int offset = 0; offset <= maxEnd; offset++) { - for (int end = offset; end <= maxEnd; end++) { - var pairDescription = '($offset, $end)'; - var uniqueId = Variables.uniqueIdentifierForSpan(offset, end); - expect(uniqueId, lessThanOrEqualTo(maxExpectedId)); - var previousUseOfThisId = idsSeen[uniqueId]; - expect(previousUseOfThisId, isNull, - reason: - '$pairDescription maps to $uniqueId, which was previously used ' - 'by $previousUseOfThisId'); - idsSeen[uniqueId] = pairDescription; - } - } - } -} diff --git a/pkg/nnbd_migration/test/verify_tests_test.dart b/pkg/nnbd_migration/test/verify_tests_test.dart deleted file mode 100644 index cd08ec6b88ea..000000000000 --- a/pkg/nnbd_migration/test/verify_tests_test.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:analyzer/file_system/physical_file_system.dart'; -import 'package:analyzer_utilities/package_root.dart' as package_root; -import 'package:analyzer_utilities/verify_tests.dart'; - -void main() { - var provider = PhysicalResourceProvider.INSTANCE; - var packageRoot = provider.pathContext.normalize(package_root.packageRoot); - var pathToAnalyze = provider.pathContext.join(packageRoot, 'nnbd_migration'); - var testDirPath = provider.pathContext.join(pathToAnalyze, 'test'); - VerifyTests(testDirPath).build(); -} diff --git a/pkg/nnbd_migration/tool/codegen/assemble_resources.dart b/pkg/nnbd_migration/tool/codegen/assemble_resources.dart deleted file mode 100644 index 27fa5dcb7eab..000000000000 --- a/pkg/nnbd_migration/tool/codegen/assemble_resources.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2022, 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. - -// This script provides an alternative way of creating the -// lib/src/front_end/resources/resources.g.dart file, based on a set of provided -// files that has already been suitably compiled. - -import 'dart:io'; - -import 'package:args/args.dart'; - -import 'generate_resources.dart'; - -main(List args) { - var argParser = ArgParser() - ..addOption('output', abbr: 'o', help: 'Output to FILE', valueHelp: 'FILE') - ..addFlag('help', abbr: 'h', negatable: false, help: 'Show help'); - var parsedArgs = argParser.parse(args); - if (parsedArgs['help'] as bool) { - print('Usage: dart assemble_resources.dart INPUT_FILES'); - print(''); - print(argParser.usage); - exit(1); - } - var content = - generateResourceFile([for (var arg in parsedArgs.rest) File(arg)]); - var output = parsedArgs['output'] as String?; - if (output == null) { - stdout.write(content); - } else { - File(output).writeAsStringSync(content); - } -} diff --git a/pkg/nnbd_migration/tool/codegen/check_generated_test.dart b/pkg/nnbd_migration/tool/codegen/check_generated_test.dart deleted file mode 100644 index 9a251ab8be33..000000000000 --- a/pkg/nnbd_migration/tool/codegen/check_generated_test.dart +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2020, 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. - -import 'package:test/test.dart'; - -import 'generate_resources.dart' as generate_resources; - -/// Validate that the -/// pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart -/// file was regenerated after changing upstream dependencies. -void main() async { - test('description', () { - generate_resources.verifyResourcesGDartGenerated(failVerification: fail); - }); -} diff --git a/pkg/nnbd_migration/tool/codegen/extract_resource.dart b/pkg/nnbd_migration/tool/codegen/extract_resource.dart deleted file mode 100644 index 486fbc4aa363..000000000000 --- a/pkg/nnbd_migration/tool/codegen/extract_resource.dart +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2021, 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. - -// This script provides a quick way, via the command line, to extract one of the -// resources encoded in the file lib/src/front_end/resources/resources.g.dart. - -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:analyzer/dart/analysis/utilities.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:args/args.dart'; -import 'package:path/path.dart' as path; - -main(List args) { - var argResults = argParser.parse(args); - if (argResults['help'] as bool) { - fail(null, showUsage: true); - } - if (argResults.rest.isNotEmpty) { - fail('Unexpected extra arguments', showUsage: true); - } - bool list = argResults['list'] as bool; - String? path = argResults['path'] as String?; - String? resource = argResults['resource'] as String?; - if (list && resource != null) { - fail('Only one of --resource and --list may be provided'); - } else if (!list && resource == null) { - fail('Either --resource or --list must be provided'); - } - var file = locateResourcesFile(path); - var parseResult = - parseString(content: file.readAsStringSync(), path: file.path); - final variableNameRegExp = RegExp(r'^_(.*)_base64$'); - for (var declaration in parseResult.unit.declarations) { - if (declaration is TopLevelVariableDeclaration) { - for (var variable in declaration.variables.variables) { - if (variable.initializer == null) continue; - var match = variableNameRegExp.matchAsPrefix(variable.name.lexeme); - if (match == null) continue; - var shortName = match.group(1); - if (list) { - stdout.writeln(shortName); - } else if (resource == shortName) { - stdout.add(decodeVariableDeclaration(variable)); - return; - } - } - } - } - if (list) { - return; - } else { - fail('Resource $resource not found in ${file.path}'); - } -} - -final argParser = ArgParser() - ..addOption('resource', - abbr: 'r', valueHelp: 'RESOURCE', help: 'Extract resource RESOURCE') - ..addFlag('list', - negatable: false, abbr: 'l', help: 'List which resources are present') - ..addOption('path', - abbr: 'p', - valueHelp: 'PATH', - help: - 'Search for resources.g.dart inside PATH rather than current working ' - 'directory') - ..addFlag('help', negatable: false, abbr: 'h'); - -Uint8List decodeVariableDeclaration(VariableDeclaration variable) { - var initializer = variable.initializer as StringLiteral; - var stringValue = initializer.stringValue!; - return base64.decode(stringValue.replaceAll('\n', '').trim()); -} - -void fail(String? message, {bool showUsage = false}) { - if (message != null) { - stderr.writeln(message); - } - if (showUsage) { - stderr.writeln(''' -usage: dart pkg/nnbd_migration/tool/codegen/extract_resource.dart -r - -Reads the file `resources.g.dart` from the appropriate directory, extracts the -embedded resource named ``, and prints it to standard out. -'''); - stderr.writeln(argParser.usage); - } - exit(1); -} - -/// Tries to guess the location of `resources.g.dart` using optional [pathHint] -/// as a starting point. -File locateResourcesFile(String? pathHint) { - pathHint ??= '.'; - final pathParts = - 'pkg/nnbd_migration/lib/src/front_end/resources/resources.g.dart' - .split('/'); - var currentPath = path.normalize(pathHint); - while (true) { - for (int i = 0; i <= pathParts.length; i++) { - var pathToTry = path.normalize(path.join( - currentPath, path.joinAll(pathParts.sublist(pathParts.length - i)))); - var file = File(pathToTry); - var type = file.statSync().type; - if (type == FileSystemEntityType.notFound && i == 0) { - fail('No such file or directory: $pathToTry'); - } - if (type == FileSystemEntityType.link) { - type = File(file.resolveSymbolicLinksSync()).statSync().type; - } - if (type == FileSystemEntityType.file) { - return file; - } - } - if (currentPath == '.') { - currentPath = Directory.current.path; - } - var nextPath = path.dirname(currentPath); - if (nextPath == currentPath) { - fail('Could not find file `resources.g.dart` starting at $pathHint'); - } - currentPath = nextPath; - } -} diff --git a/pkg/nnbd_migration/tool/codegen/generate_resources.dart b/pkg/nnbd_migration/tool/codegen/generate_resources.dart deleted file mode 100644 index 7caf5f44115c..000000000000 --- a/pkg/nnbd_migration/tool/codegen/generate_resources.dart +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) 2020, 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. - -// This script generates the -// lib/src/front_end/resources/resources.g.dart file from the contents -// of the lib/src/front_end/resources directory. - -import 'dart:convert'; -import 'dart:io'; -import 'dart:math' as math; - -import 'package:args/args.dart'; -import 'package:crypto/crypto.dart'; -import 'package:path/path.dart' as path; - -void main(List args) async { - var argParser = ArgParser() - ..addFlag('verify', negatable: false) - ..addFlag('dev', negatable: false) - ..addOption('dart_path') - ..addFlag('help', negatable: false); - var argResults = argParser.parse(args); - if (argResults['help'] == true) { - fail(''' -usage: dart pkg/nnbd_migration/tool/codegen/generate_resources.dart [--verify] - -Run with no args to generate web resources for the NNBD migration preview tool. -Run with '--verify' to validate that the web resource have been regenerated. -'''); - } - - if (FileSystemEntity.isFileSync( - path.join('tool', 'codegen', 'generate_resources.dart'))) { - // We're running from the project root - cd up two directories. - Directory.current = Directory.current.parent.parent; - } else if (!FileSystemEntity.isDirectorySync( - path.join('pkg', 'nnbd_migration'))) { - fail('Please run this tool from the root of the sdk repo.'); - } - - bool verify = argResults['verify'] as bool; - bool? dev = argResults['dev'] as bool?; - - if (verify) { - verifyResourcesGDartGenerated(); - } else { - await compileWebFrontEnd(devMode: dev!, dartPath: dartPath(argResults)!); - - print(''); - - createResourcesGDart(); - } -} - -final File dartSources = File(path.join('pkg', 'nnbd_migration', 'lib', 'src', - 'front_end', 'web', 'migration.dart')); - -final javascriptOutput = File(path.join('pkg', 'nnbd_migration', 'lib', 'src', - 'front_end', 'resources', 'migration.js')); - -final Directory resourceDir = Directory( - path.join('pkg', 'nnbd_migration', 'lib', 'src', 'front_end', 'resources')); - -final File resourcesFile = File(path.join('pkg', 'nnbd_migration', 'lib', 'src', - 'front_end', 'resources', 'resources.g.dart')); - -final List resourceTypes = [ - '.css', - '.html', - '.js', - '.png', - '.ttf', -]; - -String base64Encode(List bytes) { - var encoded = base64.encode(bytes); - - // Logic to cut lines into 80-character chunks. - var lines = []; - var index = 0; - - while (index < encoded.length) { - var line = encoded.substring(index, math.min(index + 80, encoded.length)); - lines.add(line); - index += line.length; - } - - return lines.join('\n'); -} - -Future compileWebFrontEnd( - {required bool devMode, required String dartPath}) async { - // dart compile js -m -o output source - var process = await Process.start(dartPath, [ - 'compile', - 'js', - devMode ? '-O1' : '-m', - '--no-frequency-based-minification', - '-o', - javascriptOutput.path, - dartSources.path, - ]); - process.stdout.listen((List data) => stdout.add(data)); - process.stderr.listen((List data) => stderr.add(data)); - var exitCode = await process.exitCode; - - if (exitCode != 0) { - fail('Failed compiling ${dartSources.path}.'); - } -} - -void createResourcesGDart() { - var content = generateResourceFile( - sortDir(resourceDir.listSync()).where((entity) { - var name = path.basename(entity.path); - return entity is File && resourceTypes.contains(path.extension(name)); - }).cast(), - sourcesMd5: _computeSourcesMd5()); - - // write the content - resourcesFile.writeAsStringSync(content); -} - -/// Returns the dartPath, either from [argResults] or the Platform. -String? dartPath(ArgResults argResults) { - if (argResults.wasParsed('dart_path')) { - return argResults['dart_path'] as String?; - } else { - return Platform.resolvedExecutable; - } -} - -void fail(String message) { - stderr.writeln(message); - exit(1); -} - -/// Fail the script, and print out a message indicating how to regenerate the -/// resources file. -void failGenerate(String message) { - stderr.writeln('$message.'); - stderr.writeln(); - stderr.writeln(''' -To re-generate lib/src/front_end/resources/resources.g.dart, run: - - dart pkg/nnbd_migration/tool/codegen/generate_resources.dart -'''); - exit(1); -} - -String generateResourceFile(Iterable resources, {String? sourcesMd5}) { - var filePath = path.relative(Platform.script.toFilePath()); - var buf = StringBuffer(''' -// Copyright (c) 2020, 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. - -// This file is generated; don't edit it directly. -// -// See $filePath for how -// to edit the source content and for re-generation instructions. - -import 'dart:convert' as convert; -'''); - - for (var resource in resources) { - var name = path.basename(resource.path).replaceAll('.', '_'); - print('adding $name...'); - - buf.writeln(); - buf.writeln('String get $name {'); - buf.writeln(' return _$name ??= _decode(_${name}_base64);'); - buf.writeln('}'); - } - - buf.writeln(r''' - -String _decode(String data) { - data = data.replaceAll('\n', '').trim(); - return String.fromCharCodes(convert.base64Decode(data)); -}'''); - - for (var resource in resources) { - var name = path.basename(resource.path).replaceAll('.', '_'); - - String source; - var extension = path.extension(resource.path); - if (extension == '.png' || extension == '.ttf') { - source = resource.readAsStringSync(encoding: latin1); - } else { - source = resource.readAsStringSync(); - } - - var delimiter = "'''"; - - buf.writeln(); - buf.writeln('String? _$name;'); - if (sourcesMd5 != null && - name == path.basename(javascriptOutput.path).replaceAll('.', '_')) { - // Write out the crc for the dart code. - buf.writeln("// migration_dart md5 is '$sourcesMd5'"); - } else { - // highlight_css md5 is 'fb012626bafd286510d32da815dae448' - buf.writeln("// $name md5 is '${md5String(source)}'"); - } - buf.writeln('String _${name}_base64 = $delimiter'); - buf.writeln(base64Encode(source.codeUnits)); - buf.writeln('$delimiter;'); - } - - return buf.toString(); -} - -String md5String(String str) { - return md5.convert(str.codeUnits).toString(); -} - -List sortDir(Iterable entities) { - var result = entities.toList(); - result.sort((a, b) => a.path.compareTo(b.path)); - return result; -} - -void verifyResourcesGDartGenerated({ - VerificationFunction failVerification = failGenerate, -}) { - print('Verifying that ${path.basename(resourcesFile.path)} is up-to-date...'); - - // Find the hashes for the last generated version of resources.g.dart. - var resourceHashes = {}; - // highlight_css md5 is 'fb012626bafd286510d32da815dae448' - var hashPattern = RegExp(r"// (\S+) md5 is '(\S+)'"); - for (var match in hashPattern.allMatches(resourcesFile.readAsStringSync())) { - resourceHashes[match.group(1)] = match.group(2); - } - - // For all resources (modulo compiled JS ones), verify the hash. - for (var entity in sortDir(resourceDir.listSync())) { - var name = path.basename(entity.path); - if (!resourceTypes.contains(path.extension(name))) { - continue; - } - - if (name == 'migration.js' || - name == 'dart_192.png' || - path.extension(name) == '.ttf') { - // skip the compiled js and logo - continue; - } - - var key = name.replaceAll('.', '_'); - if (!resourceHashes.containsKey(key)) { - failVerification('No entry on resources.g.dart for $name'); - } else { - var hash = md5String((entity as File).readAsStringSync()); - if (hash != resourceHashes[key]) { - failVerification('$name not up to date in resources.g.dart'); - } - } - } - - // verify the compiled dart code - String hash = _computeSourcesMd5(); - if (hash != resourceHashes['migration_dart']) { - failVerification('Compiled javascript not up to date in resources.g.dart'); - } - - print('Generated resources up to date.'); -} - -String _computeSourcesMd5() { - var sourceCode = StringBuffer(); - // collect the dart source code - for (var entity in sortDir(dartSources.parent.listSync())) { - if (entity.path.endsWith('.dart')) { - sourceCode.write((entity as File).readAsStringSync()); - } - } - var sourcesMd5 = md5String(sourceCode.toString()); - return sourcesMd5; -} - -typedef VerificationFunction = void Function(String); diff --git a/pkg/nnbd_migration/tool/steamroll_ecosystem.sh b/pkg/nnbd_migration/tool/steamroll_ecosystem.sh deleted file mode 100755 index 0c6374b0bb3c..000000000000 --- a/pkg/nnbd_migration/tool/steamroll_ecosystem.sh +++ /dev/null @@ -1,450 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2020, 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. -# - -set -ex - -# -# Build a workspace component from a package. -# -# $1: the name of the package -# $2: the name of the repository (same as package if missing) -# $3: branch to clone from (master if missing). Do not select multiple -# different branches for the same repo, that -# will not work and is not enforced. -# $4: subdirectory of the repository applicable to the package (root if missing) -function make_clone_from_package { - local package_name="$1" - local repo_name="$2" - local branch="$3" - local subdir="$4" - - [ -z "${repo_name}" ] && repo_name=${package_name} - [ -z "${branch}" ] && branch=master - [ -z "${subdir}" ] && subdir=. - - add_repo_to_workspace "${repo_name}" "${branch}" - ln -sfn "_repos/${repo_name}/${subdir}" "${package_name}" - - grep -v "^${package_name}:file://" .packages >> .packages.new || true - echo "${package_name}:file://$(pwd)/$(readlink ${package_name})/lib" \ - >> .packages.new - mv .packages.new .packages -} - -# HACK ALERT: no associative arrays in bash 3 and eval is a bad idea. Use -# files instead. :-( -repos_changed_this_run="`mktemp`" -done_this_run="`mktemp`" -trap _cleanup EXIT -function _cleanup { - rm -f "${done_this_run}" "${repos_changed_this_run}" -} - -# Returns true if we have already processed this repository or package. -# -# Repository names are prefixed with "_repos/", package names are plain. -# -# $1: repository or package name -function already_done_this_run { - grep -q "^$1$" "${done_this_run}" - return $? -} - -# Adds the parameter to the run log file. -# -# $1: package name, or repository name prefixed with '_repos/'. -function log_this_run { - echo "$1" >> "${done_this_run}" -} - -# Returns true if when pulling this repository it changed. -# -# Unlike `already_done_this_run`, repository names are plain. -# -# $1: repository name -function repo_changed_this_run { - grep -q "^$1$" "${repos_changed_this_run}" - return $? -} - -# Adds the parameter to the repository changed log. -# -# $1: repository name -function log_repo_changed_this_run { - echo "$1" >> "${repos_changed_this_run}" -} - - -function _pull_into_repo { - local branch="$1" - local revision="$2" - local sparse_param - - if [ "${branch}" == "master" ] ; then - sparse_param=--depth=1 - fi - - [ -z "${revision}" ] && revision="${branch}" - - git pull ${sparse_param} --rebase originHTTP "${revision}" -} - -# -# Add a git repository to the workspace. -# -# $1: the name of the repository -# $2: branch to clone from -# $3: revision to check out, or leave at HEAD if empty. We can't use sparse -# clones in this case. -function add_repo_to_workspace { - local repo_name="$1" - local branch="$2" - local revision - local clone - - local curr_head - local prev_head - - if already_done_this_run "_repos/${repo_name}" ; then return 0 ; fi - log_this_run "_repos/${repo_name}" - - case "${repo_name}" in - archive) clone="git@github.com:brendan-duncan/${repo_name}.git" ;; - build_verify) clone="git@github.com:kevmoo/${repo_name}.git" ;; - build_version) clone="git@github.com:kevmoo/${repo_name}.git" ;; - csv) clone="git@github.com:close2/csv.git" ;; - git) clone="git@github.com:kevmoo/${repo_name}.git" ;; - node-interop) clone="git@github.com:pulyaevskiy/node-interop.git" ;; - node_preamble) clone="git@github.com:mbullington/${repo_name}.dart.git" ;; - package_config) - clone="git@github.com:dart-lang/${repo_name}.git" - revision=1.1.0 - ;; - source_gen_test) clone="git@github.com:kevmoo/${repo_name}.git" ;; - quiver-dart) clone="git@github.com:google/${repo_name}.git" ;; - uuid) clone="git@github.com:Daegalus/dart-uuid.git" ;; - *.dart) clone="git@github.com:google/${repo_name}" ;; - *) - clone="git@github.com:dart-lang/${repo_name}.git" - echo "WARNING: using default for ${repo_name}, this might not work" - ;; - esac - - if [ -d "_repos/${repo_name}" ] ; then - pushd "_repos/${repo_name}" - prev_head="$(git rev-parse HEAD)" - ${NO_UPDATE} _pull_into_repo "${branch}" "${revision}" - curr_head="$(git rev-parse HEAD)" - if [ "${prev_head}" != "${curr_head}" ] ; then - log_repo_changed_this_run "${repo_name}" - fi - git checkout -b "${branch}" - popd - return $? - fi - - mkdir -p _repos - pushd "_repos" - git init "${repo_name}" - pushd "${repo_name}" - git remote add "origin" -t "${branch}" "${clone}" - # Use HTTP for pulls to reduce authentication overhead and excessive pressing - # of authentication tokens. This means we might be prompted for - # username/password if a package does not exist, but that's - # the tradeoff. - git remote add "originHTTP" -t "${branch}" \ - $(echo "${clone}" | sed s,^git@github.com:,https://github.com/,g) - git config core.sparsecheckout true - echo "**" >> .git/info/sparse-checkout - echo "!**/.packages" >> .git/info/sparse-checkout - echo "!**/pubspec.lock" >> .git/info/sparse-checkout - echo "!**/.dart_tool/package_config.json" >> .git/info/sparse-checkout - # TODO(jcollins-g): Usual bag of tricks does not work to stop git pull from - # prompting and force it to hard fail if authentication is required. Why? - if ! _pull_into_repo ${branch} ${revision} ; then - echo error cloning: "${clone}". Cleaning up - popd - # We remove the repository here so that if you're iteratively adding new - # repository configurations, a rerun of the script will re-initialize the - # misconfigured repository. - rm -rf "${repo_name}" - popd - return 2 - fi - log_repo_changed_this_run "${repo_name}" - popd - popd -} - - -# Tries pub get multiple times since it occasionally fails. -# Returns false if we ran out of retries. -function pub_get_with_retries { - for try in 1 2 3 4 5 ; do - if pub get --no-precompile ; then return 0 ; fi - sleep $[$try * $try] - done - return 1 -} - -# -# Puts to stdout a list of the package names for the dependencies of a package. -# -# Uses pub (super slow). -# -# $1: directory name -function generate_package_names_with_pub { - local directory_name="$1" - if [ -n "${ONE_PUB_ONLY}" ] && [ "${ONE_PUB_ONLY}" != "$1" ] ; then - return 0 - fi - [ -z "${directory_name}" ] && directory_name=. - pushd "${directory_name}" >/dev/null - pub_get_with_retries >/dev/null - popd >/dev/null - # HACK ALERT: assumes '.pub-cache' is in the path of the real pub cache. - # Packages referring to other packages via path in pubspec.yaml are not - # supported at all and we do not include dependencies derived that way - # exclusively. - grep -v '^#' "${directory_name}/.packages" | grep '.pub-cache' | cut -f 1 -d : -} - -# Returns true if we can use yq, or false if there is a path package dependency. -function _can_use_yq { - for k in dependencies dev_dependencies ; do - if yq r "$1/pubspec.yaml" "${k}.*.path" | egrep -q -v '^(- null|null)$' ; then - return 1 - fi - done - return 0 -} - -# Prints package and version number, one per line, to stdout. -function _generate_yq_helper { - yq r "$1/pubspec.yaml" dependencies | egrep '^\w+:' | sed 's/:/ /' - yq r "$1/pubspec.yaml" dev_dependencies | egrep '^\w+:' | sed 's/:/ /' -} - -# -# Puts to stdout a list of the package names for the dependencies of a package. -# -# Parses yaml (fast), but requires the 'yq' program. -# -# $1: directory name -function generate_package_names_with_yq { - local directory_name="$1" - local package - local version - if _can_use_yq "${directory_name}" ; then - _generate_yq_helper "${directory_name}" | while read package version ; do - if [ ! -z "${version}" ] ; then - echo "${package}" - else - echo "assert: should have a version number or we should have used pub" >&2 - return 1 - fi - done - else - generate_package_names_with_pub "${directory_name}" - fi -} - -# -# Add a package, recursively, to the workspace. A package may add its own -# repository, or use an existing one. -# -# $1: the package name. Must be one of the packages we know the location of. -# -function add_package_to_workspace { - local package_name="$1" - - if already_done_this_run "${package_name}" ; then return 0 ; fi - log_this_run "${package_name}" - - if [ -d "${package_name}" ] ; then { - pushd "${package_name}" - prev_head="$(git rev-parse HEAD)" - popd - } ; fi - - local repo - - case "${package_name}" in - _fe_analyzer_shared) repo=sdk - make_clone_from_package _fe_analyzer_shared "${repo}" master pkg/_fe_analyzer_shared ;; - analyzer_utilities) repo=sdk - make_clone_from_package analyzer_utilities "${repo}" master pkg/analyzer_utilities ;; - analyzer) repo=sdk - make_clone_from_package analyzer "${repo}" master pkg/analyzer ;; - build) repo=build - make_clone_from_package build "${repo}" master build ;; - build_config) repo=build - make_clone_from_package build_config "${repo}" master build_config ;; - build_daemon) repo=build - make_clone_from_package build_daemon "${repo}" master build_daemon ;; - build_integration) repo=sdk - make_clone_from_package build_integration "${repo}" master pkg/build_integration ;; - build_modules) repo=build - make_clone_from_package build_modules "${repo}" master build_modules ;; - build_node_compilers) repo=node-interop - make_clone_from_package build_node_compilers "${repo}" master build_node_compilers ;; - build_resolvers) repo=build - make_clone_from_package build_resolvers "${repo}" master build_resolvers ;; - build_runner) repo=build - make_clone_from_package build_runner "${repo}" master build_runner ;; - build_runner_core) repo=build - make_clone_from_package build_runner_core "${repo}" master build_runner_core;; - build_test) repo=build - make_clone_from_package build_test "${repo}" master build_test ;; - build_vm_compilers) repo=build - make_clone_from_package build_vm_compilers "${repo}" master build_vm_compilers ;; - build_web_compilers) repo=build - make_clone_from_package build_web_compilers "${repo}" master build_web_compilers ;; - built_collection) repo=built_collection.dart - make_clone_from_package built_collection "${repo}" ;; - built_value) repo=built_value.dart - make_clone_from_package built_value "${repo}" master built_value ;; - built_value_generator) repo=built_value.dart - make_clone_from_package built_value_generator "${repo}" master built_value_generator ;; - checked_yaml) repo=json_serializable - make_clone_from_package checked_yaml "${repo}" master checked_yaml ;; - expect) repo=sdk - make_clone_from_package expect "${repo}" master pkg/expect ;; - front_end) repo=sdk - make_clone_from_package front_end "${repo}" master pkg/front_end ;; - grinder) repo=grinder.dart - make_clone_from_package grinder "${repo}" ;; - kernel) repo=sdk - make_clone_from_package kernel "${repo}" master pkg/kernel ;; - meta) repo=sdk - make_clone_from_package meta "${repo}" master pkg/meta ;; - node_interop) repo=node-interop - make_clone_from_package node_interop "${repo}" master node_interop ;; - node_io) repo=node-interop - make_clone_from_package node_io "${repo}" master node_io ;; - js) repo=sdk - make_clone_from_package js "${repo}" master pkg/js ;; - json_annotation) repo=json_serializable - make_clone_from_package json_annotation "${repo}" master json_annotation ;; - json_serializable) repo=json_serializable - make_clone_from_package json_serializable "${repo}" master json_serializable ;; - package_config) repo=package_config - # TODO(jcollins-g): remove pin after https://github.com/dart-lang/sdk/issues/40208 - make_clone_from_package package_config "${repo}" 2453cd2e78c2db56ee2669ced17ce70dd00bf576 ;; - protobuf) repo=protobuf - make_clone_from_package protobuf "${repo}" master protobuf ;; - scratch_space) repo=build - make_clone_from_package scratch_space "${repo}" master scratch_space ;; - source_gen) repo=source_gen - make_clone_from_package source_gen "${repo}" master source_gen ;; - source_gen_test) repo=source_gen_test - make_clone_from_package source_gen_test "${repo}" ;; - test) repo=test - make_clone_from_package test "${repo}" master pkgs/test ;; - test_api) repo=test - make_clone_from_package test_api "${repo}" master pkgs/test_api ;; - test_core) repo=test - make_clone_from_package test_core "${repo}" master pkgs/test_core ;; - testing) repo=sdk - make_clone_from_package testing "${repo}" master pkg/testing ;; - vm_service) repo=sdk - make_clone_from_package vm_service "${repo}" master pkg/vm_service ;; - quiver) repo=quiver-dart - make_clone_from_package quiver "${repo}" ;; - *) repo="${package_name}" - make_clone_from_package "${package_name}" ;; - esac - - # Use the .packages file to check for packages we've completed in the - # past, and refresh them all. This enables us to detect if we need to rerun - # pub for any of them. - if [ -e ".packages" -a -z "${HAVE_DONE_GLOBAL_REFRESH}" ] ; then - HAVE_DONE_GLOBAL_REFRESH=1 - for n in $(grep -v '^#' ".packages" | cut -f 1 -d :) ; do - add_package_to_workspace "$n" - done - fi - - if [ "${package_name}" = "kernel" ] ; then - # HACK ALERT: kernel depends on unpublished packages, so we can't use pub. - for n in args meta expect front_end test testing ; do - add_package_to_workspace "$n" - done - else - # HACK ALERT: some packages have dependencies only available via path. Add - # those here. - case "${package_name}" in - analyzer) - add_package_to_workspace "analyzer_utilities" - ;; - esac - if [ -n "${NO_UPDATE}" ] || repo_changed_this_run "${repo}" ; then - if [ -z "${ONE_PUB_ONLY}" ] ; then - for n in $(generate_package_names_with_yq "${package_name}") ; do - add_package_to_workspace "$n" - done - else - for n in $(generate_package_names_with_pub "${package_name}") ; do - add_package_to_workspace "$n" - done - fi - fi - fi - rm -f "${package_name}/.packages" - rm -f "${package_name}/pubspec.lock" - rm -f "${package_name}/.dart_tool/package_config.json" -} - - -# -# Creates a flat workspace for the given package name. -# -# All repositories are checked out no more than once under _repos. These -# clones are "stock" without full history but should still be usable to make -# changes on -- the main issue is that all of them have any checked in -# .packages, pubspec.lock, or package_config.json files hidden. This could -# impact test running in the SDK if dev_dependencies are not fully specified in -# pubspec.yaml -- modify this script to add the necessary entries if need be. -# -# Symlinks to package directories are placed in the root of the workspace for -# convenience. -# -# One .packages file to rule them all is placed in the root of the workspace -- -# do not run pub get yourself on any of the packages. -# -# Set "NO_UPDATE" to "echo" to not try git pull on existing repos and to assume -# we always have to re-pub-get. This is useful in debugging the script and -# iteratively adding repository configurations. -# -# Set "ONE_PUB_ONLY" to the package being migrated to restrict running pub to -# one package only. This has the impact of only pulling in dev dependencies -# for the top level package. -# -# This script might be able to update your existing workspace, or it might -# trash it completely. Make backups. -# -function main { - if ! which yq ; then - echo "missing: yq. apt-get install yq or brew install yq" >&2 - return 2 - fi - - if [ -z "$1" -o -z "$2" ] ; then - echo usage: $0 source_package_name workspace_dir - return 2 - fi - - DEST_WORKSPACE_ROOT="$2" - - mkdir -p ${DEST_WORKSPACE_ROOT} - cd ${DEST_WORKSPACE_ROOT} - - add_package_to_workspace "$1" -} - - -main "$@" diff --git a/pkg/nnbd_migration/tool/summary_stats.dart b/pkg/nnbd_migration/tool/summary_stats.dart deleted file mode 100644 index 6e7bd5175ccb..000000000000 --- a/pkg/nnbd_migration/tool/summary_stats.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'dart:convert' show jsonDecode; -import 'dart:io'; - -void main(List args) { - var jsonPath = args[0]; - var json = - jsonDecode(File(jsonPath).readAsStringSync()) as Map; - var changes = json['changes'] as Map; - var byPath = changes['byPath'] as Map?; - if (args.isEmpty) { - print(''' -Usage: summary_stats.dart [category_name] - -Prints statistics of `dart migrate` suggestions summary, created with the -`--summary` flag of `dart migrate`. - -If [category_name] is not given, this prints the total number of suggestions -which the tool made, per category. - -If [category_name] is given, this prints the file names and suggestion counts of -each file with one or more suggestions categorized as [category_name]. -'''); - } else if (args.length == 1) { - _printTotals(byPath!); - } else { - var category = args[1]; - _printCategory(byPath!, category); - } -} - -/// Prints the file names and counts of files with suggestions of [category]. -void _printCategory(Map byPath, String category) { - byPath.forEach((String path, Object value) { - var counts = value as Map; - if (counts.containsKey(category)) { - print('$path: ${counts[category]}'); - } - }); -} - -/// Prints the total number of suggestions for each category. -void _printTotals(Map byPath) { - var summary = {}; - for (var file in byPath.values.cast>()) { - file.forEach((category, count) { - summary[category as String] = (summary[category] ?? 0) + (count as int); - }); - } - var categories = summary.keys.toList()..sort(); - for (var category in categories) { - print('$category: ${summary[category]}'); - } -}